diff options
author | John W. Linville <linville@tuxdriver.com> | 2014-06-25 21:29:03 +0200 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-06-25 21:29:03 +0200 |
commit | 5f4ef7197db98a40e43904806612a8ccf609c0a3 (patch) | |
tree | f637867461ac228db910989299c11509ed0ea129 /drivers/net | |
parent | Merge git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next (diff) | |
parent | iwlwifi: mvm: handle device start failures during restart (diff) | |
download | linux-5f4ef7197db98a40e43904806612a8ccf609c0a3.tar.xz linux-5f4ef7197db98a40e43904806612a8ccf609c0a3.zip |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next
Diffstat (limited to 'drivers/net')
31 files changed, 609 insertions, 177 deletions
diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c index f2c1439566b5..760c45c34ef3 100644 --- a/drivers/net/wireless/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/iwlwifi/dvm/power.c @@ -40,6 +40,10 @@ #include "commands.h" #include "power.h" +static bool force_cam; +module_param(force_cam, bool, 0644); +MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)"); + /* * Setting power level allows the card to go to sleep when not busy. * @@ -288,6 +292,11 @@ static void iwl_power_build_cmd(struct iwl_priv *priv, bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; int dtimper; + if (force_cam) { + iwl_power_sleep_cam_cmd(priv, cmd); + return; + } + dtimper = priv->hw->conf.ps_dtim_period ?: 1; if (priv->wowlan) diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index 51c41531d81d..51486cc9d943 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -67,7 +67,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 8 +#define IWL8000_UCODE_API_MAX 9 /* Oldest version we won't warn about */ #define IWL8000_UCODE_API_OK 8 @@ -119,10 +119,9 @@ const struct iwl_cfg iwl8260_2ac_cfg = { .ht_params = &iwl8000_ht_params, .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, - .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, }; -const struct iwl_cfg iwl8260_n_cfg = { +const struct iwl_cfg iwl8260_2ac_sdio_cfg = { .name = "Intel(R) Dual Band Wireless-AC 8260", .fw_name_pre = IWL8000_FW_PRE, IWL_DEVICE_8000, diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index b7047905f41a..034c2fc4b69f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -337,7 +337,7 @@ extern const struct iwl_cfg iwl7265_2ac_cfg; extern const struct iwl_cfg iwl7265_2n_cfg; extern const struct iwl_cfg iwl7265_n_cfg; extern const struct iwl_cfg iwl8260_2ac_cfg; -extern const struct iwl_cfg iwl8260_n_cfg; +extern const struct iwl_cfg iwl8260_2ac_sdio_cfg; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index f2a5c12269a3..bb842f4732dd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -155,6 +155,8 @@ static struct iwlwifi_opmode_table { [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL }, }; +#define IWL_DEFAULT_SCAN_CHANNELS 40 + /* * struct fw_sec: Just for the image parsing proccess. * For the fw storage we are using struct fw_desc. @@ -565,6 +567,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, } drv->fw.ucode_ver = le32_to_cpu(ucode->ver); + memcpy(drv->fw.human_readable, ucode->human_readable, + sizeof(drv->fw.human_readable)); build = le32_to_cpu(ucode->build); if (build) @@ -819,6 +823,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len)) goto invalid_tlv_len; break; + case IWL_UCODE_TLV_N_SCAN_CHANNELS: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + capa->n_scan_channels = + le32_to_cpup((__le32 *)tlv_data); + break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break; @@ -973,6 +983,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; fw->ucode_capa.standard_phy_calibration_size = IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; + fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS; if (!api_ok) api_ok = api_max; @@ -1394,3 +1405,7 @@ module_param_named(power_level, iwlwifi_mod_params.power_level, int, S_IRUGO); MODULE_PARM_DESC(power_level, "default power save level (range from 1 - 5, default: 1)"); + +module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO); +MODULE_PARM_DESC(dbgm, + "firmware monitor - to debug FW (default: false - needs lots of memory)"); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index c44cf1149648..07ff7e0028ee 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -779,7 +779,6 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, if (cfg->ht_params->ht40_bands & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_info->cap |= IEEE80211_HT_CAP_SGI_40; - ht_info->mcs.rx_mask[4] = 0x01; max_bit_rate = MAX_BIT_RATE_40_MHZ; } diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index 2953ffceda38..ced5ba95c23d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -74,12 +74,17 @@ * @IWL_FW_ERROR_DUMP_RXF: * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as * &struct iwl_fw_error_dump_txcmd packets + * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info + * info on the device / firmware. + * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor */ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_SRAM = 0, IWL_FW_ERROR_DUMP_REG = 1, IWL_FW_ERROR_DUMP_RXF = 2, IWL_FW_ERROR_DUMP_TXCMD = 3, + IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4, + IWL_FW_ERROR_DUMP_FW_MONITOR = 5, IWL_FW_ERROR_DUMP_MAX, }; @@ -120,13 +125,50 @@ struct iwl_fw_error_dump_txcmd { u8 data[]; } __packed; +enum iwl_fw_error_dump_family { + IWL_FW_ERROR_DUMP_FAMILY_7 = 7, + IWL_FW_ERROR_DUMP_FAMILY_8 = 8, +}; + +/** + * struct iwl_fw_error_dump_info - info on the device / firmware + * @device_family: the family of the device (7 / 8) + * @hw_step: the step of the device + * @fw_human_readable: human readable FW version + * @dev_human_readable: name of the device + * @bus_human_readable: name of the bus used + */ +struct iwl_fw_error_dump_info { + __le32 device_family; + __le32 hw_step; + u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ]; + u8 dev_human_readable[64]; + u8 bus_human_readable[8]; +} __packed; + +/** + * struct iwl_fw_error_fw_mon - FW monitor data + * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer + * @fw_mon_base_ptr: base pointer of the data + * @fw_mon_cycle_cnt: number of wrap arounds + * @reserved: for future use + * @data: captured data + */ +struct iwl_fw_error_fw_mon { + __le32 fw_mon_wr_ptr; + __le32 fw_mon_base_ptr; + __le32 fw_mon_cycle_cnt; + __le32 reserved[3]; + u8 data[]; +} __packed; + /** - * iwl_mvm_fw_error_next_data - advance fw error dump data pointer + * iwl_fw_error_next_data - advance fw error dump data pointer * @data: previous data block * Returns: next data block */ static inline struct iwl_fw_error_dump_data * -iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data) +iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) { return (void *)(data->data + le32_to_cpu(data->len)); } diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index b45e576a4b57..929a8063354c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -128,6 +128,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_CSCHEME = 28, IWL_UCODE_TLV_API_CHANGES_SET = 29, IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30, + IWL_UCODE_TLV_N_SCAN_CHANNELS = 31, }; struct iwl_ucode_tlv { @@ -136,7 +137,8 @@ struct iwl_ucode_tlv { u8 data[0]; }; -#define IWL_TLV_UCODE_MAGIC 0x0a4c5749 +#define IWL_TLV_UCODE_MAGIC 0x0a4c5749 +#define FW_VER_HUMAN_READABLE_SZ 64 struct iwl_tlv_ucode_header { /* @@ -147,7 +149,7 @@ struct iwl_tlv_ucode_header { */ __le32 zero; __le32 magic; - u8 human_readable[64]; + u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; __le32 ver; /* major/minor/API/serial */ __le32 build; __le64 ignore; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 0aa7c0085c9f..13e5d69845e9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -65,6 +65,8 @@ #include <linux/types.h> #include <net/mac80211.h> +#include "iwl-fw-file.h" + /** * enum iwl_ucode_tlv_flag - ucode API flags * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously @@ -117,11 +119,15 @@ enum iwl_ucode_tlv_flag { /** * enum iwl_ucode_tlv_api - ucode api * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field. + * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA. + * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit. */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), + IWL_UCODE_TLV_CAPA_EXTENDED_BEACON = BIT(1), IWL_UCODE_TLV_API_CSA_FLOW = BIT(4), + IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5), }; /** @@ -178,6 +184,7 @@ enum iwl_ucode_sec { struct iwl_ucode_capabilities { u32 max_probe_length; + u32 n_scan_channels; u32 standard_phy_calibration_size; u32 flags; u32 api[IWL_API_ARRAY_SIZE]; @@ -311,6 +318,7 @@ struct iwl_fw { bool mvm_fw; struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; + u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; }; #endif /* __iwl_fw_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index d051857729ab..f2d39cb011fc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -103,6 +103,7 @@ enum iwl_disable_11n { * @power_level: power level, default = 1 * @debug_level: levels are IWL_DL_* * @ant_coupling: antenna coupling in dB, default = 0 + * @fw_monitor: allow to use firmware monitor */ struct iwl_mod_params { int sw_crypto; @@ -120,6 +121,7 @@ struct iwl_mod_params { int ant_coupling; char *nvm_file; bool uapsd_disable; + bool fw_monitor; }; #endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 85eee79c495c..f0ae038f3339 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -174,7 +174,9 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = { * @NVM_CHANNEL_IBSS: usable as an IBSS channel * @NVM_CHANNEL_ACTIVE: active scanning allowed * @NVM_CHANNEL_RADAR: radar detection required - * @NVM_CHANNEL_DFS: dynamic freq selection candidate + * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed + * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS + * on same channel on 2.4 or same UNII band on 5.2 * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?) * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) @@ -185,7 +187,8 @@ enum iwl_nvm_channel_flags { NVM_CHANNEL_IBSS = BIT(1), NVM_CHANNEL_ACTIVE = BIT(3), NVM_CHANNEL_RADAR = BIT(4), - NVM_CHANNEL_DFS = BIT(7), + NVM_CHANNEL_INDOOR_ONLY = BIT(5), + NVM_CHANNEL_GO_CONCURRENT = BIT(6), NVM_CHANNEL_WIDE = BIT(8), NVM_CHANNEL_40MHZ = BIT(9), NVM_CHANNEL_80MHZ = BIT(10), @@ -273,6 +276,16 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, if (ch_flags & NVM_CHANNEL_RADAR) channel->flags |= IEEE80211_CHAN_RADAR; + if (ch_flags & NVM_CHANNEL_INDOOR_ONLY) + channel->flags |= IEEE80211_CHAN_INDOOR_ONLY; + + /* Set the GO concurrent flag only in case that NO_IR is set. + * Otherwise it is meaningless + */ + if ((ch_flags & NVM_CHANNEL_GO_CONCURRENT) && + (channel->flags & IEEE80211_CHAN_NO_IR)) + channel->flags |= IEEE80211_CHAN_GO_CONCURRENT; + /* Initialize regulatory-based run-time data */ /* @@ -282,7 +295,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, channel->max_power = DEFAULT_MAX_TX_POWER; is_5ghz = channel->band == IEEE80211_BAND_5GHZ; IWL_DEBUG_EEPROM(dev, - "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", + "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", channel->hw_value, is_5ghz ? "5.2" : "2.4", CHECK_AND_PRINT_I(VALID), @@ -290,7 +303,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, CHECK_AND_PRINT_I(ACTIVE), CHECK_AND_PRINT_I(RADAR), CHECK_AND_PRINT_I(WIDE), - CHECK_AND_PRINT_I(DFS), + CHECK_AND_PRINT_I(INDOOR_ONLY), + CHECK_AND_PRINT_I(GO_CONCURRENT), ch_flags, channel->max_power, ((ch_flags & NVM_CHANNEL_IBSS) && @@ -462,7 +476,8 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg, data->hw_addr[5] = hw_addr[4]; } -static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg, +static void iwl_set_hw_address_family_8000(struct device *dev, + const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *mac_override, const __le16 *nvm_hw) @@ -481,20 +496,28 @@ static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg, data->hw_addr[4] = hw_addr[5]; data->hw_addr[5] = hw_addr[4]; - if (is_valid_ether_addr(hw_addr)) + if (is_valid_ether_addr(data->hw_addr)) return; + + IWL_ERR_DEV(dev, + "mac address from nvm override section is not valid\n"); } - /* take the MAC address from the OTP */ - hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000); - data->hw_addr[0] = hw_addr[3]; - data->hw_addr[1] = hw_addr[2]; - data->hw_addr[2] = hw_addr[1]; - data->hw_addr[3] = hw_addr[0]; + if (nvm_hw) { + /* take the MAC address from the OTP */ + hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000); + data->hw_addr[0] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; + + hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000); + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[5] = hw_addr[0]; + return; + } - hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000); - data->hw_addr[4] = hw_addr[1]; - data->hw_addr[5] = hw_addr[0]; + IWL_ERR_DEV(dev, "mac address is not found\n"); } struct iwl_nvm_data * @@ -556,7 +579,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, rx_chains); } else { /* MAC address in family 8000 */ - iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw); + iwl_set_hw_address_family_8000(dev, cfg, data, mac_override, + nvm_hw); iwl_init_sbands(dev, cfg, data, regulatory, sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 4997e27672b3..47033a35a402 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -359,4 +359,10 @@ enum secure_load_status_reg { #define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10) #define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c) +/* FW monitor */ +#define MON_BUFF_BASE_ADDR (0xa03c3c) +#define MON_BUFF_END_ADDR (0xa03c40) +#define MON_BUFF_WRPTR (0xa03c44) +#define MON_BUFF_CYCLE_CNT (0xa03c48) + #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index c8c3b38228f0..7e0388a32912 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -100,12 +100,13 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { #undef EVENT_PRIO_ANT -#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62) -#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65) #define BT_ANTENNA_COUPLING_THRESHOLD (30) static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) { + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return 0; + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0, sizeof(struct iwl_bt_coex_prio_tbl_cmd), &iwl_bt_prio_tbl); @@ -535,7 +536,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) if (!chanctx_conf || chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { rcu_read_unlock(); - return BT_COEX_LOOSE_LUT; + return BT_COEX_INVALID_LUT; } ret = BT_COEX_TX_DIS_LUT; @@ -578,6 +579,29 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) return -ENOMEM; cmd.data[0] = bt_cmd; + lockdep_assert_held(&mvm->mutex); + + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { + switch (mvm->bt_force_ant_mode) { + case BT_FORCE_ANT_AUTO: + flags = BT_COEX_AUTO; + break; + case BT_FORCE_ANT_BT: + flags = BT_COEX_BT; + break; + case BT_FORCE_ANT_WIFI: + flags = BT_COEX_WIFI; + break; + default: + WARN_ON(1); + flags = 0; + } + + bt_cmd->flags = cpu_to_le32(flags); + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE); + goto send_cmd; + } + bt_cmd->max_kill = 5; bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD; bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling; @@ -642,6 +666,7 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); +send_cmd: memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); @@ -780,9 +805,9 @@ void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, mvmvif->bf_data.last_bt_coex_event = rssi; mvmvif->bf_data.bt_coex_max_thold = - enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0; + enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; mvmvif->bf_data.bt_coex_min_thold = - enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0; + enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; } /* must be called under rcu_read_lock */ @@ -919,7 +944,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, /* if the RSSI isn't valid, fake it is very low */ if (!ave_rssi) ave_rssi = -100; - if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) { + if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); @@ -930,7 +955,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, * the iteration, if one interface's rssi isn't good enough, * bt_kill_msk will be set to default values. */ - } else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) { + } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); @@ -955,6 +980,10 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) struct iwl_bt_coex_ci_cmd cmd = {}; u8 ci_bw_idx; + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + rcu_read_lock(); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, @@ -1121,6 +1150,10 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + /* * Rssi update while not associated - can happen since the statistics * are handled asynchronously @@ -1177,9 +1210,12 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, BT_HIGH_TRAFFIC) return LINK_QUAL_AGG_TIME_LIMIT_DEF; + if (mvm->last_bt_notif.ttc_enabled) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - if (lut_type == BT_COEX_LOOSE_LUT) + if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) return LINK_QUAL_AGG_TIME_LIMIT_DEF; /* tight coex, high bt traffic, reduce AGG time limit */ @@ -1190,18 +1226,29 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + enum iwl_bt_coex_lut_type lut_type; + + if (mvm->last_bt_notif.ttc_enabled) + return true; 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. + * In Tight / TxTxDis, 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. + * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. */ - return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT; + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + return lut_type != BT_COEX_LOOSE_LUT; +} + +bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) +{ + return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF; } bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, @@ -1274,6 +1321,10 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return 0; + if (ant_isolation == mvm->last_ant_isol) return 0; diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index 51685693af2e..ca79f7160573 100644 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -79,6 +79,8 @@ #define IWL_MVM_PS_SNOOZE_WINDOW 50 #define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25 #define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64 +#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62 +#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65 #define IWL_MVM_BT_COEX_SYNC2SCO 1 #define IWL_MVM_BT_COEX_CORUNNING 1 #define IWL_MVM_BT_COEX_MPLUT 1 diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 29ca72695eaa..602bbd29ec5a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -455,6 +455,43 @@ iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf, return count; } +static ssize_t +iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + static const char * const modes_str[BT_FORCE_ANT_MAX] = { + [BT_FORCE_ANT_DIS] = "dis", + [BT_FORCE_ANT_AUTO] = "auto", + [BT_FORCE_ANT_BT] = "bt", + [BT_FORCE_ANT_WIFI] = "wifi", + }; + int ret, bt_force_ant_mode; + + for (bt_force_ant_mode = 0; + bt_force_ant_mode < ARRAY_SIZE(modes_str); + bt_force_ant_mode++) { + if (!strcmp(buf, modes_str[bt_force_ant_mode])) + break; + } + + if (bt_force_ant_mode >= ARRAY_SIZE(modes_str)) + return -EINVAL; + + ret = 0; + mutex_lock(&mvm->mutex); + if (mvm->bt_force_ant_mode == bt_force_ant_mode) + goto out; + + mvm->bt_force_ant_mode = bt_force_ant_mode; + IWL_DEBUG_COEX(mvm, "Force mode: %s\n", + modes_str[mvm->bt_force_ant_mode]); + ret = iwl_send_bt_init_conf(mvm); + +out: + mutex_unlock(&mvm->mutex); + return ret ?: count; +} + #define PRINT_STATS_LE32(_str, _val) \ pos += scnprintf(buf + pos, bufsz - pos, \ fmt_table, _str, \ @@ -1101,6 +1138,7 @@ MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats); MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10); MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10); MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10); +MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10); MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); @@ -1142,6 +1180,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR); diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index 5fe82c29c8ad..39cb33a19862 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -76,6 +76,9 @@ * @BT_COEX_2W: * @BT_COEX_3W: * @BT_COEX_NW: + * @BT_COEX_AUTO: + * @BT_COEX_BT: Antenna is for BT (manufacuring tests) + * @BT_COEX_WIFI: Antenna is for BT (manufacuring tests) * @BT_COEX_SYNC2SCO: * @BT_COEX_CORUNNING: * @BT_COEX_MPLUT: @@ -89,6 +92,9 @@ enum iwl_bt_coex_flags { BT_COEX_2W = 0x1 << BT_COEX_MODE_POS, BT_COEX_3W = 0x2 << BT_COEX_MODE_POS, BT_COEX_NW = 0x3 << BT_COEX_MODE_POS, + BT_COEX_AUTO = 0x5 << BT_COEX_MODE_POS, + BT_COEX_BT = 0x6 << BT_COEX_MODE_POS, + BT_COEX_WIFI = 0x7 << BT_COEX_MODE_POS, BT_COEX_SYNC2SCO = BIT(7), BT_COEX_CORUNNING = BIT(8), BT_COEX_MPLUT = BIT(9), @@ -299,6 +305,7 @@ enum iwl_bt_activity_grading { * @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 + * @ttc_enabled: true if ttc has been enabled by the firmware * @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 @@ -311,7 +318,8 @@ struct iwl_bt_coex_profile_notif { u8 bt_traffic_load; u8 bt_agg_traffic_load; u8 bt_ci_compliance; - u8 reserved[3]; + u8 ttc_enabled; + __le16 reserved; __le32 primary_ch_lut; __le32 secondary_ch_lut; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index cbbcd8e284e4..c3a8c86b550d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -336,7 +336,7 @@ struct iwl_beacon_filter_cmd { #define IWL_BF_DEBUG_FLAG_D0I3 0 #define IWL_BF_ESCAPE_TIMER_DEFAULT 50 -#define IWL_BF_ESCAPE_TIMER_D0I3 1024 +#define IWL_BF_ESCAPE_TIMER_D0I3 0 #define IWL_BF_ESCAPE_TIMER_MAX 1024 #define IWL_BF_ESCAPE_TIMER_MIN 0 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 6959fda3fe09..1d586923d5b7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -170,18 +170,12 @@ enum iwl_scan_type { }; /* SCAN_ACTIVITY_TYPE_E_VER_1 */ /** - * Maximal number of channels to scan - * it should be equal to: - * max(IWL_NUM_CHANNELS, IWL_NUM_CHANNELS_FAMILY_8000) - */ -#define MAX_NUM_SCAN_CHANNELS 50 - -/** * struct iwl_scan_cmd - scan request command * ( SCAN_REQUEST_CMD = 0x80 ) * @len: command length in bytes * @scan_flags: scan flags from SCAN_FLAGS_* - * @channel_count: num of channels in channel list (1 - MAX_NUM_SCAN_CHANNELS) + * @channel_count: num of channels in channel list + * (1 - ucode_capa.n_scan_channels) * @quiet_time: in msecs, dwell this time for active scan on quiet channels * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than * this number of packets were received (typically 1) @@ -345,7 +339,7 @@ struct iwl_scan_results_notif { * @last_channel: last channel that was scanned * @tsf_low: TSF timer (lower half) in usecs * @tsf_high: TSF timer (higher half) in usecs - * @results: all scan results, only "scanned_channels" of them are valid + * @results: array of scan results, only "scanned_channels" of them are valid */ struct iwl_scan_complete_notif { u8 scanned_channels; @@ -354,11 +348,10 @@ struct iwl_scan_complete_notif { u8 last_channel; __le32 tsf_low; __le32 tsf_high; - struct iwl_scan_results_notif results[MAX_NUM_SCAN_CHANNELS]; + struct iwl_scan_results_notif results[]; } __packed; /* SCAN_COMPLETE_NTF_API_S_VER_2 */ /* 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 @@ -423,36 +416,24 @@ enum iwl_scan_offload_channel_flags { IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL = BIT(25), }; -/** - * iwl_scan_channel_cfg - SCAN_CHANNEL_CFG_S - * @type: bitmap - see enum iwl_scan_offload_channel_flags. - * 0: passive (0) or active (1) scan. - * 1-20: directed scan to i'th ssid. - * 22: channel width configuation - 1 for narrow. - * 24: full scan. - * 25: partial scan. - * @channel_number: channel number 1-13 etc. - * @iter_count: repetition count for the channel. - * @iter_interval: interval between two innteration on one channel. - * @dwell_time: entry 0 - active scan, entry 1 - passive scan. +/* channel configuration for struct iwl_scan_offload_cfg. Each channels needs: + * __le32 type: bitmap; bits 1-20 are for directed scan to i'th ssid and + * see enum iwl_scan_offload_channel_flags. + * __le16 channel_number: channel number 1-13 etc. + * __le16 iter_count: repetition count for the channel. + * __le32 iter_interval: interval between two innteration on one channel. + * u8 active_dwell. + * u8 passive_dwell. */ -struct iwl_scan_channel_cfg { - __le32 type[IWL_MAX_SCAN_CHANNELS]; - __le16 channel_number[IWL_MAX_SCAN_CHANNELS]; - __le16 iter_count[IWL_MAX_SCAN_CHANNELS]; - __le32 iter_interval[IWL_MAX_SCAN_CHANNELS]; - u8 dwell_time[IWL_MAX_SCAN_CHANNELS][2]; -} __packed; +#define IWL_SCAN_CHAN_SIZE 14 /** * iwl_scan_offload_cfg - SCAN_OFFLOAD_CONFIG_API_S * @scan_cmd: scan command fixed part - * @channel_cfg: scan channel configuration - * @data: probe request frames (one per band) + * @data: scan channel configuration and probe request frames */ struct iwl_scan_offload_cfg { struct iwl_scan_offload_cmd scan_cmd; - struct iwl_scan_channel_cfg channel_cfg; u8 data[0]; } __packed; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 39cebee8016f..47bd0406355d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h @@ -67,7 +67,7 @@ * enum iwl_sta_flags - flags for the ADD_STA host command * @STA_FLG_REDUCED_TX_PWR_CTRL: * @STA_FLG_REDUCED_TX_PWR_DATA: - * @STA_FLG_FLG_ANT_MSK: Antenna selection + * @STA_FLG_DISABLE_TX: set if TX should be disabled * @STA_FLG_PS: set if STA is in Power Save * @STA_FLG_INVALID: set if STA is invalid * @STA_FLG_DLP_EN: Direct Link Protocol is enabled @@ -91,10 +91,7 @@ enum iwl_sta_flags { STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3), STA_FLG_REDUCED_TX_PWR_DATA = BIT(6), - STA_FLG_FLG_ANT_A = (1 << 4), - STA_FLG_FLG_ANT_B = (2 << 4), - STA_FLG_FLG_ANT_MSK = (STA_FLG_FLG_ANT_A | - STA_FLG_FLG_ANT_B), + STA_FLG_DISABLE_TX = BIT(4), STA_FLG_PS = BIT(8), STA_FLG_DRAIN_FLOW = BIT(12), diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index 6cc5f52b807f..bdd6ff648626 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -549,6 +549,20 @@ struct iwl_beacon_notif { } __packed; /** + * struct iwl_extended_beacon_notif - notifies about beacon transmission + * @beacon_notify_hdr: tx response command associated with the beacon + * @tsf: last beacon tsf + * @ibss_mgr_status: whether IBSS is manager + * @gp2: last beacon time in gp2 + */ +struct iwl_extended_beacon_notif { + struct iwl_mvm_tx_resp beacon_notify_hdr; + __le64 tsf; + __le32 ibss_mgr_status; + __le32 gp2; +} __packed; /* BEACON_NTFY_API_S_VER_5 */ + +/** * enum iwl_dump_control - dump (flush) control flags * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty * and the TFD queues are empty. diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 8b5302777632..aad36212c4d7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -904,7 +904,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, struct iwl_mac_beacon_cmd beacon_cmd = {}; struct ieee80211_tx_info *info; u32 beacon_skb_len; - u32 rate; + u32 rate, tx_flags; if (WARN_ON(!beacon)) return -EINVAL; @@ -914,14 +914,17 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, /* TODO: for now the beacon template id is set to be the mac context id. * Might be better to handle it as another resource ... */ beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); + info = IEEE80211_SKB_CB(beacon); /* Set up TX command fields */ beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len); beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id; beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); - beacon_cmd.tx.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | - TX_CMD_FLG_BT_DIS | - TX_CMD_FLG_TSF); + tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF; + tx_flags |= + iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) << + TX_CMD_FLG_BT_PRIO_POS; + beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); mvm->mgmt_last_antenna_idx = iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, @@ -931,8 +934,6 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS); - info = IEEE80211_SKB_CB(beacon); - if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) { rate = IWL_FIRST_OFDM_RATE; } else { @@ -1205,19 +1206,31 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_beacon_notif *beacon = (void *)pkt->data; - u16 status __maybe_unused = - le16_to_cpu(beacon->beacon_notify_hdr.status.status); - u32 rate __maybe_unused = - le32_to_cpu(beacon->beacon_notify_hdr.initial_rate); + struct iwl_mvm_tx_resp *beacon_notify_hdr; + u64 tsf; lockdep_assert_held(&mvm->mutex); - IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n", - status & TX_STATUS_MSK, - beacon->beacon_notify_hdr.failure_frame, - le64_to_cpu(beacon->tsf), - rate); + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_CAPA_EXTENDED_BEACON) { + struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; + + beacon_notify_hdr = &beacon->beacon_notify_hdr; + tsf = le64_to_cpu(beacon->tsf); + mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); + } else { + struct iwl_beacon_notif *beacon = (void *)pkt->data; + + beacon_notify_hdr = &beacon->beacon_notify_hdr; + tsf = le64_to_cpu(beacon->tsf); + } + + IWL_DEBUG_RX(mvm, + "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n", + le16_to_cpu(beacon_notify_hdr->status.status) & + TX_STATUS_MSK, + beacon_notify_hdr->failure_frame, tsf, + mvm->ap_last_beacon_gp2, + le32_to_cpu(beacon_notify_hdr->initial_rate)); if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) { if (!ieee80211_csa_is_complete(mvm->csa_vif)) { diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 7dd363dd3ad3..72e3146625f8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -374,6 +374,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_sched_scan_ie_len = SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | + NL80211_FEATURE_LOW_PRIORITY_SCAN | NL80211_FEATURE_P2P_GO_OPPPS; mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; @@ -688,6 +689,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw) iwl_mvm_restart_cleanup(mvm); ret = iwl_mvm_up(mvm); + + if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* Something went wrong - we need to finish some cleanup + * that normally iwl_mvm_mac_restart_complete() below + * would do. + */ + clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + iwl_mvm_d0i3_enable_tx(mvm, NULL); + } + mutex_unlock(&mvm->mutex); return ret; @@ -1464,6 +1475,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); mvmvif->ap_ibss_active = false; + mvm->ap_last_beacon_gp2 = 0; iwl_mvm_bt_coex_vif_change(mvm); @@ -1543,7 +1555,8 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req = &hw_req->req; int ret; - if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS) + if (req->n_channels == 0 || + req->n_channels > mvm->fw->ucode_capa.n_scan_channels) return -EINVAL; mutex_lock(&mvm->mutex); diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 97630f9a7cb9..f7e54a57f46d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -235,6 +235,15 @@ enum iwl_mvm_ref_type { IWL_MVM_REF_COUNT, }; +enum iwl_bt_force_ant_mode { + BT_FORCE_ANT_DIS = 0, + BT_FORCE_ANT_AUTO, + BT_FORCE_ANT_BT, + BT_FORCE_ANT_WIFI, + + BT_FORCE_ANT_MAX, +}; + /** * struct iwl_mvm_vif_bf_data - beacon filtering related data * @bf_enabled: indicates if beacon filtering is enabled @@ -629,6 +638,7 @@ struct iwl_mvm { u32 last_ant_isol; u8 last_corun_lut; u8 bt_tx_prio; + enum iwl_bt_force_ant_mode bt_force_ant_mode; /* Thermal Throttling and CTkill */ struct iwl_mvm_tt_mgmt thermal_throttle; @@ -648,6 +658,9 @@ struct iwl_mvm { bool ps_disabled; struct ieee80211_vif *csa_vif; + + /* system time of last beacon (for AP/GO interface) */ + u32 ap_last_beacon_gp2; }; /* Extract MVM priv from op_mode and _hw */ @@ -963,6 +976,7 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, struct ieee80211_sta *sta); bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct ieee80211_sta *sta); +bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm); bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, enum ieee80211_band band); u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 808f78f6fbf9..1f1a550828fa 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -69,7 +69,9 @@ /* Default NVM size to read */ #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWL_MAX_NVM_SECTION_SIZE 7000 +#define IWL_MAX_NVM_SECTION_SIZE 0x1b58 +#define IWL_MAX_NVM_8000A_SECTION_SIZE 0xffc +#define IWL_MAX_NVM_8000B_SECTION_SIZE 0x1ffc #define NVM_WRITE_OPCODE 1 #define NVM_READ_OPCODE 0 @@ -219,7 +221,7 @@ static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, * without overflowing, so no check is needed. */ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, - u8 *data) + u8 *data, u32 size_read) { u16 length, offset = 0; int ret; @@ -231,6 +233,13 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, /* Read the NVM until exhausted (reading less than requested) */ while (ret == length) { + /* Check no memory assumptions fail and cause an overflow */ + if ((size_read + offset + length) > + mvm->cfg->base_params->eeprom_size) { + IWL_ERR(mvm, "EEPROM size is too small for NVM\n"); + return -ENOBUFS; + } + ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); if (ret < 0) { IWL_DEBUG_EEPROM(mvm->trans->dev, @@ -326,6 +335,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) u8 data[]; } *file_sec; const u8 *eof, *temp; + int max_section_size; #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) #define NVM_WORD2_ID(x) (x >> 12) @@ -334,6 +344,14 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); + /* Maximal size depends on HW family and step */ + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + max_section_size = IWL_MAX_NVM_SECTION_SIZE; + else if ((mvm->trans->hw_rev & 0xc) == 0) /* Family 8000 A-step */ + max_section_size = IWL_MAX_NVM_8000A_SECTION_SIZE; + else /* Family 8000 B-step */ + max_section_size = IWL_MAX_NVM_8000B_SECTION_SIZE; + /* * Obtain NVM image via request_firmware. Since we already used * request_firmware_nowait() for the firmware binary load and only @@ -392,7 +410,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) le16_to_cpu(file_sec->word1)); } - if (section_size > IWL_MAX_NVM_SECTION_SIZE) { + if (section_size > max_section_size) { IWL_ERR(mvm, "ERROR - section too large (%d)\n", section_size); ret = -EINVAL; @@ -459,6 +477,7 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) { int ret, section; + u32 size_read = 0; u8 *nvm_buffer, *temp; if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) @@ -475,9 +494,11 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) return -ENOMEM; for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { /* we override the constness for initial read */ - ret = iwl_nvm_read_section(mvm, section, nvm_buffer); + ret = iwl_nvm_read_section(mvm, section, nvm_buffer, + size_read); if (ret < 0) continue; + size_read += ret; temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); if (!temp) { ret = -ENOMEM; diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index cc2f7de396de..7bb763f3052b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -504,7 +504,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, scan_size = sizeof(struct iwl_scan_cmd) + mvm->fw->ucode_capa.max_probe_length + - (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel)); + (mvm->fw->ucode_capa.n_scan_channels * + sizeof(struct iwl_scan_channel)); mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); if (!mvm->scan_cmd) goto out_free; @@ -826,6 +827,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) { struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_data *dump_data; + struct iwl_fw_error_dump_info *dump_info; u32 file_len; u32 trans_len; @@ -834,10 +836,11 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) if (mvm->fw_error_dump) return; - file_len = mvm->fw_error_sram_len + + file_len = sizeof(*dump_file) + + sizeof(*dump_data) * 3 + + mvm->fw_error_sram_len + mvm->fw_error_rxf_len + - sizeof(*dump_file) + - sizeof(*dump_data) * 2; + sizeof(*dump_info); trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0); if (trans_len) @@ -852,11 +855,27 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); dump_file->file_len = cpu_to_le32(file_len); dump_data = (void *)dump_file->data; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_info)); + dump_info = (void *) dump_data->data; + dump_info->device_family = + mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); + memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, + sizeof(dump_info->fw_human_readable)); + strncpy(dump_info->dev_human_readable, mvm->cfg->name, + sizeof(dump_info->dev_human_readable)); + strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, + sizeof(dump_info->bus_human_readable)); + + dump_data = iwl_fw_error_next_data(dump_data); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len); memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len); - dump_data = iwl_mvm_fw_error_next_data(dump_data); + dump_data = iwl_fw_error_next_data(dump_data); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); dump_data->len = cpu_to_le32(mvm->fw_error_sram_len); @@ -876,7 +895,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) mvm->fw_error_sram_len = 0; if (trans_len) { - void *buf = iwl_mvm_fw_error_next_data(dump_data); + void *buf = iwl_fw_error_next_data(dump_data); u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, trans_len); dump_data = (void *)((u8 *)buf + real_trans_len); diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 3206fa097255..f2dde5604a80 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -275,7 +275,7 @@ static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - int n_ssids, + int n_ssids, u32 flags, struct iwl_mvm_scan_params *params) { bool global_bound = false; @@ -297,6 +297,9 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, params->max_out_time = 250; } + if (flags & NL80211_SCAN_FLAG_LOW_PRIORITY) + params->max_out_time = 200; + not_bound: for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { @@ -333,16 +336,14 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n"); mvm->scan_status = IWL_MVM_SCAN_OS; - memset(cmd, 0, sizeof(struct iwl_scan_cmd) + - mvm->fw->ucode_capa.max_probe_length + - (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel))); + memset(cmd, 0, ksize(cmd)); cmd->channel_count = (u8)req->n_channels; cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME); cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm); - iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, ¶ms); + iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, req->flags, ¶ms); cmd->max_out_time = cpu_to_le32(params.max_out_time); cmd->suspend_time = cpu_to_le32(params.suspend_time); if (params.passive_fragmented) @@ -597,9 +598,7 @@ static void iwl_build_scan_cmd(struct iwl_mvm *mvm, struct iwl_scan_offload_cmd *scan, struct iwl_mvm_scan_params *params) { - scan->channel_count = - mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + - mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; + scan->channel_count = req->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; @@ -676,68 +675,50 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, static void iwl_build_channel_cfg(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req, - struct iwl_scan_channel_cfg *channels, + u8 *channels_buffer, enum ieee80211_band band, - int *head, int *tail, + int *head, u32 ssid_bitmap, struct iwl_mvm_scan_params *params) { - struct ieee80211_supported_band *s_band; - int n_channels = req->n_channels; - int i, j, index = 0; - bool partial; + u32 n_channels = mvm->fw->ucode_capa.n_scan_channels; + __le32 *type = (__le32 *)channels_buffer; + __le16 *channel_number = (__le16 *)(type + n_channels); + __le16 *iter_count = channel_number + n_channels; + __le32 *iter_interval = (__le32 *)(iter_count + n_channels); + u8 *active_dwell = (u8 *)(iter_interval + n_channels); + u8 *passive_dwell = active_dwell + n_channels; + int i, index = 0; + + for (i = 0; i < req->n_channels; i++) { + struct ieee80211_channel *chan = req->channels[i]; + + if (chan->band != band) + continue; - /* - * 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. - */ - 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] = params->dwell[band].active; - channels->dwell_time[index][1] = params->dwell[band].passive; + index = *head; + (*head)++; + + channel_number[index] = cpu_to_le16(chan->hw_value); + active_dwell[index] = params->dwell[band].active; + passive_dwell[index] = params->dwell[band].passive; - channels->iter_count[index] = cpu_to_le16(1); - channels->iter_interval[index] = 0; + iter_count[index] = cpu_to_le16(1); + iter_interval[index] = 0; - if (!(s_band->channels[i].flags & IEEE80211_CHAN_NO_IR)) - channels->type[index] |= + if (!(chan->flags & IEEE80211_CHAN_NO_IR)) + 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); + type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL | + IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL); - if (s_band->channels[i].flags & IEEE80211_CHAN_NO_HT40) - channels->type[index] |= + if (chan->flags & IEEE80211_CHAN_NO_HT40) + 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); + type[index] |= cpu_to_le32(ssid_bitmap); } } @@ -749,10 +730,10 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, 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 - 1; u32 ssid_bitmap; int cmd_len; int ret; + u8 *probes; struct iwl_scan_offload_cfg *scan_cfg; struct iwl_host_cmd cmd = { @@ -763,13 +744,17 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); cmd_len = sizeof(struct iwl_scan_offload_cfg) + + mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE + 2 * SCAN_OFFLOAD_PROBE_REQ_SIZE; scan_cfg = kzalloc(cmd_len, GFP_KERNEL); if (!scan_cfg) return -ENOMEM; - iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, ¶ms); + probes = scan_cfg->data + + mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE; + + iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, ¶ms); iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, ¶ms); scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len); @@ -779,19 +764,19 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, 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, + probes); + iwl_build_channel_cfg(mvm, req, scan_cfg->data, + IEEE80211_BAND_2GHZ, &head, ssid_bitmap, ¶ms); } 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 + + probes + SCAN_OFFLOAD_PROBE_REQ_SIZE); - iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, - IEEE80211_BAND_5GHZ, &head, &tail, + iwl_build_channel_cfg(mvm, req, scan_cfg->data, + IEEE80211_BAND_5GHZ, &head, ssid_bitmap, ¶ms); } diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 1fb01ea2e704..d3a6cf7558eb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -1448,3 +1448,23 @@ int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, return 0; } + +void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, bool disable) +{ + struct iwl_mvm_add_sta_cmd cmd = { + .add_modify = STA_MODE_MODIFY, + .sta_id = mvmsta->sta_id, + .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0, + .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX), + .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), + }; + int ret; + + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_DISABLE_STA_TX)) + return; + + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index d98e8a2142b8..10c1a5352651 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -404,5 +404,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, bool agg); int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool drain); +void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, bool disable); #endif /* __sta_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 3846a6c41eb1..4f7cff506ee7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -205,7 +205,13 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, mvm->mgmt_last_antenna_idx = iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, mvm->mgmt_last_antenna_idx); - rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; + + if (info->band == IEEE80211_BAND_2GHZ && + !iwl_mvm_bt_coex_is_shared_ant_avail(mvm)) + rate_flags = BIT(ANT_A) << RATE_MCS_ANT_POS; + else + rate_flags = + BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; /* Set CCK flag as needed */ if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 6c22b23a2845..78f72c34438a 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -260,6 +260,9 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) * @wd_timeout: queue watchdog timeout (jiffies) * @reg_lock: protect hw register access * @cmd_in_flight: true when we have a host command in flight + * @fw_mon_phys: physical address of the buffer for the firmware monitor + * @fw_mon_page: points to the first page of the buffer for the firmware monitor + * @fw_mon_size: size of the buffer for the firmware monitor */ struct iwl_trans_pcie { struct iwl_rxq rxq; @@ -312,6 +315,10 @@ struct iwl_trans_pcie { /*protect hw register */ spinlock_t reg_lock; bool cmd_in_flight; + + dma_addr_t fw_mon_phys; + struct page *fw_mon_page; + u32 fw_mon_size; }; #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 788085bc65d7..5703a3d7799b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -76,6 +76,68 @@ #include "iwl-fw-error-dump.h" #include "internal.h" +static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (!trans_pcie->fw_mon_page) + return; + + dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys, + trans_pcie->fw_mon_size, DMA_FROM_DEVICE); + __free_pages(trans_pcie->fw_mon_page, + get_order(trans_pcie->fw_mon_size)); + trans_pcie->fw_mon_page = NULL; + trans_pcie->fw_mon_phys = 0; + trans_pcie->fw_mon_size = 0; +} + +static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct page *page; + dma_addr_t phys; + u32 size; + u8 power; + + if (trans_pcie->fw_mon_page) { + dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys, + trans_pcie->fw_mon_size, + DMA_FROM_DEVICE); + return; + } + + phys = 0; + for (power = 26; power >= 11; power--) { + int order; + + size = BIT(power); + order = get_order(size); + page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO, + order); + if (!page) + continue; + + phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order, + DMA_FROM_DEVICE); + if (dma_mapping_error(trans->dev, phys)) { + __free_pages(page, order); + continue; + } + IWL_INFO(trans, + "Allocated 0x%08x bytes (order %d) for firmware monitor.\n", + size, order); + break; + } + + if (!page) + return; + + trans_pcie->fw_mon_page = page; + trans_pcie->fw_mon_phys = phys; + trans_pcie->fw_mon_size = size; +} + static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) { iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, @@ -675,6 +737,7 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, const struct fw_img *image) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret = 0; int first_ucode_section; @@ -733,6 +796,20 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, return ret; } + /* supported for 7000 only for the moment */ + if (iwlwifi_mod_params.fw_monitor && + trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { + iwl_pcie_alloc_fw_monitor(trans); + + if (trans_pcie->fw_mon_size) { + iwl_write_prph(trans, MON_BUFF_BASE_ADDR, + trans_pcie->fw_mon_phys >> 4); + iwl_write_prph(trans, MON_BUFF_END_ADDR, + (trans_pcie->fw_mon_phys + + trans_pcie->fw_mon_size) >> 4); + } + } + /* release CPU reset */ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); @@ -1126,6 +1203,8 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) if (trans_pcie->napi.poll) netif_napi_del(&trans_pcie->napi); + iwl_pcie_free_fw_monitor(trans); + kfree(trans); } @@ -1494,10 +1573,12 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, txq = &trans_pcie->txq[cnt]; q = &txq->q; pos += scnprintf(buf + pos, bufsz - pos, - "hwq %.2d: read=%u write=%u use=%d stop=%d\n", + "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d%s\n", cnt, q->read_ptr, q->write_ptr, !!test_bit(cnt, trans_pcie->queue_used), - !!test_bit(cnt, trans_pcie->queue_stopped)); + !!test_bit(cnt, trans_pcie->queue_stopped), + txq->need_update, + (cnt == trans_pcie->cmd_queue ? " HCMD" : "")); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); @@ -1519,6 +1600,10 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, rxq->read); pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", rxq->write); + pos += scnprintf(buf + pos, bufsz - pos, "write_actual: %u\n", + rxq->write_actual); + pos += scnprintf(buf + pos, bufsz - pos, "need_update: %d\n", + rxq->need_update); pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", rxq->free_count); if (rxq->rb_stts) { @@ -1698,10 +1783,15 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 len; int i, ptr; + len = sizeof(*data) + + cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); + + if (trans_pcie->fw_mon_page) + len += sizeof(*data) + sizeof(struct iwl_fw_error_fw_mon) + + trans_pcie->fw_mon_size; + if (!buf) - return sizeof(*data) + - cmdq->q.n_window * (sizeof(*txcmd) + - TFD_MAX_PAYLOAD_SIZE); + return len; len = 0; data = buf; @@ -1729,7 +1819,40 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, spin_unlock_bh(&cmdq->lock); data->len = cpu_to_le32(len); - return sizeof(*data) + len; + len += sizeof(*data); + + if (trans_pcie->fw_mon_page) { + struct iwl_fw_error_fw_mon *fw_mon_data; + + data = iwl_fw_error_next_data(data); + data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR); + data->len = cpu_to_le32(trans_pcie->fw_mon_size + + sizeof(*fw_mon_data)); + fw_mon_data = (void *)data->data; + fw_mon_data->fw_mon_wr_ptr = + cpu_to_le32(iwl_read_prph(trans, MON_BUFF_WRPTR)); + fw_mon_data->fw_mon_cycle_cnt = + cpu_to_le32(iwl_read_prph(trans, MON_BUFF_CYCLE_CNT)); + fw_mon_data->fw_mon_base_ptr = + cpu_to_le32(iwl_read_prph(trans, MON_BUFF_BASE_ADDR)); + + /* + * The firmware is now asserted, it won't write anything to + * the buffer. CPU can take ownership to fetch the data. + * The buffer will be handed back to the device before the + * firmware will be restarted. + */ + dma_sync_single_for_cpu(trans->dev, trans_pcie->fw_mon_phys, + trans_pcie->fw_mon_size, + DMA_FROM_DEVICE); + memcpy(fw_mon_data->data, page_address(trans_pcie->fw_mon_page), + trans_pcie->fw_mon_size); + + len += sizeof(*data) + sizeof(*fw_mon_data) + + trans_pcie->fw_mon_size; + } + + return len; } #else static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, @@ -1870,6 +1993,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } trans->hw_rev = iwl_read32(trans, CSR_HW_REV); + /* + * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have + * changed, and now the revision step also includes bit 0-1 (no more + * "dash" value). To keep hw_rev backwards compatible - we'll store it + * in the old format. + */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + trans->hw_rev = (trans->hw_rev & 0xfff0) | + ((trans->hw_rev << 2) & 0xc); + trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 038940afbdc5..6acccb19c4f3 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1438,6 +1438,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); trans_pcie->cmd_in_flight = false; + IWL_ERR(trans, "Failed to wake NIC for hcmd\n"); idx = -EIO; goto out; } |