]> Pileus Git - ~andy/linux/commitdiff
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi...
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 18 Oct 2013 17:58:13 +0000 (13:58 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 18 Oct 2013 17:58:13 +0000 (13:58 -0400)
41 files changed:
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-csr.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/mvm/bt-coex.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-bt-coex.h
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.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/ops.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/quota.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/testmode.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/time-event.h
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c

index 76e14c046d9402cc0b7fbe39b0966c8195f0708a..85879dbaa402cdaa88d93247b678aaac9a3ff000 100644 (file)
@@ -83,6 +83,8 @@
 #define IWL7260_TX_POWER_VERSION       0xffff /* meaningless */
 #define IWL3160_NVM_VERSION            0x709
 #define IWL3160_TX_POWER_VERSION       0xffff /* meaningless */
+#define IWL7265_NVM_VERSION            0x0a1d
+#define IWL7265_TX_POWER_VERSION       0xffff /* meaningless */
 
 #define IWL7260_FW_PRE "iwlwifi-7260-"
 #define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
@@ -90,6 +92,9 @@
 #define IWL3160_FW_PRE "iwlwifi-3160-"
 #define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode"
 
+#define IWL7265_FW_PRE "iwlwifi-7265-"
+#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
+
 static const struct iwl_base_params iwl7000_base_params = {
        .eeprom_size = OTP_LOW_IMAGE_SIZE,
        .num_of_queues = IWLAGN_NUM_QUEUES,
@@ -182,5 +187,14 @@ const struct iwl_cfg iwl3160_n_cfg = {
        .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
 };
 
+const struct iwl_cfg iwl7265_2ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC 7265",
+       .fw_name_pre = IWL7265_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL7265_NVM_VERSION,
+       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+};
+
 MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
 MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
index e4d370bff30679fceacc1bc82907ccb6bc194f22..261e4a12fd8e1a99dba8f29fd0982b15f07e380c 100644 (file)
@@ -292,6 +292,7 @@ extern const struct iwl_cfg iwl7260_n_cfg;
 extern const struct iwl_cfg iwl3160_2ac_cfg;
 extern const struct iwl_cfg iwl3160_2n_cfg;
 extern const struct iwl_cfg iwl3160_n_cfg;
+extern const struct iwl_cfg iwl7265_2ac_cfg;
 #endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
index a276af476e2d7ab64e859748c9b8fd48b84ebb80..54a4fdc631b73c987f12e459e8245d38b18f0c0b 100644 (file)
 #define CSR_DRAM_INT_TBL_ENABLE                (1 << 31)
 #define CSR_DRAM_INIT_TBL_WRAP_CHECK   (1 << 27)
 
+/* SECURE boot registers */
+#define CSR_SECURE_BOOT_CONFIG_ADDR    (0x100)
+enum secure_boot_config_reg {
+       CSR_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP  = 0x00000001,
+       CSR_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ        = 0x00000002,
+};
+
+#define CSR_SECURE_BOOT_CPU1_STATUS_ADDR       (0x100)
+#define CSR_SECURE_BOOT_CPU2_STATUS_ADDR       (0x100)
+enum secure_boot_status_reg {
+       CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS          = 0x00000003,
+       CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED       = 0x00000002,
+       CSR_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS         = 0x00000004,
+       CSR_SECURE_BOOT_CPU_STATUS_VERF_FAIL            = 0x00000008,
+       CSR_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL       = 0x00000010,
+};
+
+#define CSR_UCODE_LOAD_STATUS_ADDR     (0x100)
+enum secure_load_status_reg {
+       CSR_CPU_STATUS_LOADING_STARTED                  = 0x00000001,
+       CSR_CPU_STATUS_LOADING_COMPLETED                = 0x00000002,
+       CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED            = 0x000000F8,
+       CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK         = 0x0000FF00,
+};
+
+#define CSR_SECURE_INSPECTOR_CODE_ADDR (0x100)
+#define CSR_SECURE_INSPECTOR_DATA_ADDR (0x100)
+
+#define CSR_SECURE_TIME_OUT    (100)
+
+#define FH_TCSR_0_REG0 (0x1D00)
+
 /*
  * HBUS (Host-side Bus)
  *
index 99e1da3123c9a07649008e48d2a2a56dc685d820..ff570027e9dd9fa02f3e8360af6966b480c68069 100644 (file)
@@ -483,6 +483,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
        const u8 *tlv_data;
        char buildstr[25];
        u32 build;
+       int num_of_cpus;
 
        if (len < sizeof(*ucode)) {
                IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
@@ -692,6 +693,42 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                                goto invalid_tlv_len;
                        drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data);
                        break;
+                case IWL_UCODE_TLV_SECURE_SEC_RT:
+                       iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
+                                           tlv_len);
+                       drv->fw.mvm_fw = true;
+                       drv->fw.img[IWL_UCODE_REGULAR].is_secure = true;
+                       break;
+               case IWL_UCODE_TLV_SECURE_SEC_INIT:
+                       iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT,
+                                           tlv_len);
+                       drv->fw.mvm_fw = true;
+                       drv->fw.img[IWL_UCODE_INIT].is_secure = true;
+                       break;
+               case IWL_UCODE_TLV_SECURE_SEC_WOWLAN:
+                       iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN,
+                                           tlv_len);
+                       drv->fw.mvm_fw = true;
+                       drv->fw.img[IWL_UCODE_WOWLAN].is_secure = true;
+                       break;
+               case IWL_UCODE_TLV_NUM_OF_CPU:
+                       if (tlv_len != sizeof(u32))
+                               goto invalid_tlv_len;
+                       num_of_cpus =
+                               le32_to_cpup((__le32 *)tlv_data);
+
+                       if (num_of_cpus == 2) {
+                               drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus =
+                                       true;
+                               drv->fw.img[IWL_UCODE_INIT].is_dual_cpus =
+                                       true;
+                               drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus =
+                                       true;
+                       } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) {
+                               IWL_ERR(drv, "Driver support upto 2 CPUs\n");
+                               return -EINVAL;
+                       }
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
index 8b6c6fd95ed038ac1052827a2edb2a39f04f849a..6c6c35c5228cabd71d1520f533af7bd3adf8ad26 100644 (file)
@@ -121,6 +121,10 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_SEC_WOWLAN        = 21,
        IWL_UCODE_TLV_DEF_CALIB         = 22,
        IWL_UCODE_TLV_PHY_SKU           = 23,
+       IWL_UCODE_TLV_SECURE_SEC_RT     = 24,
+       IWL_UCODE_TLV_SECURE_SEC_INIT   = 25,
+       IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26,
+       IWL_UCODE_TLV_NUM_OF_CPU        = 27,
 };
 
 struct iwl_ucode_tlv {
index a1223680bc70d2f765e32180176ac7997884a3d7..87b66a821ec8983fb79876519364880861b7ae71 100644 (file)
  * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
  * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
  * @IWL_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD
+ * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
+ *     offload profile config command.
  * @IWL_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api
  * @IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API.
  * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
  *     (rather than two) IPv6 addresses
  * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API
+ * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
+ *     from the probe request template.
+ * @IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API: modified D3 API to allow keeping
+ *     connection when going back to D0
+ * @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_SCHED_SCAN: this uCode image supports scheduled scan.
+ * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API
+ * @IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command
+ *     containing CAM (Continuous Active Mode) indication.
  */
 enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
@@ -87,11 +99,20 @@ enum iwl_ucode_tlv_flag {
        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_NEWBT_COEX          = BIT(5),
        IWL_UCODE_TLV_FLAGS_UAPSD               = BIT(6),
+       IWL_UCODE_TLV_FLAGS_SHORT_BL            = BIT(7),
        IWL_UCODE_TLV_FLAGS_RX_ENERGY_API       = BIT(8),
        IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2   = BIT(9),
        IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS     = BIT(10),
        IWL_UCODE_TLV_FLAGS_BF_UPDATED          = BIT(11),
+       IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID       = BIT(12),
+       IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API   = BIT(14),
+       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL    = BIT(15),
+       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE    = BIT(16),
+       IWL_UCODE_TLV_FLAGS_SCHED_SCAN          = BIT(17),
+       IWL_UCODE_TLV_FLAGS_STA_KEY_CMD         = BIT(19),
+       IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD       = BIT(20),
 };
 
 /* The default calibrate table size if not specified by firmware file */
@@ -133,7 +154,8 @@ enum iwl_ucode_sec {
  * For 16.0 uCode and above, there is no differentiation between sections,
  * just an offset to the HW address.
  */
-#define IWL_UCODE_SECTION_MAX 4
+#define IWL_UCODE_SECTION_MAX 6
+#define IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU  (IWL_UCODE_SECTION_MAX/2)
 
 struct iwl_ucode_capabilities {
        u32 max_probe_length;
@@ -150,6 +172,8 @@ struct fw_desc {
 
 struct fw_img {
        struct fw_desc sec[IWL_UCODE_SECTION_MAX];
+       bool is_secure;
+       bool is_dual_cpus;
 };
 
 /* uCode version contains 4 values: Major/Minor/API/Serial */
index ff8cc75c189d4d842abf8611fb8c5e7c7a63bf38..a70c7b9d9bad897345fb1e1e89d5c421e0d8a3da 100644 (file)
@@ -97,6 +97,8 @@
 
 #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS         (0x00000800)
 
+#define APMG_RTC_INT_STT_RFKILL                (0x10000000)
+
 /* Device system time */
 #define DEVICE_SYSTEM_TIME_REG 0xA0206C
 
index 0fad98b85f60dd8dedca668296b27382b15ca0f6..5b630f12bbff774e4108c4df070e96ef9a0e2088 100644 (file)
@@ -98,126 +98,258 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
 
 #undef EVENT_PRIO_ANT
 
-/* BT Antenna Coupling Threshold (dB) */
-#define IWL_BT_ANTENNA_COUPLING_THRESHOLD      (35)
-#define IWL_BT_LOAD_FORCE_SISO_THRESHOLD       (3)
-
 #define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD    (-62)
 #define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD   (-65)
-#define BT_REDUCED_TX_POWER_BIT                        BIT(7)
-
-static inline bool is_loose_coex(void)
-{
-       return iwlwifi_mod_params.ant_coupling >
-               IWL_BT_ANTENNA_COUPLING_THRESHOLD;
-}
+#define BT_ANTENNA_COUPLING_THRESHOLD          (30)
 
 int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
 {
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
+               return 0;
+
        return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC,
                                    sizeof(struct iwl_bt_coex_prio_tbl_cmd),
                                    &iwl_bt_prio_tbl);
 }
 
-static int iwl_send_bt_env(struct iwl_mvm *mvm, u8 action, u8 type)
-{
-       struct iwl_bt_coex_prot_env_cmd env_cmd;
-       int ret;
-
-       env_cmd.action = action;
-       env_cmd.type = type;
-       ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PROT_ENV, CMD_SYNC,
-                                  sizeof(env_cmd), &env_cmd);
-       if (ret)
-               IWL_ERR(mvm, "failed to send BT env command\n");
-       return ret;
-}
-
-enum iwl_bt_kill_msk {
-       BT_KILL_MSK_DEFAULT,
-       BT_KILL_MSK_SCO_HID_A2DP,
-       BT_KILL_MSK_REDUCED_TXPOW,
-       BT_KILL_MSK_MAX,
-};
-
-static const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
+const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
        [BT_KILL_MSK_DEFAULT] = 0xffff0000,
        [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
        [BT_KILL_MSK_REDUCED_TXPOW] = 0,
 };
 
-static const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
+const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
        [BT_KILL_MSK_DEFAULT] = 0xffff0000,
        [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
        [BT_KILL_MSK_REDUCED_TXPOW] = 0,
 };
 
-#define IWL_BT_DEFAULT_BOOST (0xf0f0f0f0)
-
-/* Tight Coex */
-static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = {
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaeaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xcc00ff28),
-       cpu_to_le32(0x0000aaaa),
-       cpu_to_le32(0xcc00aaaa),
-       cpu_to_le32(0x0000aaaa),
-       cpu_to_le32(0xc0004000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0xf0005000),
-       cpu_to_le32(0xf0005000),
+static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
+       cpu_to_le32(0xf0f0f0f0),
+       cpu_to_le32(0xc0c0c0c0),
+       cpu_to_le32(0xfcfcfcfc),
+       cpu_to_le32(0xff00ff00),
 };
 
-/* Loose Coex */
-static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = {
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xcc00ff28),
-       cpu_to_le32(0x0000aaaa),
-       cpu_to_le32(0xcc00aaaa),
-       cpu_to_le32(0x0000aaaa),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0xf0005000),
-       cpu_to_le32(0xf0005000),
+static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
 };
 
-/* Full concurrency */
-static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = {
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x00000000),
+static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+       {
+               /* Tight */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaeaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               /* Loose */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               /* Tx Tx disabled */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xC0004000),
+               cpu_to_le32(0xC0004000),
+               cpu_to_le32(0xF0005000),
+               cpu_to_le32(0xF0005000),
+       },
 };
 
-/* single shared antenna */
-static const __le32 iwl_single_shared_ant_lookup[BT_COEX_LUT_SIZE] = {
-       cpu_to_le32(0x40000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x44000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x40000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x44000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0xC0004000),
-       cpu_to_le32(0xF0005000),
-       cpu_to_le32(0xC0004000),
-       cpu_to_le32(0xF0005000),
+/* 20MHz / 40MHz below / 40Mhz above*/
+static const __le64 iwl_ci_mask[][3] = {
+       /* dummy entry for channel 0 */
+       {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
+       {
+               cpu_to_le64(0x0000001FFFULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x00007FFFFFULL),
+       },
+       {
+               cpu_to_le64(0x000000FFFFULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x0003FFFFFFULL),
+       },
+       {
+               cpu_to_le64(0x000003FFFCULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x000FFFFFFCULL),
+       },
+       {
+               cpu_to_le64(0x00001FFFE0ULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x007FFFFFE0ULL),
+       },
+       {
+               cpu_to_le64(0x00007FFF80ULL),
+               cpu_to_le64(0x00007FFFFFULL),
+               cpu_to_le64(0x01FFFFFF80ULL),
+       },
+       {
+               cpu_to_le64(0x0003FFFC00ULL),
+               cpu_to_le64(0x0003FFFFFFULL),
+               cpu_to_le64(0x0FFFFFFC00ULL),
+       },
+       {
+               cpu_to_le64(0x000FFFF000ULL),
+               cpu_to_le64(0x000FFFFFFCULL),
+               cpu_to_le64(0x3FFFFFF000ULL),
+       },
+       {
+               cpu_to_le64(0x007FFF8000ULL),
+               cpu_to_le64(0x007FFFFFE0ULL),
+               cpu_to_le64(0xFFFFFF8000ULL),
+       },
+       {
+               cpu_to_le64(0x01FFFE0000ULL),
+               cpu_to_le64(0x01FFFFFF80ULL),
+               cpu_to_le64(0xFFFFFE0000ULL),
+       },
+       {
+               cpu_to_le64(0x0FFFF00000ULL),
+               cpu_to_le64(0x0FFFFFFC00ULL),
+               cpu_to_le64(0x0ULL),
+       },
+       {
+               cpu_to_le64(0x3FFFC00000ULL),
+               cpu_to_le64(0x3FFFFFF000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFFE000000ULL),
+               cpu_to_le64(0xFFFFFF8000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFF8000000ULL),
+               cpu_to_le64(0xFFFFFE0000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFE00000000ULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x0)
+       },
 };
 
+static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
+       cpu_to_le32(0x22002200),
+       cpu_to_le32(0x33113311),
+};
+
+static enum iwl_bt_coex_lut_type
+iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
+{
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       enum iwl_bt_coex_lut_type ret;
+       u16 phy_ctx_id;
+
+       /*
+        * Checking that we hold mvm->mutex is a good idea, but the rate
+        * control can't acquire the mutex since it runs in Tx path.
+        * So this is racy in that case, but in the worst case, the AMPDU
+        * size limit will be wrong for a short time which is not a big
+        * issue.
+        */
+
+       rcu_read_lock();
+
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+       if (!chanctx_conf ||
+            chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+               rcu_read_unlock();
+               return BT_COEX_LOOSE_LUT;
+       }
+
+       ret = BT_COEX_TX_DIS_LUT;
+
+       if (mvm->cfg->bt_shared_single_ant) {
+               rcu_read_unlock();
+               return ret;
+       }
+
+       phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+
+       if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id)
+               ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
+       else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id)
+               ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
+       /* else - default = TX TX disallowed */
+
+       rcu_read_unlock();
+
+       return ret;
+}
+
 int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
 {
        struct iwl_bt_coex_cmd *bt_cmd;
@@ -228,17 +360,10 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                .flags = CMD_SYNC,
        };
        int ret;
+       u32 flags;
 
-       /* go to CALIB state in internal BT-Coex state machine */
-       ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
-                             BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-       if (ret)
-               return ret;
-
-       ret  = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE,
-                              BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-       if (ret)
-               return ret;
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
+               return 0;
 
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
        if (!bt_cmd)
@@ -246,40 +371,52 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
        cmd.data[0] = bt_cmd;
 
        bt_cmd->max_kill = 5;
-       bt_cmd->bt3_time_t7_value = 1;
-       bt_cmd->bt3_prio_sample_time = 2;
-       bt_cmd->bt3_timer_t2_value = 0xc;
+       bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD,
+       bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling,
+       bt_cmd->bt4_tx_tx_delta_freq_thr = 15,
+       bt_cmd->bt4_tx_rx_max_freq0 = 15,
 
-       bt_cmd->flags = iwlwifi_mod_params.bt_coex_active ?
+       flags = iwlwifi_mod_params.bt_coex_active ?
                        BT_COEX_NW : BT_COEX_DISABLE;
-       bt_cmd->flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
+       flags |= BT_CH_PRIMARY_EN | BT_CH_SECONDARY_EN | BT_SYNC_2_BT_DISABLE;
+       bt_cmd->flags = cpu_to_le32(flags);
 
-       bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE |
+       bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
                                            BT_VALID_BT_PRIO_BOOST |
                                            BT_VALID_MAX_KILL |
                                            BT_VALID_3W_TMRS |
                                            BT_VALID_KILL_ACK |
                                            BT_VALID_KILL_CTS |
                                            BT_VALID_REDUCED_TX_POWER |
-                                           BT_VALID_LUT);
+                                           BT_VALID_LUT |
+                                           BT_VALID_WIFI_RX_SW_PRIO_BOOST |
+                                           BT_VALID_WIFI_TX_SW_PRIO_BOOST |
+                                           BT_VALID_MULTI_PRIO_LUT |
+                                           BT_VALID_CORUN_LUT_20 |
+                                           BT_VALID_CORUN_LUT_40 |
+                                           BT_VALID_ANT_ISOLATION |
+                                           BT_VALID_ANT_ISOLATION_THRS |
+                                           BT_VALID_TXTX_DELTA_FREQ_THRS |
+                                           BT_VALID_TXRX_MAX_FREQ_0);
 
        if (mvm->cfg->bt_shared_single_ant)
-               memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant_lookup,
-                      sizeof(iwl_single_shared_ant_lookup));
-       else if (is_loose_coex())
-               memcpy(&bt_cmd->decision_lut, iwl_loose_lookup,
-                      sizeof(iwl_tight_lookup));
+               memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
+                      sizeof(iwl_single_shared_ant));
        else
-               memcpy(&bt_cmd->decision_lut, iwl_tight_lookup,
-                      sizeof(iwl_tight_lookup));
+               memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
+                      sizeof(iwl_combined_lookup));
 
-       bt_cmd->bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
+       memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
+              sizeof(iwl_bt_prio_boost));
+       memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
+              sizeof(iwl_bt_mprio_lut));
        bt_cmd->kill_ack_msk =
                cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
        bt_cmd->kill_cts_msk =
                cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
 
        memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
+       memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
 
        ret = iwl_mvm_send_cmd(mvm, &cmd);
 
@@ -334,13 +471,17 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
        if (!bt_cmd)
                return -ENOMEM;
        cmd.data[0] = bt_cmd;
+       bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
 
        bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
        bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
-       bt_cmd->valid_bit_msk =
-               cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
+       bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+                                            BT_VALID_KILL_ACK |
+                                            BT_VALID_KILL_CTS);
 
-       IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk);
+       IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
+                      iwl_bt_ack_kill_msk[bt_kill_msk],
+                      iwl_bt_cts_kill_msk[bt_kill_msk]);
 
        ret = iwl_mvm_send_cmd(mvm, &cmd);
 
@@ -380,8 +521,10 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
        if (!bt_cmd)
                return -ENOMEM;
        cmd.data[0] = bt_cmd;
+       bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
 
-       bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
+       bt_cmd->valid_bit_msk =
+               cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
        bt_cmd->bt_reduced_tx_power = sta_id;
 
        if (enable)
@@ -403,8 +546,25 @@ struct iwl_bt_iterator_data {
        struct iwl_mvm *mvm;
        u32 num_bss_ifaces;
        bool reduced_tx_power;
+       struct ieee80211_chanctx_conf *primary;
+       struct ieee80211_chanctx_conf *secondary;
 };
 
+static inline
+void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif,
+                                      bool enable, int rssi)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->bf_data.last_bt_coex_event = rssi;
+       mvmvif->bf_data.bt_coex_max_thold =
+               enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0;
+       mvmvif->bf_data.bt_coex_min_thold =
+               enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0;
+}
+
+/* must be called under rcu_read_lock */
 static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                                      struct ieee80211_vif *vif)
 {
@@ -413,65 +573,94 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        struct iwl_mvm *mvm = data->mvm;
        struct ieee80211_chanctx_conf *chanctx_conf;
        enum ieee80211_smps_mode smps_mode;
-       enum ieee80211_band band;
        int ave_rssi;
 
        lockdep_assert_held(&mvm->mutex);
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return;
 
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(vif->chanctx_conf);
-       if (chanctx_conf && chanctx_conf->def.chan)
-               band = chanctx_conf->def.chan->band;
-       else
-               band = -1;
-       rcu_read_unlock();
+       if (vif->type != NL80211_IFTYPE_STATION &&
+           vif->type != NL80211_IFTYPE_AP)
+               return;
 
        smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
-       /* non associated BSSes aren't to be considered */
-       if (!vif->bss_conf.assoc)
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+       /* If channel context is invalid or not on 2.4GHz .. */
+       if ((!chanctx_conf ||
+            chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
+               /* ... and it is an associated STATION, relax constraints */
+               if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc)
+                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                           smps_mode);
+               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
+       }
+
+       /* SoftAP / GO will always be primary */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               if (!mvmvif->ap_ibss_active)
+                       return;
+
+               /* the Ack / Cts kill mask must be default if AP / GO */
+               data->reduced_tx_power = false;
 
-       if (band != IEEE80211_BAND_2GHZ) {
-               iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
-                                   smps_mode);
+               if (chanctx_conf == data->primary)
+                       return;
+
+               /* downgrade the current primary no matter what its type is */
+               data->secondary = data->primary;
+               data->primary = chanctx_conf;
                return;
        }
 
-       if (data->notif->bt_status)
-               smps_mode = IEEE80211_SMPS_DYNAMIC;
+       data->num_bss_ifaces++;
+
+       /* we are now a STA / P2P Client, and take associated ones only */
+       if (!vif->bss_conf.assoc)
+               return;
+
+       /* STA / P2P Client, try to be primary if first vif */
+       if (!data->primary || data->primary == chanctx_conf)
+               data->primary = chanctx_conf;
+       else if (!data->secondary)
+               /* if secondary is not NULL, it might be a GO */
+               data->secondary = chanctx_conf;
 
-       if (data->notif->bt_traffic_load >= IWL_BT_LOAD_FORCE_SISO_THRESHOLD)
+       if (le32_to_cpu(data->notif->bt_activity_grading) >= BT_HIGH_TRAFFIC)
                smps_mode = IEEE80211_SMPS_STATIC;
+       else if (le32_to_cpu(data->notif->bt_activity_grading) >=
+                BT_LOW_TRAFFIC)
+               smps_mode = IEEE80211_SMPS_DYNAMIC;
 
        IWL_DEBUG_COEX(data->mvm,
-                      "mac %d: bt_status %d traffic_load %d smps_req %d\n",
+                      "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
                       mvmvif->id,  data->notif->bt_status,
-                      data->notif->bt_traffic_load, smps_mode);
+                      data->notif->bt_activity_grading, smps_mode);
 
        iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
 
        /* don't reduce the Tx power if in loose scheme */
-       if (is_loose_coex())
+       if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
+           mvm->cfg->bt_shared_single_ant) {
+               data->reduced_tx_power = false;
+               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
+       }
 
-       data->num_bss_ifaces++;
-
-       /* reduced Txpower only if there are open BT connections, so ...*/
-       if (!BT_MBOX_MSG(data->notif, 3, OPEN_CON_2)) {
+       /* reduced Txpower only if BT is on, so ...*/
+       if (!data->notif->bt_status) {
                /* ... cancel reduced Tx power ... */
                if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
                        IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
                data->reduced_tx_power = false;
 
                /* ... and there is no need to get reports on RSSI any more. */
-               ieee80211_disable_rssi_reports(vif);
+               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
        }
 
-       ave_rssi = ieee80211_ave_rssi(vif);
+       /* try to get the avg rssi from fw */
+       ave_rssi = mvmvif->bf_data.ave_beacon_signal;
 
        /* if the RSSI isn't valid, fake it is very low */
        if (!ave_rssi)
@@ -499,8 +688,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        }
 
        /* Begin to monitor the RSSI: it may influence the reduced Tx power */
-       ieee80211_enable_rssi_reports(vif, BT_DISABLE_REDUCED_TXPOWER_THRESHOLD,
-                                     BT_ENABLE_REDUCED_TXPOWER_THRESHOLD);
+       iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
 }
 
 static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
@@ -510,11 +698,72 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
                .notif = &mvm->last_bt_notif,
                .reduced_tx_power = true,
        };
+       struct iwl_bt_coex_ci_cmd cmd = {};
+       u8 ci_bw_idx;
 
+       rcu_read_lock();
        ieee80211_iterate_active_interfaces_atomic(
                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
                                        iwl_mvm_bt_notif_iterator, &data);
 
+       if (data.primary) {
+               struct ieee80211_chanctx_conf *chan = data.primary;
+               if (WARN_ON(!chan->def.chan)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+                       ci_bw_idx = 0;
+                       cmd.co_run_bw_primary = 0;
+               } else {
+                       cmd.co_run_bw_primary = 1;
+                       if (chan->def.center_freq1 >
+                           chan->def.chan->center_freq)
+                               ci_bw_idx = 2;
+                       else
+                               ci_bw_idx = 1;
+               }
+
+               cmd.bt_primary_ci =
+                       iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+               cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+       }
+
+       if (data.secondary) {
+               struct ieee80211_chanctx_conf *chan = data.secondary;
+               if (WARN_ON(!data.secondary->def.chan)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+                       ci_bw_idx = 0;
+                       cmd.co_run_bw_secondary = 0;
+               } else {
+                       cmd.co_run_bw_secondary = 1;
+                       if (chan->def.center_freq1 >
+                           chan->def.chan->center_freq)
+                               ci_bw_idx = 2;
+                       else
+                               ci_bw_idx = 1;
+               }
+
+               cmd.bt_secondary_ci =
+                       iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+               cmd.secondary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+       }
+
+       rcu_read_unlock();
+
+       /* Don't spam the fw with the same command over and over */
+       if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) {
+               if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, CMD_SYNC,
+                                        sizeof(cmd), &cmd))
+                       IWL_ERR(mvm, "Failed to send BT_CI cmd");
+               memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
+       }
+
        /*
         * If there are no BSS / P2P client interfaces, reduced Tx Power is
         * irrelevant since it is based on the RSSI coming from the beacon.
@@ -536,12 +785,18 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
 
 
        IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
-       IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not ");
+       IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
+                      notif->bt_status ? "ON" : "OFF");
        IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
-       IWL_DEBUG_COEX(mvm, "\tBT traffic load %d\n", notif->bt_traffic_load);
+       IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
+       IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
+                      le32_to_cpu(notif->primary_ch_lut));
+       IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
+                      le32_to_cpu(notif->secondary_ch_lut));
+       IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
+                      le32_to_cpu(notif->bt_activity_grading));
        IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
                       notif->bt_agg_traffic_load);
-       IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
 
        /* remember this notification for future use: rssi fluctuations */
        memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
@@ -565,6 +820,18 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
        struct ieee80211_sta *sta;
        struct iwl_mvm_sta *mvmsta;
 
+       struct ieee80211_chanctx_conf *chanctx_conf;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       /* If channel context is invalid or not on 2.4GHz - don't count it */
+       if (!chanctx_conf ||
+           chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+               rcu_read_unlock();
+               return;
+       }
+       rcu_read_unlock();
+
        if (vif->type != NL80211_IFTYPE_STATION ||
            mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
                return;
@@ -594,15 +861,15 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        };
        int ret;
 
-       mutex_lock(&mvm->mutex);
+       lockdep_assert_held(&mvm->mutex);
 
        /* Rssi update while not associated ?! */
        if (WARN_ON_ONCE(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT))
-               goto out_unlock;
+               return;
 
-       /* No open connection - reports should be disabled */
-       if (!BT_MBOX_MSG(&mvm->last_bt_notif, 3, OPEN_CON_2))
-               goto out_unlock;
+       /* No BT - reports should be disabled */
+       if (!mvm->last_bt_notif.bt_status)
+               return;
 
        IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
                       rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
@@ -611,7 +878,8 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
         * Check if rssi is good enough for reduced Tx power, but not in loose
         * scheme.
         */
-       if (rssi_event == RSSI_EVENT_LOW || is_loose_coex())
+       if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
+           iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
                ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
                                                  false);
        else
@@ -633,12 +901,52 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF   (4000)
+#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT        (1200)
 
- out_unlock:
-       mutex_unlock(&mvm->mutex);
+u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm,
+                                  struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       enum iwl_bt_coex_lut_type lut_type;
+
+       if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+           BT_LOW_TRAFFIC)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+
+       if (lut_type == BT_COEX_LOOSE_LUT)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       /* tight coex, high bt traffic, reduce AGG time limit */
+       return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
+}
+
+bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
+                                    struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+
+       if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+           BT_HIGH_TRAFFIC)
+               return true;
+
+       /*
+        * In Tight, BT can't Rx while we Tx, so use both antennas since BT is
+        * already killed.
+        * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while we
+        * Tx.
+        */
+       return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
 }
 
-void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
 {
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
+               return;
+
        iwl_mvm_bt_coex_notif_handle(mvm);
 }
index 2bf29f7992ee4d953536bf1ec6ab0b2fbf1debae..4b6d670c35092d18679e8437ce5dbeb14ecf7702 100644 (file)
@@ -70,7 +70,9 @@
 #define IWL_MVM_UAPSD_RX_DATA_TIMEOUT          (50 * USEC_PER_MSEC)
 #define IWL_MVM_UAPSD_TX_DATA_TIMEOUT          (50 * USEC_PER_MSEC)
 #define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS       20
-#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS       20
+#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS       8
+#define IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS        30
+#define IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS        20
 #define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT       50
 #define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT       50
 #define IWL_MVM_PS_SNOOZE_INTERVAL             25
index 417639f77b01c8a9a166de79e4374cefa2313ba6..6f45966817bb4c1d34cd3a10c4db74536f4a13bf 100644 (file)
@@ -67,6 +67,7 @@
 #include <net/cfg80211.h>
 #include <net/ipv6.h>
 #include <net/tcp.h>
+#include <net/addrconf.h>
 #include "iwl-modparams.h"
 #include "fw-api.h"
 #include "mvm.h"
@@ -381,14 +382,74 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
        union {
                struct iwl_proto_offload_cmd_v1 v1;
                struct iwl_proto_offload_cmd_v2 v2;
+               struct iwl_proto_offload_cmd_v3_small v3s;
+               struct iwl_proto_offload_cmd_v3_large v3l;
        } cmd = {};
+       struct iwl_host_cmd hcmd = {
+               .id = PROT_OFFLOAD_CONFIG_CMD,
+               .flags = CMD_SYNC,
+               .data[0] = &cmd,
+               .dataflags[0] = IWL_HCMD_DFL_DUP,
+       };
        struct iwl_proto_offload_cmd_common *common;
        u32 enabled = 0, size;
+       u32 capa_flags = mvm->fw->ucode_capa.flags;
 #if IS_ENABLED(CONFIG_IPV6)
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int i;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+       if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
+           capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+               struct iwl_ns_config *nsc;
+               struct iwl_targ_addr *addrs;
+               int n_nsc, n_addrs;
+               int c;
+
+               if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+                       nsc = cmd.v3s.ns_config;
+                       n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
+                       addrs = cmd.v3s.targ_addrs;
+                       n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
+               } else {
+                       nsc = cmd.v3l.ns_config;
+                       n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
+                       addrs = cmd.v3l.targ_addrs;
+                       n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
+               }
+
+               if (mvmvif->num_target_ipv6_addrs)
+                       enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+
+               /*
+                * For each address we have (and that will fit) fill a target
+                * address struct and combine for NS offload structs with the
+                * solicited node addresses.
+                */
+               for (i = 0, c = 0;
+                    i < mvmvif->num_target_ipv6_addrs &&
+                    i < n_addrs && c < n_nsc; i++) {
+                       struct in6_addr solicited_addr;
+                       int j;
+
+                       addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
+                                                 &solicited_addr);
+                       for (j = 0; j < c; j++)
+                               if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
+                                                 &solicited_addr) == 0)
+                                       break;
+                       if (j == c)
+                               c++;
+                       addrs[i].addr = mvmvif->target_ipv6_addrs[i];
+                       addrs[i].config_num = cpu_to_le32(j);
+                       nsc[j].dest_ipv6_addr = solicited_addr;
+                       memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
+               }
+
+               if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
+                       cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i);
+               else
+                       cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i);
+       } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
                if (mvmvif->num_target_ipv6_addrs) {
                        enabled |= IWL_D3_PROTO_OFFLOAD_NS;
                        memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
@@ -419,7 +480,13 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
        }
 #endif
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+       if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+               common = &cmd.v3s.common;
+               size = sizeof(cmd.v3s);
+       } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+               common = &cmd.v3l.common;
+               size = sizeof(cmd.v3l);
+       } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
                common = &cmd.v2.common;
                size = sizeof(cmd.v2);
        } else {
@@ -438,8 +505,8 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
 
        common->enabled = cpu_to_le32(enabled);
 
-       return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC,
-                                   size, &cmd);
+       hcmd.len[0] = size;
+       return iwl_mvm_send_cmd(mvm, &hcmd);
 }
 
 enum iwl_mvm_tcp_packet_type {
@@ -793,6 +860,74 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        return 0;
 }
 
+static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_nonqos_seq_query_cmd query_cmd = {
+               .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET),
+               .mac_id_n_color =
+                       cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                       mvmvif->color)),
+       };
+       struct iwl_host_cmd cmd = {
+               .id = NON_QOS_TX_COUNTER_CMD,
+               .flags = CMD_SYNC | CMD_WANT_SKB,
+       };
+       int err;
+       u32 size;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) {
+               cmd.data[0] = &query_cmd;
+               cmd.len[0] = sizeof(query_cmd);
+       }
+
+       err = iwl_mvm_send_cmd(mvm, &cmd);
+       if (err)
+               return err;
+
+       size = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+       size -= sizeof(cmd.resp_pkt->hdr);
+       if (size < sizeof(__le16)) {
+               err = -EINVAL;
+       } else {
+               err = le16_to_cpup((__le16 *)cmd.resp_pkt->data);
+               /* new API returns next, not last-used seqno */
+               if (mvm->fw->ucode_capa.flags &
+                               IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)
+                       err -= 0x10;
+       }
+
+       iwl_free_resp(&cmd);
+       return err;
+}
+
+void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_nonqos_seq_query_cmd query_cmd = {
+               .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET),
+               .mac_id_n_color =
+                       cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                       mvmvif->color)),
+               .value = cpu_to_le16(mvmvif->seqno),
+       };
+
+       /* return if called during restart, not resume from D3 */
+       if (!mvmvif->seqno_valid)
+               return;
+
+       mvmvif->seqno_valid = false;
+
+       if (!(mvm->fw->ucode_capa.flags &
+                       IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API))
+               return;
+
+       if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, CMD_SYNC,
+                                sizeof(query_cmd), &query_cmd))
+               IWL_ERR(mvm, "failed to set non-QoS seqno\n");
+}
+
 static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                             struct cfg80211_wowlan *wowlan,
                             bool test)
@@ -829,7 +964,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        };
        int ret, i;
        int len __maybe_unused;
-       u16 seq;
        u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
 
        if (!wowlan) {
@@ -872,26 +1006,15 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
 
-       /*
-        * The D3 firmware still hardcodes the AP station ID for the
-        * BSS we're associated with as 0. Store the real STA ID here
-        * and assign 0. When we leave this function, we'll restore
-        * the original value for the resume code.
-        */
-       old_ap_sta_id = mvm_ap_sta->sta_id;
-       mvm_ap_sta->sta_id = 0;
-       mvmvif->ap_sta_id = 0;
-
        /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */
 
        wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported;
 
-       /*
-        * We know the last used seqno, and the uCode expects to know that
-        * one, it will increment before TX.
-        */
-       seq = mvm_ap_sta->last_seq_ctl & IEEE80211_SCTL_SEQ;
-       wowlan_config_cmd.non_qos_seq = cpu_to_le16(seq);
+       /* Query the last used seqno and set it */
+       ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
+       if (ret < 0)
+               goto out_noreset;
+       wowlan_config_cmd.non_qos_seq = cpu_to_le16(ret);
 
        /*
         * For QoS counters, we store the one to use next, so subtract 0x10
@@ -899,7 +1022,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
         * increment after using the value (i.e. store the next value to use).
         */
        for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
-               seq = mvm_ap_sta->tid_data[i].seq_number;
+               u16 seq = mvm_ap_sta->tid_data[i].seq_number;
                seq -= 0x10;
                wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq);
        }
@@ -944,6 +1067,16 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        iwl_trans_stop_device(mvm->trans);
 
+       /*
+        * The D3 firmware still hardcodes the AP station ID for the
+        * BSS we're associated with as 0. Store the real STA ID here
+        * and assign 0. When we leave this function, we'll restore
+        * the original value for the resume code.
+        */
+       old_ap_sta_id = mvm_ap_sta->sta_id;
+       mvm_ap_sta->sta_id = 0;
+       mvmvif->ap_sta_id = 0;
+
        /*
         * Set the HW restart bit -- this is mostly true as we're
         * going to load new firmware and reprogram that, though
@@ -1059,6 +1192,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        if (ret)
                goto out;
 
+       ret = iwl_mvm_power_update_device_mode(mvm);
+       if (ret)
+               goto out;
+
        ret = iwl_mvm_power_update_mode(mvm, vif);
        if (ret)
                goto out;
@@ -1109,16 +1246,26 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        return __iwl_mvm_suspend(hw, wowlan, false);
 }
 
+/* converted data from the different status responses */
+struct iwl_wowlan_status_data {
+       u16 pattern_number;
+       u16 qos_seq_ctr[8];
+       u32 wakeup_reasons;
+       u32 wake_packet_length;
+       u32 wake_packet_bufsize;
+       const u8 *wake_packet;
+};
+
 static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
                                          struct ieee80211_vif *vif,
-                                         struct iwl_wowlan_status *status)
+                                         struct iwl_wowlan_status_data *status)
 {
        struct sk_buff *pkt = NULL;
        struct cfg80211_wowlan_wakeup wakeup = {
                .pattern_idx = -1,
        };
        struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
-       u32 reasons = le32_to_cpu(status->wakeup_reasons);
+       u32 reasons = status->wakeup_reasons;
 
        if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) {
                wakeup_report = NULL;
@@ -1130,7 +1277,7 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
 
        if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN)
                wakeup.pattern_idx =
-                       le16_to_cpu(status->pattern_number);
+                       status->pattern_number;
 
        if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
                       IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH))
@@ -1158,8 +1305,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
                wakeup.tcp_match = true;
 
        if (status->wake_packet_bufsize) {
-               int pktsize = le32_to_cpu(status->wake_packet_bufsize);
-               int pktlen = le32_to_cpu(status->wake_packet_length);
+               int pktsize = status->wake_packet_bufsize;
+               int pktlen = status->wake_packet_length;
                const u8 *pktdata = status->wake_packet;
                struct ieee80211_hdr *hdr = (void *)pktdata;
                int truncated = pktlen - pktsize;
@@ -1239,8 +1386,229 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
        kfree_skb(pkt);
 }
 
+static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc,
+                                 struct ieee80211_key_seq *seq)
+{
+       u64 pn;
+
+       pn = le64_to_cpu(sc->pn);
+       seq->ccmp.pn[0] = pn >> 40;
+       seq->ccmp.pn[1] = pn >> 32;
+       seq->ccmp.pn[2] = pn >> 24;
+       seq->ccmp.pn[3] = pn >> 16;
+       seq->ccmp.pn[4] = pn >> 8;
+       seq->ccmp.pn[5] = pn;
+}
+
+static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc,
+                                  struct ieee80211_key_seq *seq)
+{
+       seq->tkip.iv32 = le32_to_cpu(sc->iv32);
+       seq->tkip.iv16 = le16_to_cpu(sc->iv16);
+}
+
+static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs,
+                                  struct ieee80211_key_conf *key)
+{
+       int tid;
+
+       BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
+
+       for (tid = 0; tid < IWL_NUM_RSC; tid++) {
+               struct ieee80211_key_seq seq = {};
+
+               iwl_mvm_aes_sc_to_seq(&scs[tid], &seq);
+               ieee80211_set_key_rx_seq(key, tid, &seq);
+       }
+}
+
+static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs,
+                                   struct ieee80211_key_conf *key)
+{
+       int tid;
+
+       BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
+
+       for (tid = 0; tid < IWL_NUM_RSC; tid++) {
+               struct ieee80211_key_seq seq = {};
+
+               iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq);
+               ieee80211_set_key_rx_seq(key, tid, &seq);
+       }
+}
+
+static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
+                                  struct iwl_wowlan_status_v6 *status)
+{
+       union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_CCMP:
+               iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key);
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key);
+               break;
+       default:
+               WARN_ON(1);
+       }
+}
+
+struct iwl_mvm_d3_gtk_iter_data {
+       struct iwl_wowlan_status_v6 *status;
+       void *last_gtk;
+       u32 cipher;
+       bool find_phase, unhandled_cipher;
+       int num_keys;
+};
+
+static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_sta *sta,
+                                  struct ieee80211_key_conf *key,
+                                  void *_data)
+{
+       struct iwl_mvm_d3_gtk_iter_data *data = _data;
+
+       if (data->unhandled_cipher)
+               return;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               /* ignore WEP completely, nothing to do */
+               return;
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_TKIP:
+               /* we support these */
+               break;
+       default:
+               /* everything else (even CMAC for MFP) - disconnect from AP */
+               data->unhandled_cipher = true;
+               return;
+       }
+
+       data->num_keys++;
+
+       /*
+        * pairwise key - update sequence counters only;
+        * note that this assumes no TDLS sessions are active
+        */
+       if (sta) {
+               struct ieee80211_key_seq seq = {};
+               union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc;
+
+               if (data->find_phase)
+                       return;
+
+               switch (key->cipher) {
+               case WLAN_CIPHER_SUITE_CCMP:
+                       iwl_mvm_aes_sc_to_seq(&sc->aes.tsc, &seq);
+                       iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key);
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq);
+                       iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key);
+                       break;
+               }
+               ieee80211_set_key_tx_seq(key, &seq);
+
+               /* that's it for this key */
+               return;
+       }
+
+       if (data->find_phase) {
+               data->last_gtk = key;
+               data->cipher = key->cipher;
+               return;
+       }
+
+       if (data->status->num_of_gtk_rekeys)
+               ieee80211_remove_key(key);
+       else if (data->last_gtk == key)
+               iwl_mvm_set_key_rx_seq(key, data->status);
+}
+
+static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct iwl_wowlan_status_v6 *status)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_d3_gtk_iter_data gtkdata = {
+               .status = status,
+       };
+
+       if (!status || !vif->bss_conf.bssid)
+               return false;
+
+       /* find last GTK that we used initially, if any */
+       gtkdata.find_phase = true;
+       ieee80211_iter_keys(mvm->hw, vif,
+                           iwl_mvm_d3_update_gtks, &gtkdata);
+       /* not trying to keep connections with MFP/unhandled ciphers */
+       if (gtkdata.unhandled_cipher)
+               return false;
+       if (!gtkdata.num_keys)
+               return true;
+       if (!gtkdata.last_gtk)
+               return false;
+
+       /*
+        * invalidate all other GTKs that might still exist and update
+        * the one that we used
+        */
+       gtkdata.find_phase = false;
+       ieee80211_iter_keys(mvm->hw, vif,
+                           iwl_mvm_d3_update_gtks, &gtkdata);
+
+       if (status->num_of_gtk_rekeys) {
+               struct ieee80211_key_conf *key;
+               struct {
+                       struct ieee80211_key_conf conf;
+                       u8 key[32];
+               } conf = {
+                       .conf.cipher = gtkdata.cipher,
+                       .conf.keyidx = status->gtk.key_index,
+               };
+
+               switch (gtkdata.cipher) {
+               case WLAN_CIPHER_SUITE_CCMP:
+                       conf.conf.keylen = WLAN_KEY_LEN_CCMP;
+                       memcpy(conf.conf.key, status->gtk.decrypt_key,
+                              WLAN_KEY_LEN_CCMP);
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       conf.conf.keylen = WLAN_KEY_LEN_TKIP;
+                       memcpy(conf.conf.key, status->gtk.decrypt_key, 16);
+                       /* leave TX MIC key zeroed, we don't use it anyway */
+                       memcpy(conf.conf.key +
+                              NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
+                              status->gtk.tkip_mic_key, 8);
+                       break;
+               }
+
+               key = ieee80211_gtk_rekey_add(vif, &conf.conf);
+               if (IS_ERR(key))
+                       return false;
+               iwl_mvm_set_key_rx_seq(key, status);
+       }
+
+       if (status->num_of_gtk_rekeys) {
+               __be64 replay_ctr =
+                       cpu_to_be64(le64_to_cpu(status->replay_ctr));
+               ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid,
+                                          (void *)&replay_ctr, GFP_KERNEL);
+       }
+
+       mvmvif->seqno_valid = true;
+       /* +0x10 because the set API expects next-to-use, not last-used */
+       mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10;
+
+       return true;
+}
+
 /* releases the MVM mutex */
-static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
+static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                                         struct ieee80211_vif *vif)
 {
        u32 base = mvm->error_event_table;
@@ -1253,8 +1621,12 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                .id = WOWLAN_GET_STATUSES,
                .flags = CMD_SYNC | CMD_WANT_SKB,
        };
-       struct iwl_wowlan_status *status;
-       int ret, len;
+       struct iwl_wowlan_status_data status;
+       struct iwl_wowlan_status_v6 *status_v6;
+       int ret, len, status_size, i;
+       bool keep;
+       struct ieee80211_sta *ap_sta;
+       struct iwl_mvm_sta *mvm_ap_sta;
 
        iwl_trans_read_mem_bytes(mvm->trans, base,
                                 &err_info, sizeof(err_info));
@@ -1287,32 +1659,83 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        if (!cmd.resp_pkt)
                goto out_unlock;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)
+               status_size = sizeof(struct iwl_wowlan_status_v6);
+       else
+               status_size = sizeof(struct iwl_wowlan_status_v4);
+
        len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
-       if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) {
+       if (len - sizeof(struct iwl_cmd_header) < status_size) {
                IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
                goto out_free_resp;
        }
 
-       status = (void *)cmd.resp_pkt->data;
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) {
+               status_v6 = (void *)cmd.resp_pkt->data;
+
+               status.pattern_number = le16_to_cpu(status_v6->pattern_number);
+               for (i = 0; i < 8; i++)
+                       status.qos_seq_ctr[i] =
+                               le16_to_cpu(status_v6->qos_seq_ctr[i]);
+               status.wakeup_reasons = le32_to_cpu(status_v6->wakeup_reasons);
+               status.wake_packet_length =
+                       le32_to_cpu(status_v6->wake_packet_length);
+               status.wake_packet_bufsize =
+                       le32_to_cpu(status_v6->wake_packet_bufsize);
+               status.wake_packet = status_v6->wake_packet;
+       } else {
+               struct iwl_wowlan_status_v4 *status_v4;
+               status_v6 = NULL;
+               status_v4 = (void *)cmd.resp_pkt->data;
+
+               status.pattern_number = le16_to_cpu(status_v4->pattern_number);
+               for (i = 0; i < 8; i++)
+                       status.qos_seq_ctr[i] =
+                               le16_to_cpu(status_v4->qos_seq_ctr[i]);
+               status.wakeup_reasons = le32_to_cpu(status_v4->wakeup_reasons);
+               status.wake_packet_length =
+                       le32_to_cpu(status_v4->wake_packet_length);
+               status.wake_packet_bufsize =
+                       le32_to_cpu(status_v4->wake_packet_bufsize);
+               status.wake_packet = status_v4->wake_packet;
+       }
 
        if (len - sizeof(struct iwl_cmd_header) !=
-           sizeof(*status) +
-           ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) {
+           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;
+
+       mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
+       for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+               u16 seq = status.qos_seq_ctr[i];
+               /* firmware stores last-used value, we store next value */
+               seq += 0x10;
+               mvm_ap_sta->tid_data[i].seq_number = seq;
+       }
+
        /* now we have all the data we need, unlock to avoid mac80211 issues */
        mutex_unlock(&mvm->mutex);
 
-       iwl_mvm_report_wakeup_reasons(mvm, vif, status);
+       iwl_mvm_report_wakeup_reasons(mvm, vif, &status);
+
+       keep = iwl_mvm_setup_connection_keep(mvm, vif, status_v6);
+
        iwl_free_resp(&cmd);
-       return;
+       return keep;
 
  out_free_resp:
        iwl_free_resp(&cmd);
  out_unlock:
        mutex_unlock(&mvm->mutex);
+       return false;
 }
 
 static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
@@ -1335,6 +1758,17 @@ static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
 #endif
 }
 
+static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       /* skip the one we keep connection on */
+       if (data == vif)
+               return;
+
+       if (vif->type == NL80211_IFTYPE_STATION)
+               ieee80211_resume_disconnect(vif);
+}
+
 static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
 {
        struct iwl_d3_iter_data resume_iter_data = {
@@ -1343,6 +1777,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
        struct ieee80211_vif *vif = NULL;
        int ret;
        enum iwl_d3_status d3_status;
+       bool keep = false;
 
        mutex_lock(&mvm->mutex);
 
@@ -1368,7 +1803,7 @@ 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);
 
-       iwl_mvm_query_wakeup_reasons(mvm, vif);
+       keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
        /* has unlocked the mutex, so skip that */
        goto out;
 
@@ -1376,8 +1811,10 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
        mutex_unlock(&mvm->mutex);
 
  out:
-       if (!test && vif)
-               ieee80211_resume_disconnect(vif);
+       if (!test)
+               ieee80211_iterate_active_interfaces_rtnl(mvm->hw,
+                       IEEE80211_IFACE_ITER_NORMAL,
+                       iwl_mvm_d3_disconnect_iter, keep ? vif : NULL);
 
        /* return 1 to reconfigure the device */
        set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
index aac81b8984b05ec87f50fa09b32f7ab9a3dc36ed..0675f0c8ef9388a7d888625077860207aba0a826 100644 (file)
@@ -246,58 +246,56 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
-static ssize_t iwl_dbgfs_power_down_allow_write(struct file *file,
-                                               const char __user *user_buf,
+static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file,
+                                               char __user *user_buf,
                                                size_t count, loff_t *ppos)
 {
        struct iwl_mvm *mvm = file->private_data;
-       char buf[8] = {};
-       int allow;
-
-       if (!mvm->ucode_loaded)
-               return -EIO;
-
-       if (copy_from_user(buf, user_buf, sizeof(buf)))
-               return -EFAULT;
-
-       if (sscanf(buf, "%d", &allow) != 1)
-               return -EINVAL;
-
-       IWL_DEBUG_POWER(mvm, "%s device power down\n",
-                       allow ? "allow" : "prevent");
+       char buf[64];
+       int bufsz = sizeof(buf);
+       int pos = 0;
 
-       /*
-        * TODO: Send REPLY_DEBUG_CMD (0xf0) when FW support it
-        */
+       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n",
+                        mvm->disable_power_off);
+       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n",
+                        mvm->disable_power_off_d3);
 
-       return count;
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
-static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
-                                                  const char __user *user_buf,
-                                                  size_t count, loff_t *ppos)
+static ssize_t iwl_dbgfs_disable_power_off_write(struct file *file,
+                                                const char __user *user_buf,
+                                                size_t count, loff_t *ppos)
 {
        struct iwl_mvm *mvm = file->private_data;
-       char buf[8] = {};
-       int allow;
+       char buf[64] = {};
+       int ret;
+       int val;
 
-       if (copy_from_user(buf, user_buf, sizeof(buf)))
+       if (!mvm->ucode_loaded)
+               return -EIO;
+
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
-       if (sscanf(buf, "%d", &allow) != 1)
+       if (!strncmp("disable_power_off_d0=", buf, 21)) {
+               if (sscanf(buf + 21, "%d", &val) != 1)
+                       return -EINVAL;
+               mvm->disable_power_off = val;
+       } else if (!strncmp("disable_power_off_d3=", buf, 21)) {
+               if (sscanf(buf + 21, "%d", &val) != 1)
+                       return -EINVAL;
+               mvm->disable_power_off_d3 = val;
+       } else {
                return -EINVAL;
+       }
 
-       IWL_DEBUG_POWER(mvm, "%s device power down in d3\n",
-                       allow ? "allow" : "prevent");
-
-       /*
-        * TODO: When WoWLAN FW alive notification happens, driver will send
-        * REPLY_DEBUG_CMD setting power_down_allow flag according to
-        * mvm->prevent_power_down_d3
-        */
-       mvm->prevent_power_down_d3 = !allow;
+       mutex_lock(&mvm->mutex);
+       ret = iwl_mvm_power_update_device_mode(mvm);
+       mutex_unlock(&mvm->mutex);
 
-       return count;
+       return ret ?: count;
 }
 
 static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
@@ -371,7 +369,8 @@ static ssize_t iwl_dbgfs_pm_params_write(struct file *file,
        int val;
        int ret;
 
-       if (copy_from_user(buf, user_buf, sizeof(buf)))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
        if (!strncmp("keep_alive=", buf, 11)) {
@@ -394,7 +393,9 @@ static ssize_t iwl_dbgfs_pm_params_write(struct file *file,
                if (sscanf(buf + 16, "%d", &val) != 1)
                        return -EINVAL;
                param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
-       } else if (!strncmp("disable_power_off=", buf, 18)) {
+       } else if (!strncmp("disable_power_off=", buf, 18) &&
+                  !(mvm->fw->ucode_capa.flags &
+                    IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) {
                if (sscanf(buf + 18, "%d", &val) != 1)
                        return -EINVAL;
                param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF;
@@ -581,15 +582,21 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
        BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
 
        pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n",
-                                        notif->bt_status);
+                        notif->bt_status);
        pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n",
-                                        notif->bt_open_conn);
+                        notif->bt_open_conn);
        pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n",
-                                        notif->bt_traffic_load);
+                        notif->bt_traffic_load);
        pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n",
-                                        notif->bt_agg_traffic_load);
+                        notif->bt_agg_traffic_load);
        pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
-                                        notif->bt_ci_compliance);
+                        notif->bt_ci_compliance);
+       pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+                        le32_to_cpu(notif->primary_ch_lut));
+       pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+                        le32_to_cpu(notif->secondary_ch_lut));
+       pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n",
+                        le32_to_cpu(notif->bt_activity_grading));
 
        mutex_unlock(&mvm->mutex);
 
@@ -600,6 +607,38 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
 }
 #undef BT_MBOX_PRINT
 
+static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
+       char buf[256];
+       int bufsz = sizeof(buf);
+       int pos = 0;
+
+       mutex_lock(&mvm->mutex);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n");
+       pos += scnprintf(buf+pos, bufsz-pos,
+                      "\tPrimary Channel Bitmap 0x%016llx Fat: %d\n",
+                      le64_to_cpu(cmd->bt_primary_ci),
+                      !!cmd->co_run_bw_primary);
+       pos += scnprintf(buf+pos, bufsz-pos,
+                      "\tSecondary Channel Bitmap 0x%016llx Fat: %d\n",
+                      le64_to_cpu(cmd->bt_secondary_ci),
+                      !!cmd->co_run_bw_secondary);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
+       pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
+                        iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
+       pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
+                        iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
+
+       mutex_unlock(&mvm->mutex);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
 #define PRINT_STATS_LE32(_str, _val)                                   \
                         pos += scnprintf(buf + pos, bufsz - pos,       \
                                          fmt_table, _str,              \
@@ -615,9 +654,11 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
        int pos = 0;
        char *buf;
        int ret;
-       int bufsz = sizeof(struct mvm_statistics_rx_phy) * 20 +
-                   sizeof(struct mvm_statistics_rx_non_phy) * 10 +
-                   sizeof(struct mvm_statistics_rx_ht_phy) * 10 + 200;
+       /* 43 is the size of each data line, 33 is the size of each header */
+       size_t bufsz =
+               ((sizeof(struct mvm_statistics_rx) / sizeof(__le32)) * 43) +
+               (4 * 33) + 1;
+
        struct mvm_statistics_rx_phy *ofdm;
        struct mvm_statistics_rx_phy *cck;
        struct mvm_statistics_rx_non_phy *general;
@@ -712,6 +753,7 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
        PRINT_STATS_LE32("beacon_energy_b", general->beacon_energy_b);
        PRINT_STATS_LE32("beacon_energy_c", general->beacon_energy_c);
        PRINT_STATS_LE32("num_bt_kills", general->num_bt_kills);
+       PRINT_STATS_LE32("mac_id", general->mac_id);
        PRINT_STATS_LE32("directed_data_mpdu", general->directed_data_mpdu);
 
        pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
@@ -757,6 +799,59 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
        return count;
 }
 
+static ssize_t
+iwl_dbgfs_scan_ant_rxchain_read(struct file *file,
+                               char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       int pos = 0;
+       char buf[32];
+       const size_t bufsz = sizeof(buf);
+
+       /* print which antennas were set for the scan command by the user */
+       pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: ");
+       if (mvm->scan_rx_ant & ANT_A)
+               pos += scnprintf(buf + pos, bufsz - pos, "A");
+       if (mvm->scan_rx_ant & ANT_B)
+               pos += scnprintf(buf + pos, bufsz - pos, "B");
+       if (mvm->scan_rx_ant & ANT_C)
+               pos += scnprintf(buf + pos, bufsz - pos, "C");
+       pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t
+iwl_dbgfs_scan_ant_rxchain_write(struct file *file,
+                                const char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[8];
+       int buf_size;
+       u8 scan_rx_ant;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) - 1);
+
+       /* get the argument from the user and check if it is valid */
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       if (sscanf(buf, "%hhx", &scan_rx_ant) != 1)
+               return -EINVAL;
+       if (scan_rx_ant > ANT_ABC)
+               return -EINVAL;
+       if (scan_rx_ant & ~iwl_fw_valid_rx_ant(mvm->fw))
+               return -EINVAL;
+
+       /* change the rx antennas for scan command */
+       mvm->scan_rx_ant = scan_rx_ant;
+
+       return count;
+}
+
+
 static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
                                enum iwl_dbgfs_bf_mask param, int value)
 {
@@ -968,7 +1063,8 @@ static ssize_t iwl_dbgfs_d3_sram_write(struct file *file,
        char buf[8] = {};
        int store;
 
-       if (copy_from_user(buf, user_buf, sizeof(buf)))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
        if (sscanf(buf, "%d", &store) != 1)
@@ -1063,10 +1159,12 @@ MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram);
 MVM_DEBUGFS_READ_FILE_OPS(stations);
 MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
-MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
-MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
+MVM_DEBUGFS_READ_FILE_OPS(bt_cmd);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off);
 MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain);
+
 #ifdef CONFIG_PM_SLEEP
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram);
 #endif
@@ -1087,10 +1185,14 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
-       MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
-       MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)
+               MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir,
+                                    S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
+                            S_IWUSR | S_IRUSR);
 #ifdef CONFIG_PM_SLEEP
        MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);
index 05c61d6f384eeaa5f75ff2d461692cbec3b8a6fa..4ea5e24ca92d8da9a0a366a3ab0e0bf903f55ef1 100644 (file)
@@ -82,6 +82,8 @@
  * @BT_USE_DEFAULTS:
  * @BT_SYNC_2_BT_DISABLE:
  * @BT_COEX_CORUNNING_TBL_EN:
+ *
+ * The COEX_MODE must be set for each command. Even if it is not changed.
  */
 enum iwl_bt_coex_flags {
        BT_CH_PRIMARY_EN                = BIT(0),
@@ -95,14 +97,16 @@ enum iwl_bt_coex_flags {
        BT_COEX_NW                      = 0x3 << BT_COEX_MODE_POS,
        BT_USE_DEFAULTS                 = BIT(6),
        BT_SYNC_2_BT_DISABLE            = BIT(7),
-       /*
-        * For future use - when the flags will be enlarged
-        * BT_COEX_CORUNNING_TBL_EN     = BIT(8),
-        */
+       BT_COEX_CORUNNING_TBL_EN        = BIT(8),
+       BT_COEX_MPLUT_TBL_EN            = BIT(9),
+       /* Bit 10 is reserved */
+       BT_COEX_WF_PRIO_BOOST_CHECK_EN  = BIT(11),
 };
 
 /*
  * indicates what has changed in the BT_COEX command.
+ * BT_VALID_ENABLE must be set for each command. Commands without this bit will
+ * discarded by the firmware
  */
 enum iwl_bt_coex_valid_bit_msk {
        BT_VALID_ENABLE                 = BIT(0),
@@ -121,11 +125,8 @@ enum iwl_bt_coex_valid_bit_msk {
        BT_VALID_CORUN_LUT_40           = BIT(13),
        BT_VALID_ANT_ISOLATION          = BIT(14),
        BT_VALID_ANT_ISOLATION_THRS     = BIT(15),
-       /*
-        * For future use - when the valid flags will be enlarged
-        * BT_VALID_TXTX_DELTA_FREQ_THRS        = BIT(16),
-        * BT_VALID_TXRX_MAX_FREQ_0     = BIT(17),
-        */
+       BT_VALID_TXTX_DELTA_FREQ_THRS   = BIT(16),
+       BT_VALID_TXRX_MAX_FREQ_0        = BIT(17),
 };
 
 /**
@@ -142,48 +143,88 @@ enum iwl_bt_reduced_tx_power {
        BT_REDUCED_TX_POWER_DATA        = BIT(1),
 };
 
+enum iwl_bt_coex_lut_type {
+       BT_COEX_TIGHT_LUT = 0,
+       BT_COEX_LOOSE_LUT,
+       BT_COEX_TX_DIS_LUT,
+
+       BT_COEX_MAX_LUT,
+};
+
 #define BT_COEX_LUT_SIZE (12)
+#define BT_COEX_CORUN_LUT_SIZE (32)
+#define BT_COEX_MULTI_PRIO_LUT_SIZE (2)
+#define BT_COEX_BOOST_SIZE (4)
+#define BT_REDUCED_TX_POWER_BIT BIT(7)
 
 /**
  * struct iwl_bt_coex_cmd - bt coex configuration command
  * @flags:&enum iwl_bt_coex_flags
- * @lead_time:
  * @max_kill:
- * @bt3_time_t7_value:
- * @kill_ack_msk:
- * @kill_cts_msk:
- * @bt3_prio_sample_time:
- * @bt3_timer_t2_value:
- * @bt4_reaction_time:
- * @decision_lut[12]:
  * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
- * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
- * @bt_prio_boost: values for PTA boost register
+ * @bt4_antenna_isolation:
+ * @bt4_antenna_isolation_thr:
+ * @bt4_tx_tx_delta_freq_thr:
+ * @bt4_tx_rx_max_freq0:
+ * @bt_prio_boost:
  * @wifi_tx_prio_boost: SW boost of wifi tx priority
  * @wifi_rx_prio_boost: SW boost of wifi rx priority
+ * @kill_ack_msk:
+ * @kill_cts_msk:
+ * @decision_lut:
+ * @bt4_multiprio_lut:
+ * @bt4_corun_lut20:
+ * @bt4_corun_lut40:
+ * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
  *
  * The structure is used for the BT_COEX command.
  */
 struct iwl_bt_coex_cmd {
-       u8 flags;
-       u8 lead_time;
+       __le32 flags;
        u8 max_kill;
-       u8 bt3_time_t7_value;
+       u8 bt_reduced_tx_power;
+       u8 reserved[2];
+
+       u8 bt4_antenna_isolation;
+       u8 bt4_antenna_isolation_thr;
+       u8 bt4_tx_tx_delta_freq_thr;
+       u8 bt4_tx_rx_max_freq0;
+
+       __le32 bt_prio_boost[BT_COEX_BOOST_SIZE];
+       __le32 wifi_tx_prio_boost;
+       __le32 wifi_rx_prio_boost;
        __le32 kill_ack_msk;
        __le32 kill_cts_msk;
-       u8 bt3_prio_sample_time;
-       u8 bt3_timer_t2_value;
-       __le16 bt4_reaction_time;
-       __le32 decision_lut[BT_COEX_LUT_SIZE];
-       u8 bt_reduced_tx_power;
-       u8 reserved;
-       __le16 valid_bit_msk;
-       __le32 bt_prio_boost;
-       u8 reserved2;
-       u8 wifi_tx_prio_boost;
-       __le16 wifi_rx_prio_boost;
+
+       __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
+       __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
+       __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE];
+       __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE];
+
+       __le32 valid_bit_msk;
 } __packed; /* BT_COEX_CMD_API_S_VER_3 */
 
+/**
+ * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
+ * @bt_primary_ci:
+ * @bt_secondary_ci:
+ * @co_run_bw_primary:
+ * @co_run_bw_secondary:
+ * @primary_ch_phy_id:
+ * @secondary_ch_phy_id:
+ *
+ * Used for BT_COEX_CI command
+ */
+struct iwl_bt_coex_ci_cmd {
+       __le64 bt_primary_ci;
+       __le64 bt_secondary_ci;
+
+       u8 co_run_bw_primary;
+       u8 co_run_bw_secondary;
+       u8 primary_ch_phy_id;
+       u8 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_1 */
+
 #define BT_MBOX(n_dw, _msg, _pos, _nbits)      \
        BT_MBOX##n_dw##_##_msg##_POS = (_pos),  \
        BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS
@@ -244,23 +285,39 @@ enum iwl_bt_mxbox_dw3 {
        ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
        >> BT_MBOX##_num##_##_field##_POS)
 
+enum iwl_bt_activity_grading {
+       BT_OFF                  = 0,
+       BT_ON_NO_CONNECTION     = 1,
+       BT_LOW_TRAFFIC          = 2,
+       BT_HIGH_TRAFFIC         = 3,
+};
+
 /**
  * struct iwl_bt_coex_profile_notif - notification about BT coex
  * @mbox_msg: message from BT to WiFi
- * @:bt_status: 0 - off, 1 - on
- * @:bt_open_conn: number of BT connections open
- * @:bt_traffic_load: load of BT traffic
- * @:bt_agg_traffic_load: aggregated load of BT traffic
- * @:bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ * @msg_idx: the index of the message
+ * @bt_status: 0 - off, 1 - on
+ * @bt_open_conn: number of BT connections open
+ * @bt_traffic_load: load of BT traffic
+ * @bt_agg_traffic_load: aggregated load of BT traffic
+ * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ * @primary_ch_lut: LUT used for primary channel
+ * @secondary_ch_lut: LUT used for secondary channel
+ * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
  */
 struct iwl_bt_coex_profile_notif {
        __le32 mbox_msg[4];
+       __le32 msg_idx;
        u8 bt_status;
        u8 bt_open_conn;
        u8 bt_traffic_load;
        u8 bt_agg_traffic_load;
        u8 bt_ci_compliance;
        u8 reserved[3];
+
+       __le32 primary_ch_lut;
+       __le32 secondary_ch_lut;
+       __le32 bt_activity_grading;
 } __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */
 
 enum iwl_bt_coex_prio_table_event {
@@ -300,20 +357,4 @@ struct iwl_bt_coex_prio_tbl_cmd {
        u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
 } __packed;
 
-enum iwl_bt_coex_env_action {
-       BT_COEX_ENV_CLOSE        = 0,
-       BT_COEX_ENV_OPEN         = 1,
-}; /* BT_COEX_PROT_ENV_ACTION_API_E_VER_1 */
-
-/**
- * struct iwl_bt_coex_prot_env_cmd - BT Protection Envelope
- * @action: enum %iwl_bt_coex_env_action
- * @type: enum %iwl_bt_coex_prio_table_event
- */
-struct iwl_bt_coex_prot_env_cmd {
-       u8 action; /* 0 = closed, 1 = open */
-       u8 type; /* 0 .. 15 */
-       u8 reserved[2];
-} __packed;
-
 #endif /* __fw_api_bt_coex_h__ */
index df72fcdf81705b54e9a36f51ede8a652f8bcfa2d..4e7dd8cf87dce738cbe062d931dcabe49ce67a51 100644 (file)
@@ -100,7 +100,12 @@ enum iwl_proto_offloads {
 
 #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1    2
 #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2    6
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX   6
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L   12
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S   4
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX   12
+
+#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L    4
+#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S    2
 
 /**
  * struct iwl_proto_offload_cmd_common - ARP/NS offload common part
@@ -155,6 +160,43 @@ struct iwl_proto_offload_cmd_v2 {
        u8 reserved2[3];
 } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */
 
+struct iwl_ns_config {
+       struct in6_addr source_ipv6_addr;
+       struct in6_addr dest_ipv6_addr;
+       u8 target_mac_addr[ETH_ALEN];
+       __le16 reserved;
+} __packed; /* NS_OFFLOAD_CONFIG */
+
+struct iwl_targ_addr {
+       struct in6_addr addr;
+       __le32 config_num;
+} __packed; /* TARGET_IPV6_ADDRESS */
+
+/**
+ * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @target_ipv6_addr: target IPv6 addresses
+ * @ns_config: NS offload configurations
+ */
+struct iwl_proto_offload_cmd_v3_small {
+       struct iwl_proto_offload_cmd_common common;
+       __le32 num_valid_ipv6_addrs;
+       struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S];
+       struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
+
+/**
+ * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @target_ipv6_addr: target IPv6 addresses
+ * @ns_config: NS offload configurations
+ */
+struct iwl_proto_offload_cmd_v3_large {
+       struct iwl_proto_offload_cmd_common common;
+       __le32 num_valid_ipv6_addrs;
+       struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L];
+       struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
 
 /*
  * WOWLAN_PATTERNS
@@ -293,7 +335,7 @@ enum iwl_wowlan_wakeup_reason {
        IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET             = BIT(12),
 }; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
 
-struct iwl_wowlan_status {
+struct iwl_wowlan_status_v4 {
        __le64 replay_ctr;
        __le16 pattern_number;
        __le16 non_qos_seq_ctr;
@@ -308,6 +350,29 @@ struct iwl_wowlan_status {
        u8 wake_packet[]; /* can be truncated from _length to _bufsize */
 } __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
 
+struct iwl_wowlan_gtk_status {
+       u8 key_index;
+       u8 reserved[3];
+       u8 decrypt_key[16];
+       u8 tkip_mic_key[8];
+       struct iwl_wowlan_rsc_tsc_params_cmd rsc;
+} __packed;
+
+struct iwl_wowlan_status_v6 {
+       struct iwl_wowlan_gtk_status gtk;
+       __le64 replay_ctr;
+       __le16 pattern_number;
+       __le16 non_qos_seq_ctr;
+       __le16 qos_seq_ctr[8];
+       __le32 wakeup_reasons;
+       __le32 num_of_gtk_rekeys;
+       __le32 transmitted_ndps;
+       __le32 received_beacons;
+       __le32 wake_packet_length;
+       __le32 wake_packet_bufsize;
+       u8 wake_packet[]; /* can be truncated from _length to _bufsize */
+} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */
+
 #define IWL_WOWLAN_TCP_MAX_PACKET_LEN          64
 #define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN  128
 #define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS      2048
index 98b1feb43d388f70bc4aadf0f4434cc714744d05..39c3148bdfa8eff2a09a7641b317f8a8a1648da9 100644 (file)
@@ -170,12 +170,14 @@ struct iwl_mac_data_ap {
  * @beacon_tsf: beacon transmit time in TSF
  * @bi: beacon interval in TU
  * @bi_reciprocal: 2^32 / bi
+ * @beacon_template: beacon template ID
  */
 struct iwl_mac_data_ibss {
        __le32 beacon_time;
        __le64 beacon_tsf;
        __le32 bi;
        __le32 bi_reciprocal;
+       __le32 beacon_template;
 } __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
 
 /**
@@ -372,4 +374,13 @@ static inline u32 iwl_mvm_reciprocal(u32 v)
        return 0xFFFFFFFF / v;
 }
 
+#define IWL_NONQOS_SEQ_GET     0x1
+#define IWL_NONQOS_SEQ_SET     0x2
+struct iwl_nonqos_seq_query_cmd {
+       __le32 get_set_flag;
+       __le32 mac_id_n_color;
+       __le16 value;
+       __le16 reserved;
+} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */
+
 #endif /* __fw_api_mac_h__ */
index 8e7ab41079ca6ca51eba975a2e39c92cee5f002b..5cb93ae5cd2f73bfff7a1daf5d381f6e9ccc987b 100644 (file)
@@ -131,6 +131,33 @@ struct iwl_powertable_cmd {
        __le32 lprx_rssi_threshold;
 } __packed;
 
+/**
+ * enum iwl_device_power_flags - masks for device power command flags
+ * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
+ *     receiver and transmitter. '0' - does not allow. This flag should be
+ *     always set to '1' unless one need to disable actual power down for debug
+ *     purposes.
+ * @DEVICE_POWER_FLAGS_CAM_MSK: '1' CAM (Continuous Active Mode) is set, meaning
+ *     that power management is disabled. '0' Power management is enabled, one
+ *     of power schemes is applied.
+*/
+enum iwl_device_power_flags {
+       DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK   = BIT(0),
+       DEVICE_POWER_FLAGS_CAM_MSK              = BIT(13),
+};
+
+/**
+ * struct iwl_device_power_cmd - device wide power command.
+ * DEVICE_POWER_CMD = 0x77 (command, has simple generic response)
+ *
+ * @flags:     Power table command flags from DEVICE_POWER_FLAGS_*
+ */
+struct iwl_device_power_cmd {
+       /* PM_POWER_TABLE_CMD_API_S_VER_6 */
+       __le16 flags;
+       __le16 reserved;
+} __packed;
+
 /**
  * struct iwl_mac_power_cmd - New power command containing uAPSD support
  * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
@@ -290,7 +317,7 @@ struct iwl_beacon_filter_cmd {
 #define IWL_BF_ESCAPE_TIMER_MIN 0
 
 #define IWL_BA_ESCAPE_TIMER_DEFAULT 6
-#define IWL_BA_ESCAPE_TIMER_D3 6
+#define IWL_BA_ESCAPE_TIMER_D3 9
 #define IWL_BA_ESCAPE_TIMER_MAX 1024
 #define IWL_BA_ESCAPE_TIMER_MIN 0
 
index fdd33bc0a594a2d5456044b1a10df5d621ed8f8e..538f1c7a59664f9cb7c586eeb0974974c971b6f8 100644 (file)
@@ -68,6 +68,7 @@
 /*
  * These serve as indexes into
  * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT];
+ * TODO: avoid overlap between legacy and HT rates
  */
 enum {
        IWL_RATE_1M_INDEX = 0,
@@ -78,18 +79,31 @@ enum {
        IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
        IWL_RATE_6M_INDEX,
        IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
+       IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX,
+       IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX,
+       IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX,
        IWL_RATE_9M_INDEX,
        IWL_RATE_12M_INDEX,
+       IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX,
        IWL_RATE_18M_INDEX,
+       IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX,
        IWL_RATE_24M_INDEX,
+       IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX,
        IWL_RATE_36M_INDEX,
+       IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX,
        IWL_RATE_48M_INDEX,
+       IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX,
        IWL_RATE_54M_INDEX,
+       IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX,
        IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX,
        IWL_RATE_60M_INDEX,
-       IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX,
+       IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX,
+       IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX,
+       IWL_RATE_MCS_8_INDEX,
+       IWL_RATE_MCS_9_INDEX,
+       IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX,
        IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1,
-       IWL_RATE_COUNT,
+       IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1,
 };
 
 #define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX)
@@ -108,6 +122,7 @@ enum {
        IWL_RATE_2M_PLCP  = 20,
        IWL_RATE_5M_PLCP  = 55,
        IWL_RATE_11M_PLCP = 110,
+       IWL_RATE_INVM_PLCP = -1,
 };
 
 /*
@@ -164,6 +179,8 @@ enum {
  * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.)
  */
 #define RATE_HT_MCS_RATE_CODE_MSK      0x7
+#define RATE_HT_MCS_NSS_POS             3
+#define RATE_HT_MCS_NSS_MSK             (3 << RATE_HT_MCS_NSS_POS)
 
 /* Bit 10: (1) Use Green Field preamble */
 #define RATE_HT_MCS_GF_POS             10
index 83cb9b992ea4622f39ff03a4c5bbcea36a7d128f..c3782b48ded14d8a52458f66677dc3d3cd766fd7 100644 (file)
@@ -356,6 +356,7 @@ struct iwl_scan_complete_notif {
 /* scan offload */
 #define IWL_MAX_SCAN_CHANNELS          40
 #define IWL_SCAN_MAX_BLACKLIST_LEN     64
+#define IWL_SCAN_SHORT_BLACKLIST_LEN   16
 #define IWL_SCAN_MAX_PROFILES          11
 #define SCAN_OFFLOAD_PROBE_REQ_SIZE    512
 
@@ -368,6 +369,12 @@ struct iwl_scan_complete_notif {
 #define IWL_FULL_SCAN_MULTIPLIER 5
 #define IWL_FAST_SCHED_SCAN_ITERATIONS 3
 
+enum scan_framework_client {
+       SCAN_CLIENT_SCHED_SCAN          = BIT(0),
+       SCAN_CLIENT_NETDETECT           = BIT(1),
+       SCAN_CLIENT_ASSET_TRACKING      = BIT(2),
+};
+
 /**
  * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6
  * @scan_flags:                see enum iwl_scan_flags
@@ -449,11 +456,12 @@ struct iwl_scan_offload_cfg {
  * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S
  * @ssid:              MAC address to filter out
  * @reported_rssi:     AP rssi reported to the host
+ * @client_bitmap: clients ignore this entry  - enum scan_framework_client
  */
 struct iwl_scan_offload_blacklist {
        u8 ssid[ETH_ALEN];
        u8 reported_rssi;
-       u8 reserved;
+       u8 client_bitmap;
 } __packed;
 
 enum iwl_scan_offload_network_type {
@@ -475,6 +483,7 @@ enum iwl_scan_offload_band_selection {
  * @aut_alg:           authentication olgorithm to match - bitmap
  * @network_type:      enum iwl_scan_offload_network_type
  * @band_selection:    enum iwl_scan_offload_band_selection
+ * @client_bitmap:     clients waiting for match - enum scan_framework_client
  */
 struct iwl_scan_offload_profile {
        u8 ssid_index;
@@ -482,7 +491,8 @@ struct iwl_scan_offload_profile {
        u8 auth_alg;
        u8 network_type;
        u8 band_selection;
-       u8 reserved[3];
+       u8 client_bitmap;
+       u8 reserved[2];
 } __packed;
 
 /**
@@ -491,13 +501,18 @@ struct iwl_scan_offload_profile {
  * @profiles:          profiles to search for match
  * @blacklist_len:     length of blacklist
  * @num_profiles:      num of profiles in the list
+ * @match_notify:      clients waiting for match found notification
+ * @pass_match:                clients waiting for the results
+ * @active_clients:    active clients bitmap - enum scan_framework_client
  */
 struct iwl_scan_offload_profile_cfg {
-       struct iwl_scan_offload_blacklist blacklist[IWL_SCAN_MAX_BLACKLIST_LEN];
        struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES];
        u8 blacklist_len;
        u8 num_profiles;
-       u8 reserved[2];
+       u8 match_notify;
+       u8 pass_match;
+       u8 active_clients;
+       u8 reserved[3];
 } __packed;
 
 /**
@@ -560,4 +575,15 @@ struct iwl_scan_offload_complete {
        u8 reserved;
 } __packed;
 
+/**
+ * iwl_sched_scan_results - SCAN_OFFLOAD_MATCH_FOUND_NTF_API_S_VER_1
+ * @ssid_bitmap:       SSIDs indexes found in this iteration
+ * @client_bitmap:     clients that are active and wait for this notification
+ */
+struct iwl_sched_scan_results {
+       __le16 ssid_bitmap;
+       u8 client_bitmap;
+       u8 reserved;
+};
+
 #endif
index a30691a8a85b03a0b76815bf627480242a2458af..4aca5933a65d1619fc3e2e419ced4ddddc3d44c1 100644 (file)
@@ -247,7 +247,7 @@ struct iwl_mvm_keyinfo {
 } __packed;
 
 /**
- * struct iwl_mvm_add_sta_cmd - Add / modify a station in the fw's station table
+ * struct iwl_mvm_add_sta_cmd_v5 - Add/modify a station in the fw's sta table.
  * ( REPLY_ADD_STA = 0x18 )
  * @add_modify: 1: modify existing, 0: add new station
  * @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent
@@ -286,7 +286,7 @@ struct iwl_mvm_keyinfo {
  * ADD_STA sets up the table entry for one station, either creating a new
  * entry, or modifying a pre-existing one.
  */
-struct iwl_mvm_add_sta_cmd {
+struct iwl_mvm_add_sta_cmd_v5 {
        u8 add_modify;
        u8 unicast_tx_key_id;
        u8 multicast_tx_key_id;
@@ -312,6 +312,57 @@ struct iwl_mvm_add_sta_cmd {
        __le32 tfd_queue_msk;
 } __packed; /* ADD_STA_CMD_API_S_VER_5 */
 
+/**
+ * struct iwl_mvm_add_sta_cmd_v6 - Add / modify a station
+ * VER_6 of this command is quite similar to VER_5 except
+ * exclusion of all fields related to the security key installation.
+ */
+struct iwl_mvm_add_sta_cmd_v6 {
+       u8 add_modify;
+       u8 reserved1;
+       __le16 tid_disable_tx;
+       __le32 mac_id_n_color;
+       u8 addr[ETH_ALEN];      /* _STA_ID_MODIFY_INFO_API_S_VER_1 */
+       __le16 reserved2;
+       u8 sta_id;
+       u8 modify_mask;
+       __le16 reserved3;
+       __le32 station_flags;
+       __le32 station_flags_msk;
+       u8 add_immediate_ba_tid;
+       u8 remove_immediate_ba_tid;
+       __le16 add_immediate_ba_ssn;
+       __le16 sleep_tx_count;
+       __le16 sleep_state_flags;
+       __le16 assoc_id;
+       __le16 beamform_flags;
+       __le32 tfd_queue_msk;
+} __packed; /* ADD_STA_CMD_API_S_VER_6 */
+
+/**
+ * struct iwl_mvm_add_sta_key_cmd - add/modify sta key
+ * ( REPLY_ADD_STA_KEY = 0x17 )
+ * @sta_id: index of station in uCode's station table
+ * @key_offset: key offset in key storage
+ * @key_flags: type %iwl_sta_key_flag
+ * @key: key material data
+ * @key2: key material data
+ * @rx_secur_seq_cnt: RX security sequence counter for the key
+ * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection
+ * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx
+ */
+struct iwl_mvm_add_sta_key_cmd {
+       u8 sta_id;
+       u8 key_offset;
+       __le16 key_flags;
+       u8 key[16];
+       u8 key2[16];
+       u8 rx_secur_seq_cnt[16];
+       u8 tkip_rx_tsc_byte2;
+       u8 reserved;
+       __le16 tkip_rx_ttak[5];
+} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */
+
 /**
  * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command
  * @ADD_STA_SUCCESS: operation was executed successfully
index 66264cc5a0168d296ddac86be6617cd56f1d84d5..bad5a552dd8d10412f5d9d874026215d6c6e9359 100644 (file)
 #include "fw-api-d3.h"
 #include "fw-api-bt-coex.h"
 
-/* queue and FIFO numbers by usage */
+/* maximal number of Tx queues in any platform */
+#define IWL_MVM_MAX_QUEUES     20
+
+/* Tx queue numbers */
 enum {
        IWL_MVM_OFFCHANNEL_QUEUE = 8,
        IWL_MVM_CMD_QUEUE = 9,
-       IWL_MVM_AUX_QUEUE = 15,
-       IWL_MVM_FIRST_AGG_QUEUE = 16,
-       IWL_MVM_NUM_QUEUES = 20,
-       IWL_MVM_LAST_AGG_QUEUE = IWL_MVM_NUM_QUEUES - 1,
-       IWL_MVM_CMD_FIFO = 7
 };
 
+#define IWL_MVM_CMD_FIFO       7
+
 #define IWL_MVM_STATION_COUNT  16
 
 /* commands */
@@ -97,6 +97,7 @@ enum {
        DBG_CFG = 0x9,
 
        /* station table */
+       ADD_STA_KEY = 0x17,
        ADD_STA = 0x18,
        REMOVE_STA = 0x19,
 
@@ -114,6 +115,7 @@ enum {
        TIME_EVENT_NOTIFICATION = 0x2a,
        BINDING_CONTEXT_CMD = 0x2b,
        TIME_QUOTA_CMD = 0x2c,
+       NON_QOS_TX_COUNTER_CMD = 0x2d,
 
        LQ_CMD = 0x4e,
 
@@ -130,6 +132,7 @@ enum {
        SCAN_OFFLOAD_COMPLETE = 0x6D,
        SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
        SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
+       MATCH_FOUND_NOTIFICATION = 0xd9,
 
        /* Phy */
        PHY_CONFIGURATION_CMD = 0x6a,
@@ -178,6 +181,7 @@ enum {
        BT_COEX_PRIO_TABLE = 0xcc,
        BT_COEX_PROT_ENV = 0xcd,
        BT_PROFILE_NOTIFICATION = 0xce,
+       BT_COEX_CI = 0x5d,
 
        REPLY_BEACON_FILTERING_CMD = 0xd2,
 
index c76299a3a1e0821708a91885ffab780b624fdbc1..83fc5ca04433f36b4009404465f8646cafba9cb0 100644 (file)
@@ -199,7 +199,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
         */
 
        for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
-               if (i < IWL_MVM_FIRST_AGG_QUEUE && i != IWL_MVM_CMD_QUEUE)
+               if (i < mvm->first_agg_queue && i != IWL_MVM_CMD_QUEUE)
                        mvm->queue_to_mac80211[i] = i;
                else
                        mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
@@ -243,7 +243,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (mvm->init_ucode_run)
+       if (mvm->init_ucode_complete)
                return 0;
 
        iwl_init_notification_wait(&mvm->notif_wait,
@@ -264,6 +264,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        if (ret)
                goto error;
 
+       /* Read the NVM only at driver load time, no need to do this twice */
        if (read_nvm) {
                /* Read nvm */
                ret = iwl_nvm_init(mvm);
@@ -273,6 +274,10 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                }
        }
 
+       /* In case we read the NVM from external file, load it to the NIC */
+       if (iwlwifi_mod_params.nvm_file)
+               iwl_mvm_load_nvm_to_nic(mvm);
+
        ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
        WARN_ON(ret);
 
@@ -310,7 +315,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait,
                        MVM_UCODE_CALIB_TIMEOUT);
        if (!ret)
-               mvm->init_ucode_run = true;
+               mvm->init_ucode_complete = true;
        goto out;
 
 error:
@@ -353,8 +358,12 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (ret)
                return ret;
 
-       /* If we were in RFKILL during module loading, load init ucode now */
-       if (!mvm->init_ucode_run) {
+       /*
+        * If we haven't completed the run of the init ucode during
+        * module loading, load init ucode now
+        * (for example, if we were in RFKILL)
+        */
+       if (!mvm->init_ucode_complete) {
                ret = iwl_run_init_mvm_ucode(mvm, false);
                if (ret && !iwlmvm_mod_params.init_dbg) {
                        IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
@@ -424,6 +433,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                        goto error;
        }
 
+       ret = iwl_mvm_power_update_device_mode(mvm);
+       if (ret)
+               goto error;
+
        IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
        return 0;
  error:
index 5fe23a5ea9b66b662cd746c113b644b1318517e2..ab5a7ac90dcd68948882cc245cd8df04e62df37c 100644 (file)
@@ -80,7 +80,7 @@ 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)];
-       unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_FIRST_AGG_QUEUE)];
+       unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_MAX_QUEUES)];
        enum iwl_tsf_id preferred_tsf;
        bool found_vif;
 };
@@ -218,7 +218,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
                .preferred_tsf = NUM_TSF_IDS,
                .used_hw_queues = {
                        BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
-                       BIT(IWL_MVM_AUX_QUEUE) |
+                       BIT(mvm->aux_queue) |
                        BIT(IWL_MVM_CMD_QUEUE)
                },
                .found_vif = false,
@@ -242,9 +242,17 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
         * that we should share it with another interface.
         */
 
-       /* Currently, MAC ID 0 should be used only for the managed vif */
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+       /* Currently, MAC ID 0 should be used only for the managed/IBSS vif */
+       switch (vif->type) {
+       case NL80211_IFTYPE_ADHOC:
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (!vif->p2p)
+                       break;
+               /* fall through */
+       default:
                __clear_bit(0, data.available_mac_ids);
+       }
 
        ieee80211_iterate_active_interfaces_atomic(
                mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
@@ -302,9 +310,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
        /* Find available queues, and allocate them to the ACs */
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                u8 queue = find_first_zero_bit(data.used_hw_queues,
-                                              IWL_MVM_FIRST_AGG_QUEUE);
+                                              mvm->first_agg_queue);
 
-               if (queue >= IWL_MVM_FIRST_AGG_QUEUE) {
+               if (queue >= mvm->first_agg_queue) {
                        IWL_ERR(mvm, "Failed to allocate queue\n");
                        ret = -EIO;
                        goto exit_fail;
@@ -317,9 +325,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
        /* Allocate the CAB queue for softAP and GO interfaces */
        if (vif->type == NL80211_IFTYPE_AP) {
                u8 queue = find_first_zero_bit(data.used_hw_queues,
-                                              IWL_MVM_FIRST_AGG_QUEUE);
+                                              mvm->first_agg_queue);
 
-               if (queue >= IWL_MVM_FIRST_AGG_QUEUE) {
+               if (queue >= mvm->first_agg_queue) {
                        IWL_ERR(mvm, "Failed to allocate cab queue\n");
                        ret = -EIO;
                        goto exit_fail;
@@ -559,8 +567,12 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
                cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
 
        /* Don't use cts to self as the fw doesn't support it currently. */
-       if (vif->bss_conf.use_cts_prot)
+       if (vif->bss_conf.use_cts_prot) {
                cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
+               if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8)
+                       cmd->protection_flags |=
+                               cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN);
+       }
 
        /*
         * I think that we should enable these 2 flags regardless the HT PROT
@@ -712,6 +724,31 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
        return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
 }
 
+static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif,
+                                    u32 action)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
+
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON |
+                                      MAC_FILTER_IN_PROBE_REQUEST);
+
+       /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */
+       cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int);
+       cmd.ibss.bi_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
+
+       /* TODO: Assumes that the beacon id == mac context id */
+       cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id);
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
 struct iwl_mvm_go_iterator_data {
        bool go_active;
 };
@@ -721,7 +758,8 @@ static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)
        struct iwl_mvm_go_iterator_data *data = _data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
-       if (vif->type == NL80211_IFTYPE_AP && vif->p2p && mvmvif->ap_active)
+       if (vif->type == NL80211_IFTYPE_AP && vif->p2p &&
+           mvmvif->ap_ibss_active)
                data->go_active = true;
 }
 
@@ -833,9 +871,10 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
                cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate));
 
        /* Set up TX beacon command fields */
-       iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd,
-                                beacon->data,
-                                beacon_skb_len);
+       if (vif->type == NL80211_IFTYPE_AP)
+               iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd,
+                                        beacon->data,
+                                        beacon_skb_len);
 
        /* Submit command */
        cmd.len[0] = sizeof(beacon_cmd);
@@ -848,14 +887,15 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
        return iwl_mvm_send_cmd(mvm, &cmd);
 }
 
-/* The beacon template for the AP/GO context has changed and needs update */
+/* The beacon template for the AP/GO/IBSS has changed and needs update */
 int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif)
 {
        struct sk_buff *beacon;
        int ret;
 
-       WARN_ON(vif->type != NL80211_IFTYPE_AP);
+       WARN_ON(vif->type != NL80211_IFTYPE_AP &&
+               vif->type != NL80211_IFTYPE_ADHOC);
 
        beacon = ieee80211_beacon_get(mvm->hw, vif);
        if (!beacon)
@@ -1018,6 +1058,8 @@ static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action);
        case NL80211_IFTYPE_P2P_DEVICE:
                return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action);
+       case NL80211_IFTYPE_ADHOC:
+               return iwl_mvm_mac_ctxt_cmd_ibss(mvm, vif, action);
        default:
                break;
        }
@@ -1038,6 +1080,9 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        if (ret)
                return ret;
 
+       /* will only do anything at resume from D3 time */
+       iwl_mvm_set_last_nonqos_seq(mvm, vif);
+
        mvmvif->uploaded = true;
        return 0;
 }
index 9833cdf6177cd34d199de063cb0ff466042cfed0..f40685c3764ea8b211ad9fa7affb8ed675ef1ddd 100644 (file)
@@ -77,6 +77,7 @@
 #include "iwl-eeprom-parse.h"
 #include "fw-api-scan.h"
 #include "iwl-phy-db.h"
+#include "testmode.h"
 
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
@@ -138,6 +139,14 @@ static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
        }
 }
 
+static int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm)
+{
+       /* we create the 802.11 header and SSID element */
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID)
+               return mvm->fw->ucode_capa.max_probe_length - 24 - 2;
+       return mvm->fw->ucode_capa.max_probe_length - 24 - 34;
+}
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
        struct ieee80211_hw *hw = mvm->hw;
@@ -158,7 +167,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_SUPPORTS_STATIC_SMPS |
                    IEEE80211_HW_SUPPORTS_UAPSD;
 
-       hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
+       hw->queues = mvm->first_agg_queue;
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
        hw->rate_control_algorithm = "iwl-mvm-rs";
 
@@ -181,6 +190,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                BIT(NL80211_IFTYPE_P2P_GO) |
                BIT(NL80211_IFTYPE_P2P_DEVICE);
 
+       /* IBSS has bugs in older versions */
+       if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8)
+               hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
+
        hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
                            WIPHY_FLAG_DISABLE_BEACON_HINTS |
                            WIPHY_FLAG_IBSS_RSN;
@@ -212,9 +225,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        iwl_mvm_reset_phy_ctxts(mvm);
 
-       /* we create the 802.11 header and a max-length SSID element */
-       hw->wiphy->max_scan_ie_len =
-               mvm->fw->ucode_capa.max_probe_length - 24 - 34;
+       hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm);
+
        hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
 
        if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
@@ -231,6 +243,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        else
                hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
+               hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+               hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
+               hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
+               /* we create the 802.11 header and zero length SSID IE. */
+               hw->wiphy->max_sched_scan_ie_len =
+                                       SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
+       }
+
        hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
                               NL80211_FEATURE_P2P_GO_OPPPS;
 
@@ -548,7 +569,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
         * In short: there's not much we can do at this point, other than
         * allocating resources :)
         */
-       if (vif->type == NL80211_IFTYPE_AP) {
+       if (vif->type == NL80211_IFTYPE_AP ||
+           vif->type == NL80211_IFTYPE_ADHOC) {
                u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
                ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
                                               qmask);
@@ -698,7 +720,14 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
         * For AP/GO interface, the tear down of the resources allocated to the
         * interface is be handled as part of the stop_ap flow.
         */
-       if (vif->type == NL80211_IFTYPE_AP) {
+       if (vif->type == NL80211_IFTYPE_AP ||
+           vif->type == NL80211_IFTYPE_ADHOC) {
+#ifdef CONFIG_NL80211_TESTMODE
+               if (vif == mvm->noa_vif) {
+                       mvm->noa_vif = NULL;
+                       mvm->noa_duration = 0;
+               }
+#endif
                iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);
                goto out_release;
        }
@@ -796,6 +825,27 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                return;
                        }
                        iwl_mvm_configure_mcast_filter(mvm, vif);
+
+                       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
+                                    &mvm->status)) {
+                               /*
+                                * If we're restarting then the firmware will
+                                * obviously have lost synchronisation with
+                                * the AP. It will attempt to synchronise by
+                                * itself, but we can make it more reliable by
+                                * scheduling a session protection time event.
+                                *
+                                * The firmware needs to receive a beacon to
+                                * catch up with synchronisation, use 110% of
+                                * the beacon interval.
+                                *
+                                * Set a large maximum delay to allow for more
+                                * than a single interface.
+                                */
+                               u32 dur = (11 * vif->bss_conf.beacon_int) / 10;
+                               iwl_mvm_protect_session(mvm, vif, dur, dur,
+                                                       5 * dur);
+                       }
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /* remove AP station now that the MAC is unassoc */
                        ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
@@ -819,7 +869,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                        if (ret)
                                IWL_ERR(mvm, "failed to update power mode\n");
                }
-               iwl_mvm_bt_coex_vif_assoc(mvm, vif);
+               iwl_mvm_bt_coex_vif_change(mvm);
        } else if (changes & BSS_CHANGED_BEACON_INFO) {
                /*
                 * We received a beacon _after_ association so
@@ -848,7 +898,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        }
 }
 
-static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -871,7 +922,7 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        if (ret)
                goto out_remove;
 
-       mvmvif->ap_active = true;
+       mvmvif->ap_ibss_active = true;
 
        /* Send the bcast station. At this stage the TBTT and DTIM time events
         * are added and applied to the scheduler */
@@ -883,10 +934,12 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        if (ret)
                goto out_rm_bcast;
 
-       /* Need to update the P2P Device MAC */
+       /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
 
+       iwl_mvm_bt_coex_vif_change(mvm);
+
        mutex_unlock(&mvm->mutex);
        return 0;
 
@@ -901,7 +954,8 @@ out_unlock:
        return ret;
 }
 
-static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -910,9 +964,11 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
        mutex_lock(&mvm->mutex);
 
-       mvmvif->ap_active = false;
+       mvmvif->ap_ibss_active = false;
+
+       iwl_mvm_bt_coex_vif_change(mvm);
 
-       /* Need to update the P2P Device MAC */
+       /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
 
@@ -924,10 +980,11 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        mutex_unlock(&mvm->mutex);
 }
 
-static void iwl_mvm_bss_info_changed_ap(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif,
-                                       struct ieee80211_bss_conf *bss_conf,
-                                       u32 changes)
+static void
+iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_bss_conf *bss_conf,
+                                u32 changes)
 {
        /* Need to send a new beacon template to the FW */
        if (changes & BSS_CHANGED_BEACON) {
@@ -950,7 +1007,8 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
                iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
                break;
        case NL80211_IFTYPE_AP:
-               iwl_mvm_bss_info_changed_ap(mvm, vif, bss_conf, changes);
+       case NL80211_IFTYPE_ADHOC:
+               iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes);
                break;
        default:
                /* shouldn't happen */
@@ -1163,7 +1221,54 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
        /* Try really hard to protect the session and hear a beacon */
-       iwl_mvm_protect_session(mvm, vif, duration, min_duration);
+       iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500);
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct cfg80211_sched_scan_request *req,
+                                       struct ieee80211_sched_scan_ies *ies)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
+               IWL_DEBUG_SCAN(mvm,
+                              "SCHED SCAN request during internal scan - abort\n");
+               ret = -EBUSY;
+               goto out;
+       }
+
+       mvm->scan_status = IWL_MVM_SCAN_SCHED;
+
+       ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
+       if (ret)
+               goto err;
+
+       ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+       if (ret)
+               goto err;
+
+       ret = iwl_mvm_sched_scan_start(mvm, req);
+       if (!ret)
+               goto out;
+err:
+       mvm->scan_status = IWL_MVM_SCAN_NONE;
+out:
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+static void iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_sched_scan_stop(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1207,8 +1312,13 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
 
        switch (cmd) {
        case SET_KEY:
-               if (vif->type == NL80211_IFTYPE_AP && !sta) {
-                       /* GTK on AP interface is a TX-only key, return 0 */
+               if ((vif->type == NL80211_IFTYPE_ADHOC ||
+                    vif->type == NL80211_IFTYPE_AP) && !sta) {
+                       /*
+                        * GTK on AP interface is a TX-only key, return 0;
+                        * on IBSS they're per-station and because we're lazy
+                        * we don't support them for RX, so do the same.
+                        */
                        ret = 0;
                        key->hw_key_idx = STA_KEY_IDX_INVALID;
                        break;
@@ -1252,6 +1362,9 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
+       if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID)
+               return;
+
        iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key);
 }
 
@@ -1445,6 +1558,7 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
        iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
                                 ctx->rx_chains_static,
                                 ctx->rx_chains_dynamic);
+       iwl_mvm_bt_coex_vif_change(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1464,14 +1578,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
 
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
                /*
                 * The AP binding flow is handled as part of the start_ap flow
-                * (in bss_info_changed).
+                * (in bss_info_changed), similarly for IBSS.
                 */
                ret = 0;
                goto out_unlock;
        case NL80211_IFTYPE_STATION:
-       case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_MONITOR:
                break;
        default:
@@ -1517,10 +1631,10 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
 
        iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
 
-       if (vif->type == NL80211_IFTYPE_AP)
-               goto out_unlock;
-
        switch (vif->type) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
+               goto out_unlock;
        case NL80211_IFTYPE_MONITOR:
                mvmvif->monitor_active = false;
                iwl_mvm_update_quotas(mvm, NULL);
@@ -1550,14 +1664,72 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
        return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
 }
 
-static void iwl_mvm_mac_rssi_callback(struct ieee80211_hw *hw,
+#ifdef CONFIG_NL80211_TESTMODE
+static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = {
+       [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 },
+       [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 },
+       [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 },
+};
+
+static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
                                      struct ieee80211_vif *vif,
-                                     enum ieee80211_rssi_event rssi_event)
+                                     void *data, int len)
+{
+       struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1];
+       int err;
+       u32 noa_duration;
+
+       err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy);
+       if (err)
+               return err;
+
+       if (!tb[IWL_MVM_TM_ATTR_CMD])
+               return -EINVAL;
+
+       switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) {
+       case IWL_MVM_TM_CMD_SET_NOA:
+               if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p ||
+                   !vif->bss_conf.enable_beacon ||
+                   !tb[IWL_MVM_TM_ATTR_NOA_DURATION])
+                       return -EINVAL;
+
+               noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]);
+               if (noa_duration >= vif->bss_conf.beacon_int)
+                       return -EINVAL;
+
+               mvm->noa_duration = noa_duration;
+               mvm->noa_vif = vif;
+
+               return iwl_mvm_update_quotas(mvm, NULL);
+       case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
+               /* must be associated client vif - ignore authorized */
+               if (!vif || vif->type != NL80211_IFTYPE_STATION ||
+                   !vif->bss_conf.assoc || !vif->bss_conf.dtim_period ||
+                   !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])
+                       return -EINVAL;
+
+               if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
+                       return iwl_mvm_enable_beacon_filter(mvm, vif);
+               return iwl_mvm_disable_beacon_filter(mvm, vif);
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   void *data, int len)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int err;
 
-       iwl_mvm_bt_rssi_event(mvm, vif, rssi_event);
+       mutex_lock(&mvm->mutex);
+       err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len);
+       mutex_unlock(&mvm->mutex);
+
+       return err;
 }
+#endif
 
 struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
@@ -1578,23 +1750,27 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
        .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
        .conf_tx = iwl_mvm_mac_conf_tx,
        .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+       .sched_scan_start = iwl_mvm_mac_sched_scan_start,
+       .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
        .set_key = iwl_mvm_mac_set_key,
        .update_tkip_key = iwl_mvm_mac_update_tkip_key,
        .remain_on_channel = iwl_mvm_roc,
        .cancel_remain_on_channel = iwl_mvm_cancel_roc,
-       .rssi_callback = iwl_mvm_mac_rssi_callback,
-
        .add_chanctx = iwl_mvm_add_chanctx,
        .remove_chanctx = iwl_mvm_remove_chanctx,
        .change_chanctx = iwl_mvm_change_chanctx,
        .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
        .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
 
-       .start_ap = iwl_mvm_start_ap,
-       .stop_ap = iwl_mvm_stop_ap,
+       .start_ap = iwl_mvm_start_ap_ibss,
+       .stop_ap = iwl_mvm_stop_ap_ibss,
+       .join_ibss = iwl_mvm_start_ap_ibss,
+       .leave_ibss = iwl_mvm_stop_ap_ibss,
 
        .set_tim = iwl_mvm_set_tim,
 
+       CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
+
 #ifdef CONFIG_PM_SLEEP
        /* look at d3.c */
        .suspend = iwl_mvm_suspend,
index b0389279cc1ed774f9f90f20137d11b6691d8eae..6235cb729f5c0ecd5c40fe90d9e78b8fceb1f1c5 100644 (file)
@@ -162,6 +162,7 @@ enum iwl_power_scheme {
 struct iwl_mvm_power_ops {
        int (*power_update_mode)(struct iwl_mvm *mvm,
                                 struct ieee80211_vif *vif);
+       int (*power_update_device_mode)(struct iwl_mvm *mvm);
        int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -241,12 +242,18 @@ enum iwl_mvm_smps_type_request {
 * @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
+* @bt_coex_max_thold: maximum threshold for BT coex
+* @last_bt_coex_event: rssi of the last BT coex event
 */
 struct iwl_mvm_vif_bf_data {
        bool bf_enabled;
        bool ba_enabled;
        s8 ave_beacon_signal;
        s8 last_cqm_event;
+       s8 bt_coex_min_thold;
+       s8 bt_coex_max_thold;
+       s8 last_bt_coex_event;
 };
 
 /**
@@ -255,8 +262,8 @@ struct iwl_mvm_vif_bf_data {
  * @color: to solve races upon MAC addition and removal
  * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA
  * @uploaded: indicates the MAC context has been added to the device
- * @ap_active: indicates that ap context is configured, and that the interface
- *  should get quota etc.
+ * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
+ *     should get quota etc.
  * @monitor_active: indicates that monitor context is configured, and that the
  * interface should get quota etc.
  * @queue_params: QoS params for this MAC
@@ -272,7 +279,7 @@ struct iwl_mvm_vif {
        u8 ap_sta_id;
 
        bool uploaded;
-       bool ap_active;
+       bool ap_ibss_active;
        bool monitor_active;
        struct iwl_mvm_vif_bf_data bf_data;
 
@@ -306,6 +313,9 @@ struct iwl_mvm_vif {
 
        int tx_key_idx;
 
+       bool seqno_valid;
+       u16 seqno;
+
 #if IS_ENABLED(CONFIG_IPV6)
        /* IPv6 addresses for WoWLAN */
        struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
@@ -333,6 +343,7 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
 enum iwl_scan_status {
        IWL_MVM_SCAN_NONE,
        IWL_MVM_SCAN_OS,
+       IWL_MVM_SCAN_SCHED,
 };
 
 /**
@@ -434,7 +445,7 @@ struct iwl_mvm {
 
        enum iwl_ucode_type cur_ucode;
        bool ucode_loaded;
-       bool init_ucode_run;
+       bool init_ucode_complete;
        u32 error_event_table;
        u32 log_event_table;
 
@@ -470,6 +481,9 @@ struct iwl_mvm {
        enum iwl_scan_status scan_status;
        struct iwl_scan_cmd *scan_cmd;
 
+       /* rx chain antennas set through debugfs for the scan command */
+       u8 scan_rx_ant;
+
        /* Internal station */
        struct iwl_mvm_int_sta aux_sta;
 
@@ -479,7 +493,8 @@ struct iwl_mvm {
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        struct dentry *debugfs_dir;
        u32 dbgfs_sram_offset, dbgfs_sram_len;
-       bool prevent_power_down_d3;
+       bool disable_power_off;
+       bool disable_power_off_d3;
 #endif
 
        struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
@@ -523,12 +538,23 @@ struct iwl_mvm {
        /* BT-Coex */
        u8 bt_kill_msk;
        struct iwl_bt_coex_profile_notif last_bt_notif;
+       struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
 
        /* Thermal Throttling and CTkill */
        struct iwl_mvm_tt_mgmt thermal_throttle;
        s32 temperature;        /* Celsius */
 
        const struct iwl_mvm_power_ops *pm_ops;
+
+#ifdef CONFIG_NL80211_TESTMODE
+       u32 noa_duration;
+       struct ieee80211_vif *noa_vif;
+#endif
+
+       /* Tx queues */
+       u8 aux_queue;
+       u8 first_agg_queue;
+       u8 last_agg_queue;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -570,6 +596,9 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm);
 /* Utils */
 int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
                                        enum ieee80211_band band);
+void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
+                              enum ieee80211_band band,
+                              struct ieee80211_tx_rate *r);
 u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
 void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
 void iwl_mvm_dump_sram(struct iwl_mvm *mvm);
@@ -608,6 +637,7 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
 
 /* NVM */
 int iwl_nvm_init(struct iwl_mvm *mvm);
+int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
 
 int iwl_mvm_up(struct iwl_mvm *mvm);
 int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
@@ -682,6 +712,23 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                             struct iwl_device_cmd *cmd);
 void iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
 
+/* Scheduled scan */
+int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
+                                          struct iwl_rx_cmd_buffer *rxb,
+                                          struct iwl_device_cmd *cmd);
+int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             struct cfg80211_sched_scan_request *req,
+                             struct ieee80211_sched_scan_ies *ies);
+int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
+                                      struct cfg80211_sched_scan_request *req);
+int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
+                            struct cfg80211_sched_scan_request *req);
+void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm);
+int iwl_mvm_rx_sched_scan_results(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);
@@ -720,6 +767,13 @@ static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm,
        return mvm->pm_ops->power_disable(mvm, vif);
 }
 
+static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm)
+{
+       if (mvm->pm_ops->power_update_device_mode)
+               return mvm->pm_ops->power_update_device_mode(mvm);
+       return 0;
+}
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
                                            struct ieee80211_vif *vif,
@@ -745,6 +799,15 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
 void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif, int idx);
 extern const struct file_operations iwl_dbgfs_d3_test_ops;
+#ifdef CONFIG_PM_SLEEP
+void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif);
+#else
+static inline void
+iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+}
+#endif
 
 /* BT Coex */
 int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
@@ -754,7 +817,20 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
                             struct iwl_device_cmd *cmd);
 void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                           enum ieee80211_rssi_event rssi_event);
-void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm);
+u16 iwl_mvm_bt_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);
+
+enum iwl_bt_kill_msk {
+       BT_KILL_MSK_DEFAULT,
+       BT_KILL_MSK_SCO_HID_A2DP,
+       BT_KILL_MSK_REDUCED_TXPOW,
+       BT_KILL_MSK_MAX,
+};
+extern const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX];
+extern const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX];
 
 /* beacon filtering */
 #ifdef CONFIG_IWLWIFI_DEBUGFS
index edb94ea316545f439a18e8c16e0952066336e2a2..2beffd028b67cef7469faa40899b6b6992501976 100644 (file)
@@ -77,7 +77,7 @@ static const int nvm_to_read[] = {
 
 /* Default NVM size to read */
 #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
-#define IWL_MAX_NVM_SECTION_SIZE 6000
+#define IWL_MAX_NVM_SECTION_SIZE 7000
 
 #define NVM_WRITE_OPCODE 1
 #define NVM_READ_OPCODE 0
@@ -259,6 +259,8 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
 #define MAX_NVM_FILE_LEN       16384
 
 /*
+ * Reads external NVM from a file into mvm->nvm_sections
+ *
  * HOW TO CREATE THE NVM FILE FORMAT:
  * ------------------------------
  * 1. create hex file, format:
@@ -277,20 +279,23 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
  *
  * 4. save as "iNVM_xxx.bin" under /lib/firmware
  */
-static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm)
+static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
 {
-       int ret, section_id, section_size;
+       int ret, section_size;
+       u16 section_id;
        const struct firmware *fw_entry;
        const struct {
                __le16 word1;
                __le16 word2;
                u8 data[];
        } *file_sec;
-       const u8 *eof;
+       const u8 *eof, *temp;
 
 #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
 #define NVM_WORD2_ID(x) (x >> 12)
 
+       IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
+
        /*
         * Obtain NVM image via request_firmware. Since we already used
         * request_firmware_nowait() for the firmware binary load and only
@@ -362,12 +367,18 @@ static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm)
                        break;
                }
 
-               ret = iwl_nvm_write_section(mvm, section_id, file_sec->data,
-                                           section_size);
-               if (ret < 0) {
-                       IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret);
+               temp = kmemdup(file_sec->data, section_size, GFP_KERNEL);
+               if (!temp) {
+                       ret = -ENOMEM;
+                       break;
+               }
+               if (WARN_ON(section_id >= NVM_NUM_OF_SECTIONS)) {
+                       IWL_ERR(mvm, "Invalid NVM section ID\n");
+                       ret = -EINVAL;
                        break;
                }
+               mvm->nvm_sections[section_id].data = temp;
+               mvm->nvm_sections[section_id].length = section_size;
 
                /* advance to the next section */
                file_sec = (void *)(file_sec->data + section_size);
@@ -377,6 +388,28 @@ out:
        return ret;
 }
 
+/* Loads the NVM data stored in mvm->nvm_sections into the NIC */
+int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
+{
+       int i, ret;
+       u16 section_id;
+       struct iwl_nvm_section *sections = mvm->nvm_sections;
+
+       IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n");
+
+       for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
+               section_id = nvm_to_read[i];
+               ret = iwl_nvm_write_section(mvm, section_id,
+                                           sections[section_id].data,
+                                           sections[section_id].length);
+               if (ret < 0) {
+                       IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret);
+                       break;
+               }
+       }
+       return ret;
+}
+
 int iwl_nvm_init(struct iwl_mvm *mvm)
 {
        int ret, i, section;
@@ -385,36 +418,36 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
        /* load external NVM if configured */
        if (iwlwifi_mod_params.nvm_file) {
                /* move to External NVM flow */
-               ret = iwl_mvm_load_external_nvm(mvm);
+               ret = iwl_mvm_read_external_nvm(mvm);
                if (ret)
                        return ret;
-       }
-
-       /* Read From FW NVM */
-       IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
-
-       /* TODO: find correct NVM max size for a section */
-       nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
-                            GFP_KERNEL);
-       if (!nvm_buffer)
-               return -ENOMEM;
-       for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
-               section = nvm_to_read[i];
-               /* we override the constness for initial read */
-               ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
-               if (ret < 0)
-                       break;
-               temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
-               if (!temp) {
-                       ret = -ENOMEM;
-                       break;
+       } else {
+               /* Read From FW NVM */
+               IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
+
+               /* TODO: find correct NVM max size for a section */
+               nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
+                                    GFP_KERNEL);
+               if (!nvm_buffer)
+                       return -ENOMEM;
+               for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
+                       section = nvm_to_read[i];
+                       /* we override the constness for initial read */
+                       ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
+                       if (ret < 0)
+                               break;
+                       temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
+                       if (!temp) {
+                               ret = -ENOMEM;
+                               break;
+                       }
+                       mvm->nvm_sections[section].data = temp;
+                       mvm->nvm_sections[section].length = ret;
                }
-               mvm->nvm_sections[section].data = temp;
-               mvm->nvm_sections[section].length = ret;
+               kfree(nvm_buffer);
+               if (ret < 0)
+                       return ret;
        }
-       kfree(nvm_buffer);
-       if (ret < 0)
-               return ret;
 
        mvm->nvm_data = iwl_parse_nvm_sections(mvm);
        if (!mvm->nvm_data)
index 2fcc8ef88a68d78fbc0f2225bbabc733d8d4e619..59b7cb3c61344939c96f285e46ea9751e9197d35 100644 (file)
@@ -224,6 +224,10 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
 
        RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
        RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
+       RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
+                  iwl_mvm_rx_scan_offload_complete_notif, false),
+       RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results,
+                  false),
 
        RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
        RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
@@ -249,6 +253,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(TIME_EVENT_NOTIFICATION),
        CMD(BINDING_CONTEXT_CMD),
        CMD(TIME_QUOTA_CMD),
+       CMD(NON_QOS_TX_COUNTER_CMD),
        CMD(RADIO_VERSION_NOTIFICATION),
        CMD(SCAN_REQUEST_CMD),
        CMD(SCAN_ABORT_CMD),
@@ -260,10 +265,12 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(CALIB_RES_NOTIF_PHY_DB),
        CMD(SET_CALIB_DEFAULT_CMD),
        CMD(CALIBRATION_COMPLETE_NOTIFICATION),
+       CMD(ADD_STA_KEY),
        CMD(ADD_STA),
        CMD(REMOVE_STA),
        CMD(LQ_CMD),
        CMD(SCAN_OFFLOAD_CONFIG_CMD),
+       CMD(MATCH_FOUND_NOTIFICATION),
        CMD(SCAN_OFFLOAD_REQUEST_CMD),
        CMD(SCAN_OFFLOAD_ABORT_CMD),
        CMD(SCAN_OFFLOAD_COMPLETE),
@@ -303,6 +310,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(REPLY_BEACON_FILTERING_CMD),
        CMD(REPLY_THERMAL_MNG_BACKOFF),
        CMD(MAC_PM_POWER_TABLE),
+       CMD(BT_COEX_CI),
 };
 #undef CMD
 
@@ -344,6 +352,14 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0;
 
+       mvm->aux_queue = 15;
+       mvm->first_agg_queue = 16;
+       mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1;
+       if (mvm->cfg->base_params->num_of_queues == 16) {
+               mvm->aux_queue = 11;
+               mvm->first_agg_queue = 12;
+       }
+
        mutex_init(&mvm->mutex);
        spin_lock_init(&mvm->async_handlers_lock);
        INIT_LIST_HEAD(&mvm->time_event_list);
@@ -401,24 +417,32 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
                 mvm->cfg->name, mvm->trans->hw_rev);
 
-       err = iwl_trans_start_hw(mvm->trans);
-       if (err)
-               goto out_free;
-
        iwl_mvm_tt_initialize(mvm);
 
-       mutex_lock(&mvm->mutex);
-       err = iwl_run_init_mvm_ucode(mvm, true);
-       mutex_unlock(&mvm->mutex);
-       /* returns 0 if successful, 1 if success but in rfkill */
-       if (err < 0 && !iwlmvm_mod_params.init_dbg) {
-               IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
-               goto out_free;
-       }
+       /*
+        * If the NVM exists in an external file,
+        * there is no need to unnecessarily power up the NIC at driver load
+        */
+       if (iwlwifi_mod_params.nvm_file) {
+                       iwl_nvm_init(mvm);
+       } else {
+               err = iwl_trans_start_hw(mvm->trans);
+               if (err)
+                       goto out_free;
+
+               mutex_lock(&mvm->mutex);
+               err = iwl_run_init_mvm_ucode(mvm, true);
+               mutex_unlock(&mvm->mutex);
+               /* returns 0 if successful, 1 if success but in rfkill */
+               if (err < 0 && !iwlmvm_mod_params.init_dbg) {
+                       IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
+                       goto out_free;
+               }
 
-       /* Stop the hw after the ALIVE and NVM has been read */
-       if (!iwlmvm_mod_params.init_dbg)
-               iwl_trans_stop_hw(mvm->trans, false);
+               /* Stop the hw after the ALIVE and NVM has been read */
+               if (!iwlmvm_mod_params.init_dbg)
+                       iwl_trans_stop_hw(mvm->trans, false);
+       }
 
        scan_size = sizeof(struct iwl_scan_cmd) +
                mvm->fw->ucode_capa.max_probe_length +
@@ -449,7 +473,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
  out_free:
        iwl_phy_db_free(mvm->phy_db);
        kfree(mvm->scan_cmd);
-       iwl_trans_stop_hw(trans, true);
+       if (!iwlwifi_mod_params.nvm_file)
+               iwl_trans_stop_hw(trans, true);
        ieee80211_free_hw(mvm->hw);
        return NULL;
 }
@@ -715,6 +740,9 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
                case IWL_MVM_SCAN_OS:
                        ieee80211_scan_completed(mvm->hw, true);
                        break;
+               case IWL_MVM_SCAN_SCHED:
+                       ieee80211_sched_scan_stopped(mvm->hw);
+                       break;
                }
 
                if (mvm->restart_fw > 0)
index 21407a353a3b0e623c87ae3627957ae45ae3b162..80d5f88a9d321b080c8cc63a4f0b58b0a35f0d0f 100644 (file)
@@ -297,11 +297,6 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        }
 
        if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
-               cmd->rx_data_timeout_uapsd =
-                       cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
-               cmd->tx_data_timeout_uapsd =
-                       cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
-
                if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
                                            BIT(IEEE80211_AC_VI) |
                                            BIT(IEEE80211_AC_BE) |
@@ -316,10 +311,31 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                }
 
                cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
-               cmd->heavy_tx_thld_packets =
-                       IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
-               cmd->heavy_rx_thld_packets =
-                       IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
+
+               if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
+                   cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+                       cmd->rx_data_timeout_uapsd =
+                               cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+                       cmd->tx_data_timeout_uapsd =
+                               cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
+               } else {
+                       cmd->rx_data_timeout_uapsd =
+                               cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
+                       cmd->tx_data_timeout_uapsd =
+                               cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
+               }
+
+               if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+                       cmd->heavy_tx_thld_packets =
+                               IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
+                       cmd->heavy_rx_thld_packets =
+                               IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
+               } else {
+                       cmd->heavy_tx_thld_packets =
+                               IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
+                       cmd->heavy_rx_thld_packets =
+                               IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
+               }
                cmd->heavy_tx_thld_percentage =
                        IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
                cmd->heavy_rx_thld_percentage =
@@ -427,6 +443,32 @@ static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
                                    sizeof(cmd), &cmd);
 }
 
+static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
+{
+       struct iwl_device_power_cmd cmd = {
+               .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
+       };
+
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
+               return 0;
+
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+               cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 :
+           mvm->disable_power_off)
+               cmd.flags &=
+                       cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
+       IWL_DEBUG_POWER(mvm,
+                       "Sending device power command with flags = 0x%X\n",
+                       cmd.flags);
+
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, sizeof(cmd),
+                                   &cmd);
+}
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
                                        struct ieee80211_vif *vif, char *buf,
@@ -437,10 +479,11 @@ static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
 
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
 
-       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
-                        (cmd.flags &
-                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
-                        0 : 1);
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
+               pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
+                                (cmd.flags &
+                                cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
+                                0 : 1);
        pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
                         iwlmvm_mod_params.power_scheme);
        pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
@@ -606,6 +649,7 @@ int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
 
 const struct iwl_mvm_power_ops pm_mac_ops = {
        .power_update_mode = iwl_mvm_power_mac_update_mode,
+       .power_update_device_mode = iwl_mvm_power_update_device,
        .power_disable = iwl_mvm_power_mac_disable,
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
index 5c6ae16ec52b934c16835a4fecdffb300a1df38b..17e2bc827f9a3b20b7da9e0d7ee4dd43ac00606a 100644 (file)
@@ -110,7 +110,8 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
                        data->n_interfaces[id]++;
                break;
        case NL80211_IFTYPE_AP:
-               if (mvmvif->ap_active)
+       case NL80211_IFTYPE_ADHOC:
+               if (mvmvif->ap_ibss_active)
                        data->n_interfaces[id]++;
                break;
        case NL80211_IFTYPE_MONITOR:
@@ -119,16 +120,45 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                break;
-       case NL80211_IFTYPE_ADHOC:
-               if (vif->bss_conf.ibss_joined)
-                       data->n_interfaces[id]++;
-               break;
        default:
                WARN_ON_ONCE(1);
                break;
        }
 }
 
+static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
+                                        struct iwl_time_quota_cmd *cmd)
+{
+#ifdef CONFIG_NL80211_TESTMODE
+       struct iwl_mvm_vif *mvmvif;
+       int i, phy_id = -1, beacon_int = 0;
+
+       if (!mvm->noa_duration || !mvm->noa_vif)
+               return;
+
+       mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif);
+       if (!mvmvif->ap_ibss_active)
+               return;
+
+       phy_id = mvmvif->phy_ctxt->id;
+       beacon_int = mvm->noa_vif->bss_conf.beacon_int;
+
+       for (i = 0; i < MAX_BINDINGS; i++) {
+               u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color);
+               u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS;
+               u32 quota = le32_to_cpu(cmd->quotas[i].quota);
+
+               if (id != phy_id)
+                       continue;
+
+               quota *= (beacon_int - mvm->noa_duration);
+               quota /= beacon_int;
+
+               cmd->quotas[i].quota = cpu_to_le32(quota);
+       }
+#endif
+}
+
 int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 {
        struct iwl_time_quota_cmd cmd = {};
@@ -196,6 +226,8 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
        /* Give the remainder of the session to the first binding */
        le32_add_cpu(&cmd.quotas[0].quota, quota_rem);
 
+       iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
+
        ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
                                   sizeof(cmd), &cmd);
        if (ret)
index 4ffaa3fa153f78e7d14f2ad90223b01146562c50..a0b4cc8d9c3b412c931d04606d02b72c4d769e61 100644 (file)
@@ -82,13 +82,24 @@ static const u8 ant_toggle_lookup[] = {
        [ANT_ABC] = ANT_ABC,
 };
 
-#define IWL_DECLARE_RATE_INFO(r, s, rp, rn)                   \
-       [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,      \
-                                   IWL_RATE_SISO_##s##M_PLCP, \
-                                   IWL_RATE_MIMO2_##s##M_PLCP,\
-                                   IWL_RATE_##rp##M_INDEX,    \
+#define IWL_DECLARE_RATE_INFO(r, s, rp, rn)                          \
+       [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,             \
+                                   IWL_RATE_HT_SISO_MCS_##s##_PLCP,  \
+                                   IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \
+                                   IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \
+                                   IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\
+                                   IWL_RATE_##rp##M_INDEX,           \
                                    IWL_RATE_##rn##M_INDEX }
 
+#define IWL_DECLARE_MCS_RATE(s)                                                  \
+       [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP,                \
+                                      IWL_RATE_HT_SISO_MCS_##s##_PLCP,   \
+                                      IWL_RATE_HT_MIMO2_MCS_##s##_PLCP,  \
+                                      IWL_RATE_VHT_SISO_MCS_##s##_PLCP,  \
+                                      IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \
+                                      IWL_RATE_INVM_INDEX,               \
+                                      IWL_RATE_INVM_INDEX }
+
 /*
  * Parameter order:
  *   rate, ht rate, prev rate, next rate
@@ -102,16 +113,17 @@ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
        IWL_DECLARE_RATE_INFO(2, INV, 1, 5),     /*  2mbps */
        IWL_DECLARE_RATE_INFO(5, INV, 2, 11),    /*5.5mbps */
        IWL_DECLARE_RATE_INFO(11, INV, 9, 12),   /* 11mbps */
-       IWL_DECLARE_RATE_INFO(6, 6, 5, 11),      /*  6mbps */
-       IWL_DECLARE_RATE_INFO(9, 6, 6, 11),      /*  9mbps */
-       IWL_DECLARE_RATE_INFO(12, 12, 11, 18),   /* 12mbps */
-       IWL_DECLARE_RATE_INFO(18, 18, 12, 24),   /* 18mbps */
-       IWL_DECLARE_RATE_INFO(24, 24, 18, 36),   /* 24mbps */
-       IWL_DECLARE_RATE_INFO(36, 36, 24, 48),   /* 36mbps */
-       IWL_DECLARE_RATE_INFO(48, 48, 36, 54),   /* 48mbps */
-       IWL_DECLARE_RATE_INFO(54, 54, 48, INV),  /* 54mbps */
-       IWL_DECLARE_RATE_INFO(60, 60, 48, INV),  /* 60mbps */
-       /* FIXME:RS:          ^^    should be INV (legacy) */
+       IWL_DECLARE_RATE_INFO(6, 0, 5, 11),      /*  6mbps ; MCS 0 */
+       IWL_DECLARE_RATE_INFO(9, INV, 6, 11),    /*  9mbps */
+       IWL_DECLARE_RATE_INFO(12, 1, 11, 18),    /* 12mbps ; MCS 1 */
+       IWL_DECLARE_RATE_INFO(18, 2, 12, 24),    /* 18mbps ; MCS 2 */
+       IWL_DECLARE_RATE_INFO(24, 3, 18, 36),    /* 24mbps ; MCS 3 */
+       IWL_DECLARE_RATE_INFO(36, 4, 24, 48),    /* 36mbps ; MCS 4 */
+       IWL_DECLARE_RATE_INFO(48, 5, 36, 54),    /* 48mbps ; MCS 5 */
+       IWL_DECLARE_RATE_INFO(54, 6, 48, INV),   /* 54mbps ; MCS 6 */
+       IWL_DECLARE_MCS_RATE(7),                 /* MCS 7 */
+       IWL_DECLARE_MCS_RATE(8),                 /* MCS 8 */
+       IWL_DECLARE_MCS_RATE(9),                 /* MCS 9 */
 };
 
 static inline u8 rs_extract_rate(u32 rate_n_flags)
@@ -124,26 +136,30 @@ static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
 {
        int idx = 0;
 
-       /* HT rate format */
        if (rate_n_flags & RATE_MCS_HT_MSK) {
-               idx = rs_extract_rate(rate_n_flags);
-
-               WARN_ON_ONCE(idx >= IWL_RATE_MIMO3_6M_PLCP);
-               if (idx >= IWL_RATE_MIMO2_6M_PLCP)
-                       idx = idx - IWL_RATE_MIMO2_6M_PLCP;
+               idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK;
+               idx += IWL_RATE_MCS_0_INDEX;
 
-               idx += IWL_FIRST_OFDM_RATE;
-               /* skip 9M not supported in ht*/
+               /* skip 9M not supported in HT*/
                if (idx >= IWL_RATE_9M_INDEX)
                        idx += 1;
-               if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE))
+               if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE))
                        return idx;
+       } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+               idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+               idx += IWL_RATE_MCS_0_INDEX;
 
-       /* legacy rate format, search for match in table */
+               /* skip 9M not supported in VHT*/
+               if (idx >= IWL_RATE_9M_INDEX)
+                       idx++;
+               if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE))
+                       return idx;
        } else {
+               /* legacy rate format, search for match in table */
+
+               u8 legacy_rate = rs_extract_rate(rate_n_flags);
                for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++)
-                       if (iwl_rates[idx].plcp ==
-                                       rs_extract_rate(rate_n_flags))
+                       if (iwl_rates[idx].plcp == legacy_rate)
                                return idx;
        }
 
@@ -155,6 +171,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                                   struct ieee80211_sta *sta,
                                   struct iwl_lq_sta *lq_sta);
 static void rs_fill_link_cmd(struct iwl_mvm *mvm,
+                            struct ieee80211_sta *sta,
                             struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
 static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
 
@@ -180,35 +197,52 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
  */
 
 static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
-       7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
+       7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0
 };
 
-static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0, 42, 0,  76, 102, 124, 159, 183, 193, 202}, /* Norm */
-       {0, 0, 0, 0, 46, 0,  82, 110, 132, 168, 192, 202, 210}, /* SGI */
-       {0, 0, 0, 0, 47, 0,  91, 133, 171, 242, 305, 334, 362}, /* AGG */
-       {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */
+/* Expected TpT tables. 4 indexes:
+ * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI
+ */
+static s32 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 42, 0,  76, 102, 124, 159, 183, 193, 202, 216, 0},
+       {0, 0, 0, 0, 46, 0,  82, 110, 132, 168, 192, 202, 210, 225, 0},
+       {0, 0, 0, 0, 49, 0,  97, 145, 192, 285, 375, 420, 464, 551, 0},
+       {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0},
 };
 
-static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0,  77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
-       {0, 0, 0, 0,  83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
-       {0, 0, 0, 0,  94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */
-       {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */
+static s32 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0,  77, 0, 127, 160, 184, 220, 242, 250,  257,  269,  275},
+       {0, 0, 0, 0,  83, 0, 135, 169, 193, 229, 250, 257,  264,  275,  280},
+       {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828,  911, 1070, 1173},
+       {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284},
+};
+
+static s32 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 130, 0, 191, 223, 244,  273,  288,  294,  298,  305,  308},
+       {0, 0, 0, 0, 138, 0, 200, 231, 251,  279,  293,  298,  302,  308,  312},
+       {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466},
+       {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691},
 };
 
 static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0,  74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */
-       {0, 0, 0, 0,  81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */
-       {0, 0, 0, 0,  89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */
-       {0, 0, 0, 0,  97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/
+       {0, 0, 0, 0,  74, 0, 123, 155, 179, 213, 235, 243, 250,  261, 0},
+       {0, 0, 0, 0,  81, 0, 131, 164, 187, 221, 242, 250, 256,  267, 0},
+       {0, 0, 0, 0,  98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0},
+       {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0},
 };
 
 static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
-       {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
-       {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */
-       {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
+       {0, 0, 0, 0, 123, 0, 182, 214, 235,  264,  279,  285,  289,  296,  300},
+       {0, 0, 0, 0, 131, 0, 191, 222, 242,  270,  284,  289,  293,  300,  303},
+       {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053},
+       {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221},
+};
+
+static s32 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 182, 0, 240,  264,  278,  299,  308,  311,  313,  317,  319},
+       {0, 0, 0, 0, 190, 0, 247,  269,  282,  302,  310,  313,  315,  319,  320},
+       {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219},
+       {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545},
 };
 
 /* mbps, mcs */
@@ -263,7 +297,7 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm,
                       lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
 
        if (lq_sta->dbg_fixed_rate) {
-               rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
+               rs_fill_link_cmd(NULL, NULL, lq_sta, lq_sta->dbg_fixed_rate);
                iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false);
        }
 }
@@ -275,17 +309,6 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
 {
        int ret = -EAGAIN;
 
-       /*
-        * Don't create TX aggregation sessions when in high
-        * BT traffic, as they would just be disrupted by BT.
-        */
-       if (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) {
-               IWL_DEBUG_COEX(mvm, "BT traffic (%d), no aggregation allowed\n",
-                              BT_MBOX_MSG(&mvm->last_bt_notif,
-                                          3, TRAFFIC_LOAD));
-               return ret;
-       }
-
        IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
                     sta->addr, tid);
        ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
@@ -416,49 +439,54 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
  */
 /* FIXME:RS:remove this function and put the flags statically in the table */
 static u32 rate_n_flags_from_tbl(struct iwl_mvm *mvm,
-                                struct iwl_scale_tbl_info *tbl,
-                                int index, u8 use_green)
+                                struct iwl_scale_tbl_info *tbl, int index)
 {
        u32 rate_n_flags = 0;
 
+       rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) &
+                        RATE_MCS_ANT_ABC_MSK);
+
        if (is_legacy(tbl->lq_type)) {
-               rate_n_flags = iwl_rates[index].plcp;
+               rate_n_flags |= iwl_rates[index].plcp;
                if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
                        rate_n_flags |= RATE_MCS_CCK_MSK;
-       } else if (is_Ht(tbl->lq_type)) {
-               if (index > IWL_LAST_OFDM_RATE) {
+               return rate_n_flags;
+       }
+
+       if (is_ht(tbl->lq_type)) {
+               if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) {
                        IWL_ERR(mvm, "Invalid HT rate index %d\n", index);
-                       index = IWL_LAST_OFDM_RATE;
+                       index = IWL_LAST_HT_RATE;
                }
-               rate_n_flags = RATE_MCS_HT_MSK;
+               rate_n_flags |= RATE_MCS_HT_MSK;
 
-               if (is_siso(tbl->lq_type))
-                       rate_n_flags |= iwl_rates[index].plcp_siso;
-               else if (is_mimo2(tbl->lq_type))
-                       rate_n_flags |= iwl_rates[index].plcp_mimo2;
+               if (is_ht_siso(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_ht_siso;
+               else if (is_ht_mimo2(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_ht_mimo2;
                else
                        WARN_ON_ONCE(1);
+       } else if (is_vht(tbl->lq_type)) {
+               if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) {
+                       IWL_ERR(mvm, "Invalid VHT rate index %d\n", index);
+                       index = IWL_LAST_VHT_RATE;
+               }
+               rate_n_flags |= RATE_MCS_VHT_MSK;
+               if (is_vht_siso(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_vht_siso;
+               else if (is_vht_mimo2(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_vht_mimo2;
+               else
+                       WARN_ON_ONCE(1);
+
        } else {
                IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type);
        }
 
-       rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) &
-                                                    RATE_MCS_ANT_ABC_MSK);
-
-       if (is_Ht(tbl->lq_type)) {
-               if (tbl->is_ht40)
-                       rate_n_flags |= RATE_MCS_CHAN_WIDTH_40;
-               if (tbl->is_SGI)
-                       rate_n_flags |= RATE_MCS_SGI_MSK;
-
-               if (use_green) {
-                       rate_n_flags |= RATE_HT_MCS_GF_MSK;
-                       if (is_siso(tbl->lq_type) && tbl->is_SGI) {
-                               rate_n_flags &= ~RATE_MCS_SGI_MSK;
-                               IWL_ERR(mvm, "GF was set with SGI:SISO\n");
-                       }
-               }
-       }
+       rate_n_flags |= tbl->bw;
+       if (tbl->is_SGI)
+               rate_n_flags |= RATE_MCS_SGI_MSK;
+
        return rate_n_flags;
 }
 
@@ -473,7 +501,7 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
 {
        u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK);
        u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
-       u8 mcs;
+       u8 nss;
 
        memset(tbl, 0, offsetof(struct iwl_scale_tbl_info, win));
        *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
@@ -483,41 +511,62 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
                return -EINVAL;
        }
        tbl->is_SGI = 0;        /* default legacy setup */
-       tbl->is_ht40 = 0;
+       tbl->bw = 0;
        tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS);
        tbl->lq_type = LQ_NONE;
        tbl->max_search = IWL_MAX_SEARCH;
 
-       /* legacy rate format */
-       if (!(rate_n_flags & RATE_MCS_HT_MSK)) {
+       /* Legacy */
+       if (!(rate_n_flags & RATE_MCS_HT_MSK) &&
+           !(rate_n_flags & RATE_MCS_VHT_MSK)) {
                if (num_of_ant == 1) {
                        if (band == IEEE80211_BAND_5GHZ)
-                               tbl->lq_type = LQ_A;
+                               tbl->lq_type = LQ_LEGACY_A;
                        else
-                               tbl->lq_type = LQ_G;
+                               tbl->lq_type = LQ_LEGACY_G;
                }
-       /* HT rate format */
-       } else {
-               if (rate_n_flags & RATE_MCS_SGI_MSK)
-                       tbl->is_SGI = 1;
-
-               if (rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */
-                       tbl->is_ht40 = 1;
-
-               mcs = rs_extract_rate(rate_n_flags);
-
-               /* SISO */
-               if (mcs <= IWL_RATE_SISO_60M_PLCP) {
-                       if (num_of_ant == 1)
-                               tbl->lq_type = LQ_SISO; /*else NONE*/
-               /* MIMO2 */
-               } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) {
-                       if (num_of_ant == 2)
-                               tbl->lq_type = LQ_MIMO2;
+
+               return 0;
+       }
+
+       /* HT or VHT */
+       if (rate_n_flags & RATE_MCS_SGI_MSK)
+               tbl->is_SGI = 1;
+
+       tbl->bw = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK;
+
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               nss = ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >>
+                      RATE_HT_MCS_NSS_POS) + 1;
+
+               if (nss == 1) {
+                       tbl->lq_type = LQ_HT_SISO;
+                       WARN_ON_ONCE(num_of_ant != 1);
+               } else if (nss == 2) {
+                       tbl->lq_type = LQ_HT_MIMO2;
+                       WARN_ON_ONCE(num_of_ant != 2);
+               } else {
+                       WARN_ON_ONCE(1);
+               }
+       } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+               nss = ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+                      RATE_VHT_MCS_NSS_POS) + 1;
+
+               if (nss == 1) {
+                       tbl->lq_type = LQ_VHT_SISO;
+                       WARN_ON_ONCE(num_of_ant != 1);
+               } else if (nss == 2) {
+                       tbl->lq_type = LQ_VHT_MIMO2;
+                       WARN_ON_ONCE(num_of_ant != 2);
                } else {
-                       WARN_ON_ONCE(num_of_ant == 3);
+                       WARN_ON_ONCE(1);
                }
        }
+
+       WARN_ON_ONCE(tbl->bw == RATE_MCS_CHAN_WIDTH_160);
+       WARN_ON_ONCE(tbl->bw == RATE_MCS_CHAN_WIDTH_80 &&
+                    !is_vht(tbl->lq_type));
+
        return 0;
 }
 
@@ -549,22 +598,6 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
        return 1;
 }
 
-/**
- * Green-field mode is valid if the station supports it and
- * there are no non-GF stations present in the BSS.
- */
-static bool rs_use_green(struct ieee80211_sta *sta)
-{
-       /*
-        * There's a bug somewhere in this code that causes the
-        * scaling to get stuck because GF+SGI can't be combined
-        * in SISO rates. Until we find that bug, disable GF, it
-        * has only limited benefit and we still interoperate with
-        * GF APs since we can always receive GF transmissions.
-        */
-       return false;
-}
-
 /**
  * rs_get_supported_rates - get the available rates
  *
@@ -576,16 +609,15 @@ static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta,
                                  struct ieee80211_hdr *hdr,
                                  enum iwl_table_type rate_type)
 {
-       if (is_legacy(rate_type)) {
+       if (is_legacy(rate_type))
                return lq_sta->active_legacy_rate;
-       } else {
-               if (is_siso(rate_type))
-                       return lq_sta->active_siso_rate;
-               else {
-                       WARN_ON_ONCE(!is_mimo2(rate_type));
-                       return lq_sta->active_mimo2_rate;
-               }
-       }
+       else if (is_siso(rate_type))
+               return lq_sta->active_siso_rate;
+       else if (is_mimo2(rate_type))
+               return lq_sta->active_mimo2_rate;
+
+       WARN_ON_ONCE(1);
+       return 0;
 }
 
 static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask,
@@ -652,7 +684,6 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
        u16 rate_mask;
        u16 high_low;
        u8 switch_to_legacy = 0;
-       u8 is_green = lq_sta->is_green;
        struct iwl_mvm *mvm = lq_sta->drv;
 
        /* check if we need to switch from HT to legacy rates.
@@ -662,15 +693,15 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
                switch_to_legacy = 1;
                scale_index = rs_ht_to_legacy[scale_index];
                if (lq_sta->band == IEEE80211_BAND_5GHZ)
-                       tbl->lq_type = LQ_A;
+                       tbl->lq_type = LQ_LEGACY_A;
                else
-                       tbl->lq_type = LQ_G;
+                       tbl->lq_type = LQ_LEGACY_G;
 
                if (num_of_ant(tbl->ant_type) > 1)
                        tbl->ant_type =
                            first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
 
-               tbl->is_ht40 = 0;
+               tbl->bw = 0;
                tbl->is_SGI = 0;
                tbl->max_search = IWL_MAX_SEARCH;
        }
@@ -701,7 +732,7 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
                low = scale_index;
 
 out:
-       return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green);
+       return rate_n_flags_from_tbl(lq_sta->drv, tbl, low);
 }
 
 /*
@@ -714,6 +745,18 @@ static bool table_type_matches(struct iwl_scale_tbl_info *a,
                (a->is_SGI == b->is_SGI);
 }
 
+static u32 rs_ch_width_from_mac_flags(enum mac80211_rate_control_flags flags)
+{
+       if (flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+               return RATE_MCS_CHAN_WIDTH_40;
+       else if (flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+               return RATE_MCS_CHAN_WIDTH_80;
+       else if (flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+               return RATE_MCS_CHAN_WIDTH_160;
+
+       return RATE_MCS_CHAN_WIDTH_20;
+}
+
 /*
  * mac80211 sends us Tx status
  */
@@ -783,16 +826,23 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
                 */
                if (info->band == IEEE80211_BAND_2GHZ)
                        mac_index += IWL_FIRST_OFDM_RATE;
+       } else if (mac_flags & IEEE80211_TX_RC_VHT_MCS) {
+               mac_index &= RATE_VHT_MCS_RATE_CODE_MSK;
+               if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE))
+                       mac_index++;
        }
+
        /* Here we actually compare this rate to the latest LQ command */
        if ((mac_index < 0) ||
            (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
-           (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
+           (tbl_type.bw != rs_ch_width_from_mac_flags(mac_flags)) ||
            (tbl_type.ant_type != info->status.antenna) ||
            (!!(tx_rate & RATE_MCS_HT_MSK) !=
-                               !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
+            !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
+           (!!(tx_rate & RATE_MCS_VHT_MSK) !=
+            !!(mac_flags & IEEE80211_TX_RC_VHT_MCS)) ||
            (!!(tx_rate & RATE_HT_MCS_GF_MSK) !=
-                               !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
+            !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
            (rs_index != mac_index)) {
                IWL_DEBUG_RATE(mvm,
                               "initial rate %d does not match %d (0x%x)\n",
@@ -947,7 +997,8 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
        s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
 
        /* Check for invalid LQ type */
-       if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
+       if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_ht(tbl->lq_type) &&
+                        !(is_vht(tbl->lq_type)))) {
                tbl->expected_tpt = expected_tpt_legacy;
                return;
        }
@@ -958,18 +1009,40 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
                return;
        }
 
+       ht_tbl_pointer = expected_tpt_mimo2_20MHz;
        /* Choose among many HT tables depending on number of streams
-        * (SISO/MIMO2), channel width (20/40), SGI, and aggregation
+        * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation
         * status */
-       if (is_siso(tbl->lq_type) && !tbl->is_ht40)
-               ht_tbl_pointer = expected_tpt_siso20MHz;
-       else if (is_siso(tbl->lq_type))
-               ht_tbl_pointer = expected_tpt_siso40MHz;
-       else if (is_mimo2(tbl->lq_type) && !tbl->is_ht40)
-               ht_tbl_pointer = expected_tpt_mimo2_20MHz;
-       else {
-               WARN_ON_ONCE(!is_mimo2(tbl->lq_type));
-               ht_tbl_pointer = expected_tpt_mimo2_40MHz;
+       if (is_siso(tbl->lq_type)) {
+               switch (tbl->bw) {
+               case RATE_MCS_CHAN_WIDTH_20:
+                       ht_tbl_pointer = expected_tpt_siso_20MHz;
+                       break;
+               case RATE_MCS_CHAN_WIDTH_40:
+                       ht_tbl_pointer = expected_tpt_siso_40MHz;
+                       break;
+               case RATE_MCS_CHAN_WIDTH_80:
+                       ht_tbl_pointer = expected_tpt_siso_80MHz;
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+               }
+       } else if (is_mimo2(tbl->lq_type)) {
+               switch (tbl->bw) {
+               case RATE_MCS_CHAN_WIDTH_20:
+                       ht_tbl_pointer = expected_tpt_mimo2_20MHz;
+                       break;
+               case RATE_MCS_CHAN_WIDTH_40:
+                       ht_tbl_pointer = expected_tpt_mimo2_40MHz;
+                       break;
+               case RATE_MCS_CHAN_WIDTH_80:
+                       ht_tbl_pointer = expected_tpt_mimo2_80MHz;
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+               }
+       } else {
+               WARN_ON_ONCE(1);
        }
 
        if (!tbl->is_SGI && !lq_sta->is_agg)            /* Normal */
@@ -1084,9 +1157,47 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm,
        return new_rate;
 }
 
-static bool iwl_is_ht40_tx_allowed(struct ieee80211_sta *sta)
+/* Move to the next action and wrap around to the first action in case
+ * we're at the last action. Assumes actions start at 0.
+ */
+static inline void rs_move_next_action(struct iwl_scale_tbl_info *tbl,
+                                      u8 last_action)
+{
+       BUILD_BUG_ON(IWL_LEGACY_FIRST_ACTION != 0);
+       BUILD_BUG_ON(IWL_SISO_FIRST_ACTION != 0);
+       BUILD_BUG_ON(IWL_MIMO2_FIRST_ACTION != 0);
+
+       tbl->action = (tbl->action + 1) % (last_action + 1);
+}
+
+static void rs_set_bw_from_sta(struct iwl_scale_tbl_info *tbl,
+                              struct ieee80211_sta *sta)
+{
+       if (sta->bandwidth >= IEEE80211_STA_RX_BW_80)
+               tbl->bw = RATE_MCS_CHAN_WIDTH_80;
+       else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40)
+               tbl->bw = RATE_MCS_CHAN_WIDTH_40;
+       else
+               tbl->bw = RATE_MCS_CHAN_WIDTH_20;
+}
+
+static bool rs_sgi_allowed(struct iwl_scale_tbl_info *tbl,
+                          struct ieee80211_sta *sta)
 {
-       return sta->bandwidth >= IEEE80211_STA_RX_BW_40;
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+
+       if (is_ht20(tbl) && (ht_cap->cap &
+                            IEEE80211_HT_CAP_SGI_20))
+               return true;
+       if (is_ht40(tbl) && (ht_cap->cap &
+                            IEEE80211_HT_CAP_SGI_40))
+               return true;
+       if (is_ht80(tbl) && (vht_cap->cap &
+                            IEEE80211_VHT_CAP_SHORT_GI_80))
+               return true;
+
+       return false;
 }
 
 /*
@@ -1099,7 +1210,6 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
 {
        u16 rate_mask;
        s32 rate;
-       s8 is_green = lq_sta->is_green;
 
        if (!sta->ht_cap.ht_supported)
                return -1;
@@ -1113,16 +1223,12 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
 
        IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO2\n");
 
-       tbl->lq_type = LQ_MIMO2;
+       tbl->lq_type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2;
        tbl->action = 0;
        tbl->max_search = IWL_MAX_SEARCH;
        rate_mask = lq_sta->active_mimo2_rate;
 
-       if (iwl_is_ht40_tx_allowed(sta))
-               tbl->is_ht40 = 1;
-       else
-               tbl->is_ht40 = 0;
-
+       rs_set_bw_from_sta(tbl, sta);
        rs_set_expected_tpt_table(lq_sta, tbl);
 
        rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
@@ -1134,10 +1240,10 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
                               rate, rate_mask);
                return -1;
        }
-       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate);
 
-       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
-                      tbl->current_rate, is_green);
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index\n",
+                      tbl->current_rate);
        return 0;
 }
 
@@ -1150,7 +1256,6 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm,
                             struct iwl_scale_tbl_info *tbl, int index)
 {
        u16 rate_mask;
-       u8 is_green = lq_sta->is_green;
        s32 rate;
 
        if (!sta->ht_cap.ht_supported)
@@ -1158,19 +1263,12 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm,
 
        IWL_DEBUG_RATE(mvm, "LQ: try to switch to SISO\n");
 
-       tbl->lq_type = LQ_SISO;
+       tbl->lq_type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO;
        tbl->action = 0;
        tbl->max_search = IWL_MAX_SEARCH;
        rate_mask = lq_sta->active_siso_rate;
 
-       if (iwl_is_ht40_tx_allowed(sta))
-               tbl->is_ht40 = 1;
-       else
-               tbl->is_ht40 = 0;
-
-       if (is_green)
-               tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/
-
+       rs_set_bw_from_sta(tbl, sta);
        rs_set_expected_tpt_table(lq_sta, tbl);
        rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
 
@@ -1181,9 +1279,9 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm,
                               rate, rate_mask);
                return -1;
        }
-       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
-       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
-                      tbl->current_rate, is_green);
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate);
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index\n",
+                      tbl->current_rate);
        return 0;
 }
 
@@ -1211,14 +1309,10 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
        while (1) {
                lq_sta->action_counter++;
                switch (tbl->action) {
-               case IWL_LEGACY_SWITCH_ANTENNA1:
-               case IWL_LEGACY_SWITCH_ANTENNA2:
+               case IWL_LEGACY_SWITCH_ANTENNA:
                        IWL_DEBUG_RATE(mvm, "LQ: Legacy toggle Antenna\n");
 
-                       if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 &&
-                            tx_chains_num <= 1) ||
-                           (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 &&
-                            tx_chains_num <= 2))
+                       if (tx_chains_num <= 1)
                                break;
 
                        /* Don't change antenna if success has been great */
@@ -1273,9 +1367,7 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
                default:
                        WARN_ON_ONCE(1);
                }
-               tbl->action++;
-               if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
-                       tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+               rs_move_next_action(tbl, IWL_LEGACY_LAST_ACTION);
 
                if (tbl->action == start_action)
                        break;
@@ -1285,9 +1377,7 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
 
 out:
        lq_sta->search_better_tbl = 1;
-       tbl->action++;
-       if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
-               tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+       rs_move_next_action(tbl, IWL_LEGACY_LAST_ACTION);
        if (update_search_tbl_counter)
                search_tbl->action = tbl->action;
        return 0;
@@ -1300,12 +1390,10 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
                                 struct iwl_lq_sta *lq_sta,
                                 struct ieee80211_sta *sta, int index)
 {
-       u8 is_green = lq_sta->is_green;
        struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
        struct iwl_scale_tbl_info *search_tbl =
                                &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
        struct iwl_rate_scale_data *window = &(tbl->win[index]);
-       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
        u32 sz = (sizeof(struct iwl_scale_tbl_info) -
                  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
        u8 start_action;
@@ -1314,40 +1402,17 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
        u8 update_search_tbl_counter = 0;
        int ret;
 
-       switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
-       case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
-               /* nothing */
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
-               /* avoid antenna B unless MIMO */
-               if (tbl->action == IWL_SISO_SWITCH_ANTENNA2)
-                       tbl->action = IWL_SISO_SWITCH_MIMO2;
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
-       case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
-               /* avoid antenna B and MIMO */
-               valid_tx_ant =
-                       first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
-               if (tbl->action != IWL_SISO_SWITCH_ANTENNA1)
-                       tbl->action = IWL_SISO_SWITCH_ANTENNA1;
-               break;
-       default:
-               IWL_ERR(mvm, "Invalid BT load %d",
-                       BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
-               break;
-       }
+       if (tbl->action == IWL_SISO_SWITCH_MIMO2 &&
+           !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
+               tbl->action = IWL_SISO_SWITCH_ANTENNA;
 
        start_action = tbl->action;
        while (1) {
                lq_sta->action_counter++;
                switch (tbl->action) {
-               case IWL_SISO_SWITCH_ANTENNA1:
-               case IWL_SISO_SWITCH_ANTENNA2:
+               case IWL_SISO_SWITCH_ANTENNA:
                        IWL_DEBUG_RATE(mvm, "LQ: SISO toggle Antenna\n");
-                       if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 &&
-                            tx_chains_num <= 1) ||
-                           (tbl->action == IWL_SISO_SWITCH_ANTENNA2 &&
-                            tx_chains_num <= 2))
+                       if (tx_chains_num <= 1)
                                break;
 
                        if (window->success_ratio >= IWL_RS_GOOD_RATIO &&
@@ -1380,23 +1445,12 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
                                goto out;
                        break;
                case IWL_SISO_SWITCH_GI:
-                       if (!tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_20))
-                               break;
-                       if (tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_40))
+                       if (!rs_sgi_allowed(tbl, sta))
                                break;
 
                        IWL_DEBUG_RATE(mvm, "LQ: SISO toggle SGI/NGI\n");
 
                        memcpy(search_tbl, tbl, sz);
-                       if (is_green) {
-                               if (!tbl->is_SGI)
-                                       break;
-                               else
-                                       IWL_ERR(mvm,
-                                               "SGI was set in GF+SISO\n");
-                       }
                        search_tbl->is_SGI = !tbl->is_SGI;
                        rs_set_expected_tpt_table(lq_sta, search_tbl);
                        if (tbl->is_SGI) {
@@ -1405,16 +1459,13 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
                                        break;
                        }
                        search_tbl->current_rate =
-                               rate_n_flags_from_tbl(mvm, search_tbl,
-                                                     index, is_green);
+                               rate_n_flags_from_tbl(mvm, search_tbl, index);
                        update_search_tbl_counter = 1;
                        goto out;
                default:
                        WARN_ON_ONCE(1);
                }
-               tbl->action++;
-               if (tbl->action > IWL_SISO_SWITCH_GI)
-                       tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+               rs_move_next_action(tbl, IWL_SISO_LAST_ACTION);
 
                if (tbl->action == start_action)
                        break;
@@ -1424,9 +1475,7 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
 
  out:
        lq_sta->search_better_tbl = 1;
-       tbl->action++;
-       if (tbl->action > IWL_SISO_SWITCH_GI)
-               tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+       rs_move_next_action(tbl, IWL_SISO_LAST_ACTION);
        if (update_search_tbl_counter)
                search_tbl->action = tbl->action;
 
@@ -1440,63 +1489,20 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
                                 struct iwl_lq_sta *lq_sta,
                                 struct ieee80211_sta *sta, int index)
 {
-       s8 is_green = lq_sta->is_green;
        struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
        struct iwl_scale_tbl_info *search_tbl =
                                &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
-       struct iwl_rate_scale_data *window = &(tbl->win[index]);
-       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
        u32 sz = (sizeof(struct iwl_scale_tbl_info) -
                  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
        u8 start_action;
        u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
-       u8 tx_chains_num = num_of_ant(valid_tx_ant);
        u8 update_search_tbl_counter = 0;
        int ret;
 
-       switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
-       case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
-               /* nothing */
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
-       case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
-               /* avoid antenna B and MIMO */
-               if (tbl->action != IWL_MIMO2_SWITCH_SISO_A)
-                       tbl->action = IWL_MIMO2_SWITCH_SISO_A;
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
-               /* avoid antenna B unless MIMO */
-               if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
-                       tbl->action = IWL_MIMO2_SWITCH_SISO_A;
-               break;
-       default:
-               IWL_ERR(mvm, "Invalid BT load %d",
-                       BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
-               break;
-       }
-
        start_action = tbl->action;
        while (1) {
                lq_sta->action_counter++;
                switch (tbl->action) {
-               case IWL_MIMO2_SWITCH_ANTENNA1:
-               case IWL_MIMO2_SWITCH_ANTENNA2:
-                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle Antennas\n");
-
-                       if (tx_chains_num <= 2)
-                               break;
-
-                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
-                               break;
-
-                       memcpy(search_tbl, tbl, sz);
-                       if (rs_toggle_antenna(valid_tx_ant,
-                                             &search_tbl->current_rate,
-                                             search_tbl)) {
-                               update_search_tbl_counter = 1;
-                               goto out;
-                       }
-                       break;
                case IWL_MIMO2_SWITCH_SISO_A:
                case IWL_MIMO2_SWITCH_SISO_B:
                        IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n");
@@ -1521,11 +1527,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
                        break;
 
                case IWL_MIMO2_SWITCH_GI:
-                       if (!tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_20))
-                               break;
-                       if (tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_40))
+                       if (!rs_sgi_allowed(tbl, sta))
                                break;
 
                        IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle SGI/NGI\n");
@@ -1546,16 +1548,13 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
                                        break;
                        }
                        search_tbl->current_rate =
-                               rate_n_flags_from_tbl(mvm, search_tbl,
-                                                     index, is_green);
+                               rate_n_flags_from_tbl(mvm, search_tbl, index);
                        update_search_tbl_counter = 1;
                        goto out;
                default:
                        WARN_ON_ONCE(1);
                }
-               tbl->action++;
-               if (tbl->action > IWL_MIMO2_SWITCH_GI)
-                       tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+               rs_move_next_action(tbl, IWL_MIMO2_LAST_ACTION);
 
                if (tbl->action == start_action)
                        break;
@@ -1564,9 +1563,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
        return 0;
  out:
        lq_sta->search_better_tbl = 1;
-       tbl->action++;
-       if (tbl->action > IWL_MIMO2_SWITCH_GI)
-               tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+       rs_move_next_action(tbl, IWL_MIMO2_LAST_ACTION);
        if (update_search_tbl_counter)
                search_tbl->action = tbl->action;
 
@@ -1660,15 +1657,16 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
  * setup rate table in uCode
  */
 static void rs_update_rate_tbl(struct iwl_mvm *mvm,
+                              struct ieee80211_sta *sta,
                               struct iwl_lq_sta *lq_sta,
                               struct iwl_scale_tbl_info *tbl,
-                              int index, u8 is_green)
+                              int index)
 {
        u32 rate;
 
        /* Update uCode's rate table. */
-       rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green);
-       rs_fill_link_cmd(mvm, lq_sta, rate);
+       rate = rate_n_flags_from_tbl(mvm, tbl, index);
+       rs_fill_link_cmd(mvm, sta, lq_sta, rate);
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
 }
 
@@ -1712,7 +1710,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        u8 update_lq = 0;
        struct iwl_scale_tbl_info *tbl, *tbl1;
        u16 rate_scale_index_msk = 0;
-       u8 is_green = 0;
        u8 active_tbl = 0;
        u8 done_search = 0;
        u16 high_low;
@@ -1754,11 +1751,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                active_tbl = 1 - lq_sta->active_tbl;
 
        tbl = &(lq_sta->lq_info[active_tbl]);
-       if (is_legacy(tbl->lq_type))
-               lq_sta->is_green = 0;
-       else
-               lq_sta->is_green = rs_use_green(sta);
-       is_green = lq_sta->is_green;
 
        /* current tx rate */
        index = lq_sta->last_txrate_idx;
@@ -1797,7 +1789,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                        tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
                        /* get "active" rate info */
                        index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
-                       rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green);
+                       rs_update_rate_tbl(mvm, sta, lq_sta, tbl, index);
                }
                return;
        }
@@ -1978,24 +1970,24 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
             (current_tpt > (100 * tbl->expected_tpt[low]))))
                scale_action = 0;
 
-       if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
+       if ((le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) >=
             IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && (is_mimo(tbl->lq_type))) {
                if (lq_sta->last_bt_traffic >
-                   BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+                   le32_to_cpu(mvm->last_bt_notif.bt_activity_grading)) {
                        /*
                         * don't set scale_action, don't want to scale up if
                         * the rate scale doesn't otherwise think that is a
                         * good idea.
                         */
                } else if (lq_sta->last_bt_traffic <=
-                          BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+                          le32_to_cpu(mvm->last_bt_notif.bt_activity_grading)) {
                        scale_action = -1;
                }
        }
        lq_sta->last_bt_traffic =
-               BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD);
+               le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
 
-       if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
+       if ((le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) >=
             IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && is_mimo(tbl->lq_type)) {
                /* search for a new modulation */
                rs_stay_in_table(lq_sta, true);
@@ -2032,7 +2024,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
 lq_update:
        /* Replace uCode's rate table for the destination station. */
        if (update_lq)
-               rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green);
+               rs_update_rate_tbl(mvm, sta, lq_sta, tbl, index);
 
        rs_stay_in_table(lq_sta, false);
 
@@ -2071,7 +2063,7 @@ lq_update:
                        IWL_DEBUG_RATE(mvm,
                                       "Switch current  mcs: %X index: %d\n",
                                       tbl->current_rate, index);
-                       rs_fill_link_cmd(mvm, lq_sta, tbl->current_rate);
+                       rs_fill_link_cmd(mvm, sta, lq_sta, tbl->current_rate);
                        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
                } else {
                        done_search = 1;
@@ -2113,7 +2105,7 @@ lq_update:
        }
 
 out:
-       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green);
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index);
        lq_sta->last_txrate_idx = index;
 }
 
@@ -2140,7 +2132,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        int rate_idx;
        int i;
        u32 rate;
-       u8 use_green = rs_use_green(sta);
        u8 active_tbl = 0;
        u8 valid_tx_ant;
 
@@ -2172,10 +2163,10 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type))
                rs_toggle_antenna(valid_tx_ant, &rate, tbl);
 
-       rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx, use_green);
+       rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx);
        tbl->current_rate = rate;
        rs_set_expected_tpt_table(lq_sta, tbl);
-       rs_fill_link_cmd(NULL, lq_sta, rate);
+       rs_fill_link_cmd(NULL, NULL, lq_sta, rate);
        /* TODO restore station should remember the lq cmd */
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_SYNC, true);
 }
@@ -2190,7 +2181,6 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
        struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct iwl_lq_sta *lq_sta = mvm_sta;
-       int rate_idx;
 
        IWL_DEBUG_RATE_LIMIT(mvm, "rate scale calculate new rate for skb\n");
 
@@ -2215,36 +2205,9 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
        if (rate_control_send_low(sta, mvm_sta, txrc))
                return;
 
-       rate_idx  = lq_sta->last_txrate_idx;
-
-       if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
-               rate_idx -= IWL_FIRST_OFDM_RATE;
-               /* 6M and 9M shared same MCS index */
-               rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0;
-               WARN_ON_ONCE(rs_extract_rate(lq_sta->last_rate_n_flags) >=
-                            IWL_RATE_MIMO3_6M_PLCP);
-               if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
-                   IWL_RATE_MIMO2_6M_PLCP)
-                       rate_idx = rate_idx + MCS_INDEX_PER_STREAM;
-               info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
-               if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK)
-                       info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI;
-               if (lq_sta->last_rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */
-                       info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
-               if (lq_sta->last_rate_n_flags & RATE_HT_MCS_GF_MSK)
-                       info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD;
-       } else {
-               /* Check for invalid rates */
-               if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) ||
-                   ((sband->band == IEEE80211_BAND_5GHZ) &&
-                    (rate_idx < IWL_FIRST_OFDM_RATE)))
-                       rate_idx = rate_lowest_index(sband, sta);
-               /* On valid 5 GHz rate, adjust index */
-               else if (sband->band == IEEE80211_BAND_5GHZ)
-                       rate_idx -= IWL_FIRST_OFDM_RATE;
-               info->control.rates[0].flags = 0;
-       }
-       info->control.rates[0].idx = rate_idx;
+       iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags,
+                                 info->band, &info->control.rates[0]);
+
        info->control.rates[0].count = 1;
 }
 
@@ -2261,6 +2224,24 @@ static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
        return &sta_priv->lq_sta;
 }
 
+static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap,
+                                      int nss)
+{
+       u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
+               (0x3 << (2 * (nss - 1)));
+       rx_mcs >>= (2 * (nss - 1));
+
+       if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7)
+               return IWL_RATE_MCS_7_INDEX;
+       else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8)
+               return IWL_RATE_MCS_8_INDEX;
+       else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9)
+               return IWL_RATE_MCS_9_INDEX;
+
+       WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED);
+       return -1;
+}
+
 /*
  * Called after adding a new station to initialize rate scaling
  */
@@ -2270,6 +2251,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        int i, j;
        struct ieee80211_hw *hw = mvm->hw;
        struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
        struct iwl_mvm_sta *sta_priv;
        struct iwl_lq_sta *lq_sta;
        struct ieee80211_supported_band *sband;
@@ -2298,7 +2280,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        lq_sta->max_rate_idx = -1;
        lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
-       lq_sta->is_green = rs_use_green(sta);
        lq_sta->band = sband->band;
        /*
         * active legacy rates as per supported rates bitmap
@@ -2308,25 +2289,54 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        for_each_set_bit(i, &supp, BITS_PER_LONG)
                lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value);
 
-       /*
-        * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3),
-        * supp_rates[] does not; shift to convert format, force 9 MBits off.
-        */
-       lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
-       lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
-       lq_sta->active_siso_rate &= ~((u16)0x2);
-       lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+       /* TODO: should probably account for rx_highest for both HT/VHT */
+       if (!vht_cap || !vht_cap->vht_supported) {
+               /* active_siso_rate mask includes 9 MBits (bit 5),
+                * and CCK (bits 0-3), supp_rates[] does not;
+                * shift to convert format, force 9 MBits off.
+                */
+               lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
+               lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
+               lq_sta->active_siso_rate &= ~((u16)0x2);
+               lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+
+               /* Same here */
+               lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
+               lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
+               lq_sta->active_mimo2_rate &= ~((u16)0x2);
+               lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
+
+               lq_sta->is_vht = false;
+       } else {
+               int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1);
+               if (highest_mcs >= IWL_RATE_MCS_0_INDEX) {
+                       for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) {
+                               if (i == IWL_RATE_9M_INDEX)
+                                       continue;
+
+                               lq_sta->active_siso_rate |= BIT(i);
+                       }
+               }
+
+               highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2);
+               if (highest_mcs >= IWL_RATE_MCS_0_INDEX) {
+                       for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) {
+                               if (i == IWL_RATE_9M_INDEX)
+                                       continue;
 
-       /* Same here */
-       lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
-       lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
-       lq_sta->active_mimo2_rate &= ~((u16)0x2);
-       lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
+                               lq_sta->active_mimo2_rate |= BIT(i);
+                       }
+               }
+
+               /* TODO: avoid MCS9 in 20Mhz which isn't valid for 11ac */
+               lq_sta->is_vht = true;
+       }
 
        IWL_DEBUG_RATE(mvm,
-                      "SISO-RATE=%X MIMO2-RATE=%X\n",
+                      "SISO-RATE=%X MIMO2-RATE=%X VHT=%d\n",
                       lq_sta->active_siso_rate,
-                      lq_sta->active_mimo2_rate);
+                      lq_sta->active_mimo2_rate,
+                      lq_sta->is_vht);
 
        /* These values will be overridden later */
        lq_sta->lq.single_stream_ant_msk =
@@ -2358,6 +2368,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 }
 
 static void rs_fill_link_cmd(struct iwl_mvm *mvm,
+                            struct ieee80211_sta *sta,
                             struct iwl_lq_sta *lq_sta, u32 new_rate)
 {
        struct iwl_scale_tbl_info tbl_type;
@@ -2429,7 +2440,6 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
                rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type,
                                         &rate_idx);
 
-
                /* Indicate to uCode which entries might be MIMO.
                 * If initial rate was MIMO, this will finally end up
                 * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */
@@ -2455,7 +2465,9 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
                }
 
                /* Don't allow HT rates after next pass.
-                * rs_get_lower_rate() will change type to LQ_A or LQ_G. */
+                * rs_get_lower_rate() will change type to LQ_LEGACY_A
+                * or LQ_LEGACY_G.
+                */
                use_ht_possible = 0;
 
                /* Override next rate if needed for debug purposes */
@@ -2474,12 +2486,9 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
        lq_cmd->agg_time_limit =
                cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
 
-       /*
-        * overwrite if needed, pass aggregation time limit
-        * to uCode in uSec - This is racy - but heh, at least it helps...
-        */
-       if (mvm && BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2)
-               lq_cmd->agg_time_limit = cpu_to_le16(1200);
+       if (sta)
+               lq_cmd->agg_time_limit =
+                       cpu_to_le16(iwl_mvm_bt_coex_agg_time_limit(mvm, sta));
 }
 
 static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
@@ -2586,16 +2595,18 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
            (iwl_fw_valid_tx_ant(mvm->fw) & ANT_B) ? "ANT_B," : "",
            (iwl_fw_valid_tx_ant(mvm->fw) & ANT_C) ? "ANT_C" : "");
        desc += sprintf(buff+desc, "lq type %s\n",
-          (is_legacy(tbl->lq_type)) ? "legacy" : "HT");
-       if (is_Ht(tbl->lq_type)) {
+                       (is_legacy(tbl->lq_type)) ? "legacy" :
+                       is_vht(tbl->lq_type) ? "VHT" : "HT");
+       if (is_ht(tbl->lq_type)) {
                desc += sprintf(buff+desc, " %s",
                   (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2");
                   desc += sprintf(buff+desc, " %s",
-                  (tbl->is_ht40) ? "40MHz" : "20MHz");
-                  desc += sprintf(buff+desc, " %s %s %s\n",
+                                  (is_ht20(tbl)) ? "20MHz" :
+                                  (is_ht40(tbl)) ? "40MHz" :
+                                  (is_ht80(tbl)) ? "80Mhz" : "BAD BW");
+                  desc += sprintf(buff+desc, " %s %s\n",
                                   (tbl->is_SGI) ? "SGI" : "",
-                  (lq_sta->is_green) ? "GF enabled" : "",
-                  (lq_sta->is_agg) ? "AGG on" : "");
+                                  (lq_sta->is_agg) ? "AGG on" : "");
        }
        desc += sprintf(buff+desc, "last tx rate=0x%X\n",
                        lq_sta->last_rate_n_flags);
@@ -2653,7 +2664,7 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
        int desc = 0;
        int i, j;
        ssize_t ret;
-
+       struct iwl_scale_tbl_info *tbl;
        struct iwl_lq_sta *lq_sta = file->private_data;
 
        buff = kmalloc(1024, GFP_KERNEL);
@@ -2661,21 +2672,23 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
                return -ENOMEM;
 
        for (i = 0; i < LQ_SIZE; i++) {
+               tbl = &(lq_sta->lq_info[i]);
                desc += sprintf(buff+desc,
-                               "%s type=%d SGI=%d HT40=%d DUP=0 GF=%d\n"
+                               "%s type=%d SGI=%d BW=%s DUP=0\n"
                                "rate=0x%X\n",
                                lq_sta->active_tbl == i ? "*" : "x",
-                               lq_sta->lq_info[i].lq_type,
-                               lq_sta->lq_info[i].is_SGI,
-                               lq_sta->lq_info[i].is_ht40,
-                               lq_sta->is_green,
-                               lq_sta->lq_info[i].current_rate);
+                               tbl->lq_type,
+                               tbl->is_SGI,
+                               is_ht20(tbl) ? "20Mhz" :
+                               is_ht40(tbl) ? "40Mhz" :
+                               is_ht80(tbl) ? "80Mhz" : "ERR",
+                               tbl->current_rate);
                for (j = 0; j < IWL_RATE_COUNT; j++) {
                        desc += sprintf(buff+desc,
                                "counter=%d success=%d %%=%d\n",
-                               lq_sta->lq_info[i].win[j].counter,
-                               lq_sta->lq_info[i].win[j].success_counter,
-                               lq_sta->lq_info[i].win[j].success_ratio);
+                               tbl->win[j].counter,
+                               tbl->win[j].success_counter,
+                               tbl->win[j].success_ratio);
                }
        }
        ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
index 335cf16829023e6702ef1d5921c19cccb3196c84..721e6b31b712d77d1f7b8ba1103ceaaee8658703 100644 (file)
 #include "iwl-trans.h"
 
 struct iwl_rs_rate_info {
-       u8 plcp;        /* uCode API:  IWL_RATE_6M_PLCP, etc. */
-       u8 plcp_siso;   /* uCode API:  IWL_RATE_SISO_6M_PLCP, etc. */
-       u8 plcp_mimo2;  /* uCode API:  IWL_RATE_MIMO2_6M_PLCP, etc. */
+       u8 plcp;          /* uCode API:  IWL_RATE_6M_PLCP, etc. */
+       u8 plcp_ht_siso;  /* uCode API:  IWL_RATE_SISO_6M_PLCP, etc. */
+       u8 plcp_ht_mimo2; /* uCode API:  IWL_RATE_MIMO2_6M_PLCP, etc. */
+       u8 plcp_vht_siso;
+       u8 plcp_vht_mimo2;
        u8 prev_rs;      /* previous rate used in rs algo */
        u8 next_rs;      /* next rate used in rs algo */
 };
@@ -83,35 +85,52 @@ enum {
 #define        IWL_RATE_11M_MASK  (1 << IWL_RATE_11M_INDEX)
 
 
-/* uCode API values for OFDM high-throughput (HT) bit rates */
+/* uCode API values for HT/VHT bit rates */
 enum {
-       IWL_RATE_SISO_6M_PLCP = 0,
-       IWL_RATE_SISO_12M_PLCP = 1,
-       IWL_RATE_SISO_18M_PLCP = 2,
-       IWL_RATE_SISO_24M_PLCP = 3,
-       IWL_RATE_SISO_36M_PLCP = 4,
-       IWL_RATE_SISO_48M_PLCP = 5,
-       IWL_RATE_SISO_54M_PLCP = 6,
-       IWL_RATE_SISO_60M_PLCP = 7,
-       IWL_RATE_MIMO2_6M_PLCP  = 0x8,
-       IWL_RATE_MIMO2_12M_PLCP = 0x9,
-       IWL_RATE_MIMO2_18M_PLCP = 0xa,
-       IWL_RATE_MIMO2_24M_PLCP = 0xb,
-       IWL_RATE_MIMO2_36M_PLCP = 0xc,
-       IWL_RATE_MIMO2_48M_PLCP = 0xd,
-       IWL_RATE_MIMO2_54M_PLCP = 0xe,
-       IWL_RATE_MIMO2_60M_PLCP = 0xf,
-       IWL_RATE_MIMO3_6M_PLCP  = 0x10,
-       IWL_RATE_MIMO3_12M_PLCP = 0x11,
-       IWL_RATE_MIMO3_18M_PLCP = 0x12,
-       IWL_RATE_MIMO3_24M_PLCP = 0x13,
-       IWL_RATE_MIMO3_36M_PLCP = 0x14,
-       IWL_RATE_MIMO3_48M_PLCP = 0x15,
-       IWL_RATE_MIMO3_54M_PLCP = 0x16,
-       IWL_RATE_MIMO3_60M_PLCP = 0x17,
-       IWL_RATE_SISO_INVM_PLCP,
-       IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
-       IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
+       IWL_RATE_HT_SISO_MCS_0_PLCP = 0,
+       IWL_RATE_HT_SISO_MCS_1_PLCP = 1,
+       IWL_RATE_HT_SISO_MCS_2_PLCP = 2,
+       IWL_RATE_HT_SISO_MCS_3_PLCP = 3,
+       IWL_RATE_HT_SISO_MCS_4_PLCP = 4,
+       IWL_RATE_HT_SISO_MCS_5_PLCP = 5,
+       IWL_RATE_HT_SISO_MCS_6_PLCP = 6,
+       IWL_RATE_HT_SISO_MCS_7_PLCP = 7,
+       IWL_RATE_HT_MIMO2_MCS_0_PLCP = 0x8,
+       IWL_RATE_HT_MIMO2_MCS_1_PLCP = 0x9,
+       IWL_RATE_HT_MIMO2_MCS_2_PLCP = 0xA,
+       IWL_RATE_HT_MIMO2_MCS_3_PLCP = 0xB,
+       IWL_RATE_HT_MIMO2_MCS_4_PLCP = 0xC,
+       IWL_RATE_HT_MIMO2_MCS_5_PLCP = 0xD,
+       IWL_RATE_HT_MIMO2_MCS_6_PLCP = 0xE,
+       IWL_RATE_HT_MIMO2_MCS_7_PLCP = 0xF,
+       IWL_RATE_VHT_SISO_MCS_0_PLCP = 0,
+       IWL_RATE_VHT_SISO_MCS_1_PLCP = 1,
+       IWL_RATE_VHT_SISO_MCS_2_PLCP = 2,
+       IWL_RATE_VHT_SISO_MCS_3_PLCP = 3,
+       IWL_RATE_VHT_SISO_MCS_4_PLCP = 4,
+       IWL_RATE_VHT_SISO_MCS_5_PLCP = 5,
+       IWL_RATE_VHT_SISO_MCS_6_PLCP = 6,
+       IWL_RATE_VHT_SISO_MCS_7_PLCP = 7,
+       IWL_RATE_VHT_SISO_MCS_8_PLCP = 8,
+       IWL_RATE_VHT_SISO_MCS_9_PLCP = 9,
+       IWL_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10,
+       IWL_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11,
+       IWL_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12,
+       IWL_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13,
+       IWL_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14,
+       IWL_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15,
+       IWL_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16,
+       IWL_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17,
+       IWL_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18,
+       IWL_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19,
+       IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_VHT_SISO_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_VHT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_SISO_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_SISO_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_MIMO2_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_MIMO2_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
 };
 
 #define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
@@ -139,25 +158,33 @@ enum {
 #define IWL_RATE_DECREASE_TH           1920    /*  15% */
 
 /* possible actions when in legacy mode */
-#define IWL_LEGACY_SWITCH_ANTENNA1      0
-#define IWL_LEGACY_SWITCH_ANTENNA2      1
-#define IWL_LEGACY_SWITCH_SISO          2
-#define IWL_LEGACY_SWITCH_MIMO2         3
+enum {
+       IWL_LEGACY_SWITCH_ANTENNA,
+       IWL_LEGACY_SWITCH_SISO,
+       IWL_LEGACY_SWITCH_MIMO2,
+       IWL_LEGACY_FIRST_ACTION = IWL_LEGACY_SWITCH_ANTENNA,
+       IWL_LEGACY_LAST_ACTION = IWL_LEGACY_SWITCH_MIMO2,
+};
 
 /* possible actions when in siso mode */
-#define IWL_SISO_SWITCH_ANTENNA1        0
-#define IWL_SISO_SWITCH_ANTENNA2        1
-#define IWL_SISO_SWITCH_MIMO2           2
-#define IWL_SISO_SWITCH_GI              3
+enum {
+       IWL_SISO_SWITCH_ANTENNA,
+       IWL_SISO_SWITCH_MIMO2,
+       IWL_SISO_SWITCH_GI,
+       IWL_SISO_FIRST_ACTION = IWL_SISO_SWITCH_ANTENNA,
+       IWL_SISO_LAST_ACTION = IWL_SISO_SWITCH_GI,
+};
 
 /* possible actions when in mimo mode */
-#define IWL_MIMO2_SWITCH_ANTENNA1       0
-#define IWL_MIMO2_SWITCH_ANTENNA2       1
-#define IWL_MIMO2_SWITCH_SISO_A         2
-#define IWL_MIMO2_SWITCH_SISO_B         3
-#define IWL_MIMO2_SWITCH_GI             4
+enum {
+       IWL_MIMO2_SWITCH_SISO_A,
+       IWL_MIMO2_SWITCH_SISO_B,
+       IWL_MIMO2_SWITCH_GI,
+       IWL_MIMO2_FIRST_ACTION = IWL_MIMO2_SWITCH_SISO_A,
+       IWL_MIMO2_LAST_ACTION = IWL_MIMO2_SWITCH_GI,
+};
 
-#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_GI
+#define IWL_MAX_SEARCH IWL_MIMO2_LAST_ACTION
 
 #define IWL_ACTION_LIMIT               3       /* # possible actions */
 
@@ -188,20 +215,31 @@ enum {
 
 enum iwl_table_type {
        LQ_NONE,
-       LQ_G,           /* legacy types */
-       LQ_A,
-       LQ_SISO,        /* high-throughput types */
-       LQ_MIMO2,
+       LQ_LEGACY_G,    /* legacy types */
+       LQ_LEGACY_A,
+       LQ_HT_SISO,     /* HT types */
+       LQ_HT_MIMO2,
+       LQ_VHT_SISO,    /* VHT types */
+       LQ_VHT_MIMO2,
        LQ_MAX,
 };
 
-#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
-#define is_siso(tbl) ((tbl) == LQ_SISO)
-#define is_mimo2(tbl) ((tbl) == LQ_MIMO2)
-#define is_mimo(tbl) is_mimo2(tbl)
-#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
-#define is_a_band(tbl) ((tbl) == LQ_A)
-#define is_g_and(tbl) ((tbl) == LQ_G)
+#define is_legacy(tbl) (((tbl) == LQ_LEGACY_G) || ((tbl) == LQ_LEGACY_A))
+#define is_ht_siso(tbl) ((tbl) == LQ_HT_SISO)
+#define is_ht_mimo2(tbl) ((tbl) == LQ_HT_MIMO2)
+#define is_vht_siso(tbl) ((tbl) == LQ_VHT_SISO)
+#define is_vht_mimo2(tbl) ((tbl) == LQ_VHT_MIMO2)
+#define is_siso(tbl) (is_ht_siso(tbl) || is_vht_siso(tbl))
+#define is_mimo2(tbl) (is_ht_mimo2(tbl) || is_vht_mimo2(tbl))
+#define is_mimo(tbl) (is_mimo2(tbl))
+#define is_ht(tbl) (is_ht_siso(tbl) || is_ht_mimo2(tbl))
+#define is_vht(tbl) (is_vht_siso(tbl) || is_vht_mimo2(tbl))
+#define is_a_band(tbl) ((tbl) == LQ_LEGACY_A)
+#define is_g_band(tbl) ((tbl) == LQ_LEGACY_G)
+
+#define is_ht20(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_20)
+#define is_ht40(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_40)
+#define is_ht80(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_80)
 
 #define IWL_MAX_MCS_DISPLAY_SIZE       12
 
@@ -232,7 +270,7 @@ struct iwl_scale_tbl_info {
        enum iwl_table_type lq_type;
        u8 ant_type;
        u8 is_SGI;      /* 1 = short guard interval */
-       u8 is_ht40;     /* 1 = 40 MHz channel width */
+       u32 bw;         /* channel bandwidth; RATE_MCS_CHAN_WIDTH_XX */
        u8 action;      /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
        u8 max_search;  /* maximun number of tables we can search */
        s32 *expected_tpt;      /* throughput metrics; expected_tpt_G, etc. */
@@ -262,7 +300,7 @@ struct iwl_lq_sta {
        u64 flush_timer;        /* time staying in mode before new search */
 
        u8 action_counter;      /* # mode-switch actions tried */
-       u8 is_green;
+       bool is_vht;
        enum ieee80211_band band;
 
        /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
index 2a8cb5a60535d161f410116c32967cbd9a983987..a4af5019a4960bc1afe1e0b9a5c5296381d4f590 100644 (file)
@@ -422,6 +422,27 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
 
        mvmvif->bf_data.ave_beacon_signal = sig;
 
+       /* BT Coex */
+       if (mvmvif->bf_data.bt_coex_min_thold !=
+           mvmvif->bf_data.bt_coex_max_thold) {
+               last_event = mvmvif->bf_data.last_bt_coex_event;
+               if (sig > mvmvif->bf_data.bt_coex_max_thold &&
+                   (last_event <= mvmvif->bf_data.bt_coex_min_thold ||
+                    last_event == 0)) {
+                       mvmvif->bf_data.last_bt_coex_event = sig;
+                       IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n",
+                                    sig);
+                       iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH);
+               } else if (sig < mvmvif->bf_data.bt_coex_min_thold &&
+                          (last_event >= mvmvif->bf_data.bt_coex_max_thold ||
+                           last_event == 0)) {
+                       mvmvif->bf_data.last_bt_coex_event = sig;
+                       IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n",
+                                    sig);
+                       iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW);
+               }
+       }
+
        if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
                return;
 
index 9a7ab84953000234b463ac1636cabcf506b95433..778dcd9320fe32de4a3f6ecd1e78c23b841f7bd8 100644 (file)
 static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
 {
        u16 rx_chain;
-       u8 rx_ant = iwl_fw_valid_rx_ant(mvm->fw);
+       u8 rx_ant;
 
+       if (mvm->scan_rx_ant != ANT_NONE)
+               rx_ant = mvm->scan_rx_ant;
+       else
+               rx_ant = iwl_fw_valid_rx_ant(mvm->fw);
        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;
@@ -133,11 +137,12 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
  * request.
  */
 static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd,
-                                   struct cfg80211_scan_request *req)
+                                   struct cfg80211_scan_request *req,
+                                   int first)
 {
        int fw_idx, req_idx;
 
-       for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx > 0;
+       for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx >= first;
             req_idx--, fw_idx++) {
                cmd->direct_scan[fw_idx].id = WLAN_EID_SSID;
                cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len;
@@ -153,9 +158,9 @@ static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd,
  * just to notify that this scan is active and not passive.
  * In order to notify the FW of the number of SSIDs we wish to scan (including
  * the zero-length one), we need to set the corresponding bits in chan->type,
- * one for each SSID, and set the active bit (first). The first SSID is already
- * included in the probe template, so we need to set only req->n_ssids - 1 bits
- * in addition to the first bit.
+ * one for each SSID, and set the active bit (first). If the first SSID is
+ * already included in the probe template, so we need to set only
+ * req->n_ssids - 1 bits in addition to the first bit.
  */
 static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids)
 {
@@ -170,7 +175,8 @@ static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band)
 }
 
 static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
-                                      struct cfg80211_scan_request *req)
+                                      struct cfg80211_scan_request *req,
+                                      bool basic_ssid)
 {
        u16 passive_dwell = iwl_mvm_get_passive_dwell(req->channels[0]->band);
        u16 active_dwell = iwl_mvm_get_active_dwell(req->channels[0]->band,
@@ -178,10 +184,14 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
        struct iwl_scan_channel *chan = (struct iwl_scan_channel *)
                (cmd->data + le16_to_cpu(cmd->tx_cmd.len));
        int i;
+       int type = BIT(req->n_ssids) - 1;
+
+       if (!basic_ssid)
+               type |= BIT(req->n_ssids);
 
        for (i = 0; i < cmd->channel_count; i++) {
                chan->channel = cpu_to_le16(req->channels[i]->hw_value);
-               chan->type = cpu_to_le32(BIT(req->n_ssids) - 1);
+               chan->type = cpu_to_le32(type);
                if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN)
                        chan->type &= cpu_to_le32(~SCAN_CHANNEL_TYPE_ACTIVE);
                chan->active_dwell = cpu_to_le16(active_dwell);
@@ -268,6 +278,8 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
        u32 status;
        int ssid_len = 0;
        u8 *ssid = NULL;
+       bool basic_ssid = !(mvm->fw->ucode_capa.flags &
+                          IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID);
 
        lockdep_assert_held(&mvm->mutex);
        BUG_ON(mvm->scan_cmd == NULL);
@@ -302,14 +314,16 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
        if (req->n_ssids > 0) {
                cmd->passive2active = cpu_to_le16(1);
                cmd->scan_flags |= SCAN_FLAGS_PASSIVE2ACTIVE;
-               ssid = req->ssids[0].ssid;
-               ssid_len = req->ssids[0].ssid_len;
+               if (basic_ssid) {
+                       ssid = req->ssids[0].ssid;
+                       ssid_len = req->ssids[0].ssid_len;
+               }
        } else {
                cmd->passive2active = 0;
                cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
        }
 
-       iwl_mvm_scan_fill_ssids(cmd, req);
+       iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0);
 
        cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
        cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id;
@@ -326,7 +340,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
                            req->ie, req->ie_len,
                            mvm->fw->ucode_capa.max_probe_length));
 
-       iwl_mvm_scan_fill_channels(cmd, req);
+       iwl_mvm_scan_fill_channels(cmd, req, basic_ssid);
 
        cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) +
                le16_to_cpu(cmd->tx_cmd.len) +
@@ -377,6 +391,21 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        return 0;
 }
 
+int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
+                                 struct iwl_rx_cmd_buffer *rxb,
+                                 struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_sched_scan_results *notif = (void *)pkt->data;
+
+       if (notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
+               IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
+               ieee80211_sched_scan_results(mvm->hw);
+       }
+
+       return 0;
+}
+
 static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
                                     struct iwl_rx_packet *pkt, void *data)
 {
@@ -437,3 +466,406 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
 out_remove_notif:
        iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort);
 }
+
+int iwl_mvm_rx_scan_offload_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_scan_offload_complete *scan_notif = (void *)pkt->data;
+
+       IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n",
+                      scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
+                      "completed" : "aborted");
+
+       mvm->scan_status = IWL_MVM_SCAN_NONE;
+       ieee80211_sched_scan_stopped(mvm->hw);
+
+       return 0;
+}
+
+static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct ieee80211_sched_scan_ies *ies,
+                                         enum ieee80211_band band,
+                                         struct iwl_tx_cmd *cmd,
+                                         u8 *data)
+{
+       u16 cmd_len;
+
+       cmd->tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
+       cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+       cmd->sta_id = mvm->aux_sta.sta_id;
+
+       cmd->rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, band, false);
+
+       cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data,
+                                        vif->addr,
+                                        1, NULL, 0,
+                                        ies->ie[band], ies->len[band],
+                                        SCAN_OFFLOAD_PROBE_REQ_SIZE);
+       cmd->len = cpu_to_le16(cmd_len);
+}
+
+static void iwl_build_scan_cmd(struct iwl_mvm *mvm,
+                              struct ieee80211_vif *vif,
+                              struct cfg80211_sched_scan_request *req,
+                              struct iwl_scan_offload_cmd *scan)
+{
+       scan->channel_count =
+               mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels +
+               mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
+       scan->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
+       scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
+       scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT;
+       scan->rx_chain = iwl_mvm_scan_rx_chain(mvm);
+       scan->max_out_time = cpu_to_le32(200 * 1024);
+       scan->suspend_time = iwl_mvm_scan_suspend_time(vif);
+       scan->filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+                                         MAC_FILTER_IN_BEACON);
+       scan->scan_type = cpu_to_le32(SCAN_TYPE_BACKGROUND);
+       scan->rep_count = cpu_to_le32(1);
+}
+
+static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
+{
+       int i;
+
+       for (i = 0; i < PROBE_OPTION_MAX; i++) {
+               if (!ssid_list[i].len)
+                       break;
+               if (ssid_list[i].len == ssid_len &&
+                   !memcmp(ssid_list->ssid, ssid, ssid_len))
+                       return i;
+       }
+       return -1;
+}
+
+static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
+                                       struct iwl_scan_offload_cmd *scan,
+                                       u32 *ssid_bitmap)
+{
+       int i, j;
+       int index;
+
+       /*
+        * copy SSIDs from match list.
+        * iwl_config_sched_scan_profiles() uses the order of these ssids to
+        * config match list.
+        */
+       for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) {
+               scan->direct_scan[i].id = WLAN_EID_SSID;
+               scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
+               memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
+                      scan->direct_scan[i].len);
+       }
+
+       /* add SSIDs from scan SSID list */
+       *ssid_bitmap = 0;
+       for (j = 0; j < req->n_ssids && i < PROBE_OPTION_MAX; j++) {
+               index = iwl_ssid_exist(req->ssids[j].ssid,
+                                      req->ssids[j].ssid_len,
+                                      scan->direct_scan);
+               if (index < 0) {
+                       if (!req->ssids[j].ssid_len)
+                               continue;
+                       scan->direct_scan[i].id = WLAN_EID_SSID;
+                       scan->direct_scan[i].len = req->ssids[j].ssid_len;
+                       memcpy(scan->direct_scan[i].ssid, req->ssids[j].ssid,
+                              scan->direct_scan[i].len);
+                       *ssid_bitmap |= BIT(i + 1);
+                       i++;
+               } else {
+                       *ssid_bitmap |= BIT(index + 1);
+               }
+       }
+}
+
+static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
+                                 struct cfg80211_sched_scan_request *req,
+                                 struct iwl_scan_channel_cfg *channels,
+                                 enum ieee80211_band band,
+                                 int *head, int *tail,
+                                 u32 ssid_bitmap)
+{
+       struct ieee80211_supported_band *s_band;
+       int n_probes = req->n_ssids;
+       int n_channels = req->n_channels;
+       u8 active_dwell, passive_dwell;
+       int i, j, index = 0;
+       bool partial;
+
+       /*
+        * We have to configure all supported channels, even if we don't want to
+        * scan on them, but we have to send channels in the order that we want
+        * to scan. So add requested channels to head of the list and others to
+        * the end.
+       */
+       active_dwell = iwl_mvm_get_active_dwell(band, n_probes);
+       passive_dwell = iwl_mvm_get_passive_dwell(band);
+       s_band = &mvm->nvm_data->bands[band];
+
+       for (i = 0; i < s_band->n_channels && *head <= *tail; i++) {
+               partial = false;
+               for (j = 0; j < n_channels; j++)
+                       if (s_band->channels[i].center_freq ==
+                                               req->channels[j]->center_freq) {
+                               index = *head;
+                               (*head)++;
+                               /*
+                                * Channels that came with the request will be
+                                * in partial scan .
+                                */
+                               partial = true;
+                               break;
+                       }
+               if (!partial) {
+                       index = *tail;
+                       (*tail)--;
+               }
+               channels->channel_number[index] =
+                       cpu_to_le16(ieee80211_frequency_to_channel(
+                                       s_band->channels[i].center_freq));
+               channels->dwell_time[index][0] = active_dwell;
+               channels->dwell_time[index][1] = passive_dwell;
+
+               channels->iter_count[index] = cpu_to_le16(1);
+               channels->iter_interval[index] = 0;
+
+               if (!(s_band->channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN))
+                       channels->type[index] |=
+                               cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE);
+
+               channels->type[index] |=
+                               cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL);
+               if (partial)
+                       channels->type[index] |=
+                               cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL);
+
+               if (s_band->channels[i].flags & IEEE80211_CHAN_NO_HT40)
+                       channels->type[index] |=
+                               cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW);
+
+               /* scan for all SSIDs from req->ssids */
+               channels->type[index] |= cpu_to_le32(ssid_bitmap);
+       }
+}
+
+int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             struct cfg80211_sched_scan_request *req,
+                             struct ieee80211_sched_scan_ies *ies)
+{
+       int supported_bands = 0;
+       int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels;
+       int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
+       int head = 0;
+       int tail = band_2ghz + band_5ghz;
+       u32 ssid_bitmap;
+       int cmd_len;
+       int ret;
+
+       struct iwl_scan_offload_cfg *scan_cfg;
+       struct iwl_host_cmd cmd = {
+               .id = SCAN_OFFLOAD_CONFIG_CMD,
+               .flags = CMD_SYNC,
+       };
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (band_2ghz)
+               supported_bands++;
+       if (band_5ghz)
+               supported_bands++;
+
+       cmd_len = sizeof(struct iwl_scan_offload_cfg) +
+                               supported_bands * SCAN_OFFLOAD_PROBE_REQ_SIZE;
+
+       scan_cfg = kzalloc(cmd_len, GFP_KERNEL);
+       if (!scan_cfg)
+               return -ENOMEM;
+
+       iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd);
+       scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len);
+
+       iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap);
+       /* build tx frames for supported bands */
+       if (band_2ghz) {
+               iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
+                                             IEEE80211_BAND_2GHZ,
+                                             &scan_cfg->scan_cmd.tx_cmd[0],
+                                             scan_cfg->data);
+               iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
+                                     IEEE80211_BAND_2GHZ, &head, &tail,
+                                     ssid_bitmap);
+       }
+       if (band_5ghz) {
+               iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
+                                             IEEE80211_BAND_5GHZ,
+                                             &scan_cfg->scan_cmd.tx_cmd[1],
+                                             scan_cfg->data +
+                                               SCAN_OFFLOAD_PROBE_REQ_SIZE);
+               iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
+                                     IEEE80211_BAND_5GHZ, &head, &tail,
+                                     ssid_bitmap);
+       }
+
+       cmd.data[0] = scan_cfg;
+       cmd.len[0] = cmd_len;
+       cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+
+       IWL_DEBUG_SCAN(mvm, "Sending scheduled scan config\n");
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       kfree(scan_cfg);
+       return ret;
+}
+
+int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
+                                      struct cfg80211_sched_scan_request *req)
+{
+       struct iwl_scan_offload_profile *profile;
+       struct iwl_scan_offload_profile_cfg *profile_cfg;
+       struct iwl_scan_offload_blacklist *blacklist;
+       struct iwl_host_cmd cmd = {
+               .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD,
+               .flags = CMD_SYNC,
+               .len[1] = sizeof(*profile_cfg),
+               .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+               .dataflags[1] = IWL_HCMD_DFL_NOCOPY,
+       };
+       int blacklist_len;
+       int i;
+       int ret;
+
+       if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES))
+                       return -EIO;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL)
+               blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN;
+       else
+               blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN;
+
+       blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL);
+       if (!blacklist)
+               return -ENOMEM;
+
+       profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL);
+       if (!profile_cfg) {
+               ret = -ENOMEM;
+               goto free_blacklist;
+       }
+
+       cmd.data[0] = blacklist;
+       cmd.len[0] = sizeof(*blacklist) * blacklist_len;
+       cmd.data[1] = profile_cfg;
+
+       /* No blacklist configuration */
+
+       profile_cfg->num_profiles = req->n_match_sets;
+       profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN;
+       profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN;
+       profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN;
+
+       for (i = 0; i < req->n_match_sets; i++) {
+               profile = &profile_cfg->profiles[i];
+               profile->ssid_index = i;
+               /* Support any cipher and auth algorithm */
+               profile->unicast_cipher = 0xff;
+               profile->auth_alg = 0xff;
+               profile->network_type = IWL_NETWORK_TYPE_ANY;
+               profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY;
+               profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN;
+       }
+
+       IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n");
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       kfree(profile_cfg);
+free_blacklist:
+       kfree(blacklist);
+
+       return ret;
+}
+
+int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
+                            struct cfg80211_sched_scan_request *req)
+{
+       struct iwl_scan_offload_req scan_req = {
+               .watchdog = IWL_SCHED_SCAN_WATCHDOG,
+
+               .schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS,
+               .schedule_line[0].delay = req->interval / 1000,
+               .schedule_line[0].full_scan_mul = 1,
+
+               .schedule_line[1].iterations = 0xff,
+               .schedule_line[1].delay = req->interval / 1000,
+               .schedule_line[1].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);
+               scan_req.flags |=
+                               cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID);
+       } else {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sending Scheduled scan without filtering\n");
+       }
+
+       return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, CMD_SYNC,
+                                   sizeof(scan_req), &scan_req);
+}
+
+static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
+{
+       int ret;
+       struct iwl_host_cmd cmd = {
+               .id = SCAN_OFFLOAD_ABORT_CMD,
+               .flags = CMD_SYNC,
+       };
+       u32 status;
+
+       /* Exit instantly with error when device is not ready
+        * to receive scan abort command or it does not perform
+        * scheduled scan currently */
+       if (mvm->scan_status != IWL_MVM_SCAN_SCHED)
+               return -EIO;
+
+       ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status);
+       if (ret)
+               return ret;
+
+       if (status != CAN_ABORT_STATUS) {
+               /*
+                * The scan abort will return 1 for success or
+                * 2 for "failure".  A failure condition can be
+                * due to simply not being in an active scan which
+                * can occur if we send the scan abort before the
+                * microcode has notified us that a scan is completed.
+                */
+               IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status);
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm)
+{
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (mvm->scan_status != IWL_MVM_SCAN_SCHED) {
+               IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n");
+               return;
+       }
+
+       ret = iwl_mvm_send_sched_scan_abort(mvm);
+       if (ret)
+               IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret);
+       else
+               IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n");
+}
index 44add291531bb505a6d388fde3fdf961b7cc14e2..329952363a54f3b2c0913d018c401e9c7004fcfc 100644 (file)
 #include "sta.h"
 #include "rs.h"
 
+static void iwl_mvm_add_sta_cmd_v6_to_v5(struct iwl_mvm_add_sta_cmd_v6 *cmd_v6,
+                                        struct iwl_mvm_add_sta_cmd_v5 *cmd_v5)
+{
+       memset(cmd_v5, 0, sizeof(*cmd_v5));
+
+       cmd_v5->add_modify = cmd_v6->add_modify;
+       cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx;
+       cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color;
+       memcpy(cmd_v5->addr, cmd_v6->addr, ETH_ALEN);
+       cmd_v5->sta_id = cmd_v6->sta_id;
+       cmd_v5->modify_mask = cmd_v6->modify_mask;
+       cmd_v5->station_flags = cmd_v6->station_flags;
+       cmd_v5->station_flags_msk = cmd_v6->station_flags_msk;
+       cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid;
+       cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid;
+       cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn;
+       cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count;
+       cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags;
+       cmd_v5->assoc_id = cmd_v6->assoc_id;
+       cmd_v5->beamform_flags = cmd_v6->beamform_flags;
+       cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk;
+}
+
+static void
+iwl_mvm_add_sta_key_to_add_sta_cmd_v5(struct iwl_mvm_add_sta_key_cmd *key_cmd,
+                                     struct iwl_mvm_add_sta_cmd_v5 *sta_cmd,
+                                     u32 mac_id_n_color)
+{
+       memset(sta_cmd, 0, sizeof(*sta_cmd));
+
+       sta_cmd->sta_id = key_cmd->sta_id;
+       sta_cmd->add_modify = STA_MODE_MODIFY;
+       sta_cmd->modify_mask = STA_MODIFY_KEY;
+       sta_cmd->mac_id_n_color = cpu_to_le32(mac_id_n_color);
+
+       sta_cmd->key.key_offset = key_cmd->key_offset;
+       sta_cmd->key.key_flags = key_cmd->key_flags;
+       memcpy(sta_cmd->key.key, key_cmd->key, sizeof(sta_cmd->key.key));
+       sta_cmd->key.tkip_rx_tsc_byte2 = key_cmd->tkip_rx_tsc_byte2;
+       memcpy(sta_cmd->key.tkip_rx_ttak, key_cmd->tkip_rx_ttak,
+              sizeof(sta_cmd->key.tkip_rx_ttak));
+}
+
+static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
+                                          struct iwl_mvm_add_sta_cmd_v6 *cmd,
+                                          int *status)
+{
+       struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
+               return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd),
+                                                  cmd, status);
+
+       iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
+
+       return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5),
+                                          &cmd_v5, status);
+}
+
+static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
+                                   struct iwl_mvm_add_sta_cmd_v6 *cmd)
+{
+       struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
+               return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags,
+                                           sizeof(*cmd), cmd);
+
+       iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
+
+       return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5),
+                                   &cmd_v5);
+}
+
+static int
+iwl_mvm_send_add_sta_key_cmd_status(struct iwl_mvm *mvm,
+                                   struct iwl_mvm_add_sta_key_cmd *cmd,
+                                   u32 mac_id_n_color,
+                                   int *status)
+{
+       struct iwl_mvm_add_sta_cmd_v5 sta_cmd;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
+               return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY,
+                                                  sizeof(*cmd), cmd, status);
+
+       iwl_mvm_add_sta_key_to_add_sta_cmd_v5(cmd, &sta_cmd, mac_id_n_color);
+
+       return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(sta_cmd),
+                                          &sta_cmd, status);
+}
+
+static int iwl_mvm_send_add_sta_key_cmd(struct iwl_mvm *mvm,
+                                       u32 flags,
+                                       struct iwl_mvm_add_sta_key_cmd *cmd,
+                                       u32 mac_id_n_color)
+{
+       struct iwl_mvm_add_sta_cmd_v5 sta_cmd;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
+               return iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, flags,
+                                           sizeof(*cmd), cmd);
+
+       iwl_mvm_add_sta_key_to_add_sta_cmd_v5(cmd, &sta_cmd, mac_id_n_color);
+
+       return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(sta_cmd),
+                                   &sta_cmd);
+}
+
 static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
 {
        int sta_id;
@@ -87,7 +196,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                           bool update)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd add_sta_cmd;
+       struct iwl_mvm_add_sta_cmd_v6 add_sta_cmd;
        int ret;
        u32 status;
        u32 agg_size = 0, mpdu_dens = 0;
@@ -175,8 +284,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT);
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd),
-                                         &add_sta_cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &add_sta_cmd, &status);
        if (ret)
                return ret;
 
@@ -229,8 +337,12 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
                if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
                        mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
 
-       /* for HW restart - need to reset the seq_number etc... */
-       memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data));
+       /* for HW restart - reset everything but the sequence number */
+       for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+               u16 seq = mvm_sta->tid_data[i].seq_number;
+               memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i]));
+               mvm_sta->tid_data[i].seq_number = seq;
+       }
 
        ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);
        if (ret)
@@ -256,7 +368,7 @@ int iwl_mvm_update_sta(struct iwl_mvm *mvm,
 int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                      bool drain)
 {
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {};
        int ret;
        u32 status;
 
@@ -269,8 +381,7 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
        cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW);
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
        if (ret)
                return ret;
 
@@ -469,13 +580,13 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
                                      const u8 *addr,
                                      u16 mac_id, u16 color)
 {
-       struct iwl_mvm_add_sta_cmd cmd;
+       struct iwl_mvm_add_sta_cmd_v6 cmd;
        int ret;
        u32 status;
 
        lockdep_assert_held(&mvm->mutex);
 
-       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd));
+       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v6));
        cmd.sta_id = sta->sta_id;
        cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
                                                             color));
@@ -485,8 +596,7 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
        if (addr)
                memcpy(cmd.addr, addr, ETH_ALEN);
 
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
        if (ret)
                return ret;
 
@@ -534,10 +644,14 @@ int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                           struct iwl_mvm_int_sta *bsta)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+       static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+       static const u8 *baddr = _baddr;
 
        lockdep_assert_held(&mvm->mutex);
 
+       if (vif->type == NL80211_IFTYPE_ADHOC)
+               baddr = vif->bss_conf.bssid;
+
        if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT))
                return -ENOSPC;
 
@@ -614,7 +728,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                       int tid, u16 ssn, bool start)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {};
        int ret;
        u32 status;
 
@@ -638,8 +752,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                                  STA_MODIFY_REMOVE_BA_TID;
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
        if (ret)
                return ret;
 
@@ -674,7 +787,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                              int tid, u8 queue, bool start)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {};
        int ret;
        u32 status;
 
@@ -696,8 +809,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg);
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
        if (ret)
                return ret;
 
@@ -743,13 +855,13 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        lockdep_assert_held(&mvm->mutex);
 
-       for (txq_id = IWL_MVM_FIRST_AGG_QUEUE;
-            txq_id <= IWL_MVM_LAST_AGG_QUEUE; txq_id++)
+       for (txq_id = mvm->first_agg_queue;
+            txq_id <= mvm->last_agg_queue; txq_id++)
                if (mvm->queue_to_mac80211[txq_id] ==
                    IWL_INVALID_MAC80211_QUEUE)
                        break;
 
-       if (txq_id > IWL_MVM_LAST_AGG_QUEUE) {
+       if (txq_id > mvm->last_agg_queue) {
                IWL_ERR(mvm, "Failed to allocate agg queue\n");
                return -EIO;
        }
@@ -987,10 +1099,11 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                                u32 cmd_flags)
 {
        __le16 key_flags;
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_key_cmd cmd = {};
        int ret, status;
        u16 keyidx;
        int i;
+       u32 mac_id_n_color = mvm_sta->mac_id_n_color;
 
        keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
                 STA_KEY_FLG_KEYID_MSK;
@@ -1000,14 +1113,14 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
        switch (keyconf->cipher) {
        case WLAN_CIPHER_SUITE_TKIP:
                key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP);
-               cmd.key.tkip_rx_tsc_byte2 = tkip_iv32;
+               cmd.tkip_rx_tsc_byte2 = tkip_iv32;
                for (i = 0; i < 5; i++)
-                       cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
-               memcpy(cmd.key.key, keyconf->key, keyconf->keylen);
+                       cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
+               memcpy(cmd.key, keyconf->key, keyconf->keylen);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
                key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
-               memcpy(cmd.key.key, keyconf->key, keyconf->keylen);
+               memcpy(cmd.key, keyconf->key, keyconf->keylen);
                break;
        default:
                WARN_ON(1);
@@ -1017,20 +1130,18 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
        if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
                key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
 
-       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
-       cmd.key.key_offset = keyconf->hw_key_idx;
-       cmd.key.key_flags = key_flags;
-       cmd.add_modify = STA_MODE_MODIFY;
-       cmd.modify_mask = STA_MODIFY_KEY;
+       cmd.key_offset = keyconf->hw_key_idx;
+       cmd.key_flags = key_flags;
        cmd.sta_id = sta_id;
 
        status = ADD_STA_SUCCESS;
        if (cmd_flags == CMD_SYNC)
-               ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                                 &cmd, &status);
+               ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd,
+                                                         mac_id_n_color,
+                                                         &status);
        else
-               ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC,
-                                          sizeof(cmd), &cmd);
+               ret = iwl_mvm_send_add_sta_key_cmd(mvm, CMD_ASYNC, &cmd,
+                                                  mac_id_n_color);
 
        switch (status) {
        case ADD_STA_SUCCESS:
@@ -1197,7 +1308,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
                           struct ieee80211_key_conf *keyconf)
 {
        struct iwl_mvm_sta *mvm_sta;
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_key_cmd cmd = {};
        __le16 key_flags;
        int ret, status;
        u8 sta_id;
@@ -1252,17 +1363,14 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
        if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
                key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
 
-       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
-       cmd.key.key_flags = key_flags;
-       cmd.key.key_offset = keyconf->hw_key_idx;
+       cmd.key_flags = key_flags;
+       cmd.key_offset = keyconf->hw_key_idx;
        cmd.sta_id = sta_id;
 
-       cmd.modify_mask = STA_MODIFY_KEY;
-       cmd.add_modify = STA_MODE_MODIFY;
-
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd,
+                                                 mvm_sta->mac_id_n_color,
+                                                 &status);
 
        switch (status) {
        case ADD_STA_SUCCESS:
@@ -1309,7 +1417,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta)
 {
        struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd cmd = {
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .station_flags_msk = cpu_to_le32(STA_FLG_PS),
@@ -1317,7 +1425,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
        };
        int ret;
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+       ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
 }
@@ -1331,7 +1439,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                (reason == IEEE80211_FRAME_RELEASE_UAPSD) ?
                        STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL;
        struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd cmd = {
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
@@ -1346,7 +1454,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
        int ret;
 
        /* TODO: somehow the fw doesn't seem to take PS_POLL into account */
-       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+       ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
 }
index 94b265eb32b82bfb7a5f467cef60963f5da8d252..4dfc359a4bdda187cbb1fdcbda8b4cfe112c78fd 100644 (file)
@@ -293,10 +293,6 @@ struct iwl_mvm_sta {
        struct iwl_lq_sta lq_sta;
        struct ieee80211_vif *vif;
 
-#ifdef CONFIG_PM_SLEEP
-       u16 last_seq_ctl;
-#endif
-
        /* Temporary, until the new TLC will control the Tx protection */
        s8 tx_protection;
        bool tt_tx_protection;
diff --git a/drivers/net/wireless/iwlwifi/mvm/testmode.h b/drivers/net/wireless/iwlwifi/mvm/testmode.h
new file mode 100644 (file)
index 0000000..eb74391
--- /dev/null
@@ -0,0 +1,95 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_MVM_TESTMODE_H__
+#define __IWL_MVM_TESTMODE_H__
+
+/**
+ * enum iwl_mvm_testmode_attrs - testmode attributes inside NL80211_ATTR_TESTDATA
+ * @IWL_MVM_TM_ATTR_UNSPEC: (invalid attribute)
+ * @IWL_MVM_TM_ATTR_CMD: sub command, see &enum iwl_mvm_testmode_commands (u32)
+ * @IWL_MVM_TM_ATTR_NOA_DURATION: requested NoA duration (u32)
+ * @IWL_MVM_TM_ATTR_BEACON_FILTER_STATE: beacon filter state (0 or 1, u32)
+ */
+enum iwl_mvm_testmode_attrs {
+       IWL_MVM_TM_ATTR_UNSPEC,
+       IWL_MVM_TM_ATTR_CMD,
+       IWL_MVM_TM_ATTR_NOA_DURATION,
+       IWL_MVM_TM_ATTR_BEACON_FILTER_STATE,
+
+       /* keep last */
+       NUM_IWL_MVM_TM_ATTRS,
+       IWL_MVM_TM_ATTR_MAX = NUM_IWL_MVM_TM_ATTRS - 1,
+};
+
+/**
+ * enum iwl_mvm_testmode_commands - MVM testmode commands
+ * @IWL_MVM_TM_CMD_SET_NOA: set NoA on GO vif for testing
+ * @IWL_MVM_TM_CMD_SET_BEACON_FILTER: turn beacon filtering off/on
+ */
+enum iwl_mvm_testmode_commands {
+       IWL_MVM_TM_CMD_SET_NOA,
+       IWL_MVM_TM_CMD_SET_BEACON_FILTER,
+};
+
+#endif /* __IWL_MVM_TESTMODE_H__ */
index 76a3c177e100ab28660ac9433fe5c26edc5a79e8..33cf56fdfc41f86b8bb517a8824740bdbb71a896 100644 (file)
@@ -387,7 +387,8 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
 
 void iwl_mvm_protect_session(struct iwl_mvm *mvm,
                             struct ieee80211_vif *vif,
-                            u32 duration, u32 min_duration)
+                            u32 duration, u32 min_duration,
+                            u32 max_delay)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
@@ -426,7 +427,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
                cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG));
 
        time_cmd.max_frags = TE_V2_FRAG_NONE;
-       time_cmd.max_delay = cpu_to_le32(500);
+       time_cmd.max_delay = cpu_to_le32(max_delay);
        /* TODO: why do we need to interval = bi if it is not periodic? */
        time_cmd.interval = cpu_to_le32(1);
        time_cmd.duration = cpu_to_le32(duration);
index f86c51065ed3afe1e860f8fb15d50cc1f6eaef9b..d9c8d6cfa2db0c4a44a58b0ec2bc0eaf6fca94cc 100644 (file)
  * @duration: the duration of the session in TU.
  * @min_duration: will start a new session if the current session will end
  *     in less than min_duration.
+ * @max_delay: maximum delay before starting the time event (in TU)
  *
  * This function can be used to start a session protection which means that the
  * fw will stay on the channel for %duration_ms milliseconds. This function
  */
 void iwl_mvm_protect_session(struct iwl_mvm *mvm,
                             struct ieee80211_vif *vif,
-                            u32 duration, u32 min_duration);
+                            u32 duration, u32 min_duration,
+                            u32 max_delay);
 
 /**
  * iwl_mvm_stop_session_protection - cancel the session protection.
index e05440d90319b339f203a2f1ff9658a0d97f615e..43d97c33a75abcbf3833958b0c5cbca5a91e0950 100644 (file)
@@ -417,7 +417,7 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
 
        spin_unlock(&mvmsta->lock);
 
-       if (txq_id < IWL_MVM_FIRST_AGG_QUEUE)
+       if (txq_id < mvm->first_agg_queue)
                atomic_inc(&mvm->pending_frames[mvmsta->sta_id]);
 
        return 0;
@@ -511,16 +511,10 @@ const char *iwl_mvm_get_tx_fail_reason(u32 status)
 }
 #endif /* CONFIG_IWLWIFI_DEBUG */
 
-/**
- * translate ucode response to mac80211 tx status control values
- */
-static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags,
-                                        struct ieee80211_tx_info *info)
+void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
+                              enum ieee80211_band band,
+                              struct ieee80211_tx_rate *r)
 {
-       struct ieee80211_tx_rate *r = &info->status.rates[0];
-
-       info->status.antenna =
-               ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
        if (rate_n_flags & RATE_HT_MCS_GF_MSK)
                r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
        switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
@@ -549,10 +543,23 @@ static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags,
                r->flags |= IEEE80211_TX_RC_VHT_MCS;
        } else {
                r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
-                                                            info->band);
+                                                            band);
        }
 }
 
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags,
+                                       struct ieee80211_tx_info *info)
+{
+       struct ieee80211_tx_rate *r = &info->status.rates[0];
+
+       info->status.antenna =
+               ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
+       iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r);
+}
+
 static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                                     struct iwl_rx_packet *pkt)
 {
@@ -602,11 +609,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                }
 
                info->status.rates[0].count = tx_resp->failure_frame + 1;
-               iwl_mvm_hwrate_to_tx_control(le32_to_cpu(tx_resp->initial_rate),
-                                            info);
+               iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate),
+                                           info);
 
                /* Single frame failure in an AMPDU queue => send BAR */
-               if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE &&
+               if (txq_id >= mvm->first_agg_queue &&
                    !(info->flags & IEEE80211_TX_STAT_ACK))
                        info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
 
@@ -619,7 +626,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                ieee80211_tx_status_ni(mvm->hw, skb);
        }
 
-       if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE) {
+       if (txq_id >= mvm->first_agg_queue) {
                /* If this is an aggregation queue, we use the ssn since:
                 * ssn = wifi seq_num % 256.
                 * The seq_ctl is the sequence control of the packet to which
@@ -668,10 +675,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        iwl_mvm_check_ratid_empty(mvm, sta, tid);
                        spin_unlock_bh(&mvmsta->lock);
                }
-
-#ifdef CONFIG_PM_SLEEP
-               mvmsta->last_seq_ctl = seq_ctl;
-#endif
        } else {
                sta = NULL;
                mvmsta = NULL;
@@ -681,7 +684,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
         * If the txq is not an AMPDU queue, there is no chance we freed
         * several skbs. Check that out...
         */
-       if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && !WARN_ON(skb_freed > 1) &&
+       if (txq_id < mvm->first_agg_queue && !WARN_ON(skb_freed > 1) &&
            atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) {
                if (mvmsta) {
                        /*
@@ -777,7 +780,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
        u16 sequence = le16_to_cpu(pkt->hdr.sequence);
        struct ieee80211_sta *sta;
 
-       if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_MVM_FIRST_AGG_QUEUE))
+       if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue))
                return;
 
        if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
@@ -904,8 +907,8 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                        info->flags |= IEEE80211_TX_STAT_AMPDU;
                        info->status.ampdu_ack_len = ba_notif->txed_2_done;
                        info->status.ampdu_len = ba_notif->txed;
-                       iwl_mvm_hwrate_to_tx_control(tid_data->rate_n_flags,
-                                                    info);
+                       iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags,
+                                                   info);
                }
        }
 
index a9c3574914348ad3583fd19387a6c48211530f8d..ed69e9b78e82ba325a533624107974ea81af526d 100644 (file)
@@ -466,7 +466,7 @@ void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
        ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
        len = img->sec[IWL_UCODE_SECTION_DATA].len;
 
-       buf = kzalloc(len, GFP_KERNEL);
+       buf = kzalloc(len, GFP_ATOMIC);
        if (!buf)
                return;
 
index dc02cb9792afbbb48c23f01d748c262bee1828fd..ddf15e1cffa21ad4129c3ce6a1a9441a20f1189c 100644 (file)
@@ -258,7 +258,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
 #endif /* CONFIG_IWLDVM */
 
 #if IS_ENABLED(CONFIG_IWLMVM)
-/* 7000 Series */
+/* 7260 Series */
        {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)},
@@ -308,6 +308,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
        {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
+
+/* 7265 Series */
+       {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
 #endif /* CONFIG_IWLMVM */
 
        {0}
@@ -349,7 +352,6 @@ out_free_drv:
        iwl_drv_stop(trans_pcie->drv);
 out_free_trans:
        iwl_trans_pcie_free(iwl_trans);
-       pci_set_drvdata(pdev, NULL);
        return ret;
 }
 
@@ -360,8 +362,6 @@ static void iwl_pci_remove(struct pci_dev *pdev)
 
        iwl_drv_stop(trans_pcie->drv);
        iwl_trans_pcie_free(trans);
-
-       pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM_SLEEP
index c3f904d422b08b1074345fcba99a31390f663945..5d9337bec67a87c59f3ccef73e5f5b26644930d5 100644 (file)
@@ -220,6 +220,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
        iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
                          APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 
+       /* Clear the interrupt in APMG if the NIC is in RFKILL */
+       iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL);
+
        set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
 
 out:
@@ -443,22 +446,138 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
        return ret;
 }
 
+static int iwl_pcie_secure_set(struct iwl_trans *trans, int cpu)
+{
+       int shift_param;
+       u32 address;
+       int ret = 0;
+
+       if (cpu == 1) {
+               shift_param = 0;
+               address = CSR_SECURE_BOOT_CPU1_STATUS_ADDR;
+       } else {
+               shift_param = 16;
+               address = CSR_SECURE_BOOT_CPU2_STATUS_ADDR;
+       }
+
+       /* set CPU to started */
+       iwl_trans_set_bits_mask(trans,
+                               CSR_UCODE_LOAD_STATUS_ADDR,
+                               CSR_CPU_STATUS_LOADING_STARTED << shift_param,
+                               1);
+
+       /* set last complete descriptor number */
+       iwl_trans_set_bits_mask(trans,
+                               CSR_UCODE_LOAD_STATUS_ADDR,
+                               CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED
+                               << shift_param,
+                               1);
+
+       /* set last loaded block */
+       iwl_trans_set_bits_mask(trans,
+                               CSR_UCODE_LOAD_STATUS_ADDR,
+                               CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK
+                               << shift_param,
+                               1);
+
+       /* image loading complete */
+       iwl_trans_set_bits_mask(trans,
+                               CSR_UCODE_LOAD_STATUS_ADDR,
+                               CSR_CPU_STATUS_LOADING_COMPLETED
+                               << shift_param,
+                               1);
+
+       /* set FH_TCSR_0_REG  */
+       iwl_trans_set_bits_mask(trans, FH_TCSR_0_REG0, 0x00400000, 1);
+
+       /* verify image verification started  */
+       ret = iwl_poll_bit(trans, address,
+                          CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
+                          CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
+                          CSR_SECURE_TIME_OUT);
+       if (ret < 0) {
+               IWL_ERR(trans, "secure boot process didn't start\n");
+               return ret;
+       }
+
+       /* wait for image verification to complete  */
+       ret = iwl_poll_bit(trans, address,
+                          CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
+                          CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
+                          CSR_SECURE_TIME_OUT);
+
+       if (ret < 0) {
+               IWL_ERR(trans, "Time out on secure boot process\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
                                const struct fw_img *image)
 {
        int i, ret = 0;
 
-       for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) {
+       IWL_DEBUG_FW(trans,
+                    "working with %s image\n",
+                    image->is_secure ? "Secured" : "Non Secured");
+       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 */
+       if (image->is_secure) {
+               /* set secure boot inspector addresses */
+               iwl_write32(trans, CSR_SECURE_INSPECTOR_CODE_ADDR, 0);
+               iwl_write32(trans, CSR_SECURE_INSPECTOR_DATA_ADDR, 0);
+
+               /* release CPU1 reset if secure inspector image burned in OTP */
+               iwl_write32(trans, CSR_RESET, 0);
+       }
+
+       /* load to FW the binary sections of CPU1 */
+       IWL_DEBUG_INFO(trans, "Loading CPU1\n");
+       for (i = 0;
+            i < IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
+            i++) {
                if (!image->sec[i].data)
                        break;
-
                ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
                if (ret)
                        return ret;
        }
 
-       /* Remove all resets to allow NIC to operate */
-       iwl_write32(trans, CSR_RESET, 0);
+       /* configure the ucode to start secure process on CPU1 */
+       if (image->is_secure) {
+               /* config CPU1 to start secure protocol */
+               ret = iwl_pcie_secure_set(trans, 1);
+               if (ret)
+                       return ret;
+       } else {
+               /* Remove all resets to allow NIC to operate */
+               iwl_write32(trans, CSR_RESET, 0);
+       }
+
+       if (image->is_dual_cpus) {
+               /* load to FW the binary sections of CPU2 */
+               IWL_DEBUG_INFO(trans, "working w/ DUAL CPUs - Loading CPU2\n");
+               for (i = IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
+                       i < IWL_UCODE_SECTION_MAX; i++) {
+                       if (!image->sec[i].data)
+                               break;
+                       ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+                       if (ret)
+                               return ret;
+               }
+
+               if (image->is_secure) {
+                       /* set CPU2 for secure protocol */
+                       ret = iwl_pcie_secure_set(trans, 2);
+                       if (ret)
+                               return ret;
+               }
+       }
 
        return 0;
 }
index f45eb29c2ede0b62cc1723f82ae7f30cc1f5a8ed..b4168415538c592ea9dd84ab4e8006eceeb1fc56 100644 (file)
@@ -1539,6 +1539,9 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
                                       "Clearing HCMD_ACTIVE for command %s\n",
                                       get_cmd_string(trans_pcie, cmd->id));
                        ret = -ETIMEDOUT;
+
+                       iwl_op_mode_nic_error(trans->op_mode);
+
                        goto cancel;
                }
        }