]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge branch 'for-davem-2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorDavid S. Miller <davem@davemloft.net>
Wed, 10 Dec 2014 18:17:23 +0000 (13:17 -0500)
committerDavid S. Miller <davem@davemloft.net>
Wed, 10 Dec 2014 18:17:23 +0000 (13:17 -0500)
More iov_iter work for the networking from Al Viro.

Signed-off-by: David S. Miller <davem@davemloft.net>
281 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/isdn/hisax/hfc_2bs0.c
drivers/isdn/hisax/hfc_sx.c
drivers/isdn/hisax/hfc_usb.c
drivers/isdn/hisax/ipacx.c
drivers/isdn/hisax/isdnl1.c
drivers/isdn/hisax/isdnl3.c
drivers/isdn/hysdn/hycapi.c
drivers/isdn/pcbit/layer2.c
drivers/net/can/Makefile
drivers/net/can/cc770/cc770.c
drivers/net/can/dev.c
drivers/net/can/flexcan.c
drivers/net/can/mscan/mscan.c
drivers/net/can/sja1000/sja1000.c
drivers/net/can/slcan.c
drivers/net/can/vcan.c
drivers/net/dummy.c
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/broadcom/b44.c
drivers/net/ethernet/broadcom/bcmsysport.c
drivers/net/ethernet/chelsio/cxgb/sge.c
drivers/net/ethernet/dec/tulip/dmfe.c
drivers/net/ethernet/dec/tulip/uli526x.c
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/hp/hp100.c
drivers/net/ethernet/intel/i40e/i40e_adminq.c
drivers/net/ethernet/intel/i40e/i40e_adminq.h
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_nvm.c
drivers/net/ethernet/intel/i40e/i40e_type.h
drivers/net/ethernet/intel/i40evf/i40e_adminq.c
drivers/net/ethernet/intel/i40evf/i40e_adminq.h
drivers/net/ethernet/intel/i40evf/i40e_type.h
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/rocker/rocker.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/can/dev.h
include/linux/platform_data/st21nfca.h
include/linux/platform_data/st21nfcb.h
include/linux/tcp.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/can/error.h
include/uapi/linux/if_bridge.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/can/af_can.c
net/can/bcm.c
net/can/gw.c
net/can/raw.c
net/core/dev.c
net/core/rtnetlink.c
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/ipv4/tcp.c
net/ipv4/tcp_output.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/sched/cls_cgroup.c
net/sched/cls_flow.c
net/sched/cls_fw.c
net/sched/cls_route.c
net/sched/cls_rsvp.h
net/sched/cls_tcindex.c
net/tipc/name_table.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 c95153559ed2073ebbb96f895dd15dd02c729dfb..cb3221d2436bab6abe6a4404cef66f2085f97f1d 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 4547dc238fc7022967f9ebc380b2e8e2d22d19ae..364f080768d02b2a13ea621da81f880685c39579 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 25c874da5f2b676a156ab323ef8e94eb51504a4f..fce7588962807771a29ce1336ecc559f7e235216 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 023d35e3c7a75b573a52d99cbda5bd98c64e017f..1828ed8cae7a32119cba96b51c55c921bdea71d1 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 38ad66289ad6206204138a31d4d4bda83a4297c9..330f8f84928d4d883be8b246eb78b90bad7f6159 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 1d7db20648899103578d3badeb84aa6f38ec42f6..30939c993d94cc7e832063501f1bca4a3dd4d530 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 550bce089fa6c428467882d23c977ed40f37a532..0057c0b7a7761e4053e1183f467502b37dbc93a1 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 453559f98a75e4a895bf1a1086e1d5e754c2509a..1a3bd064c44249a7f543d6e8c9eccdb7eefec92d 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 7c13d7a8d83cb035fe396f770bc883cf14703d04..31dd24ac99269ae36b04e1b05d20a80f4af65f0b 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 838531b6a60e487d69b958bf1fd759d150e54e75..14dada42874ef7aa709311a5e69f9a526ad5a26b 100644 (file)
@@ -31,7 +31,7 @@ WaitForBusy(struct IsdnCardState *cs)
                to--;
        }
        if (!to) {
-               printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+               printk(KERN_WARNING "HiSax: %s timeout\n", __func__);
                return (0);
        } else
                return (to);
index fa1fefd711cde875fbcdf9b56a20f5375f39ced3..b1fad81f0722e64b2e0d671215e8dc89c295f4a1 100644 (file)
@@ -1159,7 +1159,8 @@ hfcsx_l2l1(struct PStack *st, int pr, void *arg)
        case (PH_PULL | INDICATION):
                spin_lock_irqsave(&bcs->cs->lock, flags);
                if (bcs->tx_skb) {
-                       printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+                       printk(KERN_WARNING "%s: this shouldn't happen\n",
+                              __func__);
                } else {
 //                             test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
                        bcs->tx_skb = skb;
index 849a80752685bdab2ad945824eadd03c924ff7f5..678bd5224bc338a2767a106fb8057f544f218050 100644 (file)
@@ -927,9 +927,8 @@ start_int_fifo(usb_fifo *fifo)
        fifo->active = 1;       /* must be marked active */
        errcode = usb_submit_urb(fifo->urb, GFP_KERNEL);
        if (errcode) {
-               printk(KERN_ERR
-                      "HFC-S USB: submit URB error(start_int_info): status:%i\n",
-                      errcode);
+               printk(KERN_ERR "HFC-S USB: submit URB error(%s): status:%i\n",
+                      __func__, errcode);
                fifo->active = 0;
                fifo->skbuff = NULL;
        }
index 5faa5de24305623bd38bcd4b0047f0063708f172..9cc26b40a43771dee4d670f2416711897bc9ac1a 100644 (file)
@@ -580,7 +580,7 @@ bch_fill_fifo(struct BCState *bcs)
        if (cs->debug & L1_DEB_HSCX_FIFO) {
                char *t = bcs->blog;
 
-               t += sprintf(t, "chb_fill_fifo() B-%d cnt %d", hscx, count);
+               t += sprintf(t, "%s() B-%d cnt %d", __func__, hscx, count);
                QuickHex(t, ptr, count);
                debugl1(cs, "%s", bcs->blog);
        }
index 800095781bfb7daa11ee5dba382176f9ba012473..a560842c0e48d4967f7b39d19e0a005553dc644f 100644 (file)
@@ -867,7 +867,7 @@ l1_msg(struct IsdnCardState *cs, int pr, void *arg) {
                        break;
                default:
                        if (cs->debug)
-                               debugl1(cs, "l1msg %04X unhandled", pr);
+                               debugl1(cs, "%s %04X unhandled", __func__, pr);
                        break;
                }
                st = st->next;
index 45b03840f71691b4495fa6004f4e34c5a7e0d0ec..c754706f83cdc190ca18e299896590430e7b9824 100644 (file)
@@ -153,7 +153,7 @@ void
 newl3state(struct l3_process *pc, int state)
 {
        if (pc->debug & L3_DEB_STATE)
-               l3_debug(pc->st, "newstate cr %d %d --> %d",
+               l3_debug(pc->st, "%s cr %d %d --> %d", __func__,
                         pc->callref & 0x7F,
                         pc->state, state);
        pc->state = state;
index 00aad10507d8a05710b5955cb47f1c4c97612fd4..93bae94314a69e7bb2a4502475c3a348e67808a4 100644 (file)
@@ -501,7 +501,7 @@ static char *hycapi_procinfo(struct capi_ctr *ctrl)
 {
        hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
 #ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_proc_info\n");
+       printk(KERN_NOTICE "%s\n", __func__);
 #endif
        if (!cinfo)
                return "";
index 42ecfef80132b098735ab2a4d96bc9ec2094eece..46e1240ae0741a54008a976602b11a90f00ea685 100644 (file)
@@ -85,7 +85,6 @@ pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum,
        }
        if ((frame = kmalloc(sizeof(struct frame_buf),
                             GFP_ATOMIC)) == NULL) {
-               printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
                dev_kfree_skb(skb);
                return -1;
        }
index fc9304143f449c8eee3aaddcaf1d701fc90b7bb5..c533c62b0f5ee7456c152e29f07e302924edb8a1 100644 (file)
@@ -29,4 +29,5 @@ obj-$(CONFIG_CAN_GRCAN)               += grcan.o
 obj-$(CONFIG_CAN_RCAR)         += rcar_can.o
 obj-$(CONFIG_CAN_XILINXCAN)    += xilinx_can.o
 
-subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+subdir-ccflags-y += -D__CHECK_ENDIAN__
+subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
index d8379278d648a2160f63d386eb5056dc47d70b7a..c486fe510f370957944f9082e1712cfa3cb7aa8f 100644 (file)
@@ -60,7 +60,7 @@ MODULE_DESCRIPTION(KBUILD_MODNAME "CAN netdevice driver");
  *
  * The message objects 1..14 can be used for TX and RX while the message
  * objects 15 is optimized for RX. It has a shadow register for reliable
- * data receiption under heavy bus load. Therefore it makes sense to use
+ * data reception under heavy bus load. Therefore it makes sense to use
  * this message object for the needed use case. The frame type (EFF/SFF)
  * for the message object 15 can be defined via kernel module parameter
  * "msgobj15_eff". If not equal 0, it will receive 29-bit EFF frames,
index 2cfe5012e4e58c3c4c51b968fba5c9c029e0db44..3ec8f6f25e5f979e16838930295fe4cd66b2841c 100644 (file)
@@ -273,6 +273,84 @@ static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
        return err;
 }
 
+static void can_update_state_error_stats(struct net_device *dev,
+                                        enum can_state new_state)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (new_state <= priv->state)
+               return;
+
+       switch (new_state) {
+       case CAN_STATE_ERROR_WARNING:
+               priv->can_stats.error_warning++;
+               break;
+       case CAN_STATE_ERROR_PASSIVE:
+               priv->can_stats.error_passive++;
+               break;
+       case CAN_STATE_BUS_OFF:
+       default:
+               break;
+       };
+}
+
+static int can_tx_state_to_frame(struct net_device *dev, enum can_state state)
+{
+       switch (state) {
+       case CAN_STATE_ERROR_ACTIVE:
+               return CAN_ERR_CRTL_ACTIVE;
+       case CAN_STATE_ERROR_WARNING:
+               return CAN_ERR_CRTL_TX_WARNING;
+       case CAN_STATE_ERROR_PASSIVE:
+               return CAN_ERR_CRTL_TX_PASSIVE;
+       default:
+               return 0;
+       }
+}
+
+static int can_rx_state_to_frame(struct net_device *dev, enum can_state state)
+{
+       switch (state) {
+       case CAN_STATE_ERROR_ACTIVE:
+               return CAN_ERR_CRTL_ACTIVE;
+       case CAN_STATE_ERROR_WARNING:
+               return CAN_ERR_CRTL_RX_WARNING;
+       case CAN_STATE_ERROR_PASSIVE:
+               return CAN_ERR_CRTL_RX_PASSIVE;
+       default:
+               return 0;
+       }
+}
+
+void can_change_state(struct net_device *dev, struct can_frame *cf,
+                     enum can_state tx_state, enum can_state rx_state)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       enum can_state new_state = max(tx_state, rx_state);
+
+       if (unlikely(new_state == priv->state)) {
+               netdev_warn(dev, "%s: oops, state did not change", __func__);
+               return;
+       }
+
+       netdev_dbg(dev, "New error state: %d\n", new_state);
+
+       can_update_state_error_stats(dev, new_state);
+       priv->state = new_state;
+
+       if (unlikely(new_state == CAN_STATE_BUS_OFF)) {
+               cf->can_id |= CAN_ERR_BUSOFF;
+               return;
+       }
+
+       cf->can_id |= CAN_ERR_CRTL;
+       cf->data[1] |= tx_state >= rx_state ?
+                      can_tx_state_to_frame(dev, tx_state) : 0;
+       cf->data[1] |= tx_state <= rx_state ?
+                      can_rx_state_to_frame(dev, rx_state) : 0;
+}
+EXPORT_SYMBOL_GPL(can_change_state);
+
 /*
  * Local echo of CAN messages
  *
index 60f86bd0434af7cd93c394677809a3e60932d768..dde05486bc9917fb88126b79933c53d5868bd0e7 100644 (file)
@@ -577,98 +577,30 @@ static int flexcan_poll_bus_err(struct net_device *dev, u32 reg_esr)
        return 1;
 }
 
-static void do_state(struct net_device *dev,
-                    struct can_frame *cf, enum can_state new_state)
-{
-       struct flexcan_priv *priv = netdev_priv(dev);
-       struct can_berr_counter bec;
-
-       __flexcan_get_berr_counter(dev, &bec);
-
-       switch (priv->can.state) {
-       case CAN_STATE_ERROR_ACTIVE:
-               /*
-                * from: ERROR_ACTIVE
-                * to  : ERROR_WARNING, ERROR_PASSIVE, BUS_OFF
-                * =>  : there was a warning int
-                */
-               if (new_state >= CAN_STATE_ERROR_WARNING &&
-                   new_state <= CAN_STATE_BUS_OFF) {
-                       netdev_dbg(dev, "Error Warning IRQ\n");
-                       priv->can.can_stats.error_warning++;
-
-                       cf->can_id |= CAN_ERR_CRTL;
-                       cf->data[1] = (bec.txerr > bec.rxerr) ?
-                               CAN_ERR_CRTL_TX_WARNING :
-                               CAN_ERR_CRTL_RX_WARNING;
-               }
-       case CAN_STATE_ERROR_WARNING:   /* fallthrough */
-               /*
-                * from: ERROR_ACTIVE, ERROR_WARNING
-                * to  : ERROR_PASSIVE, BUS_OFF
-                * =>  : error passive int
-                */
-               if (new_state >= CAN_STATE_ERROR_PASSIVE &&
-                   new_state <= CAN_STATE_BUS_OFF) {
-                       netdev_dbg(dev, "Error Passive IRQ\n");
-                       priv->can.can_stats.error_passive++;
-
-                       cf->can_id |= CAN_ERR_CRTL;
-                       cf->data[1] = (bec.txerr > bec.rxerr) ?
-                               CAN_ERR_CRTL_TX_PASSIVE :
-                               CAN_ERR_CRTL_RX_PASSIVE;
-               }
-               break;
-       case CAN_STATE_BUS_OFF:
-               netdev_err(dev, "BUG! "
-                          "hardware recovered automatically from BUS_OFF\n");
-               break;
-       default:
-               break;
-       }
-
-       /* process state changes depending on the new state */
-       switch (new_state) {
-       case CAN_STATE_ERROR_WARNING:
-               netdev_dbg(dev, "Error Warning\n");
-               cf->can_id |= CAN_ERR_CRTL;
-               cf->data[1] = (bec.txerr > bec.rxerr) ?
-                       CAN_ERR_CRTL_TX_WARNING :
-                       CAN_ERR_CRTL_RX_WARNING;
-               break;
-       case CAN_STATE_ERROR_ACTIVE:
-               netdev_dbg(dev, "Error Active\n");
-               cf->can_id |= CAN_ERR_PROT;
-               cf->data[2] = CAN_ERR_PROT_ACTIVE;
-               break;
-       case CAN_STATE_BUS_OFF:
-               cf->can_id |= CAN_ERR_BUSOFF;
-               can_bus_off(dev);
-               break;
-       default:
-               break;
-       }
-}
-
 static int flexcan_poll_state(struct net_device *dev, u32 reg_esr)
 {
        struct flexcan_priv *priv = netdev_priv(dev);
        struct sk_buff *skb;
        struct can_frame *cf;
-       enum can_state new_state;
+       enum can_state new_state = 0, rx_state = 0, tx_state = 0;
        int flt;
+       struct can_berr_counter bec;
 
        flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK;
        if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) {
-               if (likely(!(reg_esr & (FLEXCAN_ESR_TX_WRN |
-                                       FLEXCAN_ESR_RX_WRN))))
-                       new_state = CAN_STATE_ERROR_ACTIVE;
-               else
-                       new_state = CAN_STATE_ERROR_WARNING;
-       } else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE))
+               tx_state = unlikely(reg_esr & FLEXCAN_ESR_TX_WRN) ?
+                          CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
+               rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ?
+                          CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
+               new_state = max(tx_state, rx_state);
+       } else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE)) {
+               __flexcan_get_berr_counter(dev, &bec);
                new_state = CAN_STATE_ERROR_PASSIVE;
-       else
+               rx_state = bec.rxerr >= bec.txerr ? new_state : 0;
+               tx_state = bec.rxerr <= bec.txerr ? new_state : 0;
+       } else {
                new_state = CAN_STATE_BUS_OFF;
+       }
 
        /* state hasn't changed */
        if (likely(new_state == priv->can.state))
@@ -678,8 +610,11 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr)
        if (unlikely(!skb))
                return 0;
 
-       do_state(dev, cf, new_state);
-       priv->can.state = new_state;
+       can_change_state(dev, cf, tx_state, rx_state);
+
+       if (unlikely(new_state == CAN_STATE_BUS_OFF))
+               can_bus_off(dev);
+
        netif_receive_skb(skb);
 
        dev->stats.rx_packets++;
index e0c9be5e2ab74676e8e3bb9fe026b145d3f4d3ab..e36b7400d5cceaf12b416d39a0d546baa52e73a5 100644 (file)
@@ -289,18 +289,15 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
        return NETDEV_TX_OK;
 }
 
-/* This function returns the old state to see where we came from */
-static enum can_state check_set_state(struct net_device *dev, u8 canrflg)
+static enum can_state get_new_state(struct net_device *dev, u8 canrflg)
 {
        struct mscan_priv *priv = netdev_priv(dev);
-       enum can_state state, old_state = priv->can.state;
 
-       if (canrflg & MSCAN_CSCIF && old_state <= CAN_STATE_BUS_OFF) {
-               state = state_map[max(MSCAN_STATE_RX(canrflg),
-                                     MSCAN_STATE_TX(canrflg))];
-               priv->can.state = state;
-       }
-       return old_state;
+       if (unlikely(canrflg & MSCAN_CSCIF))
+               return state_map[max(MSCAN_STATE_RX(canrflg),
+                                MSCAN_STATE_TX(canrflg))];
+
+       return priv->can.state;
 }
 
 static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame)
@@ -349,7 +346,7 @@ static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame,
        struct mscan_priv *priv = netdev_priv(dev);
        struct mscan_regs __iomem *regs = priv->reg_base;
        struct net_device_stats *stats = &dev->stats;
-       enum can_state old_state;
+       enum can_state new_state;
 
        netdev_dbg(dev, "error interrupt (canrflg=%#x)\n", canrflg);
        frame->can_id = CAN_ERR_FLAG;
@@ -363,27 +360,13 @@ static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame,
                frame->data[1] = 0;
        }
 
-       old_state = check_set_state(dev, canrflg);
-       /* State changed */
-       if (old_state != priv->can.state) {
-               switch (priv->can.state) {
-               case CAN_STATE_ERROR_WARNING:
-                       frame->can_id |= CAN_ERR_CRTL;
-                       priv->can.can_stats.error_warning++;
-                       if ((priv->shadow_statflg & MSCAN_RSTAT_MSK) <
-                           (canrflg & MSCAN_RSTAT_MSK))
-                               frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
-                       if ((priv->shadow_statflg & MSCAN_TSTAT_MSK) <
-                           (canrflg & MSCAN_TSTAT_MSK))
-                               frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
-                       break;
-               case CAN_STATE_ERROR_PASSIVE:
-                       frame->can_id |= CAN_ERR_CRTL;
-                       priv->can.can_stats.error_passive++;
-                       frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
-                       break;
-               case CAN_STATE_BUS_OFF:
-                       frame->can_id |= CAN_ERR_BUSOFF;
+       new_state = get_new_state(dev, canrflg);
+       if (new_state != priv->can.state) {
+               can_change_state(dev, frame,
+                                state_map[MSCAN_STATE_TX(canrflg)],
+                                state_map[MSCAN_STATE_RX(canrflg)]);
+
+               if (priv->can.state == CAN_STATE_BUS_OFF) {
                        /*
                         * The MSCAN on the MPC5200 does recover from bus-off
                         * automatically. To avoid that we stop the chip doing
@@ -396,9 +379,6 @@ static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame,
                                         MSCAN_SLPRQ | MSCAN_INITRQ);
                        }
                        can_bus_off(dev);
-                       break;
-               default:
-                       break;
                }
        }
        priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
index b27ac6074afb1d21ab63282298002f8a4992599b..32bd7f451aa42b53f0a479af667861db6693d619 100644 (file)
@@ -392,12 +392,20 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
        struct can_frame *cf;
        struct sk_buff *skb;
        enum can_state state = priv->can.state;
+       enum can_state rx_state, tx_state;
+       unsigned int rxerr, txerr;
        uint8_t ecc, alc;
 
        skb = alloc_can_err_skb(dev, &cf);
        if (skb == NULL)
                return -ENOMEM;
 
+       txerr = priv->read_reg(priv, SJA1000_TXERR);
+       rxerr = priv->read_reg(priv, SJA1000_RXERR);
+
+       cf->data[6] = txerr;
+       cf->data[7] = rxerr;
+
        if (isrc & IRQ_DOI) {
                /* data overrun interrupt */
                netdev_dbg(dev, "data overrun interrupt\n");
@@ -412,13 +420,11 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
                /* error warning interrupt */
                netdev_dbg(dev, "error warning interrupt\n");
 
-               if (status & SR_BS) {
+               if (status & SR_BS)
                        state = CAN_STATE_BUS_OFF;
-                       cf->can_id |= CAN_ERR_BUSOFF;
-                       can_bus_off(dev);
-               } else if (status & SR_ES) {
+               else if (status & SR_ES)
                        state = CAN_STATE_ERROR_WARNING;
-               else
+               else
                        state = CAN_STATE_ERROR_ACTIVE;
        }
        if (isrc & IRQ_BEI) {
@@ -452,10 +458,11 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
        if (isrc & IRQ_EPI) {
                /* error passive interrupt */
                netdev_dbg(dev, "error passive interrupt\n");
-               if (status & SR_ES)
-                       state = CAN_STATE_ERROR_PASSIVE;
+
+               if (state == CAN_STATE_ERROR_PASSIVE)
+                       state = CAN_STATE_ERROR_WARNING;
                else
-                       state = CAN_STATE_ERROR_ACTIVE;
+                       state = CAN_STATE_ERROR_PASSIVE;
        }
        if (isrc & IRQ_ALI) {
                /* arbitration lost interrupt */
@@ -467,27 +474,15 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
                cf->data[0] = alc & 0x1f;
        }
 
-       if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING ||
-                                        state == CAN_STATE_ERROR_PASSIVE)) {
-               uint8_t rxerr = priv->read_reg(priv, SJA1000_RXERR);
-               uint8_t txerr = priv->read_reg(priv, SJA1000_TXERR);
-               cf->can_id |= CAN_ERR_CRTL;
-               if (state == CAN_STATE_ERROR_WARNING) {
-                       priv->can.can_stats.error_warning++;
-                       cf->data[1] = (txerr > rxerr) ?
-                               CAN_ERR_CRTL_TX_WARNING :
-                               CAN_ERR_CRTL_RX_WARNING;
-               } else {
-                       priv->can.can_stats.error_passive++;
-                       cf->data[1] = (txerr > rxerr) ?
-                               CAN_ERR_CRTL_TX_PASSIVE :
-                               CAN_ERR_CRTL_RX_PASSIVE;
-               }
-               cf->data[6] = txerr;
-               cf->data[7] = rxerr;
-       }
+       if (state != priv->can.state) {
+               tx_state = txerr >= rxerr ? state : 0;
+               rx_state = txerr <= rxerr ? state : 0;
 
-       priv->can.state = state;
+               can_change_state(dev, cf, tx_state, rx_state);
+
+               if(state == CAN_STATE_BUS_OFF)
+                       can_bus_off(dev);
+       }
 
        netif_rx(skb);
 
index acb5b92ace92da17f55a5d892e90e7d7530a1952..c837eb91d43e306304d1a1347a8d8f9ada6cb02f 100644 (file)
@@ -56,9 +56,6 @@
 #include <linux/can.h>
 #include <linux/can/skb.h>
 
-static __initconst const char banner[] =
-       KERN_INFO "slcan: serial line CAN interface driver\n";
-
 MODULE_ALIAS_LDISC(N_SLCAN);
 MODULE_DESCRIPTION("serial line CAN interface");
 MODULE_LICENSE("GPL");
@@ -702,8 +699,8 @@ static int __init slcan_init(void)
        if (maxdev < 4)
                maxdev = 4; /* Sanity */
 
-       printk(banner);
-       printk(KERN_INFO "slcan: %d dynamic interface channels.\n", maxdev);
+       pr_info("slcan: serial line CAN interface driver\n");
+       pr_info("slcan: %d dynamic interface channels.\n", maxdev);
 
        slcan_devs = kzalloc(sizeof(struct net_device *)*maxdev, GFP_KERNEL);
        if (!slcan_devs)
index 4e94057ef5cf55df4600496d38b5fce433f851b4..674f367087c54ac168d2c0283b1361a1c98d3696 100644 (file)
@@ -50,9 +50,6 @@
 #include <linux/slab.h>
 #include <net/rtnetlink.h>
 
-static __initconst const char banner[] =
-       KERN_INFO "vcan: Virtual CAN interface driver\n";
-
 MODULE_DESCRIPTION("virtual CAN interface");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
@@ -173,7 +170,7 @@ static struct rtnl_link_ops vcan_link_ops __read_mostly = {
 
 static __init int vcan_init_module(void)
 {
-       printk(banner);
+       pr_info("vcan: Virtual CAN interface driver\n");
 
        if (echo)
                printk(KERN_INFO "vcan: enabled echo on driver level.\n");
index 413ca4f73997f49a723d3f70c52a3b7851dde70e..49adbf1b7574211dcd97db2c8cc970e427934c87 100644 (file)
@@ -225,3 +225,4 @@ module_init(dummy_init_module);
 module_exit(dummy_cleanup_module);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_RTNL_LINK(DRV_NAME);
+MODULE_VERSION(DRV_VERSION);
index bedfdb1c430db75c9e5268cc65d036ef940f1f0c..bf6bf1118b0fbc5beabaaf84cba7999da7a26914 100644 (file)
@@ -396,7 +396,7 @@ static irqreturn_t xgbe_dma_isr(int irq, void *data)
         */
        if (napi_schedule_prep(&channel->napi)) {
                /* Disable Tx and Rx interrupts */
-               disable_irq(channel->dma_irq);
+               disable_irq_nosync(channel->dma_irq);
 
                /* Turn on polling */
                __napi_schedule(&channel->napi);
index 416620fa8fac4655f247a3d5e50aad9ad38c7ece..ffeaf476a1202461f3cd239143f20e96f113faab 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 c1d255972daec6ab10f7da7c6ca097c28f78d698..a91a8c263391b01c63128f1f5de58f439cef66c7 100644 (file)
@@ -1409,6 +1409,27 @@ static void topctrl_flush(struct bcm_sysport_priv *priv)
        topctrl_writel(priv, 0, TX_FLUSH_CNTL);
 }
 
+static int bcm_sysport_change_mac(struct net_device *dev, void *p)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       struct sockaddr *addr = p;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EINVAL;
+
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+       /* interface is disabled, changes to MAC will be reflected on next
+        * open call
+        */
+       if (!netif_running(dev))
+               return 0;
+
+       umac_set_hw_addr(priv, dev->dev_addr);
+
+       return 0;
+}
+
 static void bcm_sysport_netif_start(struct net_device *dev)
 {
        struct bcm_sysport_priv *priv = netdev_priv(dev);
@@ -1628,6 +1649,7 @@ static const struct net_device_ops bcm_sysport_netdev_ops = {
        .ndo_stop               = bcm_sysport_stop,
        .ndo_set_features       = bcm_sysport_set_features,
        .ndo_set_rx_mode        = bcm_sysport_set_rx_mode,
+       .ndo_set_mac_address    = bcm_sysport_change_mac,
 };
 
 #define REV_FMT        "v%2x.%02x"
index 4c58793890030db331ef473d5eeaf90f9f832f16..86222a1bdb12bfffcf5e3888867bf7479092307b 100644 (file)
@@ -301,7 +301,7 @@ unsigned int t1_sched_update_parms(struct sge *sge, unsigned int port,
        struct sched_port *p = &s->p[port];
        unsigned int max_avail_segs;
 
-       pr_debug("t1_sched_update_params mtu=%d speed=%d\n", mtu, speed);
+       pr_debug("%s mtu=%d speed=%d\n", __func__, mtu, speed);
        if (speed)
                p->speed = speed;
        if (mtu)
index c8205606c7757ff3345acef81d57a5c0118497e3..50a00777228e12b91b33f8bb3b7794f4f07de42c 100644 (file)
@@ -2265,7 +2265,7 @@ static int __init dmfe_init_module(void)
 
 static void __exit dmfe_cleanup_module(void)
 {
-       DMFE_DBUG(0, "dmfe_clean_module() ", debug);
+       DMFE_DBUG(0, "dmfe_cleanup_module() ", debug);
        pci_unregister_driver(&dmfe_driver);
 }
 
index 4061f9b22812f2c03df0e5076a8bce82f511503e..1c5916b13778a96e489ee3ec1bcb2d1acee63cd2 100644 (file)
@@ -1837,7 +1837,7 @@ static int __init uli526x_init_module(void)
 
 static void __exit uli526x_cleanup_module(void)
 {
-       ULI526X_DBUG(0, "uli526x_clean_module() ", debug);
+       ULI526X_DBUG(0, "uli526x_cleanup_module() ", debug);
        pci_unregister_driver(&uli526x_driver);
 }
 
index d2955ce24d0bb04ed28170c29f94b4927b43533d..fee2afe47eb3c3aab688b2321fc2283740a0cfb9 100644 (file)
@@ -1872,6 +1872,8 @@ static int fec_enet_mii_probe(struct net_device *ndev)
                phy_dev = of_phy_connect(ndev, fep->phy_node,
                                         &fec_enet_adjust_link, 0,
                                         fep->phy_interface);
+               if (!phy_dev)
+                       return -ENODEV;
        } else {
                /* check for attached phy */
                for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
index 76a6e0c77d69e1a01544a388b465e7e82ddbfd87..ae6e30d39f0f6b07ac8b9b2fec78bd9eb250e535 100644 (file)
@@ -490,7 +490,8 @@ static int hp100_probe1(struct net_device *dev, int ioaddr, u_char bus,
 
        eid = hp100_read_id(ioaddr);
        if (eid == NULL) {      /* bad checksum? */
-               printk(KERN_WARNING "hp100_probe: bad ID checksum at base port 0x%x\n", ioaddr);
+               printk(KERN_WARNING "%s: bad ID checksum at base port 0x%x\n",
+                      __func__, ioaddr);
                goto out2;
        }
 
@@ -498,7 +499,9 @@ static int hp100_probe1(struct net_device *dev, int ioaddr, u_char bus,
        for (i = uc = 0; i < 7; i++)
                uc += hp100_inb(LAN_ADDR + i);
        if (uc != 0xff) {
-               printk(KERN_WARNING "hp100_probe: bad lan address checksum at port 0x%x)\n", ioaddr);
+               printk(KERN_WARNING
+                      "%s: bad lan address checksum at port 0x%x)\n",
+                      __func__, ioaddr);
                err = -EIO;
                goto out2;
        }
index 35fa09a2c16261e3e7e10d9acbc820448d7e0a26..77f6254a89ac6078136e7cd92af1f910981c3f6f 100644 (file)
@@ -617,7 +617,8 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw)
 
        /* pre-emptive resource lock release */
        i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL);
-       hw->aq.nvm_busy = false;
+       hw->aq.nvm_release_on_done = false;
+       hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
 
        ret_code = i40e_aq_set_hmc_resource_profile(hw,
                                                    I40E_HMC_PROFILE_DEFAULT,
@@ -754,12 +755,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                goto asq_send_command_exit;
        }
 
-       if (i40e_is_nvm_update_op(desc) && hw->aq.nvm_busy) {
-               i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: NVM busy.\n");
-               status = I40E_ERR_NVM;
-               goto asq_send_command_exit;
-       }
-
        details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use);
        if (cmd_details) {
                *details = *cmd_details;
@@ -901,9 +896,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                status = I40E_ERR_ADMIN_QUEUE_TIMEOUT;
        }
 
-       if (!status && i40e_is_nvm_update_op(desc))
-               hw->aq.nvm_busy = true;
-
 asq_send_command_error:
        mutex_unlock(&hw->aq.asq_mutex);
 asq_send_command_exit:
@@ -1016,7 +1008,6 @@ clean_arq_element_out:
        mutex_unlock(&hw->aq.arq_mutex);
 
        if (i40e_is_nvm_update_op(&e->desc)) {
-               hw->aq.nvm_busy = false;
                if (hw->aq.nvm_release_on_done) {
                        i40e_release_nvm(hw);
                        hw->aq.nvm_release_on_done = false;
index 003a227b8515a610bbcd7aa761074d5693d774a9..564d0b0192f789aad0b4af4ceed04fe9fd15cd06 100644 (file)
@@ -28,6 +28,7 @@
 #define _I40E_ADMINQ_H_
 
 #include "i40e_osdep.h"
+#include "i40e_status.h"
 #include "i40e_adminq_cmd.h"
 
 #define I40E_ADMINQ_DESC(R, i)   \
@@ -94,7 +95,6 @@ struct i40e_adminq_info {
        u16 fw_min_ver;                 /* firmware minor version */
        u16 api_maj_ver;                /* api major version */
        u16 api_min_ver;                /* api minor version */
-       bool nvm_busy;
        bool nvm_release_on_done;
 
        struct mutex asq_mutex; /* Send queue lock */
@@ -109,7 +109,7 @@ struct i40e_adminq_info {
  * i40e_aq_rc_to_posix - convert errors to user-land codes
  * aq_rc: AdminQ error code to convert
  **/
-static inline int i40e_aq_rc_to_posix(u16 aq_rc)
+static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc)
 {
        int aq_to_posix[] = {
                0,           /* I40E_AQ_RC_OK */
@@ -137,6 +137,12 @@ static inline int i40e_aq_rc_to_posix(u16 aq_rc)
                -EFBIG,      /* I40E_AQ_RC_EFBIG */
        };
 
+       /* aq_rc is invalid if AQ timed out */
+       if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT)
+               return -EAGAIN;
+
+       if (aq_rc >= ARRAY_SIZE(aq_to_posix))
+               return -ERANGE;
        return aq_to_posix[aq_rc];
 }
 
index fcd815dc7d3c4c4ae81496b843b6eed3101c2514..951e8767fc502e57e91b40412dba09fa27f0e8c6 100644 (file)
@@ -822,7 +822,7 @@ static int i40e_get_eeprom(struct net_device *netdev,
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_hw *hw = &np->vsi->back->hw;
        struct i40e_pf *pf = np->vsi->back;
-       int ret_val = 0, len;
+       int ret_val = 0, len, offset;
        u8 *eeprom_buff;
        u16 i, sectors;
        bool last;
@@ -835,19 +835,21 @@ static int i40e_get_eeprom(struct net_device *netdev,
        /* check for NVMUpdate access method */
        magic = hw->vendor_id | (hw->device_id << 16);
        if (eeprom->magic && eeprom->magic != magic) {
+               struct i40e_nvm_access *cmd;
                int errno;
 
                /* make sure it is the right magic for NVMUpdate */
                if ((eeprom->magic >> 16) != hw->device_id)
                        return -EINVAL;
 
-               ret_val = i40e_nvmupd_command(hw,
-                                             (struct i40e_nvm_access *)eeprom,
-                                             bytes, &errno);
+               cmd = (struct i40e_nvm_access *)eeprom;
+               ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno);
                if (ret_val)
                        dev_info(&pf->pdev->dev,
-                                "NVMUpdate read failed err=%d status=0x%x\n",
-                                ret_val, hw->aq.asq_last_status);
+                                "NVMUpdate read failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n",
+                                ret_val, hw->aq.asq_last_status, errno,
+                                (u8)(cmd->config & I40E_NVM_MOD_PNT_MASK),
+                                cmd->offset, cmd->data_size);
 
                return errno;
        }
@@ -876,20 +878,29 @@ static int i40e_get_eeprom(struct net_device *netdev,
                        len = eeprom->len - (I40E_NVM_SECTOR_SIZE * i);
                        last = true;
                }
-               ret_val = i40e_aq_read_nvm(hw, 0x0,
-                               eeprom->offset + (I40E_NVM_SECTOR_SIZE * i),
-                               len,
+               offset = eeprom->offset + (I40E_NVM_SECTOR_SIZE * i),
+               ret_val = i40e_aq_read_nvm(hw, 0x0, offset, len,
                                (u8 *)eeprom_buff + (I40E_NVM_SECTOR_SIZE * i),
                                last, NULL);
-               if (ret_val) {
+               if (ret_val && hw->aq.asq_last_status == I40E_AQ_RC_EPERM) {
                        dev_info(&pf->pdev->dev,
-                                "read NVM failed err=%d status=0x%x\n",
-                                ret_val, hw->aq.asq_last_status);
-                       goto release_nvm;
+                                "read NVM failed, invalid offset 0x%x\n",
+                                offset);
+                       break;
+               } else if (ret_val &&
+                          hw->aq.asq_last_status == I40E_AQ_RC_EACCES) {
+                       dev_info(&pf->pdev->dev,
+                                "read NVM failed, access, offset 0x%x\n",
+                                offset);
+                       break;
+               } else if (ret_val) {
+                       dev_info(&pf->pdev->dev,
+                                "read NVM failed offset %d err=%d status=0x%x\n",
+                                offset, ret_val, hw->aq.asq_last_status);
+                       break;
                }
        }
 
-release_nvm:
        i40e_release_nvm(hw);
        memcpy(bytes, (u8 *)eeprom_buff, eeprom->len);
 free_buff:
@@ -917,6 +928,7 @@ static int i40e_set_eeprom(struct net_device *netdev,
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_hw *hw = &np->vsi->back->hw;
        struct i40e_pf *pf = np->vsi->back;
+       struct i40e_nvm_access *cmd;
        int ret_val = 0;
        int errno;
        u32 magic;
@@ -934,12 +946,14 @@ static int i40e_set_eeprom(struct net_device *netdev,
            test_bit(__I40E_RESET_INTR_RECEIVED, &pf->state))
                return -EBUSY;
 
-       ret_val = i40e_nvmupd_command(hw, (struct i40e_nvm_access *)eeprom,
-                                     bytes, &errno);
-       if (ret_val)
+       cmd = (struct i40e_nvm_access *)eeprom;
+       ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno);
+       if (ret_val && hw->aq.asq_last_status != I40E_AQ_RC_EBUSY)
                dev_info(&pf->pdev->dev,
-                        "NVMUpdate write failed err=%d status=0x%x\n",
-                        ret_val, hw->aq.asq_last_status);
+                        "NVMUpdate write failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n",
+                        ret_val, hw->aq.asq_last_status, errno,
+                        (u8)(cmd->config & I40E_NVM_MOD_PNT_MASK),
+                        cmd->offset, cmd->data_size);
 
        return errno;
 }
@@ -1393,6 +1407,9 @@ static int i40e_eeprom_test(struct net_device *netdev, u64 *data)
        netif_info(pf, hw, netdev, "eeprom test\n");
        *data = i40e_diag_eeprom_test(&pf->hw);
 
+       /* forcebly clear the NVM Update state machine */
+       pf->hw.nvmupd_state = I40E_NVMUPD_STATE_INIT;
+
        return *data;
 }
 
index 25c4f9a3011f59715f1d9489587d14c310a10097..3e70f2e45a4768986a0a90ee21bdf98790db9b53 100644 (file)
@@ -61,7 +61,7 @@ i40e_status i40e_init_nvm(struct i40e_hw *hw)
        } else { /* Blank programming mode */
                nvm->blank_nvm_mode = true;
                ret_code = I40E_ERR_NVM_BLANK_MODE;
-               hw_dbg(hw, "NVM init error: unsupported blank mode.\n");
+               i40e_debug(hw, I40E_DEBUG_NVM, "NVM init error: unsupported blank mode.\n");
        }
 
        return ret_code;
@@ -80,46 +80,45 @@ i40e_status i40e_acquire_nvm(struct i40e_hw *hw,
 {
        i40e_status ret_code = 0;
        u64 gtime, timeout;
-       u64 time = 0;
+       u64 time_left = 0;
 
        if (hw->nvm.blank_nvm_mode)
                goto i40e_i40e_acquire_nvm_exit;
 
        ret_code = i40e_aq_request_resource(hw, I40E_NVM_RESOURCE_ID, access,
-                                           0, &time, NULL);
+                                           0, &time_left, NULL);
        /* Reading the Global Device Timer */
        gtime = rd32(hw, I40E_GLVFGEN_TIMER);
 
        /* Store the timeout */
-       hw->nvm.hw_semaphore_timeout = I40E_MS_TO_GTIME(time) + gtime;
+       hw->nvm.hw_semaphore_timeout = I40E_MS_TO_GTIME(time_left) + gtime;
 
-       if (ret_code) {
-               /* Set the polling timeout */
-               if (time > I40E_MAX_NVM_TIMEOUT)
-                       timeout = I40E_MS_TO_GTIME(I40E_MAX_NVM_TIMEOUT)
-                                 + gtime;
-               else
-                       timeout = hw->nvm.hw_semaphore_timeout;
+       if (ret_code)
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVM acquire type %d failed time_left=%llu ret=%d aq_err=%d\n",
+                          access, time_left, ret_code, hw->aq.asq_last_status);
+
+       if (ret_code && time_left) {
                /* Poll until the current NVM owner timeouts */
-               while (gtime < timeout) {
+               timeout = I40E_MS_TO_GTIME(I40E_MAX_NVM_TIMEOUT) + gtime;
+               while ((gtime < timeout) && time_left) {
                        usleep_range(10000, 20000);
+                       gtime = rd32(hw, I40E_GLVFGEN_TIMER);
                        ret_code = i40e_aq_request_resource(hw,
                                                        I40E_NVM_RESOURCE_ID,
-                                                       access, 0, &time,
+                                                       access, 0, &time_left,
                                                        NULL);
                        if (!ret_code) {
                                hw->nvm.hw_semaphore_timeout =
-                                               I40E_MS_TO_GTIME(time) + gtime;
+                                           I40E_MS_TO_GTIME(time_left) + gtime;
                                break;
                        }
-                       gtime = rd32(hw, I40E_GLVFGEN_TIMER);
                }
                if (ret_code) {
                        hw->nvm.hw_semaphore_timeout = 0;
-                       hw->nvm.hw_semaphore_wait =
-                                               I40E_MS_TO_GTIME(time) + gtime;
-                       hw_dbg(hw, "NVM acquire timed out, wait %llu ms before trying again.\n",
-                                 time);
+                       i40e_debug(hw, I40E_DEBUG_NVM,
+                                  "NVM acquire timed out, wait %llu ms before trying again. status=%d aq_err=%d\n",
+                                  time_left, ret_code, hw->aq.asq_last_status);
                }
        }
 
@@ -160,7 +159,7 @@ static i40e_status i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw)
                udelay(5);
        }
        if (ret_code == I40E_ERR_TIMEOUT)
-               hw_dbg(hw, "Done bit in GLNVM_SRCTL not set\n");
+               i40e_debug(hw, I40E_DEBUG_NVM, "Done bit in GLNVM_SRCTL not set");
        return ret_code;
 }
 
@@ -179,7 +178,9 @@ i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
        u32 sr_reg;
 
        if (offset >= hw->nvm.sr_size) {
-               hw_dbg(hw, "NVM read error: Offset beyond Shadow RAM limit.\n");
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVM read error: offset %d beyond Shadow RAM limit %d\n",
+                          offset, hw->nvm.sr_size);
                ret_code = I40E_ERR_PARAM;
                goto read_nvm_exit;
        }
@@ -202,8 +203,9 @@ i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
                }
        }
        if (ret_code)
-               hw_dbg(hw, "NVM read error: Couldn't access Shadow RAM address: 0x%x\n",
-                         offset);
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVM read error: Couldn't access Shadow RAM address: 0x%x\n",
+                          offset);
 
 read_nvm_exit:
        return ret_code;
@@ -263,14 +265,20 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer,
         * Firmware will check the module-based model.
         */
        if ((offset + words) > hw->nvm.sr_size)
-               hw_dbg(hw, "NVM write error: offset beyond Shadow RAM limit.\n");
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVM write error: offset %d beyond Shadow RAM limit %d\n",
+                          (offset + words), hw->nvm.sr_size);
        else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS)
                /* We can write only up to 4KB (one sector), in one AQ write */
-               hw_dbg(hw, "NVM write fail error: cannot write more than 4KB in a single write.\n");
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVM write fail error: tried to write %d words, limit is %d.\n",
+                          words, I40E_SR_SECTOR_SIZE_IN_WORDS);
        else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS)
                 != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS))
                /* A single write cannot spread over two sectors */
-               hw_dbg(hw, "NVM write error: cannot spread over two sectors in a single write.\n");
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVM write error: cannot spread over two sectors in a single write offset=%d words=%d\n",
+                          offset, words);
        else
                ret_code = i40e_aq_update_nvm(hw, module_pointer,
                                              2 * offset,  /*bytes*/
@@ -438,6 +446,22 @@ static inline u8 i40e_nvmupd_get_transaction(u32 val)
        return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT);
 }
 
+static char *i40e_nvm_update_state_str[] = {
+       "I40E_NVMUPD_INVALID",
+       "I40E_NVMUPD_READ_CON",
+       "I40E_NVMUPD_READ_SNT",
+       "I40E_NVMUPD_READ_LCB",
+       "I40E_NVMUPD_READ_SA",
+       "I40E_NVMUPD_WRITE_ERA",
+       "I40E_NVMUPD_WRITE_CON",
+       "I40E_NVMUPD_WRITE_SNT",
+       "I40E_NVMUPD_WRITE_LCB",
+       "I40E_NVMUPD_WRITE_SA",
+       "I40E_NVMUPD_CSUM_CON",
+       "I40E_NVMUPD_CSUM_SA",
+       "I40E_NVMUPD_CSUM_LCB",
+};
+
 /**
  * i40e_nvmupd_command - Process an NVM update command
  * @hw: pointer to hardware structure
@@ -471,6 +495,8 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw,
 
        default:
                /* invalid state, should never happen */
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVMUPD: no such state %d\n", hw->nvmupd_state);
                status = I40E_NOT_SUPPORTED;
                *errno = -ESRCH;
                break;
@@ -501,7 +527,8 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
        case I40E_NVMUPD_READ_SA:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
+                       *errno = i40e_aq_rc_to_posix(status,
+                                                    hw->aq.asq_last_status);
                } else {
                        status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
                        i40e_release_nvm(hw);
@@ -511,17 +538,22 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
        case I40E_NVMUPD_READ_SNT:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
+                       *errno = i40e_aq_rc_to_posix(status,
+                                                    hw->aq.asq_last_status);
                } else {
                        status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno);
-                       hw->nvmupd_state = I40E_NVMUPD_STATE_READING;
+                       if (status)
+                               i40e_release_nvm(hw);
+                       else
+                               hw->nvmupd_state = I40E_NVMUPD_STATE_READING;
                }
                break;
 
        case I40E_NVMUPD_WRITE_ERA:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
+                       *errno = i40e_aq_rc_to_posix(status,
+                                                    hw->aq.asq_last_status);
                } else {
                        status = i40e_nvmupd_nvm_erase(hw, cmd, errno);
                        if (status)
@@ -534,7 +566,8 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
        case I40E_NVMUPD_WRITE_SA:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
+                       *errno = i40e_aq_rc_to_posix(status,
+                                                    hw->aq.asq_last_status);
                } else {
                        status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
                        if (status)
@@ -547,22 +580,28 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
        case I40E_NVMUPD_WRITE_SNT:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
+                       *errno = i40e_aq_rc_to_posix(status,
+                                                    hw->aq.asq_last_status);
                } else {
                        status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
-                       hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING;
+                       if (status)
+                               i40e_release_nvm(hw);
+                       else
+                               hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING;
                }
                break;
 
        case I40E_NVMUPD_CSUM_SA:
                status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
                if (status) {
-                       *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
+                       *errno = i40e_aq_rc_to_posix(status,
+                                                    hw->aq.asq_last_status);
                } else {
                        status = i40e_update_nvm_checksum(hw);
                        if (status) {
                                *errno = hw->aq.asq_last_status ?
-                                  i40e_aq_rc_to_posix(hw->aq.asq_last_status) :
+                                  i40e_aq_rc_to_posix(status,
+                                                      hw->aq.asq_last_status) :
                                   -EIO;
                                i40e_release_nvm(hw);
                        } else {
@@ -572,6 +611,9 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw,
                break;
 
        default:
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVMUPD: bad cmd %s in init state\n",
+                          i40e_nvm_update_state_str[upd_cmd]);
                status = I40E_ERR_NVM;
                *errno = -ESRCH;
                break;
@@ -611,6 +653,9 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw,
                break;
 
        default:
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVMUPD: bad cmd %s in reading state.\n",
+                          i40e_nvm_update_state_str[upd_cmd]);
                status = I40E_NOT_SUPPORTED;
                *errno = -ESRCH;
                break;
@@ -644,33 +689,38 @@ static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw,
 
        case I40E_NVMUPD_WRITE_LCB:
                status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
-               if (!status) {
+               if (!status)
                        hw->aq.nvm_release_on_done = true;
-                       hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
-               }
+               hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
                break;
 
        case I40E_NVMUPD_CSUM_CON:
                status = i40e_update_nvm_checksum(hw);
-               if (status)
+               if (status) {
                        *errno = hw->aq.asq_last_status ?
-                                  i40e_aq_rc_to_posix(hw->aq.asq_last_status) :
+                                  i40e_aq_rc_to_posix(status,
+                                                      hw->aq.asq_last_status) :
                                   -EIO;
+                       hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+               }
                break;
 
        case I40E_NVMUPD_CSUM_LCB:
                status = i40e_update_nvm_checksum(hw);
-               if (status) {
+               if (status)
                        *errno = hw->aq.asq_last_status ?
-                                  i40e_aq_rc_to_posix(hw->aq.asq_last_status) :
+                                  i40e_aq_rc_to_posix(status,
+                                                      hw->aq.asq_last_status) :
                                   -EIO;
-               } else {
+               else
                        hw->aq.nvm_release_on_done = true;
-                       hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
-               }
+               hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
                break;
 
        default:
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "NVMUPD: bad cmd %s in writing state.\n",
+                          i40e_nvm_update_state_str[upd_cmd]);
                status = I40E_NOT_SUPPORTED;
                *errno = -ESRCH;
                break;
@@ -702,8 +752,9 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
        /* limits on data size */
        if ((cmd->data_size < 1) ||
            (cmd->data_size > I40E_NVMUPD_MAX_DATA)) {
-               hw_dbg(hw, "i40e_nvmupd_validate_command data_size %d\n",
-                      cmd->data_size);
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_validate_command data_size %d\n",
+                          cmd->data_size);
                *errno = -EFAULT;
                return I40E_NVMUPD_INVALID;
        }
@@ -755,12 +806,16 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
                }
                break;
        }
+       i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n",
+                  i40e_nvm_update_state_str[upd_cmd],
+                  hw->nvmupd_state,
+                  hw->aq.nvm_release_on_done);
 
        if (upd_cmd == I40E_NVMUPD_INVALID) {
                *errno = -EFAULT;
-               hw_dbg(hw,
-                      "i40e_nvmupd_validate_command returns %d  errno: %d\n",
-                      upd_cmd, *errno);
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_validate_command returns %d errno %d\n",
+                          upd_cmd, *errno);
        }
        return upd_cmd;
 }
@@ -785,14 +840,18 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw,
        transaction = i40e_nvmupd_get_transaction(cmd->config);
        module = i40e_nvmupd_get_module(cmd->config);
        last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA);
-       hw_dbg(hw, "i40e_nvmupd_nvm_read mod 0x%x  off 0x%x  len 0x%x\n",
-              module, cmd->offset, cmd->data_size);
 
        status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
                                  bytes, last, NULL);
-       hw_dbg(hw, "i40e_nvmupd_nvm_read status %d\n", status);
-       if (status)
-               *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
+       if (status) {
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_nvm_read mod 0x%x  off 0x%x  len 0x%x\n",
+                          module, cmd->offset, cmd->data_size);
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_nvm_read status %d aq %d\n",
+                          status, hw->aq.asq_last_status);
+               *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+       }
 
        return status;
 }
@@ -816,13 +875,17 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
        transaction = i40e_nvmupd_get_transaction(cmd->config);
        module = i40e_nvmupd_get_module(cmd->config);
        last = (transaction & I40E_NVM_LCB);
-       hw_dbg(hw, "i40e_nvmupd_nvm_erase mod 0x%x  off 0x%x  len 0x%x\n",
-              module, cmd->offset, cmd->data_size);
        status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
                                   last, NULL);
-       hw_dbg(hw, "i40e_nvmupd_nvm_erase status %d\n", status);
-       if (status)
-               *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
+       if (status) {
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_nvm_erase mod 0x%x  off 0x%x len 0x%x\n",
+                          module, cmd->offset, cmd->data_size);
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_nvm_erase status %d aq %d\n",
+                          status, hw->aq.asq_last_status);
+               *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+       }
 
        return status;
 }
@@ -847,13 +910,18 @@ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw,
        transaction = i40e_nvmupd_get_transaction(cmd->config);
        module = i40e_nvmupd_get_module(cmd->config);
        last = (transaction & I40E_NVM_LCB);
-       hw_dbg(hw, "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n",
-              module, cmd->offset, cmd->data_size);
+
        status = i40e_aq_update_nvm(hw, module, cmd->offset,
                                    (u16)cmd->data_size, bytes, last, NULL);
-       hw_dbg(hw, "i40e_nvmupd_nvm_write status %d\n", status);
-       if (status)
-               *errno = i40e_aq_rc_to_posix(hw->aq.asq_last_status);
+       if (status) {
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n",
+                          module, cmd->offset, cmd->data_size);
+               i40e_debug(hw, I40E_DEBUG_NVM,
+                          "i40e_nvmupd_nvm_write status %d aq %d\n",
+                          status, hw->aq.asq_last_status);
+               *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+       }
 
        return status;
 }
index 3904dd8ea1f15a8034d8b771c759173a2bfa6993..c1f2eb96335771fd9eae58623bd98a90132cbe41 100644 (file)
@@ -261,8 +261,7 @@ enum i40e_aq_resource_access_type {
 };
 
 struct i40e_nvm_info {
-       u64 hw_semaphore_timeout; /* 2usec global time (GTIME resolution) */
-       u64 hw_semaphore_wait;    /* - || - */
+       u64 hw_semaphore_timeout; /* usec global time (GTIME resolution) */
        u32 timeout;              /* [ms] */
        u16 sr_size;              /* Shadow RAM size in words */
        bool blank_nvm_mode;      /* is NVM empty (no FW present)*/
@@ -482,7 +481,10 @@ struct i40e_hw {
        u32 debug_mask;
 };
 
-#define i40e_is_vf(_hw)        ((_hw)->mac.type == I40E_MAC_VF)
+static inline bool i40e_is_vf(struct i40e_hw *hw)
+{
+       return hw->mac.type == I40E_MAC_VF;
+}
 
 struct i40e_driver_version {
        u8 major_version;
index 16989946c52a0fc093f7c964c7983d0bd3ba752c..c1d25f8c1abca3276550d1932176f78f39b6859e 100644 (file)
@@ -836,9 +836,6 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw,
                hw->aq.asq_last_status = (enum i40e_admin_queue_err)retval;
        }
 
-       if (i40e_is_nvm_update_op(desc))
-               hw->aq.nvm_busy = true;
-
        i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
                   "AQTX: desc and buffer writeback:\n");
        i40evf_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff,
@@ -931,9 +928,6 @@ i40e_status i40evf_clean_arq_element(struct i40e_hw *hw,
                memcpy(e->msg_buf, hw->aq.arq.r.arq_bi[desc_idx].va,
                       e->msg_len);
 
-       if (i40e_is_nvm_update_op(&e->desc))
-               hw->aq.nvm_busy = false;
-
        i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQRX: desc and buffer:\n");
        i40evf_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, e->msg_buf,
                        hw->aq.arq_buf_size);
index 0d58378be7406bcadfade1e30d628be3409421e6..6c31bf22c2c31f88e996a402c2a4156cd9239f99 100644 (file)
@@ -28,6 +28,7 @@
 #define _I40E_ADMINQ_H_
 
 #include "i40e_osdep.h"
+#include "i40e_status.h"
 #include "i40e_adminq_cmd.h"
 
 #define I40E_ADMINQ_DESC(R, i)   \
@@ -94,7 +95,6 @@ struct i40e_adminq_info {
        u16 fw_min_ver;                 /* firmware minor version */
        u16 api_maj_ver;                /* api major version */
        u16 api_min_ver;                /* api minor version */
-       bool nvm_busy;
        bool nvm_release_on_done;
 
        struct mutex asq_mutex; /* Send queue lock */
@@ -109,7 +109,7 @@ struct i40e_adminq_info {
  * i40e_aq_rc_to_posix - convert errors to user-land codes
  * aq_rc: AdminQ error code to convert
  **/
-static inline int i40e_aq_rc_to_posix(u16 aq_rc)
+static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc)
 {
        int aq_to_posix[] = {
                0,           /* I40E_AQ_RC_OK */
@@ -137,6 +137,12 @@ static inline int i40e_aq_rc_to_posix(u16 aq_rc)
                -EFBIG,      /* I40E_AQ_RC_EFBIG */
        };
 
+       /* aq_rc is invalid if AQ timed out */
+       if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT)
+               return -EAGAIN;
+
+       if (aq_rc >= ARRAY_SIZE(aq_to_posix))
+               return -ERANGE;
        return aq_to_posix[aq_rc];
 }
 
index 77abe17217f9b354b463af6895e4d60927a813a3..68aec11f652335c42463066677775200dbbcb333 100644 (file)
@@ -260,8 +260,7 @@ enum i40e_aq_resource_access_type {
 };
 
 struct i40e_nvm_info {
-       u64 hw_semaphore_timeout; /* 2usec global time (GTIME resolution) */
-       u64 hw_semaphore_wait;    /* - || - */
+       u64 hw_semaphore_timeout; /* usec global time (GTIME resolution) */
        u32 timeout;              /* [ms] */
        u16 sr_size;              /* Shadow RAM size in words */
        bool blank_nvm_mode;      /* is NVM empty (no FW present)*/
@@ -476,7 +475,10 @@ struct i40e_hw {
        u32 debug_mask;
 };
 
-#define i40e_is_vf(_hw)        ((_hw)->mac.type == I40E_MAC_VF)
+static inline bool i40e_is_vf(struct i40e_hw *hw)
+{
+       return hw->mac.type == I40E_MAC_VF;
+}
 
 struct i40e_driver_version {
        u8 major_version;
index 71f59ee513046defd7f4334c13c9a2675cac0af7..ad2e285aefd4e8d8c4a6ff3e967e8f5f7660106f 100644 (file)
@@ -1142,7 +1142,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
 
                /* RX descriptor */
                rxdesc = &mdp->rx_ring[i];
-               rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4));
+               rxdesc->addr = virt_to_phys(skb->data);
                rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP);
 
                /* The size of the buffer is 16 byte boundary. */
@@ -1395,10 +1395,13 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
 
        int entry = mdp->cur_rx % mdp->num_rx_ring;
        int boguscnt = (mdp->dirty_rx + mdp->num_rx_ring) - mdp->cur_rx;
+       int limit;
        struct sk_buff *skb;
        u16 pkt_len = 0;
        u32 desc_status;
 
+       boguscnt = min(boguscnt, *quota);
+       limit = boguscnt;
        rxdesc = &mdp->rx_ring[entry];
        while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) {
                desc_status = edmac_to_cpu(mdp, rxdesc->status);
@@ -1407,11 +1410,6 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                if (--boguscnt < 0)
                        break;
 
-               if (*quota <= 0)
-                       break;
-
-               (*quota)--;
-
                if (!(desc_status & RDFEND))
                        ndev->stats.rx_length_errors++;
 
@@ -1478,7 +1476,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                        sh_eth_set_receive_align(skb);
 
                        skb_checksum_none_assert(skb);
-                       rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4));
+                       rxdesc->addr = virt_to_phys(skb->data);
                }
                if (entry >= mdp->num_rx_ring - 1)
                        rxdesc->status |=
@@ -1502,6 +1500,8 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                sh_eth_write(ndev, EDRRR_R, EDRRR);
        }
 
+       *quota -= limit - boguscnt - 1;
+
        return *quota <= 0;
 }
 
index 55364359b868b1aedba6680ee2f84b74003f528f..2f398fa4b9e607546e8f1255ec7a53bfa018114c 100644 (file)
@@ -3717,27 +3717,11 @@ static int rocker_port_bridge_setlink(struct net_device *dev,
 {
        struct rocker_port *rocker_port = netdev_priv(dev);
        struct nlattr *protinfo;
-       struct nlattr *afspec;
        struct nlattr *attr;
-       u16 mode;
        int err;
 
        protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
                                   IFLA_PROTINFO);
-       afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
-
-       if (afspec) {
-               attr = nla_find_nested(afspec, IFLA_BRIDGE_MODE);
-               if (attr) {
-                       if (nla_len(attr) < sizeof(mode))
-                               return -EINVAL;
-
-                       mode = nla_get_u16(attr);
-                       if (mode != BRIDGE_MODE_SWDEV)
-                               return -EINVAL;
-               }
-       }
-
        if (protinfo) {
                attr = nla_find_nested(protinfo, IFLA_BRPORT_LEARNING);
                if (attr) {
@@ -3772,7 +3756,7 @@ static int rocker_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
                                      u32 filter_mask)
 {
        struct rocker_port *rocker_port = netdev_priv(dev);
-       u16 mode = BRIDGE_MODE_SWDEV;
+       u16 mode = BRIDGE_MODE_UNDEF;
        u32 mask = BR_LEARNING | BR_LEARNING_SYNC;
 
        return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode,
index ccbb082f33913b5a2455dda696c5d92ea66f57e2..f9df9fa86d5f0010d2d87ecbeca4a8df395c08c1 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 f660553c6c483f2564a536a02aa5da05b79ee885..7762061a194458b4982326d7edaea90700a57d8b 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 8f86bd34e82398697008381352caf78fc2e3761f..514c219263a5905a11288f842789133c01f49350 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 a8f5a72ba2591059594dcf590f960d195c85d8eb..a716758f14b038b4052ebb2aa6a67f3cad56db67 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 0c934a8378dba315489df956626b284027f1a585..1b87a5dbec53c3bd8875774ca8806525d3476efd 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 30301f5b60515303923d433a3a7cc1d8cf7c60c4..0c92e0251e84f64fd0e8b53679deda60803749f9 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 676bd4ed969bd5d9f63817ea6e5229c41a7c644e..f1946a6be442cb1d031b91b0556df1b3ae275b32 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 15c58e884b6a3ed2a09f09cf318e9fd8f3bad1ce..1bd5545af903975fcc5651628b46c3a989f25678 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 52c6306727181cffa481c8110f111d20dda87be2..9c782a42665e1aaf43bfbca441631ee58da50c09 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 5b7e42f7377cd35ccba603529ca20c097ff0271e..4bc51d8a14a3aed706429f769c33f56226ee366d 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 392c2501d0a1d9c75a24af5297f73780f6e10a28..dfedfd0e0f344a8b1893de5262de261c5f403e44 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 1245ac8c5c6f3d30b941aa92e0b36cc8d4587e1d..c4005670cba2e6ac934e8804629735e7b9180792 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 4e3c989aa841dedabfb726c3f9cc69a9f03ac04a..68296117d20333e9b3eb428c50b2e15f82fbd648 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 3a6b8a5ca96cce31d6574b4e1c1535a191fd5195..7abb8367119a872f0de5063d9ff1f75025abcebd 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 ceea5668f3f6a0bc213bb8695ba6d1a95a743f64..b289378b6e3e2cb3d17153fade7a88f78132bb31 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 c2bc8282f6cb169f7d13db32fe62a00aa1d3b10a..c0f3e4d09263e38a39b5e471faba6fc0d321a7e0 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 a38d788a6101b3e87a93a8349340997c56f4e209..21391929d318c8c3d1a10932952326dc0e9f6438 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 0583c69d26dbb4a95b99fa82704e8af14f27b9b8..ddaad712c59a814b52370f4b1a46ad8309b58c48 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 2a93519f4bdf43479a423a7f9ad5e8773f4b9ab9..f816909d9474e204c36abe20e73c16e0c259956b 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 e726e405152c7947df232e07f1312df2d33d9422..08225a0067c2d8b75bddd1021359c654b5ffb012 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 057b1657c4287f1418058ac3c4f8ec6a305619f9..da84b705cbcdc476734ac5f2c41dc82443e744d6 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 2df6d2ee70c25283f3c03b3957e8e4f0fce6f618..ae6cde273414cfff67e0c455770f75c2b9243f16 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 abe8bd6b972d44881813dd585a5b5ff99e674221..1a9fe0983a6be647010a009bd1e7e7be3ff7fb6d 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 ecb783beeec2a1257ccc3abafa8bced7818246b2..cb366adc820b17a667a908d79db82da8519f3783 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 4cf9e0ac07439c3dc29aa5502b2c4c3543bfaa09..1cbd335515134e0d4c5ccbfd7fa8756c01a9801b 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 59d679cebc89e1132fdb313e4fea7e953a35728d..d1c39346b2643c1f0b51c4c4ba071616e57863b9 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 275205ab5f15ea15a00f341942efbe6f3bdc6adc..3e58bfa0c1fd1f711f56def45305f80cc8bdfa33 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 aa69ceaad0be325e05f1c98e6f22c14b8f4b84d5..e55fa11894b6cc5d90846eeed6bbf2b157e21187 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 ebbbfc7a193b00b3250b20334bfd12cb55ad2ee1..9a72640237cb7678500468343e6f09101c169e81 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 ced36b475accc6f8afa89366ebbd8cdc72ca90bc..fb11a9172f38a917878f16c06ec84b6dbeddfcfe 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 d6e54a3c88f671f08ae3174ddabf6721fb39e051..e9bd02c2e8442ccd080c6ce0f977de08a6271518 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 650be79c7ac97070f0fccd62cea916ce56d8c097..cfd0554cf140eb7390756401795794e1e3550914 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 0fc0b9f8e605f0f51afb3e49e317b2e14cfcc9b4..38332a6dfb3a58d8ed246a8dac627ce18681212b 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 8d99021d27a8806cef563879280c8bdf17c80c10..3249562d93b4f978eec71eb6886bdcf3d7c8666b 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 54a6ddc6301bcabe3c48784e850033c6a170410e..4e6e14501c2f5e654be41bbadae02ffcfc1c166f 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 8c6f3b041f7773991c57b056229583f6e326d6c8..93c5cc16c515c8df5bfc3e2be9dea61e3249ee5a 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 44cb71f5ea5b76a0ccb3c9f9db6029aa7b1b6ec8..d4acf93a9a02b9fbfd9f5d063726bdd5e84d47c5 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 90f416f239bd1478132c7abc693e1b060ac3c63b..4bcbd6297b3e848e6f9530fc7dd49ed2b0de3111 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 6212983fede27d36ce520f78f40668531e7e96ff..8ff3fe34fe05ef964444f3299e1a3763355ff01c 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 c680906bc0dc6ebbcba1aa270c11d7cc7c0681fc..e3f8bdce5abc46ab6ee39f2107cfc2a3b0590026 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 95d3a062d35c57d9634dba7bf10a836891de9c50..c6ec5b99ac7d5849995ae186da4deeb2df96140e 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 bb1e066f756a88cd3920cbc62ccf251c5fa6aa4e..63476c86cd0e1b159be33fc85753583a1ca4cd34 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 f8a9dfa657ba6a4a2e4a54d2ba218c0ff862be26..3aecc5f4871963353ef46206288d2fc6cbc9ec64 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 2a5b22cb3fef2ad8e5743b4b48c37a6f12123a10..9e98b8d5275787a5fa503e0e94911c57640ef8db 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 f407665cb1eaab430e2ffed21a3181280f44cdd9..effe6d7831d986f7f30ae89370ad4f58c6cdeadf 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 98228e922d3ae3dffd83e38b328c960700008db1..23f74b139cc8ef13e845057e165ec329e7d639c4 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 931f68aefaa476684a735bdd7389cc6a003f9e4d..defb7a44e0bc1ff554195890dcccc31bfb0c9769 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 b9a796d0a44d9d1f3b8b01092d76937998dccd09..f5832e077bb7999344877f5fd3bf792cad2ce298 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 7338b335e153ead328b7ca79552ec29070bf85a9..ec62492ffa69fd3fefd6481b2ec2e152167b0e36 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 51f88c11e642e71747ea7a4be11f2b7b7f9055ab..03f2c406a17bb034f9e7078f8c5c65344f316a9b 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 ba64b292f7a516590071beba37f103ec7099d2cf..50891c02c4c162640acca7dc44ed56aec27b7385 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 9f783db34ae5f87b76df083d9e805d3d13f83deb..456944a6a2db8877ff2a73d20d2046b832e468f4 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 138691a1365af6534b0458bf3b0bc44066372059..905991fdb7b101a5dec4694d898a2b394cef06af 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 222f26a396424715368f3cbc4a94cedd82435821..50cdf7090198b3662b83488ff52e3868162401c5 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 19740c1b156603bd5c58e3fbadf1f837b59776e3..c9a8b9360ab14855daea5a869b02b250ba8020de 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 738cfaca1e0f525a06cc0fd8f1f047f44f24dbce..a104d7ac3796491a62274f1bcf7399d15d046a22 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 0f7e1c7b6f58c914b5b6f8d42814481d057d3d5d..906e89ddf31903fb4c18969b46655d8d0bd50457 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 af26e0de1e5c6b5325dc895b3c28c1f30af8c8c4..6996fcc144cfdfa874834481b180afd07c7e20ee 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 8ba445b3fd72a92ca78cbd1042dcc38528df8efb..a043e29f07e2cd16382b00cccc17d615d75cd5bf 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 139de90c2aaff46e8bed7c3def3c85499313c7a9..ab019b45551b9ea9bef61a1861feba7601897a5f 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 751ae1d10b7f6d27650e1a1a966a65651dd2ca30..7a34e4d158d1a68916d9a43f49105827338f708a 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 02e4ede2b04220beb03fc3701b541fb89658a66a..1d2223df5cb01fc84a06a55e431825175864ba34 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 b04b8858c6905ae700ac4081e57f56f59668eaf8..e5be2d21868fa975619b72fb8217cad0f54cefe6 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 896ea906549c89d50eed5bb5069c4a22b61c6fe1..bf0a95cb71535f390dfcfcc5820d39901359d440 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 f8aa9cf0827913e1c242b95609eeb821a83cd12a..3a4b9c7fc083e41470f481bebb0da94f5d6bb75f 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 3f6f015285e57ee17666af25414e5a40ffb6dd78..aff63c3f5bf84301f39a9c2419bb66575aa41de8 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 0a70bcd241f5b9703096726e9e3e81b1cb84b5f0..6842545535582246cf5369327c7748344ce98a97 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 d9fa8e034da2cfbf206d0f5cc9e06e29c0391a5b..38de1513e4dedd5588367e44d2877ceed39234b6 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 74b796dc424287172b363fc11bc430c93bf9ef97..41ff85de73343b0a5686bfd175164807e8dc4684 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 e30a41d04c8b3ac1af8878ab1d72fc02419aa80a..20a8a64c9fe31dd5e1b9b2244b6ca86b19868fd4 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 401f7be36b934b7524d56508c579e663212c8782..f2a047f6bb3e519a6ab3ce3032be18aac3aa9aa9 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 649fdae773832b70596e25a3e7146854ec90de36..e6dc3b8709492f06d7833ce74f5b87f886c8805f 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 c302e7468559ced39edc4871466a52938091a039..06e02fcd6f7b1319ccdd2c85cb21184c2074d7b1 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 b6d666ee8359df46c5052135fd7ddcfb82525868..17de6d46222ae7c803605017c066453d73076f01 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 1560f4576c7d3ce69e7373f5fdb688a0243f2cb1..2df51eab134872fe511d5e498397e2e22ae94ff3 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 0768f83e709d3f117139d6e331f610aaac750a97..028408a6ecba44cdcfa9aca8fd160156e9f8ddb0 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 508c81359e41b7fef1e6c7025a355481ae888aae..a3bfda45d9e6a28bdd44e2000498456988ef6558 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 b571e1b0550c6131fe1c53905035cd99661c63df..b3210cfbecc8e4f68254361efca6b815a05f12e6 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 5c1ea80d5e3b0febb83e003d639844d1941b0bf1..3bd93476ec1c8fa855fb0576904b6393dd9594d4 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 3bbb511b0b3883ddc2f5c08808d0cabc5d15515b..744de262373ea38acc55dc32e1f5d1f6181be154 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 51b7116965ed8a9d68383f043d139c11071a5ac1..33bf915cd7eabfd4a8b198a1a2394c8af7e62e44 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 816883f9ff94f541906cb945d0bf8002f8f3770b..f3b11897991eeee2e1d88329d075e52a25a106e3 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 e74cdf2132f87a5a94ec2ff9f0c588b709833f24..6d3bea5c59d1eb8ab42bca2f0c1a4523a23b8291 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 2fd8ad4633e0ee717096461398c96fc67181d288..430020047b777e5ff830d2ca0485cff86b64278e 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 1354c68f6468a79d78da53882e053aea6c900d9c..1f2acf47bfb2c78ddb1ee623f9254e35fa8eb109 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 c62575d86bcdbe5e75c724cacf8605e8569309e6..88af6dd2ceaa4034e7dd167fad32fbb22fcc2fed 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 eb03943f846326207061a2ed63e95df09af93caf..d0fa6e9ed59098cb52e05102e72afbffe3719fc8 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 b8ab4a108720240786d320c477e919081d6bf123..f6d86ccce6a8c3d705c18c252f839ff81e4d6f86 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 1ee9dcd26ad9db93dbf9c4dc55a76676a441f7a0..31a5b3f4266c3edaf26a05dfca208ddd2082f8ca 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 d015fac06a62ae2fc144b38ffbebe59d060b5617..d24660fb4ef23b85cdffff195e1c04c77a43857b 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 af074563e77076fafb88ae6c3d1c21a6f4da53e3..d55fd8e3654c830d047fbe9712686ef0dbff73b3 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 adcbf4c8edd86c37400029db065dd1720b8cdfed..68b0169c8892c8a62105e0fcff817c14e1696780 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 7a9578567f4f0853a0e560fb05c7ebb6377492c1..97dfba50c6820b3432244551c2d235b3b2941fa7 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 12283b55ee84ff5aa49b629d3bd4b702b9826720..1c0d4a45c1a8e95064a4cfae49c225d419c6dd1e 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 5b85b0cc7a2aa84675ed685a216cd6fe6f1d7f64..2620dd0c45f9638c949fd34b7482533b13c33126 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 ce884847cc8a84c7d04943bf21fed89ad44b9233..30ceb67ed7a7a22a86c376f9713a4af9d8e84125 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 3cf40f3f58ec2214323b16aa2708dad1c3430163..94b6e7297a1eca297488ac8377496756c85a18e2 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 fb2a8628b8fca3ef75184471bf40faf89f7801d4..e5294d01181e404c44d78190df5ae869d8c0cd21 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 dd0dc5bf8583960a82830a61c0527b5a32f946d7..d86fe432e51f17ce0d4f7ce8897fffd3fbace3f6 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 d9c0d7b0e9d4159506ead71fcbe148aa1d335a81..d8f48975ad087db1fad57b9f764fcb137416e2d0 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 66c82df2d0a16eb948e011a263af0abd3d031db1..c0e00bae5bd0622090de58a17f2f9b4fcc35e22c 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 6dfad230be5ed658f2cb3f295e71b43d724d293c..54fafbf9a711fb9bacb3149fcb9f8ce1826c4b90 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 b350e47e19dab0606737a8950d7f2604e1f90016..6f6b35db3ab8eb9b71c8bf178d7227254340a41a 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 d4f2c29025c74d021558b1d52e35c3aeea9cff49..2b1e61fac34a2b7bcd0456c06a4caf45f86e6e6e 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 8d848735cdb8199b5eff0aef2ff182100c54036d..4f15d9decc81bd4fe80bd2ac716667e2c3b97aa1 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 6ced8549eb3a26d1e048f8d63cc8da26bad2caa1..3ee8e3848876f48cce72ce98dff2e2bfde875a23 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 ea8efed25c6abadbe97001ce04e7823f2da8d157..5d79a1f44b8e43a723b9de98ca780a9ea5bc5812 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 eb8e2984c5e90f6d8f498e69b1cee4420dffb85d..8a6c7a084aa1271da349ae82a9077b9c07284cdb 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 2371d11e4190b718c21520305130d8bddb1c4bb5..a71b9d5e353d6d114c10d712b72f553f6edfe8ec 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 62f5dbe602d3de46f8daf6408f776670e024fe05..9d4786e7ddff461d982d90a48b9e85919f69615f 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 5ef5a0eeba50eb682d30e5879f565ac48ad217e4..d73fda312c87a53278b3fda1fc5c3fb521a2b805 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 17f0ee02d6e73ab0748a839b50337e3573a58db8..4a66a655536647ed0a72d5a1e41bcccc15fae15c 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 fc0b1ed80a6a57c541efa21610cbdee5ab238f1c..2269acf41ad8e6c63498a1728d00d771e162e59b 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 e095f371545a0c4e0d5d7db4199c42c1c2a30e42..fb5936eb82e3b65121b95b882e38719a1cbb96cb 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 cc15ab81aa66146d6ce867e605652e474d2fc83b..520ad4a3018bd1657b7edf267b3bd78af46f052c 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 0e50120eb8078e159156e9380978818b677607aa..d4d2223d1f317585c7248e368501613a3574d9f1 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 5a690d5210f02b5206ca139006adc534c4b88617..e66993cb5daf185f32c9053ce30577678cd861a6 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 3a17821157d77c1503ab636fafc8d244ae538def..984a7a4fa93b4920600255ec79966b3ab5272e49 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 204ecc8faa5be9cfebef1fefbfb0ae88925b421c..b8c171df6223d827da184ec4617c6ac14791752e 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 dab7b33c54bed0d2849ba2b275b103bf0925ba1e..b896d7375b52607959535d2103986f82ec678955 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 a5983fc4e83abbb174ae27a9da455bd702366376..6ae133333363a75d76961517b6174561e6bd7aa2 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 7c2b97660a032ccdb424b5783e38c0680f5599c3..c54a537e31fbd2daa33da36e4d08a665778cf8be 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 ec7309d096abaf5a11845eea72659374e318fce1..be3a203a529bcbad36610fd21adda816bc39bc5b 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 a113ef8f0b8b6837e05b3d0900239fed5a88028e..b1768fbf98f2235a9c762b8af4a2fcc2fdf245a9 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 94c98a86ebbec84bc83fe8c6c9f3e879c09df627..ffffd2c5a76e3d523930ac6760ec9b4aca97ec99 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 d273be7272b96b68d319ef1d0502dfe2901c12e8..a5f5f0fea3bde03536f612d3c5192071c1d634ec 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 c878e3f3993c3ecb7692b980aec723e9723f656e..05c64597838d6610c876ad548b2b6b6f8c536c18 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 9f57a2db791cf879fd9780b65926df300e461f92..81ee481487cf8b648ea4640b5f2114919c99ccd2 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 1ff81afb672c5408bcbe975552b846fa8b510626..9bb398bed9bb68ba133d702c2e5be8b0b089a8ae 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 dc85d3e0ffe58ecf23faaa1fc75fd92c974b24be..892270dd3e7b898c12acee8ccc2ef262da775f6d 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 819690e978c07dc27e50b2cff1fa3e64f37e3486..8f85fbd5f237eff576e343009d1aeefe26a39f02 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 95724ff9c7268700628866f433b66e82a8a9f7c4..a5458cf01fb26be50976c3dd4699e476e540c3c5 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 af52f0bdb71e979766c600f7f75da3cc452ccf64..5fc6f52641bdf61bcf31739fc47f7f1d78596f87 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 55357d69397a3370d8f4081ecec3d578daa3b8b9..d2ec5160bbf05dd5be42d70a4780292dc8768d58 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 46ea07605eb47f449f7850dea3b8b0862555ffde..dd5aa089126a82c5bf7ee791756f1a03a177a4b8 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 dc3d20b17a265479906fa9d6777cc6e29e533f34..e88dcd0e0af17499108057b94f77e25f3ffa9dfc 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 9a39ec4204dda77ef6e268be0396d3399ffc33f8..4bec4b07e3e017ceee47755dac2e8745a74df4db 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 11952b99daf8991626a23f6ca7d48f1f03b364ca..0315eeda9b600aab5a28fa6d27d8c63fe80e27fb 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 9c34a85fdb89f1acf280e20722d81ccb2b1b0511..6220672a96f44ffc539ccdc2325876b96dc6895c 100644 (file)
@@ -1,6 +1,3 @@
-obj-m := rtl8723ae.o
-
-
 rtl8723ae-objs :=              \
                dm.o            \
                fw.o            \
index 59e416abd93ab014ea62b07643b3d839689f4297..a77c3410279275320eef1195e7fd6c78556631da 100644 (file)
@@ -1,6 +1,3 @@
-obj-m := rtl8723be.o
-
-
 rtl8723be-objs :=              \
                dm.o            \
                fw.o            \
index 87ad604a1eb3267a4756e4d36bcdea19e8502a5f..f7a26f71197e01db7fe089dc6ba9de940986ba1b 100644 (file)
@@ -1,6 +1,3 @@
-obj-m := rtl8821ae.o
-
-
 rtl8821ae-objs :=              \
                dm.o            \
                fw.o            \
index 16d10281798d764ad8fb69f73cab6de0c2ff10d0..5153640f45323ac56eb9b662bdb08415e447562b 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 440291ab7263202fc8017c3b9f09f8318f5f872a..fc02e8d6a1936d89dffae06d78441cd0ba62ab5a 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 0ea756b7751941f4594ed8f67c60bad9bb271c75..05722085a59fafb4c8a66db1095a88bcb25d7970 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 a89e56c2c74954a0ac248bb00f603cd0528af3c4..f2596c8d68b0b36b12501914e7a1ea49ce16c058 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 a0b77f1ba6d905ae07f322414250bfc4ee6cbc9c..7c2a852922304513c4bd52794e0b03eb7de11d23 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 bfb6df56c5058d36d007c0753a8aa31e25e4f9ce..8882181d65de7c71c3f5940d9aee422d5a57ecd1 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 ca213dee9c6e66537389c03a0e9682cef08471b1..baf4664b4fc44d6db40aba4e879425df66d2e5e9 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 c5d2427a3db276064c9669722b2039e5ddf90e3f..01ba865863ee93dfb6843ae26d182efa288b6284 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 e7bff8921d1143876246c90955649181af573dce..bac50e805f1d565ba17d4c0056fc8a2f2847ae90 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 69161bbc4d0b37f135afeebfaac7e2a3c548e685..410215c169208f1735ed3df0e76189bab8c980c8 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 b37ea95bc348f15b16a9c0d095588379b12edbee..c05ff0f9f9a55afec230435183dcce0d93bd5725 100644 (file)
@@ -127,6 +127,9 @@ void unregister_candev(struct net_device *dev);
 int can_restart_now(struct net_device *dev);
 void can_bus_off(struct net_device *dev);
 
+void can_change_state(struct net_device *dev, struct can_frame *cf,
+                     enum can_state tx_state, enum can_state rx_state);
+
 void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
                      unsigned int idx);
 unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx);
index 1730312398ff24ab3d0f38a63b713fafd4236ed1..5087fff96d86a53d04443235aaa9351e5cc6b5ba 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 2d11f1f5efaba8884b9e2c7a07ad7e26732b9d48..c3b432f5b63e7eea706738a951d64e2556033ee4 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 5d9cc9cd2855e3b0aaebbb880a79f7ce16c604df..67309ece0772b9a28e054af553c9aabd76021699 100644 (file)
@@ -130,7 +130,7 @@ struct tcp_sock {
        /* inet_connection_sock has to be the first member of tcp_sock */
        struct inet_connection_sock     inet_conn;
        u16     tcp_header_len; /* Bytes of tcp header to send          */
-       u16     xmit_size_goal_segs; /* Goal for segmenting output packets */
+       u16     gso_segs;       /* Max number of segs per GSO packet    */
 
 /*
  *     Header prediction flags
index e56f9099f8e34b6f2970928da3d17de9c12de019..40129b3838b2aa8d811756a61900de4ecf7e850c 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 a805b3d97c0b467d55fc7812c280a61e03771558..3c7827005c25fafdcad5962866003ba132cafaa6 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 692f786bebe2b68b22c0750b09b60ce45396e476..d1bb342d083f14efc11ba4992e8c7509a67deac1 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
@@ -615,8 +617,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 b391fd6634689f816af155e3731c925a205275da..95c34d5180fa1ec656bf996ed034817674b34ecf 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 bb748c4da5afdaff14cb8d6ed753a4cc155ace39..4ebb816241fa1ad76506d30265878cacb47b1aec 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 cff3a26a9dae0444d90b808fecdae1e1e387dd2d..58d719ddaa60c93d2c6424765e96abf254cb4dc6 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 d9a5cf7ac1c4750c8418ce867366089e1f133bdc..0ae101eef0f44a89271368d858aae83f030b2e26 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 7ee8f4cc610bebecb119ce28566604e1cd6c3f3c..14bd0e1c47fae31ba0ae70669dde92741a81cc64 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 9eca9ae2280c57245a4d40b3a17164a98859ccf0..e7257a4653b40a9c4a1c15d938a0ede07ac94fcd 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 75d10e625c4969b0ddc806e5588815347ad9e73d..9e51bb4d841ea4915c2e70f0d7187b656ae27cab 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 6c583e244de2198d41effbf8a21086296a2c7e40..12adb817c27a3f157f351915e38ecc131fd52e82 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 dad7ab20a8cb204f08b2ecda724d5c1db5138ec8..b776d72d84be8f03fde9650c4b90d1b17940d543 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 c247446ab25a4e564a068ae97e154cf7972f0a1d..1c508be9687fc593dc4a5baee0e0e443ff4e8e71 100644 (file)
@@ -71,6 +71,7 @@
 #define CAN_ERR_CRTL_TX_PASSIVE  0x20 /* reached error passive status TX */
                                      /* (at least one error counter exceeds */
                                      /* the protocol-defined level of 127)  */
+#define CAN_ERR_CRTL_ACTIVE      0x40 /* recovered to error active state */
 
 /* error in CAN protocol (type) / data[2] */
 #define CAN_ERR_PROT_UNSPEC      0x00 /* unspecified */
index 296a556454e344b2cad71bc508b1fd047ebdf380..b03ee8f62d3c2a019924e4aabf02fdc462c75ffb 100644 (file)
@@ -105,7 +105,7 @@ struct __fdb_entry {
 
 #define BRIDGE_MODE_VEB                0       /* Default loopback mode */
 #define BRIDGE_MODE_VEPA       1       /* 802.1Qbg defined VEPA mode */
-#define BRIDGE_MODE_SWDEV      2       /* Full switch device offload */
+#define BRIDGE_MODE_UNDEF      0xFFFF  /* mode undefined */
 
 /* Bridge management nested attributes
  * [IFLA_AF_SPEC] = {
index 9b19b44619286616b04f1aa9f04d026878730744..8119255feae4b44df8bac40415f4681a9ccf3c97 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 d77524510435f47d6a8ca7c173fb13c394fc0f6f..b37bd5a1cb8253cd334ea05c3da920f0cf30f6e0 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 aced97db62f0cc85004da6b8ce9c44392f837e17..32ffec6ef1643427513529f33086e89fb751f991 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 5e97a8ff850b95e5aaa40be87cae3285041d8527..29bcafc41adfd19b59b0437021d5ebd044dcef9b 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 886e9aa3ecf1ffa3d7cd93d7aea873bfb25b5c1e..a5432a6a0ae6075d13a596a9f58e2380c7452b08 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 0a7cc565f93e0ba73ffbe0e93e7b82854134f595..012e3b03589d8ce305deccf09189971dd86d1b39 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 96887ae8375b52cf545708a3f9ea14fa40fc996a..79d84b88b8f0a766c9d8db9e93f4ef35221031be 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 d786958a1decd58a8236701f48f6a74850138b48..93f92a08550694dbe3791109631ab4058afe13ca 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 844f7d1ff1cd27aeb051577559fc0ae7fa1b3f7f..322abbbbcef991a36adaaf0750c723ceebba3147 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 1754040d00a833044627bf89810b7dae8b2855b9..a2b6dfa38a0cfd7f020c9d6f5a1584a92ae5c6e6 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);
@@ -3087,12 +3094,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,
@@ -3321,7 +3330,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);
@@ -4325,7 +4334,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;
 
@@ -4336,14 +4345,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 {
@@ -4409,7 +4414,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;
 
@@ -4433,7 +4438,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);
@@ -4863,7 +4868,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);
@@ -6955,9 +6960,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 f3e4a16fb1570e665a5c4177ffa69de10df4dbbe..7384f11613369b0997df0229ecc6d8ea2c80bb5b 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 de7dc7581ff072f5e2361c3506bddaa68d121b6b..6a46252fe66f39fb1cc490987d6c14d2ec955dfa 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;
@@ -281,17 +552,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:
@@ -309,7 +585,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;
@@ -325,24 +601,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;
 }
@@ -365,6 +669,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);
@@ -372,12 +677,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) {
@@ -399,7 +716,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)
@@ -423,6 +740,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] = {
@@ -433,6 +751,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
@@ -442,6 +768,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];
 }
 
@@ -451,7 +780,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;
 
@@ -468,26 +796,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;
@@ -496,15 +826,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;
@@ -513,10 +843,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);
@@ -637,11 +967,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
@@ -660,10 +992,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;
@@ -688,6 +1030,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)
@@ -704,6 +1121,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;
@@ -732,6 +1178,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) {
@@ -843,6 +1299,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;
 
@@ -855,6 +1319,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;
@@ -880,6 +1551,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);
@@ -915,6 +1591,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;
@@ -941,16 +1657,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
@@ -969,8 +1718,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;
@@ -981,7 +1728,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);
@@ -991,11 +1749,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;
 
@@ -1015,7 +1808,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) {
@@ -1029,14 +1846,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);
@@ -1052,6 +1873,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;
@@ -1065,6 +1908,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);
@@ -1084,6 +1930,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);
 
@@ -1093,7 +1943,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)
@@ -1101,8 +2019,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;
 
@@ -1135,8 +2052,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)
@@ -1150,6 +2066,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;
 
@@ -1161,7 +2078,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;
@@ -1244,6 +2164,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.
         */
@@ -1431,6 +2354,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;
@@ -1439,11 +2590,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;
 
@@ -1515,6 +2661,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;
@@ -1550,6 +2708,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;
@@ -1558,6 +2784,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;
 
@@ -1572,11 +2803,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)
@@ -1679,34 +2914,40 @@ static const struct l2cap_ops smp_root_chan_ops = {
        .get_sndtimeo           = l2cap_chan_no_get_sndtimeo,
 };
 
-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;
@@ -1715,20 +2956,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) {
@@ -1736,6 +2971,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 f76083b8500535395ee5c063aa609bcc5e69d1cd..3296bf42ae80d627c478c5199e167429ca16079b 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 ce82337521f665c5847819402d8a9c167452fb90..66e08040ced7557ba19e7535815804bbcffca12a 100644 (file)
@@ -64,9 +64,6 @@
 
 #include "af_can.h"
 
-static __initconst const char banner[] = KERN_INFO
-       "can: controller area network core (" CAN_VERSION_STRING ")\n";
-
 MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, "
@@ -524,7 +521,7 @@ static void can_rx_delete_receiver(struct rcu_head *rp)
 
 /**
  * can_rx_unregister - unsubscribe CAN frames from a specific interface
- * @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list)
+ * @dev: pointer to netdevice (NULL => unsubscribe from 'all' CAN devices list)
  * @can_id: CAN identifier
  * @mask: CAN mask
  * @func: callback function on filter match
@@ -896,7 +893,7 @@ static __init int can_init(void)
                     offsetof(struct can_frame, data) !=
                     offsetof(struct canfd_frame, data));
 
-       printk(banner);
+       pr_info("can: controller area network core (" CAN_VERSION_STRING ")\n");
 
        memset(&can_rx_alldev_list, 0, sizeof(can_rx_alldev_list));
 
index 01671187e3fe4a862000f6cd463463f8b4042561..ee9ffd9565526eb0336fba37e17f0a31b2dd3c34 100644 (file)
@@ -78,8 +78,6 @@
                     (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
 
 #define CAN_BCM_VERSION CAN_VERSION
-static __initconst const char banner[] = KERN_INFO
-       "can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n";
 
 MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
 MODULE_LICENSE("Dual BSD/GPL");
@@ -441,7 +439,7 @@ static void bcm_rx_update_and_send(struct bcm_op *op,
        /* mark as used and throttled by default */
        lastdata->can_dlc |= (RX_RECV|RX_THR);
 
-       /* throtteling mode inactive ? */
+       /* throttling mode inactive ? */
        if (!op->kt_ival2.tv64) {
                /* send RX_CHANGED to the user immediately */
                bcm_rx_changed(op, lastdata);
@@ -452,7 +450,7 @@ static void bcm_rx_update_and_send(struct bcm_op *op,
        if (hrtimer_active(&op->thrtimer))
                return;
 
-       /* first receiption with enabled throttling mode */
+       /* first reception with enabled throttling mode */
        if (!op->kt_lastmsg.tv64)
                goto rx_changed_settime;
 
@@ -480,7 +478,7 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
                                const struct can_frame *rxdata)
 {
        /*
-        * no one uses the MSBs of can_dlc for comparation,
+        * no one uses the MSBs of can_dlc for comparison,
         * so we use it here to detect the first time of reception
         */
 
@@ -510,7 +508,7 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
 }
 
 /*
- * bcm_rx_starttimer - enable timeout monitoring for CAN frame receiption
+ * bcm_rx_starttimer - enable timeout monitoring for CAN frame reception
  */
 static void bcm_rx_starttimer(struct bcm_op *op)
 {
@@ -539,7 +537,7 @@ static void bcm_rx_timeout_tsklet(unsigned long data)
 }
 
 /*
- * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
+ * bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out
  */
 static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
 {
@@ -627,7 +625,7 @@ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
 }
 
 /*
- * bcm_rx_handler - handle a CAN frame receiption
+ * bcm_rx_handler - handle a CAN frame reception
  */
 static void bcm_rx_handler(struct sk_buff *skb, void *data)
 {
@@ -1612,7 +1610,7 @@ static int __init bcm_module_init(void)
 {
        int err;
 
-       printk(banner);
+       pr_info("can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n");
 
        err = can_proto_register(&bcm_can_proto);
        if (err < 0) {
index 050a2110d43f6b78f331b599569eaaf2d8803c24..295f62e62eb34bf4050eb0657c48426e583b65b4 100644 (file)
@@ -361,7 +361,7 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
         * The Controller Area Network controllers only accept CAN frames with
         * correct CRCs - which are not visible in the controller registers.
         * According to skbuff.h documentation the csum_start element for IP
-        * checksums is undefined/unsued when ip_summed == CHECKSUM_UNNECESSARY.
+        * checksums is undefined/unused when ip_summed == CHECKSUM_UNNECESSARY.
         * Only CAN skbs can be processed here which already have this property.
         */
 
index dfdcffbb1070651f9d813bcc0b075de511479c35..00c13ef23661bd92133fff7db45588d3bd83fc62 100644 (file)
@@ -56,8 +56,6 @@
 #include <net/net_namespace.h>
 
 #define CAN_RAW_VERSION CAN_VERSION
-static __initconst const char banner[] =
-       KERN_INFO "can: raw protocol (rev " CAN_RAW_VERSION ")\n";
 
 MODULE_DESCRIPTION("PF_CAN raw protocol");
 MODULE_LICENSE("Dual BSD/GPL");
@@ -810,7 +808,7 @@ static __init int raw_module_init(void)
 {
        int err;
 
-       printk(banner);
+       pr_info("can: raw protocol (rev " CAN_RAW_VERSION ")\n");
 
        err = can_proto_register(&raw_can_proto);
        if (err < 0)
index dd3bf582e6f0a180c7fb0135fde35b3f0c6a8ac6..3f191da383f6197dedc6dcf5468d7b646d29a85a 100644 (file)
@@ -3297,7 +3297,7 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
        rps_lock(sd);
        qlen = skb_queue_len(&sd->input_pkt_queue);
        if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {
-               if (skb_queue_len(&sd->input_pkt_queue)) {
+               if (qlen) {
 enqueue:
                        __skb_queue_tail(&sd->input_pkt_queue, skb);
                        input_queue_tail_incr_save(sd, qtail);
index a9be2c1617028c9203f93c8c8683f4099ad3e356..eaa057f14bcd4a7b4506709acbb090f0122bf4b4 100644 (file)
@@ -2751,11 +2751,17 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
        if (!br_afspec)
                goto nla_put_failure;
 
-       if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF) ||
-           nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) {
+       if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF)) {
                nla_nest_cancel(skb, br_afspec);
                goto nla_put_failure;
        }
+
+       if (mode != BRIDGE_MODE_UNDEF) {
+               if (nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) {
+                       nla_nest_cancel(skb, br_afspec);
+                       goto nla_put_failure;
+               }
+       }
        nla_nest_end(skb, br_afspec);
 
        protinfo = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED);
index 290e14f2e92e91d37b069a716955bf6c9856f61b..27eaa65e88e16ad51a70c33711f6f91d0e8fd61c 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 26da1e179737365b08436f5ec5157bc506f0e05f..d0a1282cdf43560750da759a2dfe12f98ced8815 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 2c7a93e7167e97bc22b949812550a46e2f48be9d..d1930b70c4aacf707e4463a0ca786a66cf4e7501 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 63ee7d66950e85d92e692016fbb187184ddbbf64..fa1464762d0dafdc481b68a7cbf46cfd45d8b30a 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 fe77f0c770b810b77b86fbbd089c440e76e02516..cd919493c976000e6b5cd206014110500e68886e 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 80a946dddd9095a86884bb12148e64d95aa79f4c..7baf98b146116bd52f961c371d31a33878c0b8d8 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 61e9d2972947e7827a2af431ec5000b70dc9d99f..1674b115c89132b47527d7c4b29f21f8d20f15a9 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 54ba6209eea9647758d95a71eeca7b2b679efda4..3075723c729bc98edf3a15eb0d0fbe172c300bbc 100644 (file)
@@ -835,47 +835,29 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
                                       int large_allowed)
 {
        struct tcp_sock *tp = tcp_sk(sk);
-       u32 xmit_size_goal, old_size_goal;
-
-       xmit_size_goal = mss_now;
-
-       if (large_allowed && sk_can_gso(sk)) {
-               u32 gso_size, hlen;
-
-               /* Maybe we should/could use sk->sk_prot->max_header here ? */
-               hlen = inet_csk(sk)->icsk_af_ops->net_header_len +
-                      inet_csk(sk)->icsk_ext_hdr_len +
-                      tp->tcp_header_len;
-
-               /* Goal is to send at least one packet per ms,
-                * not one big TSO packet every 100 ms.
-                * This preserves ACK clocking and is consistent
-                * with tcp_tso_should_defer() heuristic.
-                */
-               gso_size = sk->sk_pacing_rate / (2 * MSEC_PER_SEC);
-               gso_size = max_t(u32, gso_size,
-                                sysctl_tcp_min_tso_segs * mss_now);
-
-               xmit_size_goal = min_t(u32, gso_size,
-                                      sk->sk_gso_max_size - 1 - hlen);
-
-               xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal);
-
-               /* We try hard to avoid divides here */
-               old_size_goal = tp->xmit_size_goal_segs * mss_now;
-
-               if (likely(old_size_goal <= xmit_size_goal &&
-                          old_size_goal + mss_now > xmit_size_goal)) {
-                       xmit_size_goal = old_size_goal;
-               } else {
-                       tp->xmit_size_goal_segs =
-                               min_t(u16, xmit_size_goal / mss_now,
-                                     sk->sk_gso_max_segs);
-                       xmit_size_goal = tp->xmit_size_goal_segs * mss_now;
-               }
+       u32 new_size_goal, size_goal, hlen;
+
+       if (!large_allowed || !sk_can_gso(sk))
+               return mss_now;
+
+       /* Maybe we should/could use sk->sk_prot->max_header here ? */
+       hlen = inet_csk(sk)->icsk_af_ops->net_header_len +
+              inet_csk(sk)->icsk_ext_hdr_len +
+              tp->tcp_header_len;
+
+       new_size_goal = sk->sk_gso_max_size - 1 - hlen;
+       new_size_goal = tcp_bound_to_half_wnd(tp, new_size_goal);
+
+       /* We try hard to avoid divides here */
+       size_goal = tp->gso_segs * mss_now;
+       if (unlikely(new_size_goal < size_goal ||
+                    new_size_goal >= size_goal + mss_now)) {
+               tp->gso_segs = min_t(u16, new_size_goal / mss_now,
+                                    sk->sk_gso_max_segs);
+               size_goal = tp->gso_segs * mss_now;
        }
 
-       return max(xmit_size_goal, mss_now);
+       return max(size_goal, mss_now);
 }
 
 static int tcp_send_mss(struct sock *sk, int *size_goal, int flags)
index 3e225b03eb95f4b09a0f053a7897ef91f48a20ca..7f18262e2326ac4d7963347d7458273a325caa64 100644 (file)
@@ -1524,6 +1524,27 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
                ((nonagle & TCP_NAGLE_CORK) ||
                 (!nonagle && tp->packets_out && tcp_minshall_check(tp)));
 }
+
+/* Return how many segs we'd like on a TSO packet,
+ * to send one TSO packet per ms
+ */
+static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now)
+{
+       u32 bytes, segs;
+
+       bytes = min(sk->sk_pacing_rate >> 10,
+                   sk->sk_gso_max_size - 1 - MAX_TCP_HEADER);
+
+       /* Goal is to send at least one packet per ms,
+        * not one big TSO packet every 100 ms.
+        * This preserves ACK clocking and is consistent
+        * with tcp_tso_should_defer() heuristic.
+        */
+       segs = max_t(u32, bytes / mss_now, sysctl_tcp_min_tso_segs);
+
+       return min_t(u32, segs, sk->sk_gso_max_segs);
+}
+
 /* Returns the portion of skb which can be sent right away */
 static unsigned int tcp_mss_split_point(const struct sock *sk,
                                        const struct sk_buff *skb,
@@ -1731,7 +1752,7 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
  * This algorithm is from John Heffner.
  */
 static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
-                                bool *is_cwnd_limited)
+                                bool *is_cwnd_limited, u32 max_segs)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        const struct inet_connection_sock *icsk = inet_csk(sk);
@@ -1761,8 +1782,7 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
        limit = min(send_win, cong_win);
 
        /* If a full-sized TSO skb can be sent, do it. */
-       if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
-                          tp->xmit_size_goal_segs * tp->mss_cache))
+       if (limit >= max_segs * tp->mss_cache)
                goto send_now;
 
        /* Middle in queue won't get any more data, full sendable already? */
@@ -1959,6 +1979,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
        int cwnd_quota;
        int result;
        bool is_cwnd_limited = false;
+       u32 max_segs;
 
        sent_pkts = 0;
 
@@ -1972,6 +1993,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                }
        }
 
+       max_segs = tcp_tso_autosize(sk, mss_now);
        while ((skb = tcp_send_head(sk))) {
                unsigned int limit;
 
@@ -2004,10 +2026,23 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                                break;
                } else {
                        if (!push_one &&
-                           tcp_tso_should_defer(sk, skb, &is_cwnd_limited))
+                           tcp_tso_should_defer(sk, skb, &is_cwnd_limited,
+                                                max_segs))
                                break;
                }
 
+               limit = mss_now;
+               if (tso_segs > 1 && !tcp_urg_mode(tp))
+                       limit = tcp_mss_split_point(sk, skb, mss_now,
+                                                   min_t(unsigned int,
+                                                         cwnd_quota,
+                                                         max_segs),
+                                                   nonagle);
+
+               if (skb->len > limit &&
+                   unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
+                       break;
+
                /* TCP Small Queues :
                 * Control number of packets in qdisc/devices to two packets / or ~1 ms.
                 * This allows for :
@@ -2018,8 +2053,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                 * of queued bytes to ensure line rate.
                 * One example is wifi aggregation (802.11 AMPDU)
                 */
-               limit = max_t(unsigned int, sysctl_tcp_limit_output_bytes,
-                             sk->sk_pacing_rate >> 10);
+               limit = max(2 * skb->truesize, sk->sk_pacing_rate >> 10);
+               limit = min_t(u32, limit, sysctl_tcp_limit_output_bytes);
 
                if (atomic_read(&sk->sk_wmem_alloc) > limit) {
                        set_bit(TSQ_THROTTLED, &tp->tsq_flags);
@@ -2032,18 +2067,6 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                                break;
                }
 
-               limit = mss_now;
-               if (tso_segs > 1 && !tcp_urg_mode(tp))
-                       limit = tcp_mss_split_point(sk, skb, mss_now,
-                                                   min_t(unsigned int,
-                                                         cwnd_quota,
-                                                         sk->sk_gso_max_segs),
-                                                   nonagle);
-
-               if (skb->len > limit &&
-                   unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
-                       break;
-
                if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
                        break;
 
index c7c51422029822269762e40527a7ad44ddcbe991..5d6dae9e4aac099379e6ad8ed54d5ef40eac0f88 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 538fe4ef5c85dd7e285f8fb0f8166073cebcfac6..41735539087380c8e84735ef52efd1f2d265bbed 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 ba06cd003375bf9592603823b8f452dd97a5ec63..75a9bf50207ecd6cdf5ac41cae7101d03ac58a11 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 08ab7d6d15173822d844e60f920e15b5ac94c1cc..d53355b011f5cf6407367e3c5f1a3e513d1cc08e 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 18babe30283212c18ddb113f3295d211a310d014..38652f09feaf250ec0517e23c8f147c8921f7499 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 c2b91bf47f6d8d0caa64a29fce45c3cf8f76a83b..d51f6b1c549b8c24929e46b57b96df639d5939ba 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 d013429af7cee7b04aa0d8b2efdf135ba303c9da..80452cfd2dc59f0e9891d30ea2549fc9e958babd 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 71de2d3866cc08392687fefe8dd59abe9b36b064..bb146f377ee457e66a2f85cc99c264746bcace81 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 85ccfbe863db12b3cbec43dba001a7a4d3bbe143..8e461a02c6a8ed9f8851979eed7992262704f60a 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 66ddbbeccd20c1e6eba9ec44a046f29ca331dbda..058686a721a1de5e1145da56d3f176d1e08ed739 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 bb9664cb8831e337e3567dd32515f2593b641816..974ebe70f5b0139c0dd2aec7335201dec51e6aca 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 38dfc72d24b689483facf8404d12970978cb3e56..9ae893057dd761e4e82f563b9f24b4c59e6254bc 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 fa0d5237c2e08ba35cf4229b4527dbdb3030062c..dcf73958133a0d6d0c28f825f648b13cc9ef478b 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 3596b29ead6b8716aded81244bee99d9626acca5..5cf019a57fd79cd601209971c349023e6c619ea0 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 041dbd5958d4a8905440f42f3c2d843d89e07447..c0d67b2b4132b033ca28446556c5aefe7e047ee6 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 b60aa35c074f5220f0b939b347881cab8e6c9db8..f72be7433df35fc1acfa3105a151b40876c0d47f 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 677d24bb70f8af24569ef574404ba4b1b9e1c9f0..91df487aa0a9cf2a69f368a9211e80efcc06b012 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 117708263ced38dfa690e1d49a6c19d661751289..ef50e7716c4a8742840731db126351fee16bc57b 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 c4da0c2d8a14dc8c7e676dc0ca3b4900ab31318f..3621a902cb6e36e3399848694a8ab6bfc0a962a8 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 51e7887973171f999eb7c77984d7dddf081a5b75..b18f07ccb504c0f8c2aea36be80069a0376def76 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 83bc785d5855a378dc1d5bda3a093aff13e86c05..e181e290427cd9727d59ddb1989d2974937f1754 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 90b16cb4005880214f1eca87cc5e72f2f75c7c34..51feb5e630082a78d67c8342c0883168df9b2fb2 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 427ef2c7ab681245c8d392e76ccaace41cab10e3..a2de2a8cb00e2db62b46dc237f3538bc574b18b1 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 205b35f666dba827d4daafd6d29c33483e73a609..22e453cb787d4d62e3246919f32ae5db14557ef3 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 43cb1c17e267d0f614029bad9d3eebcf7b6f5e4c..44989fc8cddf19550a7a525503f9d267099470b2 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 741bfa7debb2e60c03c0c95162dae57d15463d6a..221697ab0247c5e786c70e181a43c1145a30748b 100644 (file)
@@ -177,7 +177,6 @@ static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long
                           struct sk_buff *skb, struct tcmsg *t)
 {
        struct cls_cgroup_head *head = rtnl_dereference(tp->root);
-       unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
 
        t->tcm_handle = head->handle;
@@ -198,7 +197,7 @@ static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long
        return skb->len;
 
 nla_put_failure:
-       nlmsg_trim(skb, b);
+       nla_nest_cancel(skb, nest);
        return -1;
 }
 
index 8e227180cabbef879adac8557085c0730a5c5c1e..15d68f24a521a8ec66fc42691f031d864f589416 100644 (file)
@@ -638,7 +638,7 @@ static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
        return skb->len;
 
 nla_put_failure:
-       nlmsg_trim(skb, nest);
+       nla_nest_cancel(skb, nest);
        return -1;
 }
 
index 23fda2ac0d19241638a6ec64e17c5764c5f95f11..a5269f76004c2974a2e1e650433a7ba394710965 100644 (file)
@@ -356,7 +356,6 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
 {
        struct fw_head *head = rtnl_dereference(tp->root);
        struct fw_filter *f = (struct fw_filter *)fh;
-       unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
 
        if (f == NULL)
@@ -397,7 +396,7 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
        return skb->len;
 
 nla_put_failure:
-       nlmsg_trim(skb, b);
+       nla_nest_cancel(skb, nest);
        return -1;
 }
 
index 098a27360b91782893a7363de3858e040371bc10..2ecd24688554e76e38d98b7338072a37d4c0cea1 100644 (file)
@@ -593,7 +593,6 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
                       struct sk_buff *skb, struct tcmsg *t)
 {
        struct route4_filter *f = (struct route4_filter *)fh;
-       unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
        u32 id;
 
@@ -635,7 +634,7 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
        return skb->len;
 
 nla_put_failure:
-       nlmsg_trim(skb, b);
+       nla_nest_cancel(skb, nest);
        return -1;
 }
 
index b7af3623a26a21cfd254bceb929ef5ee0d47f557..edd8ade3fbc1f4358b4275940e6f62b3d814b3dd 100644 (file)
@@ -653,7 +653,6 @@ static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
 {
        struct rsvp_filter *f = (struct rsvp_filter *)fh;
        struct rsvp_session *s;
-       unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
        struct tc_rsvp_pinfo pinfo;
 
@@ -694,7 +693,7 @@ static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
        return skb->len;
 
 nla_put_failure:
-       nlmsg_trim(skb, b);
+       nla_nest_cancel(skb, nest);
        return -1;
 }
 
index 0d9d8911a621fd2ef8c3b7bdee8edc476453164c..bd49bf547a479f139b25e0507b090d51c137c519 100644 (file)
@@ -489,11 +489,10 @@ static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
 {
        struct tcindex_data *p = rtnl_dereference(tp->root);
        struct tcindex_filter_result *r = (struct tcindex_filter_result *) fh;
-       unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
 
-       pr_debug("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p,b %p\n",
-                tp, fh, skb, t, p, r, b);
+       pr_debug("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p\n",
+                tp, fh, skb, t, p, r);
        pr_debug("p->perfect %p p->h %p\n", p->perfect, p->h);
 
        nest = nla_nest_start(skb, TCA_OPTIONS);
@@ -543,7 +542,7 @@ static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
        return skb->len;
 
 nla_put_failure:
-       nlmsg_trim(skb, b);
+       nla_nest_cancel(skb, nest);
        return -1;
 }
 
index aafa684c4db925a8ca5f985df52bbf495f2b85da..c8df0223371a7b86f971093fee1ac1ba47bffb26 100644 (file)
@@ -979,7 +979,7 @@ static void tipc_purge_publications(struct name_seq *seq)
        }
        hlist_del_init_rcu(&seq->ns_list);
        kfree(seq->sseqs);
-       spin_lock_bh(&seq->lock);
+       spin_unlock_bh(&seq->lock);
 
        kfree_rcu(seq, rcu);
 }
index 29c8675f9a1189db65c185f2ad04f96a67702989..22ba971741e5c998c5f314e083ceead4b2f6a94f 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 4c2e501203d175578287d97d544aa0639f3ddf2e..53dda7728f866dd3e7df234a242668c5dbe04f20 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 6e4177701d86fd847ab8bf933cca7c996f03b272..a17d6bc6b22ca7ccda9716af9a3367b66bc47632 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 32d8310b0f8571201032b585d2c52fc26ad4f39d..47be6163381caadf41afab40122d74f4a19448e6 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();