Merge tag 'master-2014-12-08' of git://git.kernel.org/pub/scm/linux/kernel/git/linvil...
authorDavid S. Miller <davem@davemloft.net>
Tue, 9 Dec 2014 23:12:03 +0000 (18:12 -0500)
committerDavid S. Miller <davem@davemloft.net>
Tue, 9 Dec 2014 23:12:03 +0000 (18:12 -0500)
John W. Linville says:

====================
pull request: wireless-next 2014-12-08

Please pull this last batch of pending wireless updates for the 3.19 tree...

For the wireless bits, Johannes says:

"This time I have Felix's no-status rate control work, which will allow
drivers to work better with rate control even if they don't have perfect
status reporting. In addition to this, a small hwsim fix from Patrik,
one of the regulatory patches from Arik, and a number of cleanups and
fixes I did myself.

Of note is a patch where I disable CFG80211_WEXT so that compatibility
is no longer selectable - this is intended as a wake-up call for anyone
who's still using it, and is still easily worked around (it's a one-line
patch) before we fully remove the code as well in the future."

For the Bluetooth bits, Johan says:

"Here's one more bluetooth-next pull request for 3.19:

 - Minor cleanups for ieee802154 & mac802154
 - Fix for the kernel warning with !TASK_RUNNING reported by Kirill A.
   Shutemov
 - Support for another ath3k device
 - Fix for tracking link key based security level
 - Device tree bindings for btmrvl + a state update fix
 - Fix for wrong ACL flags on LE links"

And...

"In addition to the previous one this contains two more cleanups to
mac802154 as well as support for some new HCI features from the
Bluetooth 4.2 specification.

From the original request:

'Here's what should be the last bluetooth-next pull request for 3.19.
It's rather large but the majority of it is the Low Energy Secure
Connections feature that's part of the Bluetooth 4.2 specification. The
specification went public only this week so we couldn't publish the
corresponding code before that. The code itself can nevertheless be
considered fairly mature as it's been in development for over 6 months
and gone through several interoperability test events.

Besides LE SC the pull request contains an important fix for command
complete events for mgmt sockets which also fixes some leaks of hci_conn
objects when powering off or unplugging Bluetooth adapters.

A smaller feature that's part of the pull request is service discovery
support. This is like normal device discovery except that devices not
matching specific UUIDs or strong enough RSSI are filtered out.

Other changes that the pull request contains are firmware dump support
to the btmrvl driver, firmware download support for Broadcom BCM20702A0
variants, as well as some coding style cleanups in 6lowpan &
ieee802154/mac802154 code.'"

For the NFC bits, Samuel says:

"With this one we get:

- NFC digital improvements for DEP support: Chaining, NACK and ATN
  support added.

- NCI improvements: Support for p2p target, SE IO operand addition,
  SE operands extensions to support proprietary implementations, and
  a few fixes.

- NFC HCI improvements: OPEN_PIPE and NOTIFY_ALL_CLEARED support,
  and SE IO operand addition.

- A bunch of minor improvements and fixes for STMicro st21nfcb and
  st21nfca"

For the iwlwifi bits, Emmanuel says:

"Major works are CSA and TDLS. On top of that I have a new
firmware API for scan and a few rate control improvements.
Johannes find a few tricks to improve our CPU utilization
and adds support for a new spin of 7265 called 7265D.
Along with this a few random things that don't stand out."

And...

"I deprecate here -8.ucode since -9 has been published long ago.
Along with that I have a new activity, we have now better
a infrastructure for firmware debugging. This will allow to
have configurable probes insides the firmware.
Luca continues his work on NetDetect, this feature is now
complete. All the rest is minor fixes here and there."

For the Atheros bits, Kalle says:

"Only ath10k changes this time and no major changes. Most visible are:

o new debugfs interface for runtime firmware debugging (Yanbo)

o fix shared WEP (Sujith)

o don't rebuild whenever kernel version changes (Johannes)

o lots of refactoring to make it easier to add new hw support (Michal)

There's also smaller fixes and improvements with no point of listing
here."

In addition, there are a few last minute updates to ath5k,
ath9k, brcmfmac, brcmsmac, mwifiex, rt2x00, rtlwifi, and wil6210.
Also included is a pull of the wireless tree to pick-up the fixes
originally included in "pull request: wireless 2014-12-03"...

Please let me know if there are problems!
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
228 files changed:
Documentation/devicetree/bindings/btmrvl.txt [new file with mode: 0644]
MAINTAINERS
drivers/bluetooth/Kconfig
drivers/bluetooth/ath3k.c
drivers/bluetooth/btmrvl_debugfs.c
drivers/bluetooth/btmrvl_drv.h
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btmrvl_sdio.h
drivers/bluetooth/btusb.c
drivers/net/ethernet/broadcom/b44.c
drivers/net/ieee802154/cc2520.c
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/debug.h
drivers/net/wireless/ath/ath10k/hif.h
drivers/net/wireless/ath/ath10k/htc.c
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/mac.h
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/trace.h
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath5k/qcu.c
drivers/net/wireless/ath/ath9k/ar9002_mac.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_mac.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/dfs_pattern_detector.c
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/debug.c
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/fw.c
drivers/net/wireless/ath/wil6210/fw_inc.c
drivers/net/wireless/ath/wil6210/interrupt.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/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/brcm80211/brcmfmac/core.c
drivers/net/wireless/brcm80211/brcmfmac/core.h
drivers/net/wireless/brcm80211/brcmfmac/feature.c
drivers/net/wireless/brcm80211/brcmfmac/feature.h
drivers/net/wireless/brcm80211/brcmfmac/fweh.c
drivers/net/wireless/brcm80211/brcmfmac/fwil.c
drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
drivers/net/wireless/brcm80211/brcmfmac/pcie.c
drivers/net/wireless/brcm80211/brcmfmac/vendor.c
drivers/net/wireless/brcm80211/brcmsmac/debug.c
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/brcmutil/utils.c
drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
drivers/net/wireless/brcm80211/include/brcmu_utils.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/dvm/commands.h
drivers/net/wireless/iwlwifi/dvm/lib.c
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-8000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-csr.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/coex.c
drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
drivers/net/wireless/iwlwifi/mvm/constants.h
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/offloading.c
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/tdls.c
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/time-event.h
drivers/net/wireless/iwlwifi/mvm/tt.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n_rxreorder.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/init.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_tx.c
drivers/net/wireless/mwifiex/txrx.c
drivers/net/wireless/mwifiex/uap_event.c
drivers/net/wireless/mwifiex/uap_txrx.c
drivers/net/wireless/mwifiex/util.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/p54/net2280.h [deleted file]
drivers/net/wireless/p54/p54usb.h
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00usb.c
drivers/net/wireless/rt2x00/rt2x00usb.h
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtlwifi/core.c
drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
drivers/net/wireless/rtlwifi/rtl8192ee/Makefile
drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
drivers/net/wireless/rtlwifi/rtl8723be/Makefile
drivers/net/wireless/rtlwifi/rtl8821ae/Makefile
drivers/net/wireless/ti/wlcore/event.c
drivers/nfc/pn544/i2c.c
drivers/nfc/st21nfca/i2c.c
drivers/nfc/st21nfca/st21nfca.c
drivers/nfc/st21nfca/st21nfca.h
drivers/nfc/st21nfca/st21nfca_dep.c
drivers/nfc/st21nfca/st21nfca_dep.h
drivers/nfc/st21nfcb/i2c.c
drivers/nfc/st21nfcb/ndlc.c
drivers/ssb/pcihost_wrapper.c
include/linux/platform_data/st21nfca.h
include/linux/platform_data/st21nfcb.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
include/net/cfg80211.h
include/net/mac80211.h
include/net/nfc/digital.h
include/net/nfc/hci.h
include/net/nfc/nci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/net/regulatory.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
net/6lowpan/iphc.c
net/bluetooth/Kconfig
net/bluetooth/Makefile
net/bluetooth/af_bluetooth.c
net/bluetooth/ecc.c [new file with mode: 0644]
net/bluetooth/ecc.h [new file with mode: 0644]
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/l2cap_core.c
net/bluetooth/mgmt.c
net/bluetooth/smp.c
net/bluetooth/smp.h
net/ieee802154/6lowpan_rtnl.c
net/ieee802154/af_ieee802154.c
net/ieee802154/dgram.c
net/ieee802154/netlink.c
net/ieee802154/nl-mac.c
net/ieee802154/nl-phy.c
net/ieee802154/raw.c
net/mac80211/chan.c
net/mac80211/iface.c
net/mac80211/mlme.c
net/mac80211/rate.c
net/mac80211/rate.h
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/status.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac802154/iface.c
net/mac802154/llsec.c
net/mac802154/mib.c
net/mac802154/rx.c
net/nfc/digital_dep.c
net/nfc/hci/command.c
net/nfc/hci/core.c
net/nfc/llcp_commands.c
net/nfc/llcp_core.c
net/nfc/llcp_sock.c
net/nfc/nci/core.c
net/nfc/nci/data.c
net/nfc/nci/ntf.c
net/nfc/netlink.c
net/wireless/Kconfig
net/wireless/core.c
net/wireless/nl80211.c
net/wireless/reg.c

diff --git a/Documentation/devicetree/bindings/btmrvl.txt b/Documentation/devicetree/bindings/btmrvl.txt
new file mode 100644 (file)
index 0000000..58f964b
--- /dev/null
@@ -0,0 +1,29 @@
+btmrvl
+------
+
+Required properties:
+
+  - compatible : must be "btmrvl,cfgdata"
+
+Optional properties:
+
+  - btmrvl,cal-data : Calibration data downloaded to the device during
+                     initialization. This is an array of 28 values(u8).
+
+  - btmrvl,gpio-gap : gpio and gap (in msecs) combination to be
+                     configured.
+
+Example:
+
+GPIO pin 13 is configured as a wakeup source and GAP is set to 100 msecs
+in below example.
+
+btmrvl {
+       compatible = "btmrvl,cfgdata";
+
+       btmrvl,cal-data = /bits/ 8 <
+               0x37 0x01 0x1c 0x00 0xff 0xff 0xff 0xff 0x01 0x7f 0x04 0x02
+               0x00 0x00 0xba 0xce 0xc0 0xc6 0x2d 0x00 0x00 0x00 0x00 0x00
+               0x00 0x00 0xf0 0x00>;
+       btmrvl,gpio-gap = <0x0d64>;
+};
index c951535..cb3221d 100644 (file)
@@ -7919,11 +7919,10 @@ S:      Maintained
 F:     drivers/media/dvb-frontends/rtl2832_sdr*
 
 RTL8180 WIRELESS DRIVER
-M:     "John W. Linville" <linville@tuxdriver.com>
 L:     linux-wireless@vger.kernel.org
 W:     http://wireless.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
-S:     Maintained
+S:     Orphan
 F:     drivers/net/wireless/rtl818x/rtl8180/
 
 RTL8187 WIRELESS DRIVER
index 4547dc2..364f080 100644 (file)
@@ -210,6 +210,7 @@ config BT_MRVL_SDIO
        tristate "Marvell BT-over-SDIO driver"
        depends on BT_MRVL && MMC
        select FW_LOADER
+       select WANT_DEV_COREDUMP
        help
          The driver for Marvell Bluetooth chipsets with SDIO interface.
 
index 25c874d..fce7588 100644 (file)
@@ -106,6 +106,7 @@ static const struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x13d3, 0x3375) },
        { USB_DEVICE(0x13d3, 0x3393) },
        { USB_DEVICE(0x13d3, 0x3402) },
+       { USB_DEVICE(0x13d3, 0x3408) },
        { USB_DEVICE(0x13d3, 0x3432) },
 
        /* Atheros AR5BBU12 with sflash firmware */
@@ -158,6 +159,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
        { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU22 with sflash firmware */
index 023d35e..1828ed8 100644 (file)
@@ -167,6 +167,35 @@ static const struct file_operations btmrvl_hscmd_fops = {
        .llseek = default_llseek,
 };
 
+static ssize_t btmrvl_fwdump_write(struct file *file, const char __user *ubuf,
+                                  size_t count, loff_t *ppos)
+{
+       struct btmrvl_private *priv = file->private_data;
+       char buf[16];
+       bool result;
+
+       memset(buf, 0, sizeof(buf));
+
+       if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+               return -EFAULT;
+
+       if (strtobool(buf, &result))
+               return -EINVAL;
+
+       if (!result)
+               return -EINVAL;
+
+       btmrvl_firmware_dump(priv);
+
+       return count;
+}
+
+static const struct file_operations btmrvl_fwdump_fops = {
+       .write  = btmrvl_fwdump_write,
+       .open   = simple_open,
+       .llseek = default_llseek,
+};
+
 void btmrvl_debugfs_init(struct hci_dev *hdev)
 {
        struct btmrvl_private *priv = hci_get_drvdata(hdev);
@@ -197,6 +226,8 @@ void btmrvl_debugfs_init(struct hci_dev *hdev)
                            priv, &btmrvl_hscmd_fops);
        debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
                            priv, &btmrvl_hscfgcmd_fops);
+       debugfs_create_file("fw_dump", 0200, dbg->config_dir,
+                           priv, &btmrvl_fwdump_fops);
 
        dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
        debugfs_create_u8("curpsmode", 0444, dbg->status_dir,
index 38ad662..330f8f8 100644 (file)
 /* Time to wait for command response in millisecond */
 #define WAIT_UNTIL_CMD_RESP            5000
 
+enum rdwr_status {
+       RDWR_STATUS_SUCCESS = 0,
+       RDWR_STATUS_FAILURE = 1,
+       RDWR_STATUS_DONE = 2
+};
+
+#define FW_DUMP_MAX_NAME_LEN    8
+#define FW_DUMP_HOST_READY      0xEE
+#define FW_DUMP_DONE            0xFF
+#define FW_DUMP_READ_DONE       0xFE
+
+struct memory_type_mapping {
+       u8 mem_name[FW_DUMP_MAX_NAME_LEN];
+       u8 *mem_ptr;
+       u32 mem_size;
+       u8 done_flag;
+};
+
 struct btmrvl_thread {
        struct task_struct *task;
        wait_queue_head_t wait_q;
@@ -81,6 +99,7 @@ struct btmrvl_private {
                                u8 *payload, u16 nb);
        int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
        int (*hw_process_int_status) (struct btmrvl_private *priv);
+       void (*firmware_dump)(struct btmrvl_private *priv);
        spinlock_t driver_lock;         /* spinlock used by driver */
 #ifdef CONFIG_DEBUG_FS
        void *debugfs_data;
@@ -151,6 +170,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv);
 int btmrvl_enable_ps(struct btmrvl_private *priv);
 int btmrvl_prepare_command(struct btmrvl_private *priv);
 int btmrvl_enable_hs(struct btmrvl_private *priv);
+void btmrvl_firmware_dump(struct btmrvl_private *priv);
 
 #ifdef CONFIG_DEBUG_FS
 void btmrvl_debugfs_init(struct hci_dev *hdev);
index 1d7db20..30939c9 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/of.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <linux/mmc/sdio_func.h>
 
 #include "btmrvl_drv.h"
 #include "btmrvl_sdio.h"
@@ -41,6 +42,11 @@ void btmrvl_interrupt(struct btmrvl_private *priv)
 
        priv->adapter->int_count++;
 
+       if (priv->adapter->hs_state == HS_ACTIVATED) {
+               BT_DBG("BT: HS DEACTIVATED in ISR!");
+               priv->adapter->hs_state = HS_DEACTIVATED;
+       }
+
        wake_up_interruptible(&priv->main_thread.wait_q);
 }
 EXPORT_SYMBOL_GPL(btmrvl_interrupt);
@@ -209,7 +215,7 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)
 
        ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1);
        if (ret)
-               BT_ERR("module_cfg_cmd(%x) failed\n", subcmd);
+               BT_ERR("module_cfg_cmd(%x) failed", subcmd);
 
        return ret;
 }
@@ -245,7 +251,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv)
 
        ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2);
        if (ret)
-               BT_ERR("HSCFG command failed\n");
+               BT_ERR("HSCFG command failed");
 
        return ret;
 }
@@ -263,7 +269,7 @@ int btmrvl_enable_ps(struct btmrvl_private *priv)
 
        ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, &param, 1);
        if (ret)
-               BT_ERR("PSMODE command failed\n");
+               BT_ERR("PSMODE command failed");
 
        return 0;
 }
@@ -276,7 +282,7 @@ int btmrvl_enable_hs(struct btmrvl_private *priv)
 
        ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0);
        if (ret) {
-               BT_ERR("Host sleep enable command failed\n");
+               BT_ERR("Host sleep enable command failed");
                return ret;
        }
 
@@ -323,12 +329,19 @@ int btmrvl_prepare_command(struct btmrvl_private *priv)
                } else {
                        ret = priv->hw_wakeup_firmware(priv);
                        priv->adapter->hs_state = HS_DEACTIVATED;
+                       BT_DBG("BT: HS DEACTIVATED due to host activity!");
                }
        }
 
        return ret;
 }
 
+void btmrvl_firmware_dump(struct btmrvl_private *priv)
+{
+       if (priv->firmware_dump)
+               priv->firmware_dump(priv);
+}
+
 static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
 {
        int ret = 0;
@@ -487,34 +500,36 @@ static int btmrvl_download_cal_data(struct btmrvl_private *priv,
        ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data,
                                   BT_CAL_HDR_LEN + len);
        if (ret)
-               BT_ERR("Failed to download caibration data\n");
+               BT_ERR("Failed to download caibration data");
 
        return 0;
 }
 
-static int btmrvl_cal_data_dt(struct btmrvl_private *priv)
+static int btmrvl_check_device_tree(struct btmrvl_private *priv)
 {
        struct device_node *dt_node;
        u8 cal_data[BT_CAL_HDR_LEN + BT_CAL_DATA_SIZE];
-       const char name[] = "btmrvl_caldata";
-       const char property[] = "btmrvl,caldata";
        int ret;
-
-       dt_node = of_find_node_by_name(NULL, name);
-       if (!dt_node)
-               return -ENODEV;
-
-       ret = of_property_read_u8_array(dt_node, property,
-                                       cal_data + BT_CAL_HDR_LEN,
-                                       BT_CAL_DATA_SIZE);
-       if (ret)
-               return ret;
-
-       BT_DBG("Use cal data from device tree");
-       ret = btmrvl_download_cal_data(priv, cal_data, BT_CAL_DATA_SIZE);
-       if (ret) {
-               BT_ERR("Fail to download calibrate data");
-               return ret;
+       u32 val;
+
+       for_each_compatible_node(dt_node, NULL, "btmrvl,cfgdata") {
+               ret = of_property_read_u32(dt_node, "btmrvl,gpio-gap", &val);
+               if (!ret)
+                       priv->btmrvl_dev.gpio_gap = val;
+
+               ret = of_property_read_u8_array(dt_node, "btmrvl,cal-data",
+                                               cal_data + BT_CAL_HDR_LEN,
+                                               BT_CAL_DATA_SIZE);
+               if (ret)
+                       return ret;
+
+               BT_DBG("Use cal data from device tree");
+               ret = btmrvl_download_cal_data(priv, cal_data,
+                                              BT_CAL_DATA_SIZE);
+               if (ret) {
+                       BT_ERR("Fail to download calibrate data");
+                       return ret;
+               }
        }
 
        return 0;
@@ -526,14 +541,15 @@ static int btmrvl_setup(struct hci_dev *hdev)
 
        btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
 
-       btmrvl_cal_data_dt(priv);
+       priv->btmrvl_dev.gpio_gap = 0xffff;
+
+       btmrvl_check_device_tree(priv);
 
        btmrvl_pscan_window_reporting(priv, 0x01);
 
        priv->btmrvl_dev.psmode = 1;
        btmrvl_enable_ps(priv);
 
-       priv->btmrvl_dev.gpio_gap = 0xffff;
        btmrvl_send_hscfg_cmd(priv);
 
        return 0;
index 550bce0..0057c0b 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/module.h>
+#include <linux/devcoredump.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
 #define VERSION "1.0"
 
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
+       {"ITCM", NULL, 0, 0xF0},
+       {"DTCM", NULL, 0, 0xF1},
+       {"SQRAM", NULL, 0, 0xF2},
+       {"APU", NULL, 0, 0xF3},
+       {"CIU", NULL, 0, 0xF4},
+       {"ICU", NULL, 0, 0xF5},
+       {"MAC", NULL, 0, 0xF6},
+       {"EXT7", NULL, 0, 0xF7},
+       {"EXT8", NULL, 0, 0xF8},
+       {"EXT9", NULL, 0, 0xF9},
+       {"EXT10", NULL, 0, 0xFA},
+       {"EXT11", NULL, 0, 0xFB},
+       {"EXT12", NULL, 0, 0xFC},
+       {"EXT13", NULL, 0, 0xFD},
+       {"EXTLAST", NULL, 0, 0xFE},
+};
+
 /* The btmrvl_sdio_remove() callback function is called
  * when user removes this module from kernel space or ejects
  * the card from the slot. The driver handles these 2 cases
@@ -122,6 +141,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
        .int_read_to_clear = true,
        .host_int_rsr = 0x01,
        .card_misc_cfg = 0xcc,
+       .fw_dump_ctrl = 0xe2,
+       .fw_dump_start = 0xe3,
+       .fw_dump_end = 0xea,
 };
 
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
@@ -130,6 +152,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
        .reg            = &btmrvl_reg_8688,
        .support_pscan_win_report = false,
        .sd_blksz_fw_dl = 64,
+       .supports_fw_dump = false,
 };
 
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
@@ -138,6 +161,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
        .reg            = &btmrvl_reg_87xx,
        .support_pscan_win_report = false,
        .sd_blksz_fw_dl = 256,
+       .supports_fw_dump = false,
 };
 
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
@@ -146,6 +170,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
        .reg            = &btmrvl_reg_87xx,
        .support_pscan_win_report = false,
        .sd_blksz_fw_dl = 256,
+       .supports_fw_dump = false,
 };
 
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = {
@@ -154,6 +179,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = {
        .reg            = &btmrvl_reg_8887,
        .support_pscan_win_report = true,
        .sd_blksz_fw_dl = 256,
+       .supports_fw_dump = false,
 };
 
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
@@ -162,6 +188,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
        .reg            = &btmrvl_reg_8897,
        .support_pscan_win_report = true,
        .sd_blksz_fw_dl = 256,
+       .supports_fw_dump = true,
 };
 
 static const struct sdio_device_id btmrvl_sdio_ids[] = {
@@ -764,8 +791,8 @@ static void btmrvl_sdio_interrupt(struct sdio_func *func)
 
        card = sdio_get_drvdata(func);
        if (!card || !card->priv) {
-               BT_ERR("sbi_interrupt(%p) card or priv is "
-                               "NULL, card=%p\n", func, card);
+               BT_ERR("sbi_interrupt(%p) card or priv is NULL, card=%p",
+                      func, card);
                return;
        }
 
@@ -1080,6 +1107,277 @@ static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv)
        return ret;
 }
 
+static void btmrvl_sdio_dump_regs(struct btmrvl_private *priv)
+{
+       struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+       int ret = 0;
+       unsigned int reg, reg_start, reg_end;
+       char buf[256], *ptr;
+       u8 loop, func, data;
+       int MAX_LOOP = 2;
+
+       btmrvl_sdio_wakeup_fw(priv);
+       sdio_claim_host(card->func);
+
+       for (loop = 0; loop < MAX_LOOP; loop++) {
+               memset(buf, 0, sizeof(buf));
+               ptr = buf;
+
+               if (loop == 0) {
+                       /* Read the registers of SDIO function0 */
+                       func = loop;
+                       reg_start = 0;
+                       reg_end = 9;
+               } else {
+                       func = 2;
+                       reg_start = 0;
+                       reg_end = 0x09;
+               }
+
+               ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ",
+                              func, reg_start, reg_end);
+               for (reg = reg_start; reg <= reg_end; reg++) {
+                       if (func == 0)
+                               data = sdio_f0_readb(card->func, reg, &ret);
+                       else
+                               data = sdio_readb(card->func, reg, &ret);
+
+                       if (!ret) {
+                               ptr += sprintf(ptr, "%02x ", data);
+                       } else {
+                               ptr += sprintf(ptr, "ERR");
+                               break;
+                       }
+               }
+
+               BT_INFO("%s", buf);
+       }
+
+       sdio_release_host(card->func);
+}
+
+/* This function read/write firmware */
+static enum
+rdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv,
+                                     u8 doneflag)
+{
+       struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+       int ret, tries;
+       u8 ctrl_data = 0;
+
+       sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl,
+                   &ret);
+
+       if (ret) {
+               BT_ERR("SDIO write err");
+               return RDWR_STATUS_FAILURE;
+       }
+
+       for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+               ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl,
+                                      &ret);
+
+               if (ret) {
+                       BT_ERR("SDIO read err");
+                       return RDWR_STATUS_FAILURE;
+               }
+
+               if (ctrl_data == FW_DUMP_DONE)
+                       break;
+               if (doneflag && ctrl_data == doneflag)
+                       return RDWR_STATUS_DONE;
+               if (ctrl_data != FW_DUMP_HOST_READY) {
+                       BT_INFO("The ctrl reg was changed, re-try again!");
+                       sdio_writeb(card->func, FW_DUMP_HOST_READY,
+                                   card->reg->fw_dump_ctrl, &ret);
+                       if (ret) {
+                               BT_ERR("SDIO write err");
+                               return RDWR_STATUS_FAILURE;
+                       }
+               }
+               usleep_range(100, 200);
+       }
+
+       if (ctrl_data == FW_DUMP_HOST_READY) {
+               BT_ERR("Fail to pull ctrl_data");
+               return RDWR_STATUS_FAILURE;
+       }
+
+       return RDWR_STATUS_SUCCESS;
+}
+
+/* This function dump sdio register and memory data */
+static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv)
+{
+       struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+       int ret = 0;
+       unsigned int reg, reg_start, reg_end;
+       enum rdwr_status stat;
+       u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr;
+       u8 dump_num, idx, i, read_reg, doneflag = 0;
+       u32 memory_size, fw_dump_len = 0;
+
+       /* dump sdio register first */
+       btmrvl_sdio_dump_regs(priv);
+
+       if (!card->supports_fw_dump) {
+               BT_ERR("Firmware dump not supported for this card!");
+               return;
+       }
+
+       for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
+               struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+               if (entry->mem_ptr) {
+                       vfree(entry->mem_ptr);
+                       entry->mem_ptr = NULL;
+               }
+               entry->mem_size = 0;
+       }
+
+       btmrvl_sdio_wakeup_fw(priv);
+       sdio_claim_host(card->func);
+
+       BT_INFO("== btmrvl firmware dump start ==");
+
+       stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
+       if (stat == RDWR_STATUS_FAILURE)
+               goto done;
+
+       reg = card->reg->fw_dump_start;
+       /* Read the number of the memories which will dump */
+       dump_num = sdio_readb(card->func, reg, &ret);
+
+       if (ret) {
+               BT_ERR("SDIO read memory length err");
+               goto done;
+       }
+
+       /* Read the length of every memory which will dump */
+       for (idx = 0; idx < dump_num; idx++) {
+               struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+               stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
+               if (stat == RDWR_STATUS_FAILURE)
+                       goto done;
+
+               memory_size = 0;
+               reg = card->reg->fw_dump_start;
+               for (i = 0; i < 4; i++) {
+                       read_reg = sdio_readb(card->func, reg, &ret);
+                       if (ret) {
+                               BT_ERR("SDIO read err");
+                               goto done;
+                       }
+                       memory_size |= (read_reg << i*8);
+                       reg++;
+               }
+
+               if (memory_size == 0) {
+                       BT_INFO("Firmware dump finished!");
+                       break;
+               }
+
+               BT_INFO("%s_SIZE=0x%x", entry->mem_name, memory_size);
+               entry->mem_ptr = vzalloc(memory_size + 1);
+               entry->mem_size = memory_size;
+               if (!entry->mem_ptr) {
+                       BT_ERR("Vzalloc %s failed", entry->mem_name);
+                       goto done;
+               }
+
+               fw_dump_len += (strlen("========Start dump ") +
+                               strlen(entry->mem_name) +
+                               strlen("========\n") +
+                               (memory_size + 1) +
+                               strlen("\n========End dump========\n"));
+
+               dbg_ptr = entry->mem_ptr;
+               end_ptr = dbg_ptr + memory_size;
+
+               doneflag = entry->done_flag;
+               BT_INFO("Start %s output, please wait...",
+                       entry->mem_name);
+
+               do {
+                       stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
+                       if (stat == RDWR_STATUS_FAILURE)
+                               goto done;
+
+                       reg_start = card->reg->fw_dump_start;
+                       reg_end = card->reg->fw_dump_end;
+                       for (reg = reg_start; reg <= reg_end; reg++) {
+                               *dbg_ptr = sdio_readb(card->func, reg, &ret);
+                               if (ret) {
+                                       BT_ERR("SDIO read err");
+                                       goto done;
+                               }
+                               if (dbg_ptr < end_ptr)
+                                       dbg_ptr++;
+                               else
+                                       BT_ERR("Allocated buffer not enough");
+                       }
+
+                       if (stat != RDWR_STATUS_DONE) {
+                               continue;
+                       } else {
+                               BT_INFO("%s done: size=0x%tx",
+                                       entry->mem_name,
+                                       dbg_ptr - entry->mem_ptr);
+                               break;
+                       }
+               } while (1);
+       }
+
+       BT_INFO("== btmrvl firmware dump end ==");
+
+done:
+       sdio_release_host(card->func);
+
+       if (fw_dump_len == 0)
+               return;
+
+       fw_dump_data = vzalloc(fw_dump_len+1);
+       if (!fw_dump_data) {
+               BT_ERR("Vzalloc fw_dump_data fail!");
+               return;
+       }
+       fw_dump_ptr = fw_dump_data;
+
+       /* Dump all the memory data into single file, a userspace script will
+          be used to split all the memory data to multiple files*/
+       BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start");
+       for (idx = 0; idx < dump_num; idx++) {
+               struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+               if (entry->mem_ptr) {
+                       strcpy(fw_dump_ptr, "========Start dump ");
+                       fw_dump_ptr += strlen("========Start dump ");
+
+                       strcpy(fw_dump_ptr, entry->mem_name);
+                       fw_dump_ptr += strlen(entry->mem_name);
+
+                       strcpy(fw_dump_ptr, "========\n");
+                       fw_dump_ptr += strlen("========\n");
+
+                       memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
+                       fw_dump_ptr += entry->mem_size;
+
+                       strcpy(fw_dump_ptr, "\n========End dump========\n");
+                       fw_dump_ptr += strlen("\n========End dump========\n");
+
+                       vfree(mem_type_mapping_tbl[idx].mem_ptr);
+                       mem_type_mapping_tbl[idx].mem_ptr = NULL;
+               }
+       }
+
+       /* fw_dump_data will be free in device coredump release function
+          after 5 min*/
+       dev_coredumpv(&priv->btmrvl_dev.hcidev->dev, fw_dump_data,
+                     fw_dump_len, GFP_KERNEL);
+       BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end");
+}
+
 static int btmrvl_sdio_probe(struct sdio_func *func,
                                        const struct sdio_device_id *id)
 {
@@ -1103,6 +1401,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
                card->reg = data->reg;
                card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
                card->support_pscan_win_report = data->support_pscan_win_report;
+               card->supports_fw_dump = data->supports_fw_dump;
        }
 
        if (btmrvl_sdio_register_dev(card) < 0) {
@@ -1134,6 +1433,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
        priv->hw_host_to_card = btmrvl_sdio_host_to_card;
        priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
        priv->hw_process_int_status = btmrvl_sdio_process_int_status;
+       priv->firmware_dump = btmrvl_sdio_dump_firmware;
 
        if (btmrvl_register_hdev(priv)) {
                BT_ERR("Register hdev failed!");
index 453559f..1a3bd06 100644 (file)
@@ -81,6 +81,9 @@ struct btmrvl_sdio_card_reg {
        bool int_read_to_clear;
        u8 host_int_rsr;
        u8 card_misc_cfg;
+       u8 fw_dump_ctrl;
+       u8 fw_dump_start;
+       u8 fw_dump_end;
 };
 
 struct btmrvl_sdio_card {
@@ -90,6 +93,7 @@ struct btmrvl_sdio_card {
        const char *firmware;
        const struct btmrvl_sdio_card_reg *reg;
        bool support_pscan_win_report;
+       bool supports_fw_dump;
        u16 sd_blksz_fw_dl;
        u8 rx_unit;
        struct btmrvl_private *priv;
@@ -101,6 +105,7 @@ struct btmrvl_sdio_device {
        const struct btmrvl_sdio_card_reg *reg;
        const bool support_pscan_win_report;
        u16 sd_blksz_fw_dl;
+       bool supports_fw_dump;
 };
 
 
index 7c13d7a..31dd24a 100644 (file)
@@ -110,7 +110,8 @@ static const struct usb_device_id btusb_table[] = {
          .driver_info = BTUSB_BCM_PATCHRAM },
 
        /* Foxconn - Hon Hai */
-       { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
+       { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01),
+         .driver_info = BTUSB_BCM_PATCHRAM },
 
        /* Broadcom devices with vendor specific id */
        { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
@@ -185,6 +186,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU12 with sflash firmware */
index 416620f..ffeaf47 100644 (file)
@@ -2104,6 +2104,7 @@ static int b44_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
                bp->flags &= ~B44_FLAG_WOL_ENABLE;
        spin_unlock_irq(&bp->lock);
 
+       device_set_wakeup_enable(bp->sdev->dev, wol->wolopts & WAKE_MAGIC);
        return 0;
 }
 
@@ -2452,6 +2453,7 @@ static int b44_init_one(struct ssb_device *sdev,
                }
        }
 
+       device_set_wakeup_capable(sdev->dev, true);
        netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr);
 
        return 0;
index ccbb082..f9df9fa 100644 (file)
@@ -858,7 +858,7 @@ static int cc2520_probe(struct spi_device *spi)
        pinctrl = devm_pinctrl_get_select_default(&spi->dev);
        if (IS_ERR(pinctrl))
                dev_warn(&spi->dev,
-                        "pinctrl pins are not configured");
+                        "pinctrl pins are not configured\n");
 
        pdata = cc2520_get_platform_data(spi);
        if (!pdata) {
index f660553..7762061 100644 (file)
@@ -799,6 +799,17 @@ static void ath10k_core_restart(struct work_struct *work)
        mutex_unlock(&ar->conf_mutex);
 }
 
+static void ath10k_core_init_max_sta_count(struct ath10k *ar)
+{
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+               ar->max_num_peers = TARGET_10X_NUM_PEERS;
+               ar->max_num_stations = TARGET_10X_NUM_STATIONS;
+       } else {
+               ar->max_num_peers = TARGET_NUM_PEERS;
+               ar->max_num_stations = TARGET_NUM_STATIONS;
+       }
+}
+
 int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
 {
        int status;
@@ -1035,6 +1046,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
                return ret;
        }
 
+       ath10k_core_init_max_sta_count(ar);
+
        mutex_lock(&ar->conf_mutex);
 
        ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL);
index 8f86bd3..514c219 100644 (file)
@@ -79,10 +79,12 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus)
 
 struct ath10k_skb_cb {
        dma_addr_t paddr;
+       u8 eid;
        u8 vdev_id;
 
        struct {
                u8 tid;
+               u16 freq;
                bool is_offchan;
                struct ath10k_htt_txbuf *txbuf;
                u32 txbuf_paddr;
@@ -122,6 +124,7 @@ struct ath10k_wmi {
        struct completion service_ready;
        struct completion unified_ready;
        wait_queue_head_t tx_credits_wq;
+       DECLARE_BITMAP(svc_map, WMI_SERVICE_MAX);
        struct wmi_cmd_map *cmd;
        struct wmi_vdev_param_map *vdev_param;
        struct wmi_pdev_param_map *pdev_param;
@@ -218,6 +221,8 @@ struct ath10k_peer {
        int vdev_id;
        u8 addr[ETH_ALEN];
        DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
+
+       /* protected by ar->data_lock */
        struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
 };
 
@@ -310,7 +315,6 @@ struct ath10k_debug {
        struct ath10k_fw_stats fw_stats;
        struct completion fw_stats_complete;
        bool fw_stats_done;
-       DECLARE_BITMAP(wmi_service_bitmap, WMI_SERVICE_MAX);
 
        unsigned long htt_stats_mask;
        struct delayed_work htt_stats_dwork;
@@ -320,6 +324,7 @@ struct ath10k_debug {
        /* protected by conf_mutex */
        u32 fw_dbglog_mask;
        u32 pktlog_filter;
+       u32 reg_addr;
 
        u8 htt_max_amsdu;
        u8 htt_max_ampdu;
@@ -560,8 +565,12 @@ struct ath10k {
        struct list_head peers;
        wait_queue_head_t peer_mapping_wq;
 
-       /* number of created peers; protected by data_lock */
+       /* protected by conf_mutex */
        int num_peers;
+       int num_stations;
+
+       int max_num_peers;
+       int max_num_stations;
 
        struct work_struct offchan_tx_work;
        struct sk_buff_head offchan_tx_queue;
index a8f5a72..a716758 100644 (file)
@@ -17,9 +17,8 @@
 
 #include <linux/module.h>
 #include <linux/debugfs.h>
-#include <linux/version.h>
-#include <linux/vermagic.h>
 #include <linux/vmalloc.h>
+#include <linux/utsname.h>
 
 #include "core.h"
 #include "debug.h"
@@ -124,7 +123,7 @@ EXPORT_SYMBOL(ath10k_info);
 
 void ath10k_print_driver_info(struct ath10k *ar)
 {
-       ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d.%d.%d.%d cal %s\n",
+       ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d.%d.%d.%d cal %s max_sta %d\n",
                    ar->hw_params.name,
                    ar->target_version,
                    ar->chip_id,
@@ -136,7 +135,8 @@ void ath10k_print_driver_info(struct ath10k *ar)
                    ar->fw_version_minor,
                    ar->fw_version_release,
                    ar->fw_version_build,
-                   ath10k_cal_mode_str(ar->cal_mode));
+                   ath10k_cal_mode_str(ar->cal_mode),
+                   ar->max_num_stations);
        ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n",
                    config_enabled(CONFIG_ATH10K_DEBUG),
                    config_enabled(CONFIG_ATH10K_DEBUGFS),
@@ -179,13 +179,6 @@ EXPORT_SYMBOL(ath10k_warn);
 
 #ifdef CONFIG_ATH10K_DEBUGFS
 
-void ath10k_debug_read_service_map(struct ath10k *ar,
-                                  const 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)
@@ -207,8 +200,9 @@ static ssize_t ath10k_read_wmi_services(struct file *file,
        if (len > buf_len)
                len = buf_len;
 
+       spin_lock_bh(&ar->data_lock);
        for (i = 0; i < WMI_SERVICE_MAX; i++) {
-               enabled = test_bit(i, ar->debug.wmi_service_bitmap);
+               enabled = test_bit(i, ar->wmi.svc_map);
                name = wmi_service_name(i);
 
                if (!name) {
@@ -224,6 +218,7 @@ static ssize_t ath10k_read_wmi_services(struct file *file,
                                 "%-40s %s\n",
                                 name, enabled ? "enabled" : "-");
        }
+       spin_unlock_bh(&ar->data_lock);
 
        ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
@@ -866,8 +861,8 @@ static struct ath10k_dump_file_data *ath10k_build_dump_file(struct ath10k *ar)
        strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version,
                sizeof(dump_data->fw_ver));
 
-       dump_data->kernel_ver_code = cpu_to_le32(LINUX_VERSION_CODE);
-       strlcpy(dump_data->kernel_ver, VERMAGIC_STRING,
+       dump_data->kernel_ver_code = 0;
+       strlcpy(dump_data->kernel_ver, init_utsname()->release,
                sizeof(dump_data->kernel_ver));
 
        dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec);
@@ -929,6 +924,236 @@ static const struct file_operations fops_fw_crash_dump = {
        .llseek = default_llseek,
 };
 
+static ssize_t ath10k_reg_addr_read(struct file *file,
+                                   char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       u8 buf[32];
+       unsigned int len = 0;
+       u32 reg_addr;
+
+       mutex_lock(&ar->conf_mutex);
+       reg_addr = ar->debug.reg_addr;
+       mutex_unlock(&ar->conf_mutex);
+
+       len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_addr);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_reg_addr_write(struct file *file,
+                                    const char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       u32 reg_addr;
+       int ret;
+
+       ret = kstrtou32_from_user(user_buf, count, 0, &reg_addr);
+       if (ret)
+               return ret;
+
+       if (!IS_ALIGNED(reg_addr, 4))
+               return -EFAULT;
+
+       mutex_lock(&ar->conf_mutex);
+       ar->debug.reg_addr = reg_addr;
+       mutex_unlock(&ar->conf_mutex);
+
+       return count;
+}
+
+static const struct file_operations fops_reg_addr = {
+       .read = ath10k_reg_addr_read,
+       .write = ath10k_reg_addr_write,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t ath10k_reg_value_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       u8 buf[48];
+       unsigned int len;
+       u32 reg_addr, reg_val;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state != ATH10K_STATE_ON &&
+           ar->state != ATH10K_STATE_UTF) {
+               ret = -ENETDOWN;
+               goto exit;
+       }
+
+       reg_addr = ar->debug.reg_addr;
+
+       reg_val = ath10k_hif_read32(ar, reg_addr);
+       len = scnprintf(buf, sizeof(buf), "0x%08x:0x%08x\n", reg_addr, reg_val);
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+
+       return ret;
+}
+
+static ssize_t ath10k_reg_value_write(struct file *file,
+                                     const char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       u32 reg_addr, reg_val;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state != ATH10K_STATE_ON &&
+           ar->state != ATH10K_STATE_UTF) {
+               ret = -ENETDOWN;
+               goto exit;
+       }
+
+       reg_addr = ar->debug.reg_addr;
+
+       ret = kstrtou32_from_user(user_buf, count, 0, &reg_val);
+       if (ret)
+               goto exit;
+
+       ath10k_hif_write32(ar, reg_addr, reg_val);
+
+       ret = count;
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+
+       return ret;
+}
+
+static const struct file_operations fops_reg_value = {
+       .read = ath10k_reg_value_read,
+       .write = ath10k_reg_value_write,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t ath10k_mem_value_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       u8 *buf;
+       int ret;
+
+       if (*ppos < 0)
+               return -EINVAL;
+
+       if (!count)
+               return 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       buf = vmalloc(count);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       if (ar->state != ATH10K_STATE_ON &&
+           ar->state != ATH10K_STATE_UTF) {
+               ret = -ENETDOWN;
+               goto exit;
+       }
+
+       ret = ath10k_hif_diag_read(ar, *ppos, buf, count);
+       if (ret) {
+               ath10k_warn(ar, "failed to read address 0x%08x via diagnose window fnrom debugfs: %d\n",
+                           (u32)(*ppos), ret);
+               goto exit;
+       }
+
+       ret = copy_to_user(user_buf, buf, count);
+       if (ret) {
+               ret = -EFAULT;
+               goto exit;
+       }
+
+       count -= ret;
+       *ppos += count;
+       ret = count;
+
+exit:
+       vfree(buf);
+       mutex_unlock(&ar->conf_mutex);
+
+       return ret;
+}
+
+static ssize_t ath10k_mem_value_write(struct file *file,
+                                     const char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       u8 *buf;
+       int ret;
+
+       if (*ppos < 0)
+               return -EINVAL;
+
+       if (!count)
+               return 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       buf = vmalloc(count);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       if (ar->state != ATH10K_STATE_ON &&
+           ar->state != ATH10K_STATE_UTF) {
+               ret = -ENETDOWN;
+               goto exit;
+       }
+
+       ret = copy_from_user(buf, user_buf, count);
+       if (ret) {
+               ret = -EFAULT;
+               goto exit;
+       }
+
+       ret = ath10k_hif_diag_write(ar, *ppos, buf, count);
+       if (ret) {
+               ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",
+                           (u32)(*ppos), ret);
+               goto exit;
+       }
+
+       *ppos += count;
+       ret = count;
+
+exit:
+       vfree(buf);
+       mutex_unlock(&ar->conf_mutex);
+
+       return ret;
+}
+
+static const struct file_operations fops_mem_value = {
+       .read = ath10k_mem_value_read,
+       .write = ath10k_mem_value_write,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 static int ath10k_debug_htt_stats_req(struct ath10k *ar)
 {
        u64 cookie;
@@ -1630,6 +1855,15 @@ int ath10k_debug_register(struct ath10k *ar)
        debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy,
                            ar, &fops_fw_crash_dump);
 
+       debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR,
+                           ar->debug.debugfs_phy, ar, &fops_reg_addr);
+
+       debugfs_create_file("reg_value", S_IRUSR | S_IWUSR,
+                           ar->debug.debugfs_phy, ar, &fops_reg_value);
+
+       debugfs_create_file("mem_value", S_IRUSR | S_IWUSR,
+                           ar->debug.debugfs_phy, ar, &fops_mem_value);
+
        debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
                            ar, &fops_chip_id);
 
index 0c934a8..1b87a5d 100644 (file)
@@ -35,6 +35,7 @@ enum ath10k_debug_mask {
        ATH10K_DBG_BMI          = 0x00000400,
        ATH10K_DBG_REGULATORY   = 0x00000800,
        ATH10K_DBG_TESTMODE     = 0x00001000,
+       ATH10K_DBG_WMI_PRINT    = 0x00002000,
        ATH10K_DBG_ANY          = 0xffffffff,
 };
 
@@ -61,9 +62,6 @@ int ath10k_debug_create(struct ath10k *ar);
 void ath10k_debug_destroy(struct ath10k *ar);
 int ath10k_debug_register(struct ath10k *ar);
 void ath10k_debug_unregister(struct ath10k *ar);
-void ath10k_debug_read_service_map(struct ath10k *ar,
-                                  const void *service_map,
-                                  size_t map_size);
 void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
 struct ath10k_fw_crash_data *
 ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
@@ -108,12 +106,6 @@ static inline void ath10k_debug_unregister(struct ath10k *ar)
 {
 }
 
-static inline void ath10k_debug_read_service_map(struct ath10k *ar,
-                                                const void *service_map,
-                                                size_t map_size)
-{
-}
-
 static inline void ath10k_debug_fw_stats_process(struct ath10k *ar,
                                                 struct sk_buff *skb)
 {
index 30301f5..0c92e02 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/kernel.h>
 #include "core.h"
+#include "debug.h"
 
 struct ath10k_hif_sg_item {
        u16 transfer_id;
@@ -31,11 +32,9 @@ struct ath10k_hif_sg_item {
 
 struct ath10k_hif_cb {
        int (*tx_completion)(struct ath10k *ar,
-                            struct sk_buff *wbuf,
-                            unsigned transfer_id);
+                            struct sk_buff *wbuf);
        int (*rx_completion)(struct ath10k *ar,
-                            struct sk_buff *wbuf,
-                            u8 pipe_id);
+                            struct sk_buff *wbuf);
 };
 
 struct ath10k_hif_ops {
@@ -47,6 +46,8 @@ struct ath10k_hif_ops {
        int (*diag_read)(struct ath10k *ar, u32 address, void *buf,
                         size_t buf_len);
 
+       int (*diag_write)(struct ath10k *ar, u32 address, const void *data,
+                         int nbytes);
        /*
         * API to handle HIF-specific BMI message exchanges, this API is
         * synchronous and only allowed to be called from a context that
@@ -84,6 +85,10 @@ struct ath10k_hif_ops {
 
        u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
 
+       u32 (*read32)(struct ath10k *ar, u32 address);
+
+       void (*write32)(struct ath10k *ar, u32 address, u32 value);
+
        /* Power up the device and enter BMI transfer mode for FW download */
        int (*power_up)(struct ath10k *ar);
 
@@ -108,6 +113,15 @@ static inline int ath10k_hif_diag_read(struct ath10k *ar, u32 address, void *buf
        return ar->hif.ops->diag_read(ar, address, buf, buf_len);
 }
 
+static inline int ath10k_hif_diag_write(struct ath10k *ar, u32 address,
+                                       const void *data, int nbytes)
+{
+       if (!ar->hif.ops->diag_write)
+               return -EOPNOTSUPP;
+
+       return ar->hif.ops->diag_write(ar, address, data, nbytes);
+}
+
 static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
                                              void *request, u32 request_len,
                                              void *response, u32 *response_len)
@@ -187,4 +201,25 @@ static inline int ath10k_hif_resume(struct ath10k *ar)
        return ar->hif.ops->resume(ar);
 }
 
+static inline u32 ath10k_hif_read32(struct ath10k *ar, u32 address)
+{
+       if (!ar->hif.ops->read32) {
+               ath10k_warn(ar, "hif read32 not supported\n");
+               return 0xdeaddead;
+       }
+
+       return ar->hif.ops->read32(ar, address);
+}
+
+static inline void ath10k_hif_write32(struct ath10k *ar,
+                                     u32 address, u32 data)
+{
+       if (!ar->hif.ops->write32) {
+               ath10k_warn(ar, "hif write32 not supported\n");
+               return;
+       }
+
+       ar->hif.ops->write32(ar, address, data);
+}
+
 #endif /* _HIF_H_ */
index 676bd4e..f1946a6 100644 (file)
@@ -160,6 +160,7 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 
        ath10k_htc_prepare_tx_skb(ep, skb);
 
+       skb_cb->eid = eid;
        skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
        ret = dma_mapping_error(dev, skb_cb->paddr);
        if (ret)
@@ -197,15 +198,18 @@ err_pull:
 }
 
 static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
-                                           struct sk_buff *skb,
-                                           unsigned int eid)
+                                           struct sk_buff *skb)
 {
        struct ath10k_htc *htc = &ar->htc;
-       struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+       struct ath10k_skb_cb *skb_cb;
+       struct ath10k_htc_ep *ep;
 
        if (WARN_ON_ONCE(!skb))
                return 0;
 
+       skb_cb = ATH10K_SKB_CB(skb);
+       ep = &htc->endpoint[skb_cb->eid];
+
        ath10k_htc_notify_tx_completion(ep, skb);
        /* the skb now belongs to the completion handler */
 
@@ -317,8 +321,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc,
 }
 
 static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
-                                           struct sk_buff *skb,
-                                           u8 pipe_id)
+                                           struct sk_buff *skb)
 {
        int status = 0;
        struct ath10k_htc *htc = &ar->htc;
index 15c58e8..1bd5545 100644 (file)
@@ -126,6 +126,7 @@ enum htt_data_tx_ext_tid {
  *                  (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
+ *       freq: 0 means home channel of given vdev. intended for offchannel
  */
 struct htt_data_tx_desc {
        u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */
@@ -133,7 +134,8 @@ struct htt_data_tx_desc {
        __le16 len;
        __le16 id;
        __le32 frags_paddr;
-       __le32 peerid;
+       __le16 peerid;
+       __le16 freq;
        u8 prefetch[0]; /* start of frame, for FW classification engine */
 } __packed;
 
@@ -156,6 +158,9 @@ enum htt_rx_ring_flags {
        HTT_RX_RING_FLAGS_PHY_DATA_RX  = 1 << 15
 };
 
+#define HTT_RX_RING_SIZE_MIN 128
+#define HTT_RX_RING_SIZE_MAX 2048
+
 struct htt_rx_ring_setup_ring {
        __le32 fw_idx_shadow_reg_paddr;
        __le32 rx_ring_base_paddr;
index 52c6306..9c782a4 100644 (file)
 
 #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
+#define HTT_RX_RING_SIZE 1024
+#define HTT_RX_RING_FILL_LEVEL 1000
 
 /* 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_get_csum_state(struct sk_buff *skb);
 static void ath10k_htt_txrx_compl_task(unsigned long ptr);
 
-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;
@@ -301,40 +228,29 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
        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;
-       }
-}
-
 /* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */
 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,
-                                  u32 *attention)
+                                  struct sk_buff_head *amsdu)
 {
        struct ath10k *ar = htt->ar;
        int msdu_len, msdu_chaining = 0;
-       struct sk_buff *msdu, *next;
+       struct sk_buff *msdu;
        struct htt_rx_desc *rx_desc;
 
        lockdep_assert_held(&htt->rx_ring.lock);
 
-       if (htt->rx_confused) {
-               ath10k_warn(ar, "htt is confused. refusing rx\n");
-               return -1;
-       }
-
-       msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
-       while (msdu) {
+       for (;;) {
                int last_msdu, msdu_len_invalid, msdu_chained;
 
+               msdu = ath10k_htt_rx_netbuf_pop(htt);
+               if (!msdu) {
+                       __skb_queue_purge(amsdu);
+                       return -ENOENT;
+               }
+
+               __skb_queue_tail(amsdu, msdu);
+
                rx_desc = (struct htt_rx_desc *)msdu->data;
 
                /* FIXME: we must report msdu payload since this is what caller
@@ -352,19 +268,10 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                 */
                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(ar, "htt rx stopped. cannot recover\n");
-                       htt->rx_confused = true;
-                       break;
+                       __skb_queue_purge(amsdu);
+                       return -EIO;
                }
 
-               *attention |= __le32_to_cpu(rx_desc->attention.flags) &
-                                           (RX_ATTENTION_FLAGS_TKIP_MIC_ERR |
-                                            RX_ATTENTION_FLAGS_DECRYPT_ERR |
-                                            RX_ATTENTION_FLAGS_FCS_ERR |
-                                            RX_ATTENTION_FLAGS_MGMT_TYPE);
                /*
                 * Copy the FW rx descriptor for this MSDU from the rx
                 * indication message into the MSDU's netbuf. HL uses the
@@ -421,25 +328,18 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE));
                msdu_len -= msdu->len;
 
-               /* FIXME: Do chained buffers include htt_rx_desc or not? */
+               /* Note: Chained buffers do not contain rx descriptor */
                while (msdu_chained--) {
-                       struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
-
-                       if (!next) {
-                               ath10k_warn(ar, "failed to pop chained msdu\n");
-                               ath10k_htt_rx_free_msdu_chain(*head_msdu);
-                               *head_msdu = NULL;
-                               msdu = NULL;
-                               htt->rx_confused = true;
-                               break;
+                       msdu = ath10k_htt_rx_netbuf_pop(htt);
+                       if (!msdu) {
+                               __skb_queue_purge(amsdu);
+                               return -ENOENT;
                        }
 
-                       skb_trim(next, 0);
-                       skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE));
-                       msdu_len -= next->len;
-
-                       msdu->next = next;
-                       msdu = next;
+                       __skb_queue_tail(amsdu, msdu);
+                       skb_trim(msdu, 0);
+                       skb_put(msdu, min(msdu_len, HTT_RX_BUF_SIZE));
+                       msdu_len -= msdu->len;
                        msdu_chaining = 1;
                }
 
@@ -448,18 +348,12 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
 
                trace_ath10k_htt_rx_desc(ar, &rx_desc->attention,
                                         sizeof(*rx_desc) - sizeof(u32));
-               if (last_msdu) {
-                       msdu->next = NULL;
-                       break;
-               }
 
-               next = ath10k_htt_rx_netbuf_pop(htt);
-               msdu->next = next;
-               msdu = next;
+               if (last_msdu)
+                       break;
        }
-       *tail_msdu = msdu;
 
-       if (*head_msdu == NULL)
+       if (skb_queue_empty(amsdu))
                msdu_chaining = -1;
 
        /*
@@ -495,25 +389,18 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
 
        htt->rx_confused = false;
 
-       htt->rx_ring.size = ath10k_htt_rx_ring_size(htt);
+       /* XXX: The fill level could be changed during runtime in response to
+        * the host processing latency. Is this really worth it?
+        */
+       htt->rx_ring.size = HTT_RX_RING_SIZE;
+       htt->rx_ring.size_mask = htt->rx_ring.size - 1;
+       htt->rx_ring.fill_level = HTT_RX_RING_FILL_LEVEL;
+
        if (!is_power_of_2(htt->rx_ring.size)) {
                ath10k_warn(ar, "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 =
                kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
                        GFP_KERNEL);
@@ -628,35 +515,6 @@ static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar,
        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;
-
-       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)
-{
-       u8 *qc;
-
-       if (ieee80211_is_data_qos(hdr->frame_control)) {
-               qc = ieee80211_get_qos_ctl(hdr);
-               if (qc[0] & 0x80)
-                       return true;
-       }
-       return false;
-}
-
 struct rfc1042_hdr {
        u8 llc_dsap;
        u8 llc_ssap;
@@ -691,23 +549,34 @@ static const u8 rx_legacy_rate_idx[] = {
 };
 
 static void ath10k_htt_rx_h_rates(struct ath10k *ar,
-                                 enum ieee80211_band band,
-                                 u8 info0, u32 info1, u32 info2,
-                                 struct ieee80211_rx_status *status)
+                                 struct ieee80211_rx_status *status,
+                                 struct htt_rx_desc *rxd)
 {
+       enum ieee80211_band band;
        u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
        u8 preamble = 0;
+       u32 info1, info2, info3;
 
-       /* Check if valid fields */
-       if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
+       /* Band value can't be set as undefined but freq can be 0 - use that to
+        * determine whether band is provided.
+        *
+        * FIXME: Perhaps this can go away if CCK rate reporting is a little
+        * reworked?
+        */
+       if (!status->freq)
                return;
 
-       preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
+       band = status->band;
+       info1 = __le32_to_cpu(rxd->ppdu_start.info1);
+       info2 = __le32_to_cpu(rxd->ppdu_start.info2);
+       info3 = __le32_to_cpu(rxd->ppdu_start.info3);
+
+       preamble = MS(info1, RX_PPDU_START_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);
+               cck = info1 & RX_PPDU_START_INFO1_L_SIG_RATE_SELECT;
+               rate = MS(info1, RX_PPDU_START_INFO1_L_SIG_RATE);
                rate_idx = 0;
 
                if (rate < 0x08 || rate > 0x0F)
@@ -734,11 +603,11 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
                break;
        case HTT_RX_HT:
        case HTT_RX_HT_WITH_TXBF:
-               /* HT-SIG - Table 20-11 in info1 and info2 */
-               mcs = info1 & 0x1F;
+               /* HT-SIG - Table 20-11 in info2 and info3 */
+               mcs = info2 & 0x1F;
                nss = mcs >> 3;
-               bw = (info1 >> 7) & 1;
-               sgi = (info2 >> 7) & 1;
+               bw = (info2 >> 7) & 1;
+               sgi = (info3 >> 7) & 1;
 
                status->rate_idx = mcs;
                status->flag |= RX_FLAG_HT;
@@ -749,12 +618,12 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
                break;
        case HTT_RX_VHT:
        case HTT_RX_VHT_WITH_TXBF:
-               /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
+               /* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3
                   TODO check this */
-               mcs = (info2 >> 4) & 0x0F;
-               nss = ((info1 >> 10) & 0x07) + 1;
-               bw = info1 & 3;
-               sgi = info2 & 1;
+               mcs = (info3 >> 4) & 0x0F;
+               nss = ((info2 >> 10) & 0x07) + 1;
+               bw = info2 & 3;
+               sgi = info3 & 1;
 
                status->rate_idx = mcs;
                status->vht_nss = nss;
@@ -782,41 +651,6 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
        }
 }
 
-static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt,
-                                     struct ieee80211_rx_status *rx_status,
-                                     struct sk_buff *skb,
-                                     enum htt_rx_mpdu_encrypt_type enctype,
-                                     enum rx_msdu_decap_format fmt,
-                                     bool dot11frag)
-{
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
-       rx_status->flag &= ~(RX_FLAG_DECRYPTED |
-                            RX_FLAG_IV_STRIPPED |
-                            RX_FLAG_MMIC_STRIPPED);
-
-       if (enctype == HTT_RX_MPDU_ENCRYPT_NONE)
-               return;
-
-       /*
-        * There's no explicit rx descriptor flag to indicate whether a given
-        * frame has been decrypted or not. We're forced to use the decap
-        * format as an implicit indication. However fragmentation rx is always
-        * raw and it probably never reports undecrypted raws.
-        *
-        * This makes sure sniffed frames are reported as-is without stripping
-        * the protected flag.
-        */
-       if (fmt == RX_MSDU_DECAP_RAW && !dot11frag)
-               return;
-
-       rx_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);
-}
-
 static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
                                    struct ieee80211_rx_status *status)
 {
@@ -837,6 +671,72 @@ static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
        return true;
 }
 
+static void ath10k_htt_rx_h_signal(struct ath10k *ar,
+                                  struct ieee80211_rx_status *status,
+                                  struct htt_rx_desc *rxd)
+{
+       /* FIXME: Get real NF */
+       status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
+                        rxd->ppdu_start.rssi_comb;
+       status->flag &= ~RX_FLAG_NO_SIGNAL_VAL;
+}
+
+static void ath10k_htt_rx_h_mactime(struct ath10k *ar,
+                                   struct ieee80211_rx_status *status,
+                                   struct htt_rx_desc *rxd)
+{
+       /* FIXME: TSF is known only at the end of PPDU, in the last MPDU. This
+        * means all prior MSDUs in a PPDU are reported to mac80211 without the
+        * TSF. Is it worth holding frames until end of PPDU is known?
+        *
+        * FIXME: Can we get/compute 64bit TSF?
+        */
+       status->mactime = __le32_to_cpu(rxd->ppdu_end.tsf_timestamp);
+       status->flag |= RX_FLAG_MACTIME_END;
+}
+
+static void ath10k_htt_rx_h_ppdu(struct ath10k *ar,
+                                struct sk_buff_head *amsdu,
+                                struct ieee80211_rx_status *status)
+{
+       struct sk_buff *first;
+       struct htt_rx_desc *rxd;
+       bool is_first_ppdu;
+       bool is_last_ppdu;
+
+       if (skb_queue_empty(amsdu))
+               return;
+
+       first = skb_peek(amsdu);
+       rxd = (void *)first->data - sizeof(*rxd);
+
+       is_first_ppdu = !!(rxd->attention.flags &
+                          __cpu_to_le32(RX_ATTENTION_FLAGS_FIRST_MPDU));
+       is_last_ppdu = !!(rxd->attention.flags &
+                         __cpu_to_le32(RX_ATTENTION_FLAGS_LAST_MPDU));
+
+       if (is_first_ppdu) {
+               /* New PPDU starts so clear out the old per-PPDU status. */
+               status->freq = 0;
+               status->rate_idx = 0;
+               status->vht_nss = 0;
+               status->vht_flag &= ~RX_VHT_FLAG_80MHZ;
+               status->flag &= ~(RX_FLAG_HT |
+                                 RX_FLAG_VHT |
+                                 RX_FLAG_SHORT_GI |
+                                 RX_FLAG_40MHZ |
+                                 RX_FLAG_MACTIME_END);
+               status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+
+               ath10k_htt_rx_h_signal(ar, status, rxd);
+               ath10k_htt_rx_h_channel(ar, status);
+               ath10k_htt_rx_h_rates(ar, status, rxd);
+       }
+
+       if (is_last_ppdu)
+               ath10k_htt_rx_h_mactime(ar, status, rxd);
+}
+
 static const char * const tid_to_ac[] = {
        "BE",
        "BK",
@@ -913,187 +813,263 @@ static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr)
        return round_up(ieee80211_hdrlen(hdr->frame_control), 4);
 }
 
-static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
-                               struct ieee80211_rx_status *rx_status,
-                               struct sk_buff *skb_in)
+static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
+                                       struct sk_buff *msdu,
+                                       struct ieee80211_rx_status *status,
+                                       enum htt_rx_mpdu_encrypt_type enctype,
+                                       bool is_decrypted)
 {
-       struct ath10k *ar = htt->ar;
+       struct ieee80211_hdr *hdr;
        struct htt_rx_desc *rxd;
-       struct sk_buff *skb = skb_in;
-       struct sk_buff *first;
-       enum rx_msdu_decap_format fmt;
-       enum htt_rx_mpdu_encrypt_type enctype;
+       size_t hdr_len;
+       size_t crypto_len;
+       bool is_first;
+       bool is_last;
+
+       rxd = (void *)msdu->data - sizeof(*rxd);
+       is_first = !!(rxd->msdu_end.info0 &
+                     __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
+       is_last = !!(rxd->msdu_end.info0 &
+                    __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
+
+       /* Delivered decapped frame:
+        * [802.11 header]
+        * [crypto param] <-- can be trimmed if !fcs_err &&
+        *                    !decrypt_err && !peer_idx_invalid
+        * [amsdu header] <-- only if A-MSDU
+        * [rfc1042/llc]
+        * [payload]
+        * [FCS] <-- at end, needs to be trimmed
+        */
+
+       /* This probably shouldn't happen but warn just in case */
+       if (unlikely(WARN_ON_ONCE(!is_first)))
+               return;
+
+       /* This probably shouldn't happen but warn just in case */
+       if (unlikely(WARN_ON_ONCE(!(is_first && is_last))))
+               return;
+
+       skb_trim(msdu, msdu->len - FCS_LEN);
+
+       /* In most cases this will be true for sniffed frames. It makes sense
+        * to deliver them as-is without stripping the crypto param. This would
+        * also make sense for software based decryption (which is not
+        * implemented in ath10k).
+        *
+        * If there's no error then the frame is decrypted. At least that is
+        * the case for frames that come in via fragmented rx indication.
+        */
+       if (!is_decrypted)
+               return;
+
+       /* The payload is decrypted so strip crypto params. Start from tail
+        * since hdr is used to compute some stuff.
+        */
+
+       hdr = (void *)msdu->data;
+
+       /* Tail */
+       skb_trim(msdu, msdu->len - ath10k_htt_rx_crypto_tail_len(ar, enctype));
+
+       /* MMIC */
+       if (!ieee80211_has_morefrags(hdr->frame_control) &&
+           enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+               skb_trim(msdu, msdu->len - 8);
+
+       /* Head */
+       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+       crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
+
+       memmove((void *)msdu->data + crypto_len,
+               (void *)msdu->data, hdr_len);
+       skb_pull(msdu, crypto_len);
+}
+
+static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
+                                         struct sk_buff *msdu,
+                                         struct ieee80211_rx_status *status,
+                                         const u8 first_hdr[64])
+{
        struct ieee80211_hdr *hdr;
-       u8 hdr_buf[64], da[ETH_ALEN], sa[ETH_ALEN], *qos;
-       unsigned int hdr_len;
+       size_t hdr_len;
+       u8 da[ETH_ALEN];
+       u8 sa[ETH_ALEN];
 
-       rxd = (void *)skb->data - sizeof(*rxd);
-       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
-                    RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+       /* Delivered decapped frame:
+        * [nwifi 802.11 header] <-- replaced with 802.11 hdr
+        * [rfc1042/llc]
+        *
+        * Note: The nwifi header doesn't have QoS Control and is
+        * (always?) a 3addr frame.
+        *
+        * Note2: There's no A-MSDU subframe header. Even if it's part
+        * of an A-MSDU.
+        */
 
-       hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+       /* pull decapped header and copy SA & DA */
+       hdr = (struct ieee80211_hdr *)msdu->data;
+       hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
+       ether_addr_copy(da, ieee80211_get_DA(hdr));
+       ether_addr_copy(sa, ieee80211_get_SA(hdr));
+       skb_pull(msdu, hdr_len);
+
+       /* push original 802.11 header */
+       hdr = (struct ieee80211_hdr *)first_hdr;
        hdr_len = ieee80211_hdrlen(hdr->frame_control);
-       memcpy(hdr_buf, hdr, hdr_len);
-       hdr = (struct ieee80211_hdr *)hdr_buf;
-
-       first = skb;
-       while (skb) {
-               void *decap_hdr;
-               int len;
-
-               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;
-
-               skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
-
-               /* First frame in an A-MSDU chain has more decapped data. */
-               if (skb == first) {
-                       len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
-                       len += round_up(ath10k_htt_rx_crypto_param_len(ar,
-                                               enctype), 4);
-                       decap_hdr += len;
-               }
+       memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
 
-               switch (fmt) {
-               case RX_MSDU_DECAP_RAW:
-                       /* remove trailing FCS */
-                       skb_trim(skb, skb->len - FCS_LEN);
-                       break;
-               case RX_MSDU_DECAP_NATIVE_WIFI:
-                       /* pull decapped header and copy SA & DA */
-                       hdr = (struct ieee80211_hdr *)skb->data;
-                       hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
-                       ether_addr_copy(da, ieee80211_get_DA(hdr));
-                       ether_addr_copy(sa, ieee80211_get_SA(hdr));
-                       skb_pull(skb, hdr_len);
-
-                       /* push original 802.11 header */
-                       hdr = (struct ieee80211_hdr *)hdr_buf;
-                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
-                       memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
-
-                       /* original A-MSDU header has the bit set but we're
-                        * not including A-MSDU subframe header */
-                       hdr = (struct ieee80211_hdr *)skb->data;
-                       qos = ieee80211_get_qos_ctl(hdr);
-                       qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
-
-                       /* original 802.11 header has a different DA and in
-                        * case of 4addr it may also have different SA
-                        */
-                       ether_addr_copy(ieee80211_get_DA(hdr), da);
-                       ether_addr_copy(ieee80211_get_SA(hdr), sa);
-                       break;
-               case RX_MSDU_DECAP_ETHERNET2_DIX:
-                       /* strip ethernet header and insert decapped 802.11
-                        * header, amsdu subframe header and rfc1042 header */
+       /* original 802.11 header has a different DA and in
+        * case of 4addr it may also have different SA
+        */
+       hdr = (struct ieee80211_hdr *)msdu->data;
+       ether_addr_copy(ieee80211_get_DA(hdr), da);
+       ether_addr_copy(ieee80211_get_SA(hdr), sa);
+}
 
-                       len = 0;
-                       len += sizeof(struct rfc1042_hdr);
-                       len += sizeof(struct amsdu_subframe_hdr);
+static void *ath10k_htt_rx_h_find_rfc1042(struct ath10k *ar,
+                                         struct sk_buff *msdu,
+                                         enum htt_rx_mpdu_encrypt_type enctype)
+{
+       struct ieee80211_hdr *hdr;
+       struct htt_rx_desc *rxd;
+       size_t hdr_len, crypto_len;
+       void *rfc1042;
+       bool is_first, is_last, is_amsdu;
 
-                       skb_pull(skb, sizeof(struct ethhdr));
-                       memcpy(skb_push(skb, len), decap_hdr, len);
-                       memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
-                       break;
-               case RX_MSDU_DECAP_8023_SNAP_LLC:
-                       /* insert decapped 802.11 header making a singly
-                        * A-MSDU */
-                       memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
-                       break;
-               }
+       rxd = (void *)msdu->data - sizeof(*rxd);
+       hdr = (void *)rxd->rx_hdr_status;
 
-               skb_in = skb;
-               ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype, fmt,
-                                         false);
-               skb = skb->next;
-               skb_in->next = NULL;
+       is_first = !!(rxd->msdu_end.info0 &
+                     __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
+       is_last = !!(rxd->msdu_end.info0 &
+                    __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
+       is_amsdu = !(is_first && is_last);
 
-               if (skb)
-                       rx_status->flag |= RX_FLAG_AMSDU_MORE;
-               else
-                       rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
+       rfc1042 = hdr;
+
+       if (is_first) {
+               hdr_len = ieee80211_hdrlen(hdr->frame_control);
+               crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
 
-               ath10k_process_rx(htt->ar, rx_status, skb_in);
+               rfc1042 += round_up(hdr_len, 4) +
+                          round_up(crypto_len, 4);
        }
 
-       /* FIXME: It might be nice to re-assemble the A-MSDU when there's a
-        * monitor interface active for sniffing purposes. */
+       if (is_amsdu)
+               rfc1042 += sizeof(struct amsdu_subframe_hdr);
+
+       return rfc1042;
 }
 
-static void ath10k_htt_rx_msdu(struct ath10k_htt *htt,
-                              struct ieee80211_rx_status *rx_status,
-                              struct sk_buff *skb)
+static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
+                                       struct sk_buff *msdu,
+                                       struct ieee80211_rx_status *status,
+                                       const u8 first_hdr[64],
+                                       enum htt_rx_mpdu_encrypt_type enctype)
 {
-       struct ath10k *ar = htt->ar;
-       struct htt_rx_desc *rxd;
        struct ieee80211_hdr *hdr;
-       enum rx_msdu_decap_format fmt;
-       enum htt_rx_mpdu_encrypt_type enctype;
-       int hdr_len;
+       struct ethhdr *eth;
+       size_t hdr_len;
        void *rfc1042;
+       u8 da[ETH_ALEN];
+       u8 sa[ETH_ALEN];
 
-       /* This shouldn't happen. If it does than it may be a FW bug. */
-       if (skb->next) {
-               ath10k_warn(ar, "htt rx received chained non A-MSDU frame\n");
-               ath10k_htt_rx_free_msdu_chain(skb->next);
-               skb->next = NULL;
-       }
+       /* Delivered decapped frame:
+        * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
+        * [payload]
+        */
 
-       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 = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+       rfc1042 = ath10k_htt_rx_h_find_rfc1042(ar, msdu, enctype);
+       if (WARN_ON_ONCE(!rfc1042))
+               return;
+
+       /* pull decapped header and copy SA & DA */
+       eth = (struct ethhdr *)msdu->data;
+       ether_addr_copy(da, eth->h_dest);
+       ether_addr_copy(sa, eth->h_source);
+       skb_pull(msdu, sizeof(struct ethhdr));
+
+       /* push rfc1042/llc/snap */
+       memcpy(skb_push(msdu, sizeof(struct rfc1042_hdr)), rfc1042,
+              sizeof(struct rfc1042_hdr));
+
+       /* push original 802.11 header */
+       hdr = (struct ieee80211_hdr *)first_hdr;
        hdr_len = ieee80211_hdrlen(hdr->frame_control);
+       memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
+
+       /* original 802.11 header has a different DA and in
+        * case of 4addr it may also have different SA
+        */
+       hdr = (struct ieee80211_hdr *)msdu->data;
+       ether_addr_copy(ieee80211_get_DA(hdr), da);
+       ether_addr_copy(ieee80211_get_SA(hdr), sa);
+}
+
+static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
+                                        struct sk_buff *msdu,
+                                        struct ieee80211_rx_status *status,
+                                        const u8 first_hdr[64])
+{
+       struct ieee80211_hdr *hdr;
+       size_t hdr_len;
 
-       skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
+       /* Delivered decapped frame:
+        * [amsdu header] <-- replaced with 802.11 hdr
+        * [rfc1042/llc]
+        * [payload]
+        */
+
+       skb_pull(msdu, sizeof(struct amsdu_subframe_hdr));
+
+       hdr = (struct ieee80211_hdr *)first_hdr;
+       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+       memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
+}
 
-       switch (fmt) {
+static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
+                                   struct sk_buff *msdu,
+                                   struct ieee80211_rx_status *status,
+                                   u8 first_hdr[64],
+                                   enum htt_rx_mpdu_encrypt_type enctype,
+                                   bool is_decrypted)
+{
+       struct htt_rx_desc *rxd;
+       enum rx_msdu_decap_format decap;
+       struct ieee80211_hdr *hdr;
+
+       /* First msdu's decapped header:
+        * [802.11 header] <-- padded to 4 bytes long
+        * [crypto param] <-- padded to 4 bytes long
+        * [amsdu header] <-- only if A-MSDU
+        * [rfc1042/llc]
+        *
+        * Other (2nd, 3rd, ..) msdu's decapped header:
+        * [amsdu header] <-- only if A-MSDU
+        * [rfc1042/llc]
+        */
+
+       rxd = (void *)msdu->data - sizeof(*rxd);
+       hdr = (void *)rxd->rx_hdr_status;
+       decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                  RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+       switch (decap) {
        case RX_MSDU_DECAP_RAW:
-               /* remove trailing FCS */
-               skb_trim(skb, skb->len - FCS_LEN);
+               ath10k_htt_rx_h_undecap_raw(ar, msdu, status, enctype,
+                                           is_decrypted);
                break;
        case RX_MSDU_DECAP_NATIVE_WIFI:
-               /* Pull decapped header */
-               hdr = (struct ieee80211_hdr *)skb->data;
-               hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
-               skb_pull(skb, hdr_len);
-
-               /* Push original header */
-               hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
-               hdr_len = ieee80211_hdrlen(hdr->frame_control);
-               memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+               ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
                break;
        case RX_MSDU_DECAP_ETHERNET2_DIX:
-               /* strip ethernet header and insert decapped 802.11 header and
-                * rfc1042 header */
-
-               rfc1042 = hdr;
-               rfc1042 += roundup(hdr_len, 4);
-               rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(ar,
-                                       enctype), 4);
-
-               skb_pull(skb, sizeof(struct ethhdr));
-               memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
-                      rfc1042, sizeof(struct rfc1042_hdr));
-               memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+               ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
                break;
        case RX_MSDU_DECAP_8023_SNAP_LLC:
-               /* remove A-MSDU subframe header and insert
-                * decapped 802.11 header. rfc1042 header is already there */
-
-               skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
-               memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+               ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
                break;
        }
-
-       ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false);
-
-       ath10k_process_rx(htt->ar, rx_status, skb);
 }
 
 static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
@@ -1127,10 +1103,128 @@ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
        return CHECKSUM_UNNECESSARY;
 }
 
-static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
+static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
 {
-       struct sk_buff *next = msdu_head->next;
-       struct sk_buff *to_free = next;
+       msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
+}
+
+static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
+                                struct sk_buff_head *amsdu,
+                                struct ieee80211_rx_status *status)
+{
+       struct sk_buff *first;
+       struct sk_buff *last;
+       struct sk_buff *msdu;
+       struct htt_rx_desc *rxd;
+       struct ieee80211_hdr *hdr;
+       enum htt_rx_mpdu_encrypt_type enctype;
+       u8 first_hdr[64];
+       u8 *qos;
+       size_t hdr_len;
+       bool has_fcs_err;
+       bool has_crypto_err;
+       bool has_tkip_err;
+       bool has_peer_idx_invalid;
+       bool is_decrypted;
+       u32 attention;
+
+       if (skb_queue_empty(amsdu))
+               return;
+
+       first = skb_peek(amsdu);
+       rxd = (void *)first->data - sizeof(*rxd);
+
+       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                    RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+       /* First MSDU's Rx descriptor in an A-MSDU contains full 802.11
+        * decapped header. It'll be used for undecapping of each MSDU.
+        */
+       hdr = (void *)rxd->rx_hdr_status;
+       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+       memcpy(first_hdr, hdr, hdr_len);
+
+       /* Each A-MSDU subframe will use the original header as the base and be
+        * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
+        */
+       hdr = (void *)first_hdr;
+       qos = ieee80211_get_qos_ctl(hdr);
+       qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+       /* Some attention flags are valid only in the last MSDU. */
+       last = skb_peek_tail(amsdu);
+       rxd = (void *)last->data - sizeof(*rxd);
+       attention = __le32_to_cpu(rxd->attention.flags);
+
+       has_fcs_err = !!(attention & RX_ATTENTION_FLAGS_FCS_ERR);
+       has_crypto_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
+       has_tkip_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
+       has_peer_idx_invalid = !!(attention & RX_ATTENTION_FLAGS_PEER_IDX_INVALID);
+
+       /* Note: If hardware captures an encrypted frame that it can't decrypt,
+        * e.g. due to fcs error, missing peer or invalid key data it will
+        * report the frame as raw.
+        */
+       is_decrypted = (enctype != HTT_RX_MPDU_ENCRYPT_NONE &&
+                       !has_fcs_err &&
+                       !has_crypto_err &&
+                       !has_peer_idx_invalid);
+
+       /* Clear per-MPDU flags while leaving per-PPDU flags intact. */
+       status->flag &= ~(RX_FLAG_FAILED_FCS_CRC |
+                         RX_FLAG_MMIC_ERROR |
+                         RX_FLAG_DECRYPTED |
+                         RX_FLAG_IV_STRIPPED |
+                         RX_FLAG_MMIC_STRIPPED);
+
+       if (has_fcs_err)
+               status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+       if (has_tkip_err)
+               status->flag |= RX_FLAG_MMIC_ERROR;
+
+       if (is_decrypted)
+               status->flag |= RX_FLAG_DECRYPTED |
+                               RX_FLAG_IV_STRIPPED |
+                               RX_FLAG_MMIC_STRIPPED;
+
+       skb_queue_walk(amsdu, msdu) {
+               ath10k_htt_rx_h_csum_offload(msdu);
+               ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
+                                       is_decrypted);
+
+               /* Undecapping involves copying the original 802.11 header back
+                * to sk_buff. If frame is protected and hardware has decrypted
+                * it then remove the protected bit.
+                */
+               if (!is_decrypted)
+                       continue;
+
+               hdr = (void *)msdu->data;
+               hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+       }
+}
+
+static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
+                                   struct sk_buff_head *amsdu,
+                                   struct ieee80211_rx_status *status)
+{
+       struct sk_buff *msdu;
+
+       while ((msdu = __skb_dequeue(amsdu))) {
+               /* Setup per-MSDU flags */
+               if (skb_queue_empty(amsdu))
+                       status->flag &= ~RX_FLAG_AMSDU_MORE;
+               else
+                       status->flag |= RX_FLAG_AMSDU_MORE;
+
+               ath10k_process_rx(ar, status, msdu);
+       }
+}
+
+static int ath10k_unchain_msdu(struct sk_buff_head *amsdu)
+{
+       struct sk_buff *skb, *first;
        int space;
        int total_len = 0;
 
@@ -1141,99 +1235,142 @@ static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
         * skb?
         */
 
-       msdu_head->next = NULL;
+       first = __skb_dequeue(amsdu);
 
        /* Allocate total length all at once. */
-       while (next) {
-               total_len += next->len;
-               next = next->next;
-       }
+       skb_queue_walk(amsdu, skb)
+               total_len += skb->len;
 
-       space = total_len - skb_tailroom(msdu_head);
+       space = total_len - skb_tailroom(first);
        if ((space > 0) &&
-           (pskb_expand_head(msdu_head, 0, space, GFP_ATOMIC) < 0)) {
+           (pskb_expand_head(first, 0, space, GFP_ATOMIC) < 0)) {
                /* TODO:  bump some rx-oom error stat */
                /* put it back together so we can free the
                 * whole list at once.
                 */
-               msdu_head->next = to_free;
+               __skb_queue_head(amsdu, first);
                return -1;
        }
 
        /* Walk list again, copying contents into
         * msdu_head
         */
-       next = to_free;
-       while (next) {
-               skb_copy_from_linear_data(next, skb_put(msdu_head, next->len),
-                                         next->len);
-               next = next->next;
+       while ((skb = __skb_dequeue(amsdu))) {
+               skb_copy_from_linear_data(skb, skb_put(first, skb->len),
+                                         skb->len);
+               dev_kfree_skb_any(skb);
        }
 
-       /* If here, we have consolidated skb.  Free the
-        * fragments and pass the main skb on up the
-        * stack.
-        */
-       ath10k_htt_rx_free_msdu_chain(to_free);
+       __skb_queue_head(amsdu, first);
        return 0;
 }
 
-static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt,
-                                       struct sk_buff *head,
-                                       bool channel_set,
-                                       u32 attention)
+static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
+                                   struct sk_buff_head *amsdu,
+                                   bool chained)
 {
-       struct ath10k *ar = htt->ar;
+       struct sk_buff *first;
+       struct htt_rx_desc *rxd;
+       enum rx_msdu_decap_format decap;
 
-       if (head->len == 0) {
-               ath10k_dbg(ar, ATH10K_DBG_HTT,
-                          "htt rx dropping due to zero-len\n");
-               return false;
-       }
+       first = skb_peek(amsdu);
+       rxd = (void *)first->data - sizeof(*rxd);
+       decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                  RX_MSDU_START_INFO1_DECAP_FORMAT);
 
-       if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) {
-               ath10k_dbg(ar, ATH10K_DBG_HTT,
-                          "htt rx dropping due to decrypt-err\n");
-               return false;
+       if (!chained)
+               return;
+
+       /* FIXME: Current unchaining logic can only handle simple case of raw
+        * msdu chaining. If decapping is other than raw the chaining may be
+        * more complex and this isn't handled by the current code. Don't even
+        * try re-constructing such frames - it'll be pretty much garbage.
+        */
+       if (decap != RX_MSDU_DECAP_RAW ||
+           skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) {
+               __skb_queue_purge(amsdu);
+               return;
        }
 
-       if (!channel_set) {
-               ath10k_warn(ar, "no channel configured; ignoring frame!\n");
+       ath10k_unchain_msdu(amsdu);
+}
+
+static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
+                                       struct sk_buff_head *amsdu,
+                                       struct ieee80211_rx_status *rx_status)
+{
+       struct sk_buff *msdu;
+       struct htt_rx_desc *rxd;
+       bool is_mgmt;
+       bool has_fcs_err;
+
+       msdu = skb_peek(amsdu);
+       rxd = (void *)msdu->data - sizeof(*rxd);
+
+       /* FIXME: It might be a good idea to do some fuzzy-testing to drop
+        * invalid/dangerous frames.
+        */
+
+       if (!rx_status->freq) {
+               ath10k_warn(ar, "no channel configured; ignoring frame(s)!\n");
                return false;
        }
 
-       /* Skip mgmt frames while we handle this in WMI */
-       if (attention & RX_ATTENTION_FLAGS_MGMT_TYPE) {
+       is_mgmt = !!(rxd->attention.flags &
+                    __cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE));
+       has_fcs_err = !!(rxd->attention.flags &
+                        __cpu_to_le32(RX_ATTENTION_FLAGS_FCS_ERR));
+
+       /* Management frames are handled via WMI events. The pros of such
+        * approach is that channel is explicitly provided in WMI events
+        * whereas HTT doesn't provide channel information for Rxed frames.
+        *
+        * However some firmware revisions don't report corrupted frames via
+        * WMI so don't drop them.
+        */
+       if (is_mgmt && !has_fcs_err) {
                ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
                return false;
        }
 
-       if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
-               ath10k_dbg(ar, ATH10K_DBG_HTT,
-                          "htt rx CAC running\n");
+       if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
+               ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx cac running\n");
                return false;
        }
 
        return true;
 }
 
+static void ath10k_htt_rx_h_filter(struct ath10k *ar,
+                                  struct sk_buff_head *amsdu,
+                                  struct ieee80211_rx_status *rx_status)
+{
+       if (skb_queue_empty(amsdu))
+               return;
+
+       if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status))
+               return;
+
+       __skb_queue_purge(amsdu);
+}
+
 static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                                  struct htt_rx_indication *rx)
 {
        struct ath10k *ar = htt->ar;
        struct ieee80211_rx_status *rx_status = &htt->rx_status;
        struct htt_rx_indication_mpdu_range *mpdu_ranges;
-       struct ieee80211_hdr *hdr;
+       struct sk_buff_head amsdu;
        int num_mpdu_ranges;
-       u32 attention;
        int fw_desc_len;
        u8 *fw_desc;
-       bool channel_set;
-       int i, j;
-       int ret;
+       int i, ret, mpdu_count = 0;
 
        lockdep_assert_held(&htt->rx_ring.lock);
 
+       if (htt->rx_confused)
+               return;
+
        fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
        fw_desc = (u8 *)&rx->fw_desc;
 
@@ -1241,85 +1378,33 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                             HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
        mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
 
-       /* Fill this once, while this is per-ppdu */
-       if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_START_VALID) {
-               memset(rx_status, 0, sizeof(*rx_status));
-               rx_status->signal  = ATH10K_DEFAULT_NOISE_FLOOR +
-                                    rx->ppdu.combined_rssi;
-       }
-
-       if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
-               /* TSF available only in 32-bit */
-               rx_status->mactime = __le32_to_cpu(rx->ppdu.tsf) & 0xffffffff;
-               rx_status->flag |= RX_FLAG_MACTIME_END;
-       }
-
-       channel_set = ath10k_htt_rx_h_channel(htt->ar, rx_status);
-
-       if (channel_set) {
-               ath10k_htt_rx_h_rates(htt->ar, rx_status->band,
-                                     rx->ppdu.info0,
-                                     __le32_to_cpu(rx->ppdu.info1),
-                                     __le32_to_cpu(rx->ppdu.info2),
-                                     rx_status);
-       }
-
        ath10k_dbg_dump(ar, 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++) {
-               for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
-                       struct sk_buff *msdu_head, *msdu_tail;
-
-                       attention = 0;
-                       msdu_head = NULL;
-                       msdu_tail = NULL;
-                       ret = ath10k_htt_rx_amsdu_pop(htt,
-                                                     &fw_desc,
-                                                     &fw_desc_len,
-                                                     &msdu_head,
-                                                     &msdu_tail,
-                                                     &attention);
-
-                       if (ret < 0) {
-                               ath10k_warn(ar, "failed to pop amsdu from htt rx ring %d\n",
-                                           ret);
-                               ath10k_htt_rx_free_msdu_chain(msdu_head);
-                               continue;
-                       }
-
-                       if (!ath10k_htt_rx_amsdu_allowed(htt, msdu_head,
-                                                        channel_set,
-                                                        attention)) {
-                               ath10k_htt_rx_free_msdu_chain(msdu_head);
-                               continue;
-                       }
-
-                       if (ret > 0 &&
-                           ath10k_unchain_msdu(msdu_head) < 0) {
-                               ath10k_htt_rx_free_msdu_chain(msdu_head);
-                               continue;
-                       }
-
-                       if (attention & RX_ATTENTION_FLAGS_FCS_ERR)
-                               rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
-                       else
-                               rx_status->flag &= ~RX_FLAG_FAILED_FCS_CRC;
-
-                       if (attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
-                               rx_status->flag |= RX_FLAG_MMIC_ERROR;
-                       else
-                               rx_status->flag &= ~RX_FLAG_MMIC_ERROR;
-
-                       hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
-
-                       if (ath10k_htt_rx_hdr_is_amsdu(hdr))
-                               ath10k_htt_rx_amsdu(htt, rx_status, msdu_head);
-                       else
-                               ath10k_htt_rx_msdu(htt, rx_status, msdu_head);
+       for (i = 0; i < num_mpdu_ranges; i++)
+               mpdu_count += mpdu_ranges[i].mpdu_count;
+
+       while (mpdu_count--) {
+               __skb_queue_head_init(&amsdu);
+               ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc,
+                                             &fw_desc_len, &amsdu);
+               if (ret < 0) {
+                       ath10k_warn(ar, "rx ring became corrupted: %d\n", ret);
+                       __skb_queue_purge(&amsdu);
+                       /* FIXME: It's probably a good idea to reboot the
+                        * device instead of leaving it inoperable.
+                        */
+                       htt->rx_confused = true;
+                       break;
                }
+
+               ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
+               ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
+               ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
+               ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+               ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
        }
 
        tasklet_schedule(&htt->rx_replenish_task);
@@ -1329,30 +1414,20 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
                                       struct htt_rx_fragment_indication *frag)
 {
        struct ath10k *ar = htt->ar;
-       struct sk_buff *msdu_head, *msdu_tail;
-       enum htt_rx_mpdu_encrypt_type enctype;
-       struct htt_rx_desc *rxd;
-       enum rx_msdu_decap_format fmt;
        struct ieee80211_rx_status *rx_status = &htt->rx_status;
-       struct ieee80211_hdr *hdr;
+       struct sk_buff_head amsdu;
        int ret;
-       bool tkip_mic_err;
-       bool decrypt_err;
        u8 *fw_desc;
-       int fw_desc_len, hdrlen, paramlen;
-       int trim;
-       u32 attention = 0;
+       int fw_desc_len;
 
        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;
+       __skb_queue_head_init(&amsdu);
 
        spin_lock_bh(&htt->rx_ring.lock);
        ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
-                                     &msdu_head, &msdu_tail,
-                                     &attention);
+                                     &amsdu);
        spin_unlock_bh(&htt->rx_ring.lock);
 
        tasklet_schedule(&htt->rx_replenish_task);
@@ -1362,77 +1437,21 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
        if (ret) {
                ath10k_warn(ar, "failed to pop amsdu from httr rx ring for fragmented rx %d\n",
                            ret);
-               ath10k_htt_rx_free_msdu_chain(msdu_head);
+               __skb_queue_purge(&amsdu);
                return;
        }
 
-       /* FIXME: implement signal strength */
-       rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
-
-       hdr = (struct ieee80211_hdr *)msdu_head->data;
-       rxd = (void *)msdu_head->data - sizeof(*rxd);
-       tkip_mic_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
-       decrypt_err = !!(attention & 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(ar, "we dont support non-raw fragmented rx yet\n");
-               dev_kfree_skb_any(msdu_head);
-               goto end;
-       }
-
-       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
-                    RX_MPDU_START_INFO0_ENCRYPT_TYPE);
-       ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype, fmt,
-                                 true);
-       msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
-
-       if (tkip_mic_err)
-               ath10k_warn(ar, "tkip mic error\n");
-
-       if (decrypt_err) {
-               ath10k_warn(ar, "decryption err in fragmented rx\n");
-               dev_kfree_skb_any(msdu_head);
-               goto end;
-       }
-
-       if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) {
-               hdrlen = ieee80211_hdrlen(hdr->frame_control);
-               paramlen = ath10k_htt_rx_crypto_param_len(ar, enctype);
-
-               /* It is more efficient to move the header than the payload */
-               memmove((void *)msdu_head->data + paramlen,
-                       (void *)msdu_head->data,
-                       hdrlen);
-               skb_pull(msdu_head, paramlen);
-               hdr = (struct ieee80211_hdr *)msdu_head->data;
-       }
-
-       /* remove trailing FCS */
-       trim  = 4;
-
-       /* remove crypto trailer */
-       trim += ath10k_htt_rx_crypto_tail_len(ar, enctype);
-
-       /* last fragment of TKIP frags has MIC */
-       if (!ieee80211_has_morefrags(hdr->frame_control) &&
-           enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
-               trim += MICHAEL_MIC_LEN;
-
-       if (trim > msdu_head->len) {
-               ath10k_warn(ar, "htt rx fragment: trailer longer than the frame itself? drop\n");
-               dev_kfree_skb_any(msdu_head);
-               goto end;
+       if (skb_queue_len(&amsdu) != 1) {
+               ath10k_warn(ar, "failed to pop frag amsdu: too many msdus\n");
+               __skb_queue_purge(&amsdu);
+               return;
        }
 
-       skb_trim(msdu_head, msdu_head->len - trim);
-
-       ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
-                       msdu_head->data, msdu_head->len);
-       ath10k_process_rx(htt->ar, rx_status, msdu_head);
+       ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
+       ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
+       ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+       ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
 
-end:
        if (fw_desc_len > 0) {
                ath10k_dbg(ar, ATH10K_DBG_HTT,
                           "expecting more fragmented rx in one indication %d\n",
index 5b7e42f..4bc51d8 100644 (file)
@@ -554,13 +554,14 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
        skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
        skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
-       skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID);
+       skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID);
+       skb_cb->htt.txbuf->cmd_tx.freq = __cpu_to_le16(skb_cb->htt.freq);
 
        trace_ath10k_htt_tx(ar, msdu_id, msdu->len, vdev_id, tid);
        ath10k_dbg(ar, ATH10K_DBG_HTT,
-                  "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu\n",
+                  "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu freq %hu\n",
                   flags0, flags1, msdu->len, msdu_id, frags_paddr,
-                  (u32)skb_cb->paddr, vdev_id, tid);
+                  (u32)skb_cb->paddr, vdev_id, tid, skb_cb->htt.freq);
        ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
                        msdu->data, msdu->len);
        trace_ath10k_tx_hdr(ar, msdu->data, msdu->len);
index 392c250..dfedfd0 100644 (file)
@@ -97,11 +97,13 @@ struct ath10k_pktlog_hdr {
 #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_STATIONS                    16
+#define TARGET_NUM_PEERS                       ((TARGET_NUM_STATIONS) + \
+                                                (TARGET_NUM_VDEVS))
 #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_NUM_TIDS                                ((TARGET_NUM_PEERS) * 2)
 #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
@@ -132,12 +134,15 @@ struct ath10k_pktlog_hdr {
 #define TARGET_10X_DMA_BURST_SIZE              0
 #define TARGET_10X_MAC_AGGR_DELIM              0
 #define TARGET_10X_AST_SKID_LIMIT              16
-#define TARGET_10X_NUM_PEERS                   (128 + (TARGET_10X_NUM_VDEVS))
-#define TARGET_10X_NUM_PEERS_MAX               128
+#define TARGET_10X_NUM_STATIONS                        128
+#define TARGET_10X_NUM_PEERS                   ((TARGET_10X_NUM_STATIONS) + \
+                                                (TARGET_10X_NUM_VDEVS))
 #define TARGET_10X_NUM_OFFLOAD_PEERS           0
 #define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS    0
 #define TARGET_10X_NUM_PEER_KEYS               2
-#define TARGET_10X_NUM_TIDS                    256
+#define TARGET_10X_NUM_TIDS_MAX                        256
+#define TARGET_10X_NUM_TIDS                    min((TARGET_10X_NUM_TIDS_MAX), \
+                                                   (TARGET_10X_NUM_PEERS) * 2)
 #define TARGET_10X_TX_CHAIN_MASK               (BIT(0) | BIT(1) | BIT(2))
 #define TARGET_10X_RX_CHAIN_MASK               (BIT(0) | BIT(1) | BIT(2))
 #define TARGET_10X_RX_TIMEOUT_LO_PRI           100
index 1245ac8..c400567 100644 (file)
@@ -136,7 +136,9 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
                if (ret)
                        return ret;
 
+               spin_lock_bh(&ar->data_lock);
                peer->keys[i] = arvif->wep_keys[i];
+               spin_unlock_bh(&ar->data_lock);
        }
 
        return 0;
@@ -173,12 +175,39 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
                        ath10k_warn(ar, "failed to remove peer wep key %d: %d\n",
                                    i, ret);
 
+               spin_lock_bh(&ar->data_lock);
                peer->keys[i] = NULL;
+               spin_unlock_bh(&ar->data_lock);
        }
 
        return first_errno;
 }
 
+bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
+                                   u8 keyidx)
+{
+       struct ath10k_peer *peer;
+       int i;
+
+       lockdep_assert_held(&ar->data_lock);
+
+       /* We don't know which vdev this peer belongs to,
+        * since WMI doesn't give us that information.
+        *
+        * FIXME: multi-bss needs to be handled.
+        */
+       peer = ath10k_peer_find(ar, 0, addr);
+       if (!peer)
+               return false;
+
+       for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+               if (peer->keys[i] && peer->keys[i]->keyidx == keyidx)
+                       return true;
+       }
+
+       return false;
+}
+
 static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
                                 struct ieee80211_key_conf *key)
 {
@@ -326,6 +355,9 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 
        lockdep_assert_held(&ar->conf_mutex);
 
+       if (ar->num_peers >= ar->max_num_peers)
+               return -ENOBUFS;
+
        ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
        if (ret) {
                ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n",
@@ -339,9 +371,8 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
                            addr, vdev_id, ret);
                return ret;
        }
-       spin_lock_bh(&ar->data_lock);
+
        ar->num_peers++;
-       spin_unlock_bh(&ar->data_lock);
 
        return 0;
 }
@@ -391,15 +422,11 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
        return 0;
 }
 
-static int  ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
 {
        struct ath10k *ar = arvif->ar;
        u32 vdev_param;
 
-       if (value != 0xFFFFFFFF)
-               value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
-                             ATH10K_RTS_MAX);
-
        vdev_param = ar->wmi.vdev_param->rts_threshold;
        return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
 }
@@ -432,9 +459,7 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
        if (ret)
                return ret;
 
-       spin_lock_bh(&ar->data_lock);
        ar->num_peers--;
-       spin_unlock_bh(&ar->data_lock);
 
        return 0;
 }
@@ -471,8 +496,10 @@ static void ath10k_peer_cleanup_all(struct ath10k *ar)
                list_del(&peer->list);
                kfree(peer);
        }
-       ar->num_peers = 0;
        spin_unlock_bh(&ar->data_lock);
+
+       ar->num_peers = 0;
+       ar->num_stations = 0;
 }
 
 /************************/
@@ -1997,6 +2024,18 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
        }
 }
 
+static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
+{
+       /* FIXME: Not really sure since when the behaviour changed. At some
+        * point new firmware stopped requiring creation of peer entries for
+        * offchannel tx (and actually creating them causes issues with wmi-htc
+        * tx credit replenishment and reliability). Assuming it's at least 3.4
+        * because that's when the `freq` was introduced to TX_FRM HTT command.
+        */
+       return !(ar->htt.target_version_major >= 3 &&
+                ar->htt.target_version_minor >= 4);
+}
+
 static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
@@ -2172,10 +2211,10 @@ void __ath10k_scan_finish(struct ath10k *ar)
        case ATH10K_SCAN_IDLE:
                break;
        case ATH10K_SCAN_RUNNING:
-       case ATH10K_SCAN_ABORTING:
                if (ar->scan.is_roc)
                        ieee80211_remain_on_channel_expired(ar->hw);
-               else
+       case ATH10K_SCAN_ABORTING:
+               if (!ar->scan.is_roc)
                        ieee80211_scan_completed(ar->hw,
                                                 (ar->scan.state ==
                                                  ATH10K_SCAN_ABORTING));
@@ -2341,16 +2380,21 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 
        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.freq = ar->scan.roc_freq;
                ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
                spin_unlock_bh(&ar->data_lock);
 
-               ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
-                          skb);
+               if (ath10k_mac_need_offchan_tx_work(ar)) {
+                       ATH10K_SKB_CB(skb)->htt.freq = 0;
+                       ATH10K_SKB_CB(skb)->htt.is_offchan = true;
 
-               skb_queue_tail(&ar->offchan_tx_queue, skb);
-               ieee80211_queue_work(hw, &ar->offchan_tx_work);
-               return;
+                       ath10k_dbg(ar, 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);
@@ -2414,12 +2458,28 @@ static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
        return 0;
 }
 
+static void ath10k_check_chain_mask(struct ath10k *ar, u32 cm, const char *dbg)
+{
+       /* It is not clear that allowing gaps in chainmask
+        * is helpful.  Probably it will not do what user
+        * is hoping for, so warn in that case.
+        */
+       if (cm == 15 || cm == 7 || cm == 3 || cm == 1 || cm == 0)
+               return;
+
+       ath10k_warn(ar, "mac %s antenna chainmask may be invalid: 0x%x.  Suggested values: 15, 7, 3, 1 or 0.\n",
+                   dbg, cm);
+}
+
 static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
 {
        int ret;
 
        lockdep_assert_held(&ar->conf_mutex);
 
+       ath10k_check_chain_mask(ar, tx_ant, "tx");
+       ath10k_check_chain_mask(ar, rx_ant, "rx");
+
        ar->cfg_tx_chainmask = tx_ant;
        ar->cfg_rx_chainmask = rx_ant;
 
@@ -2782,6 +2842,17 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
        return ret;
 }
 
+static u32 get_nss_from_chainmask(u16 chain_mask)
+{
+       if ((chain_mask & 0x15) == 0x15)
+               return 4;
+       else if ((chain_mask & 0x7) == 0x7)
+               return 3;
+       else if ((chain_mask & 0x3) == 0x3)
+               return 2;
+       return 1;
+}
+
 /*
  * TODO:
  * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
@@ -2914,6 +2985,20 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                goto err_vdev_delete;
        }
 
+       if (ar->cfg_tx_chainmask) {
+               u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
+
+               vdev_param = ar->wmi.vdev_param->nss;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+                                               nss);
+               if (ret) {
+                       ath10k_warn(ar, "failed to set vdev %i chainmask 0x%x, nss %i: %d\n",
+                                   arvif->vdev_id, ar->cfg_tx_chainmask, nss,
+                                   ret);
+                       goto err_vdev_delete;
+               }
+       }
+
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
                if (ret) {
@@ -3014,10 +3099,10 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        int ret;
 
-       mutex_lock(&ar->conf_mutex);
-
        cancel_work_sync(&arvif->wep_key_work);
 
+       mutex_lock(&ar->conf_mutex);
+
        spin_lock_bh(&ar->data_lock);
        ath10k_mac_vif_beacon_cleanup(arvif);
        spin_unlock_bh(&ar->data_lock);
@@ -3511,6 +3596,37 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk)
        mutex_unlock(&ar->conf_mutex);
 }
 
+static int ath10k_mac_inc_num_stations(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+           arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+               return 0;
+
+       if (ar->num_stations >= ar->max_num_stations)
+               return -ENOBUFS;
+
+       ar->num_stations++;
+
+       return 0;
+}
+
+static void ath10k_mac_dec_num_stations(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+           arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+               return;
+
+       ar->num_stations--;
+}
+
 static int ath10k_sta_state(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            struct ieee80211_sta *sta,
@@ -3520,7 +3636,6 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
-       int max_num_peers;
        int ret = 0;
 
        if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -3542,26 +3657,26 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                /*
                 * New station addition.
                 */
-               if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
-                       max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1;
-               else
-                       max_num_peers = TARGET_NUM_PEERS;
+               ath10k_dbg(ar, ATH10K_DBG_MAC,
+                          "mac vdev %d peer create %pM (new sta) sta %d / %d peer %d / %d\n",
+                          arvif->vdev_id, sta->addr,
+                          ar->num_stations + 1, ar->max_num_stations,
+                          ar->num_peers + 1, ar->max_num_peers);
 
-               if (ar->num_peers >= max_num_peers) {
-                       ath10k_warn(ar, "number of peers exceeded: peers number %d (max peers %d)\n",
-                                   ar->num_peers, max_num_peers);
-                       ret = -ENOBUFS;
+               ret = ath10k_mac_inc_num_stations(arvif);
+               if (ret) {
+                       ath10k_warn(ar, "refusing to associate station: too many connected already (%d)\n",
+                                   ar->max_num_stations);
                        goto exit;
                }
 
-               ath10k_dbg(ar, ATH10K_DBG_MAC,
-                          "mac vdev %d peer create %pM (new sta) num_peers %d\n",
-                          arvif->vdev_id, sta->addr, ar->num_peers);
-
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
-               if (ret)
+               if (ret) {
                        ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
+                       ath10k_mac_dec_num_stations(arvif);
+                       goto exit;
+               }
 
                if (vif->type == NL80211_IFTYPE_STATION) {
                        WARN_ON(arvif->is_started);
@@ -3572,6 +3687,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                                            arvif->vdev_id, ret);
                                WARN_ON(ath10k_peer_delete(ar, arvif->vdev_id,
                                                           sta->addr));
+                               ath10k_mac_dec_num_stations(arvif);
                                goto exit;
                        }
 
@@ -3602,6 +3718,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                        ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
 
+               ath10k_mac_dec_num_stations(arvif);
        } else if (old_state == IEEE80211_STA_AUTH &&
                   new_state == IEEE80211_STA_ASSOC &&
                   (vif->type == NL80211_IFTYPE_AP ||
@@ -3790,6 +3907,8 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
        if (ret)
                goto exit;
 
+       duration = max(duration, WMI_SCAN_CHAN_MIN_TIME_MSEC);
+
        memset(&arg, 0, sizeof(arg));
        ath10k_wmi_start_scan_init(ar, &arg);
        arg.vdev_id = arvif->vdev_id;
@@ -4106,6 +4225,10 @@ ath10k_default_bitrate_mask(struct ath10k *ar,
        u32 legacy = 0x00ff;
        u8 ht = 0xff, i;
        u16 vht = 0x3ff;
+       u16 nrf = ar->num_rf_chains;
+
+       if (ar->cfg_tx_chainmask)
+               nrf = get_nss_from_chainmask(ar->cfg_tx_chainmask);
 
        switch (band) {
        case IEEE80211_BAND_2GHZ:
@@ -4121,11 +4244,11 @@ ath10k_default_bitrate_mask(struct ath10k *ar,
        if (mask->control[band].legacy != legacy)
                return false;
 
-       for (i = 0; i < ar->num_rf_chains; i++)
+       for (i = 0; i < nrf; i++)
                if (mask->control[band].ht_mcs[i] != ht)
                        return false;
 
-       for (i = 0; i < ar->num_rf_chains; i++)
+       for (i = 0; i < nrf; i++)
                if (mask->control[band].vht_mcs[i] != vht)
                        return false;
 
@@ -4376,6 +4499,9 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
        u8 fixed_nss = ar->num_rf_chains;
        u8 force_sgi;
 
+       if (ar->cfg_tx_chainmask)
+               fixed_nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
+
        force_sgi = mask->control[band].gi;
        if (force_sgi == NL80211_TXRATE_FORCE_LGI)
                return -EINVAL;
@@ -4905,10 +5031,6 @@ int ath10k_mac_register(struct ath10k *ar)
                        IEEE80211_HW_AP_LINK_PS |
                        IEEE80211_HW_SPECTRUM_MGMT;
 
-       /* MSDU can have HTT TX fragment pushed in front. The additional 4
-        * bytes is used for padding/alignment if necessary. */
-       ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
-
        ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
 
        if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
index 4e3c989..6829611 100644 (file)
@@ -21,6 +21,8 @@
 #include <net/mac80211.h>
 #include "core.h"
 
+#define WEP_KEYID_SHIFT 6
+
 struct ath10k_generic_iter {
        struct ath10k *ar;
        int ret;
@@ -41,6 +43,8 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
 void ath10k_halt(struct ath10k *ar);
 void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif);
 void ath10k_drain_tx(struct ath10k *ar);
+bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
+                                   u8 keyidx);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
 {
index 3a6b8a5..7abb836 100644 (file)
@@ -823,20 +823,24 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
        struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
-       void *transfer_context;
+       struct sk_buff_head list;
+       struct sk_buff *skb;
        u32 ce_data;
        unsigned int nbytes;
        unsigned int transfer_id;
 
-       while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
-                                            &ce_data, &nbytes,
-                                            &transfer_id) == 0) {
+       __skb_queue_head_init(&list);
+       while (ath10k_ce_completed_send_next(ce_state, (void **)&skb, &ce_data,
+                                            &nbytes, &transfer_id) == 0) {
                /* no need to call tx completion for NULL pointers */
-               if (transfer_context == NULL)
+               if (skb == NULL)
                        continue;
 
-               cb->tx_completion(ar, transfer_context, transfer_id);
+               __skb_queue_tail(&list, skb);
        }
+
+       while ((skb = __skb_dequeue(&list)))
+               cb->tx_completion(ar, skb);
 }
 
 /* Called by lower (CE) layer when data is received from the Target. */
@@ -847,12 +851,14 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
        struct ath10k_pci_pipe *pipe_info =  &ar_pci->pipe_info[ce_state->id];
        struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
        struct sk_buff *skb;
+       struct sk_buff_head list;
        void *transfer_context;
        u32 ce_data;
        unsigned int nbytes, max_nbytes;
        unsigned int transfer_id;
        unsigned int flags;
 
+       __skb_queue_head_init(&list);
        while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
                                             &ce_data, &nbytes, &transfer_id,
                                             &flags) == 0) {
@@ -869,13 +875,16 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
                }
 
                skb_put(skb, nbytes);
+               __skb_queue_tail(&list, skb);
+       }
 
+       while ((skb = __skb_dequeue(&list))) {
                ath10k_dbg(ar, ATH10K_DBG_PCI, "pci rx ce pipe %d len %d\n",
                           ce_state->id, skb->len);
                ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci rx: ",
                                skb->data, skb->len);
 
-               cb->rx_completion(ar, skb, pipe_info->pipe_num);
+               cb->rx_completion(ar, skb);
        }
 
        ath10k_pci_rx_post_pipe(pipe_info);
@@ -1263,7 +1272,7 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
                id = MS(__le16_to_cpu(ce_desc[i].flags),
                        CE_DESC_FLAGS_META_DATA);
 
-               ar_pci->msg_callbacks_current.tx_completion(ar, skb, id);
+               ar_pci->msg_callbacks_current.tx_completion(ar, skb);
        }
 }
 
@@ -1988,6 +1997,7 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
 static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
        .tx_sg                  = ath10k_pci_hif_tx_sg,
        .diag_read              = ath10k_pci_hif_diag_read,
+       .diag_write             = ath10k_pci_diag_write_mem,
        .exchange_bmi_msg       = ath10k_pci_hif_exchange_bmi_msg,
        .start                  = ath10k_pci_hif_start,
        .stop                   = ath10k_pci_hif_stop,
@@ -1998,6 +2008,8 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
        .get_free_queue_number  = ath10k_pci_hif_get_free_queue_number,
        .power_up               = ath10k_pci_hif_power_up,
        .power_down             = ath10k_pci_hif_power_down,
+       .read32                 = ath10k_pci_read32,
+       .write32                = ath10k_pci_write32,
 #ifdef CONFIG_PM
        .suspend                = ath10k_pci_hif_suspend,
        .resume                 = ath10k_pci_hif_resume,
index ceea566..b289378 100644 (file)
 #include "core.h"
 
 #if !defined(_TRACE_H_)
-static inline u32 ath10k_frm_hdr_len(void *buf)
+static inline u32 ath10k_frm_hdr_len(const void *buf)
 {
-       return ieee80211_hdrlen(((struct ieee80211_hdr *)buf)->frame_control);
+       const struct ieee80211_hdr *hdr = buf;
+
+       return ieee80211_hdrlen(hdr->frame_control);
 }
 #endif
 
@@ -145,7 +147,8 @@ TRACE_EVENT(ath10k_log_dbg_dump,
 );
 
 TRACE_EVENT(ath10k_wmi_cmd,
-       TP_PROTO(struct ath10k *ar, int id, void *buf, size_t buf_len, int ret),
+       TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len,
+                int ret),
 
        TP_ARGS(ar, id, buf, buf_len, ret),
 
@@ -178,7 +181,7 @@ TRACE_EVENT(ath10k_wmi_cmd,
 );
 
 TRACE_EVENT(ath10k_wmi_event,
-       TP_PROTO(struct ath10k *ar, int id, void *buf, size_t buf_len),
+       TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len),
 
        TP_ARGS(ar, id, buf, buf_len),
 
@@ -208,7 +211,7 @@ TRACE_EVENT(ath10k_wmi_event,
 );
 
 TRACE_EVENT(ath10k_htt_stats,
-       TP_PROTO(struct ath10k *ar, void *buf, size_t buf_len),
+       TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
 
        TP_ARGS(ar, buf, buf_len),
 
@@ -235,7 +238,7 @@ TRACE_EVENT(ath10k_htt_stats,
 );
 
 TRACE_EVENT(ath10k_wmi_dbglog,
-       TP_PROTO(struct ath10k *ar, void *buf, size_t buf_len),
+       TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
 
        TP_ARGS(ar, buf, buf_len),
 
@@ -262,7 +265,7 @@ TRACE_EVENT(ath10k_wmi_dbglog,
 );
 
 TRACE_EVENT(ath10k_htt_pktlog,
-           TP_PROTO(struct ath10k *ar, void *buf, u16 buf_len),
+           TP_PROTO(struct ath10k *ar, const void *buf, u16 buf_len),
 
        TP_ARGS(ar, buf, buf_len),
 
@@ -349,7 +352,7 @@ TRACE_EVENT(ath10k_txrx_tx_unref,
 );
 
 DECLARE_EVENT_CLASS(ath10k_hdr_event,
-                   TP_PROTO(struct ath10k *ar, void *data, size_t len),
+                   TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 
        TP_ARGS(ar, data, len),
 
@@ -376,7 +379,7 @@ DECLARE_EVENT_CLASS(ath10k_hdr_event,
 );
 
 DECLARE_EVENT_CLASS(ath10k_payload_event,
-                   TP_PROTO(struct ath10k *ar, void *data, size_t len),
+                   TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 
        TP_ARGS(ar, data, len),
 
@@ -404,27 +407,27 @@ DECLARE_EVENT_CLASS(ath10k_payload_event,
 );
 
 DEFINE_EVENT(ath10k_hdr_event, ath10k_tx_hdr,
-            TP_PROTO(struct ath10k *ar, void *data, size_t len),
+            TP_PROTO(struct ath10k *ar, const void *data, size_t len),
             TP_ARGS(ar, data, len)
 );
 
 DEFINE_EVENT(ath10k_payload_event, ath10k_tx_payload,
-            TP_PROTO(struct ath10k *ar, void *data, size_t len),
+            TP_PROTO(struct ath10k *ar, const void *data, size_t len),
             TP_ARGS(ar, data, len)
 );
 
 DEFINE_EVENT(ath10k_hdr_event, ath10k_rx_hdr,
-            TP_PROTO(struct ath10k *ar, void *data, size_t len),
+            TP_PROTO(struct ath10k *ar, const void *data, size_t len),
             TP_ARGS(ar, data, len)
 );
 
 DEFINE_EVENT(ath10k_payload_event, ath10k_rx_payload,
-            TP_PROTO(struct ath10k *ar, void *data, size_t len),
+            TP_PROTO(struct ath10k *ar, const void *data, size_t len),
             TP_ARGS(ar, data, len)
 );
 
 TRACE_EVENT(ath10k_htt_rx_desc,
-           TP_PROTO(struct ath10k *ar, void *data, size_t len),
+           TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 
        TP_ARGS(ar, data, len),
 
index c2bc828..c0f3e4d 100644 (file)
@@ -1113,6 +1113,40 @@ static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band)
        return rate_idx;
 }
 
+/* If keys are configured, HW decrypts all frames
+ * with protected bit set. Mark such frames as decrypted.
+ */
+static void ath10k_wmi_handle_wep_reauth(struct ath10k *ar,
+                                        struct sk_buff *skb,
+                                        struct ieee80211_rx_status *status)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       unsigned int hdrlen;
+       bool peer_key;
+       u8 *addr, keyidx;
+
+       if (!ieee80211_is_auth(hdr->frame_control) ||
+           !ieee80211_has_protected(hdr->frame_control))
+               return;
+
+       hdrlen = ieee80211_hdrlen(hdr->frame_control);
+       if (skb->len < (hdrlen + IEEE80211_WEP_IV_LEN))
+               return;
+
+       keyidx = skb->data[hdrlen + (IEEE80211_WEP_IV_LEN - 1)] >> WEP_KEYID_SHIFT;
+       addr = ieee80211_get_SA(hdr);
+
+       spin_lock_bh(&ar->data_lock);
+       peer_key = ath10k_mac_is_peer_wep_key_set(ar, addr, keyidx);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (peer_key) {
+               ath10k_dbg(ar, ATH10K_DBG_MAC,
+                          "mac wep key present for peer %pM\n", addr);
+               status->flag |= RX_FLAG_DECRYPTED;
+       }
+}
+
 static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_mgmt_rx_event_v1 *ev_v1;
@@ -1166,8 +1200,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *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_CRC) {
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
        if (rx_status & WMI_RX_STATUS_ERR_MIC)
                status->flag |= RX_FLAG_MMIC_ERROR;
 
@@ -1200,6 +1237,8 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        hdr = (struct ieee80211_hdr *)skb->data;
        fc = le16_to_cpu(hdr->frame_control);
 
+       ath10k_wmi_handle_wep_reauth(ar, skb, status);
+
        /* FW delivers WEP Shared Auth frame with Protected Bit set and
         * encrypted payload. However in case of PMF it delivers decrypted
         * frames with Protected Bit set. */
@@ -2261,7 +2300,7 @@ static void ath10k_wmi_event_debug_print(struct ath10k *ar,
        /* the last byte is always reserved for the null character */
        buf[i] = '\0';
 
-       ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf);
+       ath10k_dbg(ar, ATH10K_DBG_WMI_PRINT, "wmi print '%s'\n", buf);
 }
 
 static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
@@ -2418,6 +2457,7 @@ static int ath10k_wmi_main_pull_svc_rdy_ev(struct sk_buff *skb,
        arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
        arg->num_mem_reqs = ev->num_mem_reqs;
        arg->service_map = ev->wmi_service_bitmap;
+       arg->service_map_len = sizeof(ev->wmi_service_bitmap);
 
        n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
                  ARRAY_SIZE(arg->mem_reqs));
@@ -2452,6 +2492,7 @@ static int ath10k_wmi_10x_pull_svc_rdy_ev(struct sk_buff *skb,
        arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
        arg->num_mem_reqs = ev->num_mem_reqs;
        arg->service_map = ev->wmi_service_bitmap;
+       arg->service_map_len = sizeof(ev->wmi_service_bitmap);
 
        n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
                  ARRAY_SIZE(arg->mem_reqs));
@@ -2470,15 +2511,18 @@ static void ath10k_wmi_event_service_ready(struct ath10k *ar,
 {
        struct wmi_svc_rdy_ev_arg arg = {};
        u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
-       DECLARE_BITMAP(svc_bmap, WMI_SERVICE_MAX) = {};
        int ret;
 
+       memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
+
        if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
                ret = ath10k_wmi_10x_pull_svc_rdy_ev(skb, &arg);
-               wmi_10x_svc_map(arg.service_map, svc_bmap);
+               wmi_10x_svc_map(arg.service_map, ar->wmi.svc_map,
+                               arg.service_map_len);
        } else {
                ret = ath10k_wmi_main_pull_svc_rdy_ev(skb, &arg);
-               wmi_main_svc_map(arg.service_map, svc_bmap);
+               wmi_main_svc_map(arg.service_map, ar->wmi.svc_map,
+                                arg.service_map_len);
        }
 
        if (ret) {
@@ -2500,9 +2544,8 @@ static void ath10k_wmi_event_service_ready(struct ath10k *ar,
        ar->num_rf_chains = __le32_to_cpu(arg.num_rf_chains);
        ar->ath_common.regulatory.current_rd = __le32_to_cpu(arg.eeprom_rd);
 
-       ath10k_debug_read_service_map(ar, svc_bmap, sizeof(svc_bmap));
        ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
-                       arg.service_map, sizeof(arg.service_map));
+                       arg.service_map, arg.service_map_len);
 
        /* only manually set fw features when not using FW IE format */
        if (ar->fw_api == 1 && ar->fw_version_build > 636)
@@ -3142,7 +3185,7 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
        u32 len, val;
 
        config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
-       config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
+       config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS);
        config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS);
 
        config.num_offload_reorder_bufs =
index a38d788..2139192 100644 (file)
@@ -222,128 +222,131 @@ static inline char *wmi_service_name(int service_id)
 #undef SVCSTR
 }
 
-#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \
-       (__le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
+#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
+       ((svc_id) < (len) && \
+        __le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
         BIT((svc_id)%(sizeof(u32))))
 
-#define SVCMAP(x, y) \
+#define SVCMAP(x, y, len) \
        do { \
-               if (WMI_SERVICE_IS_ENABLED((in), (x))) \
+               if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \
                        __set_bit(y, out); \
        } while (0)
 
-static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out)
+static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out,
+                                  size_t len)
 {
        SVCMAP(WMI_10X_SERVICE_BEACON_OFFLOAD,
-              WMI_SERVICE_BEACON_OFFLOAD);
+              WMI_SERVICE_BEACON_OFFLOAD, len);
        SVCMAP(WMI_10X_SERVICE_SCAN_OFFLOAD,
-              WMI_SERVICE_SCAN_OFFLOAD);
+              WMI_SERVICE_SCAN_OFFLOAD, len);
        SVCMAP(WMI_10X_SERVICE_ROAM_OFFLOAD,
-              WMI_SERVICE_ROAM_OFFLOAD);
+              WMI_SERVICE_ROAM_OFFLOAD, len);
        SVCMAP(WMI_10X_SERVICE_BCN_MISS_OFFLOAD,
-              WMI_SERVICE_BCN_MISS_OFFLOAD);
+              WMI_SERVICE_BCN_MISS_OFFLOAD, len);
        SVCMAP(WMI_10X_SERVICE_STA_PWRSAVE,
-              WMI_SERVICE_STA_PWRSAVE);
+              WMI_SERVICE_STA_PWRSAVE, len);
        SVCMAP(WMI_10X_SERVICE_STA_ADVANCED_PWRSAVE,
-              WMI_SERVICE_STA_ADVANCED_PWRSAVE);
+              WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
        SVCMAP(WMI_10X_SERVICE_AP_UAPSD,
-              WMI_SERVICE_AP_UAPSD);
+              WMI_SERVICE_AP_UAPSD, len);
        SVCMAP(WMI_10X_SERVICE_AP_DFS,
-              WMI_SERVICE_AP_DFS);
+              WMI_SERVICE_AP_DFS, len);
        SVCMAP(WMI_10X_SERVICE_11AC,
-              WMI_SERVICE_11AC);
+              WMI_SERVICE_11AC, len);
        SVCMAP(WMI_10X_SERVICE_BLOCKACK,
-              WMI_SERVICE_BLOCKACK);
+              WMI_SERVICE_BLOCKACK, len);
        SVCMAP(WMI_10X_SERVICE_PHYERR,
-              WMI_SERVICE_PHYERR);
+              WMI_SERVICE_PHYERR, len);
        SVCMAP(WMI_10X_SERVICE_BCN_FILTER,
-              WMI_SERVICE_BCN_FILTER);
+              WMI_SERVICE_BCN_FILTER, len);
        SVCMAP(WMI_10X_SERVICE_RTT,
-              WMI_SERVICE_RTT);
+              WMI_SERVICE_RTT, len);
        SVCMAP(WMI_10X_SERVICE_RATECTRL,
-              WMI_SERVICE_RATECTRL);
+              WMI_SERVICE_RATECTRL, len);
        SVCMAP(WMI_10X_SERVICE_WOW,
-              WMI_SERVICE_WOW);
+              WMI_SERVICE_WOW, len);
        SVCMAP(WMI_10X_SERVICE_RATECTRL_CACHE,
-              WMI_SERVICE_RATECTRL_CACHE);
+              WMI_SERVICE_RATECTRL_CACHE, len);
        SVCMAP(WMI_10X_SERVICE_IRAM_TIDS,
-              WMI_SERVICE_IRAM_TIDS);
+              WMI_SERVICE_IRAM_TIDS, len);
        SVCMAP(WMI_10X_SERVICE_BURST,
-              WMI_SERVICE_BURST);
+              WMI_SERVICE_BURST, len);
        SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_SW_SUPPORT,
-              WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT);
+              WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT, len);
        SVCMAP(WMI_10X_SERVICE_FORCE_FW_HANG,
-              WMI_SERVICE_FORCE_FW_HANG);
+              WMI_SERVICE_FORCE_FW_HANG, len);
        SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT,
-              WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT);
+              WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT, len);
 }
 
-static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out)
+static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
+                                   size_t len)
 {
        SVCMAP(WMI_MAIN_SERVICE_BEACON_OFFLOAD,
-              WMI_SERVICE_BEACON_OFFLOAD);
+              WMI_SERVICE_BEACON_OFFLOAD, len);
        SVCMAP(WMI_MAIN_SERVICE_SCAN_OFFLOAD,
-              WMI_SERVICE_SCAN_OFFLOAD);
+              WMI_SERVICE_SCAN_OFFLOAD, len);
        SVCMAP(WMI_MAIN_SERVICE_ROAM_OFFLOAD,
-              WMI_SERVICE_ROAM_OFFLOAD);
+              WMI_SERVICE_ROAM_OFFLOAD, len);
        SVCMAP(WMI_MAIN_SERVICE_BCN_MISS_OFFLOAD,
-              WMI_SERVICE_BCN_MISS_OFFLOAD);
+              WMI_SERVICE_BCN_MISS_OFFLOAD, len);
        SVCMAP(WMI_MAIN_SERVICE_STA_PWRSAVE,
-              WMI_SERVICE_STA_PWRSAVE);
+              WMI_SERVICE_STA_PWRSAVE, len);
        SVCMAP(WMI_MAIN_SERVICE_STA_ADVANCED_PWRSAVE,
-              WMI_SERVICE_STA_ADVANCED_PWRSAVE);
+              WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
        SVCMAP(WMI_MAIN_SERVICE_AP_UAPSD,
-              WMI_SERVICE_AP_UAPSD);
+              WMI_SERVICE_AP_UAPSD, len);
        SVCMAP(WMI_MAIN_SERVICE_AP_DFS,
-              WMI_SERVICE_AP_DFS);
+              WMI_SERVICE_AP_DFS, len);
        SVCMAP(WMI_MAIN_SERVICE_11AC,
-              WMI_SERVICE_11AC);
+              WMI_SERVICE_11AC, len);
        SVCMAP(WMI_MAIN_SERVICE_BLOCKACK,
-              WMI_SERVICE_BLOCKACK);
+              WMI_SERVICE_BLOCKACK, len);
        SVCMAP(WMI_MAIN_SERVICE_PHYERR,
-              WMI_SERVICE_PHYERR);
+              WMI_SERVICE_PHYERR, len);
        SVCMAP(WMI_MAIN_SERVICE_BCN_FILTER,
-              WMI_SERVICE_BCN_FILTER);
+              WMI_SERVICE_BCN_FILTER, len);
        SVCMAP(WMI_MAIN_SERVICE_RTT,
-              WMI_SERVICE_RTT);
+              WMI_SERVICE_RTT, len);
        SVCMAP(WMI_MAIN_SERVICE_RATECTRL,
-              WMI_SERVICE_RATECTRL);
+              WMI_SERVICE_RATECTRL, len);
        SVCMAP(WMI_MAIN_SERVICE_WOW,
-              WMI_SERVICE_WOW);
+              WMI_SERVICE_WOW, len);
        SVCMAP(WMI_MAIN_SERVICE_RATECTRL_CACHE,
-              WMI_SERVICE_RATECTRL_CACHE);
+              WMI_SERVICE_RATECTRL_CACHE, len);
        SVCMAP(WMI_MAIN_SERVICE_IRAM_TIDS,
-              WMI_SERVICE_IRAM_TIDS);
+              WMI_SERVICE_IRAM_TIDS, len);
        SVCMAP(WMI_MAIN_SERVICE_ARPNS_OFFLOAD,
-              WMI_SERVICE_ARPNS_OFFLOAD);
+              WMI_SERVICE_ARPNS_OFFLOAD, len);
        SVCMAP(WMI_MAIN_SERVICE_NLO,
-              WMI_SERVICE_NLO);
+              WMI_SERVICE_NLO, len);
        SVCMAP(WMI_MAIN_SERVICE_GTK_OFFLOAD,
-              WMI_SERVICE_GTK_OFFLOAD);
+              WMI_SERVICE_GTK_OFFLOAD, len);
        SVCMAP(WMI_MAIN_SERVICE_SCAN_SCH,
-              WMI_SERVICE_SCAN_SCH);
+              WMI_SERVICE_SCAN_SCH, len);
        SVCMAP(WMI_MAIN_SERVICE_CSA_OFFLOAD,
-              WMI_SERVICE_CSA_OFFLOAD);
+              WMI_SERVICE_CSA_OFFLOAD, len);
        SVCMAP(WMI_MAIN_SERVICE_CHATTER,
-              WMI_SERVICE_CHATTER);
+              WMI_SERVICE_CHATTER, len);
        SVCMAP(WMI_MAIN_SERVICE_COEX_FREQAVOID,
-              WMI_SERVICE_COEX_FREQAVOID);
+              WMI_SERVICE_COEX_FREQAVOID, len);
        SVCMAP(WMI_MAIN_SERVICE_PACKET_POWER_SAVE,
-              WMI_SERVICE_PACKET_POWER_SAVE);
+              WMI_SERVICE_PACKET_POWER_SAVE, len);
        SVCMAP(WMI_MAIN_SERVICE_FORCE_FW_HANG,
-              WMI_SERVICE_FORCE_FW_HANG);
+              WMI_SERVICE_FORCE_FW_HANG, len);
        SVCMAP(WMI_MAIN_SERVICE_GPIO,
-              WMI_SERVICE_GPIO);
+              WMI_SERVICE_GPIO, len);
        SVCMAP(WMI_MAIN_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
-              WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM);
+              WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, len);
        SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
-              WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG);
+              WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, len);
        SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
-              WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG);
+              WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, len);
        SVCMAP(WMI_MAIN_SERVICE_STA_KEEP_ALIVE,
-              WMI_SERVICE_STA_KEEP_ALIVE);
+              WMI_SERVICE_STA_KEEP_ALIVE, len);
        SVCMAP(WMI_MAIN_SERVICE_TX_ENCAP,
-              WMI_SERVICE_TX_ENCAP);
+              WMI_SERVICE_TX_ENCAP, len);
 }
 
 #undef SVCMAP
@@ -1952,6 +1955,11 @@ struct wmi_ssid_list {
 #define WLAN_SCAN_PARAMS_MAX_BSSID   4
 #define WLAN_SCAN_PARAMS_MAX_IE_LEN  256
 
+/* Values lower than this may be refused by some firmware revisions with a scan
+ * completion with a timedout reason.
+ */
+#define WMI_SCAN_CHAN_MIN_TIME_MSEC 40
+
 /* Scan priority numbers must be sequential, starting with 0 */
 enum wmi_scan_priority {
        WMI_SCAN_PRIORITY_VERY_LOW = 0,
@@ -4547,7 +4555,6 @@ struct wmi_dbglog_cfg_cmd {
        __le32 config_valid;
 } __packed;
 
-#define ATH10K_RTS_MAX         2347
 #define ATH10K_FRAGMT_THRESHOLD_MIN    540
 #define ATH10K_FRAGMT_THRESHOLD_MAX    2346
 
@@ -4572,6 +4579,7 @@ struct wmi_svc_rdy_ev_arg {
        __le32 eeprom_rd;
        __le32 num_mem_reqs;
        const __le32 *service_map;
+       size_t service_map_len;
        const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS];
 };
 
index 0583c69..ddaad71 100644 (file)
@@ -225,13 +225,7 @@ ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
        } else {
                switch (queue_type) {
                case AR5K_TX_QUEUE_DATA:
-                       for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
-                               ah->ah_txq[queue].tqi_type !=
-                               AR5K_TX_QUEUE_INACTIVE; queue++) {
-
-                               if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
-                                       return -EINVAL;
-                       }
+                       queue = queue_info->tqi_subtype;
                        break;
                case AR5K_TX_QUEUE_UAPSD:
                        queue = AR5K_TX_QUEUE_ID_UAPSD;
index 2a93519..f816909 100644 (file)
@@ -281,7 +281,7 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
 
        ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen)
                | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
-               | SM(i->txpower, AR_XmitPower0)
+               | SM(i->txpower[0], AR_XmitPower0)
                | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
                | (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
                | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
@@ -307,9 +307,9 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
                | set11nRateFlags(i->rates, 3)
                | SM(i->rtscts_rate, AR_RTSCTSRate);
 
-       ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower, AR_XmitPower1);
-       ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower, AR_XmitPower2);
-       ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower, AR_XmitPower3);
+       ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower[1], AR_XmitPower1);
+       ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower[2], AR_XmitPower2);
+       ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower[3], AR_XmitPower3);
 }
 
 static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
index e726e40..08225a0 100644 (file)
@@ -4377,6 +4377,25 @@ static u8 ar9003_hw_eeprom_get_cck_tgt_pwr(struct ath_hw *ah,
                                                 targetPowerArray, numPiers);
 }
 
+static void ar9003_hw_selfgen_tpc_txpower(struct ath_hw *ah,
+                                         struct ath9k_channel *chan,
+                                         u8 *pwr_array)
+{
+       u32 val;
+
+       /* target power values for self generated frames (ACK,RTS/CTS) */
+       if (IS_CHAN_2GHZ(chan)) {
+               val = SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_ACK) |
+                     SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_CTS) |
+                     SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+       } else {
+               val = SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_ACK) |
+                     SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_CTS) |
+                     SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+       }
+       REG_WRITE(ah, AR_TPC, val);
+}
+
 /* Set tx power registers to array of values passed in */
 static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray)
 {
@@ -5312,6 +5331,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
        struct ar9300_modal_eep_header *modal_hdr;
        u8 targetPowerValT2[ar9300RateSize];
        u8 target_power_val_t2_eep[ar9300RateSize];
+       u8 targetPowerValT2_tpc[ar9300RateSize];
        unsigned int i = 0, paprd_scale_factor = 0;
        u8 pwr_idx, min_pwridx = 0;
 
@@ -5363,6 +5383,9 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
                                           twiceAntennaReduction,
                                           powerLimit);
 
+       memcpy(targetPowerValT2_tpc, targetPowerValT2,
+              sizeof(targetPowerValT2));
+
        if (ar9003_is_paprd_enabled(ah)) {
                for (i = 0; i < ar9300RateSize; i++) {
                        if ((ah->paprd_ratemask & (1 << i)) &&
@@ -5396,6 +5419,30 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
        ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
        ar9003_hw_calibration_apply(ah, chan->channel);
        ar9003_paprd_set_txpower(ah, chan, targetPowerValT2);
+
+       ar9003_hw_selfgen_tpc_txpower(ah, chan, targetPowerValT2);
+
+       /* TPC initializations */
+       if (ah->tpc_enabled) {
+               u32 val;
+
+               ar9003_hw_init_rate_txpower(ah, targetPowerValT2_tpc, chan);
+
+               /* Enable TPC */
+               REG_WRITE(ah, AR_PHY_PWRTX_MAX,
+                         AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+               /* Disable per chain power reduction */
+               val = REG_READ(ah, AR_PHY_POWER_TX_SUB);
+               if (AR_SREV_9340(ah))
+                       REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+                                 val & 0xFFFFFFC0);
+               else
+                       REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+                                 val & 0xFFFFF000);
+       } else {
+               /* Disable TPC */
+               REG_WRITE(ah, AR_PHY_PWRTX_MAX, 0);
+       }
 }
 
 static u16 ath9k_hw_ar9300_get_spur_channel(struct ath_hw *ah,
index 057b165..da84b70 100644 (file)
@@ -101,7 +101,7 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
 
        ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen)
                | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
-               | SM(i->txpower, AR_XmitPower0)
+               | SM(i->txpower[0], AR_XmitPower0)
                | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
                | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
                | (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0)
@@ -152,9 +152,9 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
 
        ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
 
-       ACCESS_ONCE(ads->ctl20) = SM(i->txpower, AR_XmitPower1);
-       ACCESS_ONCE(ads->ctl21) = SM(i->txpower, AR_XmitPower2);
-       ACCESS_ONCE(ads->ctl22) = SM(i->txpower, AR_XmitPower3);
+       ACCESS_ONCE(ads->ctl20) = SM(i->txpower[1], AR_XmitPower1);
+       ACCESS_ONCE(ads->ctl21) = SM(i->txpower[2], AR_XmitPower2);
+       ACCESS_ONCE(ads->ctl22) = SM(i->txpower[3], AR_XmitPower3);
 }
 
 static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
index 2df6d2e..ae6cde2 100644 (file)
 #include "hw.h"
 #include "ar9003_phy.h"
 
+#define AR9300_OFDM_RATES      8
+#define AR9300_HT_SS_RATES     8
+#define AR9300_HT_DS_RATES     8
+#define AR9300_HT_TS_RATES     8
+
+#define AR9300_11NA_OFDM_SHIFT         0
+#define AR9300_11NA_HT_SS_SHIFT                8
+#define AR9300_11NA_HT_DS_SHIFT                16
+#define AR9300_11NA_HT_TS_SHIFT                24
+
+#define AR9300_11NG_OFDM_SHIFT         4
+#define AR9300_11NG_HT_SS_SHIFT                12
+#define AR9300_11NG_HT_DS_SHIFT                20
+#define AR9300_11NG_HT_TS_SHIFT                28
+
 static const int firstep_table[] =
 /* level:  0   1   2   3   4   5   6   7   8  */
        { -4, -2,  0,  2,  4,  6,  8, 10, 12 }; /* lvl 0-8, default 2 */
@@ -40,6 +55,71 @@ static const int m2ThreshLowExt_off = 127;
 static const int m1ThreshExt_off = 127;
 static const int m2ThreshExt_off = 127;
 
+static const u8 ofdm2pwr[] = {
+       ALL_TARGET_LEGACY_6_24,
+       ALL_TARGET_LEGACY_6_24,
+       ALL_TARGET_LEGACY_6_24,
+       ALL_TARGET_LEGACY_6_24,
+       ALL_TARGET_LEGACY_6_24,
+       ALL_TARGET_LEGACY_36,
+       ALL_TARGET_LEGACY_48,
+       ALL_TARGET_LEGACY_54
+};
+
+static const u8 mcs2pwr_ht20[] = {
+       ALL_TARGET_HT20_0_8_16,
+       ALL_TARGET_HT20_1_3_9_11_17_19,
+       ALL_TARGET_HT20_1_3_9_11_17_19,
+       ALL_TARGET_HT20_1_3_9_11_17_19,
+       ALL_TARGET_HT20_4,
+       ALL_TARGET_HT20_5,
+       ALL_TARGET_HT20_6,
+       ALL_TARGET_HT20_7,
+       ALL_TARGET_HT20_0_8_16,
+       ALL_TARGET_HT20_1_3_9_11_17_19,
+       ALL_TARGET_HT20_1_3_9_11_17_19,
+       ALL_TARGET_HT20_1_3_9_11_17_19,
+       ALL_TARGET_HT20_12,
+       ALL_TARGET_HT20_13,
+       ALL_TARGET_HT20_14,
+       ALL_TARGET_HT20_15,
+       ALL_TARGET_HT20_0_8_16,
+       ALL_TARGET_HT20_1_3_9_11_17_19,
+       ALL_TARGET_HT20_1_3_9_11_17_19,
+       ALL_TARGET_HT20_1_3_9_11_17_19,
+       ALL_TARGET_HT20_20,
+       ALL_TARGET_HT20_21,
+       ALL_TARGET_HT20_22,
+       ALL_TARGET_HT20_23
+};
+
+static const u8 mcs2pwr_ht40[] = {
+       ALL_TARGET_HT40_0_8_16,
+       ALL_TARGET_HT40_1_3_9_11_17_19,
+       ALL_TARGET_HT40_1_3_9_11_17_19,
+       ALL_TARGET_HT40_1_3_9_11_17_19,
+       ALL_TARGET_HT40_4,
+       ALL_TARGET_HT40_5,
+       ALL_TARGET_HT40_6,
+       ALL_TARGET_HT40_7,
+       ALL_TARGET_HT40_0_8_16,
+       ALL_TARGET_HT40_1_3_9_11_17_19,
+       ALL_TARGET_HT40_1_3_9_11_17_19,
+       ALL_TARGET_HT40_1_3_9_11_17_19,
+       ALL_TARGET_HT40_12,
+       ALL_TARGET_HT40_13,
+       ALL_TARGET_HT40_14,
+       ALL_TARGET_HT40_15,
+       ALL_TARGET_HT40_0_8_16,
+       ALL_TARGET_HT40_1_3_9_11_17_19,
+       ALL_TARGET_HT40_1_3_9_11_17_19,
+       ALL_TARGET_HT40_1_3_9_11_17_19,
+       ALL_TARGET_HT40_20,
+       ALL_TARGET_HT40_21,
+       ALL_TARGET_HT40_22,
+       ALL_TARGET_HT40_23,
+};
+
 /**
  * ar9003_hw_set_channel - set channel on single-chip device
  * @ah: atheros hardware structure
@@ -1799,6 +1879,100 @@ static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower)
                  ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14],  0));
 }
 
+static void ar9003_hw_init_txpower_cck(struct ath_hw *ah, u8 *rate_array)
+{
+       ah->tx_power[0] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+       ah->tx_power[1] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+       ah->tx_power[2] = min(rate_array[ALL_TARGET_LEGACY_1L_5L],
+                             rate_array[ALL_TARGET_LEGACY_5S]);
+       ah->tx_power[3] = min(rate_array[ALL_TARGET_LEGACY_11L],
+                             rate_array[ALL_TARGET_LEGACY_11S]);
+}
+
+static void ar9003_hw_init_txpower_ofdm(struct ath_hw *ah, u8 *rate_array,
+                                       int offset)
+{
+       int i, j;
+
+       for (i = offset; i < offset + AR9300_OFDM_RATES; i++) {
+               /* OFDM rate to power table idx */
+               j = ofdm2pwr[i - offset];
+               ah->tx_power[i] = rate_array[j];
+       }
+}
+
+static void ar9003_hw_init_txpower_ht(struct ath_hw *ah, u8 *rate_array,
+                                     int ss_offset, int ds_offset,
+                                     int ts_offset, bool is_40)
+{
+       int i, j, mcs_idx = 0;
+       const u8 *mcs2pwr = (is_40) ? mcs2pwr_ht40 : mcs2pwr_ht20;
+
+       for (i = ss_offset; i < ss_offset + AR9300_HT_SS_RATES; i++) {
+               j = mcs2pwr[mcs_idx];
+               ah->tx_power[i] = rate_array[j];
+               mcs_idx++;
+       }
+
+       for (i = ds_offset; i < ds_offset + AR9300_HT_DS_RATES; i++) {
+               j = mcs2pwr[mcs_idx];
+               ah->tx_power[i] = rate_array[j];
+               mcs_idx++;
+       }
+
+       for (i = ts_offset; i < ts_offset + AR9300_HT_TS_RATES; i++) {
+               j = mcs2pwr[mcs_idx];
+               ah->tx_power[i] = rate_array[j];
+               mcs_idx++;
+       }
+}
+
+static void ar9003_hw_init_txpower_stbc(struct ath_hw *ah, int ss_offset,
+                                       int ds_offset, int ts_offset)
+{
+       memcpy(&ah->tx_power_stbc[ss_offset], &ah->tx_power[ss_offset],
+              AR9300_HT_SS_RATES);
+       memcpy(&ah->tx_power_stbc[ds_offset], &ah->tx_power[ds_offset],
+              AR9300_HT_DS_RATES);
+       memcpy(&ah->tx_power_stbc[ts_offset], &ah->tx_power[ts_offset],
+              AR9300_HT_TS_RATES);
+}
+
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+                                struct ath9k_channel *chan)
+{
+       if (IS_CHAN_5GHZ(chan)) {
+               ar9003_hw_init_txpower_ofdm(ah, rate_array,
+                                           AR9300_11NA_OFDM_SHIFT);
+               if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+                       ar9003_hw_init_txpower_ht(ah, rate_array,
+                                                 AR9300_11NA_HT_SS_SHIFT,
+                                                 AR9300_11NA_HT_DS_SHIFT,
+                                                 AR9300_11NA_HT_TS_SHIFT,
+                                                 IS_CHAN_HT40(chan));
+                       ar9003_hw_init_txpower_stbc(ah,
+                                                   AR9300_11NA_HT_SS_SHIFT,
+                                                   AR9300_11NA_HT_DS_SHIFT,
+                                                   AR9300_11NA_HT_TS_SHIFT);
+               }
+       } else {
+               ar9003_hw_init_txpower_cck(ah, rate_array);
+               ar9003_hw_init_txpower_ofdm(ah, rate_array,
+                                           AR9300_11NG_OFDM_SHIFT);
+               if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+                       ar9003_hw_init_txpower_ht(ah, rate_array,
+                                                 AR9300_11NG_HT_SS_SHIFT,
+                                                 AR9300_11NG_HT_DS_SHIFT,
+                                                 AR9300_11NG_HT_TS_SHIFT,
+                                                 IS_CHAN_HT40(chan));
+                       ar9003_hw_init_txpower_stbc(ah,
+                                                   AR9300_11NG_HT_SS_SHIFT,
+                                                   AR9300_11NG_HT_DS_SHIFT,
+                                                   AR9300_11NG_HT_TS_SHIFT);
+               }
+       }
+}
+
 void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
index abe8bd6..1a9fe09 100644 (file)
@@ -189,6 +189,7 @@ struct ath_frame_info {
        u8 rtscts_rate;
        u8 retries : 7;
        u8 baw_tracked : 1;
+       u8 tx_power;
 };
 
 struct ath_rxbuf {
index ecb783b..cb366ad 100644 (file)
@@ -78,7 +78,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
        struct ath_tx_info info;
        struct ieee80211_supported_band *sband;
        u8 chainmask = ah->txchainmask;
-       u8 rate = 0;
+       u8 i, rate = 0;
 
        sband = &common->sbands[sc->cur_chandef.chan->band];
        rate = sband->bitrates[rateidx].hw_value;
@@ -88,7 +88,8 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
        memset(&info, 0, sizeof(info));
        info.pkt_len = skb->len + FCS_LEN;
        info.type = ATH9K_PKT_TYPE_BEACON;
-       info.txpower = MAX_RATE_POWER;
+       for (i = 0; i < 4; i++)
+               info.txpower[i] = MAX_RATE_POWER;
        info.keyix = ATH9K_TXKEYIX_INVALID;
        info.keytype = ATH9K_KEY_TYPE_CLEAR;
        info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK;
index 4cf9e0a..1cbd335 100644 (file)
 #define AH_WOW_BEACON_MISS             BIT(3)
 
 enum ath_hw_txq_subtype {
-       ATH_TXQ_AC_BE = 0,
-       ATH_TXQ_AC_BK = 1,
+       ATH_TXQ_AC_BK = 0,
+       ATH_TXQ_AC_BE = 1,
        ATH_TXQ_AC_VI = 2,
        ATH_TXQ_AC_VO = 3,
 };
@@ -940,6 +940,10 @@ struct ath_hw {
        const struct firmware *eeprom_blob;
 
        struct ath_dynack dynack;
+
+       bool tpc_enabled;
+       u8 tx_power[Ar5416RateSize];
+       u8 tx_power_stbc[Ar5416RateSize];
 };
 
 struct ath_bus_ops {
@@ -1080,6 +1084,8 @@ int ar9003_paprd_init_table(struct ath_hw *ah);
 bool ar9003_paprd_is_done(struct ath_hw *ah);
 bool ar9003_is_paprd_enabled(struct ath_hw *ah);
 void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+                                struct ath9k_channel *chan);
 
 /* Hardware family op attach helpers */
 int ar5008_hw_attach_phy_ops(struct ath_hw *ah);
index 59d679c..d1c3934 100644 (file)
@@ -532,10 +532,14 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        ah->reg_ops.read = ath9k_ioread32;
        ah->reg_ops.write = ath9k_iowrite32;
        ah->reg_ops.rmw = ath9k_reg_rmw;
-       sc->sc_ah = ah;
        pCap = &ah->caps;
 
        common = ath9k_hw_common(ah);
+
+       /* Will be cleared in ath9k_start() */
+       set_bit(ATH_OP_INVALID, &common->op_flags);
+
+       sc->sc_ah = ah;
        sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
        sc->tx99_power = MAX_RATE_POWER + 1;
        init_waitqueue_head(&sc->tx_wait);
@@ -896,9 +900,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
        common = ath9k_hw_common(ah);
        ath9k_set_hw_capab(sc, hw);
 
-       /* Will be cleared in ath9k_start() */
-       set_bit(ATH_OP_INVALID, &common->op_flags);
-
        /* Initialize regulatory */
        error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
                              ath9k_reg_notifier);
index 275205a..3e58bfa 100644 (file)
@@ -311,14 +311,7 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type,
                q = ATH9K_NUM_TX_QUEUES - 3;
                break;
        case ATH9K_TX_QUEUE_DATA:
-               for (q = 0; q < ATH9K_NUM_TX_QUEUES; q++)
-                       if (ah->txq[q].tqi_type ==
-                           ATH9K_TX_QUEUE_INACTIVE)
-                               break;
-               if (q == ATH9K_NUM_TX_QUEUES) {
-                       ath_err(common, "No available TX queue\n");
-                       return -1;
-               }
+               q = qinfo->tqi_subtype;
                break;
        default:
                ath_err(common, "Invalid TX queue type: %u\n", type);
index aa69cea..e55fa11 100644 (file)
@@ -704,7 +704,7 @@ struct ath_tx_info {
        enum ath9k_pkt_type type;
        enum ath9k_key_type keytype;
        u8 keyix;
-       u8 txpower;
+       u8 txpower[4];
 };
 
 struct ath_hw;
index ebbbfc7..9a72640 100644 (file)
@@ -512,16 +512,13 @@ irqreturn_t ath_isr(int irq, void *dev)
        if (!ah || test_bit(ATH_OP_INVALID, &common->op_flags))
                return IRQ_NONE;
 
-       /* shared irq, not for us */
+       if (!AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
+               return IRQ_NONE;
 
+       /* shared irq, not for us */
        if (!ath9k_hw_intrpend(ah))
                return IRQ_NONE;
 
-       if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
-               ath9k_hw_kill_interrupts(ah);
-               return IRQ_HANDLED;
-       }
-
        /*
         * Figure out the reason(s) for the interrupt.  Note
         * that the hal returns a pseudo-ISR that may include
@@ -532,6 +529,9 @@ irqreturn_t ath_isr(int irq, void *dev)
        ath9k_debug_sync_cause(sc, sync_cause);
        status &= ah->imask;    /* discard unasked-for bits */
 
+       if (AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
+               return IRQ_HANDLED;
+
        /*
         * If there are no status bits set, then this interrupt was not
         * for me (should have been caught above).
@@ -613,6 +613,7 @@ int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan)
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        int r;
 
+       ath9k_hw_kill_interrupts(sc->sc_ah);
        set_bit(ATH_OP_HW_RESET, &common->op_flags);
 
        ath9k_ps_wakeup(sc);
@@ -633,6 +634,7 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type)
 #ifdef CONFIG_ATH9K_DEBUGFS
        RESET_STAT_INC(sc, type);
 #endif
+       ath9k_hw_kill_interrupts(sc->sc_ah);
        set_bit(ATH_OP_HW_RESET, &common->op_flags);
        ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
 }
@@ -887,6 +889,9 @@ static void ath9k_stop(struct ieee80211_hw *hw)
                                                    &sc->cur_chan->chandef);
 
        ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
+
+       set_bit(ATH_OP_INVALID, &common->op_flags);
+
        ath9k_hw_phy_disable(ah);
 
        ath9k_hw_configpcipowersave(ah, true);
@@ -895,7 +900,6 @@ static void ath9k_stop(struct ieee80211_hw *hw)
 
        ath9k_ps_restore(sc);
 
-       set_bit(ATH_OP_INVALID, &common->op_flags);
        sc->ps_idle = prev_idle;
 
        mutex_unlock(&sc->mutex);
index ced36b4..fb11a91 100644 (file)
@@ -1724,6 +1724,8 @@ enum {
 #define AR_TPC_CTS_S           8
 #define AR_TPC_CHIRP           0x003f0000
 #define AR_TPC_CHIRP_S         16
+#define AR_TPC_RPT            0x3f000000
+#define AR_TPC_RPT_S          24
 
 #define AR_QUIET1          0x80fc
 #define AR_QUIET1_NEXT_QUIET_S         0
index d6e54a3..e9bd02c 100644 (file)
@@ -1096,6 +1096,37 @@ void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop)
        }
 }
 
+static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
+                              u8 rateidx)
+{
+       u8 max_power;
+       struct ath_hw *ah = sc->sc_ah;
+
+       if (sc->tx99_state)
+               return MAX_RATE_POWER;
+
+       if (!AR_SREV_9300_20_OR_LATER(ah)) {
+               /* ar9002 is not sipported for the moment */
+               return MAX_RATE_POWER;
+       }
+
+       if (!bf->bf_state.bfs_paprd) {
+               struct sk_buff *skb = bf->bf_mpdu;
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+               struct ath_frame_info *fi = get_frame_info(skb);
+
+               if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC))
+                       max_power = min(ah->tx_power_stbc[rateidx],
+                                       fi->tx_power);
+               else
+                       max_power = min(ah->tx_power[rateidx], fi->tx_power);
+       } else {
+               max_power = ah->paprd_training_power;
+       }
+
+       return max_power;
+}
+
 static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
                             struct ath_tx_info *info, int len, bool rts)
 {
@@ -1166,6 +1197,8 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
                                 is_40, is_sgi, is_sp);
                        if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
                                info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC;
+
+                       info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
                        continue;
                }
 
@@ -1193,6 +1226,8 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
 
                info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
                        phy, rate->bitrate * 100, len, rix, is_sp);
+
+               info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
        }
 
        /* For AR5416 - RTS cannot be followed by a frame larger than 8K */
@@ -1239,7 +1274,6 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
        memset(&info, 0, sizeof(info));
        info.is_first = true;
        info.is_last = true;
-       info.txpower = MAX_RATE_POWER;
        info.qcu = txq->axq_qnum;
 
        while (bf) {
@@ -2063,6 +2097,7 @@ static void setup_frame_info(struct ieee80211_hw *hw,
                fi->keyix = ATH9K_TXKEYIX_INVALID;
        fi->keytype = keytype;
        fi->framelen = framelen;
+       fi->tx_power = MAX_RATE_POWER;
 
        if (!rate)
                return;
index 650be79..cfd0554 100644 (file)
@@ -86,7 +86,7 @@ static const struct radar_detector_specs fcc_radar_ref_types[] = {
        FCC_PATTERN(1, 0, 5, 150, 230, 1, 23),
        FCC_PATTERN(2, 6, 10, 200, 500, 1, 16),
        FCC_PATTERN(3, 11, 20, 200, 500, 1, 12),
-       FCC_PATTERN(4, 50, 100, 1000, 2000, 20, 1),
+       FCC_PATTERN(4, 50, 100, 1000, 2000, 1, 20),
        FCC_PATTERN(5, 0, 1, 333, 333, 1, 9),
 };
 
@@ -105,7 +105,7 @@ static const struct radar_detector_specs jp_radar_ref_types[] = {
        JP_PATTERN(4, 0, 5, 150, 230, 1, 23),
        JP_PATTERN(5, 6, 10, 200, 500, 1, 16),
        JP_PATTERN(6, 11, 20, 200, 500, 1, 12),
-       JP_PATTERN(7, 50, 100, 1000, 2000, 20, 1),
+       JP_PATTERN(7, 50, 100, 1000, 2000, 1, 20),
        JP_PATTERN(5, 0, 1, 333, 333, 1, 9),
 };
 
index 0fc0b9f..38332a6 100644 (file)
@@ -798,7 +798,7 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 
        mutex_lock(&wil->mutex);
-       wil6210_disconnect(wil, params->mac, false);
+       wil6210_disconnect(wil, params->mac, params->reason_code, false);
        mutex_unlock(&wil->mutex);
 
        return 0;
index 8d99021..3249562 100644 (file)
@@ -32,6 +32,23 @@ void wil_err(struct wil6210_priv *wil, const char *fmt, ...)
        va_end(args);
 }
 
+void wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...)
+{
+       if (net_ratelimit()) {
+               struct net_device *ndev = wil_to_ndev(wil);
+               struct va_format vaf = {
+                       .fmt = fmt,
+               };
+               va_list args;
+
+               va_start(args, fmt);
+               vaf.va = &args;
+               netdev_err(ndev, "%pV", &vaf);
+               trace_wil6210_log_err(&vaf);
+               va_end(args);
+       }
+}
+
 void wil_info(struct wil6210_priv *wil, const char *fmt, ...)
 {
        struct net_device *ndev = wil_to_ndev(wil);
index 54a6ddc..4e6e145 100644 (file)
@@ -573,8 +573,10 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
        if (!frame)
                return -ENOMEM;
 
-       if (copy_from_user(frame, buf, len))
+       if (copy_from_user(frame, buf, len)) {
+               kfree(frame);
                return -EIO;
+       }
 
        params.buf = frame;
        params.len = len;
@@ -614,8 +616,10 @@ static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
                return -ENOMEM;
 
        rc = simple_write_to_buffer(wmi, len, ppos, buf, len);
-       if (rc < 0)
+       if (rc < 0) {
+               kfree(wmi);
                return rc;
+       }
 
        cmd = &wmi[1];
        cmdid = le16_to_cpu(wmi->id);
index 8c6f3b0..93c5cc1 100644 (file)
@@ -15,7 +15,6 @@
  */
 #include <linux/firmware.h>
 #include <linux/module.h>
-#include <linux/pci.h>
 #include <linux/crc32.h>
 #include "wil6210.h"
 #include "fw.h"
index 44cb71f..d4acf93 100644 (file)
@@ -446,7 +446,7 @@ static int wil_fw_load(struct wil6210_priv *wil, const void *data, size_t size)
                if (size >= sizeof(*hdr)) {
                        wil_err_fw(wil, "Stop at offset %ld"
                                   " record type %d [%zd bytes]\n",
-                                  (const void *)hdr - data,
+                                  (long)((const void *)hdr - data),
                                   le16_to_cpu(hdr->type), hdr_sz);
                }
                return -EINVAL;
@@ -471,7 +471,7 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name)
        size_t sz;
        const void *d;
 
-       rc = request_firmware(&fw, name, wil_to_pcie_dev(wil));
+       rc = request_firmware(&fw, name, wil_to_dev(wil));
        if (rc) {
                wil_err_fw(wil, "Failed to load firmware %s\n", name);
                return rc;
index 90f416f..4bcbd62 100644 (file)
@@ -36,7 +36,8 @@
  */
 
 #define WIL6210_IRQ_DISABLE    (0xFFFFFFFFUL)
-#define WIL6210_IMC_RX         BIT_DMA_EP_RX_ICR_RX_DONE
+#define WIL6210_IMC_RX         (BIT_DMA_EP_RX_ICR_RX_DONE | \
+                                BIT_DMA_EP_RX_ICR_RX_HTRSH)
 #define WIL6210_IMC_TX         (BIT_DMA_EP_TX_ICR_TX_DONE | \
                                BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
 #define WIL6210_IMC_MISC       (ISR_MISC_FW_READY | \
@@ -171,6 +172,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
        u32 isr = wil_ioread32_and_clear(wil->csr +
                                         HOSTADDR(RGF_DMA_EP_RX_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
+       bool need_unmask = true;
 
        trace_wil6210_irq_rx(isr);
        wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
@@ -182,12 +184,24 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
 
        wil6210_mask_irq_rx(wil);
 
-       if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
+       /* RX_DONE and RX_HTRSH interrupts are the same if interrupt
+        * moderation is not used. Interrupt moderation may cause RX
+        * buffer overflow while RX_DONE is delayed. The required
+        * action is always the same - should empty the accumulated
+        * packets from the RX ring.
+        */
+       if (isr & (BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH)) {
                wil_dbg_irq(wil, "RX done\n");
-               isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
+
+               if (isr & BIT_DMA_EP_RX_ICR_RX_HTRSH)
+                       wil_err_ratelimited(wil, "Received \"Rx buffer is in risk "
+                               "of overflow\" interrupt\n");
+
+               isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH);
                if (test_bit(wil_status_reset_done, &wil->status)) {
                        if (test_bit(wil_status_napi_en, &wil->status)) {
                                wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
+                               need_unmask = false;
                                napi_schedule(&wil->napi_rx);
                        } else {
                                wil_err(wil, "Got Rx interrupt while "
@@ -204,6 +218,10 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
        /* Rx IRQ will be enabled when NAPI processing finished */
 
        atomic_inc(&wil->isr_count_rx);
+
+       if (unlikely(need_unmask))
+               wil6210_unmask_irq_rx(wil);
+
        return IRQ_HANDLED;
 }
 
@@ -213,6 +231,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
        u32 isr = wil_ioread32_and_clear(wil->csr +
                                         HOSTADDR(RGF_DMA_EP_TX_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
+       bool need_unmask = true;
 
        trace_wil6210_irq_tx(isr);
        wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
@@ -231,6 +250,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
                isr &= ~(BIT(25) - 1UL);
                if (test_bit(wil_status_reset_done, &wil->status)) {
                        wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
+                       need_unmask = false;
                        napi_schedule(&wil->napi_tx);
                } else {
                        wil_err(wil, "Got Tx interrupt while in reset\n");
@@ -243,6 +263,10 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
        /* Tx IRQ will be enabled when NAPI processing finished */
 
        atomic_inc(&wil->isr_count_tx);
+
+       if (unlikely(need_unmask))
+               wil6210_unmask_irq_tx(wil);
+
        return IRQ_HANDLED;
 }
 
index 6212983..8ff3fe3 100644 (file)
@@ -67,6 +67,36 @@ static struct kernel_param_ops mtu_max_ops = {
 module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, S_IRUGO);
 MODULE_PARM_DESC(mtu_max, " Max MTU value.");
 
+static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
+static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
+
+static int ring_order_set(const char *val, const struct kernel_param *kp)
+{
+       int ret;
+       uint x;
+
+       ret = kstrtouint(val, 0, &x);
+       if (ret)
+               return ret;
+
+       if ((x < WIL_RING_SIZE_ORDER_MIN) || (x > WIL_RING_SIZE_ORDER_MAX))
+               return -EINVAL;
+
+       *((uint *)kp->arg) = x;
+
+       return 0;
+}
+
+static struct kernel_param_ops ring_order_ops = {
+       .set = ring_order_set,
+       .get = param_get_uint,
+};
+
+module_param_cb(rx_ring_order, &ring_order_ops, &rx_ring_order, S_IRUGO);
+MODULE_PARM_DESC(rx_ring_order, " Rx ring order; size = 1 << order");
+module_param_cb(tx_ring_order, &ring_order_ops, &tx_ring_order, S_IRUGO);
+MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order");
+
 #define RST_DELAY (20) /* msec, for loop in @wil_target_reset */
 #define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */
 
@@ -104,7 +134,7 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
 }
 
 static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
-                              bool from_event)
+                              u16 reason_code, bool from_event)
 {
        uint i;
        struct net_device *ndev = wil_to_ndev(wil);
@@ -117,8 +147,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
        sta->data_port_open = false;
        if (sta->status != wil_sta_unused) {
                if (!from_event)
-                       wmi_disconnect_sta(wil, sta->addr,
-                                          WLAN_REASON_DEAUTH_LEAVING);
+                       wmi_disconnect_sta(wil, sta->addr, reason_code);
 
                switch (wdev->iftype) {
                case NL80211_IFTYPE_AP:
@@ -152,7 +181,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
 }
 
 static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
-                               bool from_event)
+                               u16 reason_code, bool from_event)
 {
        int cid = -ENOENT;
        struct net_device *ndev = wil_to_ndev(wil);
@@ -167,10 +196,10 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
        }
 
        if (cid >= 0) /* disconnect 1 peer */
-               wil_disconnect_cid(wil, cid, from_event);
+               wil_disconnect_cid(wil, cid, reason_code, from_event);
        else /* disconnect all */
                for (cid = 0; cid < WIL6210_MAX_CID; cid++)
-                       wil_disconnect_cid(wil, cid, from_event);
+                       wil_disconnect_cid(wil, cid, reason_code, from_event);
 
        /* link state */
        switch (wdev->iftype) {
@@ -179,8 +208,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
                wil_link_off(wil);
                if (test_bit(wil_status_fwconnected, &wil->status)) {
                        clear_bit(wil_status_fwconnected, &wil->status);
-                       cfg80211_disconnected(ndev,
-                                             WLAN_STATUS_UNSPECIFIED_FAILURE,
+                       cfg80211_disconnected(ndev, reason_code,
                                              NULL, 0, GFP_KERNEL);
                } else if (test_bit(wil_status_fwconnecting, &wil->status)) {
                        cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
@@ -200,7 +228,7 @@ static void wil_disconnect_worker(struct work_struct *work)
                        struct wil6210_priv, disconnect_worker);
 
        mutex_lock(&wil->mutex);
-       _wil6210_disconnect(wil, NULL, false);
+       _wil6210_disconnect(wil, NULL, WLAN_REASON_UNSPECIFIED, false);
        mutex_unlock(&wil->mutex);
 }
 
@@ -222,6 +250,7 @@ static void wil_scan_timer_fn(ulong x)
 
        clear_bit(wil_status_fwready, &wil->status);
        wil_err(wil, "Scan timeout detected, start fw error recovery\n");
+       wil->recovery_state = fw_recovery_pending;
        schedule_work(&wil->fw_error_worker);
 }
 
@@ -333,7 +362,7 @@ static void wil_connect_worker(struct work_struct *work)
 
        wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
 
-       rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0);
+       rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
        wil->pending_connect_cid = -1;
        if (rc == 0) {
                wil->sta[cid].status = wil_sta_connected;
@@ -392,18 +421,19 @@ int wil_priv_init(struct wil6210_priv *wil)
  * wil6210_disconnect - disconnect one connection
  * @wil: driver context
  * @bssid: peer to disconnect, NULL to disconnect all
+ * @reason_code: Reason code for the Disassociation frame
  * @from_event: whether is invoked from FW event handler
  *
  * Disconnect and release associated resources. If invoked not from the
  * FW event handler, issue WMI command(s) to trigger MAC disconnect.
  */
 void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
-                       bool from_event)
+                       u16 reason_code, bool from_event)
 {
        wil_dbg_misc(wil, "%s()\n", __func__);
 
        del_timer_sync(&wil->connect_timer);
-       _wil6210_disconnect(wil, bssid, from_event);
+       _wil6210_disconnect(wil, bssid, reason_code, from_event);
 }
 
 void wil_priv_deinit(struct wil6210_priv *wil)
@@ -415,7 +445,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
        cancel_work_sync(&wil->disconnect_worker);
        cancel_work_sync(&wil->fw_error_worker);
        mutex_lock(&wil->mutex);
-       wil6210_disconnect(wil, NULL, false);
+       wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
        mutex_unlock(&wil->mutex);
        wmi_event_flush(wil);
        destroy_workqueue(wil->wmi_wq_conn);
@@ -463,6 +493,9 @@ static int wil_target_reset(struct wil6210_priv *wil)
 
        wil_halt_cpu(wil);
 
+       /* Clear Fw Download notification */
+       C(RGF_USER_USAGE_6, BIT(0));
+
        if (is_sparrow) {
                S(RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN);
                /* XTAL stabilization should take about 3ms */
@@ -600,7 +633,7 @@ int wil_reset(struct wil6210_priv *wil)
        WARN_ON(test_bit(wil_status_napi_en, &wil->status));
 
        cancel_work_sync(&wil->disconnect_worker);
-       wil6210_disconnect(wil, NULL, false);
+       wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
 
        wil->status = 0; /* prevent NAPI from being scheduled */
 
@@ -705,7 +738,7 @@ int __wil_up(struct wil6210_priv *wil)
                return rc;
 
        /* Rx VRING. After MAC and beacon */
-       rc = wil_rx_init(wil);
+       rc = wil_rx_init(wil, 1 << rx_ring_order);
        if (rc)
                return rc;
 
index c680906..e3f8bdc 100644 (file)
@@ -210,8 +210,6 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
        struct vring_rx_desc dd, *d = &dd;
        volatile struct vring_rx_desc *_d = &vring->va[i].rx;
        dma_addr_t pa;
-
-       /* TODO align */
        struct sk_buff *skb = dev_alloc_skb(sz + headroom);
 
        if (unlikely(!skb))
@@ -596,7 +594,7 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota)
        wil_rx_refill(wil, v->size);
 }
 
-int wil_rx_init(struct wil6210_priv *wil)
+int wil_rx_init(struct wil6210_priv *wil, u16 size)
 {
        struct vring *vring = &wil->vring_rx;
        int rc;
@@ -608,7 +606,7 @@ int wil_rx_init(struct wil6210_priv *wil)
                return -EINVAL;
        }
 
-       vring->size = WIL6210_RX_RING_SIZE;
+       vring->size = size;
        rc = wil_vring_alloc(wil, vring);
        if (rc)
                return rc;
@@ -928,8 +926,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        wil_dbg_txrx(wil, "%s()\n", __func__);
 
        if (avail < 1 + nr_frags) {
-               wil_err(wil, "Tx ring full. No space for %d fragments\n",
-                       1 + nr_frags);
+               wil_err_ratelimited(wil,
+                                   "Tx ring full. No space for %d fragments\n",
+                                   1 + nr_frags);
                return -ENOMEM;
        }
        _d = &vring->va[i].tx;
index 95d3a06..c6ec5b9 100644 (file)
@@ -49,8 +49,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
 
 #define WIL6210_MEM_SIZE (2*1024*1024UL)
 
-#define WIL6210_RX_RING_SIZE   (128)
-#define WIL6210_TX_RING_SIZE   (512)
+#define WIL_RX_RING_SIZE_ORDER_DEFAULT (9)
+#define WIL_TX_RING_SIZE_ORDER_DEFAULT (9)
+/* limit ring size in range [32..32k] */
+#define WIL_RING_SIZE_ORDER_MIN        (5)
+#define WIL_RING_SIZE_ORDER_MAX        (15)
 #define WIL6210_MAX_TX_RINGS   (24) /* HW limit */
 #define WIL6210_MAX_CID                (8) /* HW limit */
 #define WIL6210_NAPI_BUDGET    (16) /* arbitrary */
@@ -126,6 +129,7 @@ struct RGF_ICR {
        #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n)  BIT(n+1) /* n = [0..23] */
 #define RGF_DMA_EP_RX_ICR              (0x881bd0) /* struct RGF_ICR */
        #define BIT_DMA_EP_RX_ICR_RX_DONE       BIT(0)
+       #define BIT_DMA_EP_RX_ICR_RX_HTRSH      BIT(1)
 #define RGF_DMA_EP_MISC_ICR            (0x881bec) /* struct RGF_ICR */
        #define BIT_DMA_EP_MISC_ICR_RX_HTRSH    BIT(0)
        #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT   BIT(1)
@@ -468,13 +472,14 @@ struct wil6210_priv {
 #define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
 #define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
 #define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
-#define wil_to_pcie_dev(i) (&i->pdev->dev)
 
 __printf(2, 3)
 void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
 __printf(2, 3)
 void wil_err(struct wil6210_priv *wil, const char *fmt, ...);
 __printf(2, 3)
+void wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...);
+__printf(2, 3)
 void wil_info(struct wil6210_priv *wil, const char *fmt, ...);
 #define wil_dbg(wil, fmt, arg...) do { \
        netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
@@ -586,9 +591,9 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
 int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);
 int wmi_pcp_stop(struct wil6210_priv *wil);
 void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
-                       bool from_event);
+                       u16 reason_code, bool from_event);
 
-int wil_rx_init(struct wil6210_priv *wil);
+int wil_rx_init(struct wil6210_priv *wil, u16 size);
 void wil_rx_fini(struct wil6210_priv *wil);
 
 /* TX API */
index bb1e066..63476c8 100644 (file)
@@ -478,15 +478,15 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
                               void *d, int len)
 {
        struct wmi_disconnect_event *evt = d;
+       u16 reason_code = le16_to_cpu(evt->protocol_reason_status);
 
-       wil_dbg_wmi(wil, "Disconnect %pM reason %d proto %d wmi\n",
-                   evt->bssid,
-                   evt->protocol_reason_status, evt->disconnect_reason);
+       wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
+                   evt->bssid, reason_code, evt->disconnect_reason);
 
        wil->sinfo_gen++;
 
        mutex_lock(&wil->mutex);
-       wil6210_disconnect(wil, evt->bssid, true);
+       wil6210_disconnect(wil, evt->bssid, reason_code, true);
        mutex_unlock(&wil->mutex);
 }
 
index f8a9dfa..3aecc5f 100644 (file)
@@ -520,6 +520,95 @@ brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
                                                ADDR_INDIRECT);
 }
 
+static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
+{
+       struct brcmf_mbss_ssid_le mbss_ssid_le;
+       int bsscfgidx;
+       int err;
+
+       memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
+       bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
+       if (bsscfgidx < 0)
+               return bsscfgidx;
+
+       mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
+       mbss_ssid_le.SSID_len = cpu_to_le32(5);
+       sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
+
+       err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
+                                       sizeof(mbss_ssid_le));
+       if (err < 0)
+               brcmf_err("setting ssid failed %d\n", err);
+
+       return err;
+}
+
+/**
+ * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
+ *
+ * @wiphy: wiphy device of new interface.
+ * @name: name of the new interface.
+ * @flags: not used.
+ * @params: contains mac address for AP device.
+ */
+static
+struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
+                                     u32 *flags, struct vif_params *params)
+{
+       struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+       struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+       struct brcmf_cfg80211_vif *vif;
+       int err;
+
+       if (brcmf_cfg80211_vif_event_armed(cfg))
+               return ERR_PTR(-EBUSY);
+
+       brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
+
+       vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
+       if (IS_ERR(vif))
+               return (struct wireless_dev *)vif;
+
+       brcmf_cfg80211_arm_vif_event(cfg, vif);
+
+       err = brcmf_cfg80211_request_ap_if(ifp);
+       if (err) {
+               brcmf_cfg80211_arm_vif_event(cfg, NULL);
+               goto fail;
+       }
+
+       /* wait for firmware event */
+       err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
+                                                   msecs_to_jiffies(1500));
+       brcmf_cfg80211_arm_vif_event(cfg, NULL);
+       if (!err) {
+               brcmf_err("timeout occurred\n");
+               err = -EIO;
+               goto fail;
+       }
+
+       /* interface created in firmware */
+       ifp = vif->ifp;
+       if (!ifp) {
+               brcmf_err("no if pointer provided\n");
+               err = -ENOENT;
+               goto fail;
+       }
+
+       strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
+       err = brcmf_net_attach(ifp, true);
+       if (err) {
+               brcmf_err("Registering netdevice failed\n");
+               goto fail;
+       }
+
+       return &ifp->vif->wdev;
+
+fail:
+       brcmf_free_vif(vif);
+       return ERR_PTR(err);
+}
+
 static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
 {
        enum nl80211_iftype iftype;
@@ -545,12 +634,16 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
        switch (type) {
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_STATION:
-       case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_WDS:
        case NL80211_IFTYPE_MONITOR:
        case NL80211_IFTYPE_MESH_POINT:
                return ERR_PTR(-EOPNOTSUPP);
+       case NL80211_IFTYPE_AP:
+               wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
+               if (!IS_ERR(wdev))
+                       brcmf_cfg80211_update_proto_addr_mode(wdev);
+               return wdev;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_P2P_DEVICE:
@@ -1815,6 +1908,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
                return -EIO;
 
        clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
+       clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
        cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL);
 
        memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
@@ -2932,7 +3026,7 @@ brcmf_update_pmklist(struct net_device *ndev,
                     struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
 {
        int i, j;
-       int pmkid_len;
+       u32 pmkid_len;
 
        pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
 
@@ -2960,8 +3054,7 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
        struct brcmf_if *ifp = netdev_priv(ndev);
        struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
        s32 err = 0;
-       int i;
-       int pmkid_len;
+       u32 pmkid_len, i;
 
        brcmf_dbg(TRACE, "Enter\n");
        if (!check_vif_up(ifp->vif))
@@ -3000,7 +3093,7 @@ brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
        struct brcmf_if *ifp = netdev_priv(ndev);
        struct pmkid_list pmkid;
        s32 err = 0;
-       int i, pmkid_len;
+       u32 pmkid_len, i;
 
        brcmf_dbg(TRACE, "Enter\n");
        if (!check_vif_up(ifp->vif))
@@ -3361,11 +3454,10 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
 }
 
 static s32
-brcmf_configure_wpaie(struct net_device *ndev,
+brcmf_configure_wpaie(struct brcmf_if *ifp,
                      const struct brcmf_vs_tlv *wpa_ie,
                      bool is_rsn_ie)
 {
-       struct brcmf_if *ifp = netdev_priv(ndev);
        u32 auth = 0; /* d11 open authentication */
        u16 count;
        s32 err = 0;
@@ -3840,6 +3932,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
        enum nl80211_iftype dev_role;
        struct brcmf_fil_bss_enable_le bss_enable;
        u16 chanspec;
+       bool mbss;
 
        brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
                  settings->chandef.chan->hw_value,
@@ -3850,6 +3943,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
                  settings->inactivity_timeout);
 
        dev_role = ifp->vif->wdev.iftype;
+       mbss = ifp->vif->mbss;
 
        memset(&ssid_le, 0, sizeof(ssid_le));
        if (settings->ssid == NULL || settings->ssid_len == 0) {
@@ -3869,8 +3963,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
                ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
        }
 
-       brcmf_set_mpc(ifp, 0);
-       brcmf_configure_arp_offload(ifp, false);
+       if (!mbss) {
+               brcmf_set_mpc(ifp, 0);
+               brcmf_configure_arp_offload(ifp, false);
+       }
 
        /* find the RSN_IE */
        rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
@@ -3884,13 +3980,16 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
                brcmf_dbg(TRACE, "WPA(2) IE is found\n");
                if (wpa_ie != NULL) {
                        /* WPA IE */
-                       err = brcmf_configure_wpaie(ndev, wpa_ie, false);
+                       err = brcmf_configure_wpaie(ifp, wpa_ie, false);
                        if (err < 0)
                                goto exit;
                } else {
+                       struct brcmf_vs_tlv *tmp_ie;
+
+                       tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
+
                        /* RSN IE */
-                       err = brcmf_configure_wpaie(ndev,
-                               (struct brcmf_vs_tlv *)rsn_ie, true);
+                       err = brcmf_configure_wpaie(ifp, tmp_ie, true);
                        if (err < 0)
                                goto exit;
                }
@@ -3901,45 +4000,53 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
 
        brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
 
-       chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
-       err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
-       if (err < 0) {
-               brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err);
-               goto exit;
-       }
-
-       if (settings->beacon_interval) {
-               err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
-                                           settings->beacon_interval);
+       if (!mbss) {
+               chanspec = chandef_to_chanspec(&cfg->d11inf,
+                                              &settings->chandef);
+               err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
                if (err < 0) {
-                       brcmf_err("Beacon Interval Set Error, %d\n", err);
+                       brcmf_err("Set Channel failed: chspec=%d, %d\n",
+                                 chanspec, err);
                        goto exit;
                }
-       }
-       if (settings->dtim_period) {
-               err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
-                                           settings->dtim_period);
-               if (err < 0) {
-                       brcmf_err("DTIM Interval Set Error, %d\n", err);
-                       goto exit;
+
+               if (settings->beacon_interval) {
+                       err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
+                                                   settings->beacon_interval);
+                       if (err < 0) {
+                               brcmf_err("Beacon Interval Set Error, %d\n",
+                                         err);
+                               goto exit;
+                       }
+               }
+               if (settings->dtim_period) {
+                       err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
+                                                   settings->dtim_period);
+                       if (err < 0) {
+                               brcmf_err("DTIM Interval Set Error, %d\n", err);
+                               goto exit;
+                       }
                }
-       }
 
-       if (dev_role == NL80211_IFTYPE_AP) {
-               err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+               if (dev_role == NL80211_IFTYPE_AP) {
+                       err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+                       if (err < 0) {
+                               brcmf_err("BRCMF_C_DOWN error %d\n", err);
+                               goto exit;
+                       }
+                       brcmf_fil_iovar_int_set(ifp, "apsta", 0);
+               }
+
+               err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
                if (err < 0) {
-                       brcmf_err("BRCMF_C_DOWN error %d\n", err);
+                       brcmf_err("SET INFRA error %d\n", err);
                        goto exit;
                }
-               brcmf_fil_iovar_int_set(ifp, "apsta", 0);
-       }
-
-       err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
-       if (err < 0) {
-               brcmf_err("SET INFRA error %d\n", err);
-               goto exit;
        }
        if (dev_role == NL80211_IFTYPE_AP) {
+               if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
+                       brcmf_fil_iovar_int_set(ifp, "mbss", 1);
+
                err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
                if (err < 0) {
                        brcmf_err("setting AP mode failed %d\n", err);
@@ -3984,7 +4091,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
        set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
 
 exit:
-       if (err) {
+       if ((err) && (!mbss)) {
                brcmf_set_mpc(ifp, 1);
                brcmf_configure_arp_offload(ifp, true);
        }
@@ -4005,20 +4112,31 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
                /* first to make sure they get processed by fw. */
                msleep(400);
 
+               if (ifp->vif->mbss) {
+                       err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+                       return err;
+               }
+
                memset(&join_params, 0, sizeof(join_params));
                err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
                                             &join_params, sizeof(join_params));
                if (err < 0)
                        brcmf_err("SET SSID error (%d)\n", err);
-               err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
+               err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
                if (err < 0)
-                       brcmf_err("BRCMF_C_UP error %d\n", err);
+                       brcmf_err("BRCMF_C_DOWN error %d\n", err);
                err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
                if (err < 0)
                        brcmf_err("setting AP mode failed %d\n", err);
                err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
                if (err < 0)
                        brcmf_err("setting INFRA mode failed %d\n", err);
+               if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
+                       brcmf_fil_iovar_int_set(ifp, "mbss", 0);
+               /* Bring device back up so it can be used again */
+               err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
+               if (err < 0)
+                       brcmf_err("BRCMF_C_UP error %d\n", err);
        } else {
                bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
                bss_enable.enable = cpu_to_le32(0);
@@ -4370,7 +4488,9 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
                                           enum nl80211_iftype type,
                                           bool pm_block)
 {
+       struct brcmf_cfg80211_vif *vif_walk;
        struct brcmf_cfg80211_vif *vif;
+       bool mbss;
 
        brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
                  sizeof(*vif));
@@ -4386,6 +4506,17 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
 
        brcmf_init_prof(&vif->profile);
 
+       if (type == NL80211_IFTYPE_AP) {
+               mbss = false;
+               list_for_each_entry(vif_walk, &cfg->vif_list, list) {
+                       if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
+                               mbss = true;
+                               break;
+                       }
+               }
+               vif->mbss = mbss;
+       }
+
        list_add_tail(&vif->list, &cfg->vif_list);
        return vif;
 }
@@ -4628,6 +4759,7 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
                               struct net_device *ndev,
                               const struct brcmf_event_msg *e, void *data)
 {
+       struct brcmf_if *ifp = netdev_priv(ndev);
        static int generation;
        u32 event = e->event_code;
        u32 reason = e->reason;
@@ -4638,6 +4770,8 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
            ndev != cfg_to_ndev(cfg)) {
                brcmf_dbg(CONN, "AP mode link down\n");
                complete(&cfg->vif_disabled);
+               if (ifp->vif->mbss)
+                       brcmf_remove_interface(ifp->drvr, ifp->bssidx);
                return 0;
        }
 
@@ -5429,7 +5563,28 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy)
        return 0;
 }
 
-static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
+static const struct ieee80211_iface_limit brcmf_iface_limits_mbss[] = {
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_STATION) |
+                        BIT(NL80211_IFTYPE_ADHOC)
+       },
+       {
+               .max = 4,
+               .types = BIT(NL80211_IFTYPE_AP)
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                        BIT(NL80211_IFTYPE_P2P_GO)
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
+       }
+};
+
+static const struct ieee80211_iface_limit brcmf_iface_limits_sbss[] = {
        {
                .max = 2,
                .types = BIT(NL80211_IFTYPE_STATION) |
@@ -5450,8 +5605,8 @@ static struct ieee80211_iface_combination brcmf_iface_combos[] = {
        {
                 .max_interfaces = BRCMF_IFACE_MAX_CNT,
                 .num_different_channels = 1,
-                .n_limits = ARRAY_SIZE(brcmf_iface_limits),
-                .limits = brcmf_iface_limits
+                .n_limits = ARRAY_SIZE(brcmf_iface_limits_sbss),
+                .limits = brcmf_iface_limits_sbss,
        }
 };
 
@@ -5527,6 +5682,10 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
        ifc_combo = brcmf_iface_combos[0];
        if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
                ifc_combo.num_different_channels = 2;
+       if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
+               ifc_combo.n_limits = ARRAY_SIZE(brcmf_iface_limits_mbss),
+               ifc_combo.limits = brcmf_iface_limits_mbss;
+       }
        wiphy->iface_combinations = kmemdup(&ifc_combo,
                                            sizeof(ifc_combo),
                                            GFP_KERNEL);
index 2a5b22c..9e98b8d 100644 (file)
@@ -183,6 +183,7 @@ struct vif_saved_ie {
  * @pm_block: power-management blocked.
  * @list: linked list.
  * @mgmt_rx_reg: registered rx mgmt frame types.
+ * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
  */
 struct brcmf_cfg80211_vif {
        struct brcmf_if *ifp;
@@ -194,6 +195,7 @@ struct brcmf_cfg80211_vif {
        struct vif_saved_ie saved_ie;
        struct list_head list;
        u16 mgmt_rx_reg;
+       bool mbss;
 };
 
 /* association inform */
index f407665..effe6d7 100644 (file)
@@ -836,7 +836,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
        return ifp;
 }
 
-void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
+static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
 {
        struct brcmf_if *ifp;
 
@@ -869,6 +869,38 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
        }
 }
 
+void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx)
+{
+       if (drvr->iflist[bssidx]) {
+               brcmf_fws_del_interface(drvr->iflist[bssidx]);
+               brcmf_del_if(drvr, bssidx);
+       }
+}
+
+int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
+{
+       int ifidx;
+       int bsscfgidx;
+       bool available;
+       int highest;
+
+       available = false;
+       bsscfgidx = 2;
+       highest = 2;
+       for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
+               if (drvr->iflist[ifidx]) {
+                       if (drvr->iflist[ifidx]->bssidx == bsscfgidx)
+                               bsscfgidx = highest + 1;
+                       else if (drvr->iflist[ifidx]->bssidx > highest)
+                               highest = drvr->iflist[ifidx]->bssidx;
+               } else {
+                       available = true;
+               }
+       }
+
+       return available ? bsscfgidx : -ENOMEM;
+}
+
 int brcmf_attach(struct device *dev)
 {
        struct brcmf_pub *drvr = NULL;
@@ -1033,10 +1065,7 @@ void brcmf_detach(struct device *dev)
 
        /* make sure primary interface removed last */
        for (i = BRCMF_MAX_IFS-1; i > -1; i--)
-               if (drvr->iflist[i]) {
-                       brcmf_fws_del_interface(drvr->iflist[i]);
-                       brcmf_del_if(drvr, i);
-               }
+               brcmf_remove_interface(drvr, i);
 
        brcmf_cfg80211_detach(drvr->config);
 
index 98228e9..23f74b1 100644 (file)
@@ -175,7 +175,8 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
 int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
 struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
                              char *name, u8 *mac_addr);
-void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx);
+void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx);
+int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state);
 void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
index 931f68a..defb7a4 100644 (file)
@@ -97,6 +97,28 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
        }
 }
 
+/**
+ * brcmf_feat_iovar_int_set() - determine feature through iovar set.
+ *
+ * @ifp: interface to query.
+ * @id: feature id.
+ * @name: iovar name.
+ */
+static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp,
+                                    enum brcmf_feat_id id, char *name, u32 val)
+{
+       int err;
+
+       err = brcmf_fil_iovar_int_set(ifp, name, val);
+       if (err == 0) {
+               brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
+               ifp->drvr->feat_flags |= BIT(id);
+       } else {
+               brcmf_dbg(TRACE, "%s feature check failed: %d\n",
+                         brcmf_feat_names[id], err);
+       }
+}
+
 void brcmf_feat_attach(struct brcmf_pub *drvr)
 {
        struct brcmf_if *ifp = drvr->iflist[0];
@@ -104,6 +126,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
        brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
        if (drvr->bus_if->wowl_supported)
                brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
+       brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
 
        /* set chip related quirks */
        switch (drvr->bus_if->chip) {
index b9a796d..f5832e0 100644 (file)
@@ -22,6 +22,7 @@
  * MCHAN: multi-channel for concurrent P2P.
  */
 #define BRCMF_FEAT_LIST \
+       BRCMF_FEAT_DEF(MBSS) \
        BRCMF_FEAT_DEF(MCHAN) \
        BRCMF_FEAT_DEF(WOWL)
 /*
index 7338b33..ec62492 100644 (file)
@@ -221,10 +221,8 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
 
        err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
 
-       if (ifp && ifevent->action == BRCMF_E_IF_DEL) {
-               brcmf_fws_del_interface(ifp);
-               brcmf_del_if(drvr, ifevent->bssidx);
-       }
+       if (ifp && ifevent->action == BRCMF_E_IF_DEL)
+               brcmf_remove_interface(drvr, ifevent->bssidx);
 }
 
 /**
index 51f88c1..03f2c40 100644 (file)
@@ -136,7 +136,7 @@ brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
 
        mutex_lock(&ifp->drvr->proto_block);
 
-       brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len);
+       brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
@@ -154,7 +154,7 @@ brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
        mutex_lock(&ifp->drvr->proto_block);
        err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
 
-       brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len);
+       brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
@@ -171,7 +171,7 @@ brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data)
        __le32 data_le = cpu_to_le32(data);
 
        mutex_lock(&ifp->drvr->proto_block);
-       brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, data);
+       brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data);
        err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true);
        mutex_unlock(&ifp->drvr->proto_block);
 
@@ -188,7 +188,7 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data)
        err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false);
        mutex_unlock(&ifp->drvr->proto_block);
        *data = le32_to_cpu(data_le);
-       brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, *data);
+       brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data);
 
        return err;
 }
@@ -224,7 +224,7 @@ brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
 
        mutex_lock(&drvr->proto_block);
 
-       brcmf_dbg(FIL, "name=%s, len=%d\n", name, len);
+       brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
@@ -264,7 +264,7 @@ brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
                brcmf_err("Creating iovar failed\n");
        }
 
-       brcmf_dbg(FIL, "name=%s, len=%d\n", name, len);
+       brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
@@ -347,7 +347,8 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name,
 
        mutex_lock(&drvr->proto_block);
 
-       brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len);
+       brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx,
+                 ifp->bssidx, name, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
@@ -386,7 +387,8 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
                err = -EPERM;
                brcmf_err("Creating bsscfg failed\n");
        }
-       brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len);
+       brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx,
+                 ifp->bssidx, name, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
                           min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
index ba64b29..50891c0 100644 (file)
@@ -519,4 +519,10 @@ struct brcmf_fil_wowl_pattern_le {
        /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */
 };
 
+struct brcmf_mbss_ssid_le {
+       __le32  bsscfgidx;
+       __le32  SSID_len;
+       unsigned char SSID[32];
+};
+
 #endif /* FWIL_TYPES_H_ */
index 9f783db..456944a 100644 (file)
@@ -1080,8 +1080,17 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb,
 {
        struct brcmf_if *ifp;
 
+       /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+        * events this is easy because it contains the bssidx which maps
+        * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
+        * bssidx 1 is used for p2p0 and no data can be received or
+        * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
+        */
+       if (ifidx)
+               (ifidx)++;
        ifp = msgbuf->drvr->iflist[ifidx];
        if (!ifp || !ifp->ndev) {
+               brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
                brcmu_pkt_buf_free_skb(skb);
                return;
        }
@@ -1354,6 +1363,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
        }
        INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker);
        count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings);
+       count = count * sizeof(unsigned long);
        msgbuf->flow_map = kzalloc(count, GFP_KERNEL);
        if (!msgbuf->flow_map)
                goto fail;
index 138691a..905991f 100644 (file)
@@ -798,12 +798,14 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo)
        brcmf_dbg(PCIE, "Enter\n");
        /* is it a v1 or v2 implementation */
        devinfo->irq_requested = false;
+       pci_enable_msi(pdev);
        if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
                if (request_threaded_irq(pdev->irq,
                                         brcmf_pcie_quick_check_isr_v1,
                                         brcmf_pcie_isr_thread_v1,
                                         IRQF_SHARED, "brcmf_pcie_intr",
                                         devinfo)) {
+                       pci_disable_msi(pdev);
                        brcmf_err("Failed to request IRQ %d\n", pdev->irq);
                        return -EIO;
                }
@@ -813,6 +815,7 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo)
                                         brcmf_pcie_isr_thread_v2,
                                         IRQF_SHARED, "brcmf_pcie_intr",
                                         devinfo)) {
+                       pci_disable_msi(pdev);
                        brcmf_err("Failed to request IRQ %d\n", pdev->irq);
                        return -EIO;
                }
@@ -839,6 +842,7 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo)
                return;
        devinfo->irq_requested = false;
        free_irq(pdev->irq, devinfo);
+       pci_disable_msi(pdev);
 
        msleep(50);
        count = 0;
@@ -1857,6 +1861,8 @@ static struct pci_device_id brcmf_pcie_devid_table[] = {
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
        BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID),
+       BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID),
        { /* end: all zeroes */ }
 };
 
index 222f26a..50cdf70 100644 (file)
@@ -31,8 +31,8 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
                                                 struct wireless_dev *wdev,
                                                 const void *data, int len)
 {
-       struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
-       struct net_device *ndev = cfg_to_ndev(cfg);
+       struct brcmf_cfg80211_vif *vif;
+       struct brcmf_if *ifp;
        const struct brcmf_vndr_dcmd_hdr *cmdhdr = data;
        struct sk_buff *reply;
        int ret, payload, ret_len;
@@ -42,6 +42,9 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
        brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set,
                  cmdhdr->len);
 
+       vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+       ifp = vif->ifp;
+
        len -= sizeof(struct brcmf_vndr_dcmd_hdr);
        ret_len = cmdhdr->len;
        if (ret_len > 0 || len > 0) {
@@ -63,11 +66,11 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
        }
 
        if (cmdhdr->set)
-               ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd,
-                                            dcmd_buf, ret_len);
+               ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf,
+                                            ret_len);
        else
-               ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd,
-                                            dcmd_buf, ret_len);
+               ret = brcmf_fil_cmd_data_get(ifp, cmdhdr->cmd, dcmd_buf,
+                                            ret_len);
        if (ret != 0)
                goto exit;
 
index 19740c1..c9a8b93 100644 (file)
@@ -30,6 +30,7 @@
 #include "main.h"
 #include "debug.h"
 #include "brcms_trace_events.h"
+#include "phy/phy_int.h"
 
 static struct dentry *root_folder;
 
@@ -74,20 +75,33 @@ static
 int brcms_debugfs_hardware_read(struct seq_file *s, void *data)
 {
        struct brcms_pub *drvr = s->private;
+       struct brcms_hardware *hw = drvr->wlc->hw;
+       struct bcma_device *core = hw->d11core;
+       struct bcma_bus *bus = core->bus;
+       char boardrev[10];
 
-       seq_printf(s, "board vendor: %x\n"
-                  "board type: %x\n"
-                  "board revision: %x\n"
-                  "board flags: %x\n"
-                  "board flags2: %x\n"
-                  "firmware revision: %x\n",
-                  drvr->wlc->hw->d11core->bus->boardinfo.vendor,
-                  drvr->wlc->hw->d11core->bus->boardinfo.type,
-                  drvr->wlc->hw->boardrev,
-                  drvr->wlc->hw->boardflags,
-                  drvr->wlc->hw->boardflags2,
-                  drvr->wlc->ucode_rev);
-
+       seq_printf(s, "chipnum 0x%x\n"
+                  "chiprev 0x%x\n"
+                  "chippackage 0x%x\n"
+                  "corerev 0x%x\n"
+                  "boardid 0x%x\n"
+                  "boardvendor 0x%x\n"
+                  "boardrev %s\n"
+                  "boardflags 0x%x\n"
+                  "boardflags2 0x%x\n"
+                  "ucoderev 0x%x\n"
+                  "radiorev 0x%x\n"
+                  "phytype 0x%x\n"
+                  "phyrev 0x%x\n"
+                  "anarev 0x%x\n"
+                  "nvramrev %d\n",
+                  bus->chipinfo.id, bus->chipinfo.rev, bus->chipinfo.pkg,
+                  core->id.rev, bus->boardinfo.type, bus->boardinfo.vendor,
+                  brcmu_boardrev_str(hw->boardrev, boardrev),
+                  drvr->wlc->hw->boardflags, drvr->wlc->hw->boardflags2,
+                  drvr->wlc->ucode_rev, hw->band->radiorev,
+                  hw->band->phytype, hw->band->phyrev, hw->band->pi->ana_rev,
+                  hw->sromrev);
        return 0;
 }
 
index 738cfac..a104d7a 100644 (file)
@@ -445,18 +445,18 @@ static void brcms_c_detach_mfree(struct brcms_c_info *wlc)
        kfree(wlc->protection);
        kfree(wlc->stf);
        kfree(wlc->bandstate[0]);
-       kfree(wlc->corestate->macstat_snapshot);
+       if (wlc->corestate)
+               kfree(wlc->corestate->macstat_snapshot);
        kfree(wlc->corestate);
-       kfree(wlc->hw->bandstate[0]);
+       if (wlc->hw)
+               kfree(wlc->hw->bandstate[0]);
        kfree(wlc->hw);
        if (wlc->beacon)
                dev_kfree_skb_any(wlc->beacon);
        if (wlc->probe_resp)
                dev_kfree_skb_any(wlc->probe_resp);
 
-       /* free the wlc */
        kfree(wlc);
-       wlc = NULL;
 }
 
 static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit)
index 0f7e1c7..906e89d 100644 (file)
@@ -261,6 +261,21 @@ struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
 }
 EXPORT_SYMBOL(brcmu_pktq_mdeq);
 
+/* Produce a human-readable string for boardrev */
+char *brcmu_boardrev_str(u32 brev, char *buf)
+{
+       char c;
+
+       if (brev < 0x100) {
+               snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
+       } else {
+               c = (brev & 0xf000) == 0x1000 ? 'P' : 'A';
+               snprintf(buf, 8, "%c%03x", c, brev & 0xfff);
+       }
+       return buf;
+}
+EXPORT_SYMBOL(brcmu_boardrev_str);
+
 #if defined(DEBUG)
 /* pretty hex print a pkt buffer chain */
 void brcmu_prpkt(const char *msg, struct sk_buff *p0)
@@ -292,4 +307,5 @@ void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...)
        print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size);
 }
 EXPORT_SYMBOL(brcmu_dbg_hex_dump);
+
 #endif                         /* defined(DEBUG) */
index af26e0d..6996fcc 100644 (file)
@@ -68,6 +68,8 @@
 #define BRCM_PCIE_43567_DEVICE_ID      0x43d3
 #define BRCM_PCIE_43570_DEVICE_ID      0x43d9
 #define BRCM_PCIE_43602_DEVICE_ID      0x43ba
+#define BRCM_PCIE_43602_2G_DEVICE_ID   0x43bb
+#define BRCM_PCIE_43602_5G_DEVICE_ID   0x43bc
 
 /* brcmsmac IDs */
 #define BCM4313_D11N2G_ID      0x4727  /* 4313 802.11n 2.4G device */
index 8ba445b..a043e29 100644 (file)
@@ -218,4 +218,6 @@ void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...)
 }
 #endif
 
+char *brcmu_boardrev_str(u32 brev, char *buf);
+
 #endif                         /* _BRCMU_UTILS_H_ */
index 139de90..ab019b4 100644 (file)
@@ -59,7 +59,7 @@ config IWLDVM
 
 config IWLMVM
        tristate "Intel Wireless WiFi MVM Firmware support"
-       select BACKPORT_WANT_DEV_COREDUMP
+       select WANT_DEV_COREDUMP
        help
          This is the driver that supports the MVM firmware which is
          currently only available for 7260 and 3160 devices.
index 751ae1d..7a34e4d 100644 (file)
@@ -966,21 +966,21 @@ struct iwl_rem_sta_cmd {
 
 
 /* WiFi queues mask */
-#define IWL_SCD_BK_MSK                 cpu_to_le32(BIT(0))
-#define IWL_SCD_BE_MSK                 cpu_to_le32(BIT(1))
-#define IWL_SCD_VI_MSK                 cpu_to_le32(BIT(2))
-#define IWL_SCD_VO_MSK                 cpu_to_le32(BIT(3))
-#define IWL_SCD_MGMT_MSK               cpu_to_le32(BIT(3))
+#define IWL_SCD_BK_MSK                 BIT(0)
+#define IWL_SCD_BE_MSK                 BIT(1)
+#define IWL_SCD_VI_MSK                 BIT(2)
+#define IWL_SCD_VO_MSK                 BIT(3)
+#define IWL_SCD_MGMT_MSK               BIT(3)
 
 /* PAN queues mask */
-#define IWL_PAN_SCD_BK_MSK             cpu_to_le32(BIT(4))
-#define IWL_PAN_SCD_BE_MSK             cpu_to_le32(BIT(5))
-#define IWL_PAN_SCD_VI_MSK             cpu_to_le32(BIT(6))
-#define IWL_PAN_SCD_VO_MSK             cpu_to_le32(BIT(7))
-#define IWL_PAN_SCD_MGMT_MSK           cpu_to_le32(BIT(7))
-#define IWL_PAN_SCD_MULTICAST_MSK      cpu_to_le32(BIT(8))
+#define IWL_PAN_SCD_BK_MSK             BIT(4)
+#define IWL_PAN_SCD_BE_MSK             BIT(5)
+#define IWL_PAN_SCD_VI_MSK             BIT(6)
+#define IWL_PAN_SCD_VO_MSK             BIT(7)
+#define IWL_PAN_SCD_MGMT_MSK           BIT(7)
+#define IWL_PAN_SCD_MULTICAST_MSK      BIT(8)
 
-#define IWL_AGG_TX_QUEUE_MSK           cpu_to_le32(0xffc00)
+#define IWL_AGG_TX_QUEUE_MSK           0xffc00
 
 #define IWL_DROP_ALL                   BIT(1)
 
@@ -1005,12 +1005,17 @@ struct iwl_rem_sta_cmd {
  *     1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable.
  *     2: Dump all FIFO
  */
-struct iwl_txfifo_flush_cmd {
+struct iwl_txfifo_flush_cmd_v3 {
        __le32 queue_control;
        __le16 flush_control;
        __le16 reserved;
 } __packed;
 
+struct iwl_txfifo_flush_cmd_v2 {
+       __le16 queue_control;
+       __le16 flush_control;
+} __packed;
+
 /*
  * REPLY_WEP_KEY = 0x20
  */
index 02e4ede..1d2223d 100644 (file)
@@ -137,37 +137,38 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv,
  */
 int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk)
 {
-       struct iwl_txfifo_flush_cmd flush_cmd;
-       struct iwl_host_cmd cmd = {
-               .id = REPLY_TXFIFO_FLUSH,
-               .len = { sizeof(struct iwl_txfifo_flush_cmd), },
-               .data = { &flush_cmd, },
+       struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = {
+               .flush_control = cpu_to_le16(IWL_DROP_ALL),
+       };
+       struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = {
+               .flush_control = cpu_to_le16(IWL_DROP_ALL),
        };
 
-       memset(&flush_cmd, 0, sizeof(flush_cmd));
+       u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
+                           IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK;
 
-       flush_cmd.queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
-                                 IWL_SCD_BE_MSK | IWL_SCD_BK_MSK |
-                                 IWL_SCD_MGMT_MSK;
        if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS)))
-               flush_cmd.queue_control |= IWL_PAN_SCD_VO_MSK |
-                                          IWL_PAN_SCD_VI_MSK |
-                                          IWL_PAN_SCD_BE_MSK |
-                                          IWL_PAN_SCD_BK_MSK |
-                                          IWL_PAN_SCD_MGMT_MSK |
-                                          IWL_PAN_SCD_MULTICAST_MSK;
+               queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK |
+                                IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK |
+                                IWL_PAN_SCD_MGMT_MSK |
+                                IWL_PAN_SCD_MULTICAST_MSK;
 
        if (priv->nvm_data->sku_cap_11n_enable)
-               flush_cmd.queue_control |= IWL_AGG_TX_QUEUE_MSK;
+               queue_control |= IWL_AGG_TX_QUEUE_MSK;
 
        if (scd_q_msk)
-               flush_cmd.queue_control = cpu_to_le32(scd_q_msk);
-
-       IWL_DEBUG_INFO(priv, "queue control: 0x%x\n",
-                      flush_cmd.queue_control);
-       flush_cmd.flush_control = cpu_to_le16(IWL_DROP_ALL);
-
-       return iwl_dvm_send_cmd(priv, &cmd);
+               queue_control = scd_q_msk;
+
+       IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control);
+       flush_cmd_v3.queue_control = cpu_to_le32(queue_control);
+       flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control);
+
+       if (IWL_UCODE_API(priv->fw->ucode_ver) > 2)
+               return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
+                                           sizeof(flush_cmd_v3),
+                                           &flush_cmd_v3);
+       return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
+                                   sizeof(flush_cmd_v2), &flush_cmd_v2);
 }
 
 void iwlagn_dev_txfifo_flush(struct iwl_priv *priv)
index b04b885..e5be2d2 100644 (file)
 #define IWL3160_UCODE_API_MAX  10
 
 /* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK   9
-#define IWL3160_UCODE_API_OK   9
+#define IWL7260_UCODE_API_OK   10
+#define IWL3160_UCODE_API_OK   10
 
 /* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN  8
-#define IWL3160_UCODE_API_MIN  8
+#define IWL7260_UCODE_API_MIN  9
+#define IWL3160_UCODE_API_MIN  9
 
 /* NVM versions */
 #define IWL7260_NVM_VERSION            0x0a1d
@@ -89,6 +89,8 @@
 #define IWL3165_TX_POWER_VERSION       0xffff /* meaningless */
 #define IWL7265_NVM_VERSION            0x0a1d
 #define IWL7265_TX_POWER_VERSION       0xffff /* meaningless */
+#define IWL7265D_NVM_VERSION           0x0c11
+#define IWL7265_TX_POWER_VERSION       0xffff /* meaningless */
 
 #define IWL7260_FW_PRE "iwlwifi-7260-"
 #define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
 #define IWL7265_FW_PRE "iwlwifi-7265-"
 #define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
 
+#define IWL7265D_FW_PRE "iwlwifi-7265D-"
+#define IWL7265D_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
+
 #define NVM_HW_SECTION_NUM_FAMILY_7000         0
 
 static const struct iwl_base_params iwl7000_base_params = {
@@ -132,8 +137,8 @@ static const struct iwl_ht_params iwl7000_ht_params = {
        .base_params = &iwl7000_base_params,                    \
        .led_mode = IWL_LED_RF_STATE,                           \
        .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000,   \
-       .non_shared_ant = ANT_A
-
+       .non_shared_ant = ANT_A,                                \
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl7260_2ac_cfg = {
        .name = "Intel(R) Dual Band Wireless AC 7260",
@@ -267,7 +272,38 @@ const struct iwl_cfg iwl7265_n_cfg = {
        .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
 };
 
+const struct iwl_cfg iwl7265d_2ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC 7265",
+       .fw_name_pre = IWL7265D_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7265_ht_params,
+       .nvm_ver = IWL7265D_NVM_VERSION,
+       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+       .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+};
+
+const struct iwl_cfg iwl7265d_2n_cfg = {
+       .name = "Intel(R) Dual Band Wireless N 7265",
+       .fw_name_pre = IWL7265D_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7265_ht_params,
+       .nvm_ver = IWL7265D_NVM_VERSION,
+       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+       .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+};
+
+const struct iwl_cfg iwl7265d_n_cfg = {
+       .name = "Intel(R) Wireless N 7265",
+       .fw_name_pre = IWL7265D_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7265_ht_params,
+       .nvm_ver = IWL7265D_NVM_VERSION,
+       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+       .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+};
+
 MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
 MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
 MODULE_FIRMWARE(IWL3165_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
 MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
+MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
index 896ea90..bf0a95c 100644 (file)
 #define IWL8000_UCODE_API_MAX  10
 
 /* Oldest version we won't warn about */
-#define IWL8000_UCODE_API_OK   8
+#define IWL8000_UCODE_API_OK   10
 
 /* Lowest firmware API version supported */
-#define IWL8000_UCODE_API_MIN  8
+#define IWL8000_UCODE_API_MIN  9
 
 /* NVM versions */
 #define IWL8000_NVM_VERSION            0x0a1d
 /* Max SDIO RX aggregation size of the ADDBA request/response */
 #define MAX_RX_AGG_SIZE_8260_SDIO      28
 
+/* Max A-MPDU exponent for HT and VHT */
+#define MAX_HT_AMPDU_EXPONENT_8260_SDIO        IEEE80211_HT_MAX_AMPDU_32K
+#define MAX_VHT_AMPDU_EXPONENT_8260_SDIO       IEEE80211_VHT_MAX_AMPDU_32K
+
 static const struct iwl_base_params iwl8000_base_params = {
        .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
        .num_of_queues = IWLAGN_NUM_QUEUES,
@@ -119,6 +123,7 @@ static const struct iwl_ht_params iwl8000_ht_params = {
        .base_params = &iwl8000_base_params,                    \
        .led_mode = IWL_LED_RF_STATE,                           \
        .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000,   \
+       .d0i3 = true,                                           \
        .non_shared_ant = ANT_A
 
 const struct iwl_cfg iwl8260_2n_cfg = {
@@ -137,6 +142,7 @@ const struct iwl_cfg iwl8260_2ac_cfg = {
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
        .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
 const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
@@ -149,6 +155,23 @@ const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
        .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
        .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
        .disable_dummy_notification = true,
+       .max_ht_ampdu_exponent  = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
+       .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
+};
+
+const struct iwl_cfg iwl4165_2ac_sdio_cfg = {
+       .name = "Intel(R) Dual Band Wireless-AC 4165",
+       .fw_name_pre = IWL8000_FW_PRE,
+       IWL_DEVICE_8000,
+       .ht_params = &iwl8000_ht_params,
+       .nvm_ver = IWL8000_NVM_VERSION,
+       .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+       .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
+       .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
+       .bt_shared_single_ant = true,
+       .disable_dummy_notification = true,
+       .max_ht_ampdu_exponent  = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
+       .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
 };
 
 MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
index f8aa9cf..3a4b9c7 100644 (file)
@@ -257,6 +257,10 @@ struct iwl_pwr_tx_backoff {
  * @pwr_tx_backoffs: translation table between power limits and backoffs
  * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response
  * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response
+ * @max_ht_ampdu_factor: the exponent of the max length of A-MPDU that the
+ *     station can receive in HT
+ * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the
+ *     station can receive in VHT
  *
  * We enable the driver to be backward compatible wrt. hardware features.
  * API differences in uCode shouldn't be handled here but through TLVs
@@ -297,6 +301,8 @@ struct iwl_cfg {
        unsigned int max_rx_agg_size;
        bool disable_dummy_notification;
        unsigned int max_tx_agg_size;
+       unsigned int max_ht_ampdu_exponent;
+       unsigned int max_vht_ampdu_exponent;
 };
 
 /*
@@ -358,9 +364,14 @@ extern const struct iwl_cfg iwl3165_2ac_cfg;
 extern const struct iwl_cfg iwl7265_2ac_cfg;
 extern const struct iwl_cfg iwl7265_2n_cfg;
 extern const struct iwl_cfg iwl7265_n_cfg;
+extern const struct iwl_cfg iwl7265d_2ac_cfg;
+extern const struct iwl_cfg iwl7265d_2n_cfg;
+extern const struct iwl_cfg iwl7265d_n_cfg;
 extern const struct iwl_cfg iwl8260_2n_cfg;
 extern const struct iwl_cfg iwl8260_2ac_cfg;
 extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
+extern const struct iwl_cfg iwl4265_2ac_sdio_cfg;
+extern const struct iwl_cfg iwl4165_2ac_sdio_cfg;
 #endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
index 3f6f015..aff63c3 100644 (file)
 #define CSR_UCODE_DRV_GP1_CLR   (CSR_BASE+0x05c)
 #define CSR_UCODE_DRV_GP2       (CSR_BASE+0x060)
 
+#define CSR_MBOX_SET_REG       (CSR_BASE + 0x88)
+
 #define CSR_LED_REG             (CSR_BASE+0x094)
 #define CSR_DRAM_INT_TBL_REG   (CSR_BASE+0x0A0)
 #define CSR_MAC_SHADOW_REG_CTRL        (CSR_BASE+0x0A8) /* 6000 and up */
 #define CSR_HW_IF_CONFIG_REG_PREPARE             (0x08000000) /* WAKE_ME */
 #define CSR_HW_IF_CONFIG_REG_PERSIST_MODE        (0x40000000) /* PERSISTENCE */
 
+#define CSR_MBOX_SET_REG_OS_ALIVE              BIT(5)
+
 #define CSR_INT_PERIODIC_DIS                   (0x00) /* disable periodic int*/
 #define CSR_INT_PERIODIC_ENA                   (0xFF) /* 255*32 usec ~ 8 msec*/
 
@@ -305,23 +309,24 @@ enum {
 };
 
 
-#define CSR_HW_REV_TYPE_MSK            (0x000FFF0)
-#define CSR_HW_REV_TYPE_5300           (0x0000020)
-#define CSR_HW_REV_TYPE_5350           (0x0000030)
-#define CSR_HW_REV_TYPE_5100           (0x0000050)
-#define CSR_HW_REV_TYPE_5150           (0x0000040)
-#define CSR_HW_REV_TYPE_1000           (0x0000060)
-#define CSR_HW_REV_TYPE_6x00           (0x0000070)
-#define CSR_HW_REV_TYPE_6x50           (0x0000080)
-#define CSR_HW_REV_TYPE_6150           (0x0000084)
-#define CSR_HW_REV_TYPE_6x05          (0x00000B0)
-#define CSR_HW_REV_TYPE_6x30          CSR_HW_REV_TYPE_6x05
-#define CSR_HW_REV_TYPE_6x35          CSR_HW_REV_TYPE_6x05
-#define CSR_HW_REV_TYPE_2x30          (0x00000C0)
-#define CSR_HW_REV_TYPE_2x00          (0x0000100)
-#define CSR_HW_REV_TYPE_105           (0x0000110)
-#define CSR_HW_REV_TYPE_135           (0x0000120)
-#define CSR_HW_REV_TYPE_NONE           (0x00001F0)
+#define CSR_HW_REV_TYPE_MSK            (0x000FFF0)
+#define CSR_HW_REV_TYPE_5300           (0x0000020)
+#define CSR_HW_REV_TYPE_5350           (0x0000030)
+#define CSR_HW_REV_TYPE_5100           (0x0000050)
+#define CSR_HW_REV_TYPE_5150           (0x0000040)
+#define CSR_HW_REV_TYPE_1000           (0x0000060)
+#define CSR_HW_REV_TYPE_6x00           (0x0000070)
+#define CSR_HW_REV_TYPE_6x50           (0x0000080)
+#define CSR_HW_REV_TYPE_6150           (0x0000084)
+#define CSR_HW_REV_TYPE_6x05           (0x00000B0)
+#define CSR_HW_REV_TYPE_6x30           CSR_HW_REV_TYPE_6x05
+#define CSR_HW_REV_TYPE_6x35           CSR_HW_REV_TYPE_6x05
+#define CSR_HW_REV_TYPE_2x30           (0x00000C0)
+#define CSR_HW_REV_TYPE_2x00           (0x0000100)
+#define CSR_HW_REV_TYPE_105            (0x0000110)
+#define CSR_HW_REV_TYPE_135            (0x0000120)
+#define CSR_HW_REV_TYPE_7265D          (0x0000210)
+#define CSR_HW_REV_TYPE_NONE           (0x00001F0)
 
 /* EEPROM REG */
 #define CSR_EEPROM_REG_READ_VALID_MSK  (0x00000001)
index 0a70bcd..6842545 100644 (file)
@@ -143,7 +143,7 @@ do {                                                                \
 #define IWL_DL_INFO            0x00000001
 #define IWL_DL_MAC80211                0x00000002
 #define IWL_DL_HCMD            0x00000004
-#define IWL_DL_STATE           0x00000008
+#define IWL_DL_TDLS            0x00000008
 /* 0x000000F0 - 0x00000010 */
 #define IWL_DL_QUOTA           0x00000010
 #define IWL_DL_TE              0x00000020
@@ -180,6 +180,7 @@ do {                                                                \
 #define IWL_DL_TX_QUEUES       0x80000000
 
 #define IWL_DEBUG_INFO(p, f, a...)     IWL_DEBUG(p, IWL_DL_INFO, f, ## a)
+#define IWL_DEBUG_TDLS(p, f, a...)     IWL_DEBUG(p, IWL_DL_TDLS, f, ## a)
 #define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a)
 #define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a)
 #define IWL_DEBUG_TEMP(p, f, a...)     IWL_DEBUG(p, IWL_DL_TEMP, f, ## a)
index d9fa8e0..38de151 100644 (file)
@@ -78,9 +78,6 @@
 #include "iwl-config.h"
 #include "iwl-modparams.h"
 
-/* private includes */
-#include "iwl-fw-file.h"
-
 /******************************************************************************
  *
  * module boiler plate
@@ -187,6 +184,11 @@ static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img)
 static void iwl_dealloc_ucode(struct iwl_drv *drv)
 {
        int i;
+
+       kfree(drv->fw.dbg_dest_tlv);
+       for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
+               kfree(drv->fw.dbg_conf_tlv[i]);
+
        for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
                iwl_free_fw_img(drv, drv->fw.img + i);
 }
@@ -248,6 +250,9 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
        /*
         * Starting 8000B - FW name format has changed. This overwrites the
         * previous name and uses the new format.
+        *
+        * TODO:
+        * Once there is only one supported step for 8000 family - delete this!
         */
        if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
                char rev_step[2] = {
@@ -258,6 +263,13 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
                if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP)
                        rev_step[0] = 0;
 
+               /*
+                * If hw_rev wasn't set yet - default as B-step. If it IS A-step
+                * we'll reload that FW later instead.
+                */
+               if (drv->trans->hw_rev == 0)
+                       rev_step[0] = 'B';
+
                snprintf(drv->firmware_name, sizeof(drv->firmware_name),
                         "%s%s-%s.ucode", name_pre, rev_step, tag);
        }
@@ -301,6 +313,11 @@ struct iwl_firmware_pieces {
 
        u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
        u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
+
+       /* FW debug data parsed for driver usage */
+       struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+       struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+       size_t dbg_conf_tlv_len[FW_DBG_MAX];
 };
 
 /*
@@ -574,6 +591,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
        char buildstr[25];
        u32 build;
        int num_of_cpus;
+       bool usniffer_images = false;
+       bool usniffer_req = false;
 
        if (len < sizeof(*ucode)) {
                IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
@@ -846,12 +865,79 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                        capa->n_scan_channels =
                                le32_to_cpup((__le32 *)tlv_data);
                        break;
+               case IWL_UCODE_TLV_FW_DBG_DEST: {
+                       struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
+
+                       if (pieces->dbg_dest_tlv) {
+                               IWL_ERR(drv,
+                                       "dbg destination ignored, already exists\n");
+                               break;
+                       }
+
+                       pieces->dbg_dest_tlv = dest;
+                       IWL_INFO(drv, "Found debug destination: %s\n",
+                                get_fw_dbg_mode_string(dest->monitor_mode));
+
+                       drv->fw.dbg_dest_reg_num =
+                               tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
+                                                  reg_ops);
+                       drv->fw.dbg_dest_reg_num /=
+                               sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
+
+                       break;
+                       }
+               case IWL_UCODE_TLV_FW_DBG_CONF: {
+                       struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
+
+                       if (!pieces->dbg_dest_tlv) {
+                               IWL_ERR(drv,
+                                       "Ignore dbg config %d - no destination configured\n",
+                                       conf->id);
+                               break;
+                       }
+
+                       if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) {
+                               IWL_ERR(drv,
+                                       "Skip unknown configuration: %d\n",
+                                       conf->id);
+                               break;
+                       }
+
+                       if (pieces->dbg_conf_tlv[conf->id]) {
+                               IWL_ERR(drv,
+                                       "Ignore duplicate dbg config %d\n",
+                                       conf->id);
+                               break;
+                       }
+
+                       if (conf->usniffer)
+                               usniffer_req = true;
+
+                       IWL_INFO(drv, "Found debug configuration: %d\n",
+                                conf->id);
+
+                       pieces->dbg_conf_tlv[conf->id] = conf;
+                       pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
+                       break;
+                       }
+               case IWL_UCODE_TLV_SEC_RT_USNIFFER:
+                       usniffer_images = true;
+                       iwl_store_ucode_sec(pieces, tlv_data,
+                                           IWL_UCODE_REGULAR_USNIFFER,
+                                           tlv_len);
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
                }
        }
 
+       if (usniffer_req && !usniffer_images) {
+               IWL_ERR(drv,
+                       "user selected to work with usniffer but usniffer image isn't available in ucode package\n");
+               return -EINVAL;
+       }
+
        if (len) {
                IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len);
                iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len);
@@ -989,13 +1075,14 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        struct iwl_ucode_header *ucode;
        struct iwlwifi_opmode_table *op;
        int err;
-       struct iwl_firmware_pieces pieces;
+       struct iwl_firmware_pieces *pieces;
        const unsigned int api_max = drv->cfg->ucode_api_max;
        unsigned int api_ok = drv->cfg->ucode_api_ok;
        const unsigned int api_min = drv->cfg->ucode_api_min;
        u32 api_ver;
        int i;
        bool load_module = false;
+       u32 hw_rev = drv->trans->hw_rev;
 
        fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
        fw->ucode_capa.standard_phy_calibration_size =
@@ -1005,7 +1092,9 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        if (!api_ok)
                api_ok = api_max;
 
-       memset(&pieces, 0, sizeof(pieces));
+       pieces = kzalloc(sizeof(*pieces), GFP_KERNEL);
+       if (!pieces)
+               return;
 
        if (!ucode_raw) {
                if (drv->fw_index <= api_ok)
@@ -1028,10 +1117,10 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        ucode = (struct iwl_ucode_header *)ucode_raw->data;
 
        if (ucode->ver)
-               err = iwl_parse_v1_v2_firmware(drv, ucode_raw, &pieces);
+               err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
        else
-               err = iwl_parse_tlv_firmware(drv, ucode_raw, &pieces,
-                                          &fw->ucode_capa);
+               err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
+                                            &fw->ucode_capa);
 
        if (err)
                goto try_again;
@@ -1071,7 +1160,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
         * In mvm uCode there is no difference between data and instructions
         * sections.
         */
-       if (!fw->mvm_fw && validate_sec_sizes(drv, &pieces, drv->cfg))
+       if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg))
                goto try_again;
 
        /* Allocate ucode buffers for card's bus-master loading ... */
@@ -1080,9 +1169,33 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
         * 1) unmodified from disk
         * 2) backup cache for save/restore during power-downs */
        for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
-               if (iwl_alloc_ucode(drv, &pieces, i))
+               if (iwl_alloc_ucode(drv, pieces, i))
                        goto out_free_fw;
 
+       if (pieces->dbg_dest_tlv) {
+               drv->fw.dbg_dest_tlv =
+                       kmemdup(pieces->dbg_dest_tlv,
+                               sizeof(*pieces->dbg_dest_tlv) +
+                               sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
+                               drv->fw.dbg_dest_reg_num, GFP_KERNEL);
+
+               if (!drv->fw.dbg_dest_tlv)
+                       goto out_free_fw;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
+               if (pieces->dbg_conf_tlv[i]) {
+                       drv->fw.dbg_conf_tlv_len[i] =
+                               pieces->dbg_conf_tlv_len[i];
+                       drv->fw.dbg_conf_tlv[i] =
+                               kmemdup(pieces->dbg_conf_tlv[i],
+                                       drv->fw.dbg_conf_tlv_len[i],
+                                       GFP_KERNEL);
+                       if (!drv->fw.dbg_conf_tlv[i])
+                               goto out_free_fw;
+               }
+       }
+
        /* Now that we can no longer fail, copy information */
 
        /*
@@ -1090,20 +1203,20 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
         * for each event, which is of mode 1 (including timestamp) for all
         * new microcodes that include this information.
         */
-       fw->init_evtlog_ptr = pieces.init_evtlog_ptr;
-       if (pieces.init_evtlog_size)
-               fw->init_evtlog_size = (pieces.init_evtlog_size - 16)/12;
+       fw->init_evtlog_ptr = pieces->init_evtlog_ptr;
+       if (pieces->init_evtlog_size)
+               fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12;
        else
                fw->init_evtlog_size =
                        drv->cfg->base_params->max_event_log_size;
-       fw->init_errlog_ptr = pieces.init_errlog_ptr;
-       fw->inst_evtlog_ptr = pieces.inst_evtlog_ptr;
-       if (pieces.inst_evtlog_size)
-               fw->inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12;
+       fw->init_errlog_ptr = pieces->init_errlog_ptr;
+       fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr;
+       if (pieces->inst_evtlog_size)
+               fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12;
        else
                fw->inst_evtlog_size =
                        drv->cfg->base_params->max_event_log_size;
-       fw->inst_errlog_ptr = pieces.inst_errlog_ptr;
+       fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
 
        /*
         * figure out the offset of chain noise reset and gain commands
@@ -1162,10 +1275,55 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
                                op->name, err);
 #endif
        }
+
+       /*
+        * We may have loaded the wrong FW file in 8000 HW family if it is an
+        * A-step card, and if drv->trans->hw_rev wasn't properly read when
+        * the FW file had been loaded. (This might happen in SDIO.) In such a
+        * case - unload and reload the correct file.
+        *
+        * TODO:
+        * Once there is only one supported step for 8000 family - delete this!
+        */
+       if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+           CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP &&
+           drv->trans->hw_rev != hw_rev) {
+               char firmware_name[32];
+
+               /* Free previous FW resources */
+               if (drv->op_mode)
+                       _iwl_op_mode_stop(drv);
+               iwl_dealloc_ucode(drv);
+
+               /* Build name of correct-step FW */
+               snprintf(firmware_name, sizeof(firmware_name),
+                        strrchr(drv->firmware_name, '-'));
+               snprintf(drv->firmware_name, sizeof(drv->firmware_name),
+                        "%s%s", drv->cfg->fw_name_pre, firmware_name);
+
+               /* Clear data before loading correct FW */
+               list_del(&drv->list);
+
+               /* Request correct FW file this time */
+               IWL_DEBUG_INFO(drv, "attempting to load A-step FW %s\n",
+                              drv->firmware_name);
+               err = request_firmware(&ucode_raw, drv->firmware_name,
+                                      drv->trans->dev);
+               if (err) {
+                       IWL_ERR(drv, "Failed swapping FW!\n");
+                       goto out_unbind;
+               }
+
+               /* Redo callback function - this time with right FW */
+               iwl_req_fw_callback(ucode_raw, context);
+       }
+
+       kfree(pieces);
        return;
 
  try_again:
        /* try next, if any */
+       kfree(pieces);
        release_firmware(ucode_raw);
        if (iwl_request_firmware(drv, false))
                goto out_unbind;
@@ -1176,6 +1334,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        iwl_dealloc_ucode(drv);
        release_firmware(ucode_raw);
  out_unbind:
+       kfree(pieces);
        complete(&drv->request_firmware_complete);
        device_release_driver(drv->trans->dev);
 }
index 74b796d..41ff85d 100644 (file)
@@ -764,7 +764,7 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
        if (iwlwifi_mod_params.amsdu_size_8K)
                ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
 
-       ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+       ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent;
        ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
 
        ht_info->mcs.rx_mask[0] = 0xFF;
index e30a41d..20a8a64 100644 (file)
@@ -81,6 +81,7 @@
  * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
  * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several
  *     sections like this in a single file.
+ * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers
  */
 enum iwl_fw_error_dump_type {
        IWL_FW_ERROR_DUMP_SRAM = 0,
@@ -90,6 +91,8 @@ enum iwl_fw_error_dump_type {
        IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
        IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
        IWL_FW_ERROR_DUMP_PRPH = 6,
+       IWL_FW_ERROR_DUMP_TXF = 7,
+       IWL_FW_ERROR_DUMP_FH_REGS = 8,
 
        IWL_FW_ERROR_DUMP_MAX,
 };
index 401f7be..f2a047f 100644 (file)
@@ -131,6 +131,9 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_API_CHANGES_SET   = 29,
        IWL_UCODE_TLV_ENABLED_CAPABILITIES      = 30,
        IWL_UCODE_TLV_N_SCAN_CHANNELS           = 31,
+       IWL_UCODE_TLV_SEC_RT_USNIFFER   = 34,
+       IWL_UCODE_TLV_FW_DBG_DEST       = 38,
+       IWL_UCODE_TLV_FW_DBG_CONF       = 39,
 };
 
 struct iwl_ucode_tlv {
@@ -179,4 +182,309 @@ struct iwl_ucode_capa {
        __le32 api_capa;
 } __packed;
 
+/**
+ * enum iwl_ucode_tlv_flag - ucode API flags
+ * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
+ *     was a separate TLV but moved here to save space.
+ * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
+ *     treats good CRC threshold as a boolean
+ * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
+ * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
+ * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD
+ * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
+ *     offload profile config command.
+ * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
+ *     (rather than two) IPv6 addresses
+ * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
+ *     from the probe request template.
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
+ * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
+ *     P2P client interfaces simultaneously if they are in different bindings.
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
+ *     P2P client interfaces simultaneously if they are in same bindings.
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
+ * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
+ * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
+ * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
+ * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
+ */
+enum iwl_ucode_tlv_flag {
+       IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
+       IWL_UCODE_TLV_FLAGS_NEWSCAN             = BIT(1),
+       IWL_UCODE_TLV_FLAGS_MFP                 = BIT(2),
+       IWL_UCODE_TLV_FLAGS_P2P                 = BIT(3),
+       IWL_UCODE_TLV_FLAGS_DW_BC_TABLE         = BIT(4),
+       IWL_UCODE_TLV_FLAGS_SHORT_BL            = BIT(7),
+       IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS     = BIT(10),
+       IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID       = BIT(12),
+       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL    = BIT(15),
+       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE    = BIT(16),
+       IWL_UCODE_TLV_FLAGS_P2P_PM              = BIT(21),
+       IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM      = BIT(22),
+       IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM      = BIT(23),
+       IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT       = BIT(24),
+       IWL_UCODE_TLV_FLAGS_EBS_SUPPORT         = BIT(25),
+       IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD        = BIT(26),
+       IWL_UCODE_TLV_FLAGS_BCAST_FILTERING     = BIT(29),
+       IWL_UCODE_TLV_FLAGS_GO_UAPSD            = BIT(30),
+};
+
+/**
+ * enum iwl_ucode_tlv_api - ucode api
+ * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
+ * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
+ * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
+ * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
+ * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
+ * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
+ * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif.
+ * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
+ *     longer than the passive one, which is essential for fragmented scan.
+ */
+enum iwl_ucode_tlv_api {
+       IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID     = BIT(0),
+       IWL_UCODE_TLV_CAPA_EXTENDED_BEACON      = BIT(1),
+       IWL_UCODE_TLV_API_BT_COEX_SPLIT         = BIT(3),
+       IWL_UCODE_TLV_API_CSA_FLOW              = BIT(4),
+       IWL_UCODE_TLV_API_DISABLE_STA_TX        = BIT(5),
+       IWL_UCODE_TLV_API_LMAC_SCAN             = BIT(6),
+       IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF     = BIT(7),
+       IWL_UCODE_TLV_API_FRAGMENTED_SCAN       = BIT(8),
+};
+
+/**
+ * enum iwl_ucode_tlv_capa - ucode capabilities
+ * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
+ * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory
+ * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan.
+ * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality
+ * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
+ *     tx power value into TPC Report action frame and Link Measurement Report
+ *     action frame
+ * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current
+ *     channel in DS parameter set element in probe requests.
+ * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in
+ *     probe requests.
+ * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
+ * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
+ *     which also implies support for the scheduler configuration command
+ * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching
+ * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
+ */
+enum iwl_ucode_tlv_capa {
+       IWL_UCODE_TLV_CAPA_D0I3_SUPPORT                 = BIT(0),
+       IWL_UCODE_TLV_CAPA_LAR_SUPPORT                  = BIT(1),
+       IWL_UCODE_TLV_CAPA_UMAC_SCAN                    = BIT(2),
+       IWL_UCODE_TLV_CAPA_TDLS_SUPPORT                 = BIT(6),
+       IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT    = BIT(8),
+       IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT      = BIT(9),
+       IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT       = BIT(10),
+       IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT         = BIT(11),
+       IWL_UCODE_TLV_CAPA_DQA_SUPPORT                  = BIT(12),
+       IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH          = BIT(13),
+       IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT              = BIT(18),
+};
+
+/* The default calibrate table size if not specified by firmware file */
+#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE    18
+#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE                19
+#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE                 253
+
+/* The default max probe length if not specified by the firmware file */
+#define IWL_DEFAULT_MAX_PROBE_LENGTH   200
+
+/*
+ * For 16.0 uCode and above, there is no differentiation between sections,
+ * just an offset to the HW address.
+ */
+#define IWL_UCODE_SECTION_MAX 12
+#define IWL_API_ARRAY_SIZE     1
+#define IWL_CAPABILITIES_ARRAY_SIZE    1
+#define CPU1_CPU2_SEPARATOR_SECTION    0xFFFFCCCC
+
+/* uCode version contains 4 values: Major/Minor/API/Serial */
+#define IWL_UCODE_MAJOR(ver)   (((ver) & 0xFF000000) >> 24)
+#define IWL_UCODE_MINOR(ver)   (((ver) & 0x00FF0000) >> 16)
+#define IWL_UCODE_API(ver)     (((ver) & 0x0000FF00) >> 8)
+#define IWL_UCODE_SERIAL(ver)  ((ver) & 0x000000FF)
+
+/*
+ * Calibration control struct.
+ * Sent as part of the phy configuration command.
+ * @flow_trigger: bitmap for which calibrations to perform according to
+ *             flow triggers.
+ * @event_trigger: bitmap for which calibrations to perform according to
+ *             event triggers.
+ */
+struct iwl_tlv_calib_ctrl {
+       __le32 flow_trigger;
+       __le32 event_trigger;
+} __packed;
+
+enum iwl_fw_phy_cfg {
+       FW_PHY_CFG_RADIO_TYPE_POS = 0,
+       FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS,
+       FW_PHY_CFG_RADIO_STEP_POS = 2,
+       FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS,
+       FW_PHY_CFG_RADIO_DASH_POS = 4,
+       FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS,
+       FW_PHY_CFG_TX_CHAIN_POS = 16,
+       FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS,
+       FW_PHY_CFG_RX_CHAIN_POS = 20,
+       FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
+};
+
+#define IWL_UCODE_MAX_CS               1
+
+/**
+ * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
+ * @cipher: a cipher suite selector
+ * @flags: cipher scheme flags (currently reserved for a future use)
+ * @hdr_len: a size of MPDU security header
+ * @pn_len: a size of PN
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: bit shift needed to get key_idx
+ * @mic_len: mic length in bytes
+ * @hw_cipher: a HW cipher index used in host commands
+ */
+struct iwl_fw_cipher_scheme {
+       __le32 cipher;
+       u8 flags;
+       u8 hdr_len;
+       u8 pn_len;
+       u8 pn_off;
+       u8 key_idx_off;
+       u8 key_idx_mask;
+       u8 key_idx_shift;
+       u8 mic_len;
+       u8 hw_cipher;
+} __packed;
+
+enum iwl_fw_dbg_reg_operator {
+       CSR_ASSIGN,
+       CSR_SETBIT,
+       CSR_CLEARBIT,
+
+       PRPH_ASSIGN,
+       PRPH_SETBIT,
+       PRPH_CLEARBIT,
+};
+
+/**
+ * struct iwl_fw_dbg_reg_op - an operation on a register
+ *
+ * @op: %enum iwl_fw_dbg_reg_operator
+ * @addr: offset of the register
+ * @val: value
+ */
+struct iwl_fw_dbg_reg_op {
+       u8 op;
+       u8 reserved[3];
+       __le32 addr;
+       __le32 val;
+} __packed;
+
+/**
+ * enum iwl_fw_dbg_monitor_mode - available monitor recording modes
+ *
+ * @SMEM_MODE: monitor stores the data in SMEM
+ * @EXTERNAL_MODE: monitor stores the data in allocated DRAM
+ * @MARBH_MODE: monitor stores the data in MARBH buffer
+ */
+enum iwl_fw_dbg_monitor_mode {
+       SMEM_MODE = 0,
+       EXTERNAL_MODE = 1,
+       MARBH_MODE = 2,
+};
+
+/**
+ * struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data
+ *
+ * @version: version of the TLV - currently 0
+ * @monitor_mode: %enum iwl_fw_dbg_monitor_mode
+ * @base_reg: addr of the base addr register (PRPH)
+ * @end_reg:  addr of the end addr register (PRPH)
+ * @write_ptr_reg: the addr of the reg of the write pointer
+ * @wrap_count: the addr of the reg of the wrap_count
+ * @base_shift: shift right of the base addr reg
+ * @end_shift: shift right of the end addr reg
+ * @reg_ops: array of registers operations
+ *
+ * This parses IWL_UCODE_TLV_FW_DBG_DEST
+ */
+struct iwl_fw_dbg_dest_tlv {
+       u8 version;
+       u8 monitor_mode;
+       u8 reserved[2];
+       __le32 base_reg;
+       __le32 end_reg;
+       __le32 write_ptr_reg;
+       __le32 wrap_count;
+       u8 base_shift;
+       u8 end_shift;
+       struct iwl_fw_dbg_reg_op reg_ops[0];
+} __packed;
+
+struct iwl_fw_dbg_conf_hcmd {
+       u8 id;
+       u8 reserved;
+       __le16 len;
+       u8 data[0];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger - a TLV that describes a debug configuration
+ *
+ * @enabled: is this trigger enabled
+ * @reserved:
+ * @len: length, in bytes, of the %trigger field
+ * @trigger: pointer to a trigger struct
+ */
+struct iwl_fw_dbg_trigger {
+       u8 enabled;
+       u8 reserved;
+       u8 len;
+       u8 trigger[0];
+} __packed;
+
+/**
+ * enum iwl_fw_dbg_conf - configurations available
+ *
+ * @FW_DBG_CUSTOM: take this configuration from alive
+ *     Note that the trigger is NO-OP for this configuration
+ */
+enum iwl_fw_dbg_conf {
+       FW_DBG_CUSTOM = 0,
+
+       /* must be last */
+       FW_DBG_MAX,
+       FW_DBG_INVALID = 0xff,
+};
+
+/**
+ * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration
+ *
+ * @id: %enum iwl_fw_dbg_conf
+ * @usniffer: should the uSniffer image be used
+ * @num_of_hcmds: how many HCMDs to send are present here
+ * @hcmd: a variable length host command to be sent to apply the configuration.
+ *     If there is more than one HCMD to send, they will appear one after the
+ *     other and be sent in the order that they appear in.
+ * This parses IWL_UCODE_TLV_FW_DBG_CONF
+ */
+struct iwl_fw_dbg_conf_tlv {
+       u8 id;
+       u8 usniffer;
+       u8 reserved;
+       u8 num_of_hcmds;
+       struct iwl_fw_dbg_conf_hcmd hcmd;
+
+       /* struct iwl_fw_dbg_trigger sits after all variable length hcmds */
+} __packed;
+
 #endif  /* __iwl_fw_file_h__ */
index 649fdae..e6dc3b8 100644 (file)
 
 #include "iwl-fw-file.h"
 
-/**
- * enum iwl_ucode_tlv_flag - ucode API flags
- * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
- *     was a separate TLV but moved here to save space.
- * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
- *     treats good CRC threshold as a boolean
- * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
- * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
- * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
- * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD
- * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
- *     offload profile config command.
- * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
- *     (rather than two) IPv6 addresses
- * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
- *     from the probe request template.
- * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
- * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
- * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
- * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
- *     P2P client interfaces simultaneously if they are in different bindings.
- * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
- *     P2P client interfaces simultaneously if they are in same bindings.
- * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
- * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
- * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
- * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
- * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
- */
-enum iwl_ucode_tlv_flag {
-       IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
-       IWL_UCODE_TLV_FLAGS_NEWSCAN             = BIT(1),
-       IWL_UCODE_TLV_FLAGS_MFP                 = BIT(2),
-       IWL_UCODE_TLV_FLAGS_P2P                 = BIT(3),
-       IWL_UCODE_TLV_FLAGS_DW_BC_TABLE         = BIT(4),
-       IWL_UCODE_TLV_FLAGS_SHORT_BL            = BIT(7),
-       IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS     = BIT(10),
-       IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID       = BIT(12),
-       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL    = BIT(15),
-       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE    = BIT(16),
-       IWL_UCODE_TLV_FLAGS_P2P_PM              = BIT(21),
-       IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM      = BIT(22),
-       IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM      = BIT(23),
-       IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT       = BIT(24),
-       IWL_UCODE_TLV_FLAGS_EBS_SUPPORT         = BIT(25),
-       IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD        = BIT(26),
-       IWL_UCODE_TLV_FLAGS_BCAST_FILTERING     = BIT(29),
-       IWL_UCODE_TLV_FLAGS_GO_UAPSD            = BIT(30),
-};
-
-/**
- * enum iwl_ucode_tlv_api - ucode api
- * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
- * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
- * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
- * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
- * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
- * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
- * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif.
- * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
- *     longer than the passive one, which is essential for fragmented scan.
- */
-enum iwl_ucode_tlv_api {
-       IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID     = BIT(0),
-       IWL_UCODE_TLV_CAPA_EXTENDED_BEACON      = BIT(1),
-       IWL_UCODE_TLV_API_BT_COEX_SPLIT         = BIT(3),
-       IWL_UCODE_TLV_API_CSA_FLOW              = BIT(4),
-       IWL_UCODE_TLV_API_DISABLE_STA_TX        = BIT(5),
-       IWL_UCODE_TLV_API_LMAC_SCAN             = BIT(6),
-       IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF     = BIT(7),
-       IWL_UCODE_TLV_API_FRAGMENTED_SCAN       = BIT(8),
-};
-
-/**
- * enum iwl_ucode_tlv_capa - ucode capabilities
- * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
- * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
- *     tx power value into TPC Report action frame and Link Measurement Report
- *     action frame
- * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports adding DS params
- *     element in probe requests.
- * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in
- *     probe requests.
- * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
- * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
- *     which also implies support for the scheduler configuration command
- * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
- */
-enum iwl_ucode_tlv_capa {
-       IWL_UCODE_TLV_CAPA_D0I3_SUPPORT                 = BIT(0),
-       IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT    = BIT(8),
-       IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT      = BIT(9),
-       IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT       = BIT(10),
-       IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT         = BIT(11),
-       IWL_UCODE_TLV_CAPA_DQA_SUPPORT                  = BIT(12),
-       IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT              = BIT(18),
-};
-
-/* The default calibrate table size if not specified by firmware file */
-#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE    18
-#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE                19
-#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE                 253
-
-/* The default max probe length if not specified by the firmware file */
-#define IWL_DEFAULT_MAX_PROBE_LENGTH   200
-
 /**
  * enum iwl_ucode_type
  *
@@ -183,11 +77,13 @@ enum iwl_ucode_tlv_capa {
  * @IWL_UCODE_REGULAR: Normal runtime ucode
  * @IWL_UCODE_INIT: Initial ucode
  * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode
+ * @IWL_UCODE_REGULAR_USNIFFER: Normal runtime ucode when using usniffer image
  */
 enum iwl_ucode_type {
        IWL_UCODE_REGULAR,
        IWL_UCODE_INIT,
        IWL_UCODE_WOWLAN,
+       IWL_UCODE_REGULAR_USNIFFER,
        IWL_UCODE_TYPE_MAX,
 };
 
@@ -202,14 +98,6 @@ enum iwl_ucode_sec {
        IWL_UCODE_SECTION_DATA,
        IWL_UCODE_SECTION_INST,
 };
-/*
- * For 16.0 uCode and above, there is no differentiation between sections,
- * just an offset to the HW address.
- */
-#define IWL_UCODE_SECTION_MAX 12
-#define IWL_API_ARRAY_SIZE     1
-#define IWL_CAPABILITIES_ARRAY_SIZE    1
-#define CPU1_CPU2_SEPARATOR_SECTION    0xFFFFCCCC
 
 struct iwl_ucode_capabilities {
        u32 max_probe_length;
@@ -237,66 +125,6 @@ struct iwl_sf_region {
        u32 size;
 };
 
-/* uCode version contains 4 values: Major/Minor/API/Serial */
-#define IWL_UCODE_MAJOR(ver)   (((ver) & 0xFF000000) >> 24)
-#define IWL_UCODE_MINOR(ver)   (((ver) & 0x00FF0000) >> 16)
-#define IWL_UCODE_API(ver)     (((ver) & 0x0000FF00) >> 8)
-#define IWL_UCODE_SERIAL(ver)  ((ver) & 0x000000FF)
-
-/*
- * Calibration control struct.
- * Sent as part of the phy configuration command.
- * @flow_trigger: bitmap for which calibrations to perform according to
- *             flow triggers.
- * @event_trigger: bitmap for which calibrations to perform according to
- *             event triggers.
- */
-struct iwl_tlv_calib_ctrl {
-       __le32 flow_trigger;
-       __le32 event_trigger;
-} __packed;
-
-enum iwl_fw_phy_cfg {
-       FW_PHY_CFG_RADIO_TYPE_POS = 0,
-       FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS,
-       FW_PHY_CFG_RADIO_STEP_POS = 2,
-       FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS,
-       FW_PHY_CFG_RADIO_DASH_POS = 4,
-       FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS,
-       FW_PHY_CFG_TX_CHAIN_POS = 16,
-       FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS,
-       FW_PHY_CFG_RX_CHAIN_POS = 20,
-       FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
-};
-
-#define IWL_UCODE_MAX_CS               1
-
-/**
- * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
- * @cipher: a cipher suite selector
- * @flags: cipher scheme flags (currently reserved for a future use)
- * @hdr_len: a size of MPDU security header
- * @pn_len: a size of PN
- * @pn_off: an offset of pn from the beginning of the security header
- * @key_idx_off: an offset of key index byte in the security header
- * @key_idx_mask: a bit mask of key_idx bits
- * @key_idx_shift: bit shift needed to get key_idx
- * @mic_len: mic length in bytes
- * @hw_cipher: a HW cipher index used in host commands
- */
-struct iwl_fw_cipher_scheme {
-       __le32 cipher;
-       u8 flags;
-       u8 hdr_len;
-       u8 pn_len;
-       u8 pn_off;
-       u8 key_idx_off;
-       u8 key_idx_mask;
-       u8 key_idx_shift;
-       u8 mic_len;
-       u8 hw_cipher;
-} __packed;
-
 /**
  * struct iwl_fw_cscheme_list - a cipher scheme list
  * @size: a number of entries
@@ -323,6 +151,11 @@ struct iwl_fw_cscheme_list {
  * @inst_errlog_ptr: error log offfset for runtime ucode.
  * @mvm_fw: indicates this is MVM firmware
  * @cipher_scheme: optional external cipher scheme.
+ * @human_readable: human readable version
+ * @dbg_dest_tlv: points to the destination TLV for debug
+ * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
+ * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries
+ * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
  */
 struct iwl_fw {
        u32 ucode_ver;
@@ -347,6 +180,68 @@ struct iwl_fw {
 
        struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
        u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
+
+       struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+       struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+       size_t dbg_conf_tlv_len[FW_DBG_MAX];
+
+       u8 dbg_dest_reg_num;
 };
 
+static inline const char *get_fw_dbg_mode_string(int mode)
+{
+       switch (mode) {
+       case SMEM_MODE:
+               return "SMEM";
+       case EXTERNAL_MODE:
+               return "EXTERNAL_DRAM";
+       case MARBH_MODE:
+               return "MARBH";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+static inline const struct iwl_fw_dbg_trigger *
+iwl_fw_dbg_conf_get_trigger(const struct iwl_fw *fw, u8 id)
+{
+       const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
+       u8 *ptr;
+       int i;
+
+       if (!conf_tlv)
+               return NULL;
+
+       ptr = (void *)&conf_tlv->hcmd;
+       for (i = 0; i < conf_tlv->num_of_hcmds; i++) {
+               ptr += sizeof(conf_tlv->hcmd);
+               ptr += le16_to_cpu(conf_tlv->hcmd.len);
+       }
+
+       return (const struct iwl_fw_dbg_trigger *)ptr;
+}
+
+static inline bool
+iwl_fw_dbg_conf_enabled(const struct iwl_fw *fw, u8 id)
+{
+       const struct iwl_fw_dbg_trigger *trigger =
+               iwl_fw_dbg_conf_get_trigger(fw, id);
+
+       if (!trigger)
+               return false;
+
+       return trigger->enabled;
+}
+
+static inline bool
+iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id)
+{
+       const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
+
+       if (!conf_tlv)
+               return false;
+
+       return conf_tlv->usniffer;
+}
+
 #endif  /* __iwl_fw_h__ */
index c302e74..06e02fc 100644 (file)
@@ -325,6 +325,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
 {
        int num_rx_ants = num_of_ant(rx_chains);
        int num_tx_ants = num_of_ant(tx_chains);
+       unsigned int max_ampdu_exponent = (cfg->max_vht_ampdu_exponent ?:
+                                          IEEE80211_VHT_MAX_AMPDU_1024K);
 
        vht_cap->vht_supported = true;
 
@@ -332,7 +334,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
                       IEEE80211_VHT_CAP_RXSTBC_1 |
                       IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
                       3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
-                      7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+                      max_ampdu_exponent <<
+                      IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
 
        if (cfg->ht_params->ldpc)
                vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
index b6d666e..17de6d4 100644 (file)
@@ -138,7 +138,8 @@ struct iwl_cfg;
  * @nic_config: configure NIC, called before firmware is started.
  *     May sleep
  * @wimax_active: invoked when WiMax becomes active. May sleep
- * @enter_d0i3: configure the fw to enter d0i3. May sleep.
+ * @enter_d0i3: configure the fw to enter d0i3. return 1 to indicate d0i3
+ *     entrance is aborted (e.g. due to held reference). May sleep.
  * @exit_d0i3: configure the fw to exit d0i3. May sleep.
  */
 struct iwl_op_mode_ops {
index 1560f45..2df51ea 100644 (file)
@@ -322,6 +322,7 @@ enum secure_boot_config_reg {
        LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ       = 0x00000002,
 };
 
+#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0   (0xA01E30)
 #define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR      (0x1E30)
 #define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR      (0x1E34)
 enum secure_boot_status_reg {
@@ -333,6 +334,7 @@ enum secure_boot_status_reg {
        LMPM_SECURE_BOOT_STATUS_SUCCESS                 = 0x00000003,
 };
 
+#define FH_UCODE_LOAD_STATUS           (0x1AF0)
 #define CSR_UCODE_LOAD_STATUS_ADDR     (0x1E70)
 enum secure_load_status_reg {
        LMPM_CPU_UCODE_LOADING_STARTED                  = 0x00000001,
@@ -352,7 +354,7 @@ enum secure_load_status_reg {
 #define LMPM_SECURE_CPU1_HDR_MEM_SPACE         (0x420000)
 #define LMPM_SECURE_CPU2_HDR_MEM_SPACE         (0x420400)
 
-#define LMPM_SECURE_TIME_OUT   (100)
+#define LMPM_SECURE_TIME_OUT   (100) /* 10 micro */
 
 /* Rx FIFO */
 #define RXF_SIZE_ADDR                  (0xa00c88)
@@ -368,4 +370,10 @@ enum secure_load_status_reg {
 #define MON_BUFF_WRPTR                 (0xa03c44)
 #define MON_BUFF_CYCLE_CNT             (0xa03c48)
 
+/* FW chicken bits */
+#define LMPM_CHICK                     0xA01FF8
+enum {
+       LMPM_CHICK_EXTENDED_ADDR_SPACE = BIT(0),
+};
+
 #endif                         /* __iwl_prph_h__ */
index 0768f83..028408a 100644 (file)
@@ -534,6 +534,8 @@ struct iwl_trans_ops {
                              u32 value);
        void (*ref)(struct iwl_trans *trans);
        void (*unref)(struct iwl_trans *trans);
+       void (*suspend)(struct iwl_trans *trans);
+       void (*resume)(struct iwl_trans *trans);
 
        struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans);
 };
@@ -572,6 +574,9 @@ enum iwl_trans_state {
  * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
  *     start of the 802.11 header in the @rx_mpdu_cmd
  * @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
+ * @dbg_dest_tlv: points to the destination TLV for debug
+ * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
+ * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
  */
 struct iwl_trans {
        const struct iwl_trans_ops *ops;
@@ -603,6 +608,10 @@ struct iwl_trans {
 
        u64 dflt_pwr_limit;
 
+       const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+       const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+       u8 dbg_dest_reg_num;
+
        /* pointer to trans specific struct */
        /*Ensure that this pointer will always be aligned to sizeof pointer */
        char trans_specific[0] __aligned(sizeof(void *));
@@ -702,6 +711,18 @@ static inline void iwl_trans_unref(struct iwl_trans *trans)
                trans->ops->unref(trans);
 }
 
+static inline void iwl_trans_suspend(struct iwl_trans *trans)
+{
+       if (trans->ops->suspend)
+               trans->ops->suspend(trans);
+}
+
+static inline void iwl_trans_resume(struct iwl_trans *trans)
+{
+       if (trans->ops->resume)
+               trans->ops->resume(trans);
+}
+
 static inline struct iwl_trans_dump_data *
 iwl_trans_dump_data(struct iwl_trans *trans)
 {
index 508c813..a3bfda4 100644 (file)
@@ -1137,6 +1137,22 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
        return lut_type != BT_COEX_LOOSE_LUT;
 }
 
+bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant)
+{
+       /* there is no other antenna, shared antenna is always available */
+       if (mvm->cfg->bt_shared_single_ant)
+               return true;
+
+       if (ant & mvm->cfg->non_shared_ant)
+               return true;
+
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
+
+       return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+               BT_HIGH_TRAFFIC;
+}
+
 bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
 {
        /* there is no other antenna, shared antenna is always available */
index b571e1b..b3210cf 100644 (file)
@@ -612,7 +612,9 @@ int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
                                            BT_VALID_ANT_ISOLATION_THRS |
                                            BT_VALID_TXTX_DELTA_FREQ_THRS |
                                            BT_VALID_TXRX_MAX_FREQ_0 |
-                                           BT_VALID_SYNC_TO_SCO);
+                                           BT_VALID_SYNC_TO_SCO |
+                                           BT_VALID_TTC |
+                                           BT_VALID_RRC);
 
        if (IWL_MVM_BT_COEX_SYNC2SCO)
                bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
@@ -628,6 +630,12 @@ int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
                bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
        }
 
+       if (IWL_MVM_BT_COEX_TTC)
+               bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC);
+
+       if (IWL_MVM_BT_COEX_RRC)
+               bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC);
+
        if (mvm->cfg->bt_shared_single_ant)
                memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
                       sizeof(iwl_single_shared_ant));
@@ -824,6 +832,9 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        if (!vif->bss_conf.assoc)
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
+       if (data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id))
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
        IWL_DEBUG_COEX(data->mvm,
                       "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
                       mvmvif->id, data->notif->bt_status, bt_activity_grading,
@@ -1156,6 +1167,12 @@ bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
        return lut_type != BT_COEX_LOOSE_LUT;
 }
 
+bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant)
+{
+       u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+       return ag < BT_HIGH_TRAFFIC;
+}
+
 bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
 {
        u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
index 5c1ea80..3bd9347 100644 (file)
 #define IWL_MVM_BT_COEX_SYNC2SCO               1
 #define IWL_MVM_BT_COEX_CORUNNING              0
 #define IWL_MVM_BT_COEX_MPLUT                  1
-#define IWL_MVM_BT_COEX_MPLUT_REG0             0x2e402280
-#define IWL_MVM_BT_COEX_MPLUT_REG1             0x7711a751
+#define IWL_MVM_BT_COEX_RRC                    1
+#define IWL_MVM_BT_COEX_TTC                    1
+#define IWL_MVM_BT_COEX_MPLUT_REG0             0x28412201
+#define IWL_MVM_BT_COEX_MPLUT_REG1             0x11118451
 #define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS  30
 #define IWL_MVM_FW_MCAST_FILTER_PASS_ALL       0
 #define IWL_MVM_FW_BCAST_FILTER_PASS_ALL       0
index 3bbb511..744de26 100644 (file)
@@ -785,33 +785,19 @@ static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm)
        return iwl_mvm_load_d3_fw(mvm);
 }
 
-static int
-iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm,
-                              const struct iwl_wowlan_config_cmd_v3 *cmd)
-{
-       /* start only with the v2 part of the command */
-       u16 cmd_len = sizeof(cmd->common);
-
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID)
-               cmd_len = sizeof(*cmd);
-
-       return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
-                                   cmd_len, cmd);
-}
-
 static int
 iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
                          struct cfg80211_wowlan *wowlan,
-                         struct iwl_wowlan_config_cmd_v3 *wowlan_config_cmd,
+                         struct iwl_wowlan_config_cmd *wowlan_config_cmd,
                          struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
                          struct ieee80211_sta *ap_sta)
 {
        int ret;
        struct iwl_mvm_sta *mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
 
-       /* TODO: wowlan_config_cmd->common.wowlan_ba_teardown_tids */
+       /* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */
 
-       wowlan_config_cmd->common.is_11n_connection =
+       wowlan_config_cmd->is_11n_connection =
                                        ap_sta->ht_cap.ht_supported;
 
        /* Query the last used seqno and set it */
@@ -819,32 +805,32 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
        if (ret < 0)
                return ret;
 
-       wowlan_config_cmd->common.non_qos_seq = cpu_to_le16(ret);
+       wowlan_config_cmd->non_qos_seq = cpu_to_le16(ret);
 
-       iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &wowlan_config_cmd->common);
+       iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd);
 
        if (wowlan->disconnect)
-               wowlan_config_cmd->common.wakeup_filter |=
+               wowlan_config_cmd->wakeup_filter |=
                        cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
                                    IWL_WOWLAN_WAKEUP_LINK_CHANGE);
        if (wowlan->magic_pkt)
-               wowlan_config_cmd->common.wakeup_filter |=
+               wowlan_config_cmd->wakeup_filter |=
                        cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
        if (wowlan->gtk_rekey_failure)
-               wowlan_config_cmd->common.wakeup_filter |=
+               wowlan_config_cmd->wakeup_filter |=
                        cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
        if (wowlan->eap_identity_req)
-               wowlan_config_cmd->common.wakeup_filter |=
+               wowlan_config_cmd->wakeup_filter |=
                        cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
        if (wowlan->four_way_handshake)
-               wowlan_config_cmd->common.wakeup_filter |=
+               wowlan_config_cmd->wakeup_filter |=
                        cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
        if (wowlan->n_patterns)
-               wowlan_config_cmd->common.wakeup_filter |=
+               wowlan_config_cmd->wakeup_filter |=
                        cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
 
        if (wowlan->rfkill_release)
-               wowlan_config_cmd->common.wakeup_filter |=
+               wowlan_config_cmd->wakeup_filter |=
                        cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
 
        if (wowlan->tcp) {
@@ -852,7 +838,7 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
                 * Set the "link change" (really "link lost") flag as well
                 * since that implies losing the TCP connection.
                 */
-               wowlan_config_cmd->common.wakeup_filter |=
+               wowlan_config_cmd->wakeup_filter |=
                        cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS |
                                    IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE |
                                    IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET |
@@ -865,7 +851,7 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
 static int
 iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
                      struct cfg80211_wowlan *wowlan,
-                     struct iwl_wowlan_config_cmd_v3 *wowlan_config_cmd,
+                     struct iwl_wowlan_config_cmd *wowlan_config_cmd,
                      struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
                      struct ieee80211_sta *ap_sta)
 {
@@ -878,6 +864,10 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
        };
        int ret;
 
+       ret = iwl_mvm_switch_to_d3(mvm);
+       if (ret)
+               return ret;
+
        ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta);
        if (ret)
                return ret;
@@ -943,7 +933,9 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
                }
        }
 
-       ret = iwl_mvm_send_wowlan_config_cmd(mvm, wowlan_config_cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
+                                  sizeof(*wowlan_config_cmd),
+                                  wowlan_config_cmd);
        if (ret)
                goto out;
 
@@ -962,6 +954,68 @@ out:
        return ret;
 }
 
+static int
+iwl_mvm_netdetect_config(struct iwl_mvm *mvm,
+                        struct cfg80211_wowlan *wowlan,
+                        struct cfg80211_sched_scan_request *nd_config,
+                        struct ieee80211_vif *vif)
+{
+       struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+       int ret;
+
+       ret = iwl_mvm_switch_to_d3(mvm);
+       if (ret)
+               return ret;
+
+       /* rfkill release can be either for wowlan or netdetect */
+       if (wowlan->rfkill_release)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
+                                  sizeof(wowlan_config_cmd),
+                                  &wowlan_config_cmd);
+       if (ret)
+               return ret;
+
+       ret = iwl_mvm_scan_offload_start(mvm, vif, nd_config, &mvm->nd_ies);
+       if (ret)
+               return ret;
+
+       if (WARN_ON(mvm->nd_match_sets || mvm->nd_channels))
+               return -EBUSY;
+
+       /* save the sched scan matchsets... */
+       if (nd_config->n_match_sets) {
+               mvm->nd_match_sets = kmemdup(nd_config->match_sets,
+                                            sizeof(*nd_config->match_sets) *
+                                            nd_config->n_match_sets,
+                                            GFP_KERNEL);
+               if (mvm->nd_match_sets)
+                       mvm->n_nd_match_sets = nd_config->n_match_sets;
+       }
+
+       /* ...and the sched scan channels for later reporting */
+       mvm->nd_channels = kmemdup(nd_config->channels,
+                                  sizeof(*nd_config->channels) *
+                                  nd_config->n_channels,
+                                  GFP_KERNEL);
+       if (mvm->nd_channels)
+               mvm->n_nd_channels = nd_config->n_channels;
+
+       return 0;
+}
+
+static void iwl_mvm_free_nd(struct iwl_mvm *mvm)
+{
+       kfree(mvm->nd_match_sets);
+       mvm->nd_match_sets = NULL;
+       mvm->n_nd_match_sets = 0;
+       kfree(mvm->nd_channels);
+       mvm->nd_channels = NULL;
+       mvm->n_nd_channels = 0;
+}
+
 static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                             struct cfg80211_wowlan *wowlan,
                             bool test)
@@ -970,7 +1024,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        struct ieee80211_vif *vif = NULL;
        struct iwl_mvm_vif *mvmvif = NULL;
        struct ieee80211_sta *ap_sta = NULL;
-       struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {};
        struct iwl_d3_manager_config d3_cfg_cmd_data = {
                /*
                 * Program the minimum sleep time to 10 seconds, as many
@@ -1007,8 +1060,22 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
-       /* if we're associated, this is wowlan */
-       if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+       if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) {
+               /* if we're not associated, this must be netdetect */
+               if (!wowlan->nd_config && !mvm->nd_config) {
+                       ret = 1;
+                       goto out_noreset;
+               }
+
+               ret = iwl_mvm_netdetect_config(
+                       mvm, wowlan, wowlan->nd_config ?: mvm->nd_config, vif);
+               if (ret)
+                       goto out;
+
+               mvm->net_detect = true;
+       } else {
+               struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+
                ap_sta = rcu_dereference_protected(
                        mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
                        lockdep_is_held(&mvm->mutex));
@@ -1021,27 +1088,12 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                                                vif, mvmvif, ap_sta);
                if (ret)
                        goto out_noreset;
-
-               ret = iwl_mvm_switch_to_d3(mvm);
-               if (ret)
-                       goto out;
-
                ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd,
                                            vif, mvmvif, ap_sta);
                if (ret)
                        goto out;
-       } else if (mvm->nd_config) {
-               ret = iwl_mvm_switch_to_d3(mvm);
-               if (ret)
-                       goto out;
 
-               ret = iwl_mvm_scan_offload_start(mvm, vif, mvm->nd_config,
-                                                mvm->nd_ies);
-               if (ret)
-                       goto out;
-       } else {
-               ret = 1;
-               goto out_noreset;
+               mvm->net_detect = false;
        }
 
        ret = iwl_mvm_power_update_device(mvm);
@@ -1075,8 +1127,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        iwl_trans_d3_suspend(mvm->trans, test);
  out:
-       if (ret < 0)
+       if (ret < 0) {
                ieee80211_restart_hw(mvm->hw);
+               iwl_mvm_free_nd(mvm);
+       }
  out_noreset:
        mutex_unlock(&mvm->mutex);
 
@@ -1087,6 +1141,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
+       iwl_trans_suspend(mvm->trans);
        if (iwl_mvm_is_d0i3_supported(mvm)) {
                mutex_lock(&mvm->d0i3_suspend_mutex);
                __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
@@ -1465,9 +1520,8 @@ out:
        return true;
 }
 
-/* releases the MVM mutex */
-static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
-                                        struct ieee80211_vif *vif)
+static struct iwl_wowlan_status *
+iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
        u32 base = mvm->error_event_table;
        struct error_table_start {
@@ -1479,19 +1533,15 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                .id = WOWLAN_GET_STATUSES,
                .flags = CMD_WANT_SKB,
        };
-       struct iwl_wowlan_status_data status;
-       struct iwl_wowlan_status *fw_status;
-       int ret, len, status_size, i;
-       bool keep;
-       struct ieee80211_sta *ap_sta;
-       struct iwl_mvm_sta *mvm_ap_sta;
+       struct iwl_wowlan_status *status, *fw_status;
+       int ret, len, status_size;
 
        iwl_trans_read_mem_bytes(mvm->trans, base,
                                 &err_info, sizeof(err_info));
 
        if (err_info.valid) {
-               IWL_INFO(mvm, "error table is valid (%d)\n",
-                        err_info.valid);
+               IWL_INFO(mvm, "error table is valid (%d) with error (%d)\n",
+                        err_info.valid, err_info.error_id);
                if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
                        struct cfg80211_wowlan_wakeup wakeup = {
                                .rfkill_release = true,
@@ -1499,7 +1549,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                        ieee80211_report_wowlan_wakeup(vif, &wakeup,
                                                       GFP_KERNEL);
                }
-               goto out_unlock;
+               return ERR_PTR(-EIO);
        }
 
        /* only for tracing for now */
@@ -1510,22 +1560,53 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        ret = iwl_mvm_send_cmd(mvm, &cmd);
        if (ret) {
                IWL_ERR(mvm, "failed to query status (%d)\n", ret);
-               goto out_unlock;
+               return ERR_PTR(ret);
        }
 
        /* RF-kill already asserted again... */
-       if (!cmd.resp_pkt)
-               goto out_unlock;
+       if (!cmd.resp_pkt) {
+               ret = -ERFKILL;
+               goto out_free_resp;
+       }
 
        status_size = sizeof(*fw_status);
 
        len = iwl_rx_packet_payload_len(cmd.resp_pkt);
        if (len < status_size) {
                IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+               ret = -EIO;
                goto out_free_resp;
        }
 
-       fw_status = (void *)cmd.resp_pkt->data;
+       status = (void *)cmd.resp_pkt->data;
+       if (len != (status_size +
+                   ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) {
+               IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+               ret = -EIO;
+               goto out_free_resp;
+       }
+
+       fw_status = kmemdup(status, len, GFP_KERNEL);
+
+out_free_resp:
+       iwl_free_resp(&cmd);
+       return ret ? ERR_PTR(ret) : fw_status;
+}
+
+/* releases the MVM mutex */
+static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif)
+{
+       struct iwl_wowlan_status_data status;
+       struct iwl_wowlan_status *fw_status;
+       int i;
+       bool keep;
+       struct ieee80211_sta *ap_sta;
+       struct iwl_mvm_sta *mvm_ap_sta;
+
+       fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
+       if (IS_ERR_OR_NULL(fw_status))
+               goto out_unlock;
 
        status.pattern_number = le16_to_cpu(fw_status->pattern_number);
        for (i = 0; i < 8; i++)
@@ -1538,17 +1619,12 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                le32_to_cpu(fw_status->wake_packet_bufsize);
        status.wake_packet = fw_status->wake_packet;
 
-       if (len != status_size + ALIGN(status.wake_packet_bufsize, 4)) {
-               IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
-               goto out_free_resp;
-       }
-
        /* still at hard-coded place 0 for D3 image */
        ap_sta = rcu_dereference_protected(
                        mvm->fw_id_to_mac_id[0],
                        lockdep_is_held(&mvm->mutex));
        if (IS_ERR_OR_NULL(ap_sta))
-               goto out_free_resp;
+               goto out_free;
 
        mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
        for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
@@ -1565,16 +1641,151 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
 
        keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status);
 
-       iwl_free_resp(&cmd);
+       kfree(fw_status);
        return keep;
 
- out_free_resp:
-       iwl_free_resp(&cmd);
- out_unlock:
+out_free:
+       kfree(fw_status);
+out_unlock:
        mutex_unlock(&mvm->mutex);
        return false;
 }
 
+struct iwl_mvm_nd_query_results {
+       u32 matched_profiles;
+       struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
+};
+
+static int
+iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm,
+                               struct iwl_mvm_nd_query_results *results)
+{
+       struct iwl_scan_offload_profiles_query *query;
+       struct iwl_host_cmd cmd = {
+               .id = SCAN_OFFLOAD_PROFILES_QUERY_CMD,
+               .flags = CMD_WANT_SKB,
+       };
+       int ret, len;
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       if (ret) {
+               IWL_ERR(mvm, "failed to query matched profiles (%d)\n", ret);
+               return ret;
+       }
+
+       /* RF-kill already asserted again... */
+       if (!cmd.resp_pkt) {
+               ret = -ERFKILL;
+               goto out_free_resp;
+       }
+
+       len = iwl_rx_packet_payload_len(cmd.resp_pkt);
+       if (len < sizeof(*query)) {
+               IWL_ERR(mvm, "Invalid scan offload profiles query response!\n");
+               ret = -EIO;
+               goto out_free_resp;
+       }
+
+       query = (void *)cmd.resp_pkt->data;
+
+       results->matched_profiles = le32_to_cpu(query->matched_profiles);
+       memcpy(results->matches, query->matches, sizeof(results->matches));
+
+out_free_resp:
+       iwl_free_resp(&cmd);
+       return ret;
+}
+
+static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
+                                           struct ieee80211_vif *vif)
+{
+       struct cfg80211_wowlan_nd_info *net_detect = NULL;
+       struct cfg80211_wowlan_wakeup wakeup = {
+               .pattern_idx = -1,
+       };
+       struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
+       struct iwl_mvm_nd_query_results query;
+       struct iwl_wowlan_status *fw_status;
+       unsigned long matched_profiles;
+       u32 reasons = 0;
+       int i, j, n_matches, ret;
+
+       fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
+       if (!IS_ERR_OR_NULL(fw_status))
+               reasons = le32_to_cpu(fw_status->wakeup_reasons);
+
+       if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
+               wakeup.rfkill_release = true;
+
+       if (reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS)
+               goto out;
+
+       ret = iwl_mvm_netdetect_query_results(mvm, &query);
+       if (ret || !query.matched_profiles) {
+               wakeup_report = NULL;
+               goto out;
+       }
+
+       matched_profiles = query.matched_profiles;
+       if (mvm->n_nd_match_sets) {
+               n_matches = hweight_long(matched_profiles);
+       } else {
+               IWL_ERR(mvm, "no net detect match information available\n");
+               n_matches = 0;
+       }
+
+       net_detect = kzalloc(sizeof(*net_detect) +
+                            (n_matches * sizeof(net_detect->matches[0])),
+                            GFP_KERNEL);
+       if (!net_detect || !n_matches)
+               goto out_report_nd;
+
+       for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) {
+               struct iwl_scan_offload_profile_match *fw_match;
+               struct cfg80211_wowlan_nd_match *match;
+               int n_channels = 0;
+
+               fw_match = &query.matches[i];
+
+               for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; j++)
+                       n_channels += hweight8(fw_match->matching_channels[j]);
+
+               match = kzalloc(sizeof(*match) +
+                               (n_channels * sizeof(*match->channels)),
+                               GFP_KERNEL);
+               if (!match)
+                       goto out_report_nd;
+
+               net_detect->matches[net_detect->n_matches++] = match;
+
+               match->ssid.ssid_len = mvm->nd_match_sets[i].ssid.ssid_len;
+               memcpy(match->ssid.ssid, mvm->nd_match_sets[i].ssid.ssid,
+                      match->ssid.ssid_len);
+
+               if (mvm->n_nd_channels < n_channels)
+                       continue;
+
+               for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; j++)
+                       if (fw_match->matching_channels[j / 8] & (BIT(j % 8)))
+                               match->channels[match->n_channels++] =
+                                       mvm->nd_channels[j]->center_freq;
+       }
+
+out_report_nd:
+       wakeup.net_detect = net_detect;
+out:
+       iwl_mvm_free_nd(mvm);
+
+       mutex_unlock(&mvm->mutex);
+       ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
+
+       if (net_detect) {
+               for (i = 0; i < net_detect->n_matches; i++)
+                       kfree(net_detect->matches[i]);
+               kfree(net_detect);
+       }
+}
+
 static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
 {
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -1632,11 +1843,15 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
        /* query SRAM first in case we want event logging */
        iwl_mvm_read_d3_sram(mvm);
 
-       keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
+       if (mvm->net_detect) {
+               iwl_mvm_query_netdetect_reasons(mvm, vif);
+       } else {
+               keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (keep)
-               mvm->keep_vif = vif;
+               if (keep)
+                       mvm->keep_vif = vif;
 #endif
+       }
        /* has unlocked the mutex, so skip that */
        goto out;
 
@@ -1651,6 +1866,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
 
        /* return 1 to reconfigure the device */
        set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+       set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status);
        return 1;
 }
 
@@ -1658,18 +1874,10 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
-       if (iwl_mvm_is_d0i3_supported(mvm)) {
-               bool exit_now;
+       iwl_trans_resume(mvm->trans);
 
-               mutex_lock(&mvm->d0i3_suspend_mutex);
-               __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
-               exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
-                                               &mvm->d0i3_suspend_flags);
-               mutex_unlock(&mvm->d0i3_suspend_mutex);
-               if (exit_now)
-                       _iwl_mvm_exit_d0i3(mvm);
+       if (iwl_mvm_is_d0i3_supported(mvm))
                return 0;
-       }
 
        return __iwl_mvm_resume(mvm, false);
 }
index 51b7116..33bf915 100644 (file)
@@ -936,7 +936,11 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,
        if (scan_rx_ant & ~mvm->fw->valid_rx_ant)
                return -EINVAL;
 
-       mvm->scan_rx_ant = scan_rx_ant;
+       if (mvm->scan_rx_ant != scan_rx_ant) {
+               mvm->scan_rx_ant = scan_rx_ant;
+               if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+                       iwl_mvm_config_scan(mvm);
+       }
 
        return count;
 }
@@ -1194,14 +1198,8 @@ static ssize_t iwl_dbgfs_netdetect_write(struct iwl_mvm *mvm, char *buf,
                kfree(mvm->nd_config->match_sets);
                kfree(mvm->nd_config);
                mvm->nd_config = NULL;
-               kfree(mvm->nd_ies);
-               mvm->nd_ies = NULL;
        }
 
-       mvm->nd_ies = kzalloc(sizeof(*mvm->nd_ies), GFP_KERNEL);
-       if (!mvm->nd_ies)
-               return -ENOMEM;
-
        mvm->nd_config = kzalloc(sizeof(*mvm->nd_config) +
                                 (11 * sizeof(struct ieee80211_channel *)),
                                 GFP_KERNEL);
@@ -1258,8 +1256,6 @@ out_free:
                kfree(mvm->nd_config->match_sets);
        kfree(mvm->nd_config);
        mvm->nd_config = NULL;
-       kfree(mvm->nd_ies);
-       mvm->nd_ies = NULL;
 out:
        return ret;
 }
@@ -1343,6 +1339,7 @@ static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
        PRINT_MVM_REF(IWL_MVM_REF_NMI);
        PRINT_MVM_REF(IWL_MVM_REF_TM_CMD);
        PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK);
+       PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA);
 
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
index 816883f..f3b1189 100644 (file)
@@ -84,6 +84,8 @@
  * @BT_COEX_SYNC2SCO:
  * @BT_COEX_CORUNNING:
  * @BT_COEX_MPLUT:
+ * @BT_COEX_TTC:
+ * @BT_COEX_RRC:
  *
  * The COEX_MODE must be set for each command. Even if it is not changed.
  */
@@ -100,6 +102,8 @@ enum iwl_bt_coex_flags {
        BT_COEX_SYNC2SCO                = BIT(7),
        BT_COEX_CORUNNING               = BIT(8),
        BT_COEX_MPLUT                   = BIT(9),
+       BT_COEX_TTC                     = BIT(20),
+       BT_COEX_RRC                     = BIT(21),
 };
 
 /*
@@ -127,6 +131,8 @@ enum iwl_bt_coex_valid_bit_msk {
        BT_VALID_TXTX_DELTA_FREQ_THRS   = BIT(16),
        BT_VALID_TXRX_MAX_FREQ_0        = BIT(17),
        BT_VALID_SYNC_TO_SCO            = BIT(18),
+       BT_VALID_TTC                    = BIT(20),
+       BT_VALID_RRC                    = BIT(21),
 };
 
 /**
@@ -506,7 +512,8 @@ struct iwl_bt_coex_profile_notif_old {
        u8 bt_agg_traffic_load;
        u8 bt_ci_compliance;
        u8 ttc_enabled;
-       __le16 reserved;
+       u8 rrc_enabled;
+       u8 reserved;
 
        __le32 primary_ch_lut;
        __le32 secondary_ch_lut;
index e74cdf2..6d3bea5 100644 (file)
@@ -241,16 +241,12 @@ enum iwl_wowlan_wakeup_filters {
        IWL_WOWLAN_WAKEUP_BCN_FILTERING                 = BIT(16),
 }; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
 
-struct iwl_wowlan_config_cmd_v2 {
+struct iwl_wowlan_config_cmd {
        __le32 wakeup_filter;
        __le16 non_qos_seq;
        __le16 qos_seq[8];
        u8 wowlan_ba_teardown_tids;
        u8 is_11n_connection;
-} __packed; /* WOWLAN_CONFIG_API_S_VER_2 */
-
-struct iwl_wowlan_config_cmd_v3 {
-       struct iwl_wowlan_config_cmd_v2 common;
        u8 offloading_tid;
        u8 reserved[3];
 } __packed; /* WOWLAN_CONFIG_API_S_VER_3 */
index 2fd8ad4..4300200 100644 (file)
@@ -370,7 +370,7 @@ struct iwl_beacon_filter_cmd {
 #define IWL_BF_DEBUG_FLAG_DEFAULT 0
 #define IWL_BF_DEBUG_FLAG_D0I3 0
 
-#define IWL_BF_ESCAPE_TIMER_DEFAULT 50
+#define IWL_BF_ESCAPE_TIMER_DEFAULT 0
 #define IWL_BF_ESCAPE_TIMER_D0I3 0
 #define IWL_BF_ESCAPE_TIMER_MAX 1024
 #define IWL_BF_ESCAPE_TIMER_MIN 0
index 1354c68..1f2acf4 100644 (file)
@@ -794,4 +794,301 @@ struct iwl_periodic_scan_complete {
        __le32 reserved;
 } __packed;
 
+/* UMAC Scan API */
+
+/**
+ * struct iwl_mvm_umac_cmd_hdr - Command header for UMAC commands
+ * @size:      size of the command (not including header)
+ * @reserved0: for future use and alignment
+ * @ver:       API version number
+ */
+struct iwl_mvm_umac_cmd_hdr {
+       __le16 size;
+       u8 reserved0;
+       u8 ver;
+} __packed;
+
+#define IWL_MVM_MAX_SIMULTANEOUS_SCANS 8
+
+enum scan_config_flags {
+       SCAN_CONFIG_FLAG_ACTIVATE                       = BIT(0),
+       SCAN_CONFIG_FLAG_DEACTIVATE                     = BIT(1),
+       SCAN_CONFIG_FLAG_FORBID_CHUB_REQS               = BIT(2),
+       SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS                = BIT(3),
+       SCAN_CONFIG_FLAG_SET_TX_CHAINS                  = BIT(8),
+       SCAN_CONFIG_FLAG_SET_RX_CHAINS                  = BIT(9),
+       SCAN_CONFIG_FLAG_SET_AUX_STA_ID                 = BIT(10),
+       SCAN_CONFIG_FLAG_SET_ALL_TIMES                  = BIT(11),
+       SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES            = BIT(12),
+       SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS              = BIT(13),
+       SCAN_CONFIG_FLAG_SET_LEGACY_RATES               = BIT(14),
+       SCAN_CONFIG_FLAG_SET_MAC_ADDR                   = BIT(15),
+       SCAN_CONFIG_FLAG_SET_FRAGMENTED                 = BIT(16),
+       SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED               = BIT(17),
+       SCAN_CONFIG_FLAG_SET_CAM_MODE                   = BIT(18),
+       SCAN_CONFIG_FLAG_CLEAR_CAM_MODE                 = BIT(19),
+       SCAN_CONFIG_FLAG_SET_PROMISC_MODE               = BIT(20),
+       SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE             = BIT(21),
+
+       /* Bits 26-31 are for num of channels in channel_array */
+#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26)
+};
+
+enum scan_config_rates {
+       /* OFDM basic rates */
+       SCAN_CONFIG_RATE_6M     = BIT(0),
+       SCAN_CONFIG_RATE_9M     = BIT(1),
+       SCAN_CONFIG_RATE_12M    = BIT(2),
+       SCAN_CONFIG_RATE_18M    = BIT(3),
+       SCAN_CONFIG_RATE_24M    = BIT(4),
+       SCAN_CONFIG_RATE_36M    = BIT(5),
+       SCAN_CONFIG_RATE_48M    = BIT(6),
+       SCAN_CONFIG_RATE_54M    = BIT(7),
+       /* CCK basic rates */
+       SCAN_CONFIG_RATE_1M     = BIT(8),
+       SCAN_CONFIG_RATE_2M     = BIT(9),
+       SCAN_CONFIG_RATE_5M     = BIT(10),
+       SCAN_CONFIG_RATE_11M    = BIT(11),
+
+       /* Bits 16-27 are for supported rates */
+#define SCAN_CONFIG_SUPPORTED_RATE(rate)       ((rate) << 16)
+};
+
+enum iwl_channel_flags {
+       IWL_CHANNEL_FLAG_EBS                            = BIT(0),
+       IWL_CHANNEL_FLAG_ACCURATE_EBS                   = BIT(1),
+       IWL_CHANNEL_FLAG_EBS_ADD                        = BIT(2),
+       IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE        = BIT(3),
+};
+
+/**
+ * struct iwl_scan_config
+ * @hdr: umac command header
+ * @flags:                     enum scan_config_flags
+ * @tx_chains:                 valid_tx antenna - ANT_* definitions
+ * @rx_chains:                 valid_rx antenna - ANT_* definitions
+ * @legacy_rates:              default legacy rates - enum scan_config_rates
+ * @out_of_channel_time:       default max out of serving channel time
+ * @suspend_time:              default max suspend time
+ * @dwell_active:              default dwell time for active scan
+ * @dwell_passive:             default dwell time for passive scan
+ * @dwell_fragmented:          default dwell time for fragmented scan
+ * @reserved:                  for future use and alignment
+ * @mac_addr:                  default mac address to be used in probes
+ * @bcast_sta_id:              the index of the station in the fw
+ * @channel_flags:             default channel flags - enum iwl_channel_flags
+ *                             scan_config_channel_flag
+ * @channel_array:             default supported channels
+ */
+struct iwl_scan_config {
+       struct iwl_mvm_umac_cmd_hdr hdr;
+       __le32 flags;
+       __le32 tx_chains;
+       __le32 rx_chains;
+       __le32 legacy_rates;
+       __le32 out_of_channel_time;
+       __le32 suspend_time;
+       u8 dwell_active;
+       u8 dwell_passive;
+       u8 dwell_fragmented;
+       u8 reserved;
+       u8 mac_addr[ETH_ALEN];
+       u8 bcast_sta_id;
+       u8 channel_flags;
+       u8 channel_array[];
+} __packed; /* SCAN_CONFIG_DB_CMD_API_S */
+
+/**
+ * iwl_umac_scan_flags
+ *@IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request
+ *     can be preempted by other scan requests with higher priority.
+ *     The low priority scan is aborted.
+ *@IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver
+ *     when scan starts.
+ */
+enum iwl_umac_scan_flags {
+       IWL_UMAC_SCAN_FLAG_PREEMPTIVE           = BIT(0),
+       IWL_UMAC_SCAN_FLAG_START_NOTIF          = BIT(1),
+};
+
+enum iwl_umac_scan_uid_offsets {
+       IWL_UMAC_SCAN_UID_TYPE_OFFSET           = 0,
+       IWL_UMAC_SCAN_UID_SEQ_OFFSET            = 8,
+};
+
+enum iwl_umac_scan_general_flags {
+       IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC        = BIT(0),
+       IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT         = BIT(1),
+       IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL        = BIT(2),
+       IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE         = BIT(3),
+       IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT     = BIT(4),
+       IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE   = BIT(5),
+       IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID   = BIT(6),
+       IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED      = BIT(7),
+       IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED     = BIT(8),
+       IWL_UMAC_SCAN_GEN_FLAGS_MATCH           = BIT(9)
+};
+
+/**
+ * struct iwl_scan_channel_cfg_umac
+ * @flags:             bitmap - 0-19:  directed scan to i'th ssid.
+ * @channel_num:       channel number 1-13 etc.
+ * @iter_count:                repetition count for the channel.
+ * @iter_interval:     interval between two scan interations on one channel.
+ */
+struct iwl_scan_channel_cfg_umac {
+       __le32 flags;
+       u8 channel_num;
+       u8 iter_count;
+       __le16 iter_interval;
+} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */
+
+/**
+ * struct iwl_scan_umac_schedule
+ * @interval: interval in seconds between scan iterations
+ * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop
+ * @reserved: for alignment and future use
+ */
+struct iwl_scan_umac_schedule {
+       __le16 interval;
+       u8 iter_count;
+       u8 reserved;
+} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */
+
+/**
+ * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command
+ *      parameters following channels configuration array.
+ * @schedule: two scheduling plans.
+ * @delay: delay in TUs before starting the first scan iteration
+ * @reserved: for future use and alignment
+ * @preq: probe request with IEs blocks
+ * @direct_scan: list of SSIDs for directed active scan
+ */
+struct iwl_scan_req_umac_tail {
+       /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */
+       struct iwl_scan_umac_schedule schedule[2];
+       __le16 delay;
+       __le16 reserved;
+       /* SCAN_PROBE_PARAMS_API_S_VER_1 */
+       struct iwl_scan_probe_req preq;
+       struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+} __packed;
+
+/**
+ * struct iwl_scan_req_umac
+ * @hdr: umac command header
+ * @flags: &enum iwl_umac_scan_flags
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @ooc_priority: out of channel priority - &enum iwl_scan_priority
+ * @general_flags: &enum iwl_umac_scan_general_flags
+ * @reserved1: for future use and alignment
+ * @active_dwell: dwell time for active scan
+ * @passive_dwell: dwell time for passive scan
+ * @fragmented_dwell: dwell time for fragmented passive scan
+ * @max_out_time: max out of serving channel time
+ * @suspend_time: max suspend time
+ * @scan_priority: scan internal prioritization &enum iwl_scan_priority
+ * @channel_flags: &enum iwl_scan_channel_flags
+ * @n_channels: num of channels in scan request
+ * @reserved2: for future use and alignment
+ * @data: &struct iwl_scan_channel_cfg_umac and
+ *     &struct iwl_scan_req_umac_tail
+ */
+struct iwl_scan_req_umac {
+       struct iwl_mvm_umac_cmd_hdr hdr;
+       __le32 flags;
+       __le32 uid;
+       __le32 ooc_priority;
+       /* SCAN_GENERAL_PARAMS_API_S_VER_1 */
+       __le32 general_flags;
+       u8 reserved1;
+       u8 active_dwell;
+       u8 passive_dwell;
+       u8 fragmented_dwell;
+       __le32 max_out_time;
+       __le32 suspend_time;
+       __le32 scan_priority;
+       /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
+       u8 channel_flags;
+       u8 n_channels;
+       __le16 reserved2;
+       u8 data[];
+} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */
+
+/**
+ * struct iwl_umac_scan_abort
+ * @hdr: umac command header
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @flags: reserved
+ */
+struct iwl_umac_scan_abort {
+       struct iwl_mvm_umac_cmd_hdr hdr;
+       __le32 uid;
+       __le32 flags;
+} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */
+
+/**
+ * struct iwl_umac_scan_complete
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @last_schedule: last scheduling line
+ * @last_iter: last scan iteration number
+ * @scan status: &enum iwl_scan_offload_complete_status
+ * @ebs_status: &enum iwl_scan_ebs_status
+ * @time_from_last_iter: time elapsed from last iteration
+ * @reserved: for future use
+ */
+struct iwl_umac_scan_complete {
+       __le32 uid;
+       u8 last_schedule;
+       u8 last_iter;
+       u8 status;
+       u8 ebs_status;
+       __le32 time_from_last_iter;
+       __le32 reserved;
+} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */
+
+#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5
+/**
+ * struct iwl_scan_offload_profile_match - match information
+ * @bssid: matched bssid
+ * @channel: channel where the match occurred
+ * @energy:
+ * @matching_feature:
+ * @matching_channels: bitmap of channels that matched, referencing
+ *     the channels passed in tue scan offload request
+ */
+struct iwl_scan_offload_profile_match {
+       u8 bssid[ETH_ALEN];
+       __le16 reserved;
+       u8 channel;
+       u8 energy;
+       u8 matching_feature;
+       u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN];
+} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */
+
+/**
+ * struct iwl_scan_offload_profiles_query - match results query response
+ * @matched_profiles: bitmap of matched profiles, referencing the
+ *     matches passed in the scan offload request
+ * @last_scan_age: age of the last offloaded scan
+ * @n_scans_done: number of offloaded scans done
+ * @gp2_d0u: GP2 when D0U occurred
+ * @gp2_invoked: GP2 when scan offload was invoked
+ * @resume_while_scanning: not used
+ * @self_recovery: obsolete
+ * @reserved: reserved
+ * @matches: array of match information, one for each match
+ */
+struct iwl_scan_offload_profiles_query {
+       __le32 matched_profiles;
+       __le32 last_scan_age;
+       __le32 n_scans_done;
+       __le32 gp2_d0u;
+       __le32 gp2_invoked;
+       u8 resume_while_scanning;
+       u8 self_recovery;
+       __le16 reserved;
+       struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
+} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */
+
 #endif
index c62575d..88af6dd 100644 (file)
@@ -106,6 +106,12 @@ enum {
        DBG_CFG = 0x9,
        ANTENNA_COUPLING_NOTIFICATION = 0xa,
 
+       /* UMAC scan commands */
+       SCAN_CFG_CMD = 0xc,
+       SCAN_REQ_UMAC = 0xd,
+       SCAN_ABORT_UMAC = 0xe,
+       SCAN_COMPLETE_UMAC = 0xf,
+
        /* station table */
        ADD_STA_KEY = 0x17,
        ADD_STA = 0x18,
@@ -122,6 +128,11 @@ enum {
        /* global key */
        WEP_KEY = 0x20,
 
+       /* TDLS */
+       TDLS_CHANNEL_SWITCH_CMD = 0x27,
+       TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa,
+       TDLS_CONFIG_CMD = 0xa7,
+
        /* MAC and Binding commands */
        MAC_CONTEXT_CMD = 0x28,
        TIME_EVENT_CMD = 0x29, /* both CMD and response */
@@ -190,6 +201,8 @@ enum {
        /* Power - new power table command */
        MAC_PM_POWER_TABLE = 0xa9,
 
+       MFUART_LOAD_NOTIFICATION = 0xb1,
+
        REPLY_RX_PHY_CMD = 0xc0,
        REPLY_RX_MPDU_CMD = 0xc1,
        BA_NOTIF = 0xc5,
@@ -236,11 +249,9 @@ enum {
        WOWLAN_TX_POWER_PER_DB = 0xe6,
 
        /* and for NetDetect */
-       NET_DETECT_CONFIG_CMD = 0x54,
-       NET_DETECT_PROFILES_QUERY_CMD = 0x56,
-       NET_DETECT_PROFILES_CMD = 0x57,
-       NET_DETECT_HOTSPOTS_CMD = 0x58,
-       NET_DETECT_HOTSPOTS_QUERY_CMD = 0x59,
+       SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56,
+       SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD = 0x58,
+       SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD = 0x59,
 
        REPLY_MAX = 0xff,
 };
@@ -1200,6 +1211,21 @@ struct iwl_missed_beacons_notif {
        __le32 num_recvd_beacons;
 } __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */
 
+/**
+ * struct iwl_mfuart_load_notif - mfuart image version & status
+ * ( MFUART_LOAD_NOTIFICATION = 0xb1 )
+ * @installed_ver: installed image version
+ * @external_ver: external image version
+ * @status: MFUART loading status
+ * @duration: MFUART loading time
+*/
+struct iwl_mfuart_load_notif {
+       __le32 installed_ver;
+       __le32 external_ver;
+       __le32 status;
+       __le32 duration;
+} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/
+
 /**
  * struct iwl_set_calib_default_cmd - set default value for calibration.
  * ( SET_CALIB_DEFAULT_CMD = 0x8e )
@@ -1589,7 +1615,7 @@ enum iwl_sf_scenario {
 #define SF_NUM_TIMEOUT_TYPES 2         /* Aging timer and Idle timer */
 
 /* smart FIFO default values */
-#define SF_W_MARK_SISO 4096
+#define SF_W_MARK_SISO 6144
 #define SF_W_MARK_MIMO2 8192
 #define SF_W_MARK_MIMO3 6144
 #define SF_W_MARK_LEGACY 4096
@@ -1711,4 +1737,145 @@ struct iwl_scd_txq_cfg_cmd {
        u8 flags;
 } __packed;
 
+/***********************************
+ * TDLS API
+ ***********************************/
+
+/* Type of TDLS request */
+enum iwl_tdls_channel_switch_type {
+       TDLS_SEND_CHAN_SW_REQ = 0,
+       TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH,
+       TDLS_MOVE_CH,
+}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */
+
+/**
+ * Switch timing sub-element in a TDLS channel-switch command
+ * @frame_timestamp: GP2 timestamp of channel-switch request/response packet
+ *     received from peer
+ * @max_offchan_duration: What amount of microseconds out of a DTIM is given
+ *     to the TDLS off-channel communication. For instance if the DTIM is
+ *     200TU and the TDLS peer is to be given 25% of the time, the value
+ *     given will be 50TU, or 50 * 1024 if translated into microseconds.
+ * @switch_time: switch time the peer sent in its channel switch timing IE
+ * @switch_timout: switch timeout the peer sent in its channel switch timing IE
+ */
+struct iwl_tdls_channel_switch_timing {
+       __le32 frame_timestamp; /* GP2 time of peer packet Rx */
+       __le32 max_offchan_duration; /* given in micro-seconds */
+       __le32 switch_time; /* given in micro-seconds */
+       __le32 switch_timeout; /* given in micro-seconds */
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */
+
+#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200
+
+/**
+ * TDLS channel switch frame template
+ *
+ * A template representing a TDLS channel-switch request or response frame
+ *
+ * @switch_time_offset: offset to the channel switch timing IE in the template
+ * @tx_cmd: Tx parameters for the frame
+ * @data: frame data
+ */
+struct iwl_tdls_channel_switch_frame {
+       __le32 switch_time_offset;
+       struct iwl_tx_cmd tx_cmd;
+       u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE];
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */
+
+/**
+ * TDLS channel switch command
+ *
+ * The command is sent to initiate a channel switch and also in response to
+ * incoming TDLS channel-switch request/response packets from remote peers.
+ *
+ * @switch_type: see &enum iwl_tdls_channel_switch_type
+ * @peer_sta_id: station id of TDLS peer
+ * @ci: channel we switch to
+ * @timing: timing related data for command
+ * @frame: channel-switch request/response template, depending to switch_type
+ */
+struct iwl_tdls_channel_switch_cmd {
+       u8 switch_type;
+       __le32 peer_sta_id;
+       struct iwl_fw_channel_info ci;
+       struct iwl_tdls_channel_switch_timing timing;
+       struct iwl_tdls_channel_switch_frame frame;
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */
+
+/**
+ * TDLS channel switch start notification
+ *
+ * @status: non-zero on success
+ * @offchannel_duration: duration given in microseconds
+ * @sta_id: peer currently performing the channel-switch with
+ */
+struct iwl_tdls_channel_switch_notif {
+       __le32 status;
+       __le32 offchannel_duration;
+       __le32 sta_id;
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */
+
+/**
+ * TDLS station info
+ *
+ * @sta_id: station id of the TDLS peer
+ * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx
+ * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer
+ * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise
+ */
+struct iwl_tdls_sta_info {
+       u8 sta_id;
+       u8 tx_to_peer_tid;
+       __le16 tx_to_peer_ssn;
+       __le32 is_initiator;
+} __packed; /* TDLS_STA_INFO_VER_1 */
+
+/**
+ * TDLS basic config command
+ *
+ * @id_and_color: MAC id and color being configured
+ * @tdls_peer_count: amount of currently connected TDLS peers
+ * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx
+ * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP
+ * @sta_info: per-station info. Only the first tdls_peer_count entries are set
+ * @pti_req_data_offset: offset of network-level data for the PTI template
+ * @pti_req_tx_cmd: Tx parameters for PTI request template
+ * @pti_req_template: PTI request template data
+ */
+struct iwl_tdls_config_cmd {
+       __le32 id_and_color; /* mac id and color */
+       u8 tdls_peer_count;
+       u8 tx_to_ap_tid;
+       __le16 tx_to_ap_ssn;
+       struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT];
+
+       __le32 pti_req_data_offset;
+       struct iwl_tx_cmd pti_req_tx_cmd;
+       u8 pti_req_template[0];
+} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */
+
+/**
+ * TDLS per-station config information from FW
+ *
+ * @sta_id: station id of the TDLS peer
+ * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to
+ *     the peer
+ */
+struct iwl_tdls_config_sta_info_res {
+       __le16 sta_id;
+       __le16 tx_to_peer_last_seq;
+} __packed; /* TDLS_STA_INFO_RSP_VER_1 */
+
+/**
+ * TDLS config information from FW
+ *
+ * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP
+ * @sta_info: per-station TDLS config information
+ */
+struct iwl_tdls_config_res {
+       __le32 tx_to_ap_last_seq;
+       struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT];
+} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */
+
 #endif /* __fw_api_h__ */
index eb03943..d0fa6e9 100644 (file)
@@ -186,7 +186,12 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
        static const u8 alive_cmd[] = { MVM_ALIVE };
        struct iwl_sf_region st_fwrd_space;
 
-       fw = iwl_get_ucode_image(mvm, ucode_type);
+       if (ucode_type == IWL_UCODE_REGULAR &&
+           iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_CUSTOM) &&
+           iwl_fw_dbg_conf_enabled(mvm->fw, FW_DBG_CUSTOM))
+               fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
+       else
+               fw = iwl_get_ucode_image(mvm, ucode_type);
        if (WARN_ON(!fw))
                return -EINVAL;
        mvm->cur_ucode = ucode_type;
@@ -227,6 +232,10 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
        st_fwrd_space.addr = mvm->sf_space.addr;
        st_fwrd_space.size = mvm->sf_space.size;
        ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to update SF size. ret %d\n", ret);
+               return ret;
+       }
 
        iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
 
@@ -390,6 +399,42 @@ out:
        return ret;
 }
 
+static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm,
+                                    enum iwl_fw_dbg_conf conf_id)
+{
+       u8 *ptr;
+       int ret;
+       int i;
+
+       if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv),
+                     "Invalid configuration %d\n", conf_id))
+               return -EINVAL;
+
+       if (!mvm->fw->dbg_conf_tlv[conf_id])
+               return -EINVAL;
+
+       if (mvm->fw_dbg_conf != FW_DBG_INVALID)
+               IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n",
+                        mvm->fw_dbg_conf);
+
+       /* Send all HCMDs for configuring the FW debug */
+       ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd;
+       for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
+               struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
+
+               ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0,
+                                          le16_to_cpu(cmd->len), cmd->data);
+               if (ret)
+                       return ret;
+
+               ptr += sizeof(*cmd);
+               ptr += le16_to_cpu(cmd->len);
+       }
+
+       mvm->fw_dbg_conf = conf_id;
+       return ret;
+}
+
 int iwl_mvm_up(struct iwl_mvm *mvm)
 {
        int ret, i;
@@ -441,6 +486,9 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (ret)
                IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
 
+       mvm->fw_dbg_conf = FW_DBG_INVALID;
+       iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM);
+
        ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
        if (ret)
                goto error;
@@ -462,6 +510,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
                RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
 
+       mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+
        /* reset quota debouncing buffer - 0xff will yield invalid data */
        memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd));
 
@@ -501,6 +551,12 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (ret)
                goto error;
 
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+               ret = iwl_mvm_config_scan(mvm);
+               if (ret)
+                       goto error;
+       }
+
        /* allow FW/transport low power modes if not during restart */
        if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
                iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
@@ -587,3 +643,19 @@ int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                       le32_to_cpu(radio_version->radio_dash));
        return 0;
 }
+
+int iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm,
+                           struct iwl_rx_cmd_buffer *rxb,
+                           struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data;
+
+       IWL_DEBUG_INFO(mvm,
+                      "MFUART: installed ver: 0x%08x, external ver: 0x%08x, status: 0x%08x, duration: 0x%08x\n",
+                      le32_to_cpu(mfuart_notif->installed_ver),
+                      le32_to_cpu(mfuart_notif->external_ver),
+                      le32_to_cpu(mfuart_notif->status),
+                      le32_to_cpu(mfuart_notif->duration));
+       return 0;
+}
index b8ab4a1..f6d86cc 100644 (file)
@@ -83,11 +83,15 @@ struct iwl_mvm_mac_iface_iterator_data {
        struct ieee80211_vif *vif;
        unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)];
        unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)];
-       u32 used_hw_queues;
        enum iwl_tsf_id preferred_tsf;
        bool found_vif;
 };
 
+struct iwl_mvm_hw_queues_iface_iterator_data {
+       struct ieee80211_vif *exclude_vif;
+       unsigned long used_hw_queues;
+};
+
 static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
                                    struct ieee80211_vif *vif)
 {
@@ -213,6 +217,54 @@ u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif)
        return qmask;
 }
 
+static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac,
+                                        struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
+
+       /* exclude the given vif */
+       if (vif == data->exclude_vif)
+               return;
+
+       data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif);
+}
+
+static void iwl_mvm_mac_sta_hw_queues_iter(void *_data,
+                                          struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+       /* Mark the queues used by the sta */
+       data->used_hw_queues |= mvmsta->tfd_queue_msk;
+}
+
+unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *exclude_vif)
+{
+       struct iwl_mvm_hw_queues_iface_iterator_data data = {
+               .exclude_vif = exclude_vif,
+               .used_hw_queues =
+                       BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
+                       BIT(mvm->aux_queue) |
+                       BIT(IWL_MVM_CMD_QUEUE),
+       };
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* mark all VIF used hw queues */
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+               iwl_mvm_iface_hw_queues_iter, &data);
+
+       /* don't assign the same hw queues as TDLS stations */
+       ieee80211_iterate_stations_atomic(mvm->hw,
+                                         iwl_mvm_mac_sta_hw_queues_iter,
+                                         &data);
+
+       return data.used_hw_queues;
+}
+
 static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
                                       struct ieee80211_vif *vif)
 {
@@ -225,9 +277,6 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
                return;
        }
 
-       /* Mark the queues used by the vif */
-       data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif);
-
        /* Mark MAC IDs as used by clearing the available bit, and
         * (below) mark TSFs as used if their existing use is not
         * compatible with the new interface type.
@@ -274,10 +323,6 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
                .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
                /* no preference yet */
                .preferred_tsf = NUM_TSF_IDS,
-               .used_hw_queues =
-                       BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
-                       BIT(mvm->aux_queue) |
-                       BIT(IWL_MVM_CMD_QUEUE),
                .found_vif = false,
        };
        u32 ac;
@@ -316,6 +361,8 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
                mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
                iwl_mvm_mac_iface_iterator, &data);
 
+       used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, vif);
+
        /*
         * In the case we're getting here during resume, it's similar to
         * firmware restart, and with RESUME_ALL the iterator will find
@@ -365,8 +412,6 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
                return 0;
        }
 
-       used_hw_queues = data.used_hw_queues;
-
        /* Find available queues, and allocate them to the ACs */
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                u8 queue = find_first_zero_bit(&used_hw_queues,
@@ -1218,17 +1263,25 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 }
 
 static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
-                                  struct ieee80211_vif *csa_vif, u32 gp2)
+                                  struct ieee80211_vif *csa_vif, u32 gp2,
+                                  bool tx_success)
 {
        struct iwl_mvm_vif *mvmvif =
                        iwl_mvm_vif_from_mac80211(csa_vif);
 
+       /* Don't start to countdown from a failed beacon */
+       if (!tx_success && !mvmvif->csa_countdown)
+               return;
+
+       mvmvif->csa_countdown = true;
+
        if (!ieee80211_csa_is_complete(csa_vif)) {
                int c = ieee80211_csa_update_counter(csa_vif);
 
                iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
                if (csa_vif->p2p &&
-                   !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2) {
+                   !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 &&
+                   tx_success) {
                        u32 rel_time = (c + 1) *
                                       csa_vif->bss_conf.beacon_int -
                                       IWL_MVM_CHANNEL_SWITCH_TIME_GO;
@@ -1251,38 +1304,30 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
                            struct iwl_device_cmd *cmd)
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
        struct iwl_mvm_tx_resp *beacon_notify_hdr;
        struct ieee80211_vif *csa_vif;
        struct ieee80211_vif *tx_blocked_vif;
-       u64 tsf;
+       u16 status;
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_CAPA_EXTENDED_BEACON) {
-               struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
-
-               beacon_notify_hdr = &beacon->beacon_notify_hdr;
-               tsf = le64_to_cpu(beacon->tsf);
-               mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
-       } else {
-               struct iwl_beacon_notif *beacon = (void *)pkt->data;
-
-               beacon_notify_hdr = &beacon->beacon_notify_hdr;
-               tsf = le64_to_cpu(beacon->tsf);
-       }
+       beacon_notify_hdr = &beacon->beacon_notify_hdr;
+       mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
 
+       status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK;
        IWL_DEBUG_RX(mvm,
                     "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
-                    le16_to_cpu(beacon_notify_hdr->status.status) &
-                                                               TX_STATUS_MSK,
-                    beacon_notify_hdr->failure_frame, tsf,
+                    status, beacon_notify_hdr->failure_frame,
+                    le64_to_cpu(beacon->tsf),
                     mvm->ap_last_beacon_gp2,
                     le32_to_cpu(beacon_notify_hdr->initial_rate));
 
        csa_vif = rcu_dereference_protected(mvm->csa_vif,
                                            lockdep_is_held(&mvm->mutex));
        if (unlikely(csa_vif && csa_vif->csa_active))
-               iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
+               iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2,
+                                      (status == TX_STATUS_SUCCESS));
 
        tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
                                                lockdep_is_held(&mvm->mutex));
index 1ee9dcd..31a5b3f 100644 (file)
@@ -254,6 +254,26 @@ static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm,
        spin_unlock_bh(&mvm->refs_lock);
 }
 
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm)
+{
+       int i;
+       bool taken = false;
+
+       if (!iwl_mvm_is_d0i3_supported(mvm))
+               return true;
+
+       spin_lock_bh(&mvm->refs_lock);
+       for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
+               if (mvm->refs[i]) {
+                       taken = true;
+                       break;
+               }
+       }
+       spin_unlock_bh(&mvm->refs_lock);
+
+       return taken;
+}
+
 int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
 {
        iwl_mvm_ref(mvm, ref_type);
@@ -303,7 +323,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
        hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
                                    IEEE80211_RADIOTAP_MCS_HAVE_STBC;
-       hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC;
+       hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
+               IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
        hw->rate_control_algorithm = "iwl-mvm-rs";
 
        /*
@@ -316,15 +337,19 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
        if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
-           IWL_UCODE_API(mvm->fw->ucode_ver) >= 9 &&
            !iwlwifi_mod_params.uapsd_disable) {
                hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
                hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
                hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
        }
 
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
+           mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
                hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
+               hw->wiphy->features |=
+                       NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+                       NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+       }
 
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
@@ -344,8 +369,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
                hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW)
-               hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+       hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 
        hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
        hw->wiphy->n_iface_combinations =
@@ -403,7 +427,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                               NL80211_FEATURE_LOW_PRIORITY_SCAN |
                               NL80211_FEATURE_P2P_GO_OPPPS |
                               NL80211_FEATURE_DYNAMIC_SMPS |
-                              NL80211_FEATURE_STATIC_SMPS;
+                              NL80211_FEATURE_STATIC_SMPS |
+                              NL80211_FEATURE_SUPPORTS_WMM_ADMISSION;
 
        if (mvm->fw->ucode_capa.capa[0] &
            IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT)
@@ -441,7 +466,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
                                    WIPHY_WOWLAN_DISCONNECT |
                                    WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                   WIPHY_WOWLAN_RFKILL_RELEASE;
+                                   WIPHY_WOWLAN_RFKILL_RELEASE |
+                                   WIPHY_WOWLAN_NET_DETECT;
                if (!iwlwifi_mod_params.sw_crypto)
                        mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
                                             WIPHY_WOWLAN_GTK_REKEY_FAILURE |
@@ -450,6 +476,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                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.max_nd_match_sets = IWL_SCAN_MAX_PROFILES;
                mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
                hw->wiphy->wowlan = &mvm->wowlan;
        }
@@ -464,6 +491,17 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        if (ret)
                return ret;
 
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_TDLS_SUPPORT) {
+               IWL_DEBUG_TDLS(mvm, "TDLS supported\n");
+               hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+       }
+
+       if (mvm->fw->ucode_capa.capa[0] &
+           IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH) {
+               IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n");
+               hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+       }
+
        ret = ieee80211_register_hw(mvm->hw);
        if (ret)
                iwl_mvm_leds_exit(mvm);
@@ -819,7 +857,12 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
-       iwl_mvm_fw_error_dump(mvm);
+       /* clear the D3 reconfig, we only need it to avoid dumping a
+        * firmware coredump on reconfiguration, we shouldn't do that
+        * on D3->D0 transition
+        */
+       if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status))
+               iwl_mvm_fw_error_dump(mvm);
 
        iwl_trans_stop_device(mvm->trans);
 
@@ -840,6 +883,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
        iwl_mvm_reset_phy_ctxts(mvm);
        memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
        memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
+       memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained));
        memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
        memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
        memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
@@ -912,9 +956,34 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
        /* allow transport/FW low power modes */
        iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
 
+       /*
+        * If we have TDLS peers, remove them. We don't know the last seqno/PN
+        * of packets the FW sent out, so we must reconnect.
+        */
+       iwl_mvm_teardown_tdls_peers(mvm);
+
        mutex_unlock(&mvm->mutex);
 }
 
+static void iwl_mvm_resume_complete(struct iwl_mvm *mvm)
+{
+       bool exit_now;
+
+       if (!iwl_mvm_is_d0i3_supported(mvm))
+               return;
+
+       mutex_lock(&mvm->d0i3_suspend_mutex);
+       __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+       exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
+                                       &mvm->d0i3_suspend_flags);
+       mutex_unlock(&mvm->d0i3_suspend_mutex);
+
+       if (exit_now) {
+               IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
+               _iwl_mvm_exit_d0i3(mvm);
+       }
+}
+
 static void
 iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
                              enum ieee80211_reconfig_type reconfig_type)
@@ -926,6 +995,7 @@ iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
                iwl_mvm_restart_complete(mvm);
                break;
        case IEEE80211_RECONFIG_TYPE_SUSPEND:
+               iwl_mvm_resume_complete(mvm);
                break;
        }
 }
@@ -1889,9 +1959,11 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
            req->n_channels > mvm->fw->ucode_capa.n_scan_channels)
                return -EINVAL;
 
-       ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
-       if (ret)
-               return ret;
+       if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+               ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
+               if (ret)
+                       return ret;
+       }
 
        mutex_lock(&mvm->mutex);
 
@@ -1902,7 +1974,9 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
 
        iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
 
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+               ret = iwl_mvm_scan_umac(mvm, vif, hw_req);
+       else if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
                ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
        else
                ret = iwl_mvm_scan_request(mvm, vif, req);
@@ -2119,6 +2193,15 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
  out_unlock:
        mutex_unlock(&mvm->mutex);
 
+       if (sta->tdls && ret == 0) {
+               if (old_state == IEEE80211_STA_NOTEXIST &&
+                   new_state == IEEE80211_STA_NONE)
+                       ieee80211_reserve_tid(sta, IWL_MVM_TDLS_FW_TID);
+               else if (old_state == IEEE80211_STA_NONE &&
+                        new_state == IEEE80211_STA_NOTEXIST)
+                       ieee80211_unreserve_tid(sta, IWL_MVM_TDLS_FW_TID);
+       }
+
        return ret;
 }
 
@@ -2201,9 +2284,11 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
 
-       ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
-       if (ret)
-               return ret;
+       if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+               ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
+               if (ret)
+                       return ret;
+       }
 
        mutex_lock(&mvm->mutex);
 
@@ -2223,11 +2308,10 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
                goto out;
        }
 
-       mvm->scan_status = IWL_MVM_SCAN_SCHED;
-
        ret = iwl_mvm_scan_offload_start(mvm, vif, req, ies);
        if (ret)
                mvm->scan_status = IWL_MVM_SCAN_NONE;
+
 out:
        mutex_unlock(&mvm->mutex);
        return ret;
@@ -2245,6 +2329,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
        iwl_mvm_wait_for_async_handlers(mvm);
 
        return ret;
+
 }
 
 static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
@@ -2273,12 +2358,16 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
                break;
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_WEP104:
-               /*
-                * Support for TX only, at least for now, so accept
-                * the key and do nothing else. Then mac80211 will
-                * pass it for TX but we don't have to use it for RX.
+               /* For non-client mode, only use WEP keys for TX as we probably
+                * don't have a station yet anyway and would then have to keep
+                * track of the keys, linking them to each of the clients/peers
+                * as they appear. For now, don't do that, for performance WEP
+                * offload doesn't really matter much, but we need it for some
+                * other offload features in client mode.
                 */
-               return 0;
+               if (vif->type != NL80211_IFTYPE_STATION)
+                       return 0;
+               break;
        default:
                /* currently FW supports only one optional cipher scheme */
                if (hw->n_cipher_schemes &&
@@ -2601,7 +2690,7 @@ static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
        IWL_DEBUG_MAC80211(mvm, "enter\n");
 
        mutex_lock(&mvm->mutex);
-       iwl_mvm_stop_p2p_roc(mvm);
+       iwl_mvm_stop_roc(mvm);
        mutex_unlock(&mvm->mutex);
 
        IWL_DEBUG_MAC80211(mvm, "leave\n");
@@ -2714,8 +2803,8 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
 
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
-               /* Unless it's a CSA flow we have nothing to do here */
-               if (vif->csa_active) {
+               /* only needed if we're switching chanctx (i.e. during CSA) */
+               if (switching_chanctx) {
                        mvmvif->ap_ibss_active = true;
                        break;
                }
@@ -2759,23 +2848,32 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
        }
 
        /* Handle binding during CSA */
-       if ((vif->type == NL80211_IFTYPE_AP) ||
-           (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) {
+       if (vif->type == NL80211_IFTYPE_AP) {
                iwl_mvm_update_quotas(mvm, NULL);
                iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
        }
 
-       if (vif->csa_active && vif->type == NL80211_IFTYPE_STATION) {
-               struct iwl_mvm_sta *mvmsta;
+       if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
+               u32 duration = 2 * vif->bss_conf.beacon_int;
 
-               mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
-                                                         mvmvif->ap_sta_id);
+               /* iwl_mvm_protect_session() reads directly from the
+                * device (the system time), so make sure it is
+                * available.
+                */
+               ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA);
+               if (ret)
+                       goto out_remove_binding;
 
-               if (WARN_ON(!mvmsta))
-                       goto out;
+               /* Protect the session to make sure we hear the first
+                * beacon on the new channel.
+                */
+               iwl_mvm_protect_session(mvm, vif, duration, duration,
+                                       vif->bss_conf.beacon_int / 2,
+                                       true);
 
-               /* TODO: only re-enable after the first beacon */
-               iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+               iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
+
+               iwl_mvm_update_quotas(mvm, NULL);
        }
 
        goto out;
@@ -2809,7 +2907,6 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct ieee80211_vif *disabled_vif = NULL;
-       struct iwl_mvm_sta *mvmsta;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -2824,9 +2921,11 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
                break;
        case NL80211_IFTYPE_AP:
                /* This part is triggered only during CSA */
-               if (!vif->csa_active || !mvmvif->ap_ibss_active)
+               if (!switching_chanctx || !mvmvif->ap_ibss_active)
                        goto out;
 
+               mvmvif->csa_countdown = false;
+
                /* Set CS bit on all the stations */
                iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
 
@@ -2841,12 +2940,6 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
 
                disabled_vif = vif;
 
-               mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
-                                                         mvmvif->ap_sta_id);
-
-               if (!WARN_ON(!mvmsta))
-                       iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
-
                iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
                break;
        default:
@@ -2872,18 +2965,12 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
        mutex_unlock(&mvm->mutex);
 }
 
-static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
-                                     struct ieee80211_vif_chanctx_switch *vifs,
-                                     int n_vifs,
-                                     enum ieee80211_chanctx_switch_mode mode)
+static int
+iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
+                               struct ieee80211_vif_chanctx_switch *vifs)
 {
-       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
 
-       /* we only support SWAP_CONTEXTS and with a single-vif right now */
-       if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1)
-               return -EOPNOTSUPP;
-
        mutex_lock(&mvm->mutex);
        __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
        __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
@@ -2912,15 +2999,51 @@ out_remove:
        __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
 
 out_reassign:
-       ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx);
-       if (ret) {
+       if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) {
                IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
                goto out_restart;
        }
 
-       ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+       if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+                                        true)) {
+               IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
+               goto out_restart;
+       }
+
+       goto out;
+
+out_restart:
+       /* things keep failing, better restart the hw */
+       iwl_mvm_nic_restart(mvm, false);
+
+out:
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static int
+iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif_chanctx_switch *vifs)
+{
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+       __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+
+       ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
                                           true);
        if (ret) {
+               IWL_ERR(mvm,
+                       "failed to assign new_ctx during channel switch\n");
+               goto out_reassign;
+       }
+
+       goto out;
+
+out_reassign:
+       if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+                                        true)) {
                IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
                goto out_restart;
        }
@@ -2933,6 +3056,34 @@ out_restart:
 
 out:
        mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif_chanctx_switch *vifs,
+                                     int n_vifs,
+                                     enum ieee80211_chanctx_switch_mode mode)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       /* we only support a single-vif right now */
+       if (n_vifs > 1)
+               return -EOPNOTSUPP;
+
+       switch (mode) {
+       case CHANCTX_SWMODE_SWAP_CONTEXTS:
+               ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs);
+               break;
+       case CHANCTX_SWMODE_REASSIGN_VIF:
+               ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs);
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
        return ret;
 }
 
@@ -3018,27 +3169,134 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
 }
 #endif
 
-static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
-                                         struct ieee80211_vif *vif,
-                                         struct cfg80211_chan_def *chandef)
+static void iwl_mvm_channel_switch(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_channel_switch *chsw)
+{
+       /* By implementing this operation, we prevent mac80211 from
+        * starting its own channel switch timer, so that we can call
+        * ieee80211_chswitch_done() ourselves at the right time
+        * (which is when the absence time event starts).
+        */
+
+       IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw),
+                          "dummy channel switch op\n");
+}
+
+static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_channel_switch *chsw)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct ieee80211_vif *csa_vif;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 apply_time;
+       int ret;
 
        mutex_lock(&mvm->mutex);
 
-       csa_vif = rcu_dereference_protected(mvm->csa_vif,
-                                           lockdep_is_held(&mvm->mutex));
-       if (WARN(csa_vif && csa_vif->csa_active,
-                "Another CSA is already in progress"))
+       IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
+                          chsw->chandef.center_freq1);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_AP:
+               csa_vif =
+                       rcu_dereference_protected(mvm->csa_vif,
+                                                 lockdep_is_held(&mvm->mutex));
+               if (WARN_ONCE(csa_vif && csa_vif->csa_active,
+                             "Another CSA is already in progress")) {
+                       ret = -EBUSY;
+                       goto out_unlock;
+               }
+
+               rcu_assign_pointer(mvm->csa_vif, vif);
+
+               if (WARN_ONCE(mvmvif->csa_countdown,
+                             "Previous CSA countdown didn't complete")) {
+                       ret = -EBUSY;
+                       goto out_unlock;
+               }
+
+               break;
+       case NL80211_IFTYPE_STATION:
+               /* Schedule the time event to a bit before beacon 1,
+                * to make sure we're in the new channel when the
+                * GO/AP arrives.
+                */
+               apply_time = chsw->device_timestamp +
+                       ((vif->bss_conf.beacon_int * (chsw->count - 1) -
+                         IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024);
+
+               if (chsw->block_tx)
+                       iwl_mvm_csa_client_absent(mvm, vif);
+
+               iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int,
+                                           apply_time);
+               if (mvmvif->bf_data.bf_enabled) {
+                       ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+                       if (ret)
+                               goto out_unlock;
+               }
+
+               break;
+       default:
+               break;
+       }
+
+       mvmvif->ps_disabled = true;
+
+       ret = iwl_mvm_power_update_ps(mvm);
+       if (ret)
                goto out_unlock;
 
-       IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
-                          chandef->center_freq1);
-       rcu_assign_pointer(mvm->csa_vif, vif);
+       /* we won't be on this channel any longer */
+       iwl_mvm_teardown_tdls_peers(mvm);
+
+out_unlock:
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               struct iwl_mvm_sta *mvmsta;
+
+               mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
+                                                         mvmvif->ap_sta_id);
+
+               if (WARN_ON(!mvmsta)) {
+                       ret = -EIO;
+                       goto out_unlock;
+               }
+
+               iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+
+               iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+
+               ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+               if (ret)
+                       goto out_unlock;
+
+               iwl_mvm_stop_session_protection(mvm, vif);
+       }
+
+       mvmvif->ps_disabled = false;
+
+       ret = iwl_mvm_power_update_ps(mvm);
 
 out_unlock:
        mutex_unlock(&mvm->mutex);
+
+       return ret;
 }
 
 static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
@@ -3047,31 +3305,44 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_vif *mvmvif;
        struct iwl_mvm_sta *mvmsta;
+       struct ieee80211_sta *sta;
+       int i;
+       u32 msk = 0;
 
        if (!vif || vif->type != NL80211_IFTYPE_STATION)
                return;
 
        mutex_lock(&mvm->mutex);
        mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
 
-       if (WARN_ON_ONCE(!mvmsta)) {
-               mutex_unlock(&mvm->mutex);
-               return;
+       /* flush the AP-station and all TDLS peers */
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (IS_ERR_OR_NULL(sta))
+                       continue;
+
+               mvmsta = iwl_mvm_sta_from_mac80211(sta);
+               if (mvmsta->vif != vif)
+                       continue;
+
+               /* make sure only TDLS peers or the AP are flushed */
+               WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls);
+
+               msk |= mvmsta->tfd_queue_msk;
        }
 
        if (drop) {
-               if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true))
+               if (iwl_mvm_flush_tx_path(mvm, msk, true))
                        IWL_ERR(mvm, "flush request fail\n");
                mutex_unlock(&mvm->mutex);
        } else {
-               u32 tfd_queue_msk = mvmsta->tfd_queue_msk;
                mutex_unlock(&mvm->mutex);
 
                /* this can take a while, and we may need/want other operations
                 * to succeed while doing this, so do it without the mutex held
                 */
-               iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_queue_msk);
+               iwl_trans_wait_tx_queue_empty(mvm->trans, msk);
        }
 }
 
@@ -3120,7 +3391,13 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
 
        .set_tim = iwl_mvm_set_tim,
 
-       .channel_switch_beacon = iwl_mvm_channel_switch_beacon,
+       .channel_switch = iwl_mvm_channel_switch,
+       .pre_channel_switch = iwl_mvm_pre_channel_switch,
+       .post_channel_switch = iwl_mvm_post_channel_switch,
+
+       .tdls_channel_switch = iwl_mvm_tdls_channel_switch,
+       .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
+       .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
 
        CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
 
index d015fac..d24660f 100644 (file)
 /* A TimeUnit is 1024 microsecond */
 #define MSEC_TO_TU(_msec)      (_msec*1000/1024)
 
-/* This value represents the number of TUs before CSA "beacon 0" TBTT
- * when the CSA time-event needs to be scheduled to start.  It must be
- * big enough to ensure that we switch in time.
+/* For GO, this value represents the number of TUs before CSA "beacon
+ * 0" TBTT when the CSA time-event needs to be scheduled to start.  It
+ * must be big enough to ensure that we switch in time.
  */
 #define IWL_MVM_CHANNEL_SWITCH_TIME_GO         40
 
+/* For client, this value represents the number of TUs before CSA
+ * "beacon 1" TBTT, instead.  This is because we don't know when the
+ * GO/AP will be in the new channel, so we switch early enough.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT     10
+
 /*
  * This value (in TUs) is used to fine tune the CSA NoA end time which should
  * be just before "beacon 0" TBTT.
@@ -269,6 +275,7 @@ enum iwl_mvm_ref_type {
        IWL_MVM_REF_NMI,
        IWL_MVM_REF_TM_CMD,
        IWL_MVM_REF_EXIT_WORK,
+       IWL_MVM_REF_PROTECT_CSA,
 
        /* update debugfs.c when changing this */
 
@@ -288,7 +295,6 @@ enum iwl_bt_force_ant_mode {
 * struct iwl_mvm_vif_bf_data - beacon filtering related data
 * @bf_enabled: indicates if beacon filtering is enabled
 * @ba_enabled: indicated if beacon abort is enabled
-* @last_beacon_signal: last beacon rssi signal in dbm
 * @ave_beacon_signal: average beacon signal
 * @last_cqm_event: rssi of the last cqm event
 * @bt_coex_min_thold: minimum threshold for BT coex
@@ -399,6 +405,9 @@ struct iwl_mvm_vif {
 
        /* FW identified misbehaving AP */
        u8 uapsd_misbehaving_bssid[ETH_ALEN];
+
+       /* Indicates that CSA countdown may be started */
+       bool csa_countdown;
 };
 
 static inline struct iwl_mvm_vif *
@@ -519,6 +528,13 @@ enum {
 #define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100
 #define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200
 
+enum iwl_mvm_tdls_cs_state {
+       IWL_MVM_TDLS_SW_IDLE = 0,
+       IWL_MVM_TDLS_SW_REQ_SENT,
+       IWL_MVM_TDLS_SW_REQ_RCVD,
+       IWL_MVM_TDLS_SW_ACTIVE,
+};
+
 struct iwl_mvm {
        /* for logger access */
        struct device *dev;
@@ -578,6 +594,7 @@ struct iwl_mvm {
        struct work_struct sta_drained_wk;
        unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
        atomic_t pending_frames[IWL_MVM_STATION_COUNT];
+       u32 tfd_drained[IWL_MVM_STATION_COUNT];
        u8 rx_ba_sessions;
 
        /* configured by mac80211 */
@@ -588,6 +605,10 @@ struct iwl_mvm {
        void *scan_cmd;
        struct iwl_mcast_filter_cmd *mcast_filter_cmd;
 
+       /* UMAC scan tracking */
+       u32 scan_uid[IWL_MVM_MAX_SIMULTANEOUS_SCANS];
+       u8 scan_seq_num, sched_scan_seq_num;
+
        /* rx chain antennas set through debugfs for the scan command */
        u8 scan_rx_ant;
 
@@ -649,6 +670,7 @@ struct iwl_mvm {
        /* -1 for always, 0 for never, >0 for that many times */
        s8 restart_fw;
        struct work_struct fw_error_dump_wk;
+       enum iwl_fw_dbg_conf fw_dbg_conf;
 
 #ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
@@ -662,7 +684,12 @@ struct iwl_mvm {
 
        /* sched scan settings for net detect */
        struct cfg80211_sched_scan_request *nd_config;
-       struct ieee80211_scan_ies *nd_ies;
+       struct ieee80211_scan_ies nd_ies;
+       struct cfg80211_match_set *nd_match_sets;
+       int n_nd_match_sets;
+       struct ieee80211_channel **nd_channels;
+       int n_nd_channels;
+       bool net_detect;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        u32 d3_wake_sysassert; /* must be u32 for debugfs_create_bool */
        bool d3_test_active;
@@ -735,6 +762,28 @@ struct iwl_mvm {
        u32 ap_last_beacon_gp2;
 
        u8 low_latency_agg_frame_limit;
+
+       /* TDLS channel switch data */
+       struct {
+               struct delayed_work dwork;
+               enum iwl_mvm_tdls_cs_state state;
+
+               /*
+                * Current cs sta - might be different from periodic cs peer
+                * station. Value is meaningless when the cs-state is idle.
+                */
+               u8 cur_sta_id;
+
+               /* TDLS periodic channel-switch peer */
+               struct {
+                       u8 sta_id;
+                       u8 op_class;
+                       bool initiator; /* are we the link initiator */
+                       struct cfg80211_chan_def chandef;
+                       struct sk_buff *skb; /* ch sw template */
+                       u32 ch_sw_tm_ie;
+               } peer;
+       } tdls_cs;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -751,6 +800,7 @@ enum iwl_mvm_status {
        IWL_MVM_STATUS_IN_HW_RESTART,
        IWL_MVM_STATUS_IN_D0I3,
        IWL_MVM_STATUS_ROC_AUX_RUNNING,
+       IWL_MVM_STATUS_D3_RECONFIG,
 };
 
 static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
@@ -759,6 +809,26 @@ static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
               test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
 }
 
+/* Must be called with rcu_read_lock() held and it can only be
+ * released when mvmsta is not needed anymore.
+ */
+static inline struct iwl_mvm_sta *
+iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id)
+{
+       struct ieee80211_sta *sta;
+
+       if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
+               return NULL;
+
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+       /* This can happen if the station has been removed right now */
+       if (IS_ERR_OR_NULL(sta))
+               return NULL;
+
+       return iwl_mvm_sta_from_mac80211(sta);
+}
+
 static inline struct iwl_mvm_sta *
 iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
 {
@@ -832,6 +902,16 @@ int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id,
 int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
                   struct ieee80211_sta *sta);
 int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb);
+void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+                       struct iwl_tx_cmd *tx_cmd,
+                       struct ieee80211_tx_info *info, u8 sta_id);
+void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
+                              struct ieee80211_tx_info *info,
+                              struct iwl_tx_cmd *tx_cmd,
+                              struct sk_buff *skb_frag);
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+                           struct ieee80211_tx_info *info,
+                           struct ieee80211_sta *sta, __le16 fc);
 #ifdef CONFIG_IWLWIFI_DEBUG
 const char *iwl_mvm_get_tx_fail_reason(u32 status);
 #else
@@ -888,6 +968,8 @@ int iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
                                struct iwl_device_cmd *cmd);
 int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                         struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                           struct iwl_device_cmd *cmd);
 
 /* MVM PHY */
 int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
@@ -901,6 +983,8 @@ void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
 void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
                            struct iwl_mvm_phy_ctxt *ctxt);
 int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm);
+u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef);
+u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef);
 
 /* MAC (virtual interface) programming */
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -920,6 +1004,8 @@ int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
                                    struct iwl_device_cmd *cmd);
 void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif);
+unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *exclude_vif);
 
 /* Bindings */
 int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -930,6 +1016,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
                          struct ieee80211_vif *disabled_vif);
 
 /* Scanning */
+int iwl_mvm_scan_size(struct iwl_mvm *mvm);
 int iwl_mvm_scan_request(struct iwl_mvm *mvm,
                         struct ieee80211_vif *vif,
                         struct cfg80211_scan_request *req);
@@ -970,6 +1057,17 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
                                    struct cfg80211_sched_scan_request *req,
                                    struct ieee80211_scan_ies *ies);
 
+/* UMAC scan */
+int iwl_mvm_config_scan(struct iwl_mvm *mvm);
+int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                     struct ieee80211_scan_request *req);
+int iwl_mvm_sched_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_scan_ies *ies);
+int iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
+                                       struct iwl_rx_cmd_buffer *rxb,
+                                       struct iwl_device_cmd *cmd);
+
 /* MVM debugfs */
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
@@ -1049,7 +1147,7 @@ iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 }
 #endif
 void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
-                               struct iwl_wowlan_config_cmd_v2 *cmd);
+                               struct iwl_wowlan_config_cmd *cmd);
 int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
                               struct ieee80211_vif *vif,
                               bool disable_offloading,
@@ -1059,6 +1157,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
 void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm);
 void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
 int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
 
@@ -1074,12 +1173,14 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta);
 bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
                                     struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant);
 bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm);
 bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
                                    enum ieee80211_band band);
 u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
                           struct ieee80211_tx_info *info, u8 ac);
 
+bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant);
 bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
 void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
 int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
@@ -1195,6 +1296,10 @@ bool iwl_mvm_is_idle(struct iwl_mvm *mvm);
 
 /* Thermal management and CT-kill */
 void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
+void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp);
+int iwl_mvm_temp_notif(struct iwl_mvm *mvm,
+                      struct iwl_rx_cmd_buffer *rxb,
+                      struct iwl_device_cmd *cmd);
 void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
 void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff);
 void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
@@ -1206,12 +1311,33 @@ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                      bool added_vif);
 
 /* TDLS */
+
+/*
+ * We use TID 4 (VI) as a FW-used-only TID when TDLS connections are present.
+ * This TID is marked as used vs the AP and all connected TDLS peers.
+ */
+#define IWL_MVM_TDLS_FW_TID 4
+
 int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm);
 void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                               bool sta_added);
 void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
                                           struct ieee80211_vif *vif);
+int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif,
+                               struct ieee80211_sta *sta, u8 oper_class,
+                               struct cfg80211_chan_def *chandef,
+                               struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
+void iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_tdls_ch_sw_params *params);
+void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_sta *sta);
+int iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd);
+void iwl_mvm_tdls_ch_switch_work(struct work_struct *work);
 
 struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
 
index af07456..d55fd8e 100644 (file)
@@ -339,11 +339,15 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
        } *file_sec;
        const u8 *eof, *temp;
        int max_section_size;
+       const __le32 *dword_buff;
 
 #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
 #define NVM_WORD2_ID(x) (x >> 12)
 #define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8))
 #define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4)
+#define NVM_HEADER_0   (0x2A504C54)
+#define NVM_HEADER_1   (0x4E564D2A)
+#define NVM_HEADER_SIZE        (4 * sizeof(u32))
 
        IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
 
@@ -372,12 +376,6 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
        IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
                 mvm->nvm_file_name, fw_entry->size);
 
-       if (fw_entry->size < sizeof(*file_sec)) {
-               IWL_ERR(mvm, "NVM file too small\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
        if (fw_entry->size > MAX_NVM_FILE_LEN) {
                IWL_ERR(mvm, "NVM file too large\n");
                ret = -EINVAL;
@@ -385,8 +383,25 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
        }
 
        eof = fw_entry->data + fw_entry->size;
-
-       file_sec = (void *)fw_entry->data;
+       dword_buff = (__le32 *)fw_entry->data;
+
+       /* some NVM file will contain a header.
+        * The header is identified by 2 dwords header as follow:
+        * dword[0] = 0x2A504C54
+        * dword[1] = 0x4E564D2A
+        *
+        * This header must be skipped when providing the NVM data to the FW.
+        */
+       if (fw_entry->size > NVM_HEADER_SIZE &&
+           dword_buff[0] == cpu_to_le32(NVM_HEADER_0) &&
+           dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) {
+               file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE);
+               IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
+               IWL_INFO(mvm, "NVM Manufacturing date %08X\n",
+                        le32_to_cpu(dword_buff[3]));
+       } else {
+               file_sec = (void *)fw_entry->data;
+       }
 
        while (true) {
                if (file_sec->data > eof) {
index adcbf4c..68b0169 100644 (file)
@@ -67,7 +67,7 @@
 #include "mvm.h"
 
 void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
-                               struct iwl_wowlan_config_cmd_v2 *cmd)
+                               struct iwl_wowlan_config_cmd *cmd)
 {
        int i;
 
index 7a95785..97dfba5 100644 (file)
@@ -244,6 +244,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
                   iwl_mvm_rx_scan_offload_complete_notif, true),
        RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results,
                   false),
+       RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif,
+                  true),
 
        RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
        RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
@@ -254,6 +256,12 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
        RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
                   iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
+       RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true),
+
+       RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif,
+                  true),
+       RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false),
+
 };
 #undef RX_HANDLER
 #define CMD(x) [x] = #x
@@ -317,11 +325,9 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(WOWLAN_KEK_KCK_MATERIAL),
        CMD(WOWLAN_GET_STATUSES),
        CMD(WOWLAN_TX_POWER_PER_DB),
-       CMD(NET_DETECT_CONFIG_CMD),
-       CMD(NET_DETECT_PROFILES_QUERY_CMD),
-       CMD(NET_DETECT_PROFILES_CMD),
-       CMD(NET_DETECT_HOTSPOTS_CMD),
-       CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
+       CMD(SCAN_OFFLOAD_PROFILES_QUERY_CMD),
+       CMD(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD),
+       CMD(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD),
        CMD(CARD_STATE_NOTIFICATION),
        CMD(MISSED_BEACONS_NOTIFICATION),
        CMD(BT_COEX_PRIO_TABLE),
@@ -344,6 +350,13 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
        CMD(ANTENNA_COUPLING_NOTIFICATION),
        CMD(SCD_QUEUE_CFG),
+       CMD(SCAN_CFG_CMD),
+       CMD(SCAN_REQ_UMAC),
+       CMD(SCAN_ABORT_UMAC),
+       CMD(SCAN_COMPLETE_UMAC),
+       CMD(TDLS_CHANNEL_SWITCH_CMD),
+       CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION),
+       CMD(TDLS_CONFIG_CMD),
 };
 #undef CMD
 
@@ -442,6 +455,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
        INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
        INIT_WORK(&mvm->fw_error_dump_wk, iwl_mvm_fw_error_dump_wk);
+       INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
 
        spin_lock_init(&mvm->d0i3_tx_lock);
        spin_lock_init(&mvm->refs_lock);
@@ -482,6 +496,10 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
        trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
+       trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv;
+       trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num;
+       memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv,
+              sizeof(trans->dbg_conf_tlv));
 
        /* set up notification wait support */
        iwl_notification_wait_init(&mvm->notif_wait);
@@ -525,7 +543,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
                mutex_lock(&mvm->mutex);
                err = iwl_run_init_mvm_ucode(mvm, true);
-               iwl_trans_stop_device(trans);
+               if (!err || !iwlmvm_mod_params.init_dbg)
+                       iwl_trans_stop_device(trans);
                mutex_unlock(&mvm->mutex);
                /* returns 0 if successful, 1 if success but in rfkill */
                if (err < 0 && !iwlmvm_mod_params.init_dbg) {
@@ -534,16 +553,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                }
        }
 
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
-               scan_size = sizeof(struct iwl_scan_req_unified_lmac) +
-                       sizeof(struct iwl_scan_channel_cfg_lmac) *
-                               mvm->fw->ucode_capa.n_scan_channels +
-                       sizeof(struct iwl_scan_probe_req);
-       else
-               scan_size = sizeof(struct iwl_scan_cmd) +
-                       mvm->fw->ucode_capa.max_probe_length +
-                       mvm->fw->ucode_capa.n_scan_channels *
-                               sizeof(struct iwl_scan_channel);
+       scan_size = iwl_mvm_scan_size(mvm);
 
        mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
        if (!mvm->scan_cmd)
@@ -597,8 +607,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
                kfree(mvm->nd_config->match_sets);
                kfree(mvm->nd_config);
                mvm->nd_config = NULL;
-               kfree(mvm->nd_ies);
-               mvm->nd_ies = NULL;
        }
 #endif
 
@@ -996,7 +1004,7 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
 }
 
 static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
-                                   struct iwl_wowlan_config_cmd_v3 *cmd,
+                                   struct iwl_wowlan_config_cmd *cmd,
                                    struct iwl_d0i3_iter_data *iter_data)
 {
        struct ieee80211_sta *ap_sta;
@@ -1012,14 +1020,14 @@ static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
                goto out;
 
        mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
-       cmd->common.is_11n_connection = ap_sta->ht_cap.ht_supported;
+       cmd->is_11n_connection = ap_sta->ht_cap.ht_supported;
        cmd->offloading_tid = iter_data->offloading_tid;
 
        /*
         * The d0i3 uCode takes care of the nonqos counters,
         * so configure only the qos seq ones.
         */
-       iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &cmd->common);
+       iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, cmd);
 out:
        rcu_read_unlock();
 }
@@ -1031,14 +1039,11 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
        struct iwl_d0i3_iter_data d0i3_iter_data = {
                .mvm = mvm,
        };
-       struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {
-               .common = {
-                       .wakeup_filter =
-                               cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
-                                           IWL_WOWLAN_WAKEUP_BEACON_MISS |
-                                           IWL_WOWLAN_WAKEUP_LINK_CHANGE |
-                                           IWL_WOWLAN_WAKEUP_BCN_FILTERING),
-               },
+       struct iwl_wowlan_config_cmd wowlan_config_cmd = {
+               .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
+                                            IWL_WOWLAN_WAKEUP_BEACON_MISS |
+                                            IWL_WOWLAN_WAKEUP_LINK_CHANGE |
+                                            IWL_WOWLAN_WAKEUP_BCN_FILTERING),
        };
        struct iwl_d3_manager_config d3_cfg_cmd = {
                .min_sleep_time = cpu_to_le32(1000),
@@ -1050,6 +1055,19 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
        set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
        synchronize_net();
 
+       /*
+        * iwl_mvm_ref_sync takes a reference before checking the flag.
+        * so by checking there is no held reference we prevent a state
+        * in which iwl_mvm_ref_sync continues successfully while we
+        * configure the firmware to enter d0i3
+        */
+       if (iwl_mvm_ref_taken(mvm)) {
+               IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n");
+               clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+               wake_up(&mvm->d0i3_exit_waitq);
+               return 1;
+       }
+
        ieee80211_iterate_active_interfaces_atomic(mvm->hw,
                                                   IEEE80211_IFACE_ITER_NORMAL,
                                                   iwl_mvm_enter_d0i3_iterator,
index 12283b5..1c0d4a4 100644 (file)
@@ -68,7 +68,7 @@
 #include "mvm.h"
 
 /* Maps the driver specific channel width definition to the the fw values */
-static inline u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
+u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
 {
        switch (chandef->width) {
        case NL80211_CHAN_WIDTH_20_NOHT:
@@ -90,7 +90,7 @@ static inline u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
  * Maps the driver specific control channel position (relative to the center
  * freq) definitions to the the fw values
  */
-static inline u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
+u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
 {
        switch (chandef->chan->center_freq - chandef->center_freq1) {
        case -70:
index 5b85b0c..2620dd0 100644 (file)
@@ -286,6 +286,27 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
        return true;
 }
 
+static int iwl_mvm_power_get_skip_over_dtim(int dtimper, int bi)
+{
+       int numerator;
+       int dtim_interval = dtimper * bi;
+
+       if (WARN_ON(!dtim_interval))
+               return 0;
+
+       if (dtimper == 1) {
+               if (bi > 100)
+                       numerator = 408;
+               else
+                       numerator = 510;
+       } else if (dtimper < 10) {
+               numerator = 612;
+       } else {
+               return 0;
+       }
+       return max(1, (numerator / dtim_interval));
+}
+
 static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif)
 {
        struct ieee80211_chanctx_conf *chanctx_conf;
@@ -308,7 +329,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif,
                                    struct iwl_mac_power_cmd *cmd)
 {
-       int dtimper, dtimper_msec;
+       int dtimper, bi;
        int keep_alive;
        bool radar_detect = false;
        struct iwl_mvm_vif *mvmvif __maybe_unused =
@@ -317,6 +338,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
                                                            mvmvif->color));
        dtimper = vif->bss_conf.dtim_period;
+       bi = vif->bss_conf.beacon_int;
 
        /*
         * Regardless of power management state the driver must set
@@ -324,10 +346,9 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
         * immediately after association. Check that keep alive period
         * is at least 3 * DTIM
         */
-       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
-       keep_alive = max_t(int, 3 * dtimper_msec,
-                          MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
-       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+       keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
+                                 USEC_PER_SEC);
+       keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
        cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
 
        if (mvm->ps_disabled)
@@ -352,11 +373,14 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        radar_detect = iwl_mvm_power_is_radar(vif);
 
        /* Check skip over DTIM conditions */
-       if (!radar_detect && (dtimper <= 10) &&
+       if (!radar_detect && (dtimper < 10) &&
            (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
             mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
-               cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-               cmd->skip_dtim_periods = 3;
+               cmd->skip_dtim_periods =
+                       iwl_mvm_power_get_skip_over_dtim(dtimper, bi);
+               if (cmd->skip_dtim_periods)
+                       cmd->flags |=
+                               cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
        }
 
        if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
index ce88484..30ceb67 100644 (file)
@@ -158,6 +158,12 @@ struct rs_tx_column {
        allow_column_func_t checks[MAX_COLUMN_CHECKS];
 };
 
+static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                        struct iwl_scale_tbl_info *tbl)
+{
+       return iwl_mvm_bt_coex_is_ant_avail(mvm, tbl->rate.ant);
+}
+
 static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                          struct iwl_scale_tbl_info *tbl)
 {
@@ -218,6 +224,9 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
                },
+               .checks = {
+                       rs_ant_allow,
+               },
        },
        [RS_COLUMN_LEGACY_ANT_B] = {
                .mode = RS_LEGACY,
@@ -231,6 +240,9 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
                },
+               .checks = {
+                       rs_ant_allow,
+               },
        },
        [RS_COLUMN_SISO_ANT_A] = {
                .mode = RS_SISO,
@@ -246,6 +258,7 @@ static const struct rs_tx_column rs_tx_columns[] = {
                },
                .checks = {
                        rs_siso_allow,
+                       rs_ant_allow,
                },
        },
        [RS_COLUMN_SISO_ANT_B] = {
@@ -262,6 +275,7 @@ static const struct rs_tx_column rs_tx_columns[] = {
                },
                .checks = {
                        rs_siso_allow,
+                       rs_ant_allow,
                },
        },
        [RS_COLUMN_SISO_ANT_A_SGI] = {
@@ -279,6 +293,7 @@ static const struct rs_tx_column rs_tx_columns[] = {
                },
                .checks = {
                        rs_siso_allow,
+                       rs_ant_allow,
                        rs_sgi_allow,
                },
        },
@@ -297,6 +312,7 @@ static const struct rs_tx_column rs_tx_columns[] = {
                },
                .checks = {
                        rs_siso_allow,
+                       rs_ant_allow,
                        rs_sgi_allow,
                },
        },
@@ -506,7 +522,7 @@ static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate,
                                const char *prefix)
 {
        IWL_DEBUG_RATE(mvm,
-                      "%s: (%s: %d) ANT: %s BW: %d SGI: %d LDPC: %d STBC %d\n",
+                      "%s: (%s: %d) ANT: %s BW: %d SGI: %d LDPC: %d STBC: %d\n",
                       prefix, rs_pretty_lq_type(rate->type),
                       rate->index, rs_pretty_ant(rate->ant),
                       rate->bw, rate->sgi, rate->ldpc, rate->stbc);
@@ -816,7 +832,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate,
 
                if (nss == 1) {
                        rate->type = LQ_VHT_SISO;
-                       WARN_ON_ONCE(num_of_ant != 1);
+                       WARN_ON_ONCE(!rate->stbc && num_of_ant != 1);
                } else if (nss == 2) {
                        rate->type = LQ_VHT_MIMO2;
                        WARN_ON_ONCE(num_of_ant != 2);
@@ -1110,10 +1126,11 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        if (time_after(jiffies,
                       (unsigned long)(lq_sta->last_tx + RS_IDLE_TIMEOUT))) {
-               int tid;
+               int t;
+
                IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
-               for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
-                       ieee80211_stop_tx_ba_session(sta, tid);
+               for (t = 0; t < IWL_MAX_TID_COUNT; t++)
+                       ieee80211_stop_tx_ba_session(sta, t);
 
                iwl_mvm_rs_rate_init(mvm, sta, info->band, false);
                return;
@@ -1154,16 +1171,15 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                /* Rate did match, so reset the missed_rate_counter */
                lq_sta->missed_rate_counter = 0;
 
-       /* Figure out if rate scale algorithm is in active or search table */
-       if (rs_rate_match(&rate,
-                         &(lq_sta->lq_info[lq_sta->active_tbl].rate))) {
+       if (!lq_sta->search_better_tbl) {
                curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
                other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
-       } else if (rs_rate_match(&rate,
-                        &lq_sta->lq_info[1 - lq_sta->active_tbl].rate)) {
+       } else {
                curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
                other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-       } else {
+       }
+
+       if (WARN_ON_ONCE(!rs_rate_match(&rate, &curr_tbl->rate))) {
                IWL_DEBUG_RATE(mvm,
                               "Neither active nor search matches tx rate\n");
                tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
@@ -1188,6 +1204,13 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
         * first index into rate scale table.
         */
        if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+               /* ampdu_ack_len = 0 marks no BA was received. In this case
+                * treat it as a single frame loss as we don't want the success
+                * ratio to dip too quickly because a BA wasn't received
+                */
+               if (info->status.ampdu_ack_len == 0)
+                       info->status.ampdu_len = 1;
+
                ucode_rate = le32_to_cpu(table->rs_table[0]);
                rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
                rs_collect_tx_data(lq_sta, curr_tbl, rate.index,
index 3cf40f3..94b6e72 100644 (file)
@@ -96,27 +96,27 @@ int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
  * Adds the rxb to a new skb and give it to mac80211
  */
 static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
+                                           struct sk_buff *skb,
                                            struct ieee80211_hdr *hdr, u16 len,
-                                           u32 ampdu_status,
-                                           struct iwl_rx_cmd_buffer *rxb,
-                                           struct ieee80211_rx_status *stats)
+                                           u32 ampdu_status, u8 crypt_len,
+                                           struct iwl_rx_cmd_buffer *rxb)
 {
-       struct sk_buff *skb;
        unsigned int hdrlen, fraglen;
 
-       /* Dont use dev_alloc_skb(), we'll have enough headroom once
-        * ieee80211_hdr pulled.
-        */
-       skb = alloc_skb(128, GFP_ATOMIC);
-       if (!skb) {
-               IWL_ERR(mvm, "alloc_skb failed\n");
-               return;
-       }
        /* If frame is small enough to fit in skb->head, pull it completely.
-        * If not, only pull ieee80211_hdr so that splice() or TCP coalesce
-        * are more efficient.
+        * If not, only pull ieee80211_hdr (including crypto if present, and
+        * an additional 8 bytes for SNAP/ethertype, see below) so that
+        * splice() or TCP coalesce are more efficient.
+        *
+        * Since, in addition, ieee80211_data_to_8023() always pull in at
+        * least 8 bytes (possibly more for mesh) we can do the same here
+        * to save the cost of doing it later. That still doesn't pull in
+        * the actual IP header since the typical case has a SNAP header.
+        * If the latter changes (there are efforts in the standards group
+        * to do so) we should revisit this and ieee80211_data_to_8023().
         */
-       hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr);
+       hdrlen = (len <= skb_tailroom(skb)) ? len :
+                                             sizeof(*hdr) + crypt_len + 8;
 
        memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
        fraglen = len - hdrlen;
@@ -129,8 +129,6 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
                                fraglen, rxb->truesize);
        }
 
-       memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
-
        ieee80211_rx(mvm->hw, skb);
 }
 
@@ -185,7 +183,8 @@ static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
 static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
                                        struct ieee80211_hdr *hdr,
                                        struct ieee80211_rx_status *stats,
-                                       u32 rx_pkt_status)
+                                       u32 rx_pkt_status,
+                                       u8 *crypt_len)
 {
        if (!ieee80211_has_protected(hdr->frame_control) ||
            (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
@@ -205,12 +204,14 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
 
                stats->flag |= RX_FLAG_DECRYPTED;
                IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n");
+               *crypt_len = IEEE80211_CCMP_HDR_LEN;
                return 0;
 
        case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
                /* Don't drop the frame and decrypt it in SW */
                if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
                        return 0;
+               *crypt_len = IEEE80211_TKIP_IV_LEN;
                /* fall through if TTAK OK */
 
        case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
@@ -218,6 +219,9 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
                        return -1;
 
                stats->flag |= RX_FLAG_DECRYPTED;
+               if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+                               RX_MPDU_RES_STATUS_SEC_WEP_ENC)
+                       *crypt_len = IEEE80211_WEP_IV_LEN;
                return 0;
 
        case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
@@ -242,15 +246,17 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                       struct iwl_device_cmd *cmd)
 {
        struct ieee80211_hdr *hdr;
-       struct ieee80211_rx_status rx_status = {};
+       struct ieee80211_rx_status *rx_status;
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_rx_phy_info *phy_info;
        struct iwl_rx_mpdu_res_start *rx_res;
        struct ieee80211_sta *sta;
+       struct sk_buff *skb;
        u32 len;
        u32 ampdu_status;
        u32 rate_n_flags;
        u32 rx_pkt_status;
+       u8 crypt_len = 0;
 
        phy_info = &mvm->last_phy_info;
        rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
@@ -259,20 +265,32 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        rx_pkt_status = le32_to_cpup((__le32 *)
                (pkt->data + sizeof(*rx_res) + len));
 
-       memset(&rx_status, 0, sizeof(rx_status));
+       /* Dont use dev_alloc_skb(), we'll have enough headroom once
+        * ieee80211_hdr pulled.
+        */
+       skb = alloc_skb(128, GFP_ATOMIC);
+       if (!skb) {
+               IWL_ERR(mvm, "alloc_skb failed\n");
+               return 0;
+       }
+
+       rx_status = IEEE80211_SKB_RXCB(skb);
 
        /*
         * drop the packet if it has failed being decrypted by HW
         */
-       if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) {
+       if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status,
+                                        &crypt_len)) {
                IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
                               rx_pkt_status);
+               kfree_skb(skb);
                return 0;
        }
 
        if ((unlikely(phy_info->cfg_phy_cnt > 20))) {
                IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n",
                               phy_info->cfg_phy_cnt);
+               kfree_skb(skb);
                return 0;
        }
 
@@ -283,31 +301,31 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
            !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
                IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
-               rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
+               rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
        }
 
        /* This will be used in several places later */
        rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
 
        /* rx_status carries information about the packet to mac80211 */
-       rx_status.mactime = le64_to_cpu(phy_info->timestamp);
-       rx_status.device_timestamp = le32_to_cpu(phy_info->system_timestamp);
-       rx_status.band =
+       rx_status->mactime = le64_to_cpu(phy_info->timestamp);
+       rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp);
+       rx_status->band =
                (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
                                IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
-       rx_status.freq =
+       rx_status->freq =
                ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
-                                              rx_status.band);
+                                              rx_status->band);
        /*
         * TSF as indicated by the fw is at INA time, but mac80211 expects the
         * TSF at the beginning of the MPDU.
         */
-       /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
+       /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/
 
-       iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status);
+       iwl_mvm_get_signal_strength(mvm, phy_info, rx_status);
 
-       IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
-                             (unsigned long long)rx_status.mactime);
+       IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal,
+                             (unsigned long long)rx_status->mactime);
 
        rcu_read_lock();
        /*
@@ -326,15 +344,14 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        if (sta) {
                struct iwl_mvm_sta *mvmsta;
                mvmsta = iwl_mvm_sta_from_mac80211(sta);
-               rs_update_last_rssi(mvm, &mvmsta->lq_sta,
-                                   &rx_status);
+               rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
        }
 
        rcu_read_unlock();
 
        /* set the preamble flag if appropriate */
        if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
-               rx_status.flag |= RX_FLAG_SHORTPRE;
+               rx_status->flag |= RX_FLAG_SHORTPRE;
 
        if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
                /*
@@ -342,8 +359,8 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                 * together since we get a single PHY response
                 * from the firmware for all of them
                 */
-               rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
-               rx_status.ampdu_reference = mvm->ampdu_ref;
+               rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+               rx_status->ampdu_reference = mvm->ampdu_ref;
        }
 
        /* Set up the HT phy flags */
@@ -351,50 +368,50 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        case RATE_MCS_CHAN_WIDTH_20:
                break;
        case RATE_MCS_CHAN_WIDTH_40:
-               rx_status.flag |= RX_FLAG_40MHZ;
+               rx_status->flag |= RX_FLAG_40MHZ;
                break;
        case RATE_MCS_CHAN_WIDTH_80:
-               rx_status.vht_flag |= RX_VHT_FLAG_80MHZ;
+               rx_status->vht_flag |= RX_VHT_FLAG_80MHZ;
                break;
        case RATE_MCS_CHAN_WIDTH_160:
-               rx_status.vht_flag |= RX_VHT_FLAG_160MHZ;
+               rx_status->vht_flag |= RX_VHT_FLAG_160MHZ;
                break;
        }
        if (rate_n_flags & RATE_MCS_SGI_MSK)
-               rx_status.flag |= RX_FLAG_SHORT_GI;
+               rx_status->flag |= RX_FLAG_SHORT_GI;
        if (rate_n_flags & RATE_HT_MCS_GF_MSK)
-               rx_status.flag |= RX_FLAG_HT_GF;
+               rx_status->flag |= RX_FLAG_HT_GF;
        if (rate_n_flags & RATE_MCS_LDPC_MSK)
-               rx_status.flag |= RX_FLAG_LDPC;
+               rx_status->flag |= RX_FLAG_LDPC;
        if (rate_n_flags & RATE_MCS_HT_MSK) {
                u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >>
                                RATE_MCS_STBC_POS;
-               rx_status.flag |= RX_FLAG_HT;
-               rx_status.rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
-               rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT;
+               rx_status->flag |= RX_FLAG_HT;
+               rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+               rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
        } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
                u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >>
                                RATE_MCS_STBC_POS;
-               rx_status.vht_nss =
+               rx_status->vht_nss =
                        ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
                                                RATE_VHT_MCS_NSS_POS) + 1;
-               rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
-               rx_status.flag |= RX_FLAG_VHT;
-               rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT;
+               rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+               rx_status->flag |= RX_FLAG_VHT;
+               rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
                if (rate_n_flags & RATE_MCS_BF_MSK)
-                       rx_status.vht_flag |= RX_VHT_FLAG_BF;
+                       rx_status->vht_flag |= RX_VHT_FLAG_BF;
        } else {
-               rx_status.rate_idx =
+               rx_status->rate_idx =
                        iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
-                                                           rx_status.band);
+                                                           rx_status->band);
        }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_mvm_update_frame_stats(mvm, &mvm->drv_rx_stats, rate_n_flags,
-                                  rx_status.flag & RX_FLAG_AMPDU_DETAILS);
+                                  rx_status->flag & RX_FLAG_AMPDU_DETAILS);
 #endif
-       iwl_mvm_pass_packet_to_mac80211(mvm, hdr, len, ampdu_status,
-                                       rxb, &rx_status);
+       iwl_mvm_pass_packet_to_mac80211(mvm, skb, hdr, len, ampdu_status,
+                                       crypt_len, rxb);
        return 0;
 }
 
@@ -500,29 +517,8 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
                .mvm = mvm,
        };
 
-       /*
-        * set temperature debug enabled - ignore FW temperature updates
-        * and use the user set temperature.
-        */
-       if (mvm->temperature_test) {
-               if (mvm->temperature < le32_to_cpu(common->temperature))
-                       IWL_DEBUG_TEMP(mvm,
-                                      "Ignoring FW temperature update that is greater than the debug set temperature (debug temp = %d, fw temp = %d)\n",
-                                      mvm->temperature,
-                                      le32_to_cpu(common->temperature));
-               /*
-                * skip iwl_mvm_tt_handler since we are in
-                * temperature debug mode and we are ignoring
-                * the new temperature value
-                */
-               goto update;
-       }
+       iwl_mvm_tt_temp_changed(mvm, le32_to_cpu(common->temperature));
 
-       if (mvm->temperature != le32_to_cpu(common->temperature)) {
-               mvm->temperature = le32_to_cpu(common->temperature);
-               iwl_mvm_tt_handler(mvm);
-       }
-update:
        iwl_mvm_update_rx_statistics(mvm, stats);
 
        ieee80211_iterate_active_interfaces(mvm->hw,
index fb2a862..e5294d0 100644 (file)
@@ -83,15 +83,29 @@ struct iwl_mvm_scan_params {
        } dwell[IEEE80211_NUM_BANDS];
 };
 
+enum iwl_umac_scan_uid_type {
+       IWL_UMAC_SCAN_UID_REG_SCAN      = BIT(0),
+       IWL_UMAC_SCAN_UID_SCHED_SCAN    = BIT(1),
+       IWL_UMAC_SCAN_UID_ALL           = IWL_UMAC_SCAN_UID_REG_SCAN |
+                                         IWL_UMAC_SCAN_UID_SCHED_SCAN,
+};
+
+static int iwl_umac_scan_stop(struct iwl_mvm *mvm,
+                             enum iwl_umac_scan_uid_type type, bool notify);
+
+static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm)
+{
+       if (mvm->scan_rx_ant != ANT_NONE)
+               return mvm->scan_rx_ant;
+       return mvm->fw->valid_rx_ant;
+}
+
 static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
 {
        u16 rx_chain;
        u8 rx_ant;
 
-       if (mvm->scan_rx_ant != ANT_NONE)
-               rx_ant = mvm->scan_rx_ant;
-       else
-               rx_ant = mvm->fw->valid_rx_ant;
+       rx_ant = iwl_mvm_scan_rx_ant(mvm);
        rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
        rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
        rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
@@ -366,6 +380,10 @@ static int iwl_mvm_max_scan_ie_fw_cmd_room(struct iwl_mvm *mvm,
            !is_sched_scan)
                max_probe_len -= 32;
 
+       /* DS parameter set element is added on 2.4GHZ band if required */
+       if (iwl_mvm_rrm_scan_needed(mvm))
+               max_probe_len -= 3;
+
        return max_probe_len;
 }
 
@@ -537,23 +555,17 @@ int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm,
                                    struct iwl_device_cmd *cmd)
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       u8 client_bitmap = 0;
 
-       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+       if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) &&
+           !(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
                struct iwl_sched_scan_results *notif = (void *)pkt->data;
 
-               client_bitmap = notif->client_bitmap;
+               if (!(notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN))
+                       return 0;
        }
 
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
-           client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
-               if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
-                       IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
-                       ieee80211_sched_scan_results(mvm->hw);
-               } else {
-                       IWL_DEBUG_SCAN(mvm, "Scan results\n");
-               }
-       }
+       IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
+       ieee80211_sched_scan_results(mvm->hw);
 
        return 0;
 }
@@ -965,6 +977,20 @@ free_blacklist:
        return ret;
 }
 
+static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm,
+                                 struct cfg80211_sched_scan_request *req)
+{
+       if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sending scheduled scan with filtering, n_match_sets %d\n",
+                              req->n_match_sets);
+               return false;
+       }
+
+       IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n");
+       return true;
+}
+
 int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                             struct cfg80211_sched_scan_request *req)
 {
@@ -980,15 +1006,8 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER,
        };
 
-       if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
-               IWL_DEBUG_SCAN(mvm,
-                              "Sending scheduled scan with filtering, filter len %d\n",
-                              req->n_match_sets);
-       } else {
-               IWL_DEBUG_SCAN(mvm,
-                              "Sending Scheduled scan without filtering\n");
+       if (iwl_mvm_scan_pass_all(mvm, req))
                scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL);
-       }
 
        if (mvm->last_ebs_successful &&
            mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT)
@@ -1006,12 +1025,19 @@ int iwl_mvm_scan_offload_start(struct iwl_mvm *mvm,
 {
        int ret;
 
-       if ((mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+               ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+               if (ret)
+                       return ret;
+               ret = iwl_mvm_sched_scan_umac(mvm, vif, req, ies);
+       } else if ((mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+               mvm->scan_status = IWL_MVM_SCAN_SCHED;
                ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
                if (ret)
                        return ret;
                ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
        } else {
+               mvm->scan_status = IWL_MVM_SCAN_SCHED;
                ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
                if (ret)
                        return ret;
@@ -1068,6 +1094,10 @@ int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify)
 
        lockdep_assert_held(&mvm->mutex);
 
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+               return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN,
+                                         notify);
+
        if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
            (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
             mvm->scan_status != IWL_MVM_SCAN_OS)) {
@@ -1155,20 +1185,64 @@ iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm,
        }
 }
 
+static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies,
+                                          size_t len, u8 *const pos)
+{
+       static const u8 before_ds_params[] = {
+                       WLAN_EID_SSID,
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_REQUEST,
+                       WLAN_EID_EXT_SUPP_RATES,
+       };
+       size_t offs;
+       u8 *newpos = pos;
+
+       if (!iwl_mvm_rrm_scan_needed(mvm)) {
+               memcpy(newpos, ies, len);
+               return newpos + len;
+       }
+
+       offs = ieee80211_ie_split(ies, len,
+                                 before_ds_params,
+                                 ARRAY_SIZE(before_ds_params),
+                                 0);
+
+       memcpy(newpos, ies, offs);
+       newpos += offs;
+
+       /* Add a placeholder for DS Parameter Set element */
+       *newpos++ = WLAN_EID_DS_PARAMS;
+       *newpos++ = 1;
+       *newpos++ = 0;
+
+       memcpy(newpos, ies + offs, len - offs);
+       newpos += len - offs;
+
+       return newpos;
+}
+
 static void
 iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                 struct ieee80211_scan_ies *ies,
-                                struct iwl_scan_req_unified_lmac *cmd)
+                                struct iwl_scan_probe_req *preq,
+                                const u8 *mac_addr, const u8 *mac_addr_mask)
 {
-       struct iwl_scan_probe_req *preq = (void *)(cmd->data +
-               sizeof(struct iwl_scan_channel_cfg_lmac) *
-                       mvm->fw->ucode_capa.n_scan_channels);
        struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf;
-       u8 *pos;
+       u8 *pos, *newpos;
+
+       /*
+        * Unfortunately, right now the offload scan doesn't support randomising
+        * within the firmware, so until the firmware API is ready we implement
+        * it in the driver. This means that the scan iterations won't really be
+        * random, only when it's restarted, but at least that helps a bit.
+        */
+       if (mac_addr)
+               get_random_mask_addr(frame->sa, mac_addr, mac_addr_mask);
+       else
+               memcpy(frame->sa, vif->addr, ETH_ALEN);
 
        frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
        eth_broadcast_addr(frame->da);
-       memcpy(frame->sa, vif->addr, ETH_ALEN);
        eth_broadcast_addr(frame->bssid);
        frame->seq_ctrl = 0;
 
@@ -1179,11 +1253,14 @@ iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        preq->mac_header.offset = 0;
        preq->mac_header.len = cpu_to_le16(24 + 2);
 
-       memcpy(pos, ies->ies[IEEE80211_BAND_2GHZ],
-              ies->len[IEEE80211_BAND_2GHZ]);
+       /* Insert ds parameter set element on 2.4 GHz band */
+       newpos = iwl_mvm_copy_and_insert_ds_elem(mvm,
+                                                ies->ies[IEEE80211_BAND_2GHZ],
+                                                ies->len[IEEE80211_BAND_2GHZ],
+                                                pos);
        preq->band_data[0].offset = cpu_to_le16(pos - preq->buf);
-       preq->band_data[0].len = cpu_to_le16(ies->len[IEEE80211_BAND_2GHZ]);
-       pos += ies->len[IEEE80211_BAND_2GHZ];
+       preq->band_data[0].len = cpu_to_le16(newpos - pos);
+       pos = newpos;
 
        memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ],
               ies->len[IEEE80211_BAND_5GHZ]);
@@ -1244,9 +1321,10 @@ int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm,
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
        };
        struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+       struct iwl_scan_probe_req *preq;
        struct iwl_mvm_scan_params params = {};
        u32 flags;
-       int ssid_bitmap = 0;
+       u32 ssid_bitmap = 0;
        int ret, i;
 
        lockdep_assert_held(&mvm->mutex);
@@ -1305,7 +1383,13 @@ int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm,
                                       req->req.n_channels, ssid_bitmap,
                                       cmd);
 
-       iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, cmd);
+       preq = (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) *
+                       mvm->fw->ucode_capa.n_scan_channels);
+
+       iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, preq,
+               req->req.flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+                       req->req.mac_addr : NULL,
+               req->req.mac_addr_mask);
 
        ret = iwl_mvm_send_cmd(mvm, &hcmd);
        if (!ret) {
@@ -1338,6 +1422,7 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
        };
        struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+       struct iwl_scan_probe_req *preq;
        struct iwl_mvm_scan_params params = {};
        int ret;
        u32 flags = 0, ssid_bitmap = 0;
@@ -1361,15 +1446,8 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
 
        cmd->n_channels = (u8)req->n_channels;
 
-       if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
-               IWL_DEBUG_SCAN(mvm,
-                              "Sending scheduled scan with filtering, n_match_sets %d\n",
-                              req->n_match_sets);
-       } else {
-               IWL_DEBUG_SCAN(mvm,
-                              "Sending Scheduled scan without filtering\n");
+       if (iwl_mvm_scan_pass_all(mvm, req))
                flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
-       }
 
        if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0)
                flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
@@ -1399,7 +1477,13 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
        iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels,
                                       ssid_bitmap, cmd);
 
-       iwl_mvm_build_unified_scan_probe(mvm, vif, ies, cmd);
+       preq = (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) *
+                       mvm->fw->ucode_capa.n_scan_channels);
+
+       iwl_mvm_build_unified_scan_probe(mvm, vif, ies, preq,
+               req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+                       req->mac_addr : NULL,
+               req->mac_addr_mask);
 
        ret = iwl_mvm_send_cmd(mvm, &hcmd);
        if (!ret) {
@@ -1421,6 +1505,10 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
 
 int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
 {
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+               return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_REG_SCAN,
+                                         true);
+
        if (mvm->scan_status == IWL_MVM_SCAN_NONE)
                return 0;
 
@@ -1435,3 +1523,576 @@ int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
                return iwl_mvm_scan_offload_stop(mvm, true);
        return iwl_mvm_cancel_regular_scan(mvm);
 }
+
+/* UMAC scan API */
+
+struct iwl_umac_scan_done {
+       struct iwl_mvm *mvm;
+       enum iwl_umac_scan_uid_type type;
+};
+
+static int rate_to_scan_rate_flag(unsigned int rate)
+{
+       static const int rate_to_scan_rate[IWL_RATE_COUNT] = {
+               [IWL_RATE_1M_INDEX]     = SCAN_CONFIG_RATE_1M,
+               [IWL_RATE_2M_INDEX]     = SCAN_CONFIG_RATE_2M,
+               [IWL_RATE_5M_INDEX]     = SCAN_CONFIG_RATE_5M,
+               [IWL_RATE_11M_INDEX]    = SCAN_CONFIG_RATE_11M,
+               [IWL_RATE_6M_INDEX]     = SCAN_CONFIG_RATE_6M,
+               [IWL_RATE_9M_INDEX]     = SCAN_CONFIG_RATE_9M,
+               [IWL_RATE_12M_INDEX]    = SCAN_CONFIG_RATE_12M,
+               [IWL_RATE_18M_INDEX]    = SCAN_CONFIG_RATE_18M,
+               [IWL_RATE_24M_INDEX]    = SCAN_CONFIG_RATE_24M,
+               [IWL_RATE_36M_INDEX]    = SCAN_CONFIG_RATE_36M,
+               [IWL_RATE_48M_INDEX]    = SCAN_CONFIG_RATE_48M,
+               [IWL_RATE_54M_INDEX]    = SCAN_CONFIG_RATE_54M,
+       };
+
+       return rate_to_scan_rate[rate];
+}
+
+static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm)
+{
+       struct ieee80211_supported_band *band;
+       unsigned int rates = 0;
+       int i;
+
+       band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+       for (i = 0; i < band->n_bitrates; i++)
+               rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
+       band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+       for (i = 0; i < band->n_bitrates; i++)
+               rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
+
+       /* Set both basic rates and supported rates */
+       rates |= SCAN_CONFIG_SUPPORTED_RATE(rates);
+
+       return cpu_to_le32(rates);
+}
+
+int iwl_mvm_config_scan(struct iwl_mvm *mvm)
+{
+
+       struct iwl_scan_config *scan_config;
+       struct ieee80211_supported_band *band;
+       int num_channels =
+               mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels +
+               mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
+       int ret, i, j = 0, cmd_size, data_size;
+       struct iwl_host_cmd cmd = {
+               .id = SCAN_CFG_CMD,
+       };
+
+       if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels))
+               return -ENOBUFS;
+
+       cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels;
+
+       scan_config = kzalloc(cmd_size, GFP_KERNEL);
+       if (!scan_config)
+               return -ENOMEM;
+
+       data_size = cmd_size - sizeof(struct iwl_mvm_umac_cmd_hdr);
+       scan_config->hdr.size = cpu_to_le16(data_size);
+       scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE |
+                                        SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS |
+                                        SCAN_CONFIG_FLAG_SET_TX_CHAINS |
+                                        SCAN_CONFIG_FLAG_SET_RX_CHAINS |
+                                        SCAN_CONFIG_FLAG_SET_ALL_TIMES |
+                                        SCAN_CONFIG_FLAG_SET_LEGACY_RATES |
+                                        SCAN_CONFIG_FLAG_SET_MAC_ADDR |
+                                        SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
+                                        SCAN_CONFIG_N_CHANNELS(num_channels));
+       scan_config->tx_chains = cpu_to_le32(mvm->fw->valid_tx_ant);
+       scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm));
+       scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm);
+       scan_config->out_of_channel_time = cpu_to_le32(170);
+       scan_config->suspend_time = cpu_to_le32(30);
+       scan_config->dwell_active = 20;
+       scan_config->dwell_passive = 110;
+       scan_config->dwell_fragmented = 20;
+
+       memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN);
+
+       scan_config->bcast_sta_id = mvm->aux_sta.sta_id;
+       scan_config->channel_flags = IWL_CHANNEL_FLAG_EBS |
+                                    IWL_CHANNEL_FLAG_ACCURATE_EBS |
+                                    IWL_CHANNEL_FLAG_EBS_ADD |
+                                    IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE;
+
+       band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+       for (i = 0; i < band->n_channels; i++, j++)
+               scan_config->channel_array[j] = band->channels[i].center_freq;
+       band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+       for (i = 0; i < band->n_channels; i++, j++)
+               scan_config->channel_array[j] = band->channels[i].center_freq;
+
+       cmd.data[0] = scan_config;
+       cmd.len[0] = cmd_size;
+       cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+
+       IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n");
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(scan_config);
+       return ret;
+}
+
+static int iwl_mvm_find_scan_uid(struct iwl_mvm *mvm, u32 uid)
+{
+       int i;
+
+       for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+               if (mvm->scan_uid[i] == uid)
+                       return i;
+
+       return i;
+}
+
+static int iwl_mvm_find_free_scan_uid(struct iwl_mvm *mvm)
+{
+       return iwl_mvm_find_scan_uid(mvm, 0);
+}
+
+static bool iwl_mvm_find_scan_type(struct iwl_mvm *mvm,
+                                  enum iwl_umac_scan_uid_type type)
+{
+       int i;
+
+       for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+               if (mvm->scan_uid[i] & type)
+                       return true;
+
+       return false;
+}
+
+static u32 iwl_generate_scan_uid(struct iwl_mvm *mvm,
+                                enum iwl_umac_scan_uid_type type)
+{
+       u32 uid;
+
+       /* make sure exactly one bit is on in scan type */
+       WARN_ON(hweight8(type) != 1);
+
+       /*
+        * Make sure scan uids are unique. If one scan lasts long time while
+        * others are completing frequently, the seq number will wrap up and
+        * we may have more than one scan with the same uid.
+        */
+       do {
+               uid = type | (mvm->scan_seq_num <<
+                             IWL_UMAC_SCAN_UID_SEQ_OFFSET);
+               mvm->scan_seq_num++;
+       } while (iwl_mvm_find_scan_uid(mvm, uid) <
+                IWL_MVM_MAX_SIMULTANEOUS_SCANS);
+
+       IWL_DEBUG_SCAN(mvm, "Generated scan UID %u\n", uid);
+
+       return uid;
+}
+
+static void
+iwl_mvm_build_generic_umac_scan_cmd(struct iwl_mvm *mvm,
+                                   struct iwl_scan_req_umac *cmd,
+                                   struct iwl_mvm_scan_params *params)
+{
+       memset(cmd, 0, ksize(cmd));
+       cmd->hdr.size = cpu_to_le16(iwl_mvm_scan_size(mvm) -
+                                   sizeof(struct iwl_mvm_umac_cmd_hdr));
+       cmd->active_dwell = params->dwell[IEEE80211_BAND_2GHZ].active;
+       cmd->passive_dwell = params->dwell[IEEE80211_BAND_2GHZ].passive;
+       if (params->passive_fragmented)
+               cmd->fragmented_dwell =
+                               params->dwell[IEEE80211_BAND_2GHZ].passive;
+       cmd->max_out_time = cpu_to_le32(params->max_out_time);
+       cmd->suspend_time = cpu_to_le32(params->suspend_time);
+       cmd->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+}
+
+static void
+iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm,
+                              struct ieee80211_channel **channels,
+                              int n_channels, u32 ssid_bitmap,
+                              struct iwl_scan_req_umac *cmd)
+{
+       struct iwl_scan_channel_cfg_umac *channel_cfg = (void *)&cmd->data;
+       int i;
+
+       for (i = 0; i < n_channels; i++) {
+               channel_cfg[i].flags = cpu_to_le32(ssid_bitmap);
+               channel_cfg[i].channel_num = channels[i]->hw_value;
+               channel_cfg[i].iter_count = 1;
+               channel_cfg[i].iter_interval = 0;
+       }
+}
+
+static u32 iwl_mvm_scan_umac_common_flags(struct iwl_mvm *mvm, int n_ssids,
+                                         struct cfg80211_ssid *ssids,
+                                         int fragmented)
+{
+       int flags = 0;
+
+       if (n_ssids == 0)
+               flags = IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE;
+
+       if (n_ssids == 1 && ssids[0].ssid_len != 0)
+               flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT;
+
+       if (fragmented)
+               flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED;
+
+       if (iwl_mvm_rrm_scan_needed(mvm))
+               flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED;
+
+       return flags;
+}
+
+int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                     struct ieee80211_scan_request *req)
+{
+       struct iwl_host_cmd hcmd = {
+               .id = SCAN_REQ_UMAC,
+               .len = { iwl_mvm_scan_size(mvm), },
+               .data = { mvm->scan_cmd, },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+       struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data +
+               sizeof(struct iwl_scan_channel_cfg_umac) *
+                       mvm->fw->ucode_capa.n_scan_channels;
+       struct iwl_mvm_scan_params params = {};
+       u32 uid, flags;
+       u32 ssid_bitmap = 0;
+       int ret, i, uid_idx;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       uid_idx = iwl_mvm_find_free_scan_uid(mvm);
+       if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+               return -EBUSY;
+
+       /* we should have failed registration if scan_cmd was NULL */
+       if (WARN_ON(mvm->scan_cmd == NULL))
+               return -ENOMEM;
+
+       if (WARN_ON(req->req.n_ssids > PROBE_OPTION_MAX ||
+                   req->ies.common_ie_len +
+                   req->ies.len[NL80211_BAND_2GHZ] +
+                   req->ies.len[NL80211_BAND_5GHZ] + 24 + 2 >
+                   SCAN_OFFLOAD_PROBE_REQ_SIZE || req->req.n_channels >
+                   mvm->fw->ucode_capa.n_scan_channels))
+               return -ENOBUFS;
+
+       iwl_mvm_scan_calc_params(mvm, vif, req->req.n_ssids, req->req.flags,
+                                &params);
+
+       iwl_mvm_build_generic_umac_scan_cmd(mvm, cmd, &params);
+
+       uid = iwl_generate_scan_uid(mvm, IWL_UMAC_SCAN_UID_REG_SCAN);
+       mvm->scan_uid[uid_idx] = uid;
+       cmd->uid = cpu_to_le32(uid);
+
+       cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+
+       flags = iwl_mvm_scan_umac_common_flags(mvm, req->req.n_ssids,
+                                              req->req.ssids,
+                                              params.passive_fragmented);
+
+       flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL;
+
+       cmd->general_flags = cpu_to_le32(flags);
+       cmd->n_channels = req->req.n_channels;
+
+       for (i = 0; i < req->req.n_ssids; i++)
+               ssid_bitmap |= BIT(i);
+
+       iwl_mvm_umac_scan_cfg_channels(mvm, req->req.channels,
+                                      req->req.n_channels, ssid_bitmap, cmd);
+
+       sec_part->schedule[0].iter_count = 1;
+       sec_part->delay = 0;
+
+       iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, &sec_part->preq,
+               req->req.flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+                       req->req.mac_addr : NULL,
+               req->req.mac_addr_mask);
+
+       iwl_mvm_scan_fill_ssids(sec_part->direct_scan, req->req.ssids,
+                               req->req.n_ssids, 0);
+
+       ret = iwl_mvm_send_cmd(mvm, &hcmd);
+       if (!ret) {
+               IWL_DEBUG_SCAN(mvm,
+                              "Scan request was sent successfully\n");
+       } else {
+               /*
+                * If the scan failed, it usually means that the FW was unable
+                * to allocate the time events. Warn on it, but maybe we
+                * should try to send the command again with different params.
+                */
+               IWL_ERR(mvm, "Scan failed! ret %d\n", ret);
+       }
+       return ret;
+}
+
+int iwl_mvm_sched_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_scan_ies *ies)
+{
+
+       struct iwl_host_cmd hcmd = {
+               .id = SCAN_REQ_UMAC,
+               .len = { iwl_mvm_scan_size(mvm), },
+               .data = { mvm->scan_cmd, },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+       struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data +
+               sizeof(struct iwl_scan_channel_cfg_umac) *
+                       mvm->fw->ucode_capa.n_scan_channels;
+       struct iwl_mvm_scan_params params = {};
+       u32 uid, flags;
+       u32 ssid_bitmap = 0;
+       int ret, uid_idx;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       uid_idx = iwl_mvm_find_free_scan_uid(mvm);
+       if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+               return -EBUSY;
+
+       /* we should have failed registration if scan_cmd was NULL */
+       if (WARN_ON(mvm->scan_cmd == NULL))
+               return -ENOMEM;
+
+       if (WARN_ON(req->n_ssids > PROBE_OPTION_MAX ||
+                   ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] +
+                   ies->len[NL80211_BAND_5GHZ] + 24 + 2 >
+                   SCAN_OFFLOAD_PROBE_REQ_SIZE || req->n_channels >
+                   mvm->fw->ucode_capa.n_scan_channels))
+               return -ENOBUFS;
+
+       iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, req->flags,
+                                        &params);
+
+       iwl_mvm_build_generic_umac_scan_cmd(mvm, cmd, &params);
+
+       cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE);
+
+       uid = iwl_generate_scan_uid(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN);
+       mvm->scan_uid[uid_idx] = uid;
+       cmd->uid = cpu_to_le32(uid);
+
+       cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_LOW);
+
+       flags = iwl_mvm_scan_umac_common_flags(mvm, req->n_ssids, req->ssids,
+                                              params.passive_fragmented);
+
+       flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC;
+
+       if (iwl_mvm_scan_pass_all(mvm, req))
+               flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL;
+       else
+               flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH;
+
+       cmd->general_flags = cpu_to_le32(flags);
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT &&
+           mvm->last_ebs_successful)
+               cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS |
+                                    IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+                                    IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
+
+       cmd->n_channels = req->n_channels;
+
+       iwl_scan_offload_build_ssid(req, sec_part->direct_scan, &ssid_bitmap,
+                                   false);
+
+       /* This API uses bits 0-19 instead of 1-20. */
+       ssid_bitmap = ssid_bitmap >> 1;
+
+       iwl_mvm_umac_scan_cfg_channels(mvm, req->channels, req->n_channels,
+                                      ssid_bitmap, cmd);
+
+       sec_part->schedule[0].interval =
+                               cpu_to_le16(req->interval / MSEC_PER_SEC);
+       sec_part->schedule[0].iter_count = 0xff;
+
+       sec_part->delay = 0;
+
+       iwl_mvm_build_unified_scan_probe(mvm, vif, ies, &sec_part->preq,
+               req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+                       req->mac_addr : NULL,
+               req->mac_addr_mask);
+
+       ret = iwl_mvm_send_cmd(mvm, &hcmd);
+       if (!ret) {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sched scan request was sent successfully\n");
+       } else {
+               /*
+                * If the scan failed, it usually means that the FW was unable
+                * to allocate the time events. Warn on it, but maybe we
+                * should try to send the command again with different params.
+                */
+               IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret);
+       }
+       return ret;
+}
+
+int iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
+                                       struct iwl_rx_cmd_buffer *rxb,
+                                       struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+       u32 uid = __le32_to_cpu(notif->uid);
+       bool sched = !!(uid & IWL_UMAC_SCAN_UID_SCHED_SCAN);
+       int uid_idx = iwl_mvm_find_scan_uid(mvm, uid);
+
+       /*
+        * Scan uid may be set to zero in case of scan abort request from above.
+        */
+       if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+               return 0;
+
+       IWL_DEBUG_SCAN(mvm,
+                      "Scan completed, uid %u type %s, status %s, EBS status %s\n",
+                      uid, sched ? "sched" : "regular",
+                      notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
+                               "completed" : "aborted",
+                      notif->ebs_status == IWL_SCAN_EBS_SUCCESS ?
+                               "success" : "failed");
+
+       mvm->last_ebs_successful = !notif->ebs_status;
+       mvm->scan_uid[uid_idx] = 0;
+
+       if (!sched) {
+               ieee80211_scan_completed(mvm->hw,
+                                        notif->status ==
+                                               IWL_SCAN_OFFLOAD_ABORTED);
+               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+       } else if (!iwl_mvm_find_scan_type(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN)) {
+               ieee80211_sched_scan_stopped(mvm->hw);
+       } else {
+               IWL_DEBUG_SCAN(mvm, "Another sched scan is running\n");
+       }
+
+       return 0;
+}
+
+static bool iwl_scan_umac_done_check(struct iwl_notif_wait_data *notif_wait,
+                                    struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_umac_scan_done *scan_done = data;
+       struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+       u32 uid = __le32_to_cpu(notif->uid);
+       int uid_idx = iwl_mvm_find_scan_uid(scan_done->mvm, uid);
+
+       if (WARN_ON(pkt->hdr.cmd != SCAN_COMPLETE_UMAC))
+               return false;
+
+       if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+               return false;
+
+       /*
+        * Clear scan uid of scans that was aborted from above and completed
+        * in FW so the RX handler does nothing.
+        */
+       scan_done->mvm->scan_uid[uid_idx] = 0;
+
+       return !iwl_mvm_find_scan_type(scan_done->mvm, scan_done->type);
+}
+
+static int iwl_umac_scan_abort_one(struct iwl_mvm *mvm, u32 uid)
+{
+       struct iwl_umac_scan_abort cmd = {
+               .hdr.size = cpu_to_le16(sizeof(struct iwl_umac_scan_abort) -
+                                       sizeof(struct iwl_mvm_umac_cmd_hdr)),
+               .uid = cpu_to_le32(uid),
+       };
+
+       lockdep_assert_held(&mvm->mutex);
+
+       IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid);
+
+       return iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_UMAC, 0, sizeof(cmd), &cmd);
+}
+
+static int iwl_umac_scan_stop(struct iwl_mvm *mvm,
+                             enum iwl_umac_scan_uid_type type, bool notify)
+{
+       struct iwl_notification_wait wait_scan_done;
+       static const u8 scan_done_notif[] = { SCAN_COMPLETE_UMAC, };
+       struct iwl_umac_scan_done scan_done = {
+               .mvm = mvm,
+               .type = type,
+       };
+       int i, ret = -EIO;
+
+       iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
+                                  scan_done_notif,
+                                  ARRAY_SIZE(scan_done_notif),
+                                  iwl_scan_umac_done_check, &scan_done);
+
+       IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type);
+
+       for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+               if (mvm->scan_uid[i] & type) {
+                       int err;
+
+                       if (iwl_mvm_is_radio_killed(mvm) &&
+                           (type & IWL_UMAC_SCAN_UID_REG_SCAN)) {
+                               ieee80211_scan_completed(mvm->hw, true);
+                               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+                               break;
+                       }
+
+                       err = iwl_umac_scan_abort_one(mvm, mvm->scan_uid[i]);
+                       if (!err)
+                               ret = 0;
+               }
+       }
+
+       if (ret) {
+               IWL_DEBUG_SCAN(mvm, "Couldn't stop scan\n");
+               iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
+               return ret;
+       }
+
+       ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
+       if (ret)
+               return ret;
+
+       if (notify) {
+               if (type & IWL_UMAC_SCAN_UID_SCHED_SCAN)
+                       ieee80211_sched_scan_stopped(mvm->hw);
+               if (type & IWL_UMAC_SCAN_UID_REG_SCAN) {
+                       ieee80211_scan_completed(mvm->hw, true);
+                       iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+               }
+       }
+
+       return ret;
+}
+
+int iwl_mvm_scan_size(struct iwl_mvm *mvm)
+{
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+               return sizeof(struct iwl_scan_req_umac) +
+                       sizeof(struct iwl_scan_channel_cfg_umac) *
+                               mvm->fw->ucode_capa.n_scan_channels +
+                       sizeof(struct iwl_scan_req_umac_tail);
+
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               return sizeof(struct iwl_scan_req_unified_lmac) +
+                       sizeof(struct iwl_scan_channel_cfg_lmac) *
+                               mvm->fw->ucode_capa.n_scan_channels +
+                       sizeof(struct iwl_scan_probe_req);
+
+       return sizeof(struct iwl_scan_cmd) +
+               mvm->fw->ucode_capa.max_probe_length +
+                       mvm->fw->ucode_capa.n_scan_channels *
+               sizeof(struct iwl_scan_channel);
+}
index dd0dc5b..d86fe43 100644 (file)
@@ -204,6 +204,56 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        return ret;
 }
 
+static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm,
+                                struct ieee80211_sta *sta)
+{
+       unsigned long used_hw_queues;
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       u32 ac;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, NULL);
+
+       /* Find available queues, and allocate them to the ACs */
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               u8 queue = find_first_zero_bit(&used_hw_queues,
+                                              mvm->first_agg_queue);
+
+               if (queue >= mvm->first_agg_queue) {
+                       IWL_ERR(mvm, "Failed to allocate STA queue\n");
+                       return -EBUSY;
+               }
+
+               __set_bit(queue, &used_hw_queues);
+               mvmsta->hw_queue[ac] = queue;
+       }
+
+       /* Found a place for all queues - enable them */
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac],
+                                     iwl_mvm_ac_to_tx_fifo[ac]);
+               mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]);
+       }
+
+       return 0;
+}
+
+static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm,
+                                   struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       unsigned long sta_msk;
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* disable the TDLS STA-specific queues */
+       sta_msk = mvmsta->tfd_queue_msk;
+       for_each_set_bit(i, &sta_msk, sizeof(sta_msk))
+               iwl_mvm_disable_txq(mvm, i);
+}
+
 int iwl_mvm_add_sta(struct iwl_mvm *mvm,
                    struct ieee80211_vif *vif,
                    struct ieee80211_sta *sta)
@@ -237,9 +287,17 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
        atomic_set(&mvm->pending_frames[sta_id], 0);
        mvm_sta->tid_disable_agg = 0;
        mvm_sta->tfd_queue_msk = 0;
-       for (i = 0; i < IEEE80211_NUM_ACS; i++)
-               if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
-                       mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
+
+       /* allocate new queues for a TDLS station */
+       if (sta->tdls) {
+               ret = iwl_mvm_tdls_sta_init(mvm, sta);
+               if (ret)
+                       return ret;
+       } else {
+               for (i = 0; i < IEEE80211_NUM_ACS; i++)
+                       if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
+                               mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
+       }
 
        /* for HW restart - reset everything but the sequence number */
        for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
@@ -251,7 +309,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
 
        ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);
        if (ret)
-               return ret;
+               goto err;
 
        if (vif->type == NL80211_IFTYPE_STATION) {
                if (!sta->tdls) {
@@ -265,6 +323,10 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
        rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
 
        return 0;
+
+err:
+       iwl_mvm_tdls_sta_deinit(mvm, sta);
+       return ret;
 }
 
 int iwl_mvm_update_sta(struct iwl_mvm *mvm,
@@ -398,6 +460,17 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk)
                }
                RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
                clear_bit(sta_id, mvm->sta_drained);
+
+               if (mvm->tfd_drained[sta_id]) {
+                       unsigned long i, msk = mvm->tfd_drained[sta_id];
+
+                       for_each_set_bit(i, &msk, sizeof(msk))
+                               iwl_mvm_disable_txq(mvm, i);
+
+                       mvm->tfd_drained[sta_id] = 0;
+                       IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n",
+                                      sta_id, msk);
+               }
        }
 
        mutex_unlock(&mvm->mutex);
@@ -430,6 +503,15 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
                        mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
        }
 
+       /*
+        * This shouldn't happen - the TDLS channel switch should be canceled
+        * before the STA is removed.
+        */
+       if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) {
+               mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+               cancel_delayed_work(&mvm->tdls_cs.dwork);
+       }
+
        /*
         * Make sure that the tx response code sees the station as -EBUSY and
         * calls the drain worker.
@@ -443,9 +525,22 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
                rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
                                   ERR_PTR(-EBUSY));
                spin_unlock_bh(&mvm_sta->lock);
+
+               /* disable TDLS sta queues on drain complete */
+               if (sta->tdls) {
+                       mvm->tfd_drained[mvm_sta->sta_id] =
+                                                       mvm_sta->tfd_queue_msk;
+                       IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n",
+                                      mvm_sta->sta_id);
+               }
+
                ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
        } else {
                spin_unlock_bh(&mvm_sta->lock);
+
+               if (sta->tdls)
+                       iwl_mvm_tdls_sta_deinit(mvm, sta);
+
                ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
                RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
        }
@@ -1071,15 +1166,16 @@ static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,
 
 static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                                struct iwl_mvm_sta *mvm_sta,
-                               struct ieee80211_key_conf *keyconf,
-                               u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
-                               u32 cmd_flags)
+                               struct ieee80211_key_conf *keyconf, bool mcast,
+                               u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags)
 {
        struct iwl_mvm_add_sta_key_cmd cmd = {};
        __le16 key_flags;
-       int ret, status;
+       int ret;
+       u32 status;
        u16 keyidx;
        int i;
+       u8 sta_id = mvm_sta->sta_id;
 
        keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
                 STA_KEY_FLG_KEYID_MSK;
@@ -1098,12 +1194,18 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
                memcpy(cmd.key, keyconf->key, keyconf->keylen);
                break;
+       case WLAN_CIPHER_SUITE_WEP104:
+               key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES);
+       case WLAN_CIPHER_SUITE_WEP40:
+               key_flags |= cpu_to_le16(STA_KEY_FLG_WEP);
+               memcpy(cmd.key + 3, keyconf->key, keyconf->keylen);
+               break;
        default:
                key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
                memcpy(cmd.key, keyconf->key, keyconf->keylen);
        }
 
-       if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+       if (mcast)
                key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
 
        cmd.key_offset = keyconf->hw_key_idx;
@@ -1195,17 +1297,88 @@ static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm,
        return NULL;
 }
 
+static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_sta *sta,
+                                struct ieee80211_key_conf *keyconf,
+                                bool mcast)
+{
+       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+       int ret;
+       const u8 *addr;
+       struct ieee80211_key_seq seq;
+       u16 p1k[5];
+
+       switch (keyconf->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
+               /* get phase 1 key from mac80211 */
+               ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+               ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+                                          seq.tkip.iv32, p1k, 0);
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+                                          0, NULL, 0);
+               break;
+       default:
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+                                          0, NULL, 0);
+       }
+
+       return ret;
+}
+
+static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id,
+                                   struct ieee80211_key_conf *keyconf,
+                                   bool mcast)
+{
+       struct iwl_mvm_add_sta_key_cmd cmd = {};
+       __le16 key_flags;
+       int ret;
+       u32 status;
+
+       key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
+                                STA_KEY_FLG_KEYID_MSK);
+       key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
+       key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
+
+       if (mcast)
+               key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+
+       cmd.key_flags = key_flags;
+       cmd.key_offset = keyconf->hw_key_idx;
+       cmd.sta_id = sta_id;
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
+                                         &cmd, &status);
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
+               break;
+       }
+
+       return ret;
+}
+
 int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
                        struct ieee80211_vif *vif,
                        struct ieee80211_sta *sta,
                        struct ieee80211_key_conf *keyconf,
                        bool have_key_offset)
 {
-       struct iwl_mvm_sta *mvm_sta;
+       bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+       u8 sta_id;
        int ret;
-       u8 *addr, sta_id;
-       struct ieee80211_key_seq seq;
-       u16 p1k[5];
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -1234,8 +1407,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
                }
        }
 
-       mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
-       if (WARN_ON_ONCE(mvm_sta->vif != vif))
+       if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
                return -EINVAL;
 
        if (!have_key_offset) {
@@ -1249,26 +1421,26 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
                        return -ENOSPC;
        }
 
-       switch (keyconf->cipher) {
-       case WLAN_CIPHER_SUITE_TKIP:
-               addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
-               /* get phase 1 key from mac80211 */
-               ieee80211_get_key_rx_seq(keyconf, 0, &seq);
-               ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
-               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
-                                          seq.tkip.iv32, p1k, 0);
-               break;
-       case WLAN_CIPHER_SUITE_CCMP:
-               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
-                                          0, NULL, 0);
-               break;
-       default:
-               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf,
-                                          sta_id, 0, NULL, 0);
+       ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, mcast);
+       if (ret) {
+               __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+               goto end;
        }
 
-       if (ret)
-               __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+       /*
+        * For WEP, the same key is used for multicast and unicast. Upload it
+        * again, using the same key offset, and now pointing the other one
+        * to the same key slot (offset).
+        * If this fails, remove the original as well.
+        */
+       if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+           keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
+               ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, !mcast);
+               if (ret) {
+                       __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+                       __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
+               }
+       }
 
 end:
        IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
@@ -1282,11 +1454,9 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
                           struct ieee80211_sta *sta,
                           struct ieee80211_key_conf *keyconf)
 {
-       struct iwl_mvm_sta *mvm_sta;
-       struct iwl_mvm_add_sta_key_cmd cmd = {};
-       __le16 key_flags;
-       int ret, status;
+       bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
        u8 sta_id;
+       int ret;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -1299,8 +1469,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
        if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
                return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true);
 
-       ret = __test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
-       if (!ret) {
+       if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) {
                IWL_ERR(mvm, "offset %d not used in fw key table.\n",
                        keyconf->hw_key_idx);
                return -ENOENT;
@@ -1326,35 +1495,17 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
                }
        }
 
-       mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
-       if (WARN_ON_ONCE(mvm_sta->vif != vif))
+       if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
                return -EINVAL;
 
-       key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
-                                STA_KEY_FLG_KEYID_MSK);
-       key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
-       key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
-
-       if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
-               key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
-
-       cmd.key_flags = key_flags;
-       cmd.key_offset = keyconf->hw_key_idx;
-       cmd.sta_id = sta_id;
-
-       status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
-                                         &cmd, &status);
+       ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
+       if (ret)
+               return ret;
 
-       switch (status) {
-       case ADD_STA_SUCCESS:
-               IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
-               break;
-       default:
-               ret = -EIO;
-               IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
-               break;
-       }
+       /* delete WEP key twice to get rid of (now useless) offset */
+       if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+           keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)
+               ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast);
 
        return ret;
 }
@@ -1367,6 +1518,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
 {
        struct iwl_mvm_sta *mvm_sta;
        u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+       bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
 
        if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
                return;
@@ -1381,8 +1533,8 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
                }
        }
 
-       mvm_sta = (void *)sta->drv_priv;
-       iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
+       mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+       iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
                             iv32, phase1key, CMD_ASYNC);
        rcu_read_unlock();
 }
@@ -1580,3 +1732,18 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
                iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
        }
 }
+
+void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_sta *mvmsta;
+
+       rcu_read_lock();
+
+       mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id);
+
+       if (!WARN_ON(!mvmsta))
+               iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
+
+       rcu_read_unlock();
+}
index d9c0d7b..d8f4897 100644 (file)
@@ -264,6 +264,7 @@ 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.
+ * @tx_time: medium time consumed by this A-MPDU
  */
 struct iwl_mvm_tid_data {
        u16 seq_number;
@@ -274,6 +275,7 @@ struct iwl_mvm_tid_data {
        enum iwl_mvm_agg_state state;
        u16 txq_id;
        u16 ssn;
+       u16 tx_time;
 };
 
 static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
@@ -286,6 +288,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
  * struct iwl_mvm_sta - representation of a station in the driver
  * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
  * @tfd_queue_msk: the tfd queues used by the station
+ * @hw_queue: per-AC mapping of the TFD queues used by station
  * @mac_id_n_color: the MAC context this station is linked to
  * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
  *     tid.
@@ -309,6 +312,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
 struct iwl_mvm_sta {
        u32 sta_id;
        u32 tfd_queue_msk;
+       u8 hw_queue[IEEE80211_NUM_ACS];
        u32 mac_id_n_color;
        u16 tid_disable_agg;
        u8 max_agg_bufsize;
@@ -418,5 +422,6 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
 void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
                                       struct iwl_mvm_vif *mvmvif,
                                       bool disable);
+void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 
 #endif /* __sta_h__ */
index 66c82df..c0e00ba 100644 (file)
  *
  *****************************************************************************/
 
+#include <linux/etherdevice.h>
 #include "mvm.h"
 #include "time-event.h"
 
+#define TU_TO_US(x) (x * 1024)
+#define TU_TO_MS(x) (TU_TO_US(x) / 1000)
+
 void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
 {
        struct ieee80211_sta *sta;
@@ -113,17 +117,85 @@ int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        return count;
 }
 
+static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_rx_packet *pkt;
+       struct iwl_tdls_config_res *resp;
+       struct iwl_tdls_config_cmd tdls_cfg_cmd = {};
+       struct iwl_host_cmd cmd = {
+               .id = TDLS_CONFIG_CMD,
+               .flags = CMD_WANT_SKB,
+               .data = { &tdls_cfg_cmd, },
+               .len = { sizeof(struct iwl_tdls_config_cmd), },
+       };
+       struct ieee80211_sta *sta;
+       int ret, i, cnt;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       lockdep_assert_held(&mvm->mutex);
+
+       tdls_cfg_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+       tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID;
+       tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */
+
+       /* for now the Tx cmd is empty and unused */
+
+       /* populate TDLS peer data */
+       cnt = 0;
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (IS_ERR_OR_NULL(sta) || !sta->tdls)
+                       continue;
+
+               tdls_cfg_cmd.sta_info[cnt].sta_id = i;
+               tdls_cfg_cmd.sta_info[cnt].tx_to_peer_tid =
+                                                       IWL_MVM_TDLS_FW_TID;
+               tdls_cfg_cmd.sta_info[cnt].tx_to_peer_ssn = cpu_to_le16(0);
+               tdls_cfg_cmd.sta_info[cnt].is_initiator =
+                               cpu_to_le32(sta->tdls_initiator ? 1 : 0);
+
+               cnt++;
+       }
+
+       tdls_cfg_cmd.tdls_peer_count = cnt;
+       IWL_DEBUG_TDLS(mvm, "send TDLS config to FW for %d peers\n", cnt);
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       if (WARN_ON_ONCE(ret))
+               return;
+
+       pkt = cmd.resp_pkt;
+       if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+               IWL_ERR(mvm, "Bad return from TDLS_CONFIG_COMMAND (0x%08X)\n",
+                       pkt->hdr.flags);
+               goto exit;
+       }
+
+       if (WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp)))
+               goto exit;
+
+       /* we don't really care about the response at this point */
+
+exit:
+       iwl_free_resp(&cmd);
+}
+
 void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                               bool sta_added)
 {
        int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
 
-       /*
-        * Disable ps when the first TDLS sta is added and re-enable it
-        * when the last TDLS sta is removed
-        */
-       if ((tdls_sta_cnt == 1 && sta_added) ||
-           (tdls_sta_cnt == 0 && !sta_added))
+       /* when the first peer joins, send a power update first */
+       if (tdls_sta_cnt == 1 && sta_added)
+               iwl_mvm_power_update_mac(mvm);
+
+       /* configure the FW with TDLS peer info */
+       iwl_mvm_tdls_config(mvm, vif);
+
+       /* when the last peer leaves, send a power update last */
+       if (tdls_sta_cnt == 0 && !sta_added)
                iwl_mvm_power_update_mac(mvm);
 }
 
@@ -147,3 +219,488 @@ void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
 
        iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS);
 }
+
+static const char *
+iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state)
+{
+       switch (state) {
+       case IWL_MVM_TDLS_SW_IDLE:
+               return "IDLE";
+       case IWL_MVM_TDLS_SW_REQ_SENT:
+               return "REQ SENT";
+       case IWL_MVM_TDLS_SW_REQ_RCVD:
+               return "REQ RECEIVED";
+       case IWL_MVM_TDLS_SW_ACTIVE:
+               return "ACTIVE";
+       }
+
+       return NULL;
+}
+
+static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm,
+                                        enum iwl_mvm_tdls_cs_state state)
+{
+       if (mvm->tdls_cs.state == state)
+               return;
+
+       IWL_DEBUG_TDLS(mvm, "TDLS channel switch state: %s -> %s\n",
+                      iwl_mvm_tdls_cs_state_str(mvm->tdls_cs.state),
+                      iwl_mvm_tdls_cs_state_str(state));
+       mvm->tdls_cs.state = state;
+
+       if (state == IWL_MVM_TDLS_SW_IDLE)
+               mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT;
+}
+
+int iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_tdls_channel_switch_notif *notif = (void *)pkt->data;
+       struct ieee80211_sta *sta;
+       unsigned int delay;
+       struct iwl_mvm_sta *mvmsta;
+       struct ieee80211_vif *vif;
+       u32 sta_id = le32_to_cpu(notif->sta_id);
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* can fail sometimes */
+       if (!le32_to_cpu(notif->status)) {
+               iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+               goto out;
+       }
+
+       if (WARN_ON(sta_id >= IWL_MVM_STATION_COUNT))
+               goto out;
+
+       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                       lockdep_is_held(&mvm->mutex));
+       /* the station may not be here, but if it is, it must be a TDLS peer */
+       if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls))
+               goto out;
+
+       mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       vif = mvmsta->vif;
+
+       /*
+        * Update state and possibly switch again after this is over (DTIM).
+        * Also convert TU to msec.
+        */
+       delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
+       mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+                        msecs_to_jiffies(delay));
+
+       iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE);
+
+out:
+       return 0;
+}
+
+static int
+iwl_mvm_tdls_check_action(struct iwl_mvm *mvm,
+                         enum iwl_tdls_channel_switch_type type,
+                         const u8 *peer, bool peer_initiator)
+{
+       bool same_peer = false;
+       int ret = 0;
+
+       /* get the existing peer if it's there */
+       if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE &&
+           mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
+               struct ieee80211_sta *sta = rcu_dereference_protected(
+                               mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
+                               lockdep_is_held(&mvm->mutex));
+               if (!IS_ERR_OR_NULL(sta))
+                       same_peer = ether_addr_equal(peer, sta->addr);
+       }
+
+       switch (mvm->tdls_cs.state) {
+       case IWL_MVM_TDLS_SW_IDLE:
+               /*
+                * might be spurious packet from the peer after the switch is
+                * already done
+                */
+               if (type == TDLS_MOVE_CH)
+                       ret = -EINVAL;
+               break;
+       case IWL_MVM_TDLS_SW_REQ_SENT:
+               /*
+                * We received a ch-switch request while an outgoing one is
+                * pending. Allow it to proceed if the other peer is the same
+                * one we sent to, and we are not the link initiator.
+                */
+               if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH) {
+                       if (!same_peer)
+                               ret = -EBUSY;
+                       else if (!peer_initiator) /* we are the initiator */
+                               ret = -EBUSY;
+               }
+               break;
+       case IWL_MVM_TDLS_SW_REQ_RCVD:
+               /* as above, allow the link initiator to proceed */
+               if (type == TDLS_SEND_CHAN_SW_REQ) {
+                       if (!same_peer)
+                               ret = -EBUSY;
+                       else if (peer_initiator) /* they are the initiator */
+                               ret = -EBUSY;
+               } else if (type == TDLS_MOVE_CH) {
+                       ret = -EINVAL;
+               }
+               break;
+       case IWL_MVM_TDLS_SW_ACTIVE:
+               /* we don't allow initiations during active channel switch */
+               if (type == TDLS_SEND_CHAN_SW_REQ)
+                       ret = -EINVAL;
+               break;
+       }
+
+       if (ret)
+               IWL_DEBUG_TDLS(mvm,
+                              "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n",
+                              type, mvm->tdls_cs.state, peer, same_peer,
+                              peer_initiator);
+
+       return ret;
+}
+
+static int
+iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  enum iwl_tdls_channel_switch_type type,
+                                  const u8 *peer, bool peer_initiator,
+                                  u8 oper_class,
+                                  struct cfg80211_chan_def *chandef,
+                                  u32 timestamp, u16 switch_time,
+                                  u16 switch_timeout, struct sk_buff *skb,
+                                  u32 ch_sw_tm_ie)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_hdr *hdr;
+       struct iwl_tdls_channel_switch_cmd cmd = {0};
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator);
+       if (ret)
+               return ret;
+
+       if (!skb || WARN_ON(skb->len > IWL_TDLS_CH_SW_FRAME_MAX_SIZE)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd.switch_type = type;
+       cmd.timing.frame_timestamp = cpu_to_le32(timestamp);
+       cmd.timing.switch_time = cpu_to_le32(switch_time);
+       cmd.timing.switch_timeout = cpu_to_le32(switch_timeout);
+
+       rcu_read_lock();
+       sta = ieee80211_find_sta(vif, peer);
+       if (!sta) {
+               rcu_read_unlock();
+               ret = -ENOENT;
+               goto out;
+       }
+       mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       cmd.peer_sta_id = cpu_to_le32(mvmsta->sta_id);
+
+       if (!chandef) {
+               if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
+                   mvm->tdls_cs.peer.chandef.chan) {
+                       /* actually moving to the channel */
+                       chandef = &mvm->tdls_cs.peer.chandef;
+               } else if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE &&
+                          type == TDLS_MOVE_CH) {
+                       /* we need to return to base channel */
+                       struct ieee80211_chanctx_conf *chanctx =
+                                       rcu_dereference(vif->chanctx_conf);
+
+                       if (WARN_ON_ONCE(!chanctx)) {
+                               rcu_read_unlock();
+                               goto out;
+                       }
+
+                       chandef = &chanctx->def;
+               }
+       }
+
+       if (chandef) {
+               cmd.ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
+                              PHY_BAND_24 : PHY_BAND_5);
+               cmd.ci.channel = chandef->chan->hw_value;
+               cmd.ci.width = iwl_mvm_get_channel_width(chandef);
+               cmd.ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
+       }
+
+       /* keep quota calculation simple for now - 50% of DTIM for TDLS */
+       cmd.timing.max_offchan_duration =
+                       cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period *
+                                            vif->bss_conf.beacon_int) / 2);
+
+       /* Switch time is the first element in the switch-timing IE. */
+       cmd.frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2);
+
+       info = IEEE80211_SKB_CB(skb);
+       if (info->control.hw_key)
+               iwl_mvm_set_tx_cmd_crypto(mvm, info, &cmd.frame.tx_cmd, skb);
+
+       iwl_mvm_set_tx_cmd(mvm, skb, &cmd.frame.tx_cmd, info,
+                          mvmsta->sta_id);
+
+       hdr = (void *)skb->data;
+       iwl_mvm_set_tx_cmd_rate(mvm, &cmd.frame.tx_cmd, info, sta,
+                               hdr->frame_control);
+       rcu_read_unlock();
+
+       memcpy(cmd.frame.data, skb->data, skb->len);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0,
+                                  sizeof(cmd), &cmd);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n",
+                       ret);
+               goto out;
+       }
+
+       /* channel switch has started, update state */
+       if (type != TDLS_MOVE_CH) {
+               mvm->tdls_cs.cur_sta_id = mvmsta->sta_id;
+               iwl_mvm_tdls_update_cs_state(mvm,
+                                            type == TDLS_SEND_CHAN_SW_REQ ?
+                                            IWL_MVM_TDLS_SW_REQ_SENT :
+                                            IWL_MVM_TDLS_SW_REQ_RCVD);
+       }
+
+out:
+
+       /* channel switch failed - we are idle */
+       if (ret)
+               iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+
+       return ret;
+}
+
+void iwl_mvm_tdls_ch_switch_work(struct work_struct *work)
+{
+       struct iwl_mvm *mvm;
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+       struct ieee80211_vif *vif;
+       unsigned int delay;
+       int ret;
+
+       mvm = container_of(work, struct iwl_mvm, tdls_cs.dwork.work);
+       mutex_lock(&mvm->mutex);
+
+       /* called after an active channel switch has finished or timed-out */
+       iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+
+       /* station might be gone, in that case do nothing */
+       if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT)
+               goto out;
+
+       sta = rcu_dereference_protected(
+                               mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
+                               lockdep_is_held(&mvm->mutex));
+       /* the station may not be here, but if it is, it must be a TDLS peer */
+       if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls))
+               goto out;
+
+       mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       vif = mvmsta->vif;
+       ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
+                                                TDLS_SEND_CHAN_SW_REQ,
+                                                sta->addr,
+                                                mvm->tdls_cs.peer.initiator,
+                                                mvm->tdls_cs.peer.op_class,
+                                                &mvm->tdls_cs.peer.chandef,
+                                                0, 0, 0,
+                                                mvm->tdls_cs.peer.skb,
+                                                mvm->tdls_cs.peer.ch_sw_tm_ie);
+       if (ret)
+               IWL_ERR(mvm, "Not sending TDLS channel switch: %d\n", ret);
+
+       /* retry after a DTIM if we failed sending now */
+       delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
+       queue_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+                          msecs_to_jiffies(delay));
+out:
+       mutex_unlock(&mvm->mutex);
+}
+
+int
+iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u8 oper_class,
+                           struct cfg80211_chan_def *chandef,
+                           struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_sta *mvmsta;
+       unsigned int delay;
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       IWL_DEBUG_TDLS(mvm, "TDLS channel switch with %pM ch %d width %d\n",
+                      sta->addr, chandef->chan->center_freq, chandef->width);
+
+       /* we only support a single peer for channel switching */
+       if (mvm->tdls_cs.peer.sta_id != IWL_MVM_STATION_COUNT) {
+               IWL_DEBUG_TDLS(mvm,
+                              "Existing peer. Can't start switch with %pM\n",
+                              sta->addr);
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
+                                                TDLS_SEND_CHAN_SW_REQ,
+                                                sta->addr, sta->tdls_initiator,
+                                                oper_class, chandef, 0, 0, 0,
+                                                tmpl_skb, ch_sw_tm_ie);
+       if (ret)
+               goto out;
+
+       /*
+        * Mark the peer as "in tdls switch" for this vif. We only allow a
+        * single such peer per vif.
+        */
+       mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL);
+       if (!mvm->tdls_cs.peer.skb) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       mvm->tdls_cs.peer.sta_id = mvmsta->sta_id;
+       mvm->tdls_cs.peer.chandef = *chandef;
+       mvm->tdls_cs.peer.initiator = sta->tdls_initiator;
+       mvm->tdls_cs.peer.op_class = oper_class;
+       mvm->tdls_cs.peer.ch_sw_tm_ie = ch_sw_tm_ie;
+
+       /*
+        * Wait for 2 DTIM periods before attempting the next switch. The next
+        * switch will be made sooner if the current one completes before that.
+        */
+       delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period *
+                            vif->bss_conf.beacon_int);
+       mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+                        msecs_to_jiffies(delay));
+
+out:
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_sta *sta)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct ieee80211_sta *cur_sta;
+       bool wait_for_phy = false;
+
+       mutex_lock(&mvm->mutex);
+
+       IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr);
+
+       /* we only support a single peer for channel switching */
+       if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) {
+               IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr);
+               goto out;
+       }
+
+       cur_sta = rcu_dereference_protected(
+                               mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
+                               lockdep_is_held(&mvm->mutex));
+       /* make sure it's the same peer */
+       if (cur_sta != sta)
+               goto out;
+
+       /*
+        * If we're currently in a switch because of the now canceled peer,
+        * wait a DTIM here to make sure the phy is back on the base channel.
+        * We can't otherwise force it.
+        */
+       if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id &&
+           mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE)
+               wait_for_phy = true;
+
+       mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+       dev_kfree_skb(mvm->tdls_cs.peer.skb);
+       mvm->tdls_cs.peer.skb = NULL;
+
+out:
+       mutex_unlock(&mvm->mutex);
+
+       /* make sure the phy is on the base channel */
+       if (wait_for_phy)
+               msleep(TU_TO_MS(vif->bss_conf.dtim_period *
+                               vif->bss_conf.beacon_int));
+
+       /* flush the channel switch state */
+       flush_delayed_work(&mvm->tdls_cs.dwork);
+
+       IWL_DEBUG_TDLS(mvm, "TDLS ending channel switch with %pM\n", sta->addr);
+}
+
+void
+iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_tdls_ch_sw_params *params)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       enum iwl_tdls_channel_switch_type type;
+       unsigned int delay;
+
+       mutex_lock(&mvm->mutex);
+
+       IWL_DEBUG_TDLS(mvm,
+                      "Received TDLS ch switch action %d from %pM status %d\n",
+                      params->action_code, params->sta->addr, params->status);
+
+       /*
+        * we got a non-zero status from a peer we were switching to - move to
+        * the idle state and retry again later
+        */
+       if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE &&
+           params->status != 0 &&
+           mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
+           mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
+               struct ieee80211_sta *cur_sta;
+
+               /* make sure it's the same peer */
+               cur_sta = rcu_dereference_protected(
+                               mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
+                               lockdep_is_held(&mvm->mutex));
+               if (cur_sta == params->sta) {
+                       iwl_mvm_tdls_update_cs_state(mvm,
+                                                    IWL_MVM_TDLS_SW_IDLE);
+                       goto retry;
+               }
+       }
+
+       type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ?
+              TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH;
+
+       iwl_mvm_tdls_config_channel_switch(mvm, vif, type, params->sta->addr,
+                                          params->sta->tdls_initiator, 0,
+                                          params->chandef, params->timestamp,
+                                          params->switch_time,
+                                          params->switch_timeout,
+                                          params->tmpl_skb,
+                                          params->ch_sw_tm_ie);
+
+retry:
+       /* register a timeout in case we don't succeed in switching */
+       delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int *
+               1024 / 1000;
+       mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+                        msecs_to_jiffies(delay));
+       mutex_unlock(&mvm->mutex);
+}
index 6dfad23..54fafbf 100644 (file)
@@ -191,6 +191,35 @@ static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
        return true;
 }
 
+static void
+iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
+                            struct iwl_mvm_time_event_data *te_data,
+                            struct iwl_time_event_notif *notif)
+{
+       if (!le32_to_cpu(notif->status)) {
+               IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
+               iwl_mvm_te_clear_data(mvm, te_data);
+               return;
+       }
+
+       switch (te_data->vif->type) {
+       case NL80211_IFTYPE_AP:
+               iwl_mvm_csa_noa_start(mvm);
+               break;
+       case NL80211_IFTYPE_STATION:
+               iwl_mvm_csa_client_absent(mvm, te_data->vif);
+               ieee80211_chswitch_done(te_data->vif, true);
+               break;
+       default:
+               /* should never happen */
+               WARN_ON_ONCE(1);
+               break;
+       }
+
+       /* we don't need it anymore */
+       iwl_mvm_te_clear_data(mvm, te_data);
+}
+
 /*
  * Handles a FW notification for an event that is known to the driver.
  *
@@ -252,14 +281,8 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
                        set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
                        iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
                        ieee80211_ready_on_channel(mvm->hw);
-               } else if (te_data->vif->type == NL80211_IFTYPE_AP) {
-                       if (le32_to_cpu(notif->status))
-                               iwl_mvm_csa_noa_start(mvm);
-                       else
-                               IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n");
-
-                       /* we don't need it anymore */
-                       iwl_mvm_te_clear_data(mvm, te_data);
+               } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
+                       iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
                }
        } else {
                IWL_WARN(mvm, "Got TE with unknown action\n");
@@ -549,18 +572,11 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
        }
 }
 
-/*
- * Explicit request to remove a time event. The removal of a time event needs to
- * be synchronized with the flow of a time event's end notification, which also
- * removes the time event from the op mode data structures.
- */
-void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
-                              struct iwl_mvm_vif *mvmvif,
-                              struct iwl_mvm_time_event_data *te_data)
+static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+                                       struct iwl_mvm_time_event_data *te_data,
+                                       u32 *uid)
 {
-       struct iwl_time_event_cmd time_cmd = {};
-       u32 id, uid;
-       int ret;
+       u32 id;
 
        /*
         * It is possible that by the time we got to this point the time
@@ -569,7 +585,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
        spin_lock_bh(&mvm->time_event_lock);
 
        /* Save time event uid before clearing its data */
-       uid = te_data->uid;
+       *uid = te_data->uid;
        id = te_data->id;
 
        /*
@@ -584,10 +600,59 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
         * send a removal command.
         */
        if (id == TE_MAX) {
-               IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", uid);
-               return;
+               IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);
+               return false;
        }
 
+       return true;
+}
+
+/*
+ * Explicit request to remove a aux roc time event. The removal of a time
+ * event needs to be synchronized with the flow of a time event's end
+ * notification, which also removes the time event from the op mode
+ * data structures.
+ */
+static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
+                                     struct iwl_mvm_vif *mvmvif,
+                                     struct iwl_mvm_time_event_data *te_data)
+{
+       struct iwl_hs20_roc_req aux_cmd = {};
+       u32 uid;
+       int ret;
+
+       if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
+               return;
+
+       aux_cmd.event_unique_id = cpu_to_le32(uid);
+       aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+       aux_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+       IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
+                    le32_to_cpu(aux_cmd.event_unique_id));
+       ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
+                                  sizeof(aux_cmd), &aux_cmd);
+
+       if (WARN_ON(ret))
+               return;
+}
+
+/*
+ * Explicit request to remove a time event. The removal of a time event needs to
+ * be synchronized with the flow of a time event's end notification, which also
+ * removes the time event from the op mode data structures.
+ */
+void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+                              struct iwl_mvm_vif *mvmvif,
+                              struct iwl_mvm_time_event_data *te_data)
+{
+       struct iwl_time_event_cmd time_cmd = {};
+       u32 uid;
+       int ret;
+
+       if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
+               return;
+
        /* When we remove a TE, the UID is to be set in the id field */
        time_cmd.id = cpu_to_le32(uid);
        time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
@@ -666,13 +731,17 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
 }
 
-void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
+void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
 {
        struct iwl_mvm_vif *mvmvif;
        struct iwl_mvm_time_event_data *te_data;
+       bool is_p2p = false;
 
        lockdep_assert_held(&mvm->mutex);
 
+       mvmvif = NULL;
+       spin_lock_bh(&mvm->time_event_lock);
+
        /*
         * Iterate over the list of time events and find the time event that is
         * associated with a P2P_DEVICE interface.
@@ -680,22 +749,41 @@ void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
         * event at any given time and this time event coresponds to a ROC
         * request
         */
-       mvmvif = NULL;
-       spin_lock_bh(&mvm->time_event_lock);
        list_for_each_entry(te_data, &mvm->time_event_list, list) {
-               if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE &&
+                   te_data->running) {
                        mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
-                       break;
+                       is_p2p = true;
+                       goto remove_te;
                }
        }
+
+       /*
+        * Iterate over the list of aux roc time events and find the time
+        * event that is associated with a BSS interface.
+        * This assumes that a BSS interface can have only a single time
+        * event at any given time and this time event coresponds to a ROC
+        * request
+        */
+       list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) {
+               if (te_data->running) {
+                       mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+                       goto remove_te;
+               }
+       }
+
+remove_te:
        spin_unlock_bh(&mvm->time_event_lock);
 
        if (!mvmvif) {
-               IWL_WARN(mvm, "P2P_DEVICE no remain on channel event\n");
+               IWL_WARN(mvm, "No remain on channel event\n");
                return;
        }
 
-       iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+       if (is_p2p)
+               iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+       else
+               iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
 
        iwl_mvm_roc_finished(mvm);
 }
index b350e47..6f6b35d 100644 (file)
@@ -182,14 +182,14 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                          int duration, enum ieee80211_roc_type type);
 
 /**
- * iwl_mvm_stop_p2p_roc - stop remain on channel for p2p device functionlity
+ * iwl_mvm_stop_roc - stop remain on channel functionality
  * @mvm: the mvm component
  *
  * This function can be used to cancel an ongoing ROC session.
  * The function is async, it will instruct the FW to stop serving the ROC
  * session, but will not wait for the actual stopping of the session.
  */
-void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm);
+void iwl_mvm_stop_roc(struct iwl_mvm *mvm);
 
 /**
  * iwl_mvm_remove_time_event - general function to clean up of time event
index d4f2c29..2b1e61f 100644 (file)
@@ -95,32 +95,81 @@ static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
        iwl_mvm_set_hw_ctkill_state(mvm, false);
 }
 
-static bool iwl_mvm_temp_notif(struct iwl_notif_wait_data *notif_wait,
-                              struct iwl_rx_packet *pkt, void *data)
+void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
+{
+       /* ignore the notification if we are in test mode */
+       if (mvm->temperature_test)
+               return;
+
+       if (mvm->temperature == temp)
+               return;
+
+       mvm->temperature = temp;
+       iwl_mvm_tt_handler(mvm);
+}
+
+static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
+                                   struct iwl_rx_packet *pkt)
 {
-       struct iwl_mvm *mvm =
-               container_of(notif_wait, struct iwl_mvm, notif_wait);
-       int *temp = data;
        struct iwl_dts_measurement_notif *notif;
        int len = iwl_rx_packet_payload_len(pkt);
+       int temp;
 
        if (WARN_ON_ONCE(len != sizeof(*notif))) {
                IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
-               return true;
+               return -EINVAL;
        }
 
        notif = (void *)pkt->data;
 
-       *temp = le32_to_cpu(notif->temp);
+       temp = le32_to_cpu(notif->temp);
 
        /* shouldn't be negative, but since it's s32, make sure it isn't */
-       if (WARN_ON_ONCE(*temp < 0))
-               *temp = 0;
+       if (WARN_ON_ONCE(temp < 0))
+               temp = 0;
+
+       IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp);
+
+       return temp;
+}
+
+static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
+                                   struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm *mvm =
+               container_of(notif_wait, struct iwl_mvm, notif_wait);
+       int *temp = data;
+       int ret;
+
+       ret = iwl_mvm_temp_notif_parse(mvm, pkt);
+       if (ret < 0)
+               return true;
+
+       *temp = ret;
 
-       IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", *temp);
        return true;
 }
 
+int iwl_mvm_temp_notif(struct iwl_mvm *mvm,
+                      struct iwl_rx_cmd_buffer *rxb,
+                      struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       int temp;
+
+       /* the notification is handled synchronously in ctkill, so skip here */
+       if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
+               return 0;
+
+       temp = iwl_mvm_temp_notif_parse(mvm, pkt);
+       if (temp < 0)
+               return 0;
+
+       iwl_mvm_tt_temp_changed(mvm, temp);
+
+       return 0;
+}
+
 static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm)
 {
        struct iwl_dts_measurement_cmd cmd = {
@@ -141,7 +190,7 @@ int iwl_mvm_get_temp(struct iwl_mvm *mvm)
 
        iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
                                   temp_notif, ARRAY_SIZE(temp_notif),
-                                  iwl_mvm_temp_notif, &temp);
+                                  iwl_mvm_temp_notif_wait, &temp);
 
        ret = iwl_mvm_get_temp_cmd(mvm);
        if (ret) {
index 8d84873..4f15d9d 100644 (file)
@@ -73,9 +73,9 @@
 /*
  * Sets most of the Tx cmd's fields
  */
-static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
-                              struct iwl_tx_cmd *tx_cmd,
-                              struct ieee80211_tx_info *info, u8 sta_id)
+void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+                       struct iwl_tx_cmd *tx_cmd,
+                       struct ieee80211_tx_info *info, u8 sta_id)
 {
        struct ieee80211_hdr *hdr = (void *)skb->data;
        __le16 fc = hdr->frame_control;
@@ -149,11 +149,9 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
 /*
  * Sets the fields in the Tx cmd that are rate related
  */
-static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
-                                   struct iwl_tx_cmd *tx_cmd,
-                                   struct ieee80211_tx_info *info,
-                                   struct ieee80211_sta *sta,
-                                   __le16 fc)
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+                           struct ieee80211_tx_info *info,
+                           struct ieee80211_sta *sta, __le16 fc)
 {
        u32 rate_flags;
        int rate_idx;
@@ -232,10 +230,10 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
 /*
  * Sets the fields in the Tx cmd that are crypto related
  */
-static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
-                                     struct ieee80211_tx_info *info,
-                                     struct iwl_tx_cmd *tx_cmd,
-                                     struct sk_buff *skb_frag)
+void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
+                              struct ieee80211_tx_info *info,
+                              struct iwl_tx_cmd *tx_cmd,
+                              struct sk_buff *skb_frag)
 {
        struct ieee80211_key_conf *keyconf = info->control.hw_key;
 
@@ -426,6 +424,13 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
 
        WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
 
+       if (sta->tdls) {
+               /* default to TID 0 for non-QoS packets */
+               u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid;
+
+               txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]];
+       }
+
        if (is_ampdu) {
                if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON))
                        goto drop_unlock_sta;
@@ -660,6 +665,12 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        seq_ctl = le16_to_cpu(hdr->seq_ctrl);
                }
 
+               /*
+                * TODO: this is not accurate if we are freeing more than one
+                * packet.
+                */
+               info->status.tx_time =
+                       le16_to_cpu(tx_resp->wireless_media_time);
                BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1);
                info->status.status_driver_data[0] =
                                (void *)(uintptr_t)tx_resp->reduced_tpc;
@@ -852,6 +863,8 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
                mvmsta->tid_data[tid].rate_n_flags =
                        le32_to_cpu(tx_resp->initial_rate);
                mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc;
+               mvmsta->tid_data[tid].tx_time =
+                       le16_to_cpu(tx_resp->wireless_media_time);
        }
 
        rcu_read_unlock();
@@ -880,6 +893,8 @@ static void iwl_mvm_tx_info_from_ba_notif(struct ieee80211_tx_info *info,
        info->status.ampdu_len = ba_notif->txed;
        iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags,
                                    info);
+       /* TODO: not accounted if the whole A-MPDU failed */
+       info->status.tx_time = tid_data->tx_time;
        info->status.status_driver_data[0] =
                (void *)(uintptr_t)tid_data->reduced_tpc;
 }
index 6ced854..3ee8e38 100644 (file)
@@ -499,6 +499,7 @@ static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {}
 static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
+       const struct iwl_cfg *cfg_7265d __maybe_unused = NULL;
        struct iwl_trans *iwl_trans;
        struct iwl_trans_pcie *trans_pcie;
        int ret;
@@ -507,6 +508,25 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (IS_ERR(iwl_trans))
                return PTR_ERR(iwl_trans);
 
+#if IS_ENABLED(CONFIG_IWLMVM)
+       /*
+        * special-case 7265D, it has the same PCI IDs.
+        *
+        * Note that because we already pass the cfg to the transport above,
+        * all the parameters that the transport uses must, until that is
+        * changed, be identical to the ones in the 7265D configuration.
+        */
+       if (cfg == &iwl7265_2ac_cfg)
+               cfg_7265d = &iwl7265d_2ac_cfg;
+       else if (cfg == &iwl7265_2n_cfg)
+               cfg_7265d = &iwl7265d_2n_cfg;
+       else if (cfg == &iwl7265_n_cfg)
+               cfg_7265d = &iwl7265d_n_cfg;
+       if (cfg_7265d &&
+           (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D)
+               cfg = cfg_7265d;
+#endif
+
        pci_set_drvdata(pdev, iwl_trans);
 
        trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans);
index ea8efed..5d79a1f 100644 (file)
 #include "iwl-agn-hw.h"
 #include "iwl-fw-error-dump.h"
 #include "internal.h"
+#include "iwl-fh.h"
+
+/* extended range in FW SRAM */
+#define IWL_FW_MEM_EXTENDED_START      0x40000
+#define IWL_FW_MEM_EXTENDED_END                0x57FFF
 
 static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
 {
@@ -512,6 +517,9 @@ static int iwl_pcie_set_hw_ready(struct iwl_trans *trans)
                           CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
                           HW_READY_TIMEOUT);
 
+       if (ret >= 0)
+               iwl_set_bit(trans, CSR_MBOX_SET_REG, CSR_MBOX_SET_REG_OS_ALIVE);
+
        IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : "");
        return ret;
 }
@@ -624,14 +632,28 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
        }
 
        for (offset = 0; offset < section->len; offset += chunk_sz) {
-               u32 copy_size;
+               u32 copy_size, dst_addr;
+               bool extended_addr = false;
 
                copy_size = min_t(u32, chunk_sz, section->len - offset);
+               dst_addr = section->offset + offset;
+
+               if (dst_addr >= IWL_FW_MEM_EXTENDED_START &&
+                   dst_addr <= IWL_FW_MEM_EXTENDED_END)
+                       extended_addr = true;
+
+               if (extended_addr)
+                       iwl_set_bits_prph(trans, LMPM_CHICK,
+                                         LMPM_CHICK_EXTENDED_ADDR_SPACE);
 
                memcpy(v_addr, (u8 *)section->data + offset, copy_size);
-               ret = iwl_pcie_load_firmware_chunk(trans,
-                                                  section->offset + offset,
-                                                  p_addr, copy_size);
+               ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr,
+                                                  copy_size);
+
+               if (extended_addr)
+                       iwl_clear_bits_prph(trans, LMPM_CHICK,
+                                           LMPM_CHICK_EXTENDED_ADDR_SPACE);
+
                if (ret) {
                        IWL_ERR(trans,
                                "Could not load the [%d] uCode section\n",
@@ -644,14 +666,14 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
        return ret;
 }
 
-static int iwl_pcie_load_cpu_secured_sections(struct iwl_trans *trans,
-                                             const struct fw_img *image,
-                                             int cpu,
-                                             int *first_ucode_section)
+static int iwl_pcie_load_cpu_sections_8000b(struct iwl_trans *trans,
+                                           const struct fw_img *image,
+                                           int cpu,
+                                           int *first_ucode_section)
 {
        int shift_param;
-       int i, ret = 0;
-       u32 last_read_idx = 0;
+       int i, ret = 0, sec_num = 0x1;
+       u32 val, last_read_idx = 0;
 
        if (cpu == 1) {
                shift_param = 0;
@@ -672,21 +694,16 @@ static int iwl_pcie_load_cpu_secured_sections(struct iwl_trans *trans,
                        break;
                }
 
-               if (i == (*first_ucode_section) + 1)
-                       /* set CPU to started */
-                       iwl_set_bits_prph(trans,
-                                         CSR_UCODE_LOAD_STATUS_ADDR,
-                                         LMPM_CPU_HDRS_LOADING_COMPLETED
-                                         << shift_param);
-
                ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
                if (ret)
                        return ret;
+
+               /* Notify the ucode of the loaded section number and status */
+               val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS);
+               val = val | (sec_num << shift_param);
+               iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val);
+               sec_num = (sec_num << 1) | 0x1;
        }
-       /* image loading complete */
-       iwl_set_bits_prph(trans,
-                         CSR_UCODE_LOAD_STATUS_ADDR,
-                         LMPM_CPU_UCODE_LOADING_COMPLETED << shift_param);
 
        *first_ucode_section = last_read_idx;
 
@@ -739,46 +756,78 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
        return 0;
 }
 
-static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
-                               const struct fw_img *image)
+static void iwl_pcie_apply_destination(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       int ret = 0;
-       int first_ucode_section;
+       const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv;
+       int i;
 
-       IWL_DEBUG_FW(trans,
-                    "working with %s CPU\n",
-                    image->is_dual_cpus ? "Dual" : "Single");
+       if (dest->version)
+               IWL_ERR(trans,
+                       "DBG DEST version is %d - expect issues\n",
+                       dest->version);
 
-       /* configure the ucode to be ready to get the secured image */
-       if (iwl_has_secure_boot(trans->hw_rev, trans->cfg->device_family)) {
-               /* set secure boot inspector addresses */
-               iwl_write_prph(trans,
-                              LMPM_SECURE_INSPECTOR_CODE_ADDR,
-                              LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE);
+       IWL_INFO(trans, "Applying debug destination %s\n",
+                get_fw_dbg_mode_string(dest->monitor_mode));
 
-               iwl_write_prph(trans,
-                              LMPM_SECURE_INSPECTOR_DATA_ADDR,
-                              LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE);
+       if (dest->monitor_mode == EXTERNAL_MODE)
+               iwl_pcie_alloc_fw_monitor(trans);
+       else
+               IWL_WARN(trans, "PCI should have external buffer debug\n");
 
-               /* set CPU1 header address */
-               iwl_write_prph(trans,
-                              LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR,
-                              LMPM_SECURE_CPU1_HDR_MEM_SPACE);
+       for (i = 0; i < trans->dbg_dest_reg_num; i++) {
+               u32 addr = le32_to_cpu(dest->reg_ops[i].addr);
+               u32 val = le32_to_cpu(dest->reg_ops[i].val);
 
-               /* load to FW the binary Secured sections of CPU1 */
-               ret = iwl_pcie_load_cpu_secured_sections(trans, image, 1,
-                                                        &first_ucode_section);
-               if (ret)
-                       return ret;
+               switch (dest->reg_ops[i].op) {
+               case CSR_ASSIGN:
+                       iwl_write32(trans, addr, val);
+                       break;
+               case CSR_SETBIT:
+                       iwl_set_bit(trans, addr, BIT(val));
+                       break;
+               case CSR_CLEARBIT:
+                       iwl_clear_bit(trans, addr, BIT(val));
+                       break;
+               case PRPH_ASSIGN:
+                       iwl_write_prph(trans, addr, val);
+                       break;
+               case PRPH_SETBIT:
+                       iwl_set_bits_prph(trans, addr, BIT(val));
+                       break;
+               case PRPH_CLEARBIT:
+                       iwl_clear_bits_prph(trans, addr, BIT(val));
+                       break;
+               default:
+                       IWL_ERR(trans, "FW debug - unknown OP %d\n",
+                               dest->reg_ops[i].op);
+                       break;
+               }
+       }
 
-       } else {
-               /* load to FW the binary Non secured sections of CPU1 */
-               ret = iwl_pcie_load_cpu_sections(trans, image, 1,
-                                                &first_ucode_section);
-               if (ret)
-                       return ret;
+       if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) {
+               iwl_write_prph(trans, le32_to_cpu(dest->base_reg),
+                              trans_pcie->fw_mon_phys >> dest->base_shift);
+               iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
+                              (trans_pcie->fw_mon_phys +
+                               trans_pcie->fw_mon_size) >> dest->end_shift);
        }
+}
+
+static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
+                               const struct fw_img *image)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int ret = 0;
+       int first_ucode_section;
+
+       IWL_DEBUG_FW(trans, "working with %s CPU\n",
+                    image->is_dual_cpus ? "Dual" : "Single");
+
+       /* load to FW the binary non secured sections of CPU1 */
+       ret = iwl_pcie_load_cpu_sections(trans, image, 1, &first_ucode_section);
+       if (ret)
+               return ret;
 
        if (image->is_dual_cpus) {
                /* set CPU2 header address */
@@ -787,14 +836,8 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
                               LMPM_SECURE_CPU2_HDR_MEM_SPACE);
 
                /* load to FW the binary sections of CPU2 */
-               if (iwl_has_secure_boot(trans->hw_rev,
-                                       trans->cfg->device_family))
-                       ret = iwl_pcie_load_cpu_secured_sections(
-                                                       trans, image, 2,
-                                                       &first_ucode_section);
-               else
-                       ret = iwl_pcie_load_cpu_sections(trans, image, 2,
-                                                        &first_ucode_section);
+               ret = iwl_pcie_load_cpu_sections(trans, image, 2,
+                                                &first_ucode_section);
                if (ret)
                        return ret;
        }
@@ -811,6 +854,8 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
                                       (trans_pcie->fw_mon_phys +
                                        trans_pcie->fw_mon_size) >> 4);
                }
+       } else if (trans->dbg_dest_tlv) {
+               iwl_pcie_apply_destination(trans);
        }
 
        /* release CPU reset */
@@ -819,18 +864,50 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
        else
                iwl_write32(trans, CSR_RESET, 0);
 
-       if (iwl_has_secure_boot(trans->hw_rev, trans->cfg->device_family)) {
-               /* wait for image verification to complete  */
-               ret = iwl_poll_prph_bit(trans,
-                                       LMPM_SECURE_BOOT_CPU1_STATUS_ADDR,
-                                       LMPM_SECURE_BOOT_STATUS_SUCCESS,
-                                       LMPM_SECURE_BOOT_STATUS_SUCCESS,
-                                       LMPM_SECURE_TIME_OUT);
+       return 0;
+}
 
-               if (ret < 0) {
-                       IWL_ERR(trans, "Time out on secure boot process\n");
-                       return ret;
-               }
+static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
+                                          const struct fw_img *image)
+{
+       int ret = 0;
+       int first_ucode_section;
+       u32 reg;
+
+       IWL_DEBUG_FW(trans, "working with %s CPU\n",
+                    image->is_dual_cpus ? "Dual" : "Single");
+
+       /* configure the ucode to be ready to get the secured image */
+       /* release CPU reset */
+       iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
+
+       /* load to FW the binary Secured sections of CPU1 */
+       ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 1,
+                                              &first_ucode_section);
+       if (ret)
+               return ret;
+
+       /* load to FW the binary sections of CPU2 */
+       ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 2,
+                                              &first_ucode_section);
+       if (ret)
+               return ret;
+
+       /* Notify FW loading is done */
+       iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
+
+       /* wait for image verification to complete  */
+       ret = iwl_poll_prph_bit(trans, LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0,
+                               LMPM_SECURE_BOOT_STATUS_SUCCESS,
+                               LMPM_SECURE_BOOT_STATUS_SUCCESS,
+                               LMPM_SECURE_TIME_OUT);
+       if (ret < 0) {
+               reg = iwl_read_prph(trans,
+                                   LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0);
+
+               IWL_ERR(trans, "Timeout on secure boot process, reg = %x\n",
+                       reg);
+               return ret;
        }
 
        return 0;
@@ -882,7 +959,11 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
        iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
 
        /* Load the given image to the HW */
-       return iwl_pcie_load_given_ucode(trans, fw);
+       if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) &&
+           (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP))
+               return iwl_pcie_load_given_ucode_8000b(trans, fw);
+       else
+               return iwl_pcie_load_given_ucode(trans, fw);
 }
 
 static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
@@ -939,7 +1020,8 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        spin_unlock(&trans_pcie->irq_lock);
 
        /* stop and reset the on-board processor */
-       iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+       iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+       udelay(20);
 
        /* clear all status bits */
        clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
@@ -972,6 +1054,9 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
                clear_bit(STATUS_RFKILL, &trans->status);
        if (hw_rfkill != was_hw_rfkill)
                iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+
+       /* re-take ownership to prevent other users from stealing the deivce */
+       iwl_pcie_prepare_card_hw(trans);
 }
 
 void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
@@ -1031,6 +1116,9 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
        iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
        iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 
+       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+               udelay(2);
+
        ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
                           CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
                           CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
@@ -1233,6 +1321,8 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
        /* this bit wakes up the NIC */
        __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
                                 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+               udelay(2);
 
        /*
         * These bits say the device is running, and should keep running for
@@ -1941,6 +2031,31 @@ static u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans,
        return csr_len;
 }
 
+static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans,
+                                      struct iwl_fw_error_dump_data **data)
+{
+       u32 fh_regs_len = FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND;
+       unsigned long flags;
+       __le32 *val;
+       int i;
+
+       if (!iwl_trans_grab_nic_access(trans, false, &flags))
+               return 0;
+
+       (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS);
+       (*data)->len = cpu_to_le32(fh_regs_len);
+       val = (void *)(*data)->data;
+
+       for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32))
+               *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
+
+       iwl_trans_release_nic_access(trans, &flags);
+
+       *data = iwl_fw_error_next_data(*data);
+
+       return sizeof(**data) + fh_regs_len;
+}
+
 static
 struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
 {
@@ -1950,6 +2065,7 @@ struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
        struct iwl_fw_error_dump_txcmd *txcmd;
        struct iwl_trans_dump_data *dump_data;
        u32 len;
+       u32 monitor_len;
        int i, ptr;
 
        /* transport dump header */
@@ -1972,10 +2088,34 @@ struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
                        num_bytes_in_chunk;
        }
 
+       /* FH registers */
+       len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
+
        /* FW monitor */
-       if (trans_pcie->fw_mon_page)
+       if (trans_pcie->fw_mon_page) {
                len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
-                       trans_pcie->fw_mon_size;
+                      trans_pcie->fw_mon_size;
+               monitor_len = trans_pcie->fw_mon_size;
+       } else if (trans->dbg_dest_tlv) {
+               u32 base, end;
+
+               base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+               end = le32_to_cpu(trans->dbg_dest_tlv->end_reg);
+
+               base = iwl_read_prph(trans, base) <<
+                      trans->dbg_dest_tlv->base_shift;
+               end = iwl_read_prph(trans, end) <<
+                     trans->dbg_dest_tlv->end_shift;
+
+               /* Make "end" point to the actual end */
+               if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+                       end += (1 << trans->dbg_dest_tlv->end_shift);
+               monitor_len = end - base;
+               len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
+                      monitor_len;
+       } else {
+               monitor_len = 0;
+       }
 
        dump_data = vzalloc(len);
        if (!dump_data)
@@ -2012,36 +2152,71 @@ struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
 
        len += iwl_trans_pcie_dump_prph(trans, &data);
        len += iwl_trans_pcie_dump_csr(trans, &data);
+       len += iwl_trans_pcie_fh_regs_dump(trans, &data);
        /* data is already pointing to the next section */
 
-       if (trans_pcie->fw_mon_page) {
+       if ((trans_pcie->fw_mon_page &&
+            trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||
+           trans->dbg_dest_tlv) {
                struct iwl_fw_error_dump_fw_mon *fw_mon_data;
+               u32 base, write_ptr, wrap_cnt;
+
+               /* If there was a dest TLV - use the values from there */
+               if (trans->dbg_dest_tlv) {
+                       write_ptr =
+                               le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
+                       wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
+                       base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+               } else {
+                       base = MON_BUFF_BASE_ADDR;
+                       write_ptr = MON_BUFF_WRPTR;
+                       wrap_cnt = MON_BUFF_CYCLE_CNT;
+               }
 
                data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
-               data->len = cpu_to_le32(trans_pcie->fw_mon_size +
-                                       sizeof(*fw_mon_data));
                fw_mon_data = (void *)data->data;
                fw_mon_data->fw_mon_wr_ptr =
-                       cpu_to_le32(iwl_read_prph(trans, MON_BUFF_WRPTR));
+                       cpu_to_le32(iwl_read_prph(trans, write_ptr));
                fw_mon_data->fw_mon_cycle_cnt =
-                       cpu_to_le32(iwl_read_prph(trans, MON_BUFF_CYCLE_CNT));
+                       cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
                fw_mon_data->fw_mon_base_ptr =
-                       cpu_to_le32(iwl_read_prph(trans, MON_BUFF_BASE_ADDR));
-
-               /*
-                * The firmware is now asserted, it won't write anything to
-                * the buffer. CPU can take ownership to fetch the data.
-                * The buffer will be handed back to the device before the
-                * firmware will be restarted.
-                */
-               dma_sync_single_for_cpu(trans->dev, trans_pcie->fw_mon_phys,
-                                       trans_pcie->fw_mon_size,
-                                       DMA_FROM_DEVICE);
-               memcpy(fw_mon_data->data, page_address(trans_pcie->fw_mon_page),
-                      trans_pcie->fw_mon_size);
-
-               len += sizeof(*data) + sizeof(*fw_mon_data) +
-                       trans_pcie->fw_mon_size;
+                       cpu_to_le32(iwl_read_prph(trans, base));
+
+               len += sizeof(*data) + sizeof(*fw_mon_data);
+               if (trans_pcie->fw_mon_page) {
+                       data->len = cpu_to_le32(trans_pcie->fw_mon_size +
+                                               sizeof(*fw_mon_data));
+
+                       /*
+                        * The firmware is now asserted, it won't write anything
+                        * to the buffer. CPU can take ownership to fetch the
+                        * data. The buffer will be handed back to the device
+                        * before the firmware will be restarted.
+                        */
+                       dma_sync_single_for_cpu(trans->dev,
+                                               trans_pcie->fw_mon_phys,
+                                               trans_pcie->fw_mon_size,
+                                               DMA_FROM_DEVICE);
+                       memcpy(fw_mon_data->data,
+                              page_address(trans_pcie->fw_mon_page),
+                              trans_pcie->fw_mon_size);
+
+                       len += trans_pcie->fw_mon_size;
+               } else {
+                       /* If we are here then the buffer is internal */
+
+                       /*
+                        * Update pointers to reflect actual values after
+                        * shifting
+                        */
+                       base = iwl_read_prph(trans, base) <<
+                              trans->dbg_dest_tlv->base_shift;
+                       iwl_trans_read_mem(trans, base, fw_mon_data->data,
+                                          monitor_len / sizeof(u32));
+                       data->len = cpu_to_le32(sizeof(*fw_mon_data) +
+                                               monitor_len);
+                       len += monitor_len;
+               }
        }
 
        dump_data->len = len;
index eb8e298..8a6c7a0 100644 (file)
@@ -989,6 +989,65 @@ out:
        spin_unlock_bh(&txq->lock);
 }
 
+static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int ret;
+
+       lockdep_assert_held(&trans_pcie->reg_lock);
+
+       if (trans_pcie->cmd_in_flight)
+               return 0;
+
+       trans_pcie->cmd_in_flight = true;
+
+       /*
+        * wake up the NIC to make sure that the firmware will see the host
+        * command - we will let the NIC sleep once all the host commands
+        * returned. This needs to be done only on NICs that have
+        * apmg_wake_up_wa set.
+        */
+       if (trans->cfg->base_params->apmg_wake_up_wa) {
+               __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+                                        CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+               if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+                       udelay(2);
+
+               ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+                                  CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
+                                  (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+                                   CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
+                                  15000);
+               if (ret < 0) {
+                       __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+                                       CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                       trans_pcie->cmd_in_flight = false;
+                       IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
+                       return -EIO;
+               }
+       }
+
+       return 0;
+}
+
+static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       lockdep_assert_held(&trans_pcie->reg_lock);
+
+       if (WARN_ON(!trans_pcie->cmd_in_flight))
+               return 0;
+
+       trans_pcie->cmd_in_flight = false;
+
+       if (trans->cfg->base_params->apmg_wake_up_wa)
+               __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+                                       CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+       return 0;
+}
+
 /*
  * iwl_pcie_cmdq_reclaim - Reclaim TX command queue entries already Tx'd
  *
@@ -1024,14 +1083,9 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
                }
        }
 
-       if (trans->cfg->base_params->apmg_wake_up_wa &&
-           q->read_ptr == q->write_ptr) {
+       if (q->read_ptr == q->write_ptr) {
                spin_lock_irqsave(&trans_pcie->reg_lock, flags);
-               WARN_ON(!trans_pcie->cmd_in_flight);
-               trans_pcie->cmd_in_flight = false;
-               __iwl_trans_pcie_clear_bit(trans,
-                                          CSR_GP_CNTRL,
-                                          CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+               iwl_pcie_clear_cmd_in_flight(trans);
                spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
        }
 
@@ -1419,32 +1473,11 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
                mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
 
        spin_lock_irqsave(&trans_pcie->reg_lock, flags);
-
-       /*
-        * wake up the NIC to make sure that the firmware will see the host
-        * command - we will let the NIC sleep once all the host commands
-        * returned. This needs to be done only on NICs that have
-        * apmg_wake_up_wa set.
-        */
-       if (trans->cfg->base_params->apmg_wake_up_wa &&
-           !trans_pcie->cmd_in_flight) {
-               trans_pcie->cmd_in_flight = true;
-               __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
-                                        CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-               ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                                  CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
-                                  (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
-                                   CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
-                                  15000);
-               if (ret < 0) {
-                       __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
-                                  CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-                       spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
-                       trans_pcie->cmd_in_flight = false;
-                       IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
-                       idx = -EIO;
-                       goto out;
-               }
+       ret = iwl_pcie_set_cmd_in_flight(trans);
+       if (ret < 0) {
+               idx = ret;
+               spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+               goto out;
        }
 
        /* Increment and update queue's write index */
index 2371d11..a71b9d5 100644 (file)
@@ -2388,7 +2388,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
                sband->vht_cap.cap =
                        IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
                        IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
-                       IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
                        IEEE80211_VHT_CAP_RXLDPC |
                        IEEE80211_VHT_CAP_SHORT_GI_80 |
                        IEEE80211_VHT_CAP_SHORT_GI_160 |
@@ -2543,7 +2542,9 @@ static int mac80211_hwsim_get_radio(struct sk_buff *skb,
        if (cb)
                genl_dump_check_consistent(cb, hdr, &hwsim_genl_family);
 
-       param.reg_alpha2 = data->alpha2;
+       if (data->alpha2[0] && data->alpha2[1])
+               param.reg_alpha2 = data->alpha2;
+
        param.reg_strict = !!(data->hw->wiphy->regulatory_flags &
                                        REGULATORY_STRICT_REG);
        param.p2p_device = !!(data->hw->wiphy->interface_modes &
index 62f5dbe..9d4786e 100644 (file)
@@ -544,6 +544,7 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
        u32 tx_win_size = priv->add_ba_param.tx_win_size;
        static u8 dialog_tok;
        int ret;
+       unsigned long flags;
        u16 block_ack_param_set;
 
        dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid);
@@ -554,15 +555,18 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
            memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
                struct mwifiex_sta_node *sta_ptr;
 
+               spin_lock_irqsave(&priv->sta_list_spinlock, flags);
                sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
                if (!sta_ptr) {
                        dev_warn(priv->adapter->dev,
                                 "BA setup with unknown TDLS peer %pM!\n",
                                peer_mac);
+                       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
                        return -1;
                }
                if (sta_ptr->is_11ac_enabled)
                        tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
+               spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
        }
 
        block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
index 5ef5a0e..d73fda3 100644 (file)
@@ -351,6 +351,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
        new_node->init_win = seq_num;
        new_node->flags = 0;
 
+       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
        if (mwifiex_queuing_ra_based(priv)) {
                dev_dbg(priv->adapter->dev,
                        "info: AP/ADHOC:last_seq=%d start_win=%d\n",
@@ -367,6 +368,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
                else
                        last_seq = priv->rx_seq[tid];
        }
+       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 
        if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
            last_seq >= new_node->start_win) {
@@ -455,22 +457,26 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
        u32 rx_win_size = priv->add_ba_param.rx_win_size;
        u8 tid;
        int win_size;
+       unsigned long flags;
        uint16_t block_ack_param_set;
 
        if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
            ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
            priv->adapter->is_hw_11ac_capable &&
            memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) {
+               spin_lock_irqsave(&priv->sta_list_spinlock, flags);
                sta_ptr = mwifiex_get_sta_entry(priv,
                                                cmd_addba_req->peer_mac_addr);
                if (!sta_ptr) {
                        dev_warn(priv->adapter->dev,
                                 "BA setup with unknown TDLS peer %pM!\n",
                                 cmd_addba_req->peer_mac_addr);
+                       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
                        return -1;
                }
                if (sta_ptr->is_11ac_enabled)
                        rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
+               spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
        }
 
        cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP);
index 17f0ee0..4a66a65 100644 (file)
@@ -194,10 +194,17 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        tx_info->pkt_len = pkt_len;
 
        mwifiex_form_mgmt_frame(skb, buf, len);
-       mwifiex_queue_tx_pkt(priv, skb);
-
        *cookie = prandom_u32() | 1;
-       cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, GFP_ATOMIC);
+
+       if (ieee80211_is_action(mgmt->frame_control))
+               skb = mwifiex_clone_skb_for_tx_status(priv,
+                                                     skb,
+                               MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie);
+       else
+               cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
+                                       GFP_ATOMIC);
+
+       mwifiex_queue_tx_pkt(priv, skb);
 
        wiphy_dbg(wiphy, "info: management frame transmitted\n");
        return 0;
@@ -1289,33 +1296,30 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        struct mwifiex_sta_node *sta_node;
+       u8 deauth_mac[ETH_ALEN];
        unsigned long flags;
 
        if (list_empty(&priv->sta_list) || !priv->bss_started)
                return 0;
 
-       if (!params->mac || is_broadcast_ether_addr(params->mac)) {
-               wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
-               list_for_each_entry(sta_node, &priv->sta_list, list) {
-                       if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
-                                            HostCmd_ACT_GEN_SET, 0,
-                                            sta_node->mac_addr, true))
-                               return -1;
-                       mwifiex_uap_del_sta_data(priv, sta_node);
-               }
-       } else {
-               wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__,
-                         params->mac);
-               spin_lock_irqsave(&priv->sta_list_spinlock, flags);
-               sta_node = mwifiex_get_sta_entry(priv, params->mac);
-               spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
-               if (sta_node) {
-                       if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
-                                            HostCmd_ACT_GEN_SET, 0,
-                                            sta_node->mac_addr, true))
-                               return -1;
-                       mwifiex_uap_del_sta_data(priv, sta_node);
-               }
+       if (!params->mac || is_broadcast_ether_addr(params->mac))
+               return 0;
+
+       wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, params->mac);
+
+       memset(deauth_mac, 0, ETH_ALEN);
+
+       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+       sta_node = mwifiex_get_sta_entry(priv, params->mac);
+       if (sta_node)
+               ether_addr_copy(deauth_mac, params->mac);
+       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+       if (is_valid_ether_addr(deauth_mac)) {
+               if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
+                                    HostCmd_ACT_GEN_SET, 0,
+                                    deauth_mac, true))
+                       return -1;
        }
 
        return 0;
@@ -2988,6 +2992,9 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                           NL80211_FEATURE_INACTIVITY_TIMER |
                           NL80211_FEATURE_NEED_OBSS_SCAN;
 
+       if (adapter->fw_api_ver == MWIFIEX_FW_V15)
+               wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
+
        /* Reserve space for mwifiex specific private data for BSS */
        wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv);
 
index fc0b1ed..2269acf 100644 (file)
@@ -76,6 +76,8 @@
 #define MWIFIEX_BUF_FLAG_REQUEUED_PKT      BIT(0)
 #define MWIFIEX_BUF_FLAG_BRIDGED_PKT      BIT(1)
 #define MWIFIEX_BUF_FLAG_TDLS_PKT         BIT(2)
+#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS   BIT(3)
+#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS  BIT(4)
 
 #define MWIFIEX_BRIDGED_PKTS_THR_HIGH      1024
 #define MWIFIEX_BRIDGED_PKTS_THR_LOW        128
@@ -159,6 +161,8 @@ struct mwifiex_txinfo {
        u8 bss_num;
        u8 bss_type;
        u32 pkt_len;
+       u8 ack_frame_id;
+       u64 cookie;
 };
 
 enum mwifiex_wmm_ac_e {
index e095f37..fb5936e 100644 (file)
@@ -494,6 +494,7 @@ enum P2P_MODES {
 #define EVENT_TDLS_GENERIC_EVENT        0x00000052
 #define EVENT_EXT_SCAN_REPORT           0x00000058
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
+#define EVENT_TX_STATUS_REPORT         0x00000074
 
 #define EVENT_ID_MASK                   0xffff
 #define BSS_NUM_MASK                    0xf
@@ -542,6 +543,7 @@ struct mwifiex_ie_types_data {
 #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
 #define MWIFIEX_TXPD_FLAGS_TDLS_PACKET      0x10
 #define MWIFIEX_RXPD_FLAGS_TDLS_PACKET      0x01
+#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS    0x20
 
 struct txpd {
        u8 bss_type;
@@ -553,7 +555,9 @@ struct txpd {
        u8 priority;
        u8 flags;
        u8 pkt_delay_2ms;
-       u8 reserved1;
+       u8 reserved1[2];
+       u8 tx_token_id;
+       u8 reserved[2];
 } __packed;
 
 struct rxpd {
@@ -598,8 +602,9 @@ struct uap_txpd {
        u8 priority;
        u8 flags;
        u8 pkt_delay_2ms;
-       u8 reserved1;
-       __le32 reserved2;
+       u8 reserved1[2];
+       u8 tx_token_id;
+       u8 reserved[2];
 };
 
 struct uap_rxpd {
@@ -1224,6 +1229,12 @@ struct mwifiex_event_scan_result {
        u8 num_of_set;
 } __packed;
 
+struct tx_status_event {
+       u8 packet_type;
+       u8 tx_token_id;
+       u8 status;
+} __packed;
+
 #define MWIFIEX_USER_SCAN_CHAN_MAX             50
 
 #define MWIFIEX_MAX_SSID_LIST_LENGTH         10
index cc15ab8..520ad4a 100644 (file)
@@ -473,6 +473,9 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
 
                spin_lock_init(&priv->tx_ba_stream_tbl_lock);
                spin_lock_init(&priv->rx_reorder_tbl_lock);
+
+               spin_lock_init(&priv->ack_status_lock);
+               idr_init(&priv->ack_status_frames);
        }
 
        return 0;
index 0e50120..d4d2223 100644 (file)
@@ -149,7 +149,8 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
        /* Check for Rx data */
        while ((skb = skb_dequeue(&adapter->rx_data_q))) {
                atomic_dec(&adapter->rx_pending);
-               if (adapter->delay_main_work &&
+               if ((adapter->delay_main_work ||
+                    adapter->iface_type == MWIFIEX_USB) &&
                    (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
                        if (adapter->if_ops.submit_rem_rx_urbs)
                                adapter->if_ops.submit_rem_rx_urbs(adapter);
@@ -202,12 +203,15 @@ process_start:
                    (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
                        break;
 
-               /* If we process interrupts first, it would increase RX pending
-                * even further. Avoid this by checking if rx_pending has
-                * crossed high threshold and schedule rx work queue
-                * and then process interrupts
+               /* For non-USB interfaces, If we process interrupts first, it
+                * would increase RX pending even further. Avoid this by
+                * checking if rx_pending has crossed high threshold and
+                * schedule rx work queue and then process interrupts.
+                * For USB interface, there are no interrupts. We already have
+                * HIGH_RX_PENDING check in usb.c
                 */
-               if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING) {
+               if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
+                   adapter->iface_type != MWIFIEX_USB) {
                        adapter->delay_main_work = true;
                        if (!adapter->rx_processing)
                                queue_work(adapter->rx_workqueue,
@@ -604,6 +608,48 @@ int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
        return 0;
 }
 
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+                               struct sk_buff *skb, u8 flag, u64 *cookie)
+{
+       struct sk_buff *orig_skb = skb;
+       struct mwifiex_txinfo *tx_info, *orig_tx_info;
+
+       skb = skb_clone(skb, GFP_ATOMIC);
+       if (skb) {
+               unsigned long flags;
+               int id;
+
+               spin_lock_irqsave(&priv->ack_status_lock, flags);
+               id = idr_alloc(&priv->ack_status_frames, orig_skb,
+                              1, 0xff, GFP_ATOMIC);
+               spin_unlock_irqrestore(&priv->ack_status_lock, flags);
+
+               if (id >= 0) {
+                       tx_info = MWIFIEX_SKB_TXCB(skb);
+                       tx_info->ack_frame_id = id;
+                       tx_info->flags |= flag;
+                       orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
+                       orig_tx_info->ack_frame_id = id;
+                       orig_tx_info->flags |= flag;
+
+                       if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
+                               orig_tx_info->cookie = *cookie;
+
+               } else if (skb_shared(skb)) {
+                       kfree_skb(orig_skb);
+               } else {
+                       kfree_skb(skb);
+                       skb = orig_skb;
+               }
+       } else {
+               /* couldn't clone -- lose tx status ... */
+               skb = orig_skb;
+       }
+
+       return skb;
+}
+
 /*
  * CFG802.11 network device handler for data transmission.
  */
@@ -613,6 +659,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        struct sk_buff *new_skb;
        struct mwifiex_txinfo *tx_info;
+       bool multicast;
 
        dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
                jiffies, priv->bss_type, priv->bss_num);
@@ -653,6 +700,15 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        tx_info->bss_type = priv->bss_type;
        tx_info->pkt_len = skb->len;
 
+       multicast = is_multicast_ether_addr(skb->data);
+
+       if (unlikely(!multicast && skb->sk &&
+                    skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
+                    priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
+               skb = mwifiex_clone_skb_for_tx_status(priv,
+                                                     skb,
+                                       MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
+
        /* Record the current time the packet was queued; used to
         * determine the amount of time the packet was queued in
         * the driver before it was sent to the firmware.
index 5a690d5..e66993c 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/firmware.h>
 #include <linux/ctype.h>
 #include <linux/of.h>
+#include <linux/idr.h>
 
 #include "decl.h"
 #include "ioctl.h"
@@ -578,6 +579,9 @@ struct mwifiex_private {
        u8 check_tdls_tx;
        struct timer_list auto_tdls_timer;
        bool auto_tdls_timer_active;
+       struct idr ack_status_frames;
+       /* spin lock for ack status */
+       spinlock_t ack_status_lock;
 };
 
 enum mwifiex_ba_status {
@@ -971,6 +975,8 @@ int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
 int mwifiex_process_sta_event(struct mwifiex_private *);
 int mwifiex_process_uap_event(struct mwifiex_private *);
 void mwifiex_delete_all_station_list(struct mwifiex_private *priv);
+void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv,
+                                 const u8 *ra_addr);
 void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
 void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb);
 int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta);
@@ -1335,6 +1341,13 @@ void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
 void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
 void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
 
+void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
+                                  void *event_body);
+
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+                               struct sk_buff *skb, u8 flag, u64 *cookie);
+
 #ifdef CONFIG_DEBUG_FS
 void mwifiex_debugfs_init(void);
 void mwifiex_debugfs_remove(void);
index 3a17821..984a7a4 100644 (file)
@@ -1623,7 +1623,7 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
 
        if (*bytes_left >= sizeof(beacon_size)) {
                /* Extract & convert beacon size from command buffer */
-               memcpy(&beacon_size, *bss_info, sizeof(beacon_size));
+               beacon_size = le16_to_cpu(*(__le16 *)(*bss_info));
                *bytes_left -= sizeof(beacon_size);
                *bss_info += sizeof(beacon_size);
        }
index 204ecc8..b8c171d 100644 (file)
@@ -504,6 +504,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
                ret = mwifiex_parse_tdls_event(priv, adapter->event_skb);
                break;
 
+       case EVENT_TX_STATUS_REPORT:
+               dev_dbg(adapter->dev, "event: TX_STATUS Report\n");
+               mwifiex_parse_tx_status_event(priv, adapter->event_body);
+               break;
+
        default:
                dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
                        eventcause);
index dab7b33..b896d73 100644 (file)
@@ -77,6 +77,12 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv,
        local_tx_pd->pkt_delay_2ms =
                                mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
 
+       if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
+           tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
+               local_tx_pd->tx_token_id = tx_info->ack_frame_id;
+               local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
+       }
+
        if (local_tx_pd->priority <
            ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
                /*
index a5983fc..6ae1333 100644 (file)
@@ -203,3 +203,34 @@ done:
 }
 EXPORT_SYMBOL_GPL(mwifiex_write_data_complete);
 
+void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
+                                  void *event_body)
+{
+       struct tx_status_event *tx_status = (void *)priv->adapter->event_body;
+       struct sk_buff *ack_skb;
+       unsigned long flags;
+       struct mwifiex_txinfo *tx_info;
+
+       if (!tx_status->tx_token_id)
+               return;
+
+       spin_lock_irqsave(&priv->ack_status_lock, flags);
+       ack_skb = idr_find(&priv->ack_status_frames, tx_status->tx_token_id);
+       if (ack_skb)
+               idr_remove(&priv->ack_status_frames, tx_status->tx_token_id);
+       spin_unlock_irqrestore(&priv->ack_status_lock, flags);
+
+       if (ack_skb) {
+               tx_info = MWIFIEX_SKB_TXCB(ack_skb);
+
+               if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS) {
+                       /* consumes ack_skb */
+                       skb_complete_wifi_ack(ack_skb, !tx_status->status);
+               } else {
+                       cfg80211_mgmt_tx_status(priv->wdev, tx_info->cookie,
+                                               ack_skb->data, ack_skb->len,
+                                               !tx_status->status, GFP_ATOMIC);
+                       dev_kfree_skb_any(ack_skb);
+               }
+       }
+}
index 7c2b976..c54a537 100644 (file)
@@ -110,6 +110,7 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
                        mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac);
                        mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac);
                }
+               mwifiex_wmm_del_peer_ra_list(priv, deauth_mac);
                mwifiex_del_sta_entry(priv, deauth_mac);
                break;
        case EVENT_UAP_BSS_IDLE:
@@ -172,6 +173,10 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
                        return mwifiex_handle_event_ext_scan_report(priv,
                                                adapter->event_skb->data);
                break;
+       case EVENT_TX_STATUS_REPORT:
+               dev_dbg(adapter->dev, "event: TX_STATUS Report\n");
+               mwifiex_parse_tx_status_event(priv, adapter->event_body);
+               break;
        default:
                dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
                        eventcause);
index ec7309d..be3a203 100644 (file)
@@ -266,6 +266,7 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
        struct rx_packet_hdr *rx_pkt_hdr;
        u16 rx_pkt_type;
        u8 ta[ETH_ALEN], pkt_type;
+       unsigned long flags;
        struct mwifiex_sta_node *node;
 
        uap_rx_pd = (struct uap_rxpd *)(skb->data);
@@ -294,10 +295,12 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
        memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
 
        if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) {
+               spin_lock_irqsave(&priv->sta_list_spinlock, flags);
                node = mwifiex_get_sta_entry(priv, ta);
                if (node)
                        node->rx_seq[uap_rx_pd->priority] =
                                                le16_to_cpu(uap_rx_pd->seq_num);
+               spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
        }
 
        if (!priv->ap_11n_enabled ||
@@ -370,10 +373,16 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv,
        txpd->bss_num = priv->bss_num;
        txpd->bss_type = priv->bss_type;
        txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len));
-
        txpd->priority = (u8)skb->priority;
+
        txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
 
+       if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
+           tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
+               txpd->tx_token_id = tx_info->ack_frame_id;
+               txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
+       }
+
        if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
                /*
                 * Set the priority specific tx_control field, setting of 0 will
index a113ef8..b1768fb 100644 (file)
@@ -149,7 +149,7 @@ mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len,
        u8 category, action_code;
        struct ieee80211_hdr *ieee_hdr = (void *)payload;
 
-       stype =  (cpu_to_le16(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE);
+       stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE);
 
        switch (stype) {
        case IEEE80211_STYPE_ACTION:
index 94c98a8..ffffd2c 100644 (file)
@@ -147,9 +147,6 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
        struct mwifiex_sta_node *node;
        unsigned long flags;
 
-       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
-       node = mwifiex_get_sta_entry(priv, ra);
-       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 
        for (i = 0; i < MAX_NUM_TID; ++i) {
                ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra);
@@ -170,10 +167,13 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
                                ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
                        }
                } else {
+                       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+                       node = mwifiex_get_sta_entry(priv, ra);
                        ra_list->is_11n_enabled =
                                      mwifiex_is_sta_11n_enabled(priv, node);
                        if (ra_list->is_11n_enabled)
                                ra_list->max_amsdu = node->max_amsdu;
+                       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
                }
 
                dev_dbg(adapter->dev, "data: ralist %p: is_11n_enabled=%d\n",
@@ -523,6 +523,13 @@ static void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv)
        }
 }
 
+static int mwifiex_free_ack_frame(int id, void *p, void *data)
+{
+       pr_warn("Have pending ack frames!\n");
+       kfree_skb(p);
+       return 0;
+}
+
 /*
  * This function cleans up the Tx and Rx queues.
  *
@@ -558,6 +565,9 @@ mwifiex_clean_txrx(struct mwifiex_private *priv)
 
        skb_queue_walk_safe(&priv->tdls_txq, skb, tmp)
                mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+
+       idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL);
+       idr_destroy(&priv->ack_status_frames);
 }
 
 /*
@@ -600,6 +610,32 @@ mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid,
        return mwifiex_wmm_get_ralist_node(priv, tid, ra_addr);
 }
 
+/*
+ * This function deletes RA list nodes for given mac for all TIDs.
+ * Function also decrements TX pending count accordingly.
+ */
+void
+mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr)
+{
+       struct mwifiex_ra_list_tbl *ra_list;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+       for (i = 0; i < MAX_NUM_TID; ++i) {
+               ra_list = mwifiex_wmm_get_ralist_node(priv, i, ra_addr);
+
+               if (!ra_list)
+                       continue;
+               mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list);
+               atomic_sub(ra_list->total_pkt_count, &priv->wmm.tx_pkts_queued);
+               list_del(&ra_list->list);
+               kfree(ra_list);
+       }
+       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
 /*
  * This function checks if a particular RA list node exists in a given TID
  * table index.
diff --git a/drivers/net/wireless/p54/net2280.h b/drivers/net/wireless/p54/net2280.h
deleted file mode 100644 (file)
index aedfaf2..0000000
+++ /dev/null
@@ -1,451 +0,0 @@
-#ifndef NET2280_H
-#define NET2280_H
-/*
- * NetChip 2280 high/full speed USB device controller.
- * Unlike many such controllers, this one talks PCI.
- */
-
-/*
- * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
- * Copyright (C) 2003 David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*-------------------------------------------------------------------------*/
-
-/* NET2280 MEMORY MAPPED REGISTERS
- *
- * The register layout came from the chip documentation, and the bit
- * number definitions were extracted from chip specification.
- *
- * Use the shift operator ('<<') to build bit masks, with readl/writel
- * to access the registers through PCI.
- */
-
-/* main registers, BAR0 + 0x0000 */
-struct net2280_regs {
-       /* offset 0x0000 */
-       __le32                  devinit;
-#define LOCAL_CLOCK_FREQUENCY                                  8
-#define FORCE_PCI_RESET                                                7
-#define PCI_ID                                                 6
-#define PCI_ENABLE                                             5
-#define FIFO_SOFT_RESET                                                4
-#define CFG_SOFT_RESET                                         3
-#define PCI_SOFT_RESET                                         2
-#define USB_SOFT_RESET                                         1
-#define M8051_RESET                                            0
-       __le32                  eectl;
-#define EEPROM_ADDRESS_WIDTH                                   23
-#define EEPROM_CHIP_SELECT_ACTIVE                              22
-#define EEPROM_PRESENT                                         21
-#define EEPROM_VALID                                           20
-#define EEPROM_BUSY                                            19
-#define EEPROM_CHIP_SELECT_ENABLE                              18
-#define EEPROM_BYTE_READ_START                                 17
-#define EEPROM_BYTE_WRITE_START                                        16
-#define EEPROM_READ_DATA                                       8
-#define EEPROM_WRITE_DATA                                      0
-       __le32                  eeclkfreq;
-       u32                     _unused0;
-       /* offset 0x0010 */
-
-       __le32                  pciirqenb0;     /* interrupt PCI master ... */
-#define SETUP_PACKET_INTERRUPT_ENABLE                          7
-#define ENDPOINT_F_INTERRUPT_ENABLE                            6
-#define ENDPOINT_E_INTERRUPT_ENABLE                            5
-#define ENDPOINT_D_INTERRUPT_ENABLE                            4
-#define ENDPOINT_C_INTERRUPT_ENABLE                            3
-#define ENDPOINT_B_INTERRUPT_ENABLE                            2
-#define ENDPOINT_A_INTERRUPT_ENABLE                            1
-#define ENDPOINT_0_INTERRUPT_ENABLE                            0
-       __le32                  pciirqenb1;
-#define PCI_INTERRUPT_ENABLE                                   31
-#define POWER_STATE_CHANGE_INTERRUPT_ENABLE                    27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                   26
-#define PCI_PARITY_ERROR_INTERRUPT_ENABLE                      25
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE             20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE             19
-#define PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE             18
-#define PCI_RETRY_ABORT_INTERRUPT_ENABLE                       17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE                 16
-#define GPIO_INTERRUPT_ENABLE                                  13
-#define DMA_D_INTERRUPT_ENABLE                                 12
-#define DMA_C_INTERRUPT_ENABLE                                 11
-#define DMA_B_INTERRUPT_ENABLE                                 10
-#define DMA_A_INTERRUPT_ENABLE                                 9
-#define EEPROM_DONE_INTERRUPT_ENABLE                           8
-#define VBUS_INTERRUPT_ENABLE                                  7
-#define CONTROL_STATUS_INTERRUPT_ENABLE                                6
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE                       4
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE                       3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE                        2
-#define RESUME_INTERRUPT_ENABLE                                        1
-#define SOF_INTERRUPT_ENABLE                                   0
-       __le32                  cpu_irqenb0;    /* ... or onboard 8051 */
-#define SETUP_PACKET_INTERRUPT_ENABLE                          7
-#define ENDPOINT_F_INTERRUPT_ENABLE                            6
-#define ENDPOINT_E_INTERRUPT_ENABLE                            5
-#define ENDPOINT_D_INTERRUPT_ENABLE                            4
-#define ENDPOINT_C_INTERRUPT_ENABLE                            3
-#define ENDPOINT_B_INTERRUPT_ENABLE                            2
-#define ENDPOINT_A_INTERRUPT_ENABLE                            1
-#define ENDPOINT_0_INTERRUPT_ENABLE                            0
-       __le32                  cpu_irqenb1;
-#define CPU_INTERRUPT_ENABLE                                   31
-#define POWER_STATE_CHANGE_INTERRUPT_ENABLE                    27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                   26
-#define PCI_PARITY_ERROR_INTERRUPT_ENABLE                      25
-#define PCI_INTA_INTERRUPT_ENABLE                              24
-#define PCI_PME_INTERRUPT_ENABLE                               23
-#define PCI_SERR_INTERRUPT_ENABLE                              22
-#define PCI_PERR_INTERRUPT_ENABLE                              21
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE             20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE             19
-#define PCI_RETRY_ABORT_INTERRUPT_ENABLE                       17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE                 16
-#define GPIO_INTERRUPT_ENABLE                                  13
-#define DMA_D_INTERRUPT_ENABLE                                 12
-#define DMA_C_INTERRUPT_ENABLE                                 11
-#define DMA_B_INTERRUPT_ENABLE                                 10
-#define DMA_A_INTERRUPT_ENABLE                                 9
-#define EEPROM_DONE_INTERRUPT_ENABLE                           8
-#define VBUS_INTERRUPT_ENABLE                                  7
-#define CONTROL_STATUS_INTERRUPT_ENABLE                                6
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE                       4
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE                       3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE                        2
-#define RESUME_INTERRUPT_ENABLE                                        1
-#define SOF_INTERRUPT_ENABLE                                   0
-
-       /* offset 0x0020 */
-       u32                     _unused1;
-       __le32                  usbirqenb1;
-#define USB_INTERRUPT_ENABLE                                   31
-#define POWER_STATE_CHANGE_INTERRUPT_ENABLE                    27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                   26
-#define PCI_PARITY_ERROR_INTERRUPT_ENABLE                      25
-#define PCI_INTA_INTERRUPT_ENABLE                              24
-#define PCI_PME_INTERRUPT_ENABLE                               23
-#define PCI_SERR_INTERRUPT_ENABLE                              22
-#define PCI_PERR_INTERRUPT_ENABLE                              21
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE             20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE             19
-#define PCI_RETRY_ABORT_INTERRUPT_ENABLE                       17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE                 16
-#define GPIO_INTERRUPT_ENABLE                                  13
-#define DMA_D_INTERRUPT_ENABLE                                 12
-#define DMA_C_INTERRUPT_ENABLE                                 11
-#define DMA_B_INTERRUPT_ENABLE                                 10
-#define DMA_A_INTERRUPT_ENABLE                                 9
-#define EEPROM_DONE_INTERRUPT_ENABLE                           8
-#define VBUS_INTERRUPT_ENABLE                                  7
-#define CONTROL_STATUS_INTERRUPT_ENABLE                                6
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE                       4
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE                       3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE                        2
-#define RESUME_INTERRUPT_ENABLE                                        1
-#define SOF_INTERRUPT_ENABLE                                   0
-       __le32                  irqstat0;
-#define INTA_ASSERTED                                          12
-#define SETUP_PACKET_INTERRUPT                                 7
-#define ENDPOINT_F_INTERRUPT                                   6
-#define ENDPOINT_E_INTERRUPT                                   5
-#define ENDPOINT_D_INTERRUPT                                   4
-#define ENDPOINT_C_INTERRUPT                                   3
-#define ENDPOINT_B_INTERRUPT                                   2
-#define ENDPOINT_A_INTERRUPT                                   1
-#define ENDPOINT_0_INTERRUPT                                   0
-       __le32                  irqstat1;
-#define POWER_STATE_CHANGE_INTERRUPT                           27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT                          26
-#define PCI_PARITY_ERROR_INTERRUPT                             25
-#define PCI_INTA_INTERRUPT                                     24
-#define PCI_PME_INTERRUPT                                      23
-#define PCI_SERR_INTERRUPT                                     22
-#define PCI_PERR_INTERRUPT                                     21
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT                    20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT                    19
-#define PCI_RETRY_ABORT_INTERRUPT                              17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT                                16
-#define GPIO_INTERRUPT                                         13
-#define DMA_D_INTERRUPT                                                12
-#define DMA_C_INTERRUPT                                                11
-#define DMA_B_INTERRUPT                                                10
-#define DMA_A_INTERRUPT                                                9
-#define EEPROM_DONE_INTERRUPT                                  8
-#define VBUS_INTERRUPT                                         7
-#define CONTROL_STATUS_INTERRUPT                               6
-#define ROOT_PORT_RESET_INTERRUPT                              4
-#define SUSPEND_REQUEST_INTERRUPT                              3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT                       2
-#define RESUME_INTERRUPT                                       1
-#define SOF_INTERRUPT                                          0
-       /* offset 0x0030 */
-       __le32                  idxaddr;
-       __le32                  idxdata;
-       __le32                  fifoctl;
-#define PCI_BASE2_RANGE                                                16
-#define IGNORE_FIFO_AVAILABILITY                               3
-#define PCI_BASE2_SELECT                                       2
-#define FIFO_CONFIGURATION_SELECT                              0
-       u32                     _unused2;
-       /* offset 0x0040 */
-       __le32                  memaddr;
-#define START                                                  28
-#define DIRECTION                                              27
-#define FIFO_DIAGNOSTIC_SELECT                                 24
-#define MEMORY_ADDRESS                                         0
-       __le32                  memdata0;
-       __le32                  memdata1;
-       u32                     _unused3;
-       /* offset 0x0050 */
-       __le32                  gpioctl;
-#define GPIO3_LED_SELECT                                       12
-#define GPIO3_INTERRUPT_ENABLE                                 11
-#define GPIO2_INTERRUPT_ENABLE                                 10
-#define GPIO1_INTERRUPT_ENABLE                                 9
-#define GPIO0_INTERRUPT_ENABLE                                 8
-#define GPIO3_OUTPUT_ENABLE                                    7
-#define GPIO2_OUTPUT_ENABLE                                    6
-#define GPIO1_OUTPUT_ENABLE                                    5
-#define GPIO0_OUTPUT_ENABLE                                    4
-#define GPIO3_DATA                                             3
-#define GPIO2_DATA                                             2
-#define GPIO1_DATA                                             1
-#define GPIO0_DATA                                             0
-       __le32                  gpiostat;
-#define GPIO3_INTERRUPT                                                3
-#define GPIO2_INTERRUPT                                                2
-#define GPIO1_INTERRUPT                                                1
-#define GPIO0_INTERRUPT                                                0
-} __packed;
-
-/* usb control, BAR0 + 0x0080 */
-struct net2280_usb_regs {
-       /* offset 0x0080 */
-       __le32                  stdrsp;
-#define STALL_UNSUPPORTED_REQUESTS                             31
-#define SET_TEST_MODE                                          16
-#define GET_OTHER_SPEED_CONFIGURATION                          15
-#define GET_DEVICE_QUALIFIER                                   14
-#define SET_ADDRESS                                            13
-#define ENDPOINT_SET_CLEAR_HALT                                        12
-#define DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP                  11
-#define GET_STRING_DESCRIPTOR_2                                        10
-#define GET_STRING_DESCRIPTOR_1                                        9
-#define GET_STRING_DESCRIPTOR_0                                        8
-#define GET_SET_INTERFACE                                      6
-#define GET_SET_CONFIGURATION                                  5
-#define GET_CONFIGURATION_DESCRIPTOR                           4
-#define GET_DEVICE_DESCRIPTOR                                  3
-#define GET_ENDPOINT_STATUS                                    2
-#define GET_INTERFACE_STATUS                                   1
-#define GET_DEVICE_STATUS                                      0
-       __le32                  prodvendid;
-#define     PRODUCT_ID                                         16
-#define     VENDOR_ID                                          0
-       __le32                  relnum;
-       __le32                  usbctl;
-#define SERIAL_NUMBER_INDEX                                    16
-#define PRODUCT_ID_STRING_ENABLE                               13
-#define VENDOR_ID_STRING_ENABLE                                        12
-#define USB_ROOT_PORT_WAKEUP_ENABLE                            11
-#define VBUS_PIN                                               10
-#define TIMED_DISCONNECT                                       9
-#define SUSPEND_IMMEDIATELY                                    7
-#define SELF_POWERED_USB_DEVICE                                        6
-#define REMOTE_WAKEUP_SUPPORT                                  5
-#define PME_POLARITY                                           4
-#define USB_DETECT_ENABLE                                      3
-#define PME_WAKEUP_ENABLE                                      2
-#define DEVICE_REMOTE_WAKEUP_ENABLE                            1
-#define SELF_POWERED_STATUS                                    0
-       /* offset 0x0090 */
-       __le32                  usbstat;
-#define HIGH_SPEED                                             7
-#define FULL_SPEED                                             6
-#define GENERATE_RESUME                                                5
-#define GENERATE_DEVICE_REMOTE_WAKEUP                          4
-       __le32                  xcvrdiag;
-#define FORCE_HIGH_SPEED_MODE                                  31
-#define FORCE_FULL_SPEED_MODE                                  30
-#define USB_TEST_MODE                                          24
-#define LINE_STATE                                             16
-#define TRANSCEIVER_OPERATION_MODE                             2
-#define TRANSCEIVER_SELECT                                     1
-#define TERMINATION_SELECT                                     0
-       __le32                  setup0123;
-       __le32                  setup4567;
-       /* offset 0x0090 */
-       u32                     _unused0;
-       __le32                  ouraddr;
-#define FORCE_IMMEDIATE                                                7
-#define OUR_USB_ADDRESS                                                0
-       __le32                  ourconfig;
-} __packed;
-
-/* pci control, BAR0 + 0x0100 */
-struct net2280_pci_regs {
-       /* offset 0x0100 */
-       __le32                  pcimstctl;
-#define PCI_ARBITER_PARK_SELECT                                        13
-#define PCI_MULTI LEVEL_ARBITER                                        12
-#define PCI_RETRY_ABORT_ENABLE                                 11
-#define DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE                 10
-#define DMA_READ_MULTIPLE_ENABLE                               9
-#define DMA_READ_LINE_ENABLE                                   8
-#define PCI_MASTER_COMMAND_SELECT                              6
-#define                MEM_READ_OR_WRITE                               0
-#define                IO_READ_OR_WRITE                                1
-#define                CFG_READ_OR_WRITE                               2
-#define PCI_MASTER_START                                       5
-#define PCI_MASTER_READ_WRITE                                  4
-#define                PCI_MASTER_WRITE                                0
-#define                PCI_MASTER_READ                                 1
-#define PCI_MASTER_BYTE_WRITE_ENABLES                          0
-       __le32                  pcimstaddr;
-       __le32                  pcimstdata;
-       __le32                  pcimststat;
-#define PCI_ARBITER_CLEAR                                      2
-#define PCI_EXTERNAL_ARBITER                                   1
-#define PCI_HOST_MODE                                          0
-} __packed;
-
-/* dma control, BAR0 + 0x0180 ... array of four structs like this,
- * for channels 0..3.  see also struct net2280_dma:  descriptor
- * that can be loaded into some of these registers.
- */
-struct net2280_dma_regs {      /* [11.7] */
-       /* offset 0x0180, 0x01a0, 0x01c0, 0x01e0, */
-       __le32                  dmactl;
-#define DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE               25
-#define DMA_CLEAR_COUNT_ENABLE                                 21
-#define DESCRIPTOR_POLLING_RATE                                        19
-#define                POLL_CONTINUOUS                                 0
-#define                POLL_1_USEC                                     1
-#define                POLL_100_USEC                                   2
-#define                POLL_1_MSEC                                     3
-#define DMA_VALID_BIT_POLLING_ENABLE                           18
-#define DMA_VALID_BIT_ENABLE                                   17
-#define DMA_SCATTER_GATHER_ENABLE                              16
-#define DMA_OUT_AUTO_START_ENABLE                              4
-#define DMA_PREEMPT_ENABLE                                     3
-#define DMA_FIFO_VALIDATE                                      2
-#define DMA_ENABLE                                             1
-#define DMA_ADDRESS_HOLD                                       0
-       __le32                  dmastat;
-#define DMA_SCATTER_GATHER_DONE_INTERRUPT                      25
-#define DMA_TRANSACTION_DONE_INTERRUPT                         24
-#define DMA_ABORT                                              1
-#define DMA_START                                              0
-       u32                     _unused0[2];
-       /* offset 0x0190, 0x01b0, 0x01d0, 0x01f0, */
-       __le32                  dmacount;
-#define VALID_BIT                                              31
-#define DMA_DIRECTION                                          30
-#define DMA_DONE_INTERRUPT_ENABLE                              29
-#define END_OF_CHAIN                                           28
-#define DMA_BYTE_COUNT_MASK                                    ((1<<24)-1)
-#define DMA_BYTE_COUNT                                         0
-       __le32                  dmaaddr;
-       __le32                  dmadesc;
-       u32                     _unused1;
-} __packed;
-
-/* dedicated endpoint registers, BAR0 + 0x0200 */
-
-struct net2280_dep_regs {      /* [11.8] */
-       /* offset 0x0200, 0x0210, 0x220, 0x230, 0x240 */
-       __le32                  dep_cfg;
-       /* offset 0x0204, 0x0214, 0x224, 0x234, 0x244 */
-       __le32                  dep_rsp;
-       u32                     _unused[2];
-} __packed;
-
-/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs
- * like this, for ep0 then the configurable endpoints A..F
- * ep0 reserved for control; E and F have only 64 bytes of fifo
- */
-struct net2280_ep_regs {       /* [11.9] */
-       /* offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0 */
-       __le32                  ep_cfg;
-#define ENDPOINT_BYTE_COUNT                                    16
-#define ENDPOINT_ENABLE                                                10
-#define ENDPOINT_TYPE                                          8
-#define ENDPOINT_DIRECTION                                     7
-#define ENDPOINT_NUMBER                                                0
-       __le32                  ep_rsp;
-#define SET_NAK_OUT_PACKETS                                    15
-#define SET_EP_HIDE_STATUS_PHASE                               14
-#define SET_EP_FORCE_CRC_ERROR                                 13
-#define SET_INTERRUPT_MODE                                     12
-#define SET_CONTROL_STATUS_PHASE_HANDSHAKE                     11
-#define SET_NAK_OUT_PACKETS_MODE                               10
-#define SET_ENDPOINT_TOGGLE                                    9
-#define SET_ENDPOINT_HALT                                      8
-#define CLEAR_NAK_OUT_PACKETS                                  7
-#define CLEAR_EP_HIDE_STATUS_PHASE                             6
-#define CLEAR_EP_FORCE_CRC_ERROR                               5
-#define CLEAR_INTERRUPT_MODE                                   4
-#define CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE                   3
-#define CLEAR_NAK_OUT_PACKETS_MODE                             2
-#define CLEAR_ENDPOINT_TOGGLE                                  1
-#define CLEAR_ENDPOINT_HALT                                    0
-       __le32                  ep_irqenb;
-#define SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE                 6
-#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE              5
-#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE                  3
-#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE               2
-#define DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE                   1
-#define DATA_IN_TOKEN_INTERRUPT_ENABLE                         0
-       __le32                  ep_stat;
-#define FIFO_VALID_COUNT                                       24
-#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID                     22
-#define TIMEOUT                                                        21
-#define USB_STALL_SENT                                         20
-#define USB_IN_NAK_SENT                                                19
-#define USB_IN_ACK_RCVD                                                18
-#define USB_OUT_PING_NAK_SENT                                  17
-#define USB_OUT_ACK_SENT                                       16
-#define FIFO_OVERFLOW                                          13
-#define FIFO_UNDERFLOW                                         12
-#define FIFO_FULL                                              11
-#define FIFO_EMPTY                                             10
-#define FIFO_FLUSH                                             9
-#define SHORT_PACKET_OUT_DONE_INTERRUPT                                6
-#define SHORT_PACKET_TRANSFERRED_INTERRUPT                     5
-#define NAK_OUT_PACKETS                                                4
-#define DATA_PACKET_RECEIVED_INTERRUPT                         3
-#define DATA_PACKET_TRANSMITTED_INTERRUPT                      2
-#define DATA_OUT_PING_TOKEN_INTERRUPT                          1
-#define DATA_IN_TOKEN_INTERRUPT                                        0
-       /* offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0 */
-       __le32                  ep_avail;
-       __le32                  ep_data;
-       u32                     _unused0[2];
-} __packed;
-
-struct net2280_reg_write {
-       __le16 port;
-       __le32 addr;
-       __le32 val;
-} __packed;
-
-struct net2280_reg_read {
-       __le16 port;
-       __le32 addr;
-} __packed;
-#endif /* NET2280_H */
index d273be7..a5f5f0f 100644 (file)
@@ -16,7 +16,7 @@
 
 /* for isl3886 register definitions used on ver 1 devices */
 #include "p54pci.h"
-#include "net2280.h"
+#include <linux/usb/net2280.h>
 
 /* pci */
 #define NET2280_BASE           0x10000000
@@ -93,6 +93,17 @@ enum net2280_op_type {
        NET2280_DEV_CFG_U16     = 0x0883
 };
 
+struct net2280_reg_write {
+       __le16 port;
+       __le32 addr;
+       __le32 val;
+} __packed;
+
+struct net2280_reg_read {
+       __le16 port;
+       __le32 addr;
+} __packed;
+
 #define P54U_FW_BLOCK 2048
 
 #define X2_SIGNATURE "x2  "
index c878e3f..05c6459 100644 (file)
@@ -47,7 +47,7 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
  * BBP and RF register require indirect register access,
  * and use the CSR registers BBPCSR and RFCSR to achieve this.
  * These indirect registers work with busy bits,
- * and we will try maximal REGISTER_BUSY_COUNT times to access
+ * and we will try maximal REGISTER_USB_BUSY_COUNT times to access
  * the register while taking a REGISTER_BUSY_DELAY us delay
  * between each attampt. When the busy bit is still set at that time,
  * the access attempt is considered to have failed,
@@ -122,7 +122,7 @@ static int rt2500usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
 {
        unsigned int i;
 
-       for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+       for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
                rt2500usb_register_read_lock(rt2x00dev, offset, reg);
                if (!rt2x00_get_field16(*reg, field))
                        return 1;
@@ -904,7 +904,7 @@ static int rt2500usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
        unsigned int i;
        u8 value;
 
-       for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+       for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
                rt2500usb_bbp_read(rt2x00dev, 0, &value);
                if ((value != 0xff) && (value != 0x00))
                        return 0;
@@ -1023,7 +1023,7 @@ static int rt2500usb_set_state(struct rt2x00_dev *rt2x00dev,
         * We must wait until the register indicates that the
         * device has entered the correct state.
         */
-       for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+       for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
                rt2500usb_register_read(rt2x00dev, MAC_CSR17, &reg2);
                bbp_state = rt2x00_get_field16(reg2, MAC_CSR17_BBP_CURR_STATE);
                rf_state = rt2x00_get_field16(reg2, MAC_CSR17_RF_CURR_STATE);
index 9f57a2d..81ee481 100644 (file)
@@ -4119,7 +4119,20 @@ static void rt2800_config_txpower_rt28xx(struct rt2x00_dev *rt2x00dev,
         * expected. We adjust it, based on TSSI reference and boundaries values
         * provided in EEPROM.
         */
-       delta += rt2800_get_gain_calibration_delta(rt2x00dev);
+       switch (rt2x00dev->chip.rt) {
+       case RT2860:
+       case RT2872:
+       case RT2883:
+       case RT3070:
+       case RT3071:
+       case RT3090:
+       case RT3572:
+               delta += rt2800_get_gain_calibration_delta(rt2x00dev);
+               break;
+       default:
+               /* TODO: temperature compensation code for other chips. */
+               break;
+       }
 
        /*
         * Decrease power according to user settings, on devices with unknown
@@ -4136,25 +4149,19 @@ static void rt2800_config_txpower_rt28xx(struct rt2x00_dev *rt2x00dev,
         * TODO: we do not use +6 dBm option to do not increase power beyond
         * regulatory limit, however this could be utilized for devices with
         * CAPABILITY_POWER_LIMIT.
-        *
-        * TODO: add different temperature compensation code for RT3290 & RT5390
-        * to allow to use BBP_R1 for those chips.
-        */
-       if (!rt2x00_rt(rt2x00dev, RT3290) &&
-           !rt2x00_rt(rt2x00dev, RT5390)) {
-               rt2800_bbp_read(rt2x00dev, 1, &r1);
-               if (delta <= -12) {
-                       power_ctrl = 2;
-                       delta += 12;
-               } else if (delta <= -6) {
-                       power_ctrl = 1;
-                       delta += 6;
-               } else {
-                       power_ctrl = 0;
-               }
-               rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, power_ctrl);
-               rt2800_bbp_write(rt2x00dev, 1, r1);
+        */
+       if (delta <= -12) {
+               power_ctrl = 2;
+               delta += 12;
+       } else if (delta <= -6) {
+               power_ctrl = 1;
+               delta += 6;
+       } else {
+               power_ctrl = 0;
        }
+       rt2800_bbp_read(rt2x00dev, 1, &r1);
+       rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, power_ctrl);
+       rt2800_bbp_write(rt2x00dev, 1, r1);
 
        offset = TX_PWR_CFG_0;
 
index 1ff81af..9bb398b 100644 (file)
@@ -1019,9 +1019,12 @@ struct rt2x00_bar_list_entry {
  * Register defines.
  * Some registers require multiple attempts before success,
  * in those cases REGISTER_BUSY_COUNT attempts should be
- * taken with a REGISTER_BUSY_DELAY interval.
+ * taken with a REGISTER_BUSY_DELAY interval. Due to USB
+ * bus delays, we do not have to loop so many times to wait
+ * for valid register value on that bus.
  */
 #define REGISTER_BUSY_COUNT    100
+#define REGISTER_USB_BUSY_COUNT 20
 #define REGISTER_BUSY_DELAY    100
 
 /*
index dc85d3e..892270d 100644 (file)
@@ -42,37 +42,27 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev,
 {
        struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
        int status;
-       unsigned int i;
        unsigned int pipe =
            (requesttype == USB_VENDOR_REQUEST_IN) ?
            usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0);
+       unsigned long expire = jiffies + msecs_to_jiffies(timeout);
 
        if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
                return -ENODEV;
 
-       for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+       do {
                status = usb_control_msg(usb_dev, pipe, request, requesttype,
                                         value, offset, buffer, buffer_length,
-                                        timeout);
+                                        timeout / 2);
                if (status >= 0)
                        return 0;
 
-               /*
-                * Check for errors
-                * -ENODEV: Device has disappeared, no point continuing.
-                * All other errors: Try again.
-                */
-               else if (status == -ENODEV) {
+               if (status == -ENODEV) {
+                       /* Device has disappeared. */
                        clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
                        break;
                }
-       }
-
-       /* If the port is powered down, we get a -EPROTO error, and this
-        * leads to a endless loop. So just say that the device is gone.
-        */
-       if (status == -EPROTO)
-               clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
+       } while (time_before(jiffies, expire));
 
        rt2x00_err(rt2x00dev,
                   "Vendor Request 0x%02x failed for offset 0x%04x with error %d\n",
@@ -154,7 +144,7 @@ int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
        if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
                return -ENODEV;
 
-       for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+       for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
                rt2x00usb_register_read_lock(rt2x00dev, offset, reg);
                if (!rt2x00_get_field32(*reg, field))
                        return 1;
index 819690e..8f85fbd 100644 (file)
@@ -38,7 +38,7 @@
  * a higher value is required. In that case we use the REGISTER_TIMEOUT_FIRMWARE
  * and EEPROM_TIMEOUT.
  */
-#define REGISTER_TIMEOUT               500
+#define REGISTER_TIMEOUT               100
 #define REGISTER_TIMEOUT_FIRMWARE      1000
 #define EEPROM_TIMEOUT                 2000
 
index 95724ff..a5458cf 100644 (file)
@@ -1295,7 +1295,7 @@ static int rt73usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
        unsigned int i;
        u8 value;
 
-       for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+       for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
                rt73usb_bbp_read(rt2x00dev, 0, &value);
                if ((value != 0xff) && (value != 0x00))
                        return 0;
index af52f0b..5fc6f52 100644 (file)
@@ -786,6 +786,7 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
                                    unsigned int changed_flags,
                                    unsigned int *new_flags, u64 multicast)
 {
+       bool update_rcr = false;
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
 
@@ -806,6 +807,7 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
                        RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
                                 "Disable receive multicast frame\n");
                }
+               update_rcr = true;
        }
 
        if (changed_flags & FIF_FCSFAIL) {
@@ -818,6 +820,8 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
                        RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
                                 "Disable receive FCS error frame\n");
                }
+               if (!update_rcr)
+                       update_rcr = true;
        }
 
        /* if ssid not set to hw don't check bssid
@@ -832,6 +836,8 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
                                rtlpriv->cfg->ops->set_chk_bssid(hw, false);
                        else
                                rtlpriv->cfg->ops->set_chk_bssid(hw, true);
+                       if (update_rcr)
+                               update_rcr = false;
                }
        }
 
@@ -846,6 +852,8 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
                        RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
                                 "Disable receive control frame.\n");
                }
+               if (!update_rcr)
+                       update_rcr = true;
        }
 
        if (changed_flags & FIF_OTHER_BSS) {
@@ -858,7 +866,13 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
                        RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
                                 "Disable receive other BSS's frame.\n");
                }
+               if (!update_rcr)
+                       update_rcr = true;
        }
+
+       if (update_rcr)
+               rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
+                                             (u8 *)(&mac->rx_conf));
 }
 static int rtl_op_sta_add(struct ieee80211_hw *hw,
                         struct ieee80211_vif *vif,
index 55357d6..d2ec516 100644 (file)
@@ -1287,6 +1287,7 @@ void rtl92ce_enable_interrupt(struct ieee80211_hw *hw)
 
        rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
        rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF);
+       rtlpci->irq_enabled = true;
 }
 
 void rtl92ce_disable_interrupt(struct ieee80211_hw *hw)
@@ -1296,7 +1297,7 @@ void rtl92ce_disable_interrupt(struct ieee80211_hw *hw)
 
        rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED);
        rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED);
-       synchronize_irq(rtlpci->pdev->irq);
+       rtlpci->irq_enabled = false;
 }
 
 static void _rtl92ce_poweroff_adapter(struct ieee80211_hw *hw)
index 46ea076..dd5aa08 100644 (file)
@@ -228,6 +228,7 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = {
        .led_control = rtl92ce_led_control,
        .set_desc = rtl92ce_set_desc,
        .get_desc = rtl92ce_get_desc,
+       .is_tx_desc_closed = rtl92ce_is_tx_desc_closed,
        .tx_polling = rtl92ce_tx_polling,
        .enable_hw_sec = rtl92ce_enable_hw_security_config,
        .set_key = rtl92ce_set_key,
@@ -271,6 +272,8 @@ static struct rtl_hal_cfg rtl92ce_hal_cfg = {
        .maps[MAC_RCR_ACRC32] = ACRC32,
        .maps[MAC_RCR_ACF] = ACF,
        .maps[MAC_RCR_AAP] = AAP,
+       .maps[MAC_HIMR] = REG_HIMR,
+       .maps[MAC_HIMRE] = REG_HIMRE,
 
        .maps[EFUSE_TEST] = REG_EFUSE_TEST,
        .maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
index dc3d20b..e88dcd0 100644 (file)
@@ -720,16 +720,15 @@ u32 rtl92ce_get_desc(u8 *p_desc, bool istx, u8 desc_name)
                        break;
                }
        } else {
-               struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc;
                switch (desc_name) {
                case HW_DESC_OWN:
-                       ret = GET_RX_DESC_OWN(pdesc);
+                       ret = GET_RX_DESC_OWN(p_desc);
                        break;
                case HW_DESC_RXPKT_LEN:
-                       ret = GET_RX_DESC_PKT_LEN(pdesc);
+                       ret = GET_RX_DESC_PKT_LEN(p_desc);
                        break;
                case HW_DESC_RXBUFF_ADDR:
-                       ret = GET_RX_STATUS_DESC_BUFF_ADDR(pdesc);
+                       ret = GET_RX_DESC_BUFF_ADDR(p_desc);
                        break;
                default:
                        RT_ASSERT(false, "ERR rxdesc :%d not process\n",
@@ -740,6 +739,23 @@ u32 rtl92ce_get_desc(u8 *p_desc, bool istx, u8 desc_name)
        return ret;
 }
 
+bool rtl92ce_is_tx_desc_closed(struct ieee80211_hw *hw,
+                              u8 hw_queue, u16 index)
+{
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
+       u8 *entry = (u8 *)(&ring->desc[ring->idx]);
+       u8 own = (u8)rtl92ce_get_desc(entry, true, HW_DESC_OWN);
+
+       /*beacon packet will only use the first
+        *descriptor defautly,and the own may not
+        *be cleared by the hardware
+        */
+       if (own)
+               return false;
+       return true;
+}
+
 void rtl92ce_tx_polling(struct ieee80211_hw *hw, u8 hw_queue)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
index 9a39ec4..4bec4b0 100644 (file)
@@ -723,6 +723,8 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
 void rtl92ce_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
                      u8 desc_name, u8 *val);
 u32 rtl92ce_get_desc(u8 *pdesc, bool istx, u8 desc_name);
+bool rtl92ce_is_tx_desc_closed(struct ieee80211_hw *hw,
+                              u8 hw_queue, u16 index);
 void rtl92ce_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
 void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
                             bool b_firstseg, bool b_lastseg,
index 11952b9..0315eed 100644 (file)
@@ -1,6 +1,3 @@
-obj-m := rtl8192ee.o
-
-
 rtl8192ee-objs :=              \
                dm.o            \
                fw.o            \
@@ -14,6 +11,6 @@ rtl8192ee-objs :=             \
                trx.o           \
 
 
-obj-$(CONFIG_RTL8821AE) += rtl8192ee.o
+obj-$(CONFIG_RTL8192EE) += rtl8192ee.o
 
 ccflags-y += -D__CHECK_ENDIAN__
index 9c34a85..6220672 100644 (file)
@@ -1,6 +1,3 @@
-obj-m := rtl8723ae.o
-
-
 rtl8723ae-objs :=              \
                dm.o            \
                fw.o            \
index 59e416a..a77c341 100644 (file)
@@ -1,6 +1,3 @@
-obj-m := rtl8723be.o
-
-
 rtl8723be-objs :=              \
                dm.o            \
                fw.o            \
index 87ad604..f7a26f7 100644 (file)
@@ -1,6 +1,3 @@
-obj-m := rtl8821ae.o
-
-
 rtl8821ae-objs :=              \
                dm.o            \
                fw.o            \
index 16d1028..5153640 100644 (file)
@@ -259,10 +259,7 @@ void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap)
                                             &wlvif->connection_loss_work,
                                             msecs_to_jiffies(delay));
 
-               ieee80211_cqm_rssi_notify(
-                               vif,
-                               NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
-                               GFP_KERNEL);
+               ieee80211_cqm_beacon_loss_notify(vif, GFP_KERNEL);
        }
 }
 EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
index 440291a..fc02e8d 100644 (file)
@@ -29,8 +29,8 @@
 #include <linux/delay.h>
 #include <linux/nfc.h>
 #include <linux/firmware.h>
-#include <linux/unaligned/access_ok.h>
 #include <linux/platform_data/pn544.h>
+#include <asm/unaligned.h>
 
 #include <net/nfc/hci.h>
 #include <net/nfc/llc.h>
index 0ea756b..0572208 100644 (file)
@@ -28,8 +28,8 @@
 #include <linux/delay.h>
 #include <linux/nfc.h>
 #include <linux/firmware.h>
-#include <linux/unaligned/access_ok.h>
 #include <linux/platform_data/st21nfca.h>
+#include <asm/unaligned.h>
 
 #include <net/nfc/hci.h>
 #include <net/nfc/llc.h>
@@ -72,7 +72,6 @@ struct st21nfca_i2c_phy {
        struct nfc_hci_dev *hdev;
 
        unsigned int gpio_ena;
-       unsigned int gpio_irq;
        unsigned int irq_polarity;
 
        struct sk_buff *pending_skb;
@@ -531,20 +530,12 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
                                  "clf_enable");
        if (r) {
                nfc_err(&client->dev, "Failed to request enable pin\n");
-               return -ENODEV;
+               return r;
        }
 
        phy->gpio_ena = gpio;
 
-       /* IRQ */
-       r = irq_of_parse_and_map(pp, 0);
-       if (r < 0) {
-               nfc_err(&client->dev, "Unable to get irq, error: %d\n", r);
-               return r;
-       }
-
-       phy->irq_polarity = irq_get_trigger_type(r);
-       client->irq = r;
+       phy->irq_polarity = irq_get_trigger_type(client->irq);
 
        return 0;
 }
@@ -560,7 +551,6 @@ static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
        struct st21nfca_nfc_platform_data *pdata;
        struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
        int r;
-       int irq;
 
        pdata = client->dev.platform_data;
        if (pdata == NULL) {
@@ -569,36 +559,18 @@ static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
        }
 
        /* store for later use */
-       phy->gpio_irq = pdata->gpio_irq;
        phy->gpio_ena = pdata->gpio_ena;
        phy->irq_polarity = pdata->irq_polarity;
 
-       r = devm_gpio_request_one(&client->dev, phy->gpio_irq, GPIOF_IN,
-                                 "wake_up");
-       if (r) {
-               pr_err("%s : gpio_request failed\n", __FILE__);
-               return -ENODEV;
-       }
-
        if (phy->gpio_ena > 0) {
                r = devm_gpio_request_one(&client->dev, phy->gpio_ena,
                                          GPIOF_OUT_INIT_HIGH, "clf_enable");
                if (r) {
                        pr_err("%s : ena gpio_request failed\n", __FILE__);
-                       return -ENODEV;
+                       return r;
                }
        }
 
-       /* IRQ */
-       irq = gpio_to_irq(phy->gpio_irq);
-       if (irq < 0) {
-               nfc_err(&client->dev,
-                               "Unable to get irq number for GPIO %d error %d\n",
-                               phy->gpio_irq, r);
-               return -ENODEV;
-       }
-       client->irq = irq;
-
        return 0;
 }
 
@@ -656,7 +628,7 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client,
        r = st21nfca_hci_platform_init(phy);
        if (r < 0) {
                nfc_err(&client->dev, "Unable to reboot st21nfca\n");
-               return -ENODEV;
+               return r;
        }
 
        r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
@@ -687,10 +659,13 @@ static int st21nfca_hci_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_OF
 static const struct of_device_id of_st21nfca_i2c_match[] = {
        { .compatible = "st,st21nfca_i2c", },
        {}
 };
+MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match);
+#endif
 
 static struct i2c_driver st21nfca_hci_i2c_driver = {
        .driver = {
index a89e56c..f2596c8 100644 (file)
        ((p & 0x0f) == (ST21NFCA_DM_PIPE_CREATED | ST21NFCA_DM_PIPE_OPEN))
 
 #define ST21NFCA_NFC_MODE                      0x03    /* NFC_MODE parameter*/
-#define ST21NFCA_EVT_FIELD_ON                  0x11
-#define ST21NFCA_EVT_CARD_DEACTIVATED          0x12
-#define ST21NFCA_EVT_CARD_ACTIVATED            0x13
-#define ST21NFCA_EVT_FIELD_OFF                 0x14
 
 static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
 
@@ -841,31 +837,11 @@ static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev,
 static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
                                       u8 event, struct sk_buff *skb)
 {
-       int r;
-       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
-
-       pr_debug("hci event: %d\n", event);
+       pr_debug("hci event: %d gate: %x\n", event, gate);
 
-       switch (event) {
-       case ST21NFCA_EVT_CARD_ACTIVATED:
-               if (gate == ST21NFCA_RF_CARD_F_GATE)
-                       info->dep_info.curr_nfc_dep_pni = 0;
-               break;
-       case ST21NFCA_EVT_CARD_DEACTIVATED:
-               break;
-       case ST21NFCA_EVT_FIELD_ON:
-               break;
-       case ST21NFCA_EVT_FIELD_OFF:
-               break;
-       case ST21NFCA_EVT_SEND_DATA:
-               if (gate == ST21NFCA_RF_CARD_F_GATE) {
-                       r = st21nfca_tm_event_send_data(hdev, skb, gate);
-                       if (r < 0)
-                               return r;
-                       return 0;
-               }
-               info->dep_info.curr_nfc_dep_pni = 0;
-               return 1;
+       switch (gate) {
+       case ST21NFCA_RF_CARD_F_GATE:
+               return st21nfca_dep_event_received(hdev, event, skb);
        default:
                return 1;
        }
index a0b77f1..7c2a852 100644 (file)
@@ -85,6 +85,4 @@ struct st21nfca_hci_info {
 
 #define ST21NFCA_RF_CARD_F_GATE 0x24
 
-#define ST21NFCA_EVT_SEND_DATA 0x10
-
 #endif /* __LOCAL_ST21NFCA_H_ */
index bfb6df5..8882181 100644 (file)
 #define ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B 0x30
 #define ST21NFCA_GB_BIT  0x02
 
+#define ST21NFCA_EVT_SEND_DATA         0x10
+#define ST21NFCA_EVT_FIELD_ON           0x11
+#define ST21NFCA_EVT_CARD_DEACTIVATED   0x12
+#define ST21NFCA_EVT_CARD_ACTIVATED     0x13
+#define ST21NFCA_EVT_FIELD_OFF          0x14
+
 #define ST21NFCA_EVT_CARD_F_BITRATE 0x16
 #define ST21NFCA_EVT_READER_F_BITRATE 0x13
 #define        ST21NFCA_PSL_REQ_SEND_SPEED(brs) (brs & 0x38)
@@ -372,8 +378,8 @@ exit:
        return r;
 }
 
-int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, struct sk_buff *skb,
-                               u8 gate)
+static int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev,
+                               struct sk_buff *skb)
 {
        u8 cmd0, cmd1;
        int r;
@@ -400,7 +406,42 @@ int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, struct sk_buff *skb,
        }
        return r;
 }
-EXPORT_SYMBOL(st21nfca_tm_event_send_data);
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ *    1: driver does not handle the event, please do standard processing
+ */
+int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
+                               u8 event, struct sk_buff *skb)
+{
+       int r = 0;
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       pr_debug("dep event: %d\n", event);
+
+       switch (event) {
+       case ST21NFCA_EVT_CARD_ACTIVATED:
+               info->dep_info.curr_nfc_dep_pni = 0;
+               break;
+       case ST21NFCA_EVT_CARD_DEACTIVATED:
+               break;
+       case ST21NFCA_EVT_FIELD_ON:
+               break;
+       case ST21NFCA_EVT_FIELD_OFF:
+               break;
+       case ST21NFCA_EVT_SEND_DATA:
+               r = st21nfca_tm_event_send_data(hdev, skb);
+               if (r < 0)
+                       return r;
+               return 0;
+       default:
+               return 1;
+       }
+       kfree_skb(skb);
+       return r;
+}
+EXPORT_SYMBOL(st21nfca_dep_event_received);
 
 static void st21nfca_im_send_psl_req(struct nfc_hci_dev *hdev, u8 did, u8 bsi,
                                     u8 bri, u8 lri)
index ca213de..baf4664 100644 (file)
@@ -32,8 +32,8 @@ struct st21nfca_dep_info {
        u8 lri;
 } __packed;
 
-int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, struct sk_buff *skb,
-                               u8 gate);
+int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
+                               u8 event, struct sk_buff *skb);
 int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb);
 
 int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len);
index c5d2427..01ba865 100644 (file)
@@ -50,7 +50,6 @@ struct st21nfcb_i2c_phy {
        struct i2c_client *i2c_dev;
        struct llt_ndlc *ndlc;
 
-       unsigned int gpio_irq;
        unsigned int gpio_reset;
        unsigned int irq_polarity;
 
@@ -81,8 +80,6 @@ static void st21nfcb_nci_i2c_disable(void *phy_id)
 {
        struct st21nfcb_i2c_phy *phy = phy_id;
 
-       pr_info("\n");
-
        phy->powered = 0;
        /* reset chip in order to flush clf */
        gpio_set_value(phy->gpio_reset, 0);
@@ -258,19 +255,11 @@ static int st21nfcb_nci_i2c_of_request_resources(struct i2c_client *client)
                                GPIOF_OUT_INIT_HIGH, "clf_reset");
        if (r) {
                nfc_err(&client->dev, "Failed to request reset pin\n");
-               return -ENODEV;
-       }
-       phy->gpio_reset = gpio;
-
-       /* IRQ */
-       r = irq_of_parse_and_map(pp, 0);
-       if (r < 0) {
-               nfc_err(&client->dev, "Unable to get irq, error: %d\n", r);
                return r;
        }
+       phy->gpio_reset = gpio;
 
-       phy->irq_polarity = irq_get_trigger_type(r);
-       client->irq = r;
+       phy->irq_polarity = irq_get_trigger_type(client->irq);
 
        return 0;
 }
@@ -286,7 +275,6 @@ static int st21nfcb_nci_i2c_request_resources(struct i2c_client *client)
        struct st21nfcb_nfc_platform_data *pdata;
        struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client);
        int r;
-       int irq;
 
        pdata = client->dev.platform_data;
        if (pdata == NULL) {
@@ -295,33 +283,15 @@ static int st21nfcb_nci_i2c_request_resources(struct i2c_client *client)
        }
 
        /* store for later use */
-       phy->gpio_irq = pdata->gpio_irq;
        phy->gpio_reset = pdata->gpio_reset;
        phy->irq_polarity = pdata->irq_polarity;
 
-       r = devm_gpio_request_one(&client->dev, phy->gpio_irq,
-                               GPIOF_IN, "clf_irq");
-       if (r) {
-               pr_err("%s : gpio_request failed\n", __FILE__);
-               return -ENODEV;
-       }
-
        r = devm_gpio_request_one(&client->dev,
                        phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset");
        if (r) {
                pr_err("%s : reset gpio_request failed\n", __FILE__);
-               return -ENODEV;
-       }
-
-       /* IRQ */
-       irq = gpio_to_irq(phy->gpio_irq);
-       if (irq < 0) {
-               nfc_err(&client->dev,
-                       "Unable to get irq number for GPIO %d error %d\n",
-                       phy->gpio_irq, r);
-               return -ENODEV;
+               return r;
        }
-       client->irq = irq;
 
        return 0;
 }
@@ -401,10 +371,13 @@ static int st21nfcb_nci_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_OF
 static const struct of_device_id of_st21nfcb_i2c_match[] = {
        { .compatible = "st,st21nfcb_i2c", },
        {}
 };
+MODULE_DEVICE_TABLE(of, of_st21nfcb_i2c_match);
+#endif
 
 static struct i2c_driver st21nfcb_nci_i2c_driver = {
        .driver = {
index e7bff89..bac50e8 100644 (file)
@@ -266,7 +266,7 @@ int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
 
        *ndlc_id = ndlc;
 
-       /* start timers */
+       /* initialize timers */
        init_timer(&ndlc->t1_timer);
        ndlc->t1_timer.data = (unsigned long)ndlc;
        ndlc->t1_timer.function = ndlc_t1_timeout;
index 69161bb..410215c 100644 (file)
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 
+#include <linux/pm.h>
 #include <linux/pci.h>
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/ssb/ssb.h>
 
 
-#ifdef CONFIG_PM
-static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int ssb_pcihost_suspend(struct device *d)
 {
+       struct pci_dev *dev = to_pci_dev(d);
        struct ssb_bus *ssb = pci_get_drvdata(dev);
        int err;
 
@@ -28,17 +30,23 @@ static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
                return err;
        pci_save_state(dev);
        pci_disable_device(dev);
-       pci_set_power_state(dev, pci_choose_state(dev, state));
+
+       /* if there is a wakeup enabled child device on ssb bus,
+          enable pci wakeup posibility. */
+       device_set_wakeup_enable(d, d->power.wakeup_path);
+
+       pci_prepare_to_sleep(dev);
 
        return 0;
 }
 
-static int ssb_pcihost_resume(struct pci_dev *dev)
+static int ssb_pcihost_resume(struct device *d)
 {
+       struct pci_dev *dev = to_pci_dev(d);
        struct ssb_bus *ssb = pci_get_drvdata(dev);
        int err;
 
-       pci_set_power_state(dev, PCI_D0);
+       pci_back_from_sleep(dev);
        err = pci_enable_device(dev);
        if (err)
                return err;
@@ -49,10 +57,12 @@ static int ssb_pcihost_resume(struct pci_dev *dev)
 
        return 0;
 }
-#else /* CONFIG_PM */
-# define ssb_pcihost_suspend   NULL
-# define ssb_pcihost_resume    NULL
-#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops ssb_pcihost_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(ssb_pcihost_suspend, ssb_pcihost_resume)
+};
+
+#endif /* CONFIG_PM_SLEEP */
 
 static int ssb_pcihost_probe(struct pci_dev *dev,
                             const struct pci_device_id *id)
@@ -115,8 +125,9 @@ int ssb_pcihost_register(struct pci_driver *driver)
 {
        driver->probe = ssb_pcihost_probe;
        driver->remove = ssb_pcihost_remove;
-       driver->suspend = ssb_pcihost_suspend;
-       driver->resume = ssb_pcihost_resume;
+#ifdef CONFIG_PM_SLEEP
+       driver->driver.pm = &ssb_pcihost_pm_ops;
+#endif
 
        return pci_register_driver(driver);
 }
index 1730312..5087fff 100644 (file)
@@ -24,7 +24,6 @@
 #define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci"
 
 struct st21nfca_nfc_platform_data {
-       unsigned int gpio_irq;
        unsigned int gpio_ena;
        unsigned int irq_polarity;
 };
index 2d11f1f..c3b432f 100644 (file)
@@ -24,7 +24,6 @@
 #define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci"
 
 struct st21nfcb_nfc_platform_data {
-       unsigned int gpio_irq;
        unsigned int gpio_reset;
        unsigned int irq_polarity;
 };
index e56f909..40129b3 100644 (file)
@@ -163,6 +163,7 @@ enum {
 enum {
        HCI_DUT_MODE,
        HCI_FORCE_SC,
+       HCI_FORCE_LESC,
        HCI_FORCE_STATIC_ADDR,
 };
 
@@ -342,6 +343,7 @@ enum {
 #define HCI_LE_ENCRYPTION              0x01
 #define HCI_LE_CONN_PARAM_REQ_PROC     0x02
 #define HCI_LE_PING                    0x10
+#define HCI_LE_EXT_SCAN_POLICY         0x80
 
 /* Connection modes */
 #define HCI_CM_ACTIVE  0x0000
@@ -411,6 +413,7 @@ enum {
 
 /* The core spec defines 127 as the "not available" value */
 #define HCI_TX_POWER_INVALID   127
+#define HCI_RSSI_INVALID       127
 
 #define HCI_ROLE_MASTER                0x00
 #define HCI_ROLE_SLAVE         0x01
@@ -1749,6 +1752,25 @@ struct hci_ev_le_conn_complete {
        __u8     clk_accurancy;
 } __packed;
 
+/* Advertising report event types */
+#define LE_ADV_IND             0x00
+#define LE_ADV_DIRECT_IND      0x01
+#define LE_ADV_SCAN_IND                0x02
+#define LE_ADV_NONCONN_IND     0x03
+#define LE_ADV_SCAN_RSP                0x04
+
+#define ADDR_LE_DEV_PUBLIC     0x00
+#define ADDR_LE_DEV_RANDOM     0x01
+
+#define HCI_EV_LE_ADVERTISING_REPORT   0x02
+struct hci_ev_le_advertising_info {
+       __u8     evt_type;
+       __u8     bdaddr_type;
+       bdaddr_t bdaddr;
+       __u8     length;
+       __u8     data[0];
+} __packed;
+
 #define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03
 struct hci_ev_le_conn_update_complete {
        __u8     status;
@@ -1774,23 +1796,14 @@ struct hci_ev_le_remote_conn_param_req {
        __le16 timeout;
 } __packed;
 
-/* Advertising report event types */
-#define LE_ADV_IND             0x00
-#define LE_ADV_DIRECT_IND      0x01
-#define LE_ADV_SCAN_IND                0x02
-#define LE_ADV_NONCONN_IND     0x03
-#define LE_ADV_SCAN_RSP                0x04
-
-#define ADDR_LE_DEV_PUBLIC     0x00
-#define ADDR_LE_DEV_RANDOM     0x01
-
-#define HCI_EV_LE_ADVERTISING_REPORT   0x02
-struct hci_ev_le_advertising_info {
+#define HCI_EV_LE_DIRECT_ADV_REPORT    0x0B
+struct hci_ev_le_direct_adv_info {
        __u8     evt_type;
        __u8     bdaddr_type;
        bdaddr_t bdaddr;
-       __u8     length;
-       __u8     data[0];
+       __u8     direct_addr_type;
+       bdaddr_t direct_addr;
+       __s8     rssi;
 } __packed;
 
 /* Internal events generated by Bluetooth stack */
index a805b3d..3c78270 100644 (file)
@@ -75,6 +75,10 @@ struct discovery_state {
        u32                     last_adv_flags;
        u8                      last_adv_data[HCI_MAX_AD_LENGTH];
        u8                      last_adv_data_len;
+       bool                    report_invalid_rssi;
+       s8                      rssi;
+       u16                     uuid_count;
+       u8                      (*uuids)[16];
 };
 
 struct hci_conn_hash {
@@ -130,6 +134,7 @@ struct smp_irk {
 
 struct link_key {
        struct list_head list;
+       struct rcu_head rcu;
        bdaddr_t bdaddr;
        u8 type;
        u8 val[HCI_LINK_KEY_SIZE];
@@ -139,6 +144,7 @@ struct link_key {
 struct oob_data {
        struct list_head list;
        bdaddr_t bdaddr;
+       u8 bdaddr_type;
        u8 hash192[16];
        u8 rand192[16];
        u8 hash256[16];
@@ -305,6 +311,7 @@ struct hci_dev {
        __u32                   req_result;
 
        void                    *smp_data;
+       void                    *smp_bredr_data;
 
        struct discovery_state  discovery;
        struct hci_conn_hash    conn_hash;
@@ -500,6 +507,17 @@ static inline void discovery_init(struct hci_dev *hdev)
        INIT_LIST_HEAD(&hdev->discovery.all);
        INIT_LIST_HEAD(&hdev->discovery.unknown);
        INIT_LIST_HEAD(&hdev->discovery.resolve);
+       hdev->discovery.report_invalid_rssi = true;
+       hdev->discovery.rssi = HCI_RSSI_INVALID;
+}
+
+static inline void hci_discovery_filter_clear(struct hci_dev *hdev)
+{
+       hdev->discovery.report_invalid_rssi = true;
+       hdev->discovery.rssi = HCI_RSSI_INVALID;
+       hdev->discovery.uuid_count = 0;
+       kfree(hdev->discovery.uuids);
+       hdev->discovery.uuids = NULL;
 }
 
 bool hci_discovery_active(struct hci_dev *hdev);
@@ -558,6 +576,7 @@ enum {
        HCI_CONN_AUTH_INITIATOR,
        HCI_CONN_DROP,
        HCI_CONN_PARAM_REMOVAL_PEND,
+       HCI_CONN_NEW_LINK_KEY,
 };
 
 static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
@@ -920,13 +939,11 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
 struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
                                  bdaddr_t *bdaddr, u8 *val, u8 type,
                                  u8 pin_len, bool *persistent);
-struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
-                            u8 role);
 struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
                            u8 addr_type, u8 type, u8 authenticated,
                            u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand);
-struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 addr_type, u8 role);
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                            u8 addr_type, u8 role);
 int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type);
 void hci_smp_ltks_clear(struct hci_dev *hdev);
 int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
@@ -941,13 +958,12 @@ void hci_smp_irks_clear(struct hci_dev *hdev);
 
 void hci_remote_oob_data_clear(struct hci_dev *hdev);
 struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
-                                         bdaddr_t *bdaddr);
+                                         bdaddr_t *bdaddr, u8 bdaddr_type);
 int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                           u8 *hash, u8 *rand);
-int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                               u8 *hash192, u8 *rand192,
-                               u8 *hash256, u8 *rand256);
-int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
+                           u8 bdaddr_type, u8 *hash192, u8 *rand192,
+                           u8 *hash256, u8 *rand256);
+int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                              u8 bdaddr_type);
 
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
@@ -998,6 +1014,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 
 #define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
                                !test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+#define bredr_sc_enabled(dev) ((lmp_sc_capable(dev) || \
+                               test_bit(HCI_FORCE_SC, &(dev)->dbg_flags)) && \
+                              test_bit(HCI_SC_ENABLED, &(dev)->dev_flags))
 
 /* ----- HCI protocols ----- */
 #define HCI_PROTO_DEFER             0x01
index 061e648..eee3ef5 100644 (file)
@@ -141,6 +141,7 @@ struct l2cap_conninfo {
 #define L2CAP_FC_ATT           0x10
 #define L2CAP_FC_SIG_LE                0x20
 #define L2CAP_FC_SMP_LE                0x40
+#define L2CAP_FC_SMP_BREDR     0x80
 
 /* L2CAP Control Field bit masks */
 #define L2CAP_CTRL_SAR                 0xC000
@@ -255,6 +256,7 @@ struct l2cap_conn_rsp {
 #define L2CAP_CID_ATT          0x0004
 #define L2CAP_CID_LE_SIGNALING 0x0005
 #define L2CAP_CID_SMP          0x0006
+#define L2CAP_CID_SMP_BREDR    0x0007
 #define L2CAP_CID_DYN_START    0x0040
 #define L2CAP_CID_DYN_END      0xffff
 #define L2CAP_CID_LE_DYN_END   0x007f
@@ -619,8 +621,8 @@ struct l2cap_conn {
        unsigned int            mtu;
 
        __u32                   feat_mask;
-       __u8                    fixed_chan_mask;
-       bool                    hs_enabled;
+       __u8                    remote_fixed_chan;
+       __u8                    local_fixed_chan;
 
        __u8                    info_state;
        __u8                    info_ident;
index b391fd6..95c34d5 100644 (file)
@@ -184,6 +184,9 @@ struct mgmt_cp_load_link_keys {
 
 #define MGMT_LTK_UNAUTHENTICATED       0x00
 #define MGMT_LTK_AUTHENTICATED         0x01
+#define MGMT_LTK_P256_UNAUTH           0x02
+#define MGMT_LTK_P256_AUTH             0x03
+#define MGMT_LTK_P256_DEBUG            0x04
 
 struct mgmt_ltk_info {
        struct mgmt_addr_info addr;
@@ -495,6 +498,15 @@ struct mgmt_cp_set_public_address {
 } __packed;
 #define MGMT_SET_PUBLIC_ADDRESS_SIZE   6
 
+#define MGMT_OP_START_SERVICE_DISCOVERY        0x003A
+struct mgmt_cp_start_service_discovery {
+       __u8 type;
+       __s8 rssi;
+       __le16 uuid_count;
+       __u8 uuids[0][16];
+} __packed;
+#define MGMT_START_SERVICE_DISCOVERY_SIZE 4
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
index bb748c4..4ebb816 100644 (file)
@@ -4642,33 +4642,6 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
                              enum nl80211_cqm_rssi_threshold_event rssi_event,
                              gfp_t gfp);
 
-/**
- * cfg80211_radar_event - radar detection event
- * @wiphy: the wiphy
- * @chandef: chandef for the current channel
- * @gfp: context flags
- *
- * This function is called when a radar is detected on the current chanenl.
- */
-void cfg80211_radar_event(struct wiphy *wiphy,
-                         struct cfg80211_chan_def *chandef, gfp_t gfp);
-
-/**
- * cfg80211_cac_event - Channel availability check (CAC) event
- * @netdev: network device
- * @chandef: chandef for the current channel
- * @event: type of event
- * @gfp: context flags
- *
- * This function is called when a Channel availability check (CAC) is finished
- * or aborted. This must be called to notify the completion of a CAC process,
- * also by full-MAC drivers.
- */
-void cfg80211_cac_event(struct net_device *netdev,
-                       const struct cfg80211_chan_def *chandef,
-                       enum nl80211_radar_event event, gfp_t gfp);
-
-
 /**
  * cfg80211_cqm_pktloss_notify - notify userspace about packetloss to peer
  * @dev: network device
@@ -4696,6 +4669,42 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
 void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer,
                             u32 num_packets, u32 rate, u32 intvl, gfp_t gfp);
 
+/**
+ * cfg80211_cqm_beacon_loss_notify - beacon loss event
+ * @dev: network device
+ * @gfp: context flags
+ *
+ * Notify userspace about beacon loss from the connected AP.
+ */
+void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
+
+/**
+ * cfg80211_radar_event - radar detection event
+ * @wiphy: the wiphy
+ * @chandef: chandef for the current channel
+ * @gfp: context flags
+ *
+ * This function is called when a radar is detected on the current chanenl.
+ */
+void cfg80211_radar_event(struct wiphy *wiphy,
+                         struct cfg80211_chan_def *chandef, gfp_t gfp);
+
+/**
+ * cfg80211_cac_event - Channel availability check (CAC) event
+ * @netdev: network device
+ * @chandef: chandef for the current channel
+ * @event: type of event
+ * @gfp: context flags
+ *
+ * This function is called when a Channel availability check (CAC) is finished
+ * or aborted. This must be called to notify the completion of a CAC process,
+ * also by full-MAC drivers.
+ */
+void cfg80211_cac_event(struct net_device *netdev,
+                       const struct cfg80211_chan_def *chandef,
+                       enum nl80211_radar_event event, gfp_t gfp);
+
+
 /**
  * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
  * @dev: network device
index cff3a26..58d719d 100644 (file)
@@ -3618,6 +3618,26 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
 void ieee80211_tx_status(struct ieee80211_hw *hw,
                         struct sk_buff *skb);
 
+/**
+ * ieee80211_tx_status_noskb - transmit status callback without skb
+ *
+ * This function can be used as a replacement for ieee80211_tx_status
+ * in drivers that cannot reliably map tx status information back to
+ * specific skbs.
+ *
+ * Calls to this function for a single hardware must be synchronized
+ * against each other. Calls to this function, ieee80211_tx_status_ni()
+ * and ieee80211_tx_status_irqsafe() may not be mixed for a single hardware.
+ *
+ * @hw: the hardware the frame was transmitted by
+ * @sta: the receiver station to which this packet is sent
+ *     (NULL for multicast packets)
+ * @info: tx status information
+ */
+void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
+                              struct ieee80211_sta *sta,
+                              struct ieee80211_tx_info *info);
+
 /**
  * ieee80211_tx_status_ni - transmit status callback (in process context)
  *
@@ -4671,6 +4691,14 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
                               enum nl80211_cqm_rssi_threshold_event rssi_event,
                               gfp_t gfp);
 
+/**
+ * ieee80211_cqm_beacon_loss_notify - inform CQM of beacon loss
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @gfp: context flags
+ */
+void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp);
+
 /**
  * ieee80211_radar_detected - inform that a radar was detected
  *
@@ -4829,6 +4857,10 @@ struct rate_control_ops {
        void (*free_sta)(void *priv, struct ieee80211_sta *sta,
                         void *priv_sta);
 
+       void (*tx_status_noskb)(void *priv,
+                               struct ieee80211_supported_band *sband,
+                               struct ieee80211_sta *sta, void *priv_sta,
+                               struct ieee80211_tx_info *info);
        void (*tx_status)(void *priv, struct ieee80211_supported_band *sband,
                          struct ieee80211_sta *sta, void *priv_sta,
                          struct sk_buff *skb);
index d9a5cf7..0ae101e 100644 (file)
@@ -225,6 +225,19 @@ struct nfc_digital_dev {
        u8 curr_protocol;
        u8 curr_rf_tech;
        u8 curr_nfc_dep_pni;
+       u8 did;
+
+       u8 local_payload_max;
+       u8 remote_payload_max;
+
+       struct sk_buff *chaining_skb;
+       struct digital_data_exch *data_exch;
+
+       int atn_count;
+       int nack_count;
+
+       struct sk_buff *saved_skb;
+       unsigned int saved_skb_len;
 
        u16 target_fsc;
 
index 7ee8f4c..14bd0e1 100644 (file)
@@ -57,10 +57,14 @@ struct nfc_hci_ops {
        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);
+       int (*se_io)(struct nfc_hci_dev *dev, u32 se_idx,
+                     u8 *apdu, size_t apdu_length,
+                     se_io_cb_t cb, void *cb_context);
 };
 
 /* Pipes */
 #define NFC_HCI_INVALID_PIPE   0x80
+#define NFC_HCI_DO_NOT_CREATE_PIPE     0x81
 #define NFC_HCI_LINK_MGMT_PIPE 0x00
 #define NFC_HCI_ADMIN_PIPE     0x01
 
index 9eca9ae..e7257a4 100644 (file)
@@ -28,6 +28,8 @@
 #ifndef __NCI_H
 #define __NCI_H
 
+#include <net/nfc/nfc.h>
+
 /* NCI constants */
 #define NCI_MAX_NUM_MAPPING_CONFIGS                            10
 #define NCI_MAX_NUM_RF_CONFIGS                                 10
@@ -73,6 +75,8 @@
 #define NCI_NFC_A_ACTIVE_LISTEN_MODE                           0x83
 #define NCI_NFC_F_ACTIVE_LISTEN_MODE                           0x85
 
+#define NCI_RF_TECH_MODE_LISTEN_MASK                           0x80
+
 /* NCI RF Technologies */
 #define NCI_NFC_RF_TECHNOLOGY_A                                        0x00
 #define NCI_NFC_RF_TECHNOLOGY_B                                        0x01
 
 /* NCI Configuration Parameter Tags */
 #define NCI_PN_ATR_REQ_GEN_BYTES                               0x29
+#define NCI_LN_ATR_RES_GEN_BYTES                               0x61
+#define NCI_LA_SEL_INFO                                                0x32
+#define NCI_LF_PROTOCOL_TYPE                                   0x50
+#define NCI_LF_CON_BITR_F                                      0x54
+
+/* NCI Configuration Parameters masks */
+#define NCI_LA_SEL_INFO_ISO_DEP_MASK                           0x20
+#define NCI_LA_SEL_INFO_NFC_DEP_MASK                           0x40
+#define NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK                      0x02
+#define NCI_LF_CON_BITR_F_212                                  0x02
+#define NCI_LF_CON_BITR_F_424                                  0x04
 
 /* NCI Reset types */
 #define NCI_RESET_TYPE_KEEP_CONFIG                             0x00
@@ -314,26 +329,31 @@ struct nci_core_intf_error_ntf {
 struct rf_tech_specific_params_nfca_poll {
        __u16   sens_res;
        __u8    nfcid1_len;     /* 0, 4, 7, or 10 Bytes */
-       __u8    nfcid1[10];
+       __u8    nfcid1[NFC_NFCID1_MAXSIZE];
        __u8    sel_res_len;    /* 0 or 1 Bytes */
        __u8    sel_res;
 } __packed;
 
 struct rf_tech_specific_params_nfcb_poll {
        __u8    sensb_res_len;
-       __u8    sensb_res[12];  /* 11 or 12 Bytes */
+       __u8    sensb_res[NFC_SENSB_RES_MAXSIZE];       /* 11 or 12 Bytes */
 } __packed;
 
 struct rf_tech_specific_params_nfcf_poll {
        __u8    bit_rate;
        __u8    sensf_res_len;
-       __u8    sensf_res[18];  /* 16 or 18 Bytes */
+       __u8    sensf_res[NFC_SENSF_RES_MAXSIZE];       /* 16 or 18 Bytes */
 } __packed;
 
 struct rf_tech_specific_params_nfcv_poll {
        __u8    res_flags;
        __u8    dsfid;
-       __u8    uid[8]; /* 8 Bytes */
+       __u8    uid[NFC_ISO15693_UID_MAXSIZE];  /* 8 Bytes */
+} __packed;
+
+struct rf_tech_specific_params_nfcf_listen {
+       __u8    local_nfcid2_len;
+       __u8    local_nfcid2[NFC_NFCID2_MAXSIZE];       /* 0 or 8 Bytes */
 } __packed;
 
 struct nci_rf_discover_ntf {
@@ -365,7 +385,12 @@ struct activation_params_nfcb_poll_iso_dep {
 
 struct activation_params_poll_nfc_dep {
        __u8    atr_res_len;
-       __u8    atr_res[63];
+       __u8    atr_res[NFC_ATR_RES_MAXSIZE - 2]; /* ATR_RES from byte 3 */
+};
+
+struct activation_params_listen_nfc_dep {
+       __u8    atr_req_len;
+       __u8    atr_req[NFC_ATR_REQ_MAXSIZE - 2]; /* ATR_REQ from byte 3 */
 };
 
 struct nci_rf_intf_activated_ntf {
@@ -382,6 +407,7 @@ struct nci_rf_intf_activated_ntf {
                struct rf_tech_specific_params_nfcb_poll nfcb_poll;
                struct rf_tech_specific_params_nfcf_poll nfcf_poll;
                struct rf_tech_specific_params_nfcv_poll nfcv_poll;
+               struct rf_tech_specific_params_nfcf_listen nfcf_listen;
        } rf_tech_specific_params;
 
        __u8    data_exch_rf_tech_and_mode;
@@ -393,6 +419,7 @@ struct nci_rf_intf_activated_ntf {
                struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep;
                struct activation_params_nfcb_poll_iso_dep nfcb_poll_iso_dep;
                struct activation_params_poll_nfc_dep poll_nfc_dep;
+               struct activation_params_listen_nfc_dep listen_nfc_dep;
        } activation_params;
 
 } __packed;
index 75d10e6..9e51bb4 100644 (file)
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2011 Texas Instruments, Inc.
  *  Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *  Copyright (C) 2014 Marvell International Ltd.
  *
  *  Written by Ilan Elias <ilane@ti.com>
  *
@@ -49,6 +50,8 @@ enum nci_state {
        NCI_W4_ALL_DISCOVERIES,
        NCI_W4_HOST_SELECT,
        NCI_POLL_ACTIVE,
+       NCI_LISTEN_ACTIVE,
+       NCI_LISTEN_SLEEP,
 };
 
 /* NCI timeouts */
@@ -69,6 +72,12 @@ struct nci_ops {
        int   (*send)(struct nci_dev *ndev, struct sk_buff *skb);
        int   (*setup)(struct nci_dev *ndev);
        __u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol);
+       int   (*discover_se)(struct nci_dev *ndev);
+       int   (*disable_se)(struct nci_dev *ndev, u32 se_idx);
+       int   (*enable_se)(struct nci_dev *ndev, u32 se_idx);
+       int   (*se_io)(struct nci_dev *ndev, u32 se_idx,
+                               u8 *apdu, size_t apdu_length,
+                               se_io_cb_t cb, void *cb_context);
 };
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES                4
index 6c583e2..12adb81 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ * Copyright (C) 2014 Marvell International Ltd.
  *
  * Authors:
  *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
@@ -87,6 +88,7 @@ struct nfc_ops {
 #define NFC_TARGET_IDX_ANY -1
 #define NFC_MAX_GT_LEN 48
 #define NFC_ATR_RES_GT_OFFSET 15
+#define NFC_ATR_REQ_GT_OFFSET 14
 
 /**
  * struct nfc_target - NFC target descriptiom
index dad7ab2..b776d72 100644 (file)
@@ -136,6 +136,17 @@ struct regulatory_request {
  *      otherwise initiating radiation is not allowed. This will enable the
  *      relaxations enabled under the CFG80211_REG_RELAX_NO_IR configuration
  *      option
+ * @REGULATORY_IGNORE_STALE_KICKOFF: the regulatory core will _not_ make sure
+ *     all interfaces on this wiphy reside on allowed channels. If this flag
+ *     is not set, upon a regdomain change, the interfaces are given a grace
+ *     period (currently 60 seconds) to disconnect or move to an allowed
+ *     channel. Interfaces on forbidden channels are forcibly disconnected.
+ *     Currently these types of interfaces are supported for enforcement:
+ *     NL80211_IFTYPE_ADHOC, NL80211_IFTYPE_STATION, NL80211_IFTYPE_AP,
+ *     NL80211_IFTYPE_AP_VLAN, NL80211_IFTYPE_MONITOR,
+ *     NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO,
+ *     NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device
+ *     includes any modes unsupported for enforcement checking.
  */
 enum ieee80211_regulatory_flags {
        REGULATORY_CUSTOM_REG                   = BIT(0),
@@ -144,6 +155,7 @@ enum ieee80211_regulatory_flags {
        REGULATORY_COUNTRY_IE_FOLLOW_POWER      = BIT(3),
        REGULATORY_COUNTRY_IE_IGNORE            = BIT(4),
        REGULATORY_ENABLE_RELAX_NO_IR           = BIT(5),
+       REGULATORY_IGNORE_STALE_KICKOFF         = BIT(6),
 };
 
 struct ieee80211_freq_range {
index 9b19b44..8119255 100644 (file)
@@ -116,6 +116,7 @@ enum nfc_commands {
        NFC_EVENT_SE_TRANSACTION,
        NFC_CMD_GET_SE,
        NFC_CMD_SE_IO,
+       NFC_CMD_ACTIVATE_TARGET,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -196,15 +197,19 @@ enum nfc_sdp_attr {
 };
 #define NFC_SDP_ATTR_MAX (__NFC_SDP_ATTR_AFTER_LAST - 1)
 
-#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
-#define NFC_ISO15693_UID_MAXSIZE 8
+#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_ATR_REQ_MAXSIZE            64
+#define NFC_ATR_RES_MAXSIZE            64
+#define NFC_ATR_REQ_GB_MAXSIZE         48
+#define NFC_ATR_RES_GB_MAXSIZE         47
+#define NFC_GB_MAXSIZE                 48
+#define NFC_FIRMWARE_NAME_MAXSIZE      32
+#define NFC_ISO15693_UID_MAXSIZE       8
 
 /* NFC protocols */
 #define NFC_PROTO_JEWEL                1
index d775245..b37bd5a 100644 (file)
@@ -3451,6 +3451,8 @@ enum nl80211_ps_state {
  *     interval in which %NL80211_ATTR_CQM_TXE_PKTS and
  *     %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
  *     %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
+ * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
+ *     loss event
  * @__NL80211_ATTR_CQM_AFTER_LAST: internal
  * @NL80211_ATTR_CQM_MAX: highest key attribute
  */
@@ -3463,6 +3465,7 @@ enum nl80211_attr_cqm {
        NL80211_ATTR_CQM_TXE_RATE,
        NL80211_ATTR_CQM_TXE_PKTS,
        NL80211_ATTR_CQM_TXE_INTVL,
+       NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
 
        /* keep last */
        __NL80211_ATTR_CQM_AFTER_LAST,
@@ -3475,9 +3478,7 @@ enum nl80211_attr_cqm {
  *      configured threshold
  * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
  *      configured threshold
- * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss.
- *     (Note that deauth/disassoc will still follow if the AP is not
- *     available. This event might get used as roaming event, etc.)
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent)
  */
 enum nl80211_cqm_rssi_threshold_event {
        NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
index aced97d..32ffec6 100644 (file)
@@ -15,9 +15,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 /* Jon's code is based on 6lowpan implementation for Contiki which is:
index 5e97a8f..29bcafc 100644 (file)
@@ -10,6 +10,7 @@ menuconfig BT
        select CRYPTO
        select CRYPTO_BLKCIPHER
        select CRYPTO_AES
+       select CRYPTO_CMAC
        select CRYPTO_ECB
        select CRYPTO_SHA256
        help
index 886e9aa..a5432a6 100644 (file)
@@ -13,6 +13,6 @@ bluetooth_6lowpan-y := 6lowpan.o
 
 bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
        hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
-       a2mp.o amp.o
+       a2mp.o amp.o ecc.o
 
 subdir-ccflags-y += -D__CHECK_ENDIAN__
index 0a7cc56..012e3b0 100644 (file)
@@ -31,7 +31,7 @@
 #include <net/bluetooth/bluetooth.h>
 #include <linux/proc_fs.h>
 
-#define VERSION "2.19"
+#define VERSION "2.20"
 
 /* Bluetooth sockets */
 #define BT_MAX_PROTO   8
diff --git a/net/bluetooth/ecc.c b/net/bluetooth/ecc.c
new file mode 100644 (file)
index 0000000..e1709f8
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 2013, Kenneth MacKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *  * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/random.h>
+
+#include "ecc.h"
+
+/* 256-bit curve */
+#define ECC_BYTES 32
+
+#define MAX_TRIES 16
+
+/* Number of u64's needed */
+#define NUM_ECC_DIGITS (ECC_BYTES / 8)
+
+struct ecc_point {
+       u64 x[NUM_ECC_DIGITS];
+       u64 y[NUM_ECC_DIGITS];
+};
+
+typedef struct {
+       u64 m_low;
+       u64 m_high;
+} uint128_t;
+
+#define CURVE_P_32 {   0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, \
+                       0x0000000000000000ull, 0xFFFFFFFF00000001ull }
+
+#define CURVE_G_32 { \
+               {       0xF4A13945D898C296ull, 0x77037D812DEB33A0ull,   \
+                       0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull }, \
+               {       0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull,   \
+                       0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull }  \
+}
+
+#define CURVE_N_32 {   0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull,   \
+                       0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull }
+
+static u64 curve_p[NUM_ECC_DIGITS] = CURVE_P_32;
+static struct ecc_point curve_g = CURVE_G_32;
+static u64 curve_n[NUM_ECC_DIGITS] = CURVE_N_32;
+
+static void vli_clear(u64 *vli)
+{
+       int i;
+
+       for (i = 0; i < NUM_ECC_DIGITS; i++)
+               vli[i] = 0;
+}
+
+/* Returns true if vli == 0, false otherwise. */
+static bool vli_is_zero(const u64 *vli)
+{
+       int i;
+
+       for (i = 0; i < NUM_ECC_DIGITS; i++) {
+               if (vli[i])
+                       return false;
+       }
+
+       return true;
+}
+
+/* Returns nonzero if bit bit of vli is set. */
+static u64 vli_test_bit(const u64 *vli, unsigned int bit)
+{
+       return (vli[bit / 64] & ((u64) 1 << (bit % 64)));
+}
+
+/* Counts the number of 64-bit "digits" in vli. */
+static unsigned int vli_num_digits(const u64 *vli)
+{
+       int i;
+
+       /* Search from the end until we find a non-zero digit.
+        * We do it in reverse because we expect that most digits will
+        * be nonzero.
+        */
+       for (i = NUM_ECC_DIGITS - 1; i >= 0 && vli[i] == 0; i--);
+
+       return (i + 1);
+}
+
+/* Counts the number of bits required for vli. */
+static unsigned int vli_num_bits(const u64 *vli)
+{
+       unsigned int i, num_digits;
+       u64 digit;
+
+       num_digits = vli_num_digits(vli);
+       if (num_digits == 0)
+               return 0;
+
+       digit = vli[num_digits - 1];
+       for (i = 0; digit; i++)
+               digit >>= 1;
+
+       return ((num_digits - 1) * 64 + i);
+}
+
+/* Sets dest = src. */
+static void vli_set(u64 *dest, const u64 *src)
+{
+       int i;
+
+       for (i = 0; i < NUM_ECC_DIGITS; i++)
+               dest[i] = src[i];
+}
+
+/* Returns sign of left - right. */
+static int vli_cmp(const u64 *left, const u64 *right)
+{
+    int i;
+
+    for (i = NUM_ECC_DIGITS - 1; i >= 0; i--) {
+           if (left[i] > right[i])
+                   return 1;
+           else if (left[i] < right[i])
+                   return -1;
+    }
+
+    return 0;
+}
+
+/* Computes result = in << c, returning carry. Can modify in place
+ * (if result == in). 0 < shift < 64.
+ */
+static u64 vli_lshift(u64 *result, const u64 *in,
+                          unsigned int shift)
+{
+       u64 carry = 0;
+       int i;
+
+       for (i = 0; i < NUM_ECC_DIGITS; i++) {
+               u64 temp = in[i];
+
+               result[i] = (temp << shift) | carry;
+               carry = temp >> (64 - shift);
+       }
+
+       return carry;
+}
+
+/* Computes vli = vli >> 1. */
+static void vli_rshift1(u64 *vli)
+{
+       u64 *end = vli;
+       u64 carry = 0;
+
+       vli += NUM_ECC_DIGITS;
+
+       while (vli-- > end) {
+               u64 temp = *vli;
+               *vli = (temp >> 1) | carry;
+               carry = temp << 63;
+       }
+}
+
+/* Computes result = left + right, returning carry. Can modify in place. */
+static u64 vli_add(u64 *result, const u64 *left,
+                       const u64 *right)
+{
+       u64 carry = 0;
+       int i;
+
+       for (i = 0; i < NUM_ECC_DIGITS; i++) {
+               u64 sum;
+
+               sum = left[i] + right[i] + carry;
+               if (sum != left[i])
+                       carry = (sum < left[i]);
+
+               result[i] = sum;
+       }
+
+       return carry;
+}
+
+/* Computes result = left - right, returning borrow. Can modify in place. */
+static u64 vli_sub(u64 *result, const u64 *left, const u64 *right)
+{
+       u64 borrow = 0;
+       int i;
+
+       for (i = 0; i < NUM_ECC_DIGITS; i++) {
+               u64 diff;
+
+               diff = left[i] - right[i] - borrow;
+               if (diff != left[i])
+                       borrow = (diff > left[i]);
+
+               result[i] = diff;
+       }
+
+       return borrow;
+}
+
+static uint128_t mul_64_64(u64 left, u64 right)
+{
+       u64 a0 = left & 0xffffffffull;
+       u64 a1 = left >> 32;
+       u64 b0 = right & 0xffffffffull;
+       u64 b1 = right >> 32;
+       u64 m0 = a0 * b0;
+       u64 m1 = a0 * b1;
+       u64 m2 = a1 * b0;
+       u64 m3 = a1 * b1;
+       uint128_t result;
+
+       m2 += (m0 >> 32);
+       m2 += m1;
+
+       /* Overflow */
+       if (m2 < m1)
+               m3 += 0x100000000ull;
+
+       result.m_low = (m0 & 0xffffffffull) | (m2 << 32);
+       result.m_high = m3 + (m2 >> 32);
+
+       return result;
+}
+
+static uint128_t add_128_128(uint128_t a, uint128_t b)
+{
+       uint128_t result;
+
+       result.m_low = a.m_low + b.m_low;
+       result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low);
+
+       return result;
+}
+
+static void vli_mult(u64 *result, const u64 *left, const u64 *right)
+{
+       uint128_t r01 = { 0, 0 };
+       u64 r2 = 0;
+       unsigned int i, k;
+
+       /* Compute each digit of result in sequence, maintaining the
+        * carries.
+        */
+       for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) {
+               unsigned int min;
+
+               if (k < NUM_ECC_DIGITS)
+                       min = 0;
+               else
+                       min = (k + 1) - NUM_ECC_DIGITS;
+
+               for (i = min; i <= k && i < NUM_ECC_DIGITS; i++) {
+                       uint128_t product;
+
+                       product = mul_64_64(left[i], right[k - i]);
+
+                       r01 = add_128_128(r01, product);
+                       r2 += (r01.m_high < product.m_high);
+               }
+
+               result[k] = r01.m_low;
+               r01.m_low = r01.m_high;
+               r01.m_high = r2;
+               r2 = 0;
+       }
+
+       result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low;
+}
+
+static void vli_square(u64 *result, const u64 *left)
+{
+       uint128_t r01 = { 0, 0 };
+       u64 r2 = 0;
+       int i, k;
+
+       for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) {
+               unsigned int min;
+
+               if (k < NUM_ECC_DIGITS)
+                       min = 0;
+               else
+                       min = (k + 1) - NUM_ECC_DIGITS;
+
+               for (i = min; i <= k && i <= k - i; i++) {
+                       uint128_t product;
+
+                       product = mul_64_64(left[i], left[k - i]);
+
+                       if (i < k - i) {
+                               r2 += product.m_high >> 63;
+                               product.m_high = (product.m_high << 1) |
+                                                (product.m_low >> 63);
+                               product.m_low <<= 1;
+                       }
+
+                       r01 = add_128_128(r01, product);
+                       r2 += (r01.m_high < product.m_high);
+               }
+
+               result[k] = r01.m_low;
+               r01.m_low = r01.m_high;
+               r01.m_high = r2;
+               r2 = 0;
+       }
+
+       result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low;
+}
+
+/* Computes result = (left + right) % mod.
+ * Assumes that left < mod and right < mod, result != mod.
+ */
+static void vli_mod_add(u64 *result, const u64 *left, const u64 *right,
+                       const u64 *mod)
+{
+       u64 carry;
+
+       carry = vli_add(result, left, right);
+
+       /* result > mod (result = mod + remainder), so subtract mod to
+        * get remainder.
+        */
+       if (carry || vli_cmp(result, mod) >= 0)
+               vli_sub(result, result, mod);
+}
+
+/* Computes result = (left - right) % mod.
+ * Assumes that left < mod and right < mod, result != mod.
+ */
+static void vli_mod_sub(u64 *result, const u64 *left, const u64 *right,
+                       const u64 *mod)
+{
+       u64 borrow = vli_sub(result, left, right);
+
+       /* In this case, p_result == -diff == (max int) - diff.
+        * Since -x % d == d - x, we can get the correct result from
+        * result + mod (with overflow).
+        */
+       if (borrow)
+               vli_add(result, result, mod);
+}
+
+/* Computes result = product % curve_p
+   from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void vli_mmod_fast(u64 *result, const u64 *product)
+{
+       u64 tmp[NUM_ECC_DIGITS];
+       int carry;
+
+       /* t */
+       vli_set(result, product);
+
+       /* s1 */
+       tmp[0] = 0;
+       tmp[1] = product[5] & 0xffffffff00000000ull;
+       tmp[2] = product[6];
+       tmp[3] = product[7];
+       carry = vli_lshift(tmp, tmp, 1);
+       carry += vli_add(result, result, tmp);
+
+       /* s2 */
+       tmp[1] = product[6] << 32;
+       tmp[2] = (product[6] >> 32) | (product[7] << 32);
+       tmp[3] = product[7] >> 32;
+       carry += vli_lshift(tmp, tmp, 1);
+       carry += vli_add(result, result, tmp);
+
+       /* s3 */
+       tmp[0] = product[4];
+       tmp[1] = product[5] & 0xffffffff;
+       tmp[2] = 0;
+       tmp[3] = product[7];
+       carry += vli_add(result, result, tmp);
+
+       /* s4 */
+       tmp[0] = (product[4] >> 32) | (product[5] << 32);
+       tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull);
+       tmp[2] = product[7];
+       tmp[3] = (product[6] >> 32) | (product[4] << 32);
+       carry += vli_add(result, result, tmp);
+
+       /* d1 */
+       tmp[0] = (product[5] >> 32) | (product[6] << 32);
+       tmp[1] = (product[6] >> 32);
+       tmp[2] = 0;
+       tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32);
+       carry -= vli_sub(result, result, tmp);
+
+       /* d2 */
+       tmp[0] = product[6];
+       tmp[1] = product[7];
+       tmp[2] = 0;
+       tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull);
+       carry -= vli_sub(result, result, tmp);
+
+       /* d3 */
+       tmp[0] = (product[6] >> 32) | (product[7] << 32);
+       tmp[1] = (product[7] >> 32) | (product[4] << 32);
+       tmp[2] = (product[4] >> 32) | (product[5] << 32);
+       tmp[3] = (product[6] << 32);
+       carry -= vli_sub(result, result, tmp);
+
+       /* d4 */
+       tmp[0] = product[7];
+       tmp[1] = product[4] & 0xffffffff00000000ull;
+       tmp[2] = product[5];
+       tmp[3] = product[6] & 0xffffffff00000000ull;
+       carry -= vli_sub(result, result, tmp);
+
+       if (carry < 0) {
+               do {
+                       carry += vli_add(result, result, curve_p);
+               } while (carry < 0);
+       } else {
+               while (carry || vli_cmp(curve_p, result) != 1)
+                       carry -= vli_sub(result, result, curve_p);
+       }
+}
+
+/* Computes result = (left * right) % curve_p. */
+static void vli_mod_mult_fast(u64 *result, const u64 *left, const u64 *right)
+{
+       u64 product[2 * NUM_ECC_DIGITS];
+
+       vli_mult(product, left, right);
+       vli_mmod_fast(result, product);
+}
+
+/* Computes result = left^2 % curve_p. */
+static void vli_mod_square_fast(u64 *result, const u64 *left)
+{
+       u64 product[2 * NUM_ECC_DIGITS];
+
+       vli_square(product, left);
+       vli_mmod_fast(result, product);
+}
+
+#define EVEN(vli) (!(vli[0] & 1))
+/* Computes result = (1 / p_input) % mod. All VLIs are the same size.
+ * See "From Euclid's GCD to Montgomery Multiplication to the Great Divide"
+ * https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf
+ */
+static void vli_mod_inv(u64 *result, const u64 *input, const u64 *mod)
+{
+       u64 a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS];
+       u64 u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS];
+       u64 carry;
+       int cmp_result;
+
+       if (vli_is_zero(input)) {
+               vli_clear(result);
+               return;
+       }
+
+       vli_set(a, input);
+       vli_set(b, mod);
+       vli_clear(u);
+       u[0] = 1;
+       vli_clear(v);
+
+       while ((cmp_result = vli_cmp(a, b)) != 0) {
+               carry = 0;
+
+               if (EVEN(a)) {
+                       vli_rshift1(a);
+
+                       if (!EVEN(u))
+                               carry = vli_add(u, u, mod);
+
+                       vli_rshift1(u);
+                       if (carry)
+                               u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+               } else if (EVEN(b)) {
+                       vli_rshift1(b);
+
+                       if (!EVEN(v))
+                               carry = vli_add(v, v, mod);
+
+                       vli_rshift1(v);
+                       if (carry)
+                               v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+               } else if (cmp_result > 0) {
+                       vli_sub(a, a, b);
+                       vli_rshift1(a);
+
+                       if (vli_cmp(u, v) < 0)
+                               vli_add(u, u, mod);
+
+                       vli_sub(u, u, v);
+                       if (!EVEN(u))
+                               carry = vli_add(u, u, mod);
+
+                       vli_rshift1(u);
+                       if (carry)
+                               u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+               } else {
+                       vli_sub(b, b, a);
+                       vli_rshift1(b);
+
+                       if (vli_cmp(v, u) < 0)
+                               vli_add(v, v, mod);
+
+                       vli_sub(v, v, u);
+                       if (!EVEN(v))
+                               carry = vli_add(v, v, mod);
+
+                       vli_rshift1(v);
+                       if (carry)
+                               v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+               }
+       }
+
+       vli_set(result, u);
+}
+
+/* ------ Point operations ------ */
+
+/* Returns true if p_point is the point at infinity, false otherwise. */
+static bool ecc_point_is_zero(const struct ecc_point *point)
+{
+       return (vli_is_zero(point->x) && vli_is_zero(point->y));
+}
+
+/* Point multiplication algorithm using Montgomery's ladder with co-Z
+ * coordinates. From http://eprint.iacr.org/2011/338.pdf
+ */
+
+/* Double in place */
+static void ecc_point_double_jacobian(u64 *x1, u64 *y1, u64 *z1)
+{
+       /* t1 = x, t2 = y, t3 = z */
+       u64 t4[NUM_ECC_DIGITS];
+       u64 t5[NUM_ECC_DIGITS];
+
+       if (vli_is_zero(z1))
+               return;
+
+       vli_mod_square_fast(t4, y1);   /* t4 = y1^2 */
+       vli_mod_mult_fast(t5, x1, t4); /* t5 = x1*y1^2 = A */
+       vli_mod_square_fast(t4, t4);   /* t4 = y1^4 */
+       vli_mod_mult_fast(y1, y1, z1); /* t2 = y1*z1 = z3 */
+       vli_mod_square_fast(z1, z1);   /* t3 = z1^2 */
+
+       vli_mod_add(x1, x1, z1, curve_p); /* t1 = x1 + z1^2 */
+       vli_mod_add(z1, z1, z1, curve_p); /* t3 = 2*z1^2 */
+       vli_mod_sub(z1, x1, z1, curve_p); /* t3 = x1 - z1^2 */
+       vli_mod_mult_fast(x1, x1, z1);    /* t1 = x1^2 - z1^4 */
+
+       vli_mod_add(z1, x1, x1, curve_p); /* t3 = 2*(x1^2 - z1^4) */
+       vli_mod_add(x1, x1, z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */
+       if (vli_test_bit(x1, 0)) {
+               u64 carry = vli_add(x1, x1, curve_p);
+               vli_rshift1(x1);
+               x1[NUM_ECC_DIGITS - 1] |= carry << 63;
+       } else {
+               vli_rshift1(x1);
+       }
+       /* t1 = 3/2*(x1^2 - z1^4) = B */
+
+       vli_mod_square_fast(z1, x1);      /* t3 = B^2 */
+       vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - A */
+       vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */
+       vli_mod_sub(t5, t5, z1, curve_p); /* t5 = A - x3 */
+       vli_mod_mult_fast(x1, x1, t5);    /* t1 = B * (A - x3) */
+       vli_mod_sub(t4, x1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */
+
+       vli_set(x1, z1);
+       vli_set(z1, y1);
+       vli_set(y1, t4);
+}
+
+/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */
+static void apply_z(u64 *x1, u64 *y1, u64 *z)
+{
+       u64 t1[NUM_ECC_DIGITS];
+
+       vli_mod_square_fast(t1, z);    /* z^2 */
+       vli_mod_mult_fast(x1, x1, t1); /* x1 * z^2 */
+       vli_mod_mult_fast(t1, t1, z);  /* z^3 */
+       vli_mod_mult_fast(y1, y1, t1); /* y1 * z^3 */
+}
+
+/* P = (x1, y1) => 2P, (x2, y2) => P' */
+static void xycz_initial_double(u64 *x1, u64 *y1, u64 *x2, u64 *y2,
+                               u64 *p_initial_z)
+{
+       u64 z[NUM_ECC_DIGITS];
+
+       vli_set(x2, x1);
+       vli_set(y2, y1);
+
+       vli_clear(z);
+       z[0] = 1;
+
+       if (p_initial_z)
+               vli_set(z, p_initial_z);
+
+       apply_z(x1, y1, z);
+
+       ecc_point_double_jacobian(x1, y1, z);
+
+       apply_z(x2, y2, z);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ * Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3)
+ * or P => P', Q => P + Q
+ */
+static void xycz_add(u64 *x1, u64 *y1, u64 *x2, u64 *y2)
+{
+       /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+       u64 t5[NUM_ECC_DIGITS];
+
+       vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */
+       vli_mod_square_fast(t5, t5);      /* t5 = (x2 - x1)^2 = A */
+       vli_mod_mult_fast(x1, x1, t5);    /* t1 = x1*A = B */
+       vli_mod_mult_fast(x2, x2, t5);    /* t3 = x2*A = C */
+       vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */
+       vli_mod_square_fast(t5, y2);      /* t5 = (y2 - y1)^2 = D */
+
+       vli_mod_sub(t5, t5, x1, curve_p); /* t5 = D - B */
+       vli_mod_sub(t5, t5, x2, curve_p); /* t5 = D - B - C = x3 */
+       vli_mod_sub(x2, x2, x1, curve_p); /* t3 = C - B */
+       vli_mod_mult_fast(y1, y1, x2);    /* t2 = y1*(C - B) */
+       vli_mod_sub(x2, x1, t5, curve_p); /* t3 = B - x3 */
+       vli_mod_mult_fast(y2, y2, x2);    /* t4 = (y2 - y1)*(B - x3) */
+       vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */
+
+       vli_set(x2, t5);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ * Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3)
+ * or P => P - Q, Q => P + Q
+ */
+static void xycz_add_c(u64 *x1, u64 *y1, u64 *x2, u64 *y2)
+{
+       /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+       u64 t5[NUM_ECC_DIGITS];
+       u64 t6[NUM_ECC_DIGITS];
+       u64 t7[NUM_ECC_DIGITS];
+
+       vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */
+       vli_mod_square_fast(t5, t5);      /* t5 = (x2 - x1)^2 = A */
+       vli_mod_mult_fast(x1, x1, t5);    /* t1 = x1*A = B */
+       vli_mod_mult_fast(x2, x2, t5);    /* t3 = x2*A = C */
+       vli_mod_add(t5, y2, y1, curve_p); /* t4 = y2 + y1 */
+       vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */
+
+       vli_mod_sub(t6, x2, x1, curve_p); /* t6 = C - B */
+       vli_mod_mult_fast(y1, y1, t6);    /* t2 = y1 * (C - B) */
+       vli_mod_add(t6, x1, x2, curve_p); /* t6 = B + C */
+       vli_mod_square_fast(x2, y2);      /* t3 = (y2 - y1)^2 */
+       vli_mod_sub(x2, x2, t6, curve_p); /* t3 = x3 */
+
+       vli_mod_sub(t7, x1, x2, curve_p); /* t7 = B - x3 */
+       vli_mod_mult_fast(y2, y2, t7);    /* t4 = (y2 - y1)*(B - x3) */
+       vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */
+
+       vli_mod_square_fast(t7, t5);      /* t7 = (y2 + y1)^2 = F */
+       vli_mod_sub(t7, t7, t6, curve_p); /* t7 = x3' */
+       vli_mod_sub(t6, t7, x1, curve_p); /* t6 = x3' - B */
+       vli_mod_mult_fast(t6, t6, t5);    /* t6 = (y2 + y1)*(x3' - B) */
+       vli_mod_sub(y1, t6, y1, curve_p); /* t2 = y3' */
+
+       vli_set(x1, t7);
+}
+
+static void ecc_point_mult(struct ecc_point *result,
+                          const struct ecc_point *point, u64 *scalar,
+                          u64 *initial_z, int num_bits)
+{
+       /* R0 and R1 */
+       u64 rx[2][NUM_ECC_DIGITS];
+       u64 ry[2][NUM_ECC_DIGITS];
+       u64 z[NUM_ECC_DIGITS];
+       int i, nb;
+
+       vli_set(rx[1], point->x);
+       vli_set(ry[1], point->y);
+
+       xycz_initial_double(rx[1], ry[1], rx[0], ry[0], initial_z);
+
+       for (i = num_bits - 2; i > 0; i--) {
+               nb = !vli_test_bit(scalar, i);
+               xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]);
+               xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]);
+       }
+
+       nb = !vli_test_bit(scalar, 0);
+       xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]);
+
+       /* Find final 1/Z value. */
+       vli_mod_sub(z, rx[1], rx[0], curve_p); /* X1 - X0 */
+       vli_mod_mult_fast(z, z, ry[1 - nb]); /* Yb * (X1 - X0) */
+       vli_mod_mult_fast(z, z, point->x);   /* xP * Yb * (X1 - X0) */
+       vli_mod_inv(z, z, curve_p);          /* 1 / (xP * Yb * (X1 - X0)) */
+       vli_mod_mult_fast(z, z, point->y);   /* yP / (xP * Yb * (X1 - X0)) */
+       vli_mod_mult_fast(z, z, rx[1 - nb]); /* Xb * yP / (xP * Yb * (X1 - X0)) */
+       /* End 1/Z calculation */
+
+       xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]);
+
+       apply_z(rx[0], ry[0], z);
+
+       vli_set(result->x, rx[0]);
+       vli_set(result->y, ry[0]);
+}
+
+static void ecc_bytes2native(const u8 bytes[ECC_BYTES],
+                            u64 native[NUM_ECC_DIGITS])
+{
+       int i;
+
+       for (i = 0; i < NUM_ECC_DIGITS; i++) {
+               const u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+
+               native[NUM_ECC_DIGITS - 1 - i] =
+                               ((u64) digit[0] << 0) |
+                               ((u64) digit[1] << 8) |
+                               ((u64) digit[2] << 16) |
+                               ((u64) digit[3] << 24) |
+                               ((u64) digit[4] << 32) |
+                               ((u64) digit[5] << 40) |
+                               ((u64) digit[6] << 48) |
+                               ((u64) digit[7] << 56);
+       }
+}
+
+static void ecc_native2bytes(const u64 native[NUM_ECC_DIGITS],
+                            u8 bytes[ECC_BYTES])
+{
+       int i;
+
+       for (i = 0; i < NUM_ECC_DIGITS; i++) {
+               u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+
+               digit[0] = native[NUM_ECC_DIGITS - 1 - i] >> 0;
+               digit[1] = native[NUM_ECC_DIGITS - 1 - i] >> 8;
+               digit[2] = native[NUM_ECC_DIGITS - 1 - i] >> 16;
+               digit[3] = native[NUM_ECC_DIGITS - 1 - i] >> 24;
+               digit[4] = native[NUM_ECC_DIGITS - 1 - i] >> 32;
+               digit[5] = native[NUM_ECC_DIGITS - 1 - i] >> 40;
+               digit[6] = native[NUM_ECC_DIGITS - 1 - i] >> 48;
+               digit[7] = native[NUM_ECC_DIGITS - 1 - i] >> 56;
+       }
+}
+
+bool ecc_make_key(u8 public_key[64], u8 private_key[32])
+{
+       struct ecc_point pk;
+       u64 priv[NUM_ECC_DIGITS];
+       unsigned int tries = 0;
+
+       do {
+               if (tries++ >= MAX_TRIES)
+                       return false;
+
+               get_random_bytes(priv, ECC_BYTES);
+
+               if (vli_is_zero(priv))
+                       continue;
+
+               /* Make sure the private key is in the range [1, n-1]. */
+               if (vli_cmp(curve_n, priv) != 1)
+                       continue;
+
+               ecc_point_mult(&pk, &curve_g, priv, NULL, vli_num_bits(priv));
+       } while (ecc_point_is_zero(&pk));
+
+       ecc_native2bytes(priv, private_key);
+       ecc_native2bytes(pk.x, public_key);
+       ecc_native2bytes(pk.y, &public_key[32]);
+
+       return true;
+}
+
+bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32],
+                       u8 secret[32])
+{
+       u64 priv[NUM_ECC_DIGITS];
+       u64 rand[NUM_ECC_DIGITS];
+       struct ecc_point product, pk;
+
+       get_random_bytes(rand, ECC_BYTES);
+
+       ecc_bytes2native(public_key, pk.x);
+       ecc_bytes2native(&public_key[32], pk.y);
+       ecc_bytes2native(private_key, priv);
+
+       ecc_point_mult(&product, &pk, priv, rand, vli_num_bits(priv));
+
+       ecc_native2bytes(product.x, secret);
+
+       return !ecc_point_is_zero(&product);
+}
diff --git a/net/bluetooth/ecc.h b/net/bluetooth/ecc.h
new file mode 100644 (file)
index 0000000..8d6a2f4
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013, Kenneth MacKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *  * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Create a public/private key pair.
+ * Outputs:
+ *     public_key  - Will be filled in with the public key.
+ *     private_key - Will be filled in with the private key.
+ *
+ * Returns true if the key pair was generated successfully, false
+ * if an error occurred. The keys are with the LSB first.
+ */
+bool ecc_make_key(u8 public_key[64], u8 private_key[32]);
+
+/* Compute a shared secret given your secret key and someone else's
+ * public key.
+ * Note: It is recommended that you hash the result of ecdh_shared_secret
+ * before using it for symmetric encryption or HMAC.
+ *
+ * Inputs:
+ *     public_key  - The public key of the remote party
+ *     private_key - Your private key.
+ *
+ * Outputs:
+ *     secret - Will be filled in with the shared secret value.
+ *
+ * Returns true if the shared secret was generated successfully, false
+ * if an error occurred. Both input and output parameters are with the
+ * LSB first.
+ */
+bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32],
+                       u8 secret[32]);
index 96887ae..79d84b8 100644 (file)
@@ -449,6 +449,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
        conn->io_capability = hdev->io_capability;
        conn->remote_auth = 0xff;
        conn->key_type = 0xff;
+       conn->rssi = HCI_RSSI_INVALID;
        conn->tx_power = HCI_TX_POWER_INVALID;
        conn->max_tx_power = HCI_TX_POWER_INVALID;
 
index d786958..93f92a0 100644 (file)
@@ -274,15 +274,13 @@ static const struct file_operations inquiry_cache_fops = {
 static int link_keys_show(struct seq_file *f, void *ptr)
 {
        struct hci_dev *hdev = f->private;
-       struct list_head *p, *n;
+       struct link_key *key;
 
-       hci_dev_lock(hdev);
-       list_for_each_safe(p, n, &hdev->link_keys) {
-               struct link_key *key = list_entry(p, struct link_key, list);
+       rcu_read_lock();
+       list_for_each_entry_rcu(key, &hdev->link_keys, list)
                seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
                           HCI_LINK_KEY_SIZE, key->val, key->pin_len);
-       }
-       hci_dev_unlock(hdev);
+       rcu_read_unlock();
 
        return 0;
 }
@@ -408,6 +406,49 @@ static const struct file_operations force_sc_support_fops = {
        .llseek         = default_llseek,
 };
 
+static ssize_t force_lesc_support_read(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_lesc_support_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+               return -EALREADY;
+
+       change_bit(HCI_FORCE_LESC, &hdev->dbg_flags);
+
+       return count;
+}
+
+static const struct file_operations force_lesc_support_fops = {
+       .open           = simple_open,
+       .read           = force_lesc_support_read,
+       .write          = force_lesc_support_write,
+       .llseek         = default_llseek,
+};
+
 static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
                                 size_t count, loff_t *ppos)
 {
@@ -1128,6 +1169,7 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
        err = hci_req_run(&req, hci_req_sync_complete);
        if (err < 0) {
                remove_wait_queue(&hdev->req_wait_q, &wait);
+               set_current_state(TASK_RUNNING);
                return ERR_PTR(err);
        }
 
@@ -1196,6 +1238,7 @@ static int __hci_req_sync(struct hci_dev *hdev,
                hdev->req_status = 0;
 
                remove_wait_queue(&hdev->req_wait_q, &wait);
+               set_current_state(TASK_RUNNING);
 
                /* ENODATA means the HCI request command queue is empty.
                 * This can happen when a request with conditionals doesn't
@@ -1692,6 +1735,28 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
                                                 * Parameter Request
                                                 */
 
+               /* If the controller supports Extended Scanner Filter
+                * Policies, enable the correspondig event.
+                */
+               if (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY)
+                       events[1] |= 0x04;      /* LE Direct Advertising
+                                                * Report
+                                                */
+
+               /* If the controller supports the LE Read Local P-256
+                * Public Key command, enable the corresponding event.
+                */
+               if (hdev->commands[34] & 0x02)
+                       events[0] |= 0x80;      /* LE Read Local P-256
+                                                * Public Key Complete
+                                                */
+
+               /* If the controller supports the LE Generate DHKey
+                * command, enable the corresponding event.
+                */
+               if (hdev->commands[34] & 0x04)
+                       events[1] |= 0x01;      /* LE Generate DHKey Complete */
+
                hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events),
                            events);
 
@@ -1734,9 +1799,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
                hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
 
        /* Enable Secure Connections if supported and configured */
-       if ((lmp_sc_capable(hdev) ||
-            test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) &&
-           test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+       if (bredr_sc_enabled(hdev)) {
                u8 support = 0x01;
                hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
                            sizeof(support), &support);
@@ -1819,6 +1882,10 @@ static int __hci_init(struct hci_dev *hdev)
                                    hdev, &force_sc_support_fops);
                debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
                                    hdev, &sc_only_mode_fops);
+               if (lmp_le_capable(hdev))
+                       debugfs_create_file("force_lesc_support", 0644,
+                                           hdev->debugfs, hdev,
+                                           &force_lesc_support_fops);
        }
 
        if (lmp_sniff_capable(hdev)) {
@@ -2115,7 +2182,7 @@ u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
 
        BT_DBG("cache %p, %pMR", cache, &data->bdaddr);
 
-       hci_remove_remote_oob_data(hdev, &data->bdaddr);
+       hci_remove_remote_oob_data(hdev, &data->bdaddr, BDADDR_BREDR);
 
        if (!data->ssp_mode)
                flags |= MGMT_DEV_FOUND_LEGACY_PAIRING;
@@ -3099,15 +3166,11 @@ void hci_uuids_clear(struct hci_dev *hdev)
 
 void hci_link_keys_clear(struct hci_dev *hdev)
 {
-       struct list_head *p, *n;
-
-       list_for_each_safe(p, n, &hdev->link_keys) {
-               struct link_key *key;
-
-               key = list_entry(p, struct link_key, list);
+       struct link_key *key;
 
-               list_del(p);
-               kfree(key);
+       list_for_each_entry_rcu(key, &hdev->link_keys, list) {
+               list_del_rcu(&key->list);
+               kfree_rcu(key, rcu);
        }
 }
 
@@ -3135,9 +3198,14 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
 {
        struct link_key *k;
 
-       list_for_each_entry(k, &hdev->link_keys, list)
-               if (bacmp(bdaddr, &k->bdaddr) == 0)
+       rcu_read_lock();
+       list_for_each_entry_rcu(k, &hdev->link_keys, list) {
+               if (bacmp(bdaddr, &k->bdaddr) == 0) {
+                       rcu_read_unlock();
                        return k;
+               }
+       }
+       rcu_read_unlock();
 
        return NULL;
 }
@@ -3161,6 +3229,10 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
        if (!conn)
                return true;
 
+       /* BR/EDR key derived using SC from an LE link */
+       if (conn->type == LE_LINK)
+               return true;
+
        /* Neither local nor remote side had no-bonding as requirement */
        if (conn->auth_type > 0x01 && conn->remote_auth > 0x01)
                return true;
@@ -3186,37 +3258,17 @@ static u8 ltk_role(u8 type)
        return HCI_ROLE_SLAVE;
 }
 
-struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
-                            u8 role)
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                            u8 addr_type, u8 role)
 {
        struct smp_ltk *k;
 
        rcu_read_lock();
        list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
-               if (k->ediv != ediv || k->rand != rand)
-                       continue;
-
-               if (ltk_role(k->type) != role)
+               if (addr_type != k->bdaddr_type || bacmp(bdaddr, &k->bdaddr))
                        continue;
 
-               rcu_read_unlock();
-               return k;
-       }
-       rcu_read_unlock();
-
-       return NULL;
-}
-
-struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 addr_type, u8 role)
-{
-       struct smp_ltk *k;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
-               if (addr_type == k->bdaddr_type &&
-                   bacmp(bdaddr, &k->bdaddr) == 0 &&
-                   ltk_role(k->type) == role) {
+               if (smp_ltk_is_sc(k) || ltk_role(k->type) == role) {
                        rcu_read_unlock();
                        return k;
                }
@@ -3288,7 +3340,7 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
                key = kzalloc(sizeof(*key), GFP_KERNEL);
                if (!key)
                        return NULL;
-               list_add(&key->list, &hdev->link_keys);
+               list_add_rcu(&key->list, &hdev->link_keys);
        }
 
        BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
@@ -3326,7 +3378,7 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
        struct smp_ltk *key, *old_key;
        u8 role = ltk_role(type);
 
-       old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, role);
+       old_key = hci_find_ltk(hdev, bdaddr, addr_type, role);
        if (old_key)
                key = old_key;
        else {
@@ -3381,8 +3433,8 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
 
        BT_DBG("%s removing %pMR", hdev->name, bdaddr);
 
-       list_del(&key->list);
-       kfree(key);
+       list_del_rcu(&key->list);
+       kfree_rcu(key, rcu);
 
        return 0;
 }
@@ -3441,26 +3493,31 @@ static void hci_cmd_timeout(struct work_struct *work)
 }
 
 struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
-                                         bdaddr_t *bdaddr)
+                                         bdaddr_t *bdaddr, u8 bdaddr_type)
 {
        struct oob_data *data;
 
-       list_for_each_entry(data, &hdev->remote_oob_data, list)
-               if (bacmp(bdaddr, &data->bdaddr) == 0)
-                       return data;
+       list_for_each_entry(data, &hdev->remote_oob_data, list) {
+               if (bacmp(bdaddr, &data->bdaddr) != 0)
+                       continue;
+               if (data->bdaddr_type != bdaddr_type)
+                       continue;
+               return data;
+       }
 
        return NULL;
 }
 
-int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                              u8 bdaddr_type)
 {
        struct oob_data *data;
 
-       data = hci_find_remote_oob_data(hdev, bdaddr);
+       data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type);
        if (!data)
                return -ENOENT;
 
-       BT_DBG("%s removing %pMR", hdev->name, bdaddr);
+       BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type);
 
        list_del(&data->list);
        kfree(data);
@@ -3479,52 +3536,37 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev)
 }
 
 int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                           u8 *hash, u8 *rand)
+                           u8 bdaddr_type, u8 *hash192, u8 *rand192,
+                           u8 *hash256, u8 *rand256)
 {
        struct oob_data *data;
 
-       data = hci_find_remote_oob_data(hdev, bdaddr);
+       data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type);
        if (!data) {
                data = kmalloc(sizeof(*data), GFP_KERNEL);
                if (!data)
                        return -ENOMEM;
 
                bacpy(&data->bdaddr, bdaddr);
+               data->bdaddr_type = bdaddr_type;
                list_add(&data->list, &hdev->remote_oob_data);
        }
 
-       memcpy(data->hash192, hash, sizeof(data->hash192));
-       memcpy(data->rand192, rand, sizeof(data->rand192));
-
-       memset(data->hash256, 0, sizeof(data->hash256));
-       memset(data->rand256, 0, sizeof(data->rand256));
-
-       BT_DBG("%s for %pMR", hdev->name, bdaddr);
-
-       return 0;
-}
-
-int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                               u8 *hash192, u8 *rand192,
-                               u8 *hash256, u8 *rand256)
-{
-       struct oob_data *data;
-
-       data = hci_find_remote_oob_data(hdev, bdaddr);
-       if (!data) {
-               data = kmalloc(sizeof(*data), GFP_KERNEL);
-               if (!data)
-                       return -ENOMEM;
-
-               bacpy(&data->bdaddr, bdaddr);
-               list_add(&data->list, &hdev->remote_oob_data);
+       if (hash192 && rand192) {
+               memcpy(data->hash192, hash192, sizeof(data->hash192));
+               memcpy(data->rand192, rand192, sizeof(data->rand192));
+       } else {
+               memset(data->hash192, 0, sizeof(data->hash192));
+               memset(data->rand192, 0, sizeof(data->rand192));
        }
 
-       memcpy(data->hash192, hash192, sizeof(data->hash192));
-       memcpy(data->rand192, rand192, sizeof(data->rand192));
-
-       memcpy(data->hash256, hash256, sizeof(data->hash256));
-       memcpy(data->rand256, rand256, sizeof(data->rand256));
+       if (hash256 && rand256) {
+               memcpy(data->hash256, hash256, sizeof(data->hash256));
+               memcpy(data->rand256, rand256, sizeof(data->rand256));
+       } else {
+               memset(data->hash256, 0, sizeof(data->hash256));
+               memset(data->rand256, 0, sizeof(data->rand256));
+       }
 
        BT_DBG("%s for %pMR", hdev->name, bdaddr);
 
@@ -4224,6 +4266,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
        hci_remote_oob_data_clear(hdev);
        hci_bdaddr_list_clear(&hdev->le_white_list);
        hci_conn_params_clear_all(hdev);
+       hci_discovery_filter_clear(hdev);
        hci_dev_unlock(hdev);
 
        hci_dev_put(hdev);
@@ -5596,6 +5639,19 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
         */
        filter_policy = update_white_list(req);
 
+       /* When the controller is using random resolvable addresses and
+        * with that having LE privacy enabled, then controllers with
+        * Extended Scanner Filter Policies support can now enable support
+        * for handling directed advertising.
+        *
+        * So instead of using filter polices 0x00 (no whitelist)
+        * and 0x01 (whitelist enabled) use the new filter policies
+        * 0x02 (no whitelist) and 0x03 (whitelist enabled).
+        */
+       if (test_bit(HCI_PRIVACY, &hdev->dev_flags) &&
+           (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY))
+               filter_policy |= 0x02;
+
        memset(&param_cp, 0, sizeof(param_cp));
        param_cp.type = LE_SCAN_PASSIVE;
        param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
@@ -5647,6 +5703,15 @@ void hci_update_background_scan(struct hci_dev *hdev)
        if (hdev->discovery.state != DISCOVERY_STOPPED)
                return;
 
+       /* Reset RSSI and UUID filters when starting background scanning
+        * since these filters are meant for service discovery only.
+        *
+        * The Start Discovery and Start Service Discovery operations
+        * ensure to set proper values for RSSI threshold and UUID
+        * filter list. So it is safe to just reset them here.
+        */
+       hci_discovery_filter_clear(hdev);
+
        hci_req_init(&req, hdev);
 
        if (list_empty(&hdev->pend_le_conns) &&
index 844f7d1..322abbb 100644 (file)
@@ -2043,13 +2043,14 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
                data.pscan_mode         = info->pscan_mode;
                memcpy(data.dev_class, info->dev_class, 3);
                data.clock_offset       = info->clock_offset;
-               data.rssi               = 0x00;
+               data.rssi               = HCI_RSSI_INVALID;
                data.ssp_mode           = 0x00;
 
                flags = hci_inquiry_cache_update(hdev, &data, false);
 
                mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
-                                 info->dev_class, 0, flags, NULL, 0, NULL, 0);
+                                 info->dev_class, HCI_RSSI_INVALID,
+                                 flags, NULL, 0, NULL, 0);
        }
 
        hci_dev_unlock(hdev);
@@ -3191,6 +3192,38 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static void conn_set_key(struct hci_conn *conn, u8 key_type, u8 pin_len)
+{
+       if (key_type == HCI_LK_CHANGED_COMBINATION)
+               return;
+
+       conn->pin_length = pin_len;
+       conn->key_type = key_type;
+
+       switch (key_type) {
+       case HCI_LK_LOCAL_UNIT:
+       case HCI_LK_REMOTE_UNIT:
+       case HCI_LK_DEBUG_COMBINATION:
+               return;
+       case HCI_LK_COMBINATION:
+               if (pin_len == 16)
+                       conn->pending_sec_level = BT_SECURITY_HIGH;
+               else
+                       conn->pending_sec_level = BT_SECURITY_MEDIUM;
+               break;
+       case HCI_LK_UNAUTH_COMBINATION_P192:
+       case HCI_LK_UNAUTH_COMBINATION_P256:
+               conn->pending_sec_level = BT_SECURITY_MEDIUM;
+               break;
+       case HCI_LK_AUTH_COMBINATION_P192:
+               conn->pending_sec_level = BT_SECURITY_HIGH;
+               break;
+       case HCI_LK_AUTH_COMBINATION_P256:
+               conn->pending_sec_level = BT_SECURITY_FIPS;
+               break;
+       }
+}
+
 static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_link_key_req *ev = (void *) skb->data;
@@ -3217,6 +3250,8 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
        if (conn) {
+               clear_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+
                if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 ||
                     key->type == HCI_LK_UNAUTH_COMBINATION_P256) &&
                    conn->auth_type != 0xff && (conn->auth_type & 0x01)) {
@@ -3232,8 +3267,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                        goto not_found;
                }
 
-               conn->key_type = key->type;
-               conn->pin_length = key->pin_len;
+               conn_set_key(conn, key->type, key->pin_len);
        }
 
        bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -3263,16 +3297,15 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_lock(hdev);
 
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
-       if (conn) {
-               hci_conn_hold(conn);
-               conn->disc_timeout = HCI_DISCONN_TIMEOUT;
-               pin_len = conn->pin_length;
+       if (!conn)
+               goto unlock;
 
-               if (ev->key_type != HCI_LK_CHANGED_COMBINATION)
-                       conn->key_type = ev->key_type;
+       hci_conn_hold(conn);
+       conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+       hci_conn_drop(conn);
 
-               hci_conn_drop(conn);
-       }
+       set_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+       conn_set_key(conn, ev->key_type, conn->pin_length);
 
        if (!test_bit(HCI_MGMT, &hdev->dev_flags))
                goto unlock;
@@ -3282,6 +3315,12 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (!key)
                goto unlock;
 
+       /* Update connection information since adding the key will have
+        * fixed up the type in the case of changed combination keys.
+        */
+       if (ev->key_type == HCI_LK_CHANGED_COMBINATION)
+               conn_set_key(conn, key->type, key->pin_len);
+
        mgmt_new_link_key(hdev, key, persistent);
 
        /* Keep debug keys around only if the HCI_KEEP_DEBUG_KEYS flag
@@ -3291,15 +3330,16 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
         */
        if (key->type == HCI_LK_DEBUG_COMBINATION &&
            !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) {
-               list_del(&key->list);
-               kfree(key);
-       } else if (conn) {
-               if (persistent)
-                       clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
-               else
-                       set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+               list_del_rcu(&key->list);
+               kfree_rcu(key, rcu);
+               goto unlock;
        }
 
+       if (persistent)
+               clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+       else
+               set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+
 unlock:
        hci_dev_unlock(hdev);
 }
@@ -3734,7 +3774,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                cp.authentication = conn->auth_type;
 
-               if (hci_find_remote_oob_data(hdev, &conn->dst) &&
+               if (hci_find_remote_oob_data(hdev, &conn->dst, BDADDR_BREDR) &&
                    (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)))
                        cp.oob_data = 0x01;
                else
@@ -3989,9 +4029,9 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
        if (!test_bit(HCI_MGMT, &hdev->dev_flags))
                goto unlock;
 
-       data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
+       data = hci_find_remote_oob_data(hdev, &ev->bdaddr, BDADDR_BREDR);
        if (data) {
-               if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+               if (bredr_sc_enabled(hdev)) {
                        struct hci_cp_remote_oob_ext_data_reply cp;
 
                        bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -4386,7 +4426,8 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
 }
 
 static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
-                              u8 bdaddr_type, s8 rssi, u8 *data, u8 len)
+                              u8 bdaddr_type, bdaddr_t *direct_addr,
+                              u8 direct_addr_type, s8 rssi, u8 *data, u8 len)
 {
        struct discovery_state *d = &hdev->discovery;
        struct smp_irk *irk;
@@ -4394,6 +4435,32 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
        bool match;
        u32 flags;
 
+       /* If the direct address is present, then this report is from
+        * a LE Direct Advertising Report event. In that case it is
+        * important to see if the address is matching the local
+        * controller address.
+        */
+       if (direct_addr) {
+               /* Only resolvable random addresses are valid for these
+                * kind of reports and others can be ignored.
+                */
+               if (!hci_bdaddr_is_rpa(direct_addr, direct_addr_type))
+                       return;
+
+               /* If the controller is not using resolvable random
+                * addresses, then this report can be ignored.
+                */
+               if (!test_bit(HCI_PRIVACY, &hdev->dev_flags))
+                       return;
+
+               /* If the local IRK of the controller does not match
+                * with the resolvable random address provided, then
+                * this report can be ignored.
+                */
+               if (!smp_irk_matches(hdev, hdev->irk, direct_addr))
+                       return;
+       }
+
        /* Check if we need to convert to identity address */
        irk = hci_get_irk(hdev, bdaddr, bdaddr_type);
        if (irk) {
@@ -4530,7 +4597,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                rssi = ev->data[ev->length];
                process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
-                                  ev->bdaddr_type, rssi, ev->data, ev->length);
+                                  ev->bdaddr_type, NULL, 0, rssi,
+                                  ev->data, ev->length);
 
                ptr += sizeof(*ev) + ev->length + 1;
        }
@@ -4554,10 +4622,20 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (conn == NULL)
                goto not_found;
 
-       ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->role);
-       if (ltk == NULL)
+       ltk = hci_find_ltk(hdev, &conn->dst, conn->dst_type, conn->role);
+       if (!ltk)
                goto not_found;
 
+       if (smp_ltk_is_sc(ltk)) {
+               /* With SC both EDiv and Rand are set to zero */
+               if (ev->ediv || ev->rand)
+                       goto not_found;
+       } else {
+               /* For non-SC keys check that EDiv and Rand match */
+               if (ev->ediv != ltk->ediv || ev->rand != ltk->rand)
+                       goto not_found;
+       }
+
        memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
        cp.handle = cpu_to_le16(conn->handle);
 
@@ -4661,6 +4739,27 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev,
        hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp);
 }
 
+static void hci_le_direct_adv_report_evt(struct hci_dev *hdev,
+                                        struct sk_buff *skb)
+{
+       u8 num_reports = skb->data[0];
+       void *ptr = &skb->data[1];
+
+       hci_dev_lock(hdev);
+
+       while (num_reports--) {
+               struct hci_ev_le_direct_adv_info *ev = ptr;
+
+               process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
+                                  ev->bdaddr_type, &ev->direct_addr,
+                                  ev->direct_addr_type, ev->rssi, NULL, 0);
+
+               ptr += sizeof(*ev);
+       }
+
+       hci_dev_unlock(hdev);
+}
+
 static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_le_meta *le_ev = (void *) skb->data;
@@ -4688,6 +4787,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_le_remote_conn_param_req_evt(hdev, skb);
                break;
 
+       case HCI_EV_LE_DIRECT_ADV_REPORT:
+               hci_le_direct_adv_report_evt(hdev, skb);
+               break;
+
        default:
                break;
        }
index 8e12731..a8da7ea 100644 (file)
@@ -46,7 +46,6 @@
 bool disable_ertm;
 
 static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
-static u8 l2cap_fixed_chan[8] = { L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS, };
 
 static LIST_HEAD(chan_list);
 static DEFINE_RWLOCK(chan_list_lock);
@@ -840,7 +839,10 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
        if (!skb)
                return;
 
-       if (lmp_no_flush_capable(conn->hcon->hdev))
+       /* Use NO_FLUSH if supported or we have an LE link (which does
+        * not support auto-flushing packets) */
+       if (lmp_no_flush_capable(conn->hcon->hdev) ||
+           conn->hcon->type == LE_LINK)
                flags = ACL_START_NO_FLUSH;
        else
                flags = ACL_START;
@@ -874,8 +876,13 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
                return;
        }
 
-       if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
-           lmp_no_flush_capable(hcon->hdev))
+       /* Use NO_FLUSH for LE links (where this is the only option) or
+        * if the BR/EDR link supports it and flushing has not been
+        * explicitly requested (through FLAG_FLUSHABLE).
+        */
+       if (hcon->type == LE_LINK ||
+           (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
+            lmp_no_flush_capable(hcon->hdev)))
                flags = ACL_START_NO_FLUSH;
        else
                flags = ACL_START;
@@ -1112,10 +1119,10 @@ static bool __amp_capable(struct l2cap_chan *chan)
        struct hci_dev *hdev;
        bool amp_available = false;
 
-       if (!conn->hs_enabled)
+       if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
                return false;
 
-       if (!(conn->fixed_chan_mask & L2CAP_FC_A2MP))
+       if (!(conn->remote_fixed_chan & L2CAP_FC_A2MP))
                return false;
 
        read_lock(&hci_dev_list_lock);
@@ -3088,12 +3095,14 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
 
 static inline bool __l2cap_ews_supported(struct l2cap_conn *conn)
 {
-       return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
+       return ((conn->local_fixed_chan & L2CAP_FC_A2MP) &&
+               (conn->feat_mask & L2CAP_FEAT_EXT_WINDOW));
 }
 
 static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
 {
-       return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
+       return ((conn->local_fixed_chan & L2CAP_FC_A2MP) &&
+               (conn->feat_mask & L2CAP_FEAT_EXT_FLOW));
 }
 
 static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
@@ -3322,7 +3331,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
                        break;
 
                case L2CAP_CONF_EWS:
-                       if (!chan->conn->hs_enabled)
+                       if (!(chan->conn->local_fixed_chan & L2CAP_FC_A2MP))
                                return -ECONNREFUSED;
 
                        set_bit(FLAG_EXT_CTRL, &chan->flags);
@@ -4326,7 +4335,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
                if (!disable_ertm)
                        feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
                                | L2CAP_FEAT_FCS;
-               if (conn->hs_enabled)
+               if (conn->local_fixed_chan & L2CAP_FC_A2MP)
                        feat_mask |= L2CAP_FEAT_EXT_FLOW
                                | L2CAP_FEAT_EXT_WINDOW;
 
@@ -4337,14 +4346,10 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
                u8 buf[12];
                struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
 
-               if (conn->hs_enabled)
-                       l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
-               else
-                       l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
-
                rsp->type   = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
                rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
-               memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan));
+               rsp->data[0] = conn->local_fixed_chan;
+               memset(rsp->data + 1, 0, 7);
                l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
                               buf);
        } else {
@@ -4410,7 +4415,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
                break;
 
        case L2CAP_IT_FIXED_CHAN:
-               conn->fixed_chan_mask = rsp->data[0];
+               conn->remote_fixed_chan = rsp->data[0];
                conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
                conn->info_ident = 0;
 
@@ -4434,7 +4439,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
        if (cmd_len != sizeof(*req))
                return -EPROTO;
 
-       if (!conn->hs_enabled)
+       if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
                return -EINVAL;
 
        psm = le16_to_cpu(req->psm);
@@ -4864,7 +4869,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
 
        BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
 
-       if (!conn->hs_enabled)
+       if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
                return -EINVAL;
 
        chan = l2cap_get_chan_by_dcid(conn, icid);
@@ -6956,9 +6961,15 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
 
        conn->feat_mask = 0;
 
-       if (hcon->type == ACL_LINK)
-               conn->hs_enabled = test_bit(HCI_HS_ENABLED,
-                                           &hcon->hdev->dev_flags);
+       conn->local_fixed_chan = L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS;
+
+       if (hcon->type == ACL_LINK &&
+           test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags))
+               conn->local_fixed_chan |= L2CAP_FC_A2MP;
+
+       if (bredr_sc_enabled(hcon->hdev) &&
+           test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
+               conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
 
        mutex_init(&conn->ident_lock);
        mutex_init(&conn->chan_lock);
index f3e4a16..7384f11 100644 (file)
@@ -35,7 +35,7 @@
 #include "smp.h"
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  7
+#define MGMT_REVISION  8
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -93,6 +93,7 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_READ_CONFIG_INFO,
        MGMT_OP_SET_EXTERNAL_CONFIG,
        MGMT_OP_SET_PUBLIC_ADDRESS,
+       MGMT_OP_START_SERVICE_DISCOVERY,
 };
 
 static const u16 mgmt_events[] = {
@@ -134,8 +135,10 @@ struct pending_cmd {
        u16 opcode;
        int index;
        void *param;
+       size_t param_len;
        struct sock *sk;
        void *user_data;
+       void (*cmd_complete)(struct pending_cmd *cmd, u8 status);
 };
 
 /* HCI to MGMT error code conversion table */
@@ -574,6 +577,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
        if (lmp_le_capable(hdev)) {
                settings |= MGMT_SETTING_LE;
                settings |= MGMT_SETTING_ADVERTISING;
+               settings |= MGMT_SETTING_SECURE_CONN;
                settings |= MGMT_SETTING_PRIVACY;
        }
 
@@ -1202,14 +1206,13 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
        cmd->opcode = opcode;
        cmd->index = hdev->id;
 
-       cmd->param = kmalloc(len, GFP_KERNEL);
+       cmd->param = kmemdup(data, len, GFP_KERNEL);
        if (!cmd->param) {
                kfree(cmd);
                return NULL;
        }
 
-       if (data)
-               memcpy(cmd->param, data, len);
+       cmd->param_len = len;
 
        cmd->sk = sk;
        sock_hold(sk);
@@ -1469,6 +1472,32 @@ static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
        mgmt_pending_remove(cmd);
 }
 
+static void cmd_complete_rsp(struct pending_cmd *cmd, void *data)
+{
+       if (cmd->cmd_complete) {
+               u8 *status = data;
+
+               cmd->cmd_complete(cmd, *status);
+               mgmt_pending_remove(cmd);
+
+               return;
+       }
+
+       cmd_status_rsp(cmd, data);
+}
+
+static void generic_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+       cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
+                    cmd->param_len);
+}
+
+static void addr_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+       cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
+                    sizeof(struct mgmt_addr_info));
+}
+
 static u8 mgmt_bredr_support(struct hci_dev *hdev)
 {
        if (!lmp_bredr_capable(hdev))
@@ -2792,6 +2821,8 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       cmd->cmd_complete = addr_cmd_complete;
+
        dc.handle = cpu_to_le16(conn->handle);
        dc.reason = 0x13; /* Remote User Terminated Connection */
        err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
@@ -2855,6 +2886,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
+       cmd->cmd_complete = generic_cmd_complete;
+
        err = hci_disconnect(conn, HCI_ERROR_REMOTE_USER_TERM);
        if (err < 0)
                mgmt_pending_remove(cmd);
@@ -3007,6 +3040,8 @@ static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
+       cmd->cmd_complete = addr_cmd_complete;
+
        bacpy(&reply.bdaddr, &cp->addr.bdaddr);
        reply.pin_len = cp->pin_len;
        memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
@@ -3096,7 +3131,7 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete)
 
        cmd = find_pairing(conn);
        if (cmd)
-               pairing_complete(cmd, status);
+               cmd->cmd_complete(cmd, status);
 }
 
 static void pairing_complete_cb(struct hci_conn *conn, u8 status)
@@ -3109,7 +3144,7 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
        if (!cmd)
                BT_DBG("Unable to find a pending command");
        else
-               pairing_complete(cmd, mgmt_status(status));
+               cmd->cmd_complete(cmd, mgmt_status(status));
 }
 
 static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
@@ -3125,7 +3160,7 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
        if (!cmd)
                BT_DBG("Unable to find a pending command");
        else
-               pairing_complete(cmd, mgmt_status(status));
+               cmd->cmd_complete(cmd, mgmt_status(status));
 }
 
 static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -3222,6 +3257,8 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       cmd->cmd_complete = pairing_complete;
+
        /* For LE, just connecting isn't a proof that the pairing finished */
        if (cp->addr.type == BDADDR_BREDR) {
                conn->connect_cfm_cb = pairing_complete_cb;
@@ -3338,6 +3375,8 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
                goto done;
        }
 
+       cmd->cmd_complete = addr_cmd_complete;
+
        /* Continue with pairing via HCI */
        if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
                struct hci_cp_user_passkey_reply cp;
@@ -3562,7 +3601,7 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
                goto unlock;
        }
 
-       if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+       if (bredr_sc_enabled(hdev))
                err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA,
                                   0, NULL);
        else
@@ -3598,7 +3637,8 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
                }
 
                err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
-                                             cp->hash, cp->rand);
+                                             cp->addr.type, cp->hash,
+                                             cp->rand, NULL, NULL);
                if (err < 0)
                        status = MGMT_STATUS_FAILED;
                else
@@ -3608,6 +3648,7 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
                                   status, &cp->addr, sizeof(cp->addr));
        } else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) {
                struct mgmt_cp_add_remote_oob_ext_data *cp = data;
+               u8 *rand192, *hash192;
                u8 status;
 
                if (cp->addr.type != BDADDR_BREDR) {
@@ -3618,9 +3659,17 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
                        goto unlock;
                }
 
-               err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr,
-                                                 cp->hash192, cp->rand192,
-                                                 cp->hash256, cp->rand256);
+               if (bdaddr_type_is_le(cp->addr.type)) {
+                       rand192 = NULL;
+                       hash192 = NULL;
+               } else {
+                       rand192 = cp->rand192;
+                       hash192 = cp->hash192;
+               }
+
+               err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
+                                             cp->addr.type, hash192, rand192,
+                                             cp->hash256, cp->rand256);
                if (err < 0)
                        status = MGMT_STATUS_FAILED;
                else
@@ -3661,7 +3710,7 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
                goto done;
        }
 
-       err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
+       err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr, cp->addr.type);
        if (err < 0)
                status = MGMT_STATUS_INVALID_PARAMS;
        else
@@ -3675,64 +3724,150 @@ done:
        return err;
 }
 
-static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
+static bool trigger_discovery(struct hci_request *req, u8 *status)
 {
-       struct pending_cmd *cmd;
-       u8 type;
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_scan_param param_cp;
+       struct hci_cp_le_set_scan_enable enable_cp;
+       struct hci_cp_inquiry inq_cp;
+       /* General inquiry access code (GIAC) */
+       u8 lap[3] = { 0x33, 0x8b, 0x9e };
+       u8 own_addr_type;
        int err;
 
-       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+       switch (hdev->discovery.type) {
+       case DISCOV_TYPE_BREDR:
+               *status = mgmt_bredr_support(hdev);
+               if (*status)
+                       return false;
 
-       cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
-       if (!cmd)
-               return -ENOENT;
+               if (test_bit(HCI_INQUIRY, &hdev->flags)) {
+                       *status = MGMT_STATUS_BUSY;
+                       return false;
+               }
 
-       type = hdev->discovery.type;
+               hci_inquiry_cache_flush(hdev);
 
-       err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
-                          &type, sizeof(type));
-       mgmt_pending_remove(cmd);
+               memset(&inq_cp, 0, sizeof(inq_cp));
+               memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
+               inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
+               hci_req_add(req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
+               break;
 
-       return err;
+       case DISCOV_TYPE_LE:
+       case DISCOV_TYPE_INTERLEAVED:
+               *status = mgmt_le_support(hdev);
+               if (*status)
+                       return false;
+
+               if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
+                   !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+                       *status = MGMT_STATUS_NOT_SUPPORTED;
+                       return false;
+               }
+
+               if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
+                       /* Don't let discovery abort an outgoing
+                        * connection attempt that's using directed
+                        * advertising.
+                        */
+                       if (hci_conn_hash_lookup_state(hdev, LE_LINK,
+                                                      BT_CONNECT)) {
+                               *status = MGMT_STATUS_REJECTED;
+                               return false;
+                       }
+
+                       disable_advertising(req);
+               }
+
+               /* If controller is scanning, it means the background scanning
+                * is running. Thus, we should temporarily stop it in order to
+                * set the discovery scanning parameters.
+                */
+               if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+                       hci_req_add_le_scan_disable(req);
+
+               memset(&param_cp, 0, sizeof(param_cp));
+
+               /* All active scans will be done with either a resolvable
+                * private address (when privacy feature has been enabled)
+                * or unresolvable private address.
+                */
+               err = hci_update_random_address(req, true, &own_addr_type);
+               if (err < 0) {
+                       *status = MGMT_STATUS_FAILED;
+                       return false;
+               }
+
+               param_cp.type = LE_SCAN_ACTIVE;
+               param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
+               param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+               param_cp.own_address_type = own_addr_type;
+               hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+                           &param_cp);
+
+               memset(&enable_cp, 0, sizeof(enable_cp));
+               enable_cp.enable = LE_SCAN_ENABLE;
+               enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+               hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+                           &enable_cp);
+               break;
+
+       default:
+               *status = MGMT_STATUS_INVALID_PARAMS;
+               return false;
+       }
+
+       return true;
 }
 
 static void start_discovery_complete(struct hci_dev *hdev, u8 status)
 {
-       unsigned long timeout = 0;
+       struct pending_cmd *cmd;
+       unsigned long timeout;
 
        BT_DBG("status %d", status);
 
+       hci_dev_lock(hdev);
+
+       cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+       if (!cmd)
+               cmd = mgmt_pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
+
+       if (cmd) {
+               cmd->cmd_complete(cmd, mgmt_status(status));
+               mgmt_pending_remove(cmd);
+       }
+
        if (status) {
-               hci_dev_lock(hdev);
-               mgmt_start_discovery_failed(hdev, status);
-               hci_dev_unlock(hdev);
-               return;
+               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               goto unlock;
        }
 
-       hci_dev_lock(hdev);
        hci_discovery_set_state(hdev, DISCOVERY_FINDING);
-       hci_dev_unlock(hdev);
 
        switch (hdev->discovery.type) {
        case DISCOV_TYPE_LE:
                timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
                break;
-
        case DISCOV_TYPE_INTERLEAVED:
                timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
                break;
-
        case DISCOV_TYPE_BREDR:
+               timeout = 0;
                break;
-
        default:
                BT_ERR("Invalid discovery type %d", hdev->discovery.type);
+               timeout = 0;
+               break;
        }
 
-       if (!timeout)
-               return;
+       if (timeout)
+               queue_delayed_work(hdev->workqueue,
+                                  &hdev->le_scan_disable, timeout);
 
-       queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout);
+unlock:
+       hci_dev_unlock(hdev);
 }
 
 static int start_discovery(struct sock *sk, struct hci_dev *hdev,
@@ -3740,13 +3875,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
 {
        struct mgmt_cp_start_discovery *cp = data;
        struct pending_cmd *cmd;
-       struct hci_cp_le_set_scan_param param_cp;
-       struct hci_cp_le_set_scan_enable enable_cp;
-       struct hci_cp_inquiry inq_cp;
        struct hci_request req;
-       /* General inquiry access code (GIAC) */
-       u8 lap[3] = { 0x33, 0x8b, 0x9e };
-       u8 status, own_addr_type;
+       u8 status;
        int err;
 
        BT_DBG("%s", hdev->name);
@@ -3760,184 +3890,182 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                goto failed;
        }
 
-       if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
+       if (hdev->discovery.state != DISCOVERY_STOPPED ||
+           test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
                err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
                                   MGMT_STATUS_BUSY, &cp->type,
                                   sizeof(cp->type));
                goto failed;
        }
 
-       if (hdev->discovery.state != DISCOVERY_STOPPED) {
-               err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-                                  MGMT_STATUS_BUSY, &cp->type,
-                                  sizeof(cp->type));
-               goto failed;
-       }
-
-       cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
+       cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
        }
 
+       cmd->cmd_complete = generic_cmd_complete;
+
+       /* Clear the discovery filter first to free any previously
+        * allocated memory for the UUID list.
+        */
+       hci_discovery_filter_clear(hdev);
+
        hdev->discovery.type = cp->type;
+       hdev->discovery.report_invalid_rssi = false;
 
        hci_req_init(&req, hdev);
 
-       switch (hdev->discovery.type) {
-       case DISCOV_TYPE_BREDR:
-               status = mgmt_bredr_support(hdev);
-               if (status) {
-                       err = cmd_complete(sk, hdev->id,
-                                          MGMT_OP_START_DISCOVERY, status,
-                                          &cp->type, sizeof(cp->type));
-                       mgmt_pending_remove(cmd);
-                       goto failed;
-               }
+       if (!trigger_discovery(&req, &status)) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                  status, &cp->type, sizeof(cp->type));
+               mgmt_pending_remove(cmd);
+               goto failed;
+       }
 
-               if (test_bit(HCI_INQUIRY, &hdev->flags)) {
-                       err = cmd_complete(sk, hdev->id,
-                                          MGMT_OP_START_DISCOVERY,
-                                          MGMT_STATUS_BUSY, &cp->type,
-                                          sizeof(cp->type));
-                       mgmt_pending_remove(cmd);
-                       goto failed;
-               }
+       err = hci_req_run(&req, start_discovery_complete);
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               goto failed;
+       }
 
-               hci_inquiry_cache_flush(hdev);
+       hci_discovery_set_state(hdev, DISCOVERY_STARTING);
 
-               memset(&inq_cp, 0, sizeof(inq_cp));
-               memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
-               inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
-               hci_req_add(&req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
-               break;
+failed:
+       hci_dev_unlock(hdev);
+       return err;
+}
 
-       case DISCOV_TYPE_LE:
-       case DISCOV_TYPE_INTERLEAVED:
-               status = mgmt_le_support(hdev);
-               if (status) {
-                       err = cmd_complete(sk, hdev->id,
-                                          MGMT_OP_START_DISCOVERY, status,
-                                          &cp->type, sizeof(cp->type));
-                       mgmt_pending_remove(cmd);
-                       goto failed;
-               }
+static void service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+       cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, 1);
+}
 
-               if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
-                   !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
-                       err = cmd_complete(sk, hdev->id,
-                                          MGMT_OP_START_DISCOVERY,
-                                          MGMT_STATUS_NOT_SUPPORTED,
-                                          &cp->type, sizeof(cp->type));
-                       mgmt_pending_remove(cmd);
-                       goto failed;
-               }
+static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
+                                  void *data, u16 len)
+{
+       struct mgmt_cp_start_service_discovery *cp = data;
+       struct pending_cmd *cmd;
+       struct hci_request req;
+       const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
+       u16 uuid_count, expected_len;
+       u8 status;
+       int err;
 
-               if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
-                       /* Don't let discovery abort an outgoing
-                        * connection attempt that's using directed
-                        * advertising.
-                        */
-                       if (hci_conn_hash_lookup_state(hdev, LE_LINK,
-                                                      BT_CONNECT)) {
-                               err = cmd_complete(sk, hdev->id,
-                                                  MGMT_OP_START_DISCOVERY,
-                                                  MGMT_STATUS_REJECTED,
-                                                  &cp->type,
-                                                  sizeof(cp->type));
-                               mgmt_pending_remove(cmd);
-                               goto failed;
-                       }
+       BT_DBG("%s", hdev->name);
 
-                       disable_advertising(&req);
-               }
+       hci_dev_lock(hdev);
 
-               /* If controller is scanning, it means the background scanning
-                * is running. Thus, we should temporarily stop it in order to
-                * set the discovery scanning parameters.
-                */
-               if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
-                       hci_req_add_le_scan_disable(&req);
+       if (!hdev_is_powered(hdev)) {
+               err = cmd_complete(sk, hdev->id,
+                                  MGMT_OP_START_SERVICE_DISCOVERY,
+                                  MGMT_STATUS_NOT_POWERED,
+                                  &cp->type, sizeof(cp->type));
+               goto failed;
+       }
 
-               memset(&param_cp, 0, sizeof(param_cp));
+       if (hdev->discovery.state != DISCOVERY_STOPPED ||
+           test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
+               err = cmd_complete(sk, hdev->id,
+                                  MGMT_OP_START_SERVICE_DISCOVERY,
+                                  MGMT_STATUS_BUSY, &cp->type,
+                                  sizeof(cp->type));
+               goto failed;
+       }
 
-               /* All active scans will be done with either a resolvable
-                * private address (when privacy feature has been enabled)
-                * or unresolvable private address.
-                */
-               err = hci_update_random_address(&req, true, &own_addr_type);
-               if (err < 0) {
+       uuid_count = __le16_to_cpu(cp->uuid_count);
+       if (uuid_count > max_uuid_count) {
+               BT_ERR("service_discovery: too big uuid_count value %u",
+                      uuid_count);
+               err = cmd_complete(sk, hdev->id,
+                                  MGMT_OP_START_SERVICE_DISCOVERY,
+                                  MGMT_STATUS_INVALID_PARAMS, &cp->type,
+                                  sizeof(cp->type));
+               goto failed;
+       }
+
+       expected_len = sizeof(*cp) + uuid_count * 16;
+       if (expected_len != len) {
+               BT_ERR("service_discovery: expected %u bytes, got %u bytes",
+                      expected_len, len);
+               err = cmd_complete(sk, hdev->id,
+                                  MGMT_OP_START_SERVICE_DISCOVERY,
+                                  MGMT_STATUS_INVALID_PARAMS, &cp->type,
+                                  sizeof(cp->type));
+               goto failed;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY,
+                              hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto failed;
+       }
+
+       cmd->cmd_complete = service_discovery_cmd_complete;
+
+       /* Clear the discovery filter first to free any previously
+        * allocated memory for the UUID list.
+        */
+       hci_discovery_filter_clear(hdev);
+
+       hdev->discovery.type = cp->type;
+       hdev->discovery.rssi = cp->rssi;
+       hdev->discovery.uuid_count = uuid_count;
+
+       if (uuid_count > 0) {
+               hdev->discovery.uuids = kmemdup(cp->uuids, uuid_count * 16,
+                                               GFP_KERNEL);
+               if (!hdev->discovery.uuids) {
                        err = cmd_complete(sk, hdev->id,
-                                          MGMT_OP_START_DISCOVERY,
+                                          MGMT_OP_START_SERVICE_DISCOVERY,
                                           MGMT_STATUS_FAILED,
                                           &cp->type, sizeof(cp->type));
                        mgmt_pending_remove(cmd);
                        goto failed;
                }
+       }
 
-               param_cp.type = LE_SCAN_ACTIVE;
-               param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
-               param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
-               param_cp.own_address_type = own_addr_type;
-               hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
-                           &param_cp);
-
-               memset(&enable_cp, 0, sizeof(enable_cp));
-               enable_cp.enable = LE_SCAN_ENABLE;
-               enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
-               hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
-                           &enable_cp);
-               break;
+       hci_req_init(&req, hdev);
 
-       default:
-               err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-                                  MGMT_STATUS_INVALID_PARAMS,
-                                  &cp->type, sizeof(cp->type));
+       if (!trigger_discovery(&req, &status)) {
+               err = cmd_complete(sk, hdev->id,
+                                  MGMT_OP_START_SERVICE_DISCOVERY,
+                                  status, &cp->type, sizeof(cp->type));
                mgmt_pending_remove(cmd);
                goto failed;
        }
 
        err = hci_req_run(&req, start_discovery_complete);
-       if (err < 0)
+       if (err < 0) {
                mgmt_pending_remove(cmd);
-       else
-               hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+               goto failed;
+       }
+
+       hci_discovery_set_state(hdev, DISCOVERY_STARTING);
 
 failed:
        hci_dev_unlock(hdev);
        return err;
 }
 
-static int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
+static void stop_discovery_complete(struct hci_dev *hdev, u8 status)
 {
        struct pending_cmd *cmd;
-       int err;
 
-       cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
-       if (!cmd)
-               return -ENOENT;
-
-       err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
-                          &hdev->discovery.type, sizeof(hdev->discovery.type));
-       mgmt_pending_remove(cmd);
-
-       return err;
-}
-
-static void stop_discovery_complete(struct hci_dev *hdev, u8 status)
-{
        BT_DBG("status %d", status);
 
        hci_dev_lock(hdev);
 
-       if (status) {
-               mgmt_stop_discovery_failed(hdev, status);
-               goto unlock;
+       cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
+       if (cmd) {
+               cmd->cmd_complete(cmd, mgmt_status(status));
+               mgmt_pending_remove(cmd);
        }
 
-       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+       if (!status)
+               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
 
-unlock:
        hci_dev_unlock(hdev);
 }
 
@@ -3967,12 +4095,14 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
+       cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto unlock;
        }
 
+       cmd->cmd_complete = generic_cmd_complete;
+
        hci_req_init(&req, hdev);
 
        hci_stop_discovery(&req);
@@ -4572,18 +4702,13 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
 {
        struct mgmt_mode *cp = data;
        struct pending_cmd *cmd;
-       u8 val, status;
+       u8 val;
        int err;
 
        BT_DBG("request for %s", hdev->name);
 
-       status = mgmt_bredr_support(hdev);
-       if (status)
-               return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
-                                 status);
-
-       if (!lmp_sc_capable(hdev) &&
-           !test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+           !lmp_sc_capable(hdev) && !test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
@@ -4593,7 +4718,10 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
 
        hci_dev_lock(hdev);
 
-       if (!hdev_is_powered(hdev)) {
+       if (!hdev_is_powered(hdev) ||
+           (!lmp_sc_capable(hdev) &&
+            !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) ||
+           !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
                bool changed;
 
                if (cp->val) {
@@ -4910,18 +5038,26 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                else
                        addr_type = ADDR_LE_DEV_RANDOM;
 
-               if (key->master)
-                       type = SMP_LTK;
-               else
-                       type = SMP_LTK_SLAVE;
-
                switch (key->type) {
                case MGMT_LTK_UNAUTHENTICATED:
                        authenticated = 0x00;
+                       type = key->master ? SMP_LTK : SMP_LTK_SLAVE;
                        break;
                case MGMT_LTK_AUTHENTICATED:
                        authenticated = 0x01;
+                       type = key->master ? SMP_LTK : SMP_LTK_SLAVE;
+                       break;
+               case MGMT_LTK_P256_UNAUTH:
+                       authenticated = 0x00;
+                       type = SMP_LTK_P256;
                        break;
+               case MGMT_LTK_P256_AUTH:
+                       authenticated = 0x01;
+                       type = SMP_LTK_P256;
+                       break;
+               case MGMT_LTK_P256_DEBUG:
+                       authenticated = 0x00;
+                       type = SMP_LTK_P256_DEBUG;
                default:
                        continue;
                }
@@ -4939,67 +5075,42 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
        return err;
 }
 
-struct cmd_conn_lookup {
-       struct hci_conn *conn;
-       bool valid_tx_power;
-       u8 mgmt_status;
-};
-
-static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
+static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
 {
-       struct cmd_conn_lookup *match = data;
-       struct mgmt_cp_get_conn_info *cp;
-       struct mgmt_rp_get_conn_info rp;
        struct hci_conn *conn = cmd->user_data;
+       struct mgmt_rp_get_conn_info rp;
 
-       if (conn != match->conn)
-               return;
-
-       cp = (struct mgmt_cp_get_conn_info *) cmd->param;
-
-       memset(&rp, 0, sizeof(rp));
-       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
-       rp.addr.type = cp->addr.type;
+       memcpy(&rp.addr, cmd->param, sizeof(rp.addr));
 
-       if (!match->mgmt_status) {
+       if (status == MGMT_STATUS_SUCCESS) {
                rp.rssi = conn->rssi;
-
-               if (match->valid_tx_power) {
-                       rp.tx_power = conn->tx_power;
-                       rp.max_tx_power = conn->max_tx_power;
-               } else {
-                       rp.tx_power = HCI_TX_POWER_INVALID;
-                       rp.max_tx_power = HCI_TX_POWER_INVALID;
-               }
+               rp.tx_power = conn->tx_power;
+               rp.max_tx_power = conn->max_tx_power;
+       } else {
+               rp.rssi = HCI_RSSI_INVALID;
+               rp.tx_power = HCI_TX_POWER_INVALID;
+               rp.max_tx_power = HCI_TX_POWER_INVALID;
        }
 
-       cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
-                    match->mgmt_status, &rp, sizeof(rp));
+       cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
+                    &rp, sizeof(rp));
 
        hci_conn_drop(conn);
        hci_conn_put(conn);
-
-       mgmt_pending_remove(cmd);
 }
 
-static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
+static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status)
 {
        struct hci_cp_read_rssi *cp;
+       struct pending_cmd *cmd;
        struct hci_conn *conn;
-       struct cmd_conn_lookup match;
        u16 handle;
+       u8 status;
 
-       BT_DBG("status 0x%02x", status);
+       BT_DBG("status 0x%02x", hci_status);
 
        hci_dev_lock(hdev);
 
-       /* TX power data is valid in case request completed successfully,
-        * otherwise we assume it's not valid. At the moment we assume that
-        * either both or none of current and max values are valid to keep code
-        * simple.
-        */
-       match.valid_tx_power = !status;
-
        /* Commands sent in request are either Read RSSI or Read Transmit Power
         * Level so we check which one was last sent to retrieve connection
         * handle.  Both commands have handle as first parameter so it's safe to
@@ -5012,29 +5123,29 @@ static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
        cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
        if (!cp) {
                cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
+       } else {
+               status = mgmt_status(hci_status);
        }
 
        if (!cp) {
-               BT_ERR("invalid sent_cmd in response");
+               BT_ERR("invalid sent_cmd in conn_info response");
                goto unlock;
        }
 
        handle = __le16_to_cpu(cp->handle);
        conn = hci_conn_hash_lookup_handle(hdev, handle);
        if (!conn) {
-               BT_ERR("unknown handle (%d) in response", handle);
+               BT_ERR("unknown handle (%d) in conn_info response", handle);
                goto unlock;
        }
 
-       match.conn = conn;
-       match.mgmt_status = mgmt_status(status);
+       cmd = mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn);
+       if (!cmd)
+               goto unlock;
 
-       /* Cache refresh is complete, now reply for mgmt request for given
-        * connection only.
-        */
-       mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
-                            get_conn_info_complete, &match);
+       cmd->cmd_complete(cmd, status);
+       mgmt_pending_remove(cmd);
 
 unlock:
        hci_dev_unlock(hdev);
@@ -5080,6 +5191,12 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       if (mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                  MGMT_STATUS_BUSY, &rp, sizeof(rp));
+               goto unlock;
+       }
+
        /* To avoid client trying to guess when to poll again for information we
         * calculate conn info age as random value between min/max set in hdev.
         */
@@ -5135,6 +5252,7 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
 
                hci_conn_hold(conn);
                cmd->user_data = hci_conn_get(conn);
+               cmd->cmd_complete = conn_info_cmd_complete;
 
                conn->conn_info_timestamp = jiffies;
        } else {
@@ -5152,10 +5270,40 @@ unlock:
        return err;
 }
 
-static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
+static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
 {
-       struct mgmt_cp_get_clock_info *cp;
+       struct hci_conn *conn = cmd->user_data;
        struct mgmt_rp_get_clock_info rp;
+       struct hci_dev *hdev;
+
+       memset(&rp, 0, sizeof(rp));
+       memcpy(&rp.addr, &cmd->param, sizeof(rp.addr));
+
+       if (status)
+               goto complete;
+
+       hdev = hci_dev_get(cmd->index);
+       if (hdev) {
+               rp.local_clock = cpu_to_le32(hdev->clock);
+               hci_dev_put(hdev);
+       }
+
+       if (conn) {
+               rp.piconet_clock = cpu_to_le32(conn->clock);
+               rp.accuracy = cpu_to_le16(conn->clock_accuracy);
+       }
+
+complete:
+       cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, sizeof(rp));
+
+       if (conn) {
+               hci_conn_drop(conn);
+               hci_conn_put(conn);
+       }
+}
+
+static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
+{
        struct hci_cp_read_clock *hci_cp;
        struct pending_cmd *cmd;
        struct hci_conn *conn;
@@ -5179,29 +5327,8 @@ static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
        if (!cmd)
                goto unlock;
 
-       cp = cmd->param;
-
-       memset(&rp, 0, sizeof(rp));
-       memcpy(&rp.addr, &cp->addr, sizeof(rp.addr));
-
-       if (status)
-               goto send_rsp;
-
-       rp.local_clock = cpu_to_le32(hdev->clock);
-
-       if (conn) {
-               rp.piconet_clock = cpu_to_le32(conn->clock);
-               rp.accuracy = cpu_to_le16(conn->clock_accuracy);
-       }
-
-send_rsp:
-       cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status),
-                    &rp, sizeof(rp));
+       cmd->cmd_complete(cmd, mgmt_status(status));
        mgmt_pending_remove(cmd);
-       if (conn) {
-               hci_conn_drop(conn);
-               hci_conn_put(conn);
-       }
 
 unlock:
        hci_dev_unlock(hdev);
@@ -5257,6 +5384,8 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       cmd->cmd_complete = clock_info_cmd_complete;
+
        hci_req_init(&req, hdev);
 
        memset(&hci_cp, 0, sizeof(hci_cp));
@@ -5746,6 +5875,7 @@ static const struct mgmt_handler {
        { read_config_info,       false, MGMT_READ_CONFIG_INFO_SIZE },
        { set_external_config,    false, MGMT_SET_EXTERNAL_CONFIG_SIZE },
        { set_public_address,     false, MGMT_SET_PUBLIC_ADDRESS_SIZE },
+       { start_service_discovery,true,  MGMT_START_SERVICE_DISCOVERY_SIZE },
 };
 
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
@@ -5882,7 +6012,7 @@ void mgmt_index_removed(struct hci_dev *hdev)
        if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
                return;
 
-       mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+       mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
 
        if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
                mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL);
@@ -6017,7 +6147,7 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
        }
 
        mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
-       mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status_not_powered);
+       mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status_not_powered);
 
        if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
                mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
@@ -6101,8 +6231,19 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
 
 static u8 mgmt_ltk_type(struct smp_ltk *ltk)
 {
-       if (ltk->authenticated)
-               return MGMT_LTK_AUTHENTICATED;
+       switch (ltk->type) {
+       case SMP_LTK:
+       case SMP_LTK_SLAVE:
+               if (ltk->authenticated)
+                       return MGMT_LTK_AUTHENTICATED;
+               return MGMT_LTK_UNAUTHENTICATED;
+       case SMP_LTK_P256:
+               if (ltk->authenticated)
+                       return MGMT_LTK_P256_AUTH;
+               return MGMT_LTK_P256_UNAUTH;
+       case SMP_LTK_P256_DEBUG:
+               return MGMT_LTK_P256_DEBUG;
+       }
 
        return MGMT_LTK_UNAUTHENTICATED;
 }
@@ -6276,15 +6417,9 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
 
 static void disconnect_rsp(struct pending_cmd *cmd, void *data)
 {
-       struct mgmt_cp_disconnect *cp = cmd->param;
        struct sock **sk = data;
-       struct mgmt_rp_disconnect rp;
 
-       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
-       rp.addr.type = cp->addr.type;
-
-       cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp,
-                    sizeof(rp));
+       cmd->cmd_complete(cmd, 0);
 
        *sk = cmd->sk;
        sock_hold(*sk);
@@ -6296,16 +6431,10 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
 {
        struct hci_dev *hdev = data;
        struct mgmt_cp_unpair_device *cp = cmd->param;
-       struct mgmt_rp_unpair_device rp;
-
-       memset(&rp, 0, sizeof(rp));
-       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
-       rp.addr.type = cp->addr.type;
 
        device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
 
-       cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp));
-
+       cmd->cmd_complete(cmd, 0);
        mgmt_pending_remove(cmd);
 }
 
@@ -6366,7 +6495,6 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
 {
        u8 bdaddr_type = link_to_bdaddr(link_type, addr_type);
        struct mgmt_cp_disconnect *cp;
-       struct mgmt_rp_disconnect rp;
        struct pending_cmd *cmd;
 
        mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
@@ -6384,12 +6512,7 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
        if (cp->addr.type != bdaddr_type)
                return;
 
-       bacpy(&rp.addr.bdaddr, bdaddr);
-       rp.addr.type = bdaddr_type;
-
-       cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
-                    mgmt_status(status), &rp, sizeof(rp));
-
+       cmd->cmd_complete(cmd, mgmt_status(status));
        mgmt_pending_remove(cmd);
 }
 
@@ -6428,18 +6551,12 @@ void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                  u8 status)
 {
        struct pending_cmd *cmd;
-       struct mgmt_rp_pin_code_reply rp;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
        if (!cmd)
                return;
 
-       bacpy(&rp.addr.bdaddr, bdaddr);
-       rp.addr.type = BDADDR_BREDR;
-
-       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
-                    mgmt_status(status), &rp, sizeof(rp));
-
+       cmd->cmd_complete(cmd, mgmt_status(status));
        mgmt_pending_remove(cmd);
 }
 
@@ -6447,18 +6564,12 @@ void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                      u8 status)
 {
        struct pending_cmd *cmd;
-       struct mgmt_rp_pin_code_reply rp;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
        if (!cmd)
                return;
 
-       bacpy(&rp.addr.bdaddr, bdaddr);
-       rp.addr.type = BDADDR_BREDR;
-
-       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
-                    mgmt_status(status), &rp, sizeof(rp));
-
+       cmd->cmd_complete(cmd, mgmt_status(status));
        mgmt_pending_remove(cmd);
 }
 
@@ -6498,21 +6609,15 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                      u8 opcode)
 {
        struct pending_cmd *cmd;
-       struct mgmt_rp_user_confirm_reply rp;
-       int err;
 
        cmd = mgmt_pending_find(opcode, hdev);
        if (!cmd)
                return -ENOENT;
 
-       bacpy(&rp.addr.bdaddr, bdaddr);
-       rp.addr.type = link_to_bdaddr(link_type, addr_type);
-       err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status),
-                          &rp, sizeof(rp));
-
+       cmd->cmd_complete(cmd, mgmt_status(status));
        mgmt_pending_remove(cmd);
 
-       return err;
+       return 0;
 }
 
 int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -6784,8 +6889,7 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
                cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
                           mgmt_status(status));
        } else {
-               if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
-                   hash256 && rand256) {
+               if (bredr_sc_enabled(hdev) && hash256 && rand256) {
                        struct mgmt_rp_read_local_oob_ext_data rp;
 
                        memcpy(rp.hash192, hash192, sizeof(rp.hash192));
@@ -6812,6 +6916,73 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
        mgmt_pending_remove(cmd);
 }
 
+static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])
+{
+       int i;
+
+       for (i = 0; i < uuid_count; i++) {
+               if (!memcmp(uuid, uuids[i], 16))
+                       return true;
+       }
+
+       return false;
+}
+
+static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16])
+{
+       u16 parsed = 0;
+
+       while (parsed < eir_len) {
+               u8 field_len = eir[0];
+               u8 uuid[16];
+               int i;
+
+               if (field_len == 0)
+                       break;
+
+               if (eir_len - parsed < field_len + 1)
+                       break;
+
+               switch (eir[1]) {
+               case EIR_UUID16_ALL:
+               case EIR_UUID16_SOME:
+                       for (i = 0; i + 3 <= field_len; i += 2) {
+                               memcpy(uuid, bluetooth_base_uuid, 16);
+                               uuid[13] = eir[i + 3];
+                               uuid[12] = eir[i + 2];
+                               if (has_uuid(uuid, uuid_count, uuids))
+                                       return true;
+                       }
+                       break;
+               case EIR_UUID32_ALL:
+               case EIR_UUID32_SOME:
+                       for (i = 0; i + 5 <= field_len; i += 4) {
+                               memcpy(uuid, bluetooth_base_uuid, 16);
+                               uuid[15] = eir[i + 5];
+                               uuid[14] = eir[i + 4];
+                               uuid[13] = eir[i + 3];
+                               uuid[12] = eir[i + 2];
+                               if (has_uuid(uuid, uuid_count, uuids))
+                                       return true;
+                       }
+                       break;
+               case EIR_UUID128_ALL:
+               case EIR_UUID128_SOME:
+                       for (i = 0; i + 17 <= field_len; i += 16) {
+                               memcpy(uuid, eir + i + 2, 16);
+                               if (has_uuid(uuid, uuid_count, uuids))
+                                       return true;
+                       }
+                       break;
+               }
+
+               parsed += field_len + 1;
+               eir += field_len + 1;
+       }
+
+       return false;
+}
+
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                       u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
                       u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
@@ -6819,6 +6990,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        char buf[512];
        struct mgmt_ev_device_found *ev = (void *) buf;
        size_t ev_size;
+       bool match;
 
        /* Don't send events for a non-kernel initiated discovery. With
         * LE one exception is if we have pend_le_reports > 0 in which
@@ -6831,6 +7003,18 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                        return;
        }
 
+       /* When using service discovery with a RSSI threshold, then check
+        * if such a RSSI threshold is specified. If a RSSI threshold has
+        * been specified, then all results with a RSSI smaller than the
+        * RSSI threshold will be dropped.
+        *
+        * For BR/EDR devices (pre 1.2) providing no RSSI during inquiry,
+        * the results are also dropped.
+        */
+       if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
+           (rssi < hdev->discovery.rssi || rssi == HCI_RSSI_INVALID))
+               return;
+
        /* Make sure that the buffer is big enough. The 5 extra bytes
         * are for the potential CoD field.
         */
@@ -6839,20 +7023,75 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        memset(buf, 0, sizeof(buf));
 
+       /* In case of device discovery with BR/EDR devices (pre 1.2), the
+        * RSSI value was reported as 0 when not available. This behavior
+        * is kept when using device discovery. This is required for full
+        * backwards compatibility with the API.
+        *
+        * However when using service discovery, the value 127 will be
+        * returned when the RSSI is not available.
+        */
+       if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi)
+               rssi = 0;
+
        bacpy(&ev->addr.bdaddr, bdaddr);
        ev->addr.type = link_to_bdaddr(link_type, addr_type);
        ev->rssi = rssi;
        ev->flags = cpu_to_le32(flags);
 
-       if (eir_len > 0)
+       if (eir_len > 0) {
+               /* When using service discovery and a list of UUID is
+                * provided, results with no matching UUID should be
+                * dropped. In case there is a match the result is
+                * kept and checking possible scan response data
+                * will be skipped.
+                */
+               if (hdev->discovery.uuid_count > 0) {
+                       match = eir_has_uuids(eir, eir_len,
+                                             hdev->discovery.uuid_count,
+                                             hdev->discovery.uuids);
+                       if (!match)
+                               return;
+               }
+
+               /* Copy EIR or advertising data into event */
                memcpy(ev->eir, eir, eir_len);
+       } else {
+               /* When using service discovery and a list of UUID is
+                * provided, results with empty EIR or advertising data
+                * should be dropped since they do not match any UUID.
+                */
+               if (hdev->discovery.uuid_count > 0)
+                       return;
+       }
 
        if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
                eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
                                          dev_class, 3);
 
-       if (scan_rsp_len > 0)
+       if (scan_rsp_len > 0) {
+               /* When using service discovery and a list of UUID is
+                * provided, results with no matching UUID should be
+                * dropped if there is no previous match from the
+                * advertising data.
+                */
+               if (hdev->discovery.uuid_count > 0) {
+                       if (!match && !eir_has_uuids(scan_rsp, scan_rsp_len,
+                                                    hdev->discovery.uuid_count,
+                                                    hdev->discovery.uuids))
+                               return;
+               }
+
+               /* Append scan response data to event */
                memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len);
+       } else {
+               /* When using service discovery and a list of UUID is
+                * provided, results with empty scan response and no
+                * previous matched advertising data should be dropped.
+                */
+               if (hdev->discovery.uuid_count > 0 && !match)
+                       return;
+       }
 
        ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
        ev_size = sizeof(*ev) + eir_len + scan_rsp_len;
@@ -6886,23 +7125,9 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
 {
        struct mgmt_ev_discovering ev;
-       struct pending_cmd *cmd;
 
        BT_DBG("%s discovering %u", hdev->name, discovering);
 
-       if (discovering)
-               cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
-       else
-               cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
-
-       if (cmd != NULL) {
-               u8 type = hdev->discovery.type;
-
-               cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type,
-                            sizeof(type));
-               mgmt_pending_remove(cmd);
-       }
-
        memset(&ev, 0, sizeof(ev));
        ev.type = hdev->discovery.type;
        ev.discovering = discovering;
index 069b76e..96bf16d 100644 (file)
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
 
+#include "ecc.h"
 #include "smp.h"
 
+/* Low-level debug macros to be used for stuff that we don't want
+ * accidentially in dmesg, i.e. the values of the various crypto keys
+ * and the inputs & outputs of crypto functions.
+ */
+#ifdef DEBUG
+#define SMP_DBG(fmt, ...) printk(KERN_DEBUG "%s: " fmt, __func__, \
+                                ##__VA_ARGS__)
+#else
+#define SMP_DBG(fmt, ...) no_printk(KERN_DEBUG "%s: " fmt, __func__, \
+                                   ##__VA_ARGS__)
+#endif
+
 #define SMP_ALLOW_CMD(smp, code)       set_bit(code, &smp->allow_cmd)
 
+/* Keys which are not distributed with Secure Connections */
+#define SMP_SC_NO_DIST (SMP_DIST_ENC_KEY | SMP_DIST_LINK_KEY);
+
 #define SMP_TIMEOUT    msecs_to_jiffies(30000)
 
-#define AUTH_REQ_MASK   0x07
-#define KEY_DIST_MASK  0x07
+#define AUTH_REQ_MASK(dev)     (test_bit(HCI_SC_ENABLED, &(dev)->dev_flags) ? \
+                                0x1f : 0x07)
+#define KEY_DIST_MASK          0x07
+
+/* Maximum message length that can be passed to aes_cmac */
+#define CMAC_MSG_MAX   80
 
 enum {
        SMP_FLAG_TK_VALID,
@@ -44,6 +64,12 @@ enum {
        SMP_FLAG_MITM_AUTH,
        SMP_FLAG_COMPLETE,
        SMP_FLAG_INITIATOR,
+       SMP_FLAG_SC,
+       SMP_FLAG_REMOTE_PK,
+       SMP_FLAG_DEBUG_KEY,
+       SMP_FLAG_WAIT_USER,
+       SMP_FLAG_DHKEY_PENDING,
+       SMP_FLAG_OOB,
 };
 
 struct smp_chan {
@@ -57,6 +83,7 @@ struct smp_chan {
        u8              rrnd[16]; /* SMP Pairing Random (remote) */
        u8              pcnf[16]; /* SMP Pairing Confirm */
        u8              tk[16]; /* SMP Temporary Key */
+       u8              rr[16];
        u8              enc_key_size;
        u8              remote_key_dist;
        bdaddr_t        id_addr;
@@ -67,9 +94,43 @@ struct smp_chan {
        struct smp_ltk  *ltk;
        struct smp_ltk  *slave_ltk;
        struct smp_irk  *remote_irk;
+       u8              *link_key;
        unsigned long   flags;
+       u8              method;
+       u8              passkey_round;
+
+       /* Secure Connections variables */
+       u8                      local_pk[64];
+       u8                      local_sk[32];
+       u8                      remote_pk[64];
+       u8                      dhkey[32];
+       u8                      mackey[16];
 
        struct crypto_blkcipher *tfm_aes;
+       struct crypto_hash      *tfm_cmac;
+};
+
+/* These debug key values are defined in the SMP section of the core
+ * specification. debug_pk is the public debug key and debug_sk the
+ * private debug key.
+ */
+static const u8 debug_pk[64] = {
+               0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+               0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+               0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+               0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
+
+               0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
+               0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
+               0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
+               0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
+};
+
+static const u8 debug_sk[32] = {
+               0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
+               0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
+               0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
+               0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
 };
 
 static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
@@ -80,14 +141,22 @@ static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
                dst[len - 1 - i] = src[i];
 }
 
-static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
+/* The following functions map to the LE SC SMP crypto functions
+ * AES-CMAC, f4, f5, f6, g2 and h6.
+ */
+
+static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m,
+                   size_t len, u8 mac[16])
 {
-       struct blkcipher_desc desc;
+       uint8_t tmp[16], mac_msb[16], msg_msb[CMAC_MSG_MAX];
+       struct hash_desc desc;
        struct scatterlist sg;
-       uint8_t tmp[16], data[16];
        int err;
 
-       if (tfm == NULL) {
+       if (len > CMAC_MSG_MAX)
+               return -EFBIG;
+
+       if (!tfm) {
                BT_ERR("tfm %p", tfm);
                return -EINVAL;
        }
@@ -95,105 +164,233 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
        desc.tfm = tfm;
        desc.flags = 0;
 
-       /* The most significant octet of key corresponds to k[0] */
+       crypto_hash_init(&desc);
+
+       /* Swap key and message from LSB to MSB */
        swap_buf(k, tmp, 16);
+       swap_buf(m, msg_msb, len);
 
-       err = crypto_blkcipher_setkey(tfm, tmp, 16);
+       SMP_DBG("msg (len %zu) %*phN", len, (int) len, m);
+       SMP_DBG("key %16phN", k);
+
+       err = crypto_hash_setkey(tfm, tmp, 16);
        if (err) {
                BT_ERR("cipher setkey failed: %d", err);
                return err;
        }
 
-       /* Most significant octet of plaintextData corresponds to data[0] */
-       swap_buf(r, data, 16);
+       sg_init_one(&sg, msg_msb, len);
 
-       sg_init_one(&sg, data, 16);
+       err = crypto_hash_update(&desc, &sg, len);
+       if (err) {
+               BT_ERR("Hash update error %d", err);
+               return err;
+       }
 
-       err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
+       err = crypto_hash_final(&desc, mac_msb);
+       if (err) {
+               BT_ERR("Hash final error %d", err);
+               return err;
+       }
+
+       swap_buf(mac_msb, mac, 16);
+
+       SMP_DBG("mac %16phN", mac);
+
+       return 0;
+}
+
+static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
+                 const u8 x[16], u8 z, u8 res[16])
+{
+       u8 m[65];
+       int err;
+
+       SMP_DBG("u %32phN", u);
+       SMP_DBG("v %32phN", v);
+       SMP_DBG("x %16phN z %02x", x, z);
+
+       m[0] = z;
+       memcpy(m + 1, v, 32);
+       memcpy(m + 33, u, 32);
+
+       err = aes_cmac(tfm_cmac, x, m, sizeof(m), res);
        if (err)
-               BT_ERR("Encrypt data error %d", err);
+               return err;
 
-       /* Most significant octet of encryptedData corresponds to data[0] */
-       swap_buf(data, r, 16);
+       SMP_DBG("res %16phN", res);
 
        return err;
 }
 
-static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3])
+static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16],
+                 u8 a1[7], u8 a2[7], u8 mackey[16], u8 ltk[16])
 {
-       u8 _res[16];
+       /* The btle, salt and length "magic" values are as defined in
+        * the SMP section of the Bluetooth core specification. In ASCII
+        * the btle value ends up being 'btle'. The salt is just a
+        * random number whereas length is the value 256 in little
+        * endian format.
+        */
+       const u8 btle[4] = { 0x65, 0x6c, 0x74, 0x62 };
+       const u8 salt[16] = { 0xbe, 0x83, 0x60, 0x5a, 0xdb, 0x0b, 0x37, 0x60,
+                             0x38, 0xa5, 0xf5, 0xaa, 0x91, 0x83, 0x88, 0x6c };
+       const u8 length[2] = { 0x00, 0x01 };
+       u8 m[53], t[16];
        int err;
 
-       /* r' = padding || r */
-       memcpy(_res, r, 3);
-       memset(_res + 3, 0, 13);
+       SMP_DBG("w %32phN", w);
+       SMP_DBG("n1 %16phN n2 %16phN", n1, n2);
+       SMP_DBG("a1 %7phN a2 %7phN", a1, a2);
 
-       err = smp_e(tfm, irk, _res);
-       if (err) {
-               BT_ERR("Encrypt error");
+       err = aes_cmac(tfm_cmac, salt, w, 32, t);
+       if (err)
                return err;
-       }
 
-       /* The output of the random address function ah is:
-        *      ah(h, r) = e(k, r') mod 2^24
-        * The output of the security function e is then truncated to 24 bits
-        * by taking the least significant 24 bits of the output of e as the
-        * result of ah.
-        */
-       memcpy(res, _res, 3);
+       SMP_DBG("t %16phN", t);
+
+       memcpy(m, length, 2);
+       memcpy(m + 2, a2, 7);
+       memcpy(m + 9, a1, 7);
+       memcpy(m + 16, n2, 16);
+       memcpy(m + 32, n1, 16);
+       memcpy(m + 48, btle, 4);
+
+       m[52] = 0; /* Counter */
+
+       err = aes_cmac(tfm_cmac, t, m, sizeof(m), mackey);
+       if (err)
+               return err;
+
+       SMP_DBG("mackey %16phN", mackey);
+
+       m[52] = 1; /* Counter */
+
+       err = aes_cmac(tfm_cmac, t, m, sizeof(m), ltk);
+       if (err)
+               return err;
+
+       SMP_DBG("ltk %16phN", ltk);
 
        return 0;
 }
 
-bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr)
+static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16],
+                 const u8 n1[16], u8 n2[16], const u8 r[16],
+                 const u8 io_cap[3], const u8 a1[7], const u8 a2[7],
+                 u8 res[16])
 {
-       struct l2cap_chan *chan = hdev->smp_data;
-       struct crypto_blkcipher *tfm;
-       u8 hash[3];
+       u8 m[65];
        int err;
 
-       if (!chan || !chan->data)
-               return false;
+       SMP_DBG("w %16phN", w);
+       SMP_DBG("n1 %16phN n2 %16phN", n1, n2);
+       SMP_DBG("r %16phN io_cap %3phN a1 %7phN a2 %7phN", r, io_cap, a1, a2);
 
-       tfm = chan->data;
+       memcpy(m, a2, 7);
+       memcpy(m + 7, a1, 7);
+       memcpy(m + 14, io_cap, 3);
+       memcpy(m + 17, r, 16);
+       memcpy(m + 33, n2, 16);
+       memcpy(m + 49, n1, 16);
 
-       BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
+       err = aes_cmac(tfm_cmac, w, m, sizeof(m), res);
+       if (err)
+               return err;
 
-       err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
+       BT_DBG("res %16phN", res);
+
+       return err;
+}
+
+static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
+                 const u8 x[16], const u8 y[16], u32 *val)
+{
+       u8 m[80], tmp[16];
+       int err;
+
+       SMP_DBG("u %32phN", u);
+       SMP_DBG("v %32phN", v);
+       SMP_DBG("x %16phN y %16phN", x, y);
+
+       memcpy(m, y, 16);
+       memcpy(m + 16, v, 32);
+       memcpy(m + 48, u, 32);
+
+       err = aes_cmac(tfm_cmac, x, m, sizeof(m), tmp);
        if (err)
-               return false;
+               return err;
 
-       return !memcmp(bdaddr->b, hash, 3);
+       *val = get_unaligned_le32(tmp);
+       *val %= 1000000;
+
+       SMP_DBG("val %06u", *val);
+
+       return 0;
 }
 
-int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa)
+static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16],
+                 const u8 key_id[4], u8 res[16])
 {
-       struct l2cap_chan *chan = hdev->smp_data;
-       struct crypto_blkcipher *tfm;
        int err;
 
-       if (!chan || !chan->data)
-               return -EOPNOTSUPP;
+       SMP_DBG("w %16phN key_id %4phN", w, key_id);
 
-       tfm = chan->data;
+       err = aes_cmac(tfm_cmac, w, key_id, 4, res);
+       if (err)
+               return err;
 
-       get_random_bytes(&rpa->b[3], 3);
+       SMP_DBG("res %16phN", res);
 
-       rpa->b[5] &= 0x3f;      /* Clear two most significant bits */
-       rpa->b[5] |= 0x40;      /* Set second most significant bit */
+       return err;
+}
 
-       err = smp_ah(tfm, irk, &rpa->b[3], rpa->b);
-       if (err < 0)
+/* The following functions map to the legacy SMP crypto functions e, c1,
+ * s1 and ah.
+ */
+
+static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
+{
+       struct blkcipher_desc desc;
+       struct scatterlist sg;
+       uint8_t tmp[16], data[16];
+       int err;
+
+       if (!tfm) {
+               BT_ERR("tfm %p", tfm);
+               return -EINVAL;
+       }
+
+       desc.tfm = tfm;
+       desc.flags = 0;
+
+       /* The most significant octet of key corresponds to k[0] */
+       swap_buf(k, tmp, 16);
+
+       err = crypto_blkcipher_setkey(tfm, tmp, 16);
+       if (err) {
+               BT_ERR("cipher setkey failed: %d", err);
                return err;
+       }
 
-       BT_DBG("RPA %pMR", rpa);
+       /* Most significant octet of plaintextData corresponds to data[0] */
+       swap_buf(r, data, 16);
 
-       return 0;
+       sg_init_one(&sg, data, 16);
+
+       err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
+       if (err)
+               BT_ERR("Encrypt data error %d", err);
+
+       /* Most significant octet of encryptedData corresponds to data[0] */
+       swap_buf(data, r, 16);
+
+       return err;
 }
 
-static int smp_c1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r[16],
-                 u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat,
-                 bdaddr_t *ra, u8 res[16])
+static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
+                 const u8 r[16], const u8 preq[7], const u8 pres[7], u8 _iat,
+                 const bdaddr_t *ia, u8 _rat, const bdaddr_t *ra, u8 res[16])
 {
        u8 p1[16], p2[16];
        int err;
@@ -232,8 +429,8 @@ static int smp_c1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r[16],
        return err;
 }
 
-static int smp_s1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r1[16],
-                 u8 r2[16], u8 _r[16])
+static int smp_s1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
+                 const u8 r1[16], const u8 r2[16], u8 _r[16])
 {
        int err;
 
@@ -248,6 +445,80 @@ static int smp_s1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r1[16],
        return err;
 }
 
+static int smp_ah(struct crypto_blkcipher *tfm, const u8 irk[16],
+                 const u8 r[3], u8 res[3])
+{
+       u8 _res[16];
+       int err;
+
+       /* r' = padding || r */
+       memcpy(_res, r, 3);
+       memset(_res + 3, 0, 13);
+
+       err = smp_e(tfm, irk, _res);
+       if (err) {
+               BT_ERR("Encrypt error");
+               return err;
+       }
+
+       /* The output of the random address function ah is:
+        *      ah(h, r) = e(k, r') mod 2^24
+        * The output of the security function e is then truncated to 24 bits
+        * by taking the least significant 24 bits of the output of e as the
+        * result of ah.
+        */
+       memcpy(res, _res, 3);
+
+       return 0;
+}
+
+bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
+                    const bdaddr_t *bdaddr)
+{
+       struct l2cap_chan *chan = hdev->smp_data;
+       struct crypto_blkcipher *tfm;
+       u8 hash[3];
+       int err;
+
+       if (!chan || !chan->data)
+               return false;
+
+       tfm = chan->data;
+
+       BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
+
+       err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
+       if (err)
+               return false;
+
+       return !memcmp(bdaddr->b, hash, 3);
+}
+
+int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa)
+{
+       struct l2cap_chan *chan = hdev->smp_data;
+       struct crypto_blkcipher *tfm;
+       int err;
+
+       if (!chan || !chan->data)
+               return -EOPNOTSUPP;
+
+       tfm = chan->data;
+
+       get_random_bytes(&rpa->b[3], 3);
+
+       rpa->b[5] &= 0x3f;      /* Clear two most significant bits */
+       rpa->b[5] |= 0x40;      /* Set second most significant bit */
+
+       err = smp_ah(tfm, irk, &rpa->b[3], rpa->b);
+       if (err < 0)
+               return err;
+
+       BT_DBG("RPA %pMR", rpa);
+
+       return 0;
+}
+
 static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
 {
        struct l2cap_chan *chan = conn->smp;
@@ -282,17 +553,22 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
        schedule_delayed_work(&smp->security_timer, SMP_TIMEOUT);
 }
 
-static __u8 authreq_to_seclevel(__u8 authreq)
+static u8 authreq_to_seclevel(u8 authreq)
 {
-       if (authreq & SMP_AUTH_MITM)
-               return BT_SECURITY_HIGH;
-       else
+       if (authreq & SMP_AUTH_MITM) {
+               if (authreq & SMP_AUTH_SC)
+                       return BT_SECURITY_FIPS;
+               else
+                       return BT_SECURITY_HIGH;
+       } else {
                return BT_SECURITY_MEDIUM;
+       }
 }
 
 static __u8 seclevel_to_authreq(__u8 sec_level)
 {
        switch (sec_level) {
+       case BT_SECURITY_FIPS:
        case BT_SECURITY_HIGH:
                return SMP_AUTH_MITM | SMP_AUTH_BONDING;
        case BT_SECURITY_MEDIUM:
@@ -310,7 +586,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
        struct smp_chan *smp = chan->data;
        struct hci_conn *hcon = conn->hcon;
        struct hci_dev *hdev = hcon->hdev;
-       u8 local_dist = 0, remote_dist = 0;
+       u8 local_dist = 0, remote_dist = 0, oob_flag = SMP_OOB_NOT_PRESENT;
 
        if (test_bit(HCI_BONDABLE, &conn->hcon->hdev->dev_flags)) {
                local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
@@ -326,24 +602,52 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
        if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
                local_dist |= SMP_DIST_ID_KEY;
 
+       if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+           (authreq & SMP_AUTH_SC)) {
+               struct oob_data *oob_data;
+               u8 bdaddr_type;
+
+               if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+                       local_dist |= SMP_DIST_LINK_KEY;
+                       remote_dist |= SMP_DIST_LINK_KEY;
+               }
+
+               if (hcon->dst_type == ADDR_LE_DEV_PUBLIC)
+                       bdaddr_type = BDADDR_LE_PUBLIC;
+               else
+                       bdaddr_type = BDADDR_LE_RANDOM;
+
+               oob_data = hci_find_remote_oob_data(hdev, &hcon->dst,
+                                                   bdaddr_type);
+               if (oob_data) {
+                       set_bit(SMP_FLAG_OOB, &smp->flags);
+                       oob_flag = SMP_OOB_PRESENT;
+                       memcpy(smp->rr, oob_data->rand256, 16);
+                       memcpy(smp->pcnf, oob_data->hash256, 16);
+               }
+
+       } else {
+               authreq &= ~SMP_AUTH_SC;
+       }
+
        if (rsp == NULL) {
                req->io_capability = conn->hcon->io_capability;
-               req->oob_flag = SMP_OOB_NOT_PRESENT;
+               req->oob_flag = oob_flag;
                req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
                req->init_key_dist = local_dist;
                req->resp_key_dist = remote_dist;
-               req->auth_req = (authreq & AUTH_REQ_MASK);
+               req->auth_req = (authreq & AUTH_REQ_MASK(hdev));
 
                smp->remote_key_dist = remote_dist;
                return;
        }
 
        rsp->io_capability = conn->hcon->io_capability;
-       rsp->oob_flag = SMP_OOB_NOT_PRESENT;
+       rsp->oob_flag = oob_flag;
        rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
        rsp->init_key_dist = req->init_key_dist & remote_dist;
        rsp->resp_key_dist = req->resp_key_dist & local_dist;
-       rsp->auth_req = (authreq & AUTH_REQ_MASK);
+       rsp->auth_req = (authreq & AUTH_REQ_MASK(hdev));
 
        smp->remote_key_dist = rsp->init_key_dist;
 }
@@ -366,6 +670,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
 {
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
+       struct hci_conn *hcon = conn->hcon;
        bool complete;
 
        BUG_ON(!smp);
@@ -373,12 +678,24 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
        cancel_delayed_work_sync(&smp->security_timer);
 
        complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
-       mgmt_smp_complete(conn->hcon, complete);
+       mgmt_smp_complete(hcon, complete);
 
        kfree(smp->csrk);
        kfree(smp->slave_csrk);
+       kfree(smp->link_key);
 
        crypto_free_blkcipher(smp->tfm_aes);
+       crypto_free_hash(smp->tfm_cmac);
+
+       /* Ensure that we don't leave any debug key around if debug key
+        * support hasn't been explicitly enabled.
+        */
+       if (smp->ltk && smp->ltk->type == SMP_LTK_P256_DEBUG &&
+           !test_bit(HCI_KEEP_DEBUG_KEYS, &hcon->hdev->dev_flags)) {
+               list_del_rcu(&smp->ltk->list);
+               kfree_rcu(smp->ltk, rcu);
+               smp->ltk = NULL;
+       }
 
        /* If pairing failed clean up any keys we might have */
        if (!complete) {
@@ -400,7 +717,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
 
        chan->data = NULL;
        kfree(smp);
-       hci_conn_drop(conn->hcon);
+       hci_conn_drop(hcon);
 }
 
 static void smp_failure(struct l2cap_conn *conn, u8 reason)
@@ -424,6 +741,7 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason)
 #define REQ_PASSKEY    0x02
 #define CFM_PASSKEY    0x03
 #define REQ_OOB                0x04
+#define DSP_PASSKEY    0x05
 #define OVERLAP                0xFF
 
 static const u8 gen_method[5][5] = {
@@ -434,6 +752,14 @@ static const u8 gen_method[5][5] = {
        { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP     },
 };
 
+static const u8 sc_method[5][5] = {
+       { JUST_WORKS,  JUST_CFM,    REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
+       { JUST_WORKS,  CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+       { DSP_PASSKEY, DSP_PASSKEY, REQ_PASSKEY, JUST_WORKS, DSP_PASSKEY },
+       { JUST_WORKS,  JUST_CFM,    JUST_WORKS,  JUST_WORKS, JUST_CFM    },
+       { DSP_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+};
+
 static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
 {
        /* If either side has unknown io_caps, use JUST_CFM (which gets
@@ -443,6 +769,9 @@ static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
            remote_io > SMP_IO_KEYBOARD_DISPLAY)
                return JUST_CFM;
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags))
+               return sc_method[remote_io][local_io];
+
        return gen_method[remote_io][local_io];
 }
 
@@ -452,7 +781,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
-       u8 method;
        u32 passkey = 0;
        int ret = 0;
 
@@ -469,26 +797,28 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
         * table.
         */
        if (!(auth & SMP_AUTH_MITM))
-               method = JUST_CFM;
+               smp->method = JUST_CFM;
        else
-               method = get_auth_method(smp, local_io, remote_io);
+               smp->method = get_auth_method(smp, local_io, remote_io);
 
        /* Don't confirm locally initiated pairing attempts */
-       if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
-               method = JUST_WORKS;
+       if (smp->method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR,
+                                               &smp->flags))
+               smp->method = JUST_WORKS;
 
        /* Don't bother user space with no IO capabilities */
-       if (method == JUST_CFM && hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
-               method = JUST_WORKS;
+       if (smp->method == JUST_CFM &&
+           hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
+               smp->method = JUST_WORKS;
 
        /* If Just Works, Continue with Zero TK */
-       if (method == JUST_WORKS) {
+       if (smp->method == JUST_WORKS) {
                set_bit(SMP_FLAG_TK_VALID, &smp->flags);
                return 0;
        }
 
        /* Not Just Works/Confirm results in MITM Authentication */
-       if (method != JUST_CFM) {
+       if (smp->method != JUST_CFM) {
                set_bit(SMP_FLAG_MITM_AUTH, &smp->flags);
                if (hcon->pending_sec_level < BT_SECURITY_HIGH)
                        hcon->pending_sec_level = BT_SECURITY_HIGH;
@@ -497,15 +827,15 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        /* If both devices have Keyoard-Display I/O, the master
         * Confirms and the slave Enters the passkey.
         */
-       if (method == OVERLAP) {
+       if (smp->method == OVERLAP) {
                if (hcon->role == HCI_ROLE_MASTER)
-                       method = CFM_PASSKEY;
+                       smp->method = CFM_PASSKEY;
                else
-                       method = REQ_PASSKEY;
+                       smp->method = REQ_PASSKEY;
        }
 
        /* Generate random passkey. */
-       if (method == CFM_PASSKEY) {
+       if (smp->method == CFM_PASSKEY) {
                memset(smp->tk, 0, sizeof(smp->tk));
                get_random_bytes(&passkey, sizeof(passkey));
                passkey %= 1000000;
@@ -514,10 +844,10 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
                set_bit(SMP_FLAG_TK_VALID, &smp->flags);
        }
 
-       if (method == REQ_PASSKEY)
+       if (smp->method == REQ_PASSKEY)
                ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
                                                hcon->type, hcon->dst_type);
-       else if (method == JUST_CFM)
+       else if (smp->method == JUST_CFM)
                ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
                                                hcon->type, hcon->dst_type,
                                                passkey, 1);
@@ -638,11 +968,13 @@ static void smp_notify_keys(struct l2cap_conn *conn)
                mgmt_new_irk(hdev, smp->remote_irk);
                /* Now that user space can be considered to know the
                 * identity address track the connection based on it
-                * from now on.
+                * from now on (assuming this is an LE link).
                 */
-               bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
-               hcon->dst_type = smp->remote_irk->addr_type;
-               queue_work(hdev->workqueue, &conn->id_addr_update_work);
+               if (hcon->type == LE_LINK) {
+                       bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
+                       hcon->dst_type = smp->remote_irk->addr_type;
+                       queue_work(hdev->workqueue, &conn->id_addr_update_work);
+               }
 
                /* When receiving an indentity resolving key for
                 * a remote device that does not use a resolvable
@@ -661,10 +993,20 @@ static void smp_notify_keys(struct l2cap_conn *conn)
                }
        }
 
-       /* The LTKs and CSRKs should be persistent only if both sides
-        * had the bonding bit set in their authentication requests.
-        */
-       persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING);
+       if (hcon->type == ACL_LINK) {
+               if (hcon->key_type == HCI_LK_DEBUG_COMBINATION)
+                       persistent = false;
+               else
+                       persistent = !test_bit(HCI_CONN_FLUSH_KEY,
+                                              &hcon->flags);
+       } else {
+               /* The LTKs and CSRKs should be persistent only if both sides
+                * had the bonding bit set in their authentication requests.
+                */
+               persistent = !!((req->auth_req & rsp->auth_req) &
+                               SMP_AUTH_BONDING);
+       }
+
 
        if (smp->csrk) {
                smp->csrk->bdaddr_type = hcon->dst_type;
@@ -689,6 +1031,81 @@ static void smp_notify_keys(struct l2cap_conn *conn)
                bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
                mgmt_new_ltk(hdev, smp->slave_ltk, persistent);
        }
+
+       if (smp->link_key) {
+               struct link_key *key;
+               u8 type;
+
+               if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags))
+                       type = HCI_LK_DEBUG_COMBINATION;
+               else if (hcon->sec_level == BT_SECURITY_FIPS)
+                       type = HCI_LK_AUTH_COMBINATION_P256;
+               else
+                       type = HCI_LK_UNAUTH_COMBINATION_P256;
+
+               key = hci_add_link_key(hdev, smp->conn->hcon, &hcon->dst,
+                                      smp->link_key, type, 0, &persistent);
+               if (key) {
+                       mgmt_new_link_key(hdev, key, persistent);
+
+                       /* Don't keep debug keys around if the relevant
+                        * flag is not set.
+                        */
+                       if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) &&
+                           key->type == HCI_LK_DEBUG_COMBINATION) {
+                               list_del_rcu(&key->list);
+                               kfree_rcu(key, rcu);
+                       }
+               }
+       }
+}
+
+static void sc_add_ltk(struct smp_chan *smp)
+{
+       struct hci_conn *hcon = smp->conn->hcon;
+       u8 key_type, auth;
+
+       if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags))
+               key_type = SMP_LTK_P256_DEBUG;
+       else
+               key_type = SMP_LTK_P256;
+
+       if (hcon->pending_sec_level == BT_SECURITY_FIPS)
+               auth = 1;
+       else
+               auth = 0;
+
+       memset(smp->tk + smp->enc_key_size, 0,
+              SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
+
+       smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
+                              key_type, auth, smp->tk, smp->enc_key_size,
+                              0, 0);
+}
+
+static void sc_generate_link_key(struct smp_chan *smp)
+{
+       /* These constants are as specified in the core specification.
+        * In ASCII they spell out to 'tmp1' and 'lebr'.
+        */
+       const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 };
+       const u8 lebr[4] = { 0x72, 0x62, 0x65, 0x6c };
+
+       smp->link_key = kzalloc(16, GFP_KERNEL);
+       if (!smp->link_key)
+               return;
+
+       if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) {
+               kfree(smp->link_key);
+               smp->link_key = NULL;
+               return;
+       }
+
+       if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) {
+               kfree(smp->link_key);
+               smp->link_key = NULL;
+               return;
+       }
 }
 
 static void smp_allow_key_dist(struct smp_chan *smp)
@@ -705,6 +1122,35 @@ static void smp_allow_key_dist(struct smp_chan *smp)
                SMP_ALLOW_CMD(smp, SMP_CMD_SIGN_INFO);
 }
 
+static void sc_generate_ltk(struct smp_chan *smp)
+{
+       /* These constants are as specified in the core specification.
+        * In ASCII they spell out to 'tmp2' and 'brle'.
+        */
+       const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 };
+       const u8 brle[4] = { 0x65, 0x6c, 0x72, 0x62 };
+       struct hci_conn *hcon = smp->conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
+       struct link_key *key;
+
+       key = hci_find_link_key(hdev, &hcon->dst);
+       if (!key) {
+               BT_ERR("%s No Link Key found to generate LTK", hdev->name);
+               return;
+       }
+
+       if (key->type == HCI_LK_DEBUG_COMBINATION)
+               set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+
+       if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk))
+               return;
+
+       if (smp_h6(smp->tfm_cmac, smp->tk, brle, smp->tk))
+               return;
+
+       sc_add_ltk(smp);
+}
+
 static void smp_distribute_keys(struct smp_chan *smp)
 {
        struct smp_cmd_pairing *req, *rsp;
@@ -733,6 +1179,16 @@ static void smp_distribute_keys(struct smp_chan *smp)
                *keydist &= req->resp_key_dist;
        }
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+               if (hcon->type == LE_LINK && (*keydist & SMP_DIST_LINK_KEY))
+                       sc_generate_link_key(smp);
+               if (hcon->type == ACL_LINK && (*keydist & SMP_DIST_ENC_KEY))
+                       sc_generate_ltk(smp);
+
+               /* Clear the keys which are generated but not distributed */
+               *keydist &= ~SMP_SC_NO_DIST;
+       }
+
        BT_DBG("keydist 0x%x", *keydist);
 
        if (*keydist & SMP_DIST_ENC_KEY) {
@@ -844,6 +1300,14 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
                return NULL;
        }
 
+       smp->tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(smp->tfm_cmac)) {
+               BT_ERR("Unable to create CMAC crypto context");
+               crypto_free_blkcipher(smp->tfm_aes);
+               kfree(smp);
+               return NULL;
+       }
+
        smp->conn = conn;
        chan->data = smp;
 
@@ -856,6 +1320,213 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
        return smp;
 }
 
+static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16])
+{
+       struct hci_conn *hcon = smp->conn->hcon;
+       u8 *na, *nb, a[7], b[7];
+
+       if (hcon->out) {
+               na   = smp->prnd;
+               nb   = smp->rrnd;
+       } else {
+               na   = smp->rrnd;
+               nb   = smp->prnd;
+       }
+
+       memcpy(a, &hcon->init_addr, 6);
+       memcpy(b, &hcon->resp_addr, 6);
+       a[6] = hcon->init_addr_type;
+       b[6] = hcon->resp_addr_type;
+
+       return smp_f5(smp->tfm_cmac, smp->dhkey, na, nb, a, b, mackey, ltk);
+}
+
+static void sc_dhkey_check(struct smp_chan *smp)
+{
+       struct hci_conn *hcon = smp->conn->hcon;
+       struct smp_cmd_dhkey_check check;
+       u8 a[7], b[7], *local_addr, *remote_addr;
+       u8 io_cap[3], r[16];
+
+       memcpy(a, &hcon->init_addr, 6);
+       memcpy(b, &hcon->resp_addr, 6);
+       a[6] = hcon->init_addr_type;
+       b[6] = hcon->resp_addr_type;
+
+       if (hcon->out) {
+               local_addr = a;
+               remote_addr = b;
+               memcpy(io_cap, &smp->preq[1], 3);
+       } else {
+               local_addr = b;
+               remote_addr = a;
+               memcpy(io_cap, &smp->prsp[1], 3);
+       }
+
+       memset(r, 0, sizeof(r));
+
+       if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+               put_unaligned_le32(hcon->passkey_notify, r);
+
+       if (smp->method == REQ_OOB)
+               memcpy(r, smp->rr, 16);
+
+       smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap,
+              local_addr, remote_addr, check.e);
+
+       smp_send_cmd(smp->conn, SMP_CMD_DHKEY_CHECK, sizeof(check), &check);
+}
+
+static u8 sc_passkey_send_confirm(struct smp_chan *smp)
+{
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_conn *hcon = conn->hcon;
+       struct smp_cmd_pairing_confirm cfm;
+       u8 r;
+
+       r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01);
+       r |= 0x80;
+
+       get_random_bytes(smp->prnd, sizeof(smp->prnd));
+
+       if (smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd, r,
+                  cfm.confirm_val))
+               return SMP_UNSPECIFIED;
+
+       smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm);
+
+       return 0;
+}
+
+static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op)
+{
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
+       u8 cfm[16], r;
+
+       /* Ignore the PDU if we've already done 20 rounds (0 - 19) */
+       if (smp->passkey_round >= 20)
+               return 0;
+
+       switch (smp_op) {
+       case SMP_CMD_PAIRING_RANDOM:
+               r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01);
+               r |= 0x80;
+
+               if (smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
+                          smp->rrnd, r, cfm))
+                       return SMP_UNSPECIFIED;
+
+               if (memcmp(smp->pcnf, cfm, 16))
+                       return SMP_CONFIRM_FAILED;
+
+               smp->passkey_round++;
+
+               if (smp->passkey_round == 20) {
+                       /* Generate MacKey and LTK */
+                       if (sc_mackey_and_ltk(smp, smp->mackey, smp->tk))
+                               return SMP_UNSPECIFIED;
+               }
+
+               /* The round is only complete when the initiator
+                * receives pairing random.
+                */
+               if (!hcon->out) {
+                       smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+                                    sizeof(smp->prnd), smp->prnd);
+                       if (smp->passkey_round == 20)
+                               SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+                       else
+                               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+                       return 0;
+               }
+
+               /* Start the next round */
+               if (smp->passkey_round != 20)
+                       return sc_passkey_round(smp, 0);
+
+               /* Passkey rounds are complete - start DHKey Check */
+               sc_dhkey_check(smp);
+               SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+
+               break;
+
+       case SMP_CMD_PAIRING_CONFIRM:
+               if (test_bit(SMP_FLAG_WAIT_USER, &smp->flags)) {
+                       set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
+                       return 0;
+               }
+
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+               if (hcon->out) {
+                       smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+                                    sizeof(smp->prnd), smp->prnd);
+                       return 0;
+               }
+
+               return sc_passkey_send_confirm(smp);
+
+       case SMP_CMD_PUBLIC_KEY:
+       default:
+               /* Initiating device starts the round */
+               if (!hcon->out)
+                       return 0;
+
+               BT_DBG("%s Starting passkey round %u", hdev->name,
+                      smp->passkey_round + 1);
+
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+               return sc_passkey_send_confirm(smp);
+       }
+
+       return 0;
+}
+
+static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey)
+{
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_conn *hcon = conn->hcon;
+       u8 smp_op;
+
+       clear_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+
+       switch (mgmt_op) {
+       case MGMT_OP_USER_PASSKEY_NEG_REPLY:
+               smp_failure(smp->conn, SMP_PASSKEY_ENTRY_FAILED);
+               return 0;
+       case MGMT_OP_USER_CONFIRM_NEG_REPLY:
+               smp_failure(smp->conn, SMP_NUMERIC_COMP_FAILED);
+               return 0;
+       case MGMT_OP_USER_PASSKEY_REPLY:
+               hcon->passkey_notify = le32_to_cpu(passkey);
+               smp->passkey_round = 0;
+
+               if (test_and_clear_bit(SMP_FLAG_CFM_PENDING, &smp->flags))
+                       smp_op = SMP_CMD_PAIRING_CONFIRM;
+               else
+                       smp_op = 0;
+
+               if (sc_passkey_round(smp, smp_op))
+                       return -EIO;
+
+               return 0;
+       }
+
+       /* Initiator sends DHKey check first */
+       if (hcon->out) {
+               sc_dhkey_check(smp);
+               SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+       } else if (test_and_clear_bit(SMP_FLAG_DHKEY_PENDING, &smp->flags)) {
+               sc_dhkey_check(smp);
+               sc_add_ltk(smp);
+       }
+
+       return 0;
+}
+
 int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
 {
        struct l2cap_conn *conn = hcon->l2cap_data;
@@ -881,6 +1552,11 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
 
        smp = chan->data;
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+               err = sc_user_reply(smp, mgmt_op, passkey);
+               goto unlock;
+       }
+
        switch (mgmt_op) {
        case MGMT_OP_USER_PASSKEY_REPLY:
                value = le32_to_cpu(passkey);
@@ -916,6 +1592,46 @@ unlock:
        return err;
 }
 
+static void build_bredr_pairing_cmd(struct smp_chan *smp,
+                                   struct smp_cmd_pairing *req,
+                                   struct smp_cmd_pairing *rsp)
+{
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_dev *hdev = conn->hcon->hdev;
+       u8 local_dist = 0, remote_dist = 0;
+
+       if (test_bit(HCI_BONDABLE, &hdev->dev_flags)) {
+               local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
+               remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
+       }
+
+       if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags))
+               remote_dist |= SMP_DIST_ID_KEY;
+
+       if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
+               local_dist |= SMP_DIST_ID_KEY;
+
+       if (!rsp) {
+               memset(req, 0, sizeof(*req));
+
+               req->init_key_dist   = local_dist;
+               req->resp_key_dist   = remote_dist;
+               req->max_key_size    = SMP_MAX_ENC_KEY_SIZE;
+
+               smp->remote_key_dist = remote_dist;
+
+               return;
+       }
+
+       memset(rsp, 0, sizeof(*rsp));
+
+       rsp->max_key_size    = SMP_MAX_ENC_KEY_SIZE;
+       rsp->init_key_dist   = req->init_key_dist & remote_dist;
+       rsp->resp_key_dist   = req->resp_key_dist & local_dist;
+
+       smp->remote_key_dist = rsp->init_key_dist;
+}
+
 static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_pairing rsp, *req = (void *) skb->data;
@@ -942,16 +1658,49 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
                return SMP_UNSPECIFIED;
 
        /* We didn't start the pairing, so match remote */
-       auth = req->auth_req & AUTH_REQ_MASK;
+       auth = req->auth_req & AUTH_REQ_MASK(hdev);
 
        if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) &&
            (auth & SMP_AUTH_BONDING))
                return SMP_PAIRING_NOTSUPP;
 
+       if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+               return SMP_AUTH_REQUIREMENTS;
+
        smp->preq[0] = SMP_CMD_PAIRING_REQ;
        memcpy(&smp->preq[1], req, sizeof(*req));
        skb_pull(skb, sizeof(*req));
 
+       /* SMP over BR/EDR requires special treatment */
+       if (conn->hcon->type == ACL_LINK) {
+               /* We must have a BR/EDR SC link */
+               if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags))
+                       return SMP_CROSS_TRANSP_NOT_ALLOWED;
+
+               set_bit(SMP_FLAG_SC, &smp->flags);
+
+               build_bredr_pairing_cmd(smp, req, &rsp);
+
+               key_size = min(req->max_key_size, rsp.max_key_size);
+               if (check_enc_key_size(conn, key_size))
+                       return SMP_ENC_KEY_SIZE;
+
+               /* Clear bits which are generated but not distributed */
+               smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+
+               smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+               memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
+               smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
+
+               smp_distribute_keys(smp);
+               return 0;
+       }
+
+       build_pairing_cmd(conn, req, &rsp, auth);
+
+       if (rsp.auth_req & SMP_AUTH_SC)
+               set_bit(SMP_FLAG_SC, &smp->flags);
+
        if (conn->hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
                sec_level = BT_SECURITY_MEDIUM;
        else
@@ -970,8 +1719,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
                        return SMP_AUTH_REQUIREMENTS;
        }
 
-       build_pairing_cmd(conn, req, &rsp, auth);
-
        key_size = min(req->max_key_size, rsp.max_key_size);
        if (check_enc_key_size(conn, key_size))
                return SMP_ENC_KEY_SIZE;
@@ -982,7 +1729,18 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
 
        smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
-       SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+       clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
+
+       if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+               SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
+               /* Clear bits which are generated but not distributed */
+               smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+               /* Wait for Public Key from Initiating Device */
+               return 0;
+       } else {
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+       }
 
        /* Request setup of TK */
        ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
@@ -992,11 +1750,46 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        return 0;
 }
 
+static u8 sc_send_public_key(struct smp_chan *smp)
+{
+       struct hci_dev *hdev = smp->conn->hcon->hdev;
+
+       BT_DBG("");
+
+       if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) {
+               BT_DBG("Using debug keys");
+               memcpy(smp->local_pk, debug_pk, 64);
+               memcpy(smp->local_sk, debug_sk, 32);
+               set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+       } else {
+               while (true) {
+                       /* Generate local key pair for Secure Connections */
+                       if (!ecc_make_key(smp->local_pk, smp->local_sk))
+                               return SMP_UNSPECIFIED;
+
+                       /* This is unlikely, but we need to check that
+                        * we didn't accidentially generate a debug key.
+                        */
+                       if (memcmp(smp->local_sk, debug_sk, 32))
+                               break;
+               }
+       }
+
+       SMP_DBG("Local Public Key X: %32phN", smp->local_pk);
+       SMP_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]);
+       SMP_DBG("Local Private Key:  %32phN", smp->local_sk);
+
+       smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk);
+
+       return 0;
+}
+
 static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
+       struct hci_dev *hdev = conn->hcon->hdev;
        u8 key_size, auth;
        int ret;
 
@@ -1016,7 +1809,31 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        if (check_enc_key_size(conn, key_size))
                return SMP_ENC_KEY_SIZE;
 
-       auth = rsp->auth_req & AUTH_REQ_MASK;
+       auth = rsp->auth_req & AUTH_REQ_MASK(hdev);
+
+       if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+               return SMP_AUTH_REQUIREMENTS;
+
+       smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+       memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
+
+       /* Update remote key distribution in case the remote cleared
+        * some bits that we had enabled in our request.
+        */
+       smp->remote_key_dist &= rsp->resp_key_dist;
+
+       /* For BR/EDR this means we're done and can start phase 3 */
+       if (conn->hcon->type == ACL_LINK) {
+               /* Clear bits which are generated but not distributed */
+               smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+               smp_distribute_keys(smp);
+               return 0;
+       }
+
+       if ((req->auth_req & SMP_AUTH_SC) && (auth & SMP_AUTH_SC))
+               set_bit(SMP_FLAG_SC, &smp->flags);
+       else if (conn->hcon->pending_sec_level > BT_SECURITY_HIGH)
+               conn->hcon->pending_sec_level = BT_SECURITY_HIGH;
 
        /* If we need MITM check that it can be achieved */
        if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
@@ -1030,14 +1847,18 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 
        get_random_bytes(smp->prnd, sizeof(smp->prnd));
 
-       smp->prsp[0] = SMP_CMD_PAIRING_RSP;
-       memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
-
        /* Update remote key distribution in case the remote cleared
         * some bits that we had enabled in our request.
         */
        smp->remote_key_dist &= rsp->resp_key_dist;
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+               /* Clear bits which are generated but not distributed */
+               smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+               SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
+               return sc_send_public_key(smp);
+       }
+
        auth |= req->auth_req;
 
        ret = tk_request(conn, 0, auth, req->io_capability, rsp->io_capability);
@@ -1053,6 +1874,28 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        return 0;
 }
 
+static u8 sc_check_confirm(struct smp_chan *smp)
+{
+       struct l2cap_conn *conn = smp->conn;
+
+       BT_DBG("");
+
+       /* Public Key exchange must happen before any other steps */
+       if (!test_bit(SMP_FLAG_REMOTE_PK, &smp->flags))
+               return SMP_UNSPECIFIED;
+
+       if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+               return sc_passkey_round(smp, SMP_CMD_PAIRING_CONFIRM);
+
+       if (conn->hcon->out) {
+               smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+                            smp->prnd);
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+       }
+
+       return 0;
+}
+
 static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct l2cap_chan *chan = conn->smp;
@@ -1066,6 +1909,9 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
        skb_pull(skb, sizeof(smp->pcnf));
 
+       if (test_bit(SMP_FLAG_SC, &smp->flags))
+               return sc_check_confirm(smp);
+
        if (conn->hcon->out) {
                smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
                             smp->prnd);
@@ -1085,6 +1931,10 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
+       struct hci_conn *hcon = conn->hcon;
+       u8 *pkax, *pkbx, *na, *nb;
+       u32 passkey;
+       int err;
 
        BT_DBG("conn %p", conn);
 
@@ -1094,7 +1944,75 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd));
        skb_pull(skb, sizeof(smp->rrnd));
 
-       return smp_random(smp);
+       if (!test_bit(SMP_FLAG_SC, &smp->flags))
+               return smp_random(smp);
+
+       if (hcon->out) {
+               pkax = smp->local_pk;
+               pkbx = smp->remote_pk;
+               na   = smp->prnd;
+               nb   = smp->rrnd;
+       } else {
+               pkax = smp->remote_pk;
+               pkbx = smp->local_pk;
+               na   = smp->rrnd;
+               nb   = smp->prnd;
+       }
+
+       if (smp->method == REQ_OOB) {
+               if (!hcon->out)
+                       smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+                                    sizeof(smp->prnd), smp->prnd);
+               SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+               goto mackey_and_ltk;
+       }
+
+       /* Passkey entry has special treatment */
+       if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+               return sc_passkey_round(smp, SMP_CMD_PAIRING_RANDOM);
+
+       if (hcon->out) {
+               u8 cfm[16];
+
+               err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
+                            smp->rrnd, 0, cfm);
+               if (err)
+                       return SMP_UNSPECIFIED;
+
+               if (memcmp(smp->pcnf, cfm, 16))
+                       return SMP_CONFIRM_FAILED;
+       } else {
+               smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+                            smp->prnd);
+               SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+       }
+
+mackey_and_ltk:
+       /* Generate MacKey and LTK */
+       err = sc_mackey_and_ltk(smp, smp->mackey, smp->tk);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
+               if (hcon->out) {
+                       sc_dhkey_check(smp);
+                       SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+               }
+               return 0;
+       }
+
+       err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
+                                       hcon->dst_type, passkey, 0);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+
+       return 0;
 }
 
 static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
@@ -1102,8 +2020,7 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        struct smp_ltk *key;
        struct hci_conn *hcon = conn->hcon;
 
-       key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
-                                  hcon->role);
+       key = hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role);
        if (!key)
                return false;
 
@@ -1136,8 +2053,7 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
         */
        if (key_pref == SMP_USE_LTK &&
            test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
-           hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
-                                hcon->role))
+           hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role))
                return false;
 
        if (hcon->sec_level >= sec_level)
@@ -1151,6 +2067,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        struct smp_cmd_security_req *rp = (void *) skb->data;
        struct smp_cmd_pairing cp;
        struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
        struct smp_chan *smp;
        u8 sec_level, auth;
 
@@ -1162,7 +2079,10 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (hcon->role != HCI_ROLE_MASTER)
                return SMP_CMD_NOTSUPP;
 
-       auth = rp->auth_req & AUTH_REQ_MASK;
+       auth = rp->auth_req & AUTH_REQ_MASK(hdev);
+
+       if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+               return SMP_AUTH_REQUIREMENTS;
 
        if (hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
                sec_level = BT_SECURITY_MEDIUM;
@@ -1245,6 +2165,9 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
 
        authreq = seclevel_to_authreq(sec_level);
 
+       if (test_bit(HCI_SC_ENABLED, &hcon->hdev->dev_flags))
+               authreq |= SMP_AUTH_SC;
+
        /* Require MITM if IO Capability allows or the security level
         * requires it.
         */
@@ -1432,6 +2355,234 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
        return 0;
 }
 
+static u8 sc_select_method(struct smp_chan *smp)
+{
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_conn *hcon = conn->hcon;
+       struct smp_cmd_pairing *local, *remote;
+       u8 local_mitm, remote_mitm, local_io, remote_io, method;
+
+       if (test_bit(SMP_FLAG_OOB, &smp->flags))
+               return REQ_OOB;
+
+       /* The preq/prsp contain the raw Pairing Request/Response PDUs
+        * which are needed as inputs to some crypto functions. To get
+        * the "struct smp_cmd_pairing" from them we need to skip the
+        * first byte which contains the opcode.
+        */
+       if (hcon->out) {
+               local = (void *) &smp->preq[1];
+               remote = (void *) &smp->prsp[1];
+       } else {
+               local = (void *) &smp->prsp[1];
+               remote = (void *) &smp->preq[1];
+       }
+
+       local_io = local->io_capability;
+       remote_io = remote->io_capability;
+
+       local_mitm = (local->auth_req & SMP_AUTH_MITM);
+       remote_mitm = (remote->auth_req & SMP_AUTH_MITM);
+
+       /* If either side wants MITM, look up the method from the table,
+        * otherwise use JUST WORKS.
+        */
+       if (local_mitm || remote_mitm)
+               method = get_auth_method(smp, local_io, remote_io);
+       else
+               method = JUST_WORKS;
+
+       /* Don't confirm locally initiated pairing attempts */
+       if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
+               method = JUST_WORKS;
+
+       return method;
+}
+
+static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct smp_cmd_public_key *key = (void *) skb->data;
+       struct hci_conn *hcon = conn->hcon;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
+       struct hci_dev *hdev = hcon->hdev;
+       struct smp_cmd_pairing_confirm cfm;
+       int err;
+
+       BT_DBG("conn %p", conn);
+
+       if (skb->len < sizeof(*key))
+               return SMP_INVALID_PARAMS;
+
+       memcpy(smp->remote_pk, key, 64);
+
+       /* Non-initiating device sends its public key after receiving
+        * the key from the initiating device.
+        */
+       if (!hcon->out) {
+               err = sc_send_public_key(smp);
+               if (err)
+                       return err;
+       }
+
+       SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk);
+       SMP_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]);
+
+       if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey))
+               return SMP_UNSPECIFIED;
+
+       SMP_DBG("DHKey %32phN", smp->dhkey);
+
+       set_bit(SMP_FLAG_REMOTE_PK, &smp->flags);
+
+       smp->method = sc_select_method(smp);
+
+       BT_DBG("%s selected method 0x%02x", hdev->name, smp->method);
+
+       /* JUST_WORKS and JUST_CFM result in an unauthenticated key */
+       if (smp->method == JUST_WORKS || smp->method == JUST_CFM)
+               hcon->pending_sec_level = BT_SECURITY_MEDIUM;
+       else
+               hcon->pending_sec_level = BT_SECURITY_FIPS;
+
+       if (!memcmp(debug_pk, smp->remote_pk, 64))
+               set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+
+       if (smp->method == DSP_PASSKEY) {
+               get_random_bytes(&hcon->passkey_notify,
+                                sizeof(hcon->passkey_notify));
+               hcon->passkey_notify %= 1000000;
+               hcon->passkey_entered = 0;
+               smp->passkey_round = 0;
+               if (mgmt_user_passkey_notify(hdev, &hcon->dst, hcon->type,
+                                            hcon->dst_type,
+                                            hcon->passkey_notify,
+                                            hcon->passkey_entered))
+                       return SMP_UNSPECIFIED;
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+               return sc_passkey_round(smp, SMP_CMD_PUBLIC_KEY);
+       }
+
+       if (smp->method == REQ_OOB) {
+               err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk,
+                            smp->rr, 0, cfm.confirm_val);
+               if (err)
+                       return SMP_UNSPECIFIED;
+
+               if (memcmp(cfm.confirm_val, smp->pcnf, 16))
+                       return SMP_CONFIRM_FAILED;
+
+               if (hcon->out)
+                       smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+                                    sizeof(smp->prnd), smp->prnd);
+
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+               return 0;
+       }
+
+       if (hcon->out)
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+       if (smp->method == REQ_PASSKEY) {
+               if (mgmt_user_passkey_request(hdev, &hcon->dst, hcon->type,
+                                             hcon->dst_type))
+                       return SMP_UNSPECIFIED;
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+               set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+               return 0;
+       }
+
+       /* The Initiating device waits for the non-initiating device to
+        * send the confirm value.
+        */
+       if (conn->hcon->out)
+               return 0;
+
+       err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd,
+                    0, cfm.confirm_val);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm);
+       SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+       return 0;
+}
+
+static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct smp_cmd_dhkey_check *check = (void *) skb->data;
+       struct l2cap_chan *chan = conn->smp;
+       struct hci_conn *hcon = conn->hcon;
+       struct smp_chan *smp = chan->data;
+       u8 a[7], b[7], *local_addr, *remote_addr;
+       u8 io_cap[3], r[16], e[16];
+       int err;
+
+       BT_DBG("conn %p", conn);
+
+       if (skb->len < sizeof(*check))
+               return SMP_INVALID_PARAMS;
+
+       memcpy(a, &hcon->init_addr, 6);
+       memcpy(b, &hcon->resp_addr, 6);
+       a[6] = hcon->init_addr_type;
+       b[6] = hcon->resp_addr_type;
+
+       if (hcon->out) {
+               local_addr = a;
+               remote_addr = b;
+               memcpy(io_cap, &smp->prsp[1], 3);
+       } else {
+               local_addr = b;
+               remote_addr = a;
+               memcpy(io_cap, &smp->preq[1], 3);
+       }
+
+       memset(r, 0, sizeof(r));
+
+       if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+               put_unaligned_le32(hcon->passkey_notify, r);
+
+       err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r,
+                    io_cap, remote_addr, local_addr, e);
+       if (err)
+               return SMP_UNSPECIFIED;
+
+       if (memcmp(check->e, e, 16))
+               return SMP_DHKEY_CHECK_FAILED;
+
+       if (!hcon->out) {
+               if (test_bit(SMP_FLAG_WAIT_USER, &smp->flags)) {
+                       set_bit(SMP_FLAG_DHKEY_PENDING, &smp->flags);
+                       return 0;
+               }
+
+               /* Slave sends DHKey check as response to master */
+               sc_dhkey_check(smp);
+       }
+
+       sc_add_ltk(smp);
+
+       if (hcon->out) {
+               hci_le_start_enc(hcon, 0, 0, smp->tk);
+               hcon->enc_key_size = smp->enc_key_size;
+       }
+
+       return 0;
+}
+
+static int smp_cmd_keypress_notify(struct l2cap_conn *conn,
+                                  struct sk_buff *skb)
+{
+       struct smp_cmd_keypress_notify *kp = (void *) skb->data;
+
+       BT_DBG("value 0x%02x", kp->value);
+
+       return 0;
+}
+
 static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb)
 {
        struct l2cap_conn *conn = chan->conn;
@@ -1440,11 +2591,6 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb)
        __u8 code, reason;
        int err = 0;
 
-       if (hcon->type != LE_LINK) {
-               kfree_skb(skb);
-               return 0;
-       }
-
        if (skb->len < 1)
                return -EILSEQ;
 
@@ -1516,6 +2662,18 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb)
                reason = smp_cmd_sign_info(conn, skb);
                break;
 
+       case SMP_CMD_PUBLIC_KEY:
+               reason = smp_cmd_public_key(conn, skb);
+               break;
+
+       case SMP_CMD_DHKEY_CHECK:
+               reason = smp_cmd_dhkey_check(conn, skb);
+               break;
+
+       case SMP_CMD_KEYPRESS_NOTIFY:
+               reason = smp_cmd_keypress_notify(conn, skb);
+               break;
+
        default:
                BT_DBG("Unknown command code 0x%2.2x", code);
                reason = SMP_CMD_NOTSUPP;
@@ -1551,6 +2709,74 @@ static void smp_teardown_cb(struct l2cap_chan *chan, int err)
        l2cap_chan_put(chan);
 }
 
+static void bredr_pairing(struct l2cap_chan *chan)
+{
+       struct l2cap_conn *conn = chan->conn;
+       struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
+       struct smp_cmd_pairing req;
+       struct smp_chan *smp;
+
+       BT_DBG("chan %p", chan);
+
+       /* Only new pairings are interesting */
+       if (!test_bit(HCI_CONN_NEW_LINK_KEY, &hcon->flags))
+               return;
+
+       /* Don't bother if we're not encrypted */
+       if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+               return;
+
+       /* Only master may initiate SMP over BR/EDR */
+       if (hcon->role != HCI_ROLE_MASTER)
+               return;
+
+       /* Secure Connections support must be enabled */
+       if (!test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+               return;
+
+       /* BR/EDR must use Secure Connections for SMP */
+       if (!test_bit(HCI_CONN_AES_CCM, &hcon->flags) &&
+           !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+               return;
+
+       /* If our LE support is not enabled don't do anything */
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return;
+
+       /* Don't bother if remote LE support is not enabled */
+       if (!lmp_host_le_capable(hcon))
+               return;
+
+       /* Remote must support SMP fixed chan for BR/EDR */
+       if (!(conn->remote_fixed_chan & L2CAP_FC_SMP_BREDR))
+               return;
+
+       /* Don't bother if SMP is already ongoing */
+       if (chan->data)
+               return;
+
+       smp = smp_chan_create(conn);
+       if (!smp) {
+               BT_ERR("%s unable to create SMP context for BR/EDR",
+                      hdev->name);
+               return;
+       }
+
+       set_bit(SMP_FLAG_SC, &smp->flags);
+
+       BT_DBG("%s starting SMP over BR/EDR", hdev->name);
+
+       /* Prepare and send the BR/EDR SMP Pairing Request */
+       build_bredr_pairing_cmd(smp, &req, NULL);
+
+       smp->preq[0] = SMP_CMD_PAIRING_REQ;
+       memcpy(&smp->preq[1], &req, sizeof(req));
+
+       smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(req), &req);
+       SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RSP);
+}
+
 static void smp_resume_cb(struct l2cap_chan *chan)
 {
        struct smp_chan *smp = chan->data;
@@ -1559,6 +2785,11 @@ static void smp_resume_cb(struct l2cap_chan *chan)
 
        BT_DBG("chan %p", chan);
 
+       if (hcon->type == ACL_LINK) {
+               bredr_pairing(chan);
+               return;
+       }
+
        if (!smp)
                return;
 
@@ -1573,11 +2804,15 @@ static void smp_resume_cb(struct l2cap_chan *chan)
 static void smp_ready_cb(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
+       struct hci_conn *hcon = conn->hcon;
 
        BT_DBG("chan %p", chan);
 
        conn->smp = chan;
        l2cap_chan_hold(chan);
+
+       if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+               bredr_pairing(chan);
 }
 
 static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -1682,34 +2917,40 @@ static const struct l2cap_ops smp_root_chan_ops = {
        .memcpy_fromiovec       = l2cap_chan_no_memcpy_fromiovec,
 };
 
-int smp_register(struct hci_dev *hdev)
+static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
 {
        struct l2cap_chan *chan;
        struct crypto_blkcipher *tfm_aes;
 
-       BT_DBG("%s", hdev->name);
+       if (cid == L2CAP_CID_SMP_BREDR) {
+               tfm_aes = NULL;
+               goto create_chan;
+       }
 
        tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
        if (IS_ERR(tfm_aes)) {
-               int err = PTR_ERR(tfm_aes);
                BT_ERR("Unable to create crypto context");
-               return err;
+               return ERR_PTR(PTR_ERR(tfm_aes));
        }
 
+create_chan:
        chan = l2cap_chan_create();
        if (!chan) {
                crypto_free_blkcipher(tfm_aes);
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
        }
 
        chan->data = tfm_aes;
 
-       l2cap_add_scid(chan, L2CAP_CID_SMP);
+       l2cap_add_scid(chan, cid);
 
        l2cap_chan_set_defaults(chan);
 
        bacpy(&chan->src, &hdev->bdaddr);
-       chan->src_type = BDADDR_LE_PUBLIC;
+       if (cid == L2CAP_CID_SMP)
+               chan->src_type = BDADDR_LE_PUBLIC;
+       else
+               chan->src_type = BDADDR_BREDR;
        chan->state = BT_LISTEN;
        chan->mode = L2CAP_MODE_BASIC;
        chan->imtu = L2CAP_DEFAULT_MTU;
@@ -1718,20 +2959,14 @@ int smp_register(struct hci_dev *hdev)
        /* Set correct nesting level for a parent/listening channel */
        atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
 
-       hdev->smp_data = chan;
-
-       return 0;
+       return chan;
 }
 
-void smp_unregister(struct hci_dev *hdev)
+static void smp_del_chan(struct l2cap_chan *chan)
 {
-       struct l2cap_chan *chan = hdev->smp_data;
-       struct crypto_blkcipher *tfm_aes;
-
-       if (!chan)
-               return;
+       struct crypto_blkcipher *tfm_aes;
 
-       BT_DBG("%s chan %p", hdev->name, chan);
+       BT_DBG("chan %p", chan);
 
        tfm_aes = chan->data;
        if (tfm_aes) {
@@ -1739,6 +2974,52 @@ void smp_unregister(struct hci_dev *hdev)
                crypto_free_blkcipher(tfm_aes);
        }
 
-       hdev->smp_data = NULL;
        l2cap_chan_put(chan);
 }
+
+int smp_register(struct hci_dev *hdev)
+{
+       struct l2cap_chan *chan;
+
+       BT_DBG("%s", hdev->name);
+
+       chan = smp_add_cid(hdev, L2CAP_CID_SMP);
+       if (IS_ERR(chan))
+               return PTR_ERR(chan);
+
+       hdev->smp_data = chan;
+
+       if (!lmp_sc_capable(hdev) &&
+           !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+               return 0;
+
+       chan = smp_add_cid(hdev, L2CAP_CID_SMP_BREDR);
+       if (IS_ERR(chan)) {
+               int err = PTR_ERR(chan);
+               chan = hdev->smp_data;
+               hdev->smp_data = NULL;
+               smp_del_chan(chan);
+               return err;
+       }
+
+       hdev->smp_bredr_data = chan;
+
+       return 0;
+}
+
+void smp_unregister(struct hci_dev *hdev)
+{
+       struct l2cap_chan *chan;
+
+       if (hdev->smp_bredr_data) {
+               chan = hdev->smp_bredr_data;
+               hdev->smp_bredr_data = NULL;
+               smp_del_chan(chan);
+       }
+
+       if (hdev->smp_data) {
+               chan = hdev->smp_data;
+               hdev->smp_data = NULL;
+               smp_del_chan(chan);
+       }
+}
index f76083b..3296bf4 100644 (file)
@@ -50,10 +50,13 @@ struct smp_cmd_pairing {
 #define SMP_DIST_ENC_KEY       0x01
 #define SMP_DIST_ID_KEY                0x02
 #define SMP_DIST_SIGN          0x04
+#define SMP_DIST_LINK_KEY      0x08
 
 #define SMP_AUTH_NONE          0x00
 #define SMP_AUTH_BONDING       0x01
 #define SMP_AUTH_MITM          0x04
+#define SMP_AUTH_SC            0x08
+#define SMP_AUTH_KEYPRESS      0x10
 
 #define SMP_CMD_PAIRING_CONFIRM        0x03
 struct smp_cmd_pairing_confirm {
@@ -102,7 +105,23 @@ struct smp_cmd_security_req {
        __u8    auth_req;
 } __packed;
 
-#define SMP_CMD_MAX            0x0b
+#define SMP_CMD_PUBLIC_KEY     0x0c
+struct smp_cmd_public_key {
+       __u8    x[32];
+       __u8    y[32];
+} __packed;
+
+#define SMP_CMD_DHKEY_CHECK    0x0d
+struct smp_cmd_dhkey_check {
+       __u8    e[16];
+} __packed;
+
+#define SMP_CMD_KEYPRESS_NOTIFY        0x0e
+struct smp_cmd_keypress_notify {
+       __u8    value;
+} __packed;
+
+#define SMP_CMD_MAX            0x0e
 
 #define SMP_PASSKEY_ENTRY_FAILED       0x01
 #define SMP_OOB_NOT_AVAIL              0x02
@@ -114,6 +133,10 @@ struct smp_cmd_security_req {
 #define SMP_UNSPECIFIED                        0x08
 #define SMP_REPEATED_ATTEMPTS          0x09
 #define SMP_INVALID_PARAMS             0x0a
+#define SMP_DHKEY_CHECK_FAILED         0x0b
+#define SMP_NUMERIC_COMP_FAILED                0x0c
+#define SMP_BREDR_PAIRING_IN_PROGRESS  0x0d
+#define SMP_CROSS_TRANSP_NOT_ALLOWED   0x0e
 
 #define SMP_MIN_ENC_KEY_SIZE           7
 #define SMP_MAX_ENC_KEY_SIZE           16
@@ -123,12 +146,29 @@ enum {
        SMP_STK,
        SMP_LTK,
        SMP_LTK_SLAVE,
+       SMP_LTK_P256,
+       SMP_LTK_P256_DEBUG,
 };
 
+static inline bool smp_ltk_is_sc(struct smp_ltk *key)
+{
+       switch (key->type) {
+       case SMP_LTK_P256:
+       case SMP_LTK_P256_DEBUG:
+               return true;
+       }
+
+       return false;
+}
+
 static inline u8 smp_ltk_sec_level(struct smp_ltk *key)
 {
-       if (key->authenticated)
-               return BT_SECURITY_HIGH;
+       if (key->authenticated) {
+               if (smp_ltk_is_sc(key))
+                       return BT_SECURITY_FIPS;
+               else
+                       return BT_SECURITY_HIGH;
+       }
 
        return BT_SECURITY_MEDIUM;
 }
@@ -145,8 +185,9 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
 int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
 int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
 
-bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr);
-int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa);
+bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
+                    const bdaddr_t *bdaddr);
+int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa);
 
 int smp_register(struct hci_dev *hdev);
 void smp_unregister(struct hci_dev *hdev);
index 290e14f..27eaa65 100644 (file)
@@ -439,7 +439,6 @@ static void lowpan_set_lockdep_class_one(struct net_device *dev,
                          &lowpan_netdev_xmit_lock_key);
 }
 
-
 static int lowpan_dev_init(struct net_device *dev)
 {
        netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL);
@@ -597,7 +596,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
 
        entry->ldev = dev;
 
-       /* Set the lowpan harware address to the wpan hardware address. */
+       /* Set the lowpan hardware address to the wpan hardware address. */
        memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN);
 
        mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
index 26da1e1..d0a1282 100644 (file)
@@ -99,6 +99,7 @@ static int ieee802154_sock_release(struct socket *sock)
        }
        return 0;
 }
+
 static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                                   struct msghdr *msg, size_t len)
 {
@@ -231,7 +232,6 @@ static const struct proto_ops ieee802154_dgram_ops = {
 #endif
 };
 
-
 /* Create a socket. Initialise the socket, blank the addresses
  * set the state.
  */
@@ -320,7 +320,6 @@ drop:
        return NET_RX_DROP;
 }
 
-
 static struct packet_type ieee802154_packet_type = {
        .type = htons(ETH_P_IEEE802154),
        .func = ieee802154_rcv,
@@ -354,6 +353,7 @@ err_dgram:
 out:
        return rc;
 }
+
 static void __exit af_ieee802154_remove(void)
 {
        dev_remove_pack(&ieee802154_packet_type);
index 2c7a93e..d1930b7 100644 (file)
@@ -154,7 +154,6 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
                spin_unlock_bh(&sk->sk_receive_queue.lock);
                return put_user(amount, (int __user *)arg);
        }
-
        }
 
        return -ENOIOCTLCMD;
index 63ee7d6..fa14647 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
  *
  * Copyright 2007, 2008 Siemens AG
  *
@@ -73,7 +73,7 @@ out:
 }
 
 struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
-               int flags, u8 req)
+                                       int flags, u8 req)
 {
        void *hdr;
        struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
@@ -147,7 +147,6 @@ static const struct genl_multicast_group ieee802154_mcgrps[] = {
        [IEEE802154_BEACON_MCGRP] = { .name = IEEE802154_MCAST_BEACON_NAME, },
 };
 
-
 int __init ieee802154_nl_init(void)
 {
        return genl_register_family_with_ops_groups(&nl802154_family,
index fe77f0c..cd91949 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
  *
  * Copyright 2007, 2008 Siemens AG
  *
@@ -346,7 +346,6 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
        else
                page = 0;
 
-
        if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
                ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
                dev_put(dev);
@@ -397,7 +396,6 @@ int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
        else
                page = 0;
 
-
        ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels,
                                                 page, duration);
 
@@ -548,8 +546,6 @@ out:
        return rc;
 }
 
-
-
 static int
 ieee802154_llsec_parse_key_id(struct genl_info *info,
                              struct ieee802154_llsec_key_id *desc)
@@ -765,8 +761,6 @@ out:
        return rc;
 }
 
-
-
 struct llsec_dump_data {
        struct sk_buff *skb;
        int s_idx, s_idx2;
@@ -843,8 +837,6 @@ ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info,
        return rc;
 }
 
-
-
 static int
 ieee802154_llsec_parse_key(struct genl_info *info,
                           struct ieee802154_llsec_key *key)
@@ -989,8 +981,6 @@ int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb)
        return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys);
 }
 
-
-
 static int
 llsec_parse_dev(struct genl_info *info,
                struct ieee802154_llsec_device *dev)
@@ -1121,8 +1111,6 @@ int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb)
        return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs);
 }
 
-
-
 static int llsec_add_devkey(struct net_device *dev, struct genl_info *info)
 {
        struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
@@ -1237,8 +1225,6 @@ int ieee802154_llsec_dump_devkeys(struct sk_buff *skb,
        return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys);
 }
 
-
-
 static int
 llsec_parse_seclevel(struct genl_info *info,
                     struct ieee802154_llsec_seclevel *sl)
index 80a946d..7baf98b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
  *
  * Copyright 2007, 2008 Siemens AG
  *
@@ -94,7 +94,6 @@ int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)
        if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
                return -EINVAL; /* phy name should be null-terminated */
 
-
        phy = wpan_phy_find(name);
        if (!phy)
                return -ENODEV;
index 61e9d29..1674b11 100644 (file)
@@ -221,7 +221,6 @@ static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)
        return NET_RX_SUCCESS;
 }
 
-
 void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb)
 {
        struct sock *sk;
index c7c5142..5d6dae9 100644 (file)
@@ -932,6 +932,21 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
        }
 }
 
+static void
+ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata,
+                            const struct cfg80211_chan_def *chandef)
+{
+       struct ieee80211_sub_if_data *vlan;
+
+       sdata->vif.bss_conf.chandef = *chandef;
+
+       if (sdata->vif.type != NL80211_IFTYPE_AP)
+               return;
+
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+               vlan->vif.bss_conf.chandef = *chandef;
+}
+
 static int
 ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
 {
@@ -994,7 +1009,7 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
        if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
                changed = BSS_CHANGED_BANDWIDTH;
 
-       sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+       ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
 
        if (changed)
                ieee80211_bss_info_change_notify(sdata, changed);
@@ -1336,7 +1351,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
                            sdata->reserved_chandef.width)
                                changed = BSS_CHANGED_BANDWIDTH;
 
-                       sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+                       ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
                        if (changed)
                                ieee80211_bss_info_change_notify(sdata,
                                                                 changed);
@@ -1507,7 +1522,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-       sdata->vif.bss_conf.chandef = *chandef;
+       ieee80211_vif_update_chandef(sdata, chandef);
 
        ret = ieee80211_assign_vif_chanctx(sdata, ctx);
        if (ret) {
@@ -1649,7 +1664,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
                break;
        }
 
-       sdata->vif.bss_conf.chandef = *chandef;
+       ieee80211_vif_update_chandef(sdata, chandef);
 
        ieee80211_recalc_chanctx_chantype(local, ctx);
 
index 538fe4e..4173553 100644 (file)
@@ -520,6 +520,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                sdata->vif.cab_queue = master->vif.cab_queue;
                memcpy(sdata->vif.hw_queue, master->vif.hw_queue,
                       sizeof(sdata->vif.hw_queue));
+               sdata->vif.bss_conf.chandef = master->vif.bss_conf.chandef;
                break;
                }
        case NL80211_IFTYPE_AP:
index ba06cd0..75a9bf5 100644 (file)
@@ -552,13 +552,17 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
        cap = vht_cap.cap;
 
        if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
-               cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
-               cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+               u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+
+               cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+               if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
+                   bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+                       cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
        }
 
        if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
                cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
-               cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+               cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
        }
 
        /*
@@ -2263,9 +2267,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
                                     "detected beacon loss from AP (missed %d beacons) - probing\n",
                                     beacon_loss_count);
 
-               ieee80211_cqm_rssi_notify(&sdata->vif,
-                                         NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
-                                         GFP_KERNEL);
+               ieee80211_cqm_beacon_loss_notify(&sdata->vif, GFP_KERNEL);
        }
 
        /*
@@ -4898,3 +4900,13 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
        cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
 }
 EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
+
+void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       trace_api_cqm_beacon_loss_notify(sdata->local, sdata);
+
+       cfg80211_cqm_beacon_loss_notify(sdata->dev, gfp);
+}
+EXPORT_SYMBOL(ieee80211_cqm_beacon_loss_notify);
index 08ab7d6..d53355b 100644 (file)
@@ -446,7 +446,8 @@ static void rate_fixup_ratelist(struct ieee80211_vif *vif,
         *
         * XXX: Should this check all retry rates?
         */
-       if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) {
+       if (!(rates[0].flags &
+             (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))) {
                u32 basic_rates = vif->bss_conf.basic_rates;
                s8 baserate = basic_rates ? ffs(basic_rates) - 1 : 0;
 
index 18babe3..38652f0 100644 (file)
@@ -37,13 +37,35 @@ static inline void rate_control_tx_status(struct ieee80211_local *local,
        struct rate_control_ref *ref = local->rate_ctrl;
        struct ieee80211_sta *ista = &sta->sta;
        void *priv_sta = sta->rate_ctrl_priv;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
        if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
                return;
 
-       ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
+       if (ref->ops->tx_status)
+               ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
+       else
+               ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
 }
 
+static inline void
+rate_control_tx_status_noskb(struct ieee80211_local *local,
+                            struct ieee80211_supported_band *sband,
+                            struct sta_info *sta,
+                            struct ieee80211_tx_info *info)
+{
+       struct rate_control_ref *ref = local->rate_ctrl;
+       struct ieee80211_sta *ista = &sta->sta;
+       void *priv_sta = sta->rate_ctrl_priv;
+
+       if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
+               return;
+
+       if (WARN_ON_ONCE(!ref->ops->tx_status_noskb))
+               return;
+
+       ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
+}
 
 static inline void rate_control_rate_init(struct sta_info *sta)
 {
index c2b91bf..d51f6b1 100644 (file)
@@ -223,11 +223,10 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
 static void
 minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
                   struct ieee80211_sta *sta, void *priv_sta,
-                  struct sk_buff *skb)
+                  struct ieee80211_tx_info *info)
 {
        struct minstrel_priv *mp = priv;
        struct minstrel_sta_info *mi = priv_sta;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_tx_rate *ar = info->status.rates;
        int i, ndx;
        int success;
@@ -674,7 +673,7 @@ static u32 minstrel_get_expected_throughput(void *priv_sta)
 
 const struct rate_control_ops mac80211_minstrel = {
        .name = "minstrel",
-       .tx_status = minstrel_tx_status,
+       .tx_status_noskb = minstrel_tx_status,
        .get_rate = minstrel_get_rate,
        .rate_init = minstrel_rate_init,
        .alloc = minstrel_alloc,
index d013429..80452cf 100644 (file)
@@ -706,11 +706,10 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
 static void
 minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
                       struct ieee80211_sta *sta, void *priv_sta,
-                      struct sk_buff *skb)
+                      struct ieee80211_tx_info *info)
 {
        struct minstrel_ht_sta_priv *msp = priv_sta;
        struct minstrel_ht_sta *mi = &msp->ht;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_tx_rate *ar = info->status.rates;
        struct minstrel_rate_stats *rate, *rate2;
        struct minstrel_priv *mp = priv;
@@ -718,7 +717,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
        int i;
 
        if (!msp->is_ht)
-               return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb);
+               return mac80211_minstrel.tx_status_noskb(priv, sband, sta,
+                                                        &msp->legacy, info);
 
        /* This packet was aggregated but doesn't carry status info */
        if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -779,9 +779,6 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
        if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
                update = true;
                minstrel_ht_update_stats(mp, mi);
-               if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
-                   mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
-                       minstrel_aggr_check(sta, skb);
        }
 
        if (update)
@@ -1023,6 +1020,10 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
        if (!msp->is_ht)
                return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc);
 
+       if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
+           mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
+               minstrel_aggr_check(sta, txrc->skb);
+
        info->flags |= mi->tx_flags;
        minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
 
@@ -1339,7 +1340,7 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
 
 static const struct rate_control_ops mac80211_minstrel_ht = {
        .name = "minstrel_ht",
-       .tx_status = minstrel_ht_tx_status,
+       .tx_status_noskb = minstrel_ht_tx_status,
        .get_rate = minstrel_ht_get_rate,
        .rate_init = minstrel_ht_rate_init,
        .rate_update = minstrel_ht_rate_update,
index 71de2d3..bb146f3 100644 (file)
@@ -592,10 +592,9 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
 #define STA_LOST_TDLS_PKT_THRESHOLD    10
 #define STA_LOST_TDLS_PKT_TIME         (10*HZ) /* 10secs since last ACK */
 
-static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
+static void ieee80211_lost_packet(struct sta_info *sta,
+                                 struct ieee80211_tx_info *info)
 {
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
        /* This packet was aggregated but doesn't carry status info */
        if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
            !(info->flags & IEEE80211_TX_STAT_AMPDU))
@@ -622,24 +621,13 @@ static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
        sta->lost_packets = 0;
 }
 
-void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+static int ieee80211_tx_get_rates(struct ieee80211_hw *hw,
+                                 struct ieee80211_tx_info *info,
+                                 int *retry_count)
 {
-       struct sk_buff *skb2;
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       __le16 fc;
-       struct ieee80211_supported_band *sband;
-       struct ieee80211_sub_if_data *sdata;
-       struct net_device *prev_dev = NULL;
-       struct sta_info *sta, *tmp;
-       int retry_count = -1, i;
        int rates_idx = -1;
-       bool send_to_cooked;
-       bool acked;
-       struct ieee80211_bar *bar;
-       int rtap_len;
-       int shift = 0;
+       int count = -1;
+       int i;
 
        for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
                if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -657,12 +645,91 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                        break;
                }
 
-               retry_count += info->status.rates[i].count;
+               count += info->status.rates[i].count;
        }
        rates_idx = i - 1;
 
-       if (retry_count < 0)
-               retry_count = 0;
+       if (count < 0)
+               count = 0;
+
+       *retry_count = count;
+       return rates_idx;
+}
+
+void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
+                              struct ieee80211_sta *pubsta,
+                              struct ieee80211_tx_info *info)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_supported_band *sband;
+       int retry_count;
+       int rates_idx;
+       bool acked;
+
+       rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+
+       sband = hw->wiphy->bands[info->band];
+
+       acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
+       if (pubsta) {
+               struct sta_info *sta;
+
+               sta = container_of(pubsta, struct sta_info, sta);
+
+               if (!acked)
+                       sta->tx_retry_failed++;
+               sta->tx_retry_count += retry_count;
+
+               if (acked) {
+                       sta->last_rx = jiffies;
+
+                       if (sta->lost_packets)
+                               sta->lost_packets = 0;
+
+                       /* Track when last TDLS packet was ACKed */
+                       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
+                               sta->last_tdls_pkt_time = jiffies;
+               } else {
+                       ieee80211_lost_packet(sta, info);
+               }
+
+               rate_control_tx_status_noskb(local, sband, sta, info);
+       }
+
+       if (acked) {
+                   local->dot11TransmittedFrameCount++;
+                   if (!pubsta)
+                           local->dot11MulticastTransmittedFrameCount++;
+                   if (retry_count > 0)
+                           local->dot11RetryCount++;
+                   if (retry_count > 1)
+                           local->dot11MultipleRetryCount++;
+       } else {
+               local->dot11FailedCount++;
+       }
+}
+EXPORT_SYMBOL(ieee80211_tx_status_noskb);
+
+void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+       struct sk_buff *skb2;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       __le16 fc;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_sub_if_data *sdata;
+       struct net_device *prev_dev = NULL;
+       struct sta_info *sta, *tmp;
+       int retry_count;
+       int rates_idx;
+       bool send_to_cooked;
+       bool acked;
+       struct ieee80211_bar *bar;
+       int rtap_len;
+       int shift = 0;
+
+       rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
 
        rcu_read_lock();
 
@@ -767,7 +834,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                                if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
                                        sta->last_tdls_pkt_time = jiffies;
                        } else {
-                               ieee80211_lost_packet(sta, skb);
+                               ieee80211_lost_packet(sta, info);
                        }
                }
 
index 85ccfbe..8e461a0 100644 (file)
@@ -1829,6 +1829,12 @@ TRACE_EVENT(api_cqm_rssi_notify,
        )
 );
 
+DEFINE_EVENT(local_sdata_evt, api_cqm_beacon_loss_notify,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+       TP_ARGS(local, sdata)
+);
+
 TRACE_EVENT(api_scan_completed,
        TP_PROTO(struct ieee80211_local *local, bool aborted),
 
index 66ddbbe..058686a 100644 (file)
@@ -60,7 +60,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
        rcu_read_unlock();
 
        /* assume HW handles this */
-       if (tx->rate.flags & IEEE80211_TX_RC_MCS)
+       if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
                return 0;
 
        /* uh huh? */
index bb9664c..974ebe7 100644 (file)
@@ -1339,6 +1339,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
        int ext_rates_len;
        int shift;
        u32 rate_flags;
+       bool have_80mhz = false;
 
        *offset = 0;
 
@@ -1467,7 +1468,15 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
                *offset = noffset;
        }
 
-       if (sband->vht_cap.vht_supported) {
+       /* Check if any channel in this sband supports at least 80 MHz */
+       for (i = 0; i < sband->n_channels; i++) {
+               if (!(sband->channels[i].flags & IEEE80211_CHAN_NO_80MHZ)) {
+                       have_80mhz = true;
+                       break;
+               }
+       }
+
+       if (sband->vht_cap.vht_supported && have_80mhz) {
                if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
                        goto out_err;
                pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
index 38dfc72..9ae8930 100644 (file)
@@ -510,11 +510,9 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
        if (ret)
                goto err;
 
-       if (ndev) {
-               ret = register_netdevice(ndev);
-               if (ret < 0)
-                       goto err;
-       }
+       ret = register_netdevice(ndev);
+       if (ret < 0)
+               goto err;
 
        mutex_lock(&local->iflist_mtx);
        list_add_tail_rcu(&sdata->list, &local->interfaces);
index fa0d523..dcf7395 100644 (file)
@@ -75,8 +75,6 @@ void mac802154_llsec_destroy(struct mac802154_llsec *sec)
        }
 }
 
-
-
 int mac802154_llsec_get_params(struct mac802154_llsec *sec,
                               struct ieee802154_llsec_params *params)
 {
@@ -117,8 +115,6 @@ int mac802154_llsec_set_params(struct mac802154_llsec *sec,
        return 0;
 }
 
-
-
 static struct mac802154_llsec_key*
 llsec_key_alloc(const struct ieee802154_llsec_key *template)
 {
@@ -294,8 +290,6 @@ int mac802154_llsec_key_del(struct mac802154_llsec *sec,
        return -ENOENT;
 }
 
-
-
 static bool llsec_dev_use_shortaddr(__le16 short_addr)
 {
        return short_addr != cpu_to_le16(IEEE802154_ADDR_UNDEF) &&
@@ -304,12 +298,12 @@ static bool llsec_dev_use_shortaddr(__le16 short_addr)
 
 static u32 llsec_dev_hash_short(__le16 short_addr, __le16 pan_id)
 {
-       return ((__force u16) short_addr) << 16 | (__force u16) pan_id;
+       return ((__force u16)short_addr) << 16 | (__force u16)pan_id;
 }
 
 static u64 llsec_dev_hash_long(__le64 hwaddr)
 {
-       return (__force u64) hwaddr;
+       return (__force u64)hwaddr;
 }
 
 static struct mac802154_llsec_device*
@@ -411,8 +405,6 @@ int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr)
        return 0;
 }
 
-
-
 static struct mac802154_llsec_device_key*
 llsec_devkey_find(struct mac802154_llsec_device *dev,
                  const struct ieee802154_llsec_key_id *key)
@@ -475,8 +467,6 @@ int mac802154_llsec_devkey_del(struct mac802154_llsec *sec,
        return 0;
 }
 
-
-
 static struct mac802154_llsec_seclevel*
 llsec_find_seclevel(const struct mac802154_llsec *sec,
                    const struct ieee802154_llsec_seclevel *sl)
@@ -532,8 +522,6 @@ int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec,
        return 0;
 }
 
-
-
 static int llsec_recover_addr(struct mac802154_llsec *sec,
                              struct ieee802154_addr *addr)
 {
@@ -609,7 +597,6 @@ found:
        return llsec_key_get(key);
 }
 
-
 static void llsec_geniv(u8 iv[16], __le64 addr,
                        const struct ieee802154_sechdr *sec)
 {
@@ -786,8 +773,6 @@ fail:
        return rc;
 }
 
-
-
 static struct mac802154_llsec_device*
 llsec_lookup_dev(struct mac802154_llsec *sec,
                 const struct ieee802154_addr *addr)
index 3596b29..5cf019a 100644 (file)
@@ -104,7 +104,6 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
        }
 }
 
-
 int mac802154_get_params(struct net_device *dev,
                         struct ieee802154_llsec_params *params)
 {
@@ -136,7 +135,6 @@ int mac802154_set_params(struct net_device *dev,
        return res;
 }
 
-
 int mac802154_add_key(struct net_device *dev,
                      const struct ieee802154_llsec_key_id *id,
                      const struct ieee802154_llsec_key *key)
@@ -168,7 +166,6 @@ int mac802154_del_key(struct net_device *dev,
        return res;
 }
 
-
 int mac802154_add_dev(struct net_device *dev,
                      const struct ieee802154_llsec_device *llsec_dev)
 {
@@ -198,7 +195,6 @@ int mac802154_del_dev(struct net_device *dev, __le64 dev_addr)
        return res;
 }
 
-
 int mac802154_add_devkey(struct net_device *dev,
                         __le64 device_addr,
                         const struct ieee802154_llsec_device_key *key)
@@ -231,7 +227,6 @@ int mac802154_del_devkey(struct net_device *dev,
        return res;
 }
 
-
 int mac802154_add_seclevel(struct net_device *dev,
                           const struct ieee802154_llsec_seclevel *sl)
 {
@@ -262,7 +257,6 @@ int mac802154_del_seclevel(struct net_device *dev,
        return res;
 }
 
-
 void mac802154_lock_table(struct net_device *dev)
 {
        struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
index 041dbd5..c0d67b2 100644 (file)
@@ -85,8 +85,7 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
        default:
                spin_unlock_bh(&sdata->mib_lock);
                pr_debug("invalid dest mode\n");
-               kfree_skb(skb);
-               return NET_RX_DROP;
+               goto fail;
        }
 
        spin_unlock_bh(&sdata->mib_lock);
index b60aa35..f72be74 100644 (file)
@@ -17,6 +17,9 @@
 
 #include "digital.h"
 
+#define DIGITAL_NFC_DEP_N_RETRY_NACK   2
+#define DIGITAL_NFC_DEP_N_RETRY_ATN    2
+
 #define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4
 #define DIGITAL_NFC_DEP_FRAME_DIR_IN  0xD5
 
 #define DIGITAL_ATR_REQ_MIN_SIZE 16
 #define DIGITAL_ATR_REQ_MAX_SIZE 64
 
-#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30
-#define DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B \
-                               (DIGITAL_LR_BITS_PAYLOAD_SIZE_254B >> 4)
+#define DIGITAL_DID_MAX        14
+
+#define DIGITAL_PAYLOAD_SIZE_MAX       254
+#define DIGITAL_PAYLOAD_BITS_TO_PP(s)  (((s) & 0x3) << 4)
+#define DIGITAL_PAYLOAD_PP_TO_BITS(s)  (((s) >> 4) & 0x3)
+#define DIGITAL_PAYLOAD_BITS_TO_FSL(s) ((s) & 0x3)
+#define DIGITAL_PAYLOAD_FSL_TO_BITS(s) ((s) & 0x3)
+
 #define DIGITAL_GB_BIT 0x02
 
+#define DIGITAL_NFC_DEP_REQ_RES_HEADROOM       2 /* SoD: [SB (NFC-A)] + LEN */
+#define DIGITAL_NFC_DEP_REQ_RES_TAILROOM       2 /* EoD: 2-byte CRC */
+
 #define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0)
 
 #define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10
+#define DIGITAL_NFC_DEP_PFB_MI_BIT     0x10
+#define DIGITAL_NFC_DEP_PFB_NACK_BIT   0x10
+#define DIGITAL_NFC_DEP_PFB_DID_BIT    0x04
 
 #define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
                                ((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT)
-#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb)  ((pfb) & 0x10)
+#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb)  ((pfb) & DIGITAL_NFC_DEP_PFB_MI_BIT)
+#define DIGITAL_NFC_DEP_NACK_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_NACK_BIT)
 #define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08)
-#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04)
+#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_DID_BIT)
 #define DIGITAL_NFC_DEP_PFB_PNI(pfb)     ((pfb) & 0x03)
 
 #define DIGITAL_NFC_DEP_PFB_I_PDU          0x00
@@ -97,6 +112,34 @@ struct digital_dep_req_res {
 
 static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
                                    struct sk_buff *resp);
+static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp);
+
+static const u8 digital_payload_bits_map[4] = {
+       [0] = 64,
+       [1] = 128,
+       [2] = 192,
+       [3] = 254
+};
+
+static u8 digital_payload_bits_to_size(u8 payload_bits)
+{
+       if (payload_bits >= ARRAY_SIZE(digital_payload_bits_map))
+               return 0;
+
+       return digital_payload_bits_map[payload_bits];
+}
+
+static u8 digital_payload_size_to_bits(u8 payload_size)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(digital_payload_bits_map); i++)
+               if (digital_payload_bits_map[i] == payload_size)
+                       return i;
+
+       return 0xff;
+}
 
 static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev,
                                     struct sk_buff *skb)
@@ -129,6 +172,106 @@ static int digital_skb_pull_dep_sod(struct nfc_digital_dev *ddev,
        return 0;
 }
 
+static struct sk_buff *
+digital_send_dep_data_prep(struct nfc_digital_dev *ddev, struct sk_buff *skb,
+                          struct digital_dep_req_res *dep_req_res,
+                          struct digital_data_exch *data_exch)
+{
+       struct sk_buff *new_skb;
+
+       if (skb->len > ddev->remote_payload_max) {
+               dep_req_res->pfb |= DIGITAL_NFC_DEP_PFB_MI_BIT;
+
+               new_skb = digital_skb_alloc(ddev, ddev->remote_payload_max);
+               if (!new_skb) {
+                       kfree_skb(ddev->chaining_skb);
+                       ddev->chaining_skb = NULL;
+
+                       return ERR_PTR(-ENOMEM);
+               }
+
+               skb_reserve(new_skb, ddev->tx_headroom + NFC_HEADER_SIZE +
+                                       DIGITAL_NFC_DEP_REQ_RES_HEADROOM);
+               memcpy(skb_put(new_skb, ddev->remote_payload_max), skb->data,
+                      ddev->remote_payload_max);
+               skb_pull(skb, ddev->remote_payload_max);
+
+               ddev->chaining_skb = skb;
+               ddev->data_exch = data_exch;
+       } else {
+               ddev->chaining_skb = NULL;
+               new_skb = skb;
+       }
+
+       return new_skb;
+}
+
+static struct sk_buff *
+digital_recv_dep_data_gather(struct nfc_digital_dev *ddev, u8 pfb,
+                            struct sk_buff *resp,
+                            int (*send_ack)(struct nfc_digital_dev *ddev,
+                                            struct digital_data_exch
+                                                            *data_exch),
+                            struct digital_data_exch *data_exch)
+{
+       struct sk_buff *new_skb;
+       int rc;
+
+       if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb) && (!ddev->chaining_skb)) {
+               ddev->chaining_skb =
+                       nfc_alloc_recv_skb(8 * ddev->local_payload_max,
+                                          GFP_KERNEL);
+               if (!ddev->chaining_skb) {
+                       rc = -ENOMEM;
+                       goto error;
+               }
+       }
+
+       if (ddev->chaining_skb) {
+               if (resp->len > skb_tailroom(ddev->chaining_skb)) {
+                       new_skb = skb_copy_expand(ddev->chaining_skb,
+                                                 skb_headroom(
+                                                         ddev->chaining_skb),
+                                                 8 * ddev->local_payload_max,
+                                                 GFP_KERNEL);
+                       if (!new_skb) {
+                               rc = -ENOMEM;
+                               goto error;
+                       }
+
+                       kfree_skb(ddev->chaining_skb);
+                       ddev->chaining_skb = new_skb;
+               }
+
+               memcpy(skb_put(ddev->chaining_skb, resp->len), resp->data,
+                      resp->len);
+
+               kfree_skb(resp);
+               resp = NULL;
+
+               if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
+                       rc = send_ack(ddev, data_exch);
+                       if (rc)
+                               goto error;
+
+                       return NULL;
+               }
+
+               resp = ddev->chaining_skb;
+               ddev->chaining_skb = NULL;
+       }
+
+       return resp;
+
+error:
+       kfree_skb(resp);
+
+       kfree_skb(ddev->chaining_skb);
+       ddev->chaining_skb = NULL;
+
+       return ERR_PTR(rc);
+}
+
 static void digital_in_recv_psl_res(struct nfc_digital_dev *ddev, void *arg,
                                    struct sk_buff *resp)
 {
@@ -198,6 +341,8 @@ static int digital_in_send_psl_req(struct nfc_digital_dev *ddev,
 {
        struct sk_buff *skb;
        struct digital_psl_req *psl_req;
+       int rc;
+       u8 payload_size, payload_bits;
 
        skb = digital_skb_alloc(ddev, sizeof(*psl_req));
        if (!skb)
@@ -211,14 +356,24 @@ static int digital_in_send_psl_req(struct nfc_digital_dev *ddev,
        psl_req->cmd = DIGITAL_CMD_PSL_REQ;
        psl_req->did = 0;
        psl_req->brs = (0x2 << 3) | 0x2; /* 424F both directions */
-       psl_req->fsl = DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B;
+
+       payload_size = min(ddev->local_payload_max, ddev->remote_payload_max);
+       payload_bits = digital_payload_size_to_bits(payload_size);
+       psl_req->fsl = DIGITAL_PAYLOAD_BITS_TO_FSL(payload_bits);
+
+       ddev->local_payload_max = payload_size;
+       ddev->remote_payload_max = payload_size;
 
        digital_skb_push_dep_sod(ddev, skb);
 
        ddev->skb_add_crc(skb);
 
-       return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res,
-                                  target);
+       rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res,
+                                target);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
 }
 
 static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
@@ -226,7 +381,7 @@ static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
 {
        struct nfc_target *target = arg;
        struct digital_atr_res *atr_res;
-       u8 gb_len;
+       u8 gb_len, payload_bits;
        int rc;
 
        if (IS_ERR(resp)) {
@@ -256,6 +411,14 @@ static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
 
        atr_res = (struct digital_atr_res *)resp->data;
 
+       payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_res->pp);
+       ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits);
+
+       if (!ddev->remote_payload_max) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
        rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len);
        if (rc)
                goto exit;
@@ -286,6 +449,8 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
        struct sk_buff *skb;
        struct digital_atr_req *atr_req;
        uint size;
+       int rc;
+       u8 payload_bits;
 
        size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len;
 
@@ -314,7 +479,9 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
        atr_req->bs = 0;
        atr_req->br = 0;
 
-       atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+       ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX;
+       payload_bits = digital_payload_size_to_bits(ddev->local_payload_max);
+       atr_req->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits);
 
        if (gb_len) {
                atr_req->pp |= DIGITAL_GB_BIT;
@@ -325,8 +492,113 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
 
        ddev->skb_add_crc(skb);
 
-       return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
-                                  target);
+       rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
+                                target);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static int digital_in_send_ack(struct nfc_digital_dev *ddev,
+                              struct digital_data_exch *data_exch)
+{
+       struct digital_dep_req_res *dep_req;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+
+       dep_req = (struct digital_dep_req_res *)skb->data;
+
+       dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+       dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+       dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+                      ddev->curr_nfc_dep_pni;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       ddev->saved_skb = skb_get(skb);
+       ddev->saved_skb_len = skb->len;
+
+       rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+                                data_exch);
+       if (rc) {
+               kfree_skb(skb);
+               kfree_skb(ddev->saved_skb);
+               ddev->saved_skb = NULL;
+       }
+
+       return rc;
+}
+
+static int digital_in_send_nack(struct nfc_digital_dev *ddev,
+                               struct digital_data_exch *data_exch)
+{
+       struct digital_dep_req_res *dep_req;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+
+       dep_req = (struct digital_dep_req_res *)skb->data;
+
+       dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+       dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+       dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+                      DIGITAL_NFC_DEP_PFB_NACK_BIT | ddev->curr_nfc_dep_pni;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+                                data_exch);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static int digital_in_send_atn(struct nfc_digital_dev *ddev,
+                              struct digital_data_exch *data_exch)
+{
+       struct digital_dep_req_res *dep_req;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+
+       dep_req = (struct digital_dep_req_res *)skb->data;
+
+       dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+       dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+       dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+                                data_exch);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
 }
 
 static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
@@ -355,12 +627,30 @@ static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
 
        ddev->skb_add_crc(skb);
 
+       ddev->saved_skb = skb_get(skb);
+       ddev->saved_skb_len = skb->len;
+
        rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
                                 data_exch);
+       if (rc) {
+               kfree_skb(skb);
+               kfree_skb(ddev->saved_skb);
+               ddev->saved_skb = NULL;
+       }
 
        return rc;
 }
 
+static int digital_in_send_saved_skb(struct nfc_digital_dev *ddev,
+                                    struct digital_data_exch *data_exch)
+{
+       skb_get(ddev->saved_skb);
+       skb_push(ddev->saved_skb, ddev->saved_skb_len);
+
+       return digital_in_send_cmd(ddev, ddev->saved_skb, 1500,
+                                  digital_in_recv_dep_res, data_exch);
+}
+
 static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
                                    struct sk_buff *resp)
 {
@@ -373,25 +663,67 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
                resp = NULL;
+
+               if (((rc != -ETIMEDOUT) || ddev->nack_count) &&
+                   (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) {
+                       ddev->atn_count = 0;
+
+                       rc = digital_in_send_nack(ddev, data_exch);
+                       if (rc)
+                               goto error;
+
+                       return;
+               } else if ((rc == -ETIMEDOUT) &&
+                          (ddev->atn_count++ < DIGITAL_NFC_DEP_N_RETRY_ATN)) {
+                       ddev->nack_count = 0;
+
+                       rc = digital_in_send_atn(ddev, data_exch);
+                       if (rc)
+                               goto error;
+
+                       return;
+               }
+
+               goto exit;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
                goto exit;
        }
 
        rc = ddev->skb_check_crc(resp);
        if (rc) {
+               if ((resp->len >= 4) &&
+                   (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) {
+                       ddev->atn_count = 0;
+
+                       rc = digital_in_send_nack(ddev, data_exch);
+                       if (rc)
+                               goto error;
+
+                       kfree_skb(resp);
+
+                       return;
+               }
+
                PROTOCOL_ERR("14.4.1.6");
                goto error;
        }
 
-       rc = digital_skb_pull_dep_sod(ddev, resp);
-       if (rc) {
-               PROTOCOL_ERR("14.4.1.2");
+       ddev->atn_count = 0;
+       ddev->nack_count = 0;
+
+       if (resp->len > ddev->local_payload_max) {
+               rc = -EMSGSIZE;
                goto exit;
        }
 
+       size = sizeof(struct digital_dep_req_res);
        dep_res = (struct digital_dep_req_res *)resp->data;
 
-       if (resp->len < sizeof(struct digital_dep_req_res) ||
-           dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
+       if (resp->len < size || dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
            dep_res->cmd != DIGITAL_CMD_DEP_RES) {
                rc = -EIO;
                goto error;
@@ -399,6 +731,24 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
 
        pfb = dep_res->pfb;
 
+       if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) {
+               PROTOCOL_ERR("14.8.2.1");
+               rc = -EIO;
+               goto error;
+       }
+
+       if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (size > resp->len) {
+               rc = -EIO;
+               goto error;
+       }
+
+       skb_pull(resp, size);
+
        switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
        case DIGITAL_NFC_DEP_PFB_I_PDU:
                if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
@@ -409,21 +759,71 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
 
                ddev->curr_nfc_dep_pni =
                        DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+               kfree_skb(ddev->saved_skb);
+               ddev->saved_skb = NULL;
+
+               resp = digital_recv_dep_data_gather(ddev, pfb, resp,
+                                                   digital_in_send_ack,
+                                                   data_exch);
+               if (IS_ERR(resp)) {
+                       rc = PTR_ERR(resp);
+                       resp = NULL;
+                       goto error;
+               }
+
+               /* If resp is NULL then we're still chaining so return and
+                * wait for the next part of the PDU.  Else, the PDU is
+                * complete so pass it up.
+                */
+               if (!resp)
+                       return;
+
                rc = 0;
                break;
 
        case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
+               if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
+                       PROTOCOL_ERR("14.12.3.3");
+                       rc = -EIO;
+                       goto exit;
+               }
+
+               ddev->curr_nfc_dep_pni =
+                       DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+               if (ddev->chaining_skb && !DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) {
+                       kfree_skb(ddev->saved_skb);
+                       ddev->saved_skb = NULL;
+
+                       rc = digital_in_send_dep_req(ddev, NULL,
+                                                    ddev->chaining_skb,
+                                                    ddev->data_exch);
+                       if (rc)
+                               goto error;
+
+                       return;
+               }
+
                pr_err("Received a ACK/NACK PDU\n");
-               rc = -EIO;
-               goto error;
+               rc = -EINVAL;
+               goto exit;
 
        case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
-               if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
-                       rc = -EINVAL;
-                       goto error;
+               if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { /* ATN */
+                       rc = digital_in_send_saved_skb(ddev, data_exch);
+                       if (rc) {
+                               kfree_skb(ddev->saved_skb);
+                               goto error;
+                       }
+
+                       return;
                }
 
-               rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]);
+               kfree_skb(ddev->saved_skb);
+               ddev->saved_skb = NULL;
+
+               rc = digital_in_send_rtox(ddev, data_exch, resp->data[0]);
                if (rc)
                        goto error;
 
@@ -431,30 +831,18 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
                return;
        }
 
-       if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
-               pr_err("MI bit set. Chained PDU not supported\n");
-               rc = -EIO;
-               goto error;
-       }
-
-       size = sizeof(struct digital_dep_req_res);
-
-       if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb))
-               size++;
-
-       if (size > resp->len) {
-               rc = -EIO;
-               goto error;
-       }
-
-       skb_pull(resp, size);
-
 exit:
        data_exch->cb(data_exch->cb_context, resp, rc);
 
 error:
        kfree(data_exch);
 
+       kfree_skb(ddev->chaining_skb);
+       ddev->chaining_skb = NULL;
+
+       kfree_skb(ddev->saved_skb);
+       ddev->saved_skb = NULL;
+
        if (rc)
                kfree_skb(resp);
 }
@@ -464,20 +852,47 @@ int digital_in_send_dep_req(struct nfc_digital_dev *ddev,
                            struct digital_data_exch *data_exch)
 {
        struct digital_dep_req_res *dep_req;
+       struct sk_buff *chaining_skb, *tmp_skb;
+       int rc;
 
        skb_push(skb, sizeof(struct digital_dep_req_res));
 
        dep_req = (struct digital_dep_req_res *)skb->data;
+
        dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
        dep_req->cmd = DIGITAL_CMD_DEP_REQ;
        dep_req->pfb = ddev->curr_nfc_dep_pni;
 
-       digital_skb_push_dep_sod(ddev, skb);
+       ddev->atn_count = 0;
+       ddev->nack_count = 0;
 
-       ddev->skb_add_crc(skb);
+       chaining_skb = ddev->chaining_skb;
+
+       tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_req, data_exch);
+       if (IS_ERR(tmp_skb))
+               return PTR_ERR(tmp_skb);
+
+       digital_skb_push_dep_sod(ddev, tmp_skb);
+
+       ddev->skb_add_crc(tmp_skb);
 
-       return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
-                                  data_exch);
+       ddev->saved_skb = skb_get(tmp_skb);
+       ddev->saved_skb_len = tmp_skb->len;
+
+       rc = digital_in_send_cmd(ddev, tmp_skb, 1500, digital_in_recv_dep_res,
+                                data_exch);
+       if (rc) {
+               if (tmp_skb != skb)
+                       kfree_skb(tmp_skb);
+
+               kfree_skb(chaining_skb);
+               ddev->chaining_skb = NULL;
+
+               kfree_skb(ddev->saved_skb);
+               ddev->saved_skb = NULL;
+       }
+
+       return rc;
 }
 
 static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech)
@@ -507,11 +922,106 @@ static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech)
        }
 }
 
+static int digital_tg_send_ack(struct nfc_digital_dev *ddev,
+                              struct digital_data_exch *data_exch)
+{
+       struct digital_dep_req_res *dep_res;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+
+       dep_res = (struct digital_dep_req_res *)skb->data;
+
+       dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+       dep_res->cmd = DIGITAL_CMD_DEP_RES;
+       dep_res->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+                      ddev->curr_nfc_dep_pni;
+
+       if (ddev->did) {
+               dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
+
+               memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+                      sizeof(ddev->did));
+       }
+
+       ddev->curr_nfc_dep_pni =
+               DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       ddev->saved_skb = skb_get(skb);
+       ddev->saved_skb_len = skb->len;
+
+       rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+                                data_exch);
+       if (rc) {
+               kfree_skb(skb);
+               kfree_skb(ddev->saved_skb);
+               ddev->saved_skb = NULL;
+       }
+
+       return rc;
+}
+
+static int digital_tg_send_atn(struct nfc_digital_dev *ddev)
+{
+       struct digital_dep_req_res *dep_res;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+
+       dep_res = (struct digital_dep_req_res *)skb->data;
+
+       dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+       dep_res->cmd = DIGITAL_CMD_DEP_RES;
+       dep_res->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU;
+
+       if (ddev->did) {
+               dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
+
+               memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+                      sizeof(ddev->did));
+       }
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static int digital_tg_send_saved_skb(struct nfc_digital_dev *ddev)
+{
+       skb_get(ddev->saved_skb);
+       skb_push(ddev->saved_skb, ddev->saved_skb_len);
+
+       return digital_tg_send_cmd(ddev, ddev->saved_skb, 1500,
+                                  digital_tg_recv_dep_req, NULL);
+}
+
 static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
                                    struct sk_buff *resp)
 {
        int rc;
        struct digital_dep_req_res *dep_req;
+       u8 pfb;
        size_t size;
 
        if (IS_ERR(resp)) {
@@ -532,6 +1042,11 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
                goto exit;
        }
 
+       if (resp->len > ddev->local_payload_max) {
+               rc = -EMSGSIZE;
+               goto exit;
+       }
+
        size = sizeof(struct digital_dep_req_res);
        dep_req = (struct digital_dep_req_res *)resp->data;
 
@@ -541,34 +1056,147 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
                goto exit;
        }
 
-       if (DIGITAL_NFC_DEP_DID_BIT_SET(dep_req->pfb))
-               size++;
+       pfb = dep_req->pfb;
 
-       if (resp->len < size) {
+       if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) {
+               if (ddev->did && (ddev->did == resp->data[3])) {
+                       size++;
+               } else {
+                       rc = -EIO;
+                       goto exit;
+               }
+       } else if (ddev->did) {
                rc = -EIO;
                goto exit;
        }
 
-       switch (DIGITAL_NFC_DEP_PFB_TYPE(dep_req->pfb)) {
+       if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (size > resp->len) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       skb_pull(resp, size);
+
+       switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
        case DIGITAL_NFC_DEP_PFB_I_PDU:
                pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n");
-               ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(dep_req->pfb);
+
+               if ((ddev->atn_count && (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) !=
+                                               ddev->curr_nfc_dep_pni)) ||
+                   (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni)) {
+                       PROTOCOL_ERR("14.12.3.4");
+                       rc = -EIO;
+                       goto exit;
+               }
+
+               if (ddev->atn_count) {
+                       ddev->atn_count = 0;
+
+                       rc = digital_tg_send_saved_skb(ddev);
+                       if (rc)
+                               goto exit;
+
+                       return;
+               }
+
+               kfree_skb(ddev->saved_skb);
+               ddev->saved_skb = NULL;
+
+               resp = digital_recv_dep_data_gather(ddev, pfb, resp,
+                                                   digital_tg_send_ack, NULL);
+               if (IS_ERR(resp)) {
+                       rc = PTR_ERR(resp);
+                       resp = NULL;
+                       goto exit;
+               }
+
+               /* If resp is NULL then we're still chaining so return and
+                * wait for the next part of the PDU.  Else, the PDU is
+                * complete so pass it up.
+                */
+               if (!resp)
+                       return;
+
+               rc = 0;
                break;
        case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
-               pr_err("Received a ACK/NACK PDU\n");
-               rc = -EINVAL;
-               goto exit;
+               if (!DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* ACK */
+                       if ((ddev->atn_count &&
+                            (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) !=
+                                               ddev->curr_nfc_dep_pni)) ||
+                           (DIGITAL_NFC_DEP_PFB_PNI(pfb) !=
+                                               ddev->curr_nfc_dep_pni) ||
+                           !ddev->chaining_skb || !ddev->saved_skb) {
+                               rc = -EIO;
+                               goto exit;
+                       }
+
+                       if (ddev->atn_count) {
+                               ddev->atn_count = 0;
+
+                               rc = digital_tg_send_saved_skb(ddev);
+                               if (rc)
+                                       goto exit;
+
+                               return;
+                       }
+
+                       kfree_skb(ddev->saved_skb);
+                       ddev->saved_skb = NULL;
+
+                       rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb);
+                       if (rc)
+                               goto exit;
+               } else { /* NACK */
+                       if ((DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) !=
+                                               ddev->curr_nfc_dep_pni) ||
+                           !ddev->saved_skb) {
+                               rc = -EIO;
+                               goto exit;
+                       }
+
+                       ddev->atn_count = 0;
+
+                       rc = digital_tg_send_saved_skb(ddev);
+                       if (rc) {
+                               kfree_skb(ddev->saved_skb);
+                               goto exit;
+                       }
+               }
+
+               return;
        case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
-               pr_err("Received a SUPERVISOR PDU\n");
-               rc = -EINVAL;
-               goto exit;
-       }
+               if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
+                       rc = -EINVAL;
+                       goto exit;
+               }
 
-       skb_pull(resp, size);
+               rc = digital_tg_send_atn(ddev);
+               if (rc)
+                       goto exit;
+
+               ddev->atn_count++;
+
+               kfree_skb(resp);
+               return;
+       }
 
        rc = nfc_tm_data_received(ddev->nfc_dev, resp);
 
 exit:
+       kfree_skb(ddev->chaining_skb);
+       ddev->chaining_skb = NULL;
+
+       ddev->atn_count = 0;
+
+       kfree_skb(ddev->saved_skb);
+       ddev->saved_skb = NULL;
+
        if (rc)
                kfree_skb(resp);
 }
@@ -576,20 +1204,54 @@ exit:
 int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb)
 {
        struct digital_dep_req_res *dep_res;
+       struct sk_buff *chaining_skb, *tmp_skb;
+       int rc;
 
        skb_push(skb, sizeof(struct digital_dep_req_res));
+
        dep_res = (struct digital_dep_req_res *)skb->data;
 
        dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
        dep_res->cmd = DIGITAL_CMD_DEP_RES;
        dep_res->pfb = ddev->curr_nfc_dep_pni;
 
-       digital_skb_push_dep_sod(ddev, skb);
+       if (ddev->did) {
+               dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
 
-       ddev->skb_add_crc(skb);
+               memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+                      sizeof(ddev->did));
+       }
+
+       ddev->curr_nfc_dep_pni =
+               DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+       chaining_skb = ddev->chaining_skb;
+
+       tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_res, NULL);
+       if (IS_ERR(tmp_skb))
+               return PTR_ERR(tmp_skb);
+
+       digital_skb_push_dep_sod(ddev, tmp_skb);
+
+       ddev->skb_add_crc(tmp_skb);
+
+       ddev->saved_skb = skb_get(tmp_skb);
+       ddev->saved_skb_len = tmp_skb->len;
+
+       rc = digital_tg_send_cmd(ddev, tmp_skb, 1500, digital_tg_recv_dep_req,
+                                NULL);
+       if (rc) {
+               if (tmp_skb != skb)
+                       kfree_skb(tmp_skb);
+
+               kfree_skb(chaining_skb);
+               ddev->chaining_skb = NULL;
 
-       return digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
-                                  NULL);
+               kfree_skb(ddev->saved_skb);
+               ddev->saved_skb = NULL;
+       }
+
+       return rc;
 }
 
 static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev,
@@ -632,9 +1294,10 @@ static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did,
 
        ddev->skb_add_crc(skb);
 
+       ddev->curr_nfc_dep_pni = 0;
+
        rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete,
                                 (void *)(unsigned long)rf_tech);
-
        if (rc)
                kfree_skb(skb);
 
@@ -647,7 +1310,7 @@ static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg,
        int rc;
        struct digital_psl_req *psl_req;
        u8 rf_tech;
-       u8 dsi;
+       u8 dsi, payload_size, payload_bits;
 
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
@@ -692,6 +1355,18 @@ static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg,
                goto exit;
        }
 
+       payload_bits = DIGITAL_PAYLOAD_FSL_TO_BITS(psl_req->fsl);
+       payload_size = digital_payload_bits_to_size(payload_bits);
+
+       if (!payload_size || (payload_size > min(ddev->local_payload_max,
+                                                ddev->remote_payload_max))) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       ddev->local_payload_max = payload_size;
+       ddev->remote_payload_max = payload_size;
+
        rc = digital_tg_send_psl_res(ddev, psl_req->did, rf_tech);
 
 exit:
@@ -712,6 +1387,8 @@ static void digital_tg_send_atr_res_complete(struct nfc_digital_dev *ddev,
        if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB)
                offset++;
 
+       ddev->atn_count = 0;
+
        if (resp->data[offset] == DIGITAL_CMD_PSL_REQ)
                digital_tg_recv_psl_req(ddev, arg, resp);
        else
@@ -723,7 +1400,7 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev,
 {
        struct digital_atr_res *atr_res;
        struct sk_buff *skb;
-       u8 *gb;
+       u8 *gb, payload_bits;
        size_t gb_len;
        int rc;
 
@@ -744,7 +1421,11 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev,
        atr_res->cmd = DIGITAL_CMD_ATR_RES;
        memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3));
        atr_res->to = 8;
-       atr_res->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+
+       ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX;
+       payload_bits = digital_payload_size_to_bits(ddev->local_payload_max);
+       atr_res->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits);
+
        if (gb_len) {
                skb_put(skb, gb_len);
 
@@ -756,12 +1437,12 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev,
 
        ddev->skb_add_crc(skb);
 
+       ddev->curr_nfc_dep_pni = 0;
+
        rc = digital_tg_send_cmd(ddev, skb, 999,
                                 digital_tg_send_atr_res_complete, NULL);
-       if (rc) {
+       if (rc)
                kfree_skb(skb);
-               return rc;
-       }
 
        return rc;
 }
@@ -772,7 +1453,7 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
        int rc;
        struct digital_atr_req *atr_req;
        size_t gb_len, min_size;
-       u8 poll_tech_count;
+       u8 poll_tech_count, payload_bits;
 
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
@@ -815,11 +1496,22 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
        atr_req = (struct digital_atr_req *)resp->data;
 
        if (atr_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
-           atr_req->cmd != DIGITAL_CMD_ATR_REQ) {
+           atr_req->cmd != DIGITAL_CMD_ATR_REQ ||
+           atr_req->did > DIGITAL_DID_MAX) {
                rc = -EINVAL;
                goto exit;
        }
 
+       payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_req->pp);
+       ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits);
+
+       if (!ddev->remote_payload_max) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       ddev->did = atr_req->did;
+
        rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
                                     NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED);
        if (rc)
index 677d24b..91df487 100644 (file)
@@ -345,6 +345,9 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
 
        pr_debug("\n");
 
+       if (hdev->gate2pipe[dest_gate] == NFC_HCI_DO_NOT_CREATE_PIPE)
+               return 0;
+
        if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
                return -EADDRINUSE;
 
index 1177082..ef50e77 100644 (file)
@@ -167,6 +167,48 @@ exit:
 void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
                          struct sk_buff *skb)
 {
+       int r = 0;
+       u8 gate = nfc_hci_pipe2gate(hdev, pipe);
+       u8 local_gate, new_pipe;
+       u8 gate_opened = 0x00;
+
+       pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
+
+       switch (cmd) {
+       case NFC_HCI_ADM_NOTIFY_PIPE_CREATED:
+               if (skb->len != 5) {
+                       r = -EPROTO;
+                       break;
+               }
+
+               local_gate = skb->data[3];
+               new_pipe = skb->data[4];
+               nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
+
+               /* save the new created pipe and bind with local gate,
+                * the description for skb->data[3] is destination gate id
+                * but since we received this cmd from host controller, we
+                * are the destination and it is our local gate
+                */
+               hdev->gate2pipe[local_gate] = new_pipe;
+               break;
+       case NFC_HCI_ANY_OPEN_PIPE:
+               /* if the pipe is already created, we allow remote host to
+                * open it
+                */
+               if (gate != 0xff)
+                       nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK,
+                                             &gate_opened, 1);
+               break;
+       case NFC_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
+               nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
+               break;
+       default:
+               pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate);
+               r = -EINVAL;
+               break;
+       }
+
        kfree_skb(skb);
 }
 
@@ -717,6 +759,19 @@ static int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
        return 0;
 }
 
+static int hci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
+                    u8 *apdu, size_t apdu_length,
+                    se_io_cb_t cb, void *cb_context)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->se_io)
+               return hdev->ops->se_io(hdev, se_idx, apdu,
+                                       apdu_length, cb, cb_context);
+
+       return 0;
+}
+
 static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
 {
        mutex_lock(&hdev->msg_tx_mutex);
@@ -830,6 +885,7 @@ static struct nfc_ops hci_nfc_ops = {
        .discover_se = hci_discover_se,
        .enable_se = hci_enable_se,
        .disable_se = hci_disable_se,
+       .se_io = hci_se_io,
 };
 
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
index c4da0c2..3621a90 100644 (file)
@@ -401,7 +401,8 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
        u8 *miux_tlv = NULL, miux_tlv_length;
        u8 *rw_tlv = NULL, rw_tlv_length, rw;
        int err;
-       u16 size = 0, miux;
+       u16 size = 0;
+       __be16 miux;
 
        pr_debug("Sending CONNECT\n");
 
@@ -465,7 +466,8 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
        u8 *miux_tlv = NULL, miux_tlv_length;
        u8 *rw_tlv = NULL, rw_tlv_length, rw;
        int err;
-       u16 size = 0, miux;
+       u16 size = 0;
+       __be16 miux;
 
        pr_debug("Sending CC\n");
 
index 51e7887..b18f07c 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011  Intel Corporation. All rights reserved.
+ * Copyright (C) 2014 Marvell International Ltd.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -1511,8 +1512,10 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
        struct nfc_llcp_local *local;
 
        local = nfc_llcp_find_local(dev);
-       if (local == NULL)
+       if (local == NULL) {
+               kfree_skb(skb);
                return -ENODEV;
+       }
 
        __nfc_llcp_recv(local, skb);
 
index 83bc785..e181e29 100644 (file)
@@ -524,13 +524,13 @@ static int llcp_sock_getname(struct socket *sock, struct sockaddr *uaddr,
 
 static inline unsigned int llcp_accept_poll(struct sock *parent)
 {
-       struct nfc_llcp_sock *llcp_sock, *n, *parent_sock;
+       struct nfc_llcp_sock *llcp_sock, *parent_sock;
        struct sock *sk;
 
        parent_sock = nfc_llcp_sock(parent);
 
-       list_for_each_entry_safe(llcp_sock, n, &parent_sock->accept_queue,
-                                accept_queue) {
+       list_for_each_entry(llcp_sock, &parent_sock->accept_queue,
+                           accept_queue) {
                sk = &llcp_sock->sk;
 
                if (sk->sk_state == LLCP_CONNECTED)
index 90b16cb..51feb5e 100644 (file)
@@ -3,6 +3,7 @@
  *  NFC Controller (NFCC) and a Device Host (DH).
  *
  *  Copyright (C) 2011 Texas Instruments, Inc.
+ *  Copyright (C) 2014 Marvell International Ltd.
  *
  *  Written by Ilan Elias <ilane@ti.com>
  *
@@ -196,18 +197,24 @@ static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt)
        nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
 }
 
+struct nci_rf_discover_param {
+       __u32   im_protocols;
+       __u32   tm_protocols;
+};
+
 static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
 {
+       struct nci_rf_discover_param *param =
+               (struct nci_rf_discover_param *)opt;
        struct nci_rf_disc_cmd cmd;
-       __u32 protocols = opt;
 
        cmd.num_disc_configs = 0;
 
        if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-           (protocols & NFC_PROTO_JEWEL_MASK ||
-            protocols & NFC_PROTO_MIFARE_MASK ||
-            protocols & NFC_PROTO_ISO14443_MASK ||
-            protocols & NFC_PROTO_NFC_DEP_MASK)) {
+           (param->im_protocols & NFC_PROTO_JEWEL_MASK ||
+            param->im_protocols & NFC_PROTO_MIFARE_MASK ||
+            param->im_protocols & NFC_PROTO_ISO14443_MASK ||
+            param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
                cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
                        NCI_NFC_A_PASSIVE_POLL_MODE;
                cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -215,7 +222,7 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
        }
 
        if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-           (protocols & NFC_PROTO_ISO14443_B_MASK)) {
+           (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) {
                cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
                        NCI_NFC_B_PASSIVE_POLL_MODE;
                cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -223,8 +230,8 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
        }
 
        if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-           (protocols & NFC_PROTO_FELICA_MASK ||
-            protocols & NFC_PROTO_NFC_DEP_MASK)) {
+           (param->im_protocols & NFC_PROTO_FELICA_MASK ||
+            param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
                cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
                        NCI_NFC_F_PASSIVE_POLL_MODE;
                cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -232,13 +239,25 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
        }
 
        if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-           (protocols & NFC_PROTO_ISO15693_MASK)) {
+           (param->im_protocols & NFC_PROTO_ISO15693_MASK)) {
                cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
                        NCI_NFC_V_PASSIVE_POLL_MODE;
                cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
                cmd.num_disc_configs++;
        }
 
+       if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) &&
+           (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) {
+               cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
+                       NCI_NFC_A_PASSIVE_LISTEN_MODE;
+               cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
+               cmd.num_disc_configs++;
+               cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
+                       NCI_NFC_F_PASSIVE_LISTEN_MODE;
+               cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
+               cmd.num_disc_configs++;
+       }
+
        nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD,
                     (1 + (cmd.num_disc_configs * sizeof(struct disc_config))),
                     &cmd);
@@ -280,7 +299,7 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt)
 {
        struct nci_rf_deactivate_cmd cmd;
 
-       cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE;
+       cmd.type = opt;
 
        nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD,
                     sizeof(struct nci_rf_deactivate_cmd), &cmd);
@@ -441,6 +460,7 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
 {
        struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
        struct nci_set_config_param param;
+       int rc;
 
        param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
        if ((param.val == NULL) || (param.len == 0))
@@ -451,14 +471,45 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
 
        param.id = NCI_PN_ATR_REQ_GEN_BYTES;
 
+       rc = nci_request(ndev, nci_set_config_req, (unsigned long)&param,
+                        msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+       if (rc)
+               return rc;
+
+       param.id = NCI_LN_ATR_RES_GEN_BYTES;
+
        return nci_request(ndev, nci_set_config_req, (unsigned long)&param,
                           msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
 }
 
+static int nci_set_listen_parameters(struct nfc_dev *nfc_dev)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+       int rc;
+       __u8 val;
+
+       val = NCI_LA_SEL_INFO_NFC_DEP_MASK;
+
+       rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val);
+       if (rc)
+               return rc;
+
+       val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK;
+
+       rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val);
+       if (rc)
+               return rc;
+
+       val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424;
+
+       return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val);
+}
+
 static int nci_start_poll(struct nfc_dev *nfc_dev,
                          __u32 im_protocols, __u32 tm_protocols)
 {
        struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+       struct nci_rf_discover_param param;
        int rc;
 
        if ((atomic_read(&ndev->state) == NCI_DISCOVERY) ||
@@ -476,13 +527,14 @@ static int nci_start_poll(struct nfc_dev *nfc_dev,
            (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) {
                pr_debug("target active or w4 select, implicitly deactivate\n");
 
-               rc = nci_request(ndev, nci_rf_deactivate_req, 0,
+               rc = nci_request(ndev, nci_rf_deactivate_req,
+                                NCI_DEACTIVATE_TYPE_IDLE_MODE,
                                 msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
                if (rc)
                        return -EBUSY;
        }
 
-       if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+       if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
                rc = nci_set_local_general_bytes(nfc_dev);
                if (rc) {
                        pr_err("failed to set local general bytes\n");
@@ -490,7 +542,15 @@ static int nci_start_poll(struct nfc_dev *nfc_dev,
                }
        }
 
-       rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
+       if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               rc = nci_set_listen_parameters(nfc_dev);
+               if (rc)
+                       pr_err("failed to set listen parameters\n");
+       }
+
+       param.im_protocols = im_protocols;
+       param.tm_protocols = tm_protocols;
+       rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)&param,
                         msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
 
        if (!rc)
@@ -509,7 +569,7 @@ static void nci_stop_poll(struct nfc_dev *nfc_dev)
                return;
        }
 
-       nci_request(ndev, nci_rf_deactivate_req, 0,
+       nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE,
                    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
 }
 
@@ -594,7 +654,8 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
        ndev->target_active_prot = 0;
 
        if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
-               nci_request(ndev, nci_rf_deactivate_req, 0,
+               nci_request(ndev, nci_rf_deactivate_req,
+                           NCI_DEACTIVATE_TYPE_SLEEP_MODE,
                            msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
        }
 }
@@ -622,9 +683,24 @@ static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
 
 static int nci_dep_link_down(struct nfc_dev *nfc_dev)
 {
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+       int rc;
+
        pr_debug("entry\n");
 
-       nci_deactivate_target(nfc_dev, NULL);
+       if (nfc_dev->rf_mode == NFC_RF_INITIATOR) {
+               nci_deactivate_target(nfc_dev, NULL);
+       } else {
+               if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE ||
+                   atomic_read(&ndev->state) == NCI_DISCOVERY) {
+                       nci_request(ndev, nci_rf_deactivate_req, 0,
+                               msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
+               }
+
+               rc = nfc_tm_deactivated(nfc_dev);
+               if (rc)
+                       pr_err("error when signaling tm deactivation\n");
+       }
 
        return 0;
 }
@@ -658,18 +734,58 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
        return rc;
 }
 
+static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+       int rc;
+
+       rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
+       if (rc)
+               pr_err("unable to send data\n");
+
+       return rc;
+}
+
 static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
 {
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+       if (ndev->ops->enable_se)
+               return ndev->ops->enable_se(ndev, se_idx);
+
        return 0;
 }
 
 static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
 {
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+       if (ndev->ops->disable_se)
+               return ndev->ops->disable_se(ndev, se_idx);
+
        return 0;
 }
 
 static int nci_discover_se(struct nfc_dev *nfc_dev)
 {
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+       if (ndev->ops->discover_se)
+               return ndev->ops->discover_se(ndev);
+
+       return 0;
+}
+
+static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
+                    u8 *apdu, size_t apdu_length,
+                    se_io_cb_t cb, void *cb_context)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+       if (ndev->ops->se_io)
+               return ndev->ops->se_io(ndev, se_idx, apdu,
+                               apdu_length, cb, cb_context);
+
        return 0;
 }
 
@@ -683,9 +799,11 @@ static struct nfc_ops nci_nfc_ops = {
        .activate_target = nci_activate_target,
        .deactivate_target = nci_deactivate_target,
        .im_transceive = nci_transceive,
+       .tm_send = nci_tm_send,
        .enable_se = nci_enable_se,
        .disable_se = nci_disable_se,
        .discover_se = nci_discover_se,
+       .se_io = nci_se_io,
 };
 
 /* ---- Interface to NCI drivers ---- */
index 427ef2c..a2de2a8 100644 (file)
@@ -3,6 +3,7 @@
  *  NFC Controller (NFCC) and a Device Host (DH).
  *
  *  Copyright (C) 2011 Texas Instruments, Inc.
+ *  Copyright (C) 2014 Marvell International Ltd.
  *
  *  Written by Ilan Elias <ilane@ti.com>
  *
@@ -184,11 +185,16 @@ exit:
 
 static void nci_add_rx_data_frag(struct nci_dev *ndev,
                                 struct sk_buff *skb,
-                                __u8 pbf)
+                                __u8 pbf, __u8 status)
 {
        int reassembly_len;
        int err = 0;
 
+       if (status) {
+               err = status;
+               goto exit;
+       }
+
        if (ndev->rx_data_reassembly) {
                reassembly_len = ndev->rx_data_reassembly->len;
 
@@ -223,13 +229,24 @@ static void nci_add_rx_data_frag(struct nci_dev *ndev,
        }
 
 exit:
-       nci_data_exchange_complete(ndev, skb, err);
+       if (ndev->nfc_dev->rf_mode == NFC_RF_INITIATOR) {
+               nci_data_exchange_complete(ndev, skb, err);
+       } else if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) {
+               /* Data received in Target mode, forward to nfc core */
+               err = nfc_tm_data_received(ndev->nfc_dev, skb);
+               if (err)
+                       pr_err("unable to handle received data\n");
+       } else {
+               pr_err("rf mode unknown\n");
+               kfree_skb(skb);
+       }
 }
 
 /* Rx Data packet */
 void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
        __u8 pbf = nci_pbf(skb->data);
+       __u8 status = 0;
 
        pr_debug("len %d\n", skb->len);
 
@@ -247,8 +264,9 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
            ndev->target_active_prot == NFC_PROTO_ISO15693) {
                /* frame I/F => remove the status byte */
                pr_debug("frame I/F => remove the status byte\n");
+               status = skb->data[skb->len - 1];
                skb_trim(skb, (skb->len - 1));
        }
 
-       nci_add_rx_data_frag(ndev, skb, pbf);
+       nci_add_rx_data_frag(ndev, skb, pbf, nci_to_errno(status));
 }
index 205b35f..22e453c 100644 (file)
@@ -103,7 +103,7 @@ static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev,
                        struct rf_tech_specific_params_nfca_poll *nfca_poll,
                                                     __u8 *data)
 {
-       nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data));
+       nfca_poll->sens_res = __le16_to_cpu(*((__le16 *)data));
        data += 2;
 
        nfca_poll->nfcid1_len = min_t(__u8, *data++, NFC_NFCID1_MAXSIZE);
@@ -167,7 +167,19 @@ static __u8 *nci_extract_rf_params_nfcv_passive_poll(struct nci_dev *ndev,
        return data;
 }
 
-__u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol)
+static __u8 *nci_extract_rf_params_nfcf_passive_listen(struct nci_dev *ndev,
+                       struct rf_tech_specific_params_nfcf_listen *nfcf_listen,
+                                                    __u8 *data)
+{
+       nfcf_listen->local_nfcid2_len = min_t(__u8, *data++,
+                                             NFC_NFCID2_MAXSIZE);
+       memcpy(nfcf_listen->local_nfcid2, data, nfcf_listen->local_nfcid2_len);
+       data += nfcf_listen->local_nfcid2_len;
+
+       return data;
+}
+
+static __u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol)
 {
        if (ndev->ops->get_rfprotocol)
                return ndev->ops->get_rfprotocol(ndev, rf_protocol);
@@ -401,17 +413,29 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
                        struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
 {
        struct activation_params_poll_nfc_dep *poll;
+       struct activation_params_listen_nfc_dep *listen;
 
        switch (ntf->activation_rf_tech_and_mode) {
        case NCI_NFC_A_PASSIVE_POLL_MODE:
        case NCI_NFC_F_PASSIVE_POLL_MODE:
                poll = &ntf->activation_params.poll_nfc_dep;
-               poll->atr_res_len = min_t(__u8, *data++, 63);
+               poll->atr_res_len = min_t(__u8, *data++,
+                                         NFC_ATR_RES_MAXSIZE - 2);
                pr_debug("atr_res_len %d\n", poll->atr_res_len);
                if (poll->atr_res_len > 0)
                        memcpy(poll->atr_res, data, poll->atr_res_len);
                break;
 
+       case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+       case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+               listen = &ntf->activation_params.listen_nfc_dep;
+               listen->atr_req_len = min_t(__u8, *data++,
+                                           NFC_ATR_REQ_MAXSIZE - 2);
+               pr_debug("atr_req_len %d\n", listen->atr_req_len);
+               if (listen->atr_req_len > 0)
+                       memcpy(listen->atr_req, data, listen->atr_req_len);
+               break;
+
        default:
                pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
                       ntf->activation_rf_tech_and_mode);
@@ -444,6 +468,48 @@ static void nci_target_auto_activated(struct nci_dev *ndev,
        nfc_targets_found(ndev->nfc_dev, ndev->targets, ndev->n_targets);
 }
 
+static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
+               struct nci_rf_intf_activated_ntf *ntf)
+{
+       ndev->remote_gb_len = 0;
+
+       if (ntf->activation_params_len <= 0)
+               return NCI_STATUS_OK;
+
+       switch (ntf->activation_rf_tech_and_mode) {
+       case NCI_NFC_A_PASSIVE_POLL_MODE:
+       case NCI_NFC_F_PASSIVE_POLL_MODE:
+               ndev->remote_gb_len = min_t(__u8,
+                       (ntf->activation_params.poll_nfc_dep.atr_res_len
+                                               - NFC_ATR_RES_GT_OFFSET),
+                       NFC_ATR_RES_GB_MAXSIZE);
+               memcpy(ndev->remote_gb,
+                      (ntf->activation_params.poll_nfc_dep.atr_res
+                                               + NFC_ATR_RES_GT_OFFSET),
+                      ndev->remote_gb_len);
+               break;
+
+       case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+       case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+               ndev->remote_gb_len = min_t(__u8,
+                       (ntf->activation_params.listen_nfc_dep.atr_req_len
+                                               - NFC_ATR_REQ_GT_OFFSET),
+                       NFC_ATR_REQ_GB_MAXSIZE);
+               memcpy(ndev->remote_gb,
+                      (ntf->activation_params.listen_nfc_dep.atr_req
+                                               + NFC_ATR_REQ_GT_OFFSET),
+                      ndev->remote_gb_len);
+               break;
+
+       default:
+               pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
+                      ntf->activation_rf_tech_and_mode);
+               return NCI_STATUS_RF_PROTOCOL_ERROR;
+       }
+
+       return NCI_STATUS_OK;
+}
+
 static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
                                             struct sk_buff *skb)
 {
@@ -493,6 +559,16 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
                                &(ntf.rf_tech_specific_params.nfcv_poll), data);
                        break;
 
+               case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+                       /* no RF technology specific parameters */
+                       break;
+
+               case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+                       data = nci_extract_rf_params_nfcf_passive_listen(ndev,
+                               &(ntf.rf_tech_specific_params.nfcf_listen),
+                               data);
+                       break;
+
                default:
                        pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
                               ntf.activation_rf_tech_and_mode);
@@ -546,32 +622,39 @@ exit:
 
                /* store general bytes to be reported later in dep_link_up */
                if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
-                       ndev->remote_gb_len = 0;
-
-                       if (ntf.activation_params_len > 0) {
-                               /* ATR_RES general bytes at offset 15 */
-                               ndev->remote_gb_len = min_t(__u8,
-                                       (ntf.activation_params
-                                       .poll_nfc_dep.atr_res_len
-                                       - NFC_ATR_RES_GT_OFFSET),
-                                       NFC_MAX_GT_LEN);
-                               memcpy(ndev->remote_gb,
-                                      (ntf.activation_params.poll_nfc_dep
-                                      .atr_res + NFC_ATR_RES_GT_OFFSET),
-                                      ndev->remote_gb_len);
-                       }
+                       err = nci_store_general_bytes_nfc_dep(ndev, &ntf);
+                       if (err != NCI_STATUS_OK)
+                               pr_err("unable to store general bytes\n");
                }
        }
 
-       if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
-               /* A single target was found and activated automatically */
-               atomic_set(&ndev->state, NCI_POLL_ACTIVE);
-               if (err == NCI_STATUS_OK)
-                       nci_target_auto_activated(ndev, &ntf);
-       } else {        /* ndev->state == NCI_W4_HOST_SELECT */
-               /* A selected target was activated, so complete the request */
-               atomic_set(&ndev->state, NCI_POLL_ACTIVE);
-               nci_req_complete(ndev, err);
+       if (!(ntf.activation_rf_tech_and_mode & NCI_RF_TECH_MODE_LISTEN_MASK)) {
+               /* Poll mode */
+               if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
+                       /* A single target was found and activated
+                        * automatically */
+                       atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+                       if (err == NCI_STATUS_OK)
+                               nci_target_auto_activated(ndev, &ntf);
+               } else {        /* ndev->state == NCI_W4_HOST_SELECT */
+                       /* A selected target was activated, so complete the
+                        * request */
+                       atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+                       nci_req_complete(ndev, err);
+               }
+       } else {
+               /* Listen mode */
+               atomic_set(&ndev->state, NCI_LISTEN_ACTIVE);
+               if (err == NCI_STATUS_OK &&
+                   ntf.rf_protocol == NCI_RF_PROTOCOL_NFC_DEP) {
+                       err = nfc_tm_activated(ndev->nfc_dev,
+                                              NFC_PROTO_NFC_DEP_MASK,
+                                              NFC_COMM_PASSIVE,
+                                              ndev->remote_gb,
+                                              ndev->remote_gb_len);
+                       if (err != NCI_STATUS_OK)
+                               pr_err("error when signaling tm activation\n");
+               }
        }
 }
 
@@ -595,8 +678,21 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
        if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
                nci_data_exchange_complete(ndev, NULL, -EIO);
 
-       nci_clear_target_list(ndev);
-       atomic_set(&ndev->state, NCI_IDLE);
+       switch (ntf->type) {
+       case NCI_DEACTIVATE_TYPE_IDLE_MODE:
+               nci_clear_target_list(ndev);
+               atomic_set(&ndev->state, NCI_IDLE);
+               break;
+       case NCI_DEACTIVATE_TYPE_SLEEP_MODE:
+       case NCI_DEACTIVATE_TYPE_SLEEP_AF_MODE:
+               atomic_set(&ndev->state, NCI_W4_HOST_SELECT);
+               break;
+       case NCI_DEACTIVATE_TYPE_DISCOVERY:
+               nci_clear_target_list(ndev);
+               atomic_set(&ndev->state, NCI_DISCOVERY);
+               break;
+       }
+
        nci_req_complete(ndev, NCI_STATUS_OK);
 }
 
index 43cb1c1..44989fc 100644 (file)
@@ -810,6 +810,31 @@ out:
        return rc;
 }
 
+static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       u32 device_idx, target_idx, protocol;
+       int rc;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+               return -EINVAL;
+
+       device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(device_idx);
+       if (!dev)
+               return -ENODEV;
+
+       target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
+       protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
+
+       nfc_deactivate_target(dev, target_idx);
+       rc = nfc_activate_target(dev, target_idx, protocol);
+
+       nfc_put_device(dev);
+       return 0;
+}
+
 static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info)
 {
        struct nfc_dev *dev;
@@ -1285,6 +1310,51 @@ static int nfc_genl_dump_ses_done(struct netlink_callback *cb)
        return 0;
 }
 
+static int nfc_se_io(struct nfc_dev *dev, u32 se_idx,
+                    u8 *apdu, size_t apdu_length,
+                    se_io_cb_t cb, void *cb_context)
+{
+       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->se_io) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       se = nfc_find_se(dev, se_idx);
+       if (!se) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       if (se->state != NFC_SE_ENABLED) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       rc = dev->ops->se_io(dev, se_idx, apdu,
+                       apdu_length, cb, cb_context);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
 struct se_io_ctx {
        u32 dev_idx;
        u32 se_idx;
@@ -1367,7 +1437,7 @@ static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
        ctx->dev_idx = dev_idx;
        ctx->se_idx = se_idx;
 
-       return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
+       return nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
 }
 
 static const struct genl_ops nfc_genl_ops[] = {
@@ -1455,6 +1525,11 @@ static const struct genl_ops nfc_genl_ops[] = {
                .doit = nfc_genl_se_io,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_ACTIVATE_TARGET,
+               .doit = nfc_genl_activate_target,
+               .policy = nfc_genl_policy,
+       },
 };
 
 
index 29c8675..22ba971 100644 (file)
@@ -175,7 +175,7 @@ config CFG80211_INTERNAL_REGDB
          Most distributions have a CRDA package.  So if unsure, say N.
 
 config CFG80211_WEXT
-       bool "cfg80211 wireless extensions compatibility"
+       bool
        depends on CFG80211
        select WEXT_CORE
        help
index 4c2e501..53dda77 100644 (file)
@@ -546,6 +546,20 @@ int wiphy_register(struct wiphy *wiphy)
                     !rdev->ops->tdls_cancel_channel_switch)))
                return -EINVAL;
 
+       /*
+        * if a wiphy has unsupported modes for regulatory channel enforcement,
+        * opt-out of enforcement checking
+        */
+       if (wiphy->interface_modes & ~(BIT(NL80211_IFTYPE_STATION) |
+                                      BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                                      BIT(NL80211_IFTYPE_AP) |
+                                      BIT(NL80211_IFTYPE_P2P_GO) |
+                                      BIT(NL80211_IFTYPE_ADHOC) |
+                                      BIT(NL80211_IFTYPE_P2P_DEVICE) |
+                                      BIT(NL80211_IFTYPE_AP_VLAN) |
+                                      BIT(NL80211_IFTYPE_MONITOR)))
+               wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
+
        if (WARN_ON(wiphy->coalesce &&
                    (!wiphy->coalesce->n_rules ||
                     !wiphy->coalesce->n_patterns) &&
index 6e41777..a17d6bc 100644 (file)
@@ -2317,7 +2317,8 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
 static int nl80211_send_chandef(struct sk_buff *msg,
                                const struct cfg80211_chan_def *chandef)
 {
-       WARN_ON(!cfg80211_chandef_valid(chandef));
+       if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+               return -EINVAL;
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
                        chandef->chan->center_freq))
@@ -5421,11 +5422,11 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
        struct nlattr *nl_reg_rule;
-       char *alpha2 = NULL;
-       int rem_reg_rules = 0, r = 0;
+       char *alpha2;
+       int rem_reg_rules, r;
        u32 num_rules = 0, rule_idx = 0, size_of_regd;
        enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
-       struct ieee80211_regdomain *rd = NULL;
+       struct ieee80211_regdomain *rd;
 
        if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
                return -EINVAL;
@@ -6562,8 +6563,6 @@ static int nl80211_dump_survey(struct sk_buff *skb,
        }
 
        while (1) {
-               struct ieee80211_channel *chan;
-
                res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
                if (res == -ENOENT)
                        break;
@@ -6576,9 +6575,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                        goto out;
                }
 
-               chan = ieee80211_get_channel(&rdev->wiphy,
-                                            survey.channel->center_freq);
-               if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+               if (survey.channel->flags & IEEE80211_CHAN_DISABLED) {
                        survey_idx++;
                        continue;
                }
@@ -11770,55 +11767,155 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
 }
 EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
 
-void cfg80211_cqm_rssi_notify(struct net_device *dev,
-                             enum nl80211_cqm_rssi_threshold_event rssi_event,
-                             gfp_t gfp)
+static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
+                                           const char *mac, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-       struct sk_buff *msg;
-       struct nlattr *pinfoattr;
-       void *hdr;
-
-       trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       void **cb;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
-               return;
+               return NULL;
 
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
-       if (!hdr) {
+       cb = (void **)msg->cb;
+
+       cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+       if (!cb[0]) {
                nlmsg_free(msg);
-               return;
+               return NULL;
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
            nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
                goto nla_put_failure;
 
-       pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
-       if (!pinfoattr)
+       if (mac && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac))
                goto nla_put_failure;
 
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
-                       rssi_event))
+       cb[1] = nla_nest_start(msg, NL80211_ATTR_CQM);
+       if (!cb[1])
                goto nla_put_failure;
 
-       nla_nest_end(msg, pinfoattr);
+       cb[2] = rdev;
 
-       genlmsg_end(msg, hdr);
+       return msg;
+ nla_put_failure:
+       nlmsg_free(msg);
+       return NULL;
+}
+
+static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp)
+{
+       void **cb = (void **)msg->cb;
+       struct cfg80211_registered_device *rdev = cb[2];
+
+       nla_nest_end(msg, cb[1]);
+       genlmsg_end(msg, cb[0]);
+
+       memset(msg->cb, 0, sizeof(msg->cb));
 
        genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
                                NL80211_MCGRP_MLME, gfp);
+}
+
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+                             enum nl80211_cqm_rssi_threshold_event rssi_event,
+                             gfp_t gfp)
+{
+       struct sk_buff *msg;
+
+       trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
+       if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
+                   rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
+               return;
+
+       msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+       if (!msg)
+               return;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+                       rssi_event))
+               goto nla_put_failure;
+
+       cfg80211_send_cqm(msg, gfp);
+
        return;
 
  nla_put_failure:
-       genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
 EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
 
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+                            const u8 *peer, u32 num_packets,
+                            u32 rate, u32 intvl, gfp_t gfp)
+{
+       struct sk_buff *msg;
+
+       msg = cfg80211_prepare_cqm(dev, peer, gfp);
+       if (!msg)
+               return;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
+               goto nla_put_failure;
+
+       cfg80211_send_cqm(msg, gfp);
+       return;
+
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+                                const u8 *peer, u32 num_packets, gfp_t gfp)
+{
+       struct sk_buff *msg;
+
+       trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
+       msg = cfg80211_prepare_cqm(dev, peer, gfp);
+       if (!msg)
+               return;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
+               goto nla_put_failure;
+
+       cfg80211_send_cqm(msg, gfp);
+       return;
+
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
+
+void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
+{
+       struct sk_buff *msg;
+
+       msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+       if (!msg)
+               return;
+
+       if (nla_put_flag(msg, NL80211_ATTR_CQM_BEACON_LOSS_EVENT))
+               goto nla_put_failure;
+
+       cfg80211_send_cqm(msg, gfp);
+       return;
+
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify);
+
 static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
                                     struct net_device *netdev, const u8 *bssid,
                                     const u8 *replay_ctr, gfp_t gfp)
@@ -12007,59 +12104,6 @@ void cfg80211_ch_switch_started_notify(struct net_device *dev,
 }
 EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
 
-void cfg80211_cqm_txe_notify(struct net_device *dev,
-                            const u8 *peer, u32 num_packets,
-                            u32 rate, u32 intvl, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-       struct sk_buff *msg;
-       struct nlattr *pinfoattr;
-       void *hdr;
-
-       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
-       if (!msg)
-               return;
-
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
-       if (!hdr) {
-               nlmsg_free(msg);
-               return;
-       }
-
-       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
-           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
-               goto nla_put_failure;
-
-       pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
-       if (!pinfoattr)
-               goto nla_put_failure;
-
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
-               goto nla_put_failure;
-
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
-               goto nla_put_failure;
-
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
-               goto nla_put_failure;
-
-       nla_nest_end(msg, pinfoattr);
-
-       genlmsg_end(msg, hdr);
-
-       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
-                               NL80211_MCGRP_MLME, gfp);
-       return;
-
- nla_put_failure:
-       genlmsg_cancel(msg, hdr);
-       nlmsg_free(msg);
-}
-EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
-
 void
 nl80211_radar_notify(struct cfg80211_registered_device *rdev,
                     const struct cfg80211_chan_def *chandef,
@@ -12108,54 +12152,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void cfg80211_cqm_pktloss_notify(struct net_device *dev,
-                                const u8 *peer, u32 num_packets, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-       struct sk_buff *msg;
-       struct nlattr *pinfoattr;
-       void *hdr;
-
-       trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
-
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
-       if (!msg)
-               return;
-
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
-       if (!hdr) {
-               nlmsg_free(msg);
-               return;
-       }
-
-       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
-           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
-               goto nla_put_failure;
-
-       pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
-       if (!pinfoattr)
-               goto nla_put_failure;
-
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
-               goto nla_put_failure;
-
-       nla_nest_end(msg, pinfoattr);
-
-       genlmsg_end(msg, hdr);
-
-       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
-                               NL80211_MCGRP_MLME, gfp);
-       return;
-
- nla_put_failure:
-       genlmsg_cancel(msg, hdr);
-       nlmsg_free(msg);
-}
-EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
-
 void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
                           u64 cookie, bool acked, gfp_t gfp)
 {
index 32d8310..47be616 100644 (file)
@@ -56,6 +56,7 @@
 #include <net/cfg80211.h>
 #include "core.h"
 #include "reg.h"
+#include "rdev-ops.h"
 #include "regdb.h"
 #include "nl80211.h"
 
 #define REG_DBG_PRINT(args...)
 #endif
 
+/*
+ * Grace period we give before making sure all current interfaces reside on
+ * channels allowed by the current regulatory domain.
+ */
+#define REG_ENFORCE_GRACE_MS 60000
+
 /**
  * enum reg_request_treatment - regulatory request treatment
  *
@@ -210,6 +217,9 @@ struct reg_beacon {
        struct ieee80211_channel chan;
 };
 
+static void reg_check_chans_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work);
+
 static void reg_todo(struct work_struct *work);
 static DECLARE_WORK(reg_work, reg_todo);
 
@@ -1518,6 +1528,96 @@ static void reg_call_notifier(struct wiphy *wiphy,
                wiphy->reg_notifier(wiphy, request);
 }
 
+static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+       struct ieee80211_channel *ch;
+       struct cfg80211_chan_def chandef;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       bool ret = true;
+
+       wdev_lock(wdev);
+
+       if (!wdev->netdev || !netif_running(wdev->netdev))
+               goto out;
+
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+               if (!wdev->beacon_interval)
+                       goto out;
+
+               ret = cfg80211_reg_can_beacon(wiphy,
+                                             &wdev->chandef, wdev->iftype);
+               break;
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_ADHOC:
+               if (!wdev->current_bss ||
+                   !wdev->current_bss->pub.channel)
+                       goto out;
+
+               ch = wdev->current_bss->pub.channel;
+               if (rdev->ops->get_channel &&
+                   !rdev_get_channel(rdev, wdev, &chandef))
+                       ret = cfg80211_chandef_usable(wiphy, &chandef,
+                                                     IEEE80211_CHAN_DISABLED);
+               else
+                       ret = !(ch->flags & IEEE80211_CHAN_DISABLED);
+               break;
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_P2P_DEVICE:
+               /* no enforcement required */
+               break;
+       default:
+               /* others not implemented for now */
+               WARN_ON(1);
+               break;
+       }
+
+out:
+       wdev_unlock(wdev);
+       return ret;
+}
+
+static void reg_leave_invalid_chans(struct wiphy *wiphy)
+{
+       struct wireless_dev *wdev;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+       ASSERT_RTNL();
+
+       list_for_each_entry(wdev, &rdev->wdev_list, list)
+               if (!reg_wdev_chan_valid(wiphy, wdev))
+                       cfg80211_leave(rdev, wdev);
+}
+
+static void reg_check_chans_work(struct work_struct *work)
+{
+       struct cfg80211_registered_device *rdev;
+
+       REG_DBG_PRINT("Verifying active interfaces after reg change\n");
+       rtnl_lock();
+
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list)
+               if (!(rdev->wiphy.regulatory_flags &
+                     REGULATORY_IGNORE_STALE_KICKOFF))
+                       reg_leave_invalid_chans(&rdev->wiphy);
+
+       rtnl_unlock();
+}
+
+static void reg_check_channels(void)
+{
+       /*
+        * Give usermode a chance to do something nicer (move to another
+        * channel, orderly disconnection), before forcing a disconnection.
+        */
+       mod_delayed_work(system_power_efficient_wq,
+                        &reg_check_chans,
+                        msecs_to_jiffies(REG_ENFORCE_GRACE_MS));
+}
+
 static void wiphy_update_regulatory(struct wiphy *wiphy,
                                    enum nl80211_reg_initiator initiator)
 {
@@ -1557,6 +1657,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
                wiphy = &rdev->wiphy;
                wiphy_update_regulatory(wiphy, initiator);
        }
+
+       reg_check_channels();
 }
 
 static void handle_channel_custom(struct wiphy *wiphy,
@@ -1976,8 +2078,10 @@ static void reg_process_hint(struct regulatory_request *reg_request)
 
        /* This is required so that the orig_* parameters are saved */
        if (treatment == REG_REQ_ALREADY_SET && wiphy &&
-           wiphy->regulatory_flags & REGULATORY_STRICT_REG)
+           wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
                wiphy_update_regulatory(wiphy, reg_request->initiator);
+               reg_check_channels();
+       }
 
        return;
 
@@ -2858,6 +2962,7 @@ void regulatory_exit(void)
 
        cancel_work_sync(&reg_work);
        cancel_delayed_work_sync(&reg_timeout);
+       cancel_delayed_work_sync(&reg_check_chans);
 
        /* Lock to suppress warnings */
        rtnl_lock();