diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mld')
41 files changed, 1280 insertions, 593 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index 687a9450ac98..bda488ae9eec 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -124,10 +124,12 @@ void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld, rcu_read_lock(); baid_data = rcu_dereference(mld->fw_id_to_ba[baid]); - if (IWL_FW_CHECK(mld, !baid_data, - "Got valid BAID %d but not allocated, invalid BAR release!\n", - baid)) + if (!baid_data) { + IWL_DEBUG_HT(mld, + "Got valid BAID %d but not allocated\n", + baid); goto out_unlock; + } if (IWL_FW_CHECK(mld, tid != baid_data->tid || sta_id > mld->fw->ucode_capa.num_stations || @@ -444,7 +446,7 @@ static void iwl_mld_init_reorder_buffer(struct iwl_mld *mld, struct iwl_mld_baid_data *data, u16 ssn) { - for (int i = 0; i < mld->trans->num_rx_queues; i++) { + for (int i = 0; i < mld->trans->info.num_rxqs; i++) { struct iwl_mld_reorder_buffer *reorder_buf = &data->reorder_buf[i]; struct iwl_mld_reorder_buf_entry *entries = @@ -468,7 +470,7 @@ static void iwl_mld_free_reorder_buffer(struct iwl_mld *mld, iwl_mld_sync_rx_queues(mld, IWL_MLD_RXQ_NOTIF_DEL_BA, &delba_data, sizeof(delba_data)); - for (int i = 0; i < mld->trans->num_rx_queues; i++) { + for (int i = 0; i < mld->trans->info.num_rxqs; i++) { struct iwl_mld_reorder_buffer *reorder_buf = &data->reorder_buf[i]; struct iwl_mld_reorder_buf_entry *entries = @@ -530,7 +532,7 @@ int iwl_mld_ampdu_rx_start(struct iwl_mld *mld, struct ieee80211_sta *sta, * before starting the BA session in the firmware */ baid_data = kzalloc(sizeof(*baid_data) + - mld->trans->num_rx_queues * reorder_buf_size, + mld->trans->info.num_rxqs * reorder_buf_size, GFP_KERNEL); if (!baid_data) return -ENOMEM; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c index 571eabd0b511..26511b49d89a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ap.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c @@ -11,6 +11,7 @@ #include "tx.h" #include "power.h" #include "key.h" +#include "phy.h" #include "iwl-utils.h" #include "fw/api/sta.h" @@ -269,6 +270,7 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_chanctx_conf *ctx; int ret; if (vif->type == NL80211_IFTYPE_AP) @@ -314,6 +316,13 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, return iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, FW_CTXT_ACTION_MODIFY); + /* When the channel context was added, the link is not yet active, so + * min_def is always used. Update the PHY again here in case def should + * actually be used. + */ + ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); + iwl_mld_update_phy_chandef(mld, ctx); + return 0; rm_bcast: iwl_mld_remove_bcast_sta(mld, vif, link); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/coex.c b/drivers/net/wireless/intel/iwlwifi/mld/coex.c index 5f262bd43f21..32c727b3b391 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/coex.c @@ -24,17 +24,13 @@ int iwl_mld_send_bt_init_conf(struct iwl_mld *mld) void iwl_mld_handle_bt_coex_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) { - const struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; + const struct iwl_bt_coex_profile_notif *notif = (const void *)pkt->data; const struct iwl_bt_coex_profile_notif zero_notif = {}; /* zeroed structure means that BT is OFF */ bool bt_is_active = memcmp(notif, &zero_notif, sizeof(*notif)); - if (bt_is_active == mld->bt_is_active) - return; - + mld->last_bt_notif = *notif; IWL_DEBUG_INFO(mld, "BT was turned %s\n", bt_is_active ? "ON" : "OFF"); - mld->bt_is_active = bt_is_active; - iwl_mld_emlsr_check_bt(mld); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index ee99298eebf5..339b148d6793 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -774,7 +774,7 @@ iwl_mld_update_ptk_rx_seq(struct iwl_mld *mld, return; for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { - for (int i = 1; i < mld->trans->num_rx_queues; i++) + for (int i = 1; i < mld->trans->info.num_rxqs; i++) memcpy(mld_ptk_pn->q[i].pn[tid], wowlan_status->ptk.aes_seq[tid].ccmp.pn, IEEE80211_CCMP_PN_LEN); @@ -1347,7 +1347,8 @@ int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld) if (ret) { IWL_ERR(mld, "d3 suspend: trans_d3_suspend failed %d\n", ret); } else { - mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; + /* Async notification might send hcmds, which is not allowed in suspend */ + iwl_mld_cancel_async_notifications(mld); mld->fw_status.in_d3 = true; } @@ -1372,7 +1373,6 @@ int iwl_mld_no_wowlan_resume(struct iwl_mld *mld) IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan resume flow\n"); - mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; mld->fw_status.in_d3 = false; iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); @@ -1437,7 +1437,7 @@ iwl_mld_suspend_set_ucast_pn(struct iwl_mld *mld, struct ieee80211_sta *sta, ieee80211_get_key_rx_seq(key, tid, &seq); /* and use the internal data for all queues */ - for (int que = 1; que < mld->trans->num_rx_queues; que++) { + for (int que = 1; que < mld->trans->info.num_rxqs; que++) { u8 *cur_pn = mld_ptk_pn->q[que].pn[tid]; if (memcmp(max_pn, cur_pn, IEEE80211_CCMP_PN_LEN) < 0) @@ -1903,7 +1903,6 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) IWL_DEBUG_WOWLAN(mld, "Starting the wowlan resume flow\n"); - mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; if (!mld->fw_status.in_d3) { IWL_DEBUG_WOWLAN(mld, "Device_powered_off() was called during wowlan\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index 93f9f78e4276..352da8aa7898 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -546,6 +546,9 @@ iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) #endif MLD_DEBUGFS_ADD_FILE(inject_packet, debugfs_dir, 0200); + debugfs_create_bool("rx_ts_ptp", 0600, debugfs_dir, + &mld->monitor.ptp_time); + /* Create a symlink with mac80211. It will be removed when mac80211 * exits (before the opmode exits which removes the target.) */ @@ -997,8 +1000,8 @@ void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, mld_link_dir = debugfs_create_dir("iwlmld", dir); } -static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, - size_t count, void *data) +static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, + size_t count, void *data, bool v3) { struct ieee80211_link_sta *link_sta = data; struct iwl_mld_link_sta *mld_link_sta; @@ -1020,6 +1023,10 @@ static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) return -EIO; + /* input is in FW format (v2 or v3) so convert to v3 */ + rate = iwl_v3_rate_from_v2_v3(cpu_to_le32(rate), v3); + rate = le32_to_cpu(iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3)); + ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE : IWL_TLC_DEBUG_FIXED_RATE, @@ -1033,6 +1040,18 @@ static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, return ret ? : count; } +static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, false); +} + +static ssize_t iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, true); +} + static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, size_t count, void *data) { @@ -1072,6 +1091,7 @@ static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64); LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64); +LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate_v3, 64); void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1079,5 +1099,6 @@ void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, struct dentry *dir) { LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200); + LINK_STA_DEBUGFS_ADD_FILE(fixed_rate_v3, dir, 0200); LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c index 4b083d447ee2..73ed8d5cab43 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -8,10 +8,10 @@ #include "fw/api/alive.h" #include "fw/api/scan.h" #include "fw/api/rx.h" +#include "phy.h" #include "fw/dbg.h" #include "fw/pnvm.h" #include "hcmd.h" -#include "iwl-nvm-parse.h" #include "power.h" #include "mcc.h" #include "led.h" @@ -49,7 +49,7 @@ static int iwl_mld_send_rss_cfg_cmd(struct iwl_mld *mld) /* Do not direct RSS traffic to Q 0 which is our fallback queue */ for (int i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++) cmd.indirection_table[i] = - 1 + (i % (mld->trans->num_rx_queues - 1)); + 1 + (i % (mld->trans->info.num_rxqs - 1)); netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key)); return iwl_mld_send_cmd_pdu(mld, RSS_CONFIG_CMD, &cmd); @@ -99,17 +99,23 @@ static void iwl_mld_alive_imr_data(struct iwl_trans *trans, } } +struct iwl_mld_alive_data { + __le32 sku_id[3]; + bool valid; +}; + static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt, void *data) { unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); + unsigned int expected_sz; struct iwl_mld *mld = container_of(notif_wait, struct iwl_mld, notif_wait); struct iwl_trans *trans = mld->trans; u32 version = iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP, UCODE_ALIVE_NTFY, 0); - struct iwl_alive_ntf_v6 *palive; - bool *alive_valid = data; + struct iwl_mld_alive_data *alive_data = data; + struct iwl_alive_ntf *palive; struct iwl_umac_alive *umac; struct iwl_lmac_alive *lmac1; struct iwl_lmac_alive *lmac2 = NULL; @@ -117,7 +123,19 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, u32 umac_error_table; u16 status; - if (version < 6 || version > 7 || pkt_len != sizeof(*palive)) + switch (version) { + case 6: + case 7: + expected_sz = sizeof(struct iwl_alive_ntf_v6); + break; + case 8: + expected_sz = sizeof(struct iwl_alive_ntf); + break; + default: + return false; + } + + if (pkt_len != expected_sz) return false; palive = (void *)pkt->data; @@ -129,12 +147,15 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, lmac2 = &palive->lmac_data[1]; status = le16_to_cpu(palive->status); - trans->sku_id[0] = le32_to_cpu(palive->sku_id.data[0]); - trans->sku_id[1] = le32_to_cpu(palive->sku_id.data[1]); - trans->sku_id[2] = le32_to_cpu(palive->sku_id.data[2]); + BUILD_BUG_ON(sizeof(alive_data->sku_id) != + sizeof(palive->sku_id.data)); + memcpy(alive_data->sku_id, palive->sku_id.data, + sizeof(palive->sku_id.data)); IWL_DEBUG_FW(mld, "Got sku_id: 0x0%x 0x0%x 0x0%x\n", - trans->sku_id[0], trans->sku_id[1], trans->sku_id[2]); + le32_to_cpu(alive_data->sku_id[0]), + le32_to_cpu(alive_data->sku_id[1]), + le32_to_cpu(alive_data->sku_id[2])); lmac_error_event_table = le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr); @@ -147,13 +168,13 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr) & ~FW_ADDR_CACHE_CONTROL; - if (umac_error_table >= trans->cfg->min_umac_error_event_table) + if (umac_error_table >= trans->mac_cfg->base->min_umac_error_event_table) iwl_fw_umac_set_alive_err_table(trans, umac_error_table); else IWL_ERR(mld, "Not valid error log pointer 0x%08X\n", umac_error_table); - *alive_valid = status == IWL_ALIVE_STATUS_OK; + alive_data->valid = status == IWL_ALIVE_STATUS_OK; IWL_DEBUG_FW(mld, "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n", @@ -171,6 +192,10 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, IWL_DEBUG_FW(mld, "FW alive flags 0x%x\n", le16_to_cpu(palive->flags)); + if (version >= 8) + IWL_DEBUG_FW(mld, "platform_id 0x%llx\n", + le64_to_cpu(palive->platform_id)); + iwl_fwrt_update_fw_versions(&mld->fwrt, lmac1, umac); return true; @@ -208,24 +233,22 @@ static void iwl_mld_print_alive_notif_timeout(struct iwl_mld *mld) pc_data->pc_address); } -static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld) +static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld, + struct iwl_mld_alive_data *alive_data) { - const struct fw_img *fw = - iwl_get_ucode_image(mld->fw, IWL_UCODE_REGULAR); static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY }; struct iwl_notification_wait alive_wait; - bool alive_valid = false; int ret; lockdep_assert_wiphy(mld->wiphy); iwl_init_notification_wait(&mld->notif_wait, &alive_wait, alive_cmd, ARRAY_SIZE(alive_cmd), - iwl_alive_fn, &alive_valid); + iwl_alive_fn, alive_data); iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL); - ret = iwl_trans_start_fw(mld->trans, fw, true); + ret = iwl_trans_start_fw(mld->trans, mld->fw, IWL_UCODE_REGULAR, true); if (ret) { iwl_remove_notification(&mld->notif_wait, &alive_wait); return ret; @@ -239,28 +262,26 @@ static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld) iwl_fw_dbg_error_collect(&mld->fwrt, FW_DBG_TRIGGER_ALIVE_TIMEOUT); iwl_mld_print_alive_notif_timeout(mld); - goto alive_failure; + return ret; } - if (!alive_valid) { + if (!alive_data->valid) { IWL_ERR(mld, "Loaded firmware is not valid!\n"); - ret = -EIO; - goto alive_failure; + return -EIO; } - iwl_trans_fw_alive(mld->trans, 0); + iwl_trans_fw_alive(mld->trans); return 0; - -alive_failure: - iwl_trans_stop_device(mld->trans); - return ret; } static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) { struct iwl_notification_wait init_wait; - struct iwl_init_extended_cfg_cmd init_cfg = {}; + struct iwl_init_extended_cfg_cmd init_cfg = { + .init_flags = cpu_to_le32(BIT(IWL_INIT_PHY)), + }; + struct iwl_mld_alive_data alive_data = {}; static const u16 init_complete[] = { INIT_COMPLETE_NOTIF, }; @@ -268,19 +289,15 @@ static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) lockdep_assert_wiphy(mld->wiphy); - ret = iwl_mld_load_fw_wait_alive(mld); + ret = iwl_mld_load_fw_wait_alive(mld, &alive_data); if (ret) return ret; - mld->trans->step_urm = - !!(iwl_read_umac_prph(mld->trans, CNVI_PMU_STEP_FLOW) & - CNVI_PMU_STEP_FLOW_FORCE_URM); - ret = iwl_pnvm_load(mld->trans, &mld->notif_wait, - &mld->fw->ucode_capa); + &mld->fw->ucode_capa, alive_data.sku_id); if (ret) { IWL_ERR(mld, "Timeout waiting for PNVM load %d\n", ret); - goto init_failure; + return ret; } iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, @@ -298,31 +315,24 @@ static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) if (ret) { IWL_ERR(mld, "Failed to send init config command: %d\n", ret); iwl_remove_notification(&mld->notif_wait, &init_wait); - goto init_failure; + return ret; + } + + ret = iwl_mld_send_phy_cfg_cmd(mld); + if (ret) { + IWL_ERR(mld, "Failed to send PHY config command: %d\n", ret); + iwl_remove_notification(&mld->notif_wait, &init_wait); + return ret; } ret = iwl_wait_notification(&mld->notif_wait, &init_wait, MLD_INIT_COMPLETE_TIMEOUT); if (ret) { IWL_ERR(mld, "Failed to get INIT_COMPLETE %d\n", ret); - goto init_failure; - } - - if (!mld->nvm_data) { - mld->nvm_data = iwl_get_nvm(mld->trans, mld->fw, 0, 0); - if (IS_ERR(mld->nvm_data)) { - ret = PTR_ERR(mld->nvm_data); - mld->nvm_data = NULL; - IWL_ERR(mld, "Failed to read NVM: %d\n", ret); - goto init_failure; - } + return ret; } return 0; - -init_failure: - iwl_trans_stop_device(mld->trans); - return ret; } int iwl_mld_load_fw(struct iwl_mld *mld) @@ -333,7 +343,7 @@ int iwl_mld_load_fw(struct iwl_mld *mld) ret = iwl_trans_start_hw(mld->trans); if (ret) - goto err; + return ret; ret = iwl_mld_run_fw_init_sequence(mld); if (ret) @@ -361,9 +371,10 @@ void iwl_mld_stop_fw(struct iwl_mld *mld) iwl_trans_stop_device(mld->trans); - wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); - - iwl_mld_purge_async_handlers_list(mld); + /* HW is stopped, no more coming RX. Cancel all notifications in + * case they were sent just before stopping the HW. + */ + iwl_mld_cancel_async_notifications(mld); mld->fw_status.running = false; } @@ -526,7 +537,7 @@ int iwl_mld_start_fw(struct iwl_mld *mld) ret = iwl_mld_load_fw(mld); if (IWL_FW_CHECK(mld, ret, "Failed to start firmware %d\n", ret)) { iwl_fw_dbg_error_collect(&mld->fwrt, FW_DBG_TRIGGER_DRIVER); - goto error; + return ret; } IWL_DEBUG_INFO(mld, "uCode started.\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index e49e2260ac05..235b55e0fe59 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -22,9 +22,17 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif) struct iwl_mld *mld = mld_vif->mld; struct iwl_mld_link *link; + mld_vif->emlsr.blocked_reasons &= ~IWL_MLD_EMLSR_BLOCKED_ROC; + + if (mld_vif->aux_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &mld_vif->aux_sta); + /* EMLSR is turned back on during recovery */ vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; + if (mld_vif->roc_activity != ROC_NUM_ACTIVITIES) + ieee80211_remain_on_channel_expired(mld->hw); + mld_vif->roc_activity = ROC_NUM_ACTIVITIES; for_each_mld_vif_valid_link(mld_vif, link) { @@ -103,6 +111,24 @@ static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld, IEEE80211_HE_MAC_CAP2_ACK_EN); } +static void iwl_mld_set_he_support(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd, + int cmd_ver) +{ + if (vif->type == NL80211_IFTYPE_AP) { + if (cmd_ver == 2) + cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1); + else + cmd->wifi_gen.he_ap_support = 1; + } else { + if (cmd_ver == 2) + cmd->wifi_gen_v2.he_support = cpu_to_le16(1); + else + cmd->wifi_gen.he_support = 1; + } +} + /* fill the common part for all interface types */ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, struct ieee80211_vif *vif, @@ -112,6 +138,9 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); struct ieee80211_bss_conf *link_conf; unsigned int link_id; + int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, + MAC_CONFIG_CMD), 0); lockdep_assert_wiphy(mld->wiphy); @@ -138,12 +167,11 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, * and enable both when we have MLO. */ if (ieee80211_vif_is_mld(vif)) { - if (vif->type == NL80211_IFTYPE_AP) - cmd->he_ap_support = cpu_to_le16(1); + iwl_mld_set_he_support(mld, vif, cmd, cmd_ver); + if (cmd_ver == 2) + cmd->wifi_gen_v2.eht_support = cpu_to_le32(1); else - cmd->he_support = cpu_to_le16(1); - - cmd->eht_support = cpu_to_le32(1); + cmd->wifi_gen.eht_support = 1; return; } @@ -151,10 +179,7 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, if (!link_conf->he_support) continue; - if (vif->type == NL80211_IFTYPE_AP) - cmd->he_ap_support = cpu_to_le16(1); - else - cmd->he_support = cpu_to_le16(1); + iwl_mld_set_he_support(mld, vif, cmd, cmd_ver); /* EHT, if supported, was already set above */ break; @@ -226,11 +251,6 @@ static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld, if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); - - if (vif->p2p) - cmd->client.ctwin = - cpu_to_le32(vif->bss_conf.p2p_noa_attr.oppps_ctwindow & - IEEE80211_P2P_OPPPS_CTWINDOW_MASK); } static void iwl_mld_fill_mac_cmd_ap(struct iwl_mld *mld, @@ -393,6 +413,7 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) wiphy_delayed_work_init(&mld_vif->emlsr.tmp_non_bss_done_wk, iwl_mld_emlsr_tmp_non_bss_done_wk); } + iwl_mld_init_internal_sta(&mld_vif->aux_sta); return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index ec14d0736cee..49e2ce65557d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -10,6 +10,7 @@ #include "link.h" #include "session-protect.h" #include "d3.h" +#include "fw/api/time-event.h" enum iwl_mld_cca_40mhz_wa_status { CCA_40_MHZ_WA_NONE, @@ -59,6 +60,7 @@ enum iwl_mld_emlsr_blocked { * loaded enough to justify EMLSR. * @IWL_MLD_EMLSR_EXIT_RFI: Exit EMLSR due to RFI * @IWL_MLD_EMLSR_EXIT_FW_REQUEST: Exit EMLSR because the FW requested it + * @IWL_MLD_EMLSR_EXIT_INVALID: internal exit reason due to invalid data */ enum iwl_mld_emlsr_exit { IWL_MLD_EMLSR_EXIT_BLOCK = 0x1, @@ -72,6 +74,7 @@ enum iwl_mld_emlsr_exit { IWL_MLD_EMLSR_EXIT_CHAN_LOAD = 0x100, IWL_MLD_EMLSR_EXIT_RFI = 0x200, IWL_MLD_EMLSR_EXIT_FW_REQUEST = 0x400, + IWL_MLD_EMLSR_EXIT_INVALID = 0x800, }; /** @@ -123,8 +126,6 @@ struct iwl_mld_emlsr { * Only valid for STA. (FIXME: needs to be per link) * @num_associated_stas: number of associated STAs. Relevant only for AP mode. * @ap_ibss_active: whether the AP/IBSS was started - * @roc_activity: the id of the roc_activity running. Relevant for p2p device - * only. Set to %ROC_NUM_ACTIVITIES when not in use. * @cca_40mhz_workaround: When we are connected in 2.4 GHz and 40 MHz, and the * environment is too loaded, we work around this by reconnecting to the * same AP with 20 MHz. This manages the status of the workaround. @@ -140,6 +141,9 @@ struct iwl_mld_emlsr { * @use_ps_poll: use ps_poll frames * @disable_bf: disable beacon filter * @dbgfs_slink: debugfs symlink for this interface + * @roc_activity: the id of the roc_activity running. Relevant for STA and + * p2p device only. Set to %ROC_NUM_ACTIVITIES when not in use. + * @aux_sta: station used for remain on channel. Used in P2P device. */ struct iwl_mld_vif { /* Add here fields that need clean up on restart */ @@ -151,7 +155,6 @@ struct iwl_mld_vif { struct ieee80211_key_conf __rcu *bigtks[2]; u8 num_associated_stas; bool ap_ibss_active; - u32 roc_activity; enum iwl_mld_cca_40mhz_wa_status cca_40mhz_workaround; #ifdef CONFIG_IWLWIFI_DEBUGFS bool beacon_inject_active; @@ -174,6 +177,8 @@ struct iwl_mld_vif { bool disable_bf; struct dentry *dbgfs_slink; #endif + enum iwl_roc_activity roc_activity; + struct iwl_mld_int_sta aux_sta; }; static inline struct iwl_mld_vif * diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 82a4979a3af3..d0f56189ad3f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -580,7 +580,7 @@ iwl_mld_get_omi_bw_reduction_pointers(struct iwl_mld *mld, *link_sta = NULL; - if (mld->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_SC) + if (mld->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC) return NULL; vif = iwl_mld_get_bss_vif(mld); @@ -782,10 +782,11 @@ iwl_mld_init_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link, iwl_mld_init_internal_sta(&mld_link->bcast_sta); iwl_mld_init_internal_sta(&mld_link->mcast_sta); - iwl_mld_init_internal_sta(&mld_link->aux_sta); + iwl_mld_init_internal_sta(&mld_link->mon_sta); - wiphy_delayed_work_init(&mld_link->rx_omi.finished_work, - iwl_mld_omi_bw_finished_work); + if (!mld->fw_status.in_hw_restart) + wiphy_delayed_work_init(&mld_link->rx_omi.finished_work, + iwl_mld_omi_bw_finished_work); return iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index 42b7bdcbd741..39f04aae5579 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -39,7 +39,7 @@ struct iwl_probe_resp_data { * @vif: the vif this link belongs to * @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS. * @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS. - * @aux_sta: station used for remain on channel. Used in P2P device. + * @mon_sta: station used for TX injection in monitor interface. * @link_id: over the air link ID * @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs, * but higher layers work differently, so we store the keys here for @@ -72,7 +72,7 @@ struct iwl_mld_link { struct ieee80211_vif *vif; struct iwl_mld_int_sta bcast_sta; struct iwl_mld_int_sta mcast_sta; - struct iwl_mld_int_sta aux_sta; + struct iwl_mld_int_sta mon_sta; u8 link_id; struct { @@ -89,7 +89,7 @@ struct iwl_mld_link { struct iwl_probe_resp_data __rcu *probe_resp_data; }; -/* Cleanup function for struct iwl_mld_phy, will be called in restart */ +/* Cleanup function for struct iwl_mld_link, will be called in restart */ static inline void iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link) { @@ -105,8 +105,8 @@ iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link) iwl_mld_free_internal_sta(mld, &link->bcast_sta); if (link->mcast_sta.sta_id != IWL_INVALID_STA) iwl_mld_free_internal_sta(mld, &link->mcast_sta); - if (link->aux_sta.sta_id != IWL_INVALID_STA) - iwl_mld_free_internal_sta(mld, &link->aux_sta); + if (link->mon_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &link->mon_sta); } /* Convert a percentage from [0,100] to [0,255] */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c index a4a612afb3b3..f7faa87b8ba6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c @@ -21,7 +21,7 @@ static bool iwl_mld_calc_low_latency(struct iwl_mld *mld, { struct iwl_mld_low_latency *ll = &mld->low_latency; bool global_low_latency = false; - u8 num_rx_q = mld->trans->num_rx_queues; + u8 num_rx_q = mld->trans->info.num_rxqs; for (int mac_id = 0; mac_id < NUM_MAC_INDEX_DRIVER; mac_id++) { u32 total_vo_vi_pkts = 0; @@ -131,12 +131,12 @@ int iwl_mld_low_latency_init(struct iwl_mld *mld) struct iwl_mld_low_latency *ll = &mld->low_latency; unsigned long ts = jiffies; - ll->pkts_counters = kcalloc(mld->trans->num_rx_queues, + ll->pkts_counters = kcalloc(mld->trans->info.num_rxqs, sizeof(*ll->pkts_counters), GFP_KERNEL); if (!ll->pkts_counters) return -ENOMEM; - for (int q = 0; q < mld->trans->num_rx_queues; q++) + for (int q = 0; q < mld->trans->info.num_rxqs; q++) spin_lock_init(&ll->pkts_counters[q].lock); wiphy_delayed_work_init(&ll->work, iwl_mld_low_latency_wk); @@ -167,7 +167,7 @@ void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld) memset(ll->window_start, 0, sizeof(ll->window_start)); memset(ll->result, 0, sizeof(ll->result)); - for (int q = 0; q < mld->trans->num_rx_queues; q++) + for (int q = 0; q < mld->trans->info.num_rxqs; q++) memset(ll->pkts_counters[q].vo_vi, 0, sizeof(ll->pkts_counters[q].vo_vi)); } @@ -276,7 +276,7 @@ void iwl_mld_low_latency_update_counters(struct iwl_mld *mld, return; if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) || - queue >= mld->trans->num_rx_queues)) + queue >= mld->trans->info.num_rxqs)) return; if (mld->low_latency.stopped) @@ -324,7 +324,7 @@ void iwl_mld_low_latency_restart(struct iwl_mld *mld) ll->window_start[mac] = 0; low_latency |= ll->result[mac]; - for (int q = 0; q < mld->trans->num_rx_queues; q++) { + for (int q = 0; q < mld->trans->info.num_rxqs; q++) { spin_lock_bh(&ll->pkts_counters[q].lock); ll->pkts_counters[q].vo_vi[mac] = 0; spin_unlock_bh(&ll->pkts_counters[q].lock); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 68d97d3b8f02..4ba050397632 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -243,7 +243,6 @@ static void iwl_mac_hw_set_flags(struct iwl_mld *mld) ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); ieee80211_hw_set(hw, HAS_RATE_CONTROL); ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); - ieee80211_hw_set(hw, DISALLOW_PUNCTURING_5GHZ); ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); ieee80211_hw_set(hw, TDLS_WIDER_BW); @@ -305,7 +304,7 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld) wiphy->max_remain_on_channel_duration = 10000; - wiphy->hw_version = mld->trans->hw_id; + wiphy->hw_version = mld->trans->info.hw_id; wiphy->hw_timestamp_max_peers = 1; @@ -351,9 +350,9 @@ static void iwl_mac_hw_set_misc(struct iwl_mld *mld) hw->queues = IEEE80211_NUM_ACS; hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; - hw->netdev_features |= mld->cfg->features; + hw->netdev_features |= mld->trans->mac_cfg->base->features; - hw->max_tx_fragments = mld->trans->max_skb_frags; + hw->max_tx_fragments = mld->trans->info.max_skb_frags; hw->max_listen_interval = IWL_MLD_CONN_LISTEN_INTERVAL; hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL; @@ -376,6 +375,24 @@ static void iwl_mac_hw_set_misc(struct iwl_mld *mld) static int iwl_mld_hw_verify_preconditions(struct iwl_mld *mld) { + int ratecheck; + + /* check for rates version 3 */ + ratecheck = + (iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11) + + (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP, + TLC_MNG_UPDATE_NOTIF, 0) >= 4) + + (iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP, + REPLY_RX_MPDU_CMD, 0) >= 6) + + (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP, + RX_NO_DATA_NOTIF, 0) >= 4) + + (iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, TX_CMD, 0) >= 9); + + if (ratecheck != 0 && ratecheck != 5) { + IWL_ERR(mld, "Firmware has inconsistent rates\n"); + return -EINVAL; + } + /* 11ax is expected to be enabled for all supported devices */ if (WARN_ON(!mld->nvm_data->sku_cap_11ax_enable)) return -EINVAL; @@ -541,15 +558,6 @@ void iwl_mld_mac80211_stop(struct ieee80211_hw *hw, bool suspend) (IS_ENABLED(CONFIG_PM_SLEEP) && iwl_mld_no_wowlan_suspend(mld))) iwl_mld_stop_fw(mld); - /* HW is stopped, no more coming RX. OTOH, the worker can't run as the - * wiphy lock is held. Cancel it in case it was scheduled just before - * we stopped the HW. - */ - wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); - - /* Empty out the list, as the worker won't do that */ - iwl_mld_purge_async_handlers_list(mld); - /* Clear in_hw_restart flag when stopping the hw, as mac80211 won't * execute the restart. */ @@ -897,9 +905,8 @@ void iwl_mld_change_chanctx(struct ieee80211_hw *hw, return; } update: - phy->chandef = *chandef; - iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY); + iwl_mld_update_phy_chandef(mld, ctx); } static u8 @@ -1031,12 +1038,19 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw, iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); if (vif->type == NL80211_IFTYPE_MONITOR) { - /* TODO: task=sniffer add sniffer station */ + ret = iwl_mld_add_mon_sta(mld, vif, link); + if (ret) + goto deactivate_link; + mld->monitor.p80 = iwl_mld_chandef_get_primary_80(&vif->bss_conf.chanreq.oper); } return 0; + +deactivate_link: + if (mld_link->active) + iwl_mld_deactivate_link(mld, link); err: RCU_INIT_POINTER(mld_link->chan_ctx, NULL); return ret; @@ -1062,7 +1076,8 @@ void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mld_deactivate_link(mld, link); - /* TODO: task=sniffer remove sniffer station */ + if (vif->type == NL80211_IFTYPE_MONITOR) + iwl_mld_remove_mon_sta(mld, vif, link); if (n_active > 1) { /* Indicate to mac80211 that EML is disabled */ @@ -1258,9 +1273,14 @@ iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw, } static void -iwl_mld_smps_wa(struct iwl_mld *mld, struct ieee80211_vif *vif, bool enable) +iwl_mld_smps_workaround(struct iwl_mld *mld, struct ieee80211_vif *vif, bool enable) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + bool workaround_required = + iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) < 2; + + if (!workaround_required) + return; /* Send the device-level power commands since the * firmware checks the POWER_TABLE_CMD's POWER_SAVE_EN bit to @@ -1307,7 +1327,7 @@ void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw, } if (changes & BSS_CHANGED_PS) { - iwl_mld_smps_wa(mld, vif, vif->cfg.ps); + iwl_mld_smps_workaround(mld, vif, vif->cfg.ps); iwl_mld_update_mac_power(mld, vif, false); } @@ -1320,13 +1340,22 @@ iwl_mld_mac80211_hw_scan(struct ieee80211_hw *hw, struct ieee80211_scan_request *hw_req) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; if (WARN_ON(!hw_req->req.n_channels || hw_req->req.n_channels > mld->fw->ucode_capa.n_scan_channels)) return -EINVAL; - return iwl_mld_regular_scan_start(mld, vif, &hw_req->req, &hw_req->ies); + ret = iwl_mld_regular_scan_start(mld, vif, &hw_req->req, &hw_req->ies); + if (!ret) { + /* We will be busy with scanning, so the counters may not reflect the + * reality. Stop checking the counters until the scan ends + */ + iwl_mld_start_ignoring_tpt_updates(mld); + } + + return ret; } static void @@ -1342,8 +1371,11 @@ iwl_mld_mac80211_cancel_hw_scan(struct ieee80211_hw *hw, * cancel scan before ieee80211_scan_work() could run. * To handle that, simply return if the scan is not running. */ - if (mld->scan.status & IWL_MLD_SCAN_REGULAR) + if (mld->scan.status & IWL_MLD_SCAN_REGULAR) { iwl_mld_scan_stop(mld, IWL_MLD_SCAN_REGULAR, true); + /* Scan is over, we can check again the tpt counters */ + iwl_mld_stop_ignoring_tpt_updates(mld); + } } static int @@ -1720,7 +1752,7 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, FW_CTXT_ACTION_MODIFY); if (ret) return ret; - iwl_mld_smps_wa(mld, vif, vif->cfg.ps); + iwl_mld_smps_workaround(mld, vif, vif->cfg.ps); } /* MFP is set by default before the station is authorized. @@ -1763,7 +1795,7 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld, &mld_vif->emlsr.check_tpt_wk); iwl_mld_reset_cca_40mhz_workaround(mld, vif); - iwl_mld_smps_wa(mld, vif, true); + iwl_mld_smps_workaround(mld, vif, true); } /* once we move into assoc state, need to update the FW to @@ -2004,7 +2036,7 @@ static int iwl_mld_alloc_ptk_pn(struct iwl_mld *mld, struct ieee80211_key_conf *key, struct iwl_mld_ptk_pn **ptk_pn) { - u8 num_rx_queues = mld->trans->num_rx_queues; + u8 num_rx_queues = mld->trans->info.num_rxqs; int keyidx = key->keyidx; struct ieee80211_key_seq seq; @@ -2460,15 +2492,17 @@ iwl_mld_change_vif_links(struct ieee80211_hw *hw, added |= BIT(0); for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - if (removed & BIT(i)) + if (removed & BIT(i) && !WARN_ON(!old[i])) iwl_mld_remove_link(mld, old[i]); } for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { if (added & BIT(i)) { link_conf = link_conf_dereference_protected(vif, i); - if (WARN_ON(!link_conf)) - return -EINVAL; + if (!link_conf) { + err = -EINVAL; + goto remove_added_links; + } err = iwl_mld_add_link(mld, link_conf); if (err) @@ -2503,7 +2537,11 @@ remove_added_links: iwl_mld_remove_link(mld, link_conf); } - return err; + if (WARN_ON(!iwl_mld_error_before_recovery(mld))) + return err; + + /* reconfig will fix us anyway */ + return 0; } static int iwl_mld_change_sta_links(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c index daca14e208bd..19cb562e7a73 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include <net/cfg80211.h> @@ -158,7 +158,7 @@ iwl_mld_get_regdomain(struct iwl_mld *mld, } IWL_DEBUG_LAR(mld, "MCC update response version: %d\n", resp_ver); - regd = iwl_parse_nvm_mcc_info(mld->trans->dev, mld->cfg, + regd = iwl_parse_nvm_mcc_info(mld->trans, __le32_to_cpu(resp->n_channels), resp->channels, __le16_to_cpu(resp->mcc), diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 73d2166a4c25..8cdd960c5245 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -26,6 +26,8 @@ #include "hcmd.h" #include "fw/api/location.h" +#include "iwl-nvm-parse.h" + #define DRV_DESCRIPTION "Intel(R) MLD wireless driver for Linux" MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_LICENSE("GPL"); @@ -60,7 +62,7 @@ static void iwl_mld_hw_set_regulatory(struct iwl_mld *mld) VISIBLE_IF_IWLWIFI_KUNIT void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, - const struct iwl_cfg *cfg, const struct iwl_fw *fw, + const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, struct ieee80211_hw *hw, struct dentry *dbgfs_dir) { mld->dev = trans->dev; @@ -75,7 +77,6 @@ void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, /* Setup async RX handling */ spin_lock_init(&mld->async_handlers_lock); - INIT_LIST_HEAD(&mld->async_handlers_list); wiphy_work_init(&mld->async_handlers_wk, iwl_mld_async_handlers_wk); @@ -192,6 +193,7 @@ static const struct iwl_hcmd_names iwl_mld_system_names[] = { HCMD_NAME(SOC_CONFIGURATION_CMD), HCMD_NAME(INIT_EXTENDED_CFG_CMD), HCMD_NAME(FW_ERROR_RECOVERY_CMD), + HCMD_NAME(RFI_CONFIG_CMD), HCMD_NAME(RFI_GET_FREQ_TABLE_CMD), HCMD_NAME(SYSTEM_STATISTICS_CMD), HCMD_NAME(SYSTEM_STATISTICS_END_NOTIF), @@ -287,7 +289,9 @@ static const struct iwl_hcmd_names iwl_mld_statistics_names[] = { * Access is done through binary search */ static const struct iwl_hcmd_names iwl_mld_prot_offload_names[] = { - HCMD_NAME(STORED_BEACON_NTF), + HCMD_NAME(WOWLAN_WAKE_PKT_NOTIFICATION), + HCMD_NAME(WOWLAN_INFO_NOTIFICATION), + HCMD_NAME(D3_END_NOTIFICATION), }; /* Please keep this array *SORTED* by hex value. @@ -322,33 +326,40 @@ EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(global_iwl_mld_goups_size); static void iwl_mld_configure_trans(struct iwl_op_mode *op_mode) { - const struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); static const u8 no_reclaim_cmds[] = {TX_CMD}; - struct iwl_trans_config trans_cfg = { - .op_mode = op_mode, - /* Rx is not supported yet, but add it to avoid warnings */ - .rx_buf_size = iwl_amsdu_size_to_rxb_size(), - .command_groups = iwl_mld_groups, - .command_groups_size = ARRAY_SIZE(iwl_mld_groups), - .fw_reset_handshake = true, - .queue_alloc_cmd_ver = - iwl_fw_lookup_cmd_ver(mld->fw, - WIDE_ID(DATA_PATH_GROUP, - SCD_QUEUE_CONFIG_CMD), - 0), - .no_reclaim_cmds = no_reclaim_cmds, - .n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds), - .cb_data_offs = offsetof(struct ieee80211_tx_info, - driver_data[2]), - }; struct iwl_trans *trans = mld->trans; + u32 eckv_value; - trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; - trans->iml = mld->fw->iml; - trans->iml_len = mld->fw->iml_len; - trans->wide_cmd_header = true; + iwl_bios_setup_step(trans, &mld->fwrt); + iwl_uefi_get_step_table(trans); - iwl_trans_configure(trans, &trans_cfg); + if (iwl_bios_get_eckv(&mld->fwrt, &eckv_value)) + IWL_DEBUG_RADIO(mld, "ECKV table doesn't exist in BIOS\n"); + else + trans->conf.ext_32khz_clock_valid = !!eckv_value; + + trans->conf.rx_buf_size = iwl_amsdu_size_to_rxb_size(); + trans->conf.command_groups = iwl_mld_groups; + trans->conf.command_groups_size = ARRAY_SIZE(iwl_mld_groups); + trans->conf.fw_reset_handshake = true; + trans->conf.queue_alloc_cmd_ver = + iwl_fw_lookup_cmd_ver(mld->fw, WIDE_ID(DATA_PATH_GROUP, + SCD_QUEUE_CONFIG_CMD), + 0); + trans->conf.cb_data_offs = offsetof(struct ieee80211_tx_info, + driver_data[2]); + BUILD_BUG_ON(sizeof(no_reclaim_cmds) > + sizeof(trans->conf.no_reclaim_cmds)); + memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds, + sizeof(no_reclaim_cmds)); + trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); + + trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD; + trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); + trans->conf.wide_cmd_header = true; + + iwl_trans_op_mode_enter(trans, op_mode); } /* @@ -359,13 +370,12 @@ iwl_mld_configure_trans(struct iwl_op_mode *op_mode) #define NUM_FW_LOAD_RETRIES 3 static struct iwl_op_mode * -iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) { struct ieee80211_hw *hw; struct iwl_op_mode *op_mode; struct iwl_mld *mld; - u32 eckv_value; int ret; /* Allocate and initialize a new hardware device */ @@ -383,16 +393,13 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_construct_mld(mld, trans, cfg, fw, hw, dbgfs_dir); + /* we'll verify later it matches between commands */ + mld->fw_rates_ver_3 = iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11; + iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir); iwl_mld_get_bios_tables(mld); iwl_uefi_get_sgom_table(trans, &mld->fwrt); - iwl_uefi_get_step_table(trans); - if (iwl_bios_get_eckv(&mld->fwrt, &eckv_value)) - IWL_DEBUG_RADIO(mld, "ECKV table doesn't exist in BIOS\n"); - else - trans->ext_32khz_clock_valid = !!eckv_value; - iwl_bios_setup_step(trans, &mld->fwrt); mld->bios_enable_puncturing = iwl_uefi_get_puncturing(&mld->fwrt); iwl_mld_hw_set_regulatory(mld); @@ -411,10 +418,17 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, break; } + if (!ret) { + mld->nvm_data = iwl_get_nvm(mld->trans, mld->fw, 0, 0); + if (IS_ERR(mld->nvm_data)) { + IWL_ERR(mld, "Failed to read NVM: %d\n", ret); + ret = PTR_ERR(mld->nvm_data); + } + } + if (ret) { wiphy_unlock(mld->wiphy); rtnl_unlock(); - iwl_fw_flush_dumps(&mld->fwrt); goto err; } @@ -475,8 +489,9 @@ iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode) iwl_mld_ptp_remove(mld); iwl_mld_leds_exit(mld); - wiphy_lock(mld->wiphy); iwl_mld_thermal_exit(mld); + + wiphy_lock(mld->wiphy); iwl_mld_low_latency_stop(mld); iwl_mld_deinit_time_sync(mld); wiphy_unlock(mld->wiphy); @@ -664,6 +679,13 @@ static bool iwl_mld_sw_reset(struct iwl_op_mode *op_mode, { struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + /* SW reset can happen for TOP error w/o NIC error, so + * also abort scan here and set in_hw_restart, when we + * had a NIC error both were already done. + */ + iwl_mld_report_scan_aborted(mld); + mld->fw_status.in_hw_restart = true; + /* Do restart only in the following conditions are met: * - we consider the FW as running * - The trigger that brought us here is defined as one that requires @@ -692,7 +714,6 @@ static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); wiphy_lock(mld->wiphy); - mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; iwl_mld_stop_fw(mld); mld->fw_status.in_d3 = false; wiphy_unlock(mld->wiphy); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h index a4a16da6ebf3..1a2c44f44eff 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -116,6 +116,7 @@ * @monitor.ampdu_toggle: the state of the previous packet to track A-MPDU * @monitor.cur_aid: current association id tracked by the sniffer * @monitor.cur_bssid: current bssid tracked by the sniffer + * @monitor.ptp_time: set the Rx mactime using the device's PTP clock time * @monitor.p80: primary channel position relative to he whole bandwidth, in * steps of 80 MHz * @fw_id_to_link_sta: maps a fw id of a sta to the corresponding @@ -126,7 +127,6 @@ * cleanup using iwl_mld_free_internal_sta * @netdetect: indicates the FW is in suspend mode with netdetect configured * @p2p_device_vif: points to the p2p device vif if exists - * @bt_is_active: indicates that BT is active * @dev: pointer to device struct. For printing purposes * @trans: pointer to the transport layer * @cfg: pointer to the device configuration @@ -154,6 +154,8 @@ * @radio_kill: bitmap of radio kill status * @radio_kill.hw: radio is killed by hw switch * @radio_kill.ct: radio is killed because the device it too hot + * @power_budget_mw: maximum cTDP power budget as defined for this system and + * device * @addresses: device MAC addresses. * @scan: instance of the scan object * @wowlan: WoWLAN support data. @@ -174,6 +176,7 @@ * @mcast_filter_cmd: pointer to the multicast filter command. * @mgmt_tx_ant: stores the last TX antenna index; used for setting * TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure). + * @fw_rates_ver_3: FW rates are in version 3 * @low_latency: low-latency manager. * @tzone: thermal zone device's data * @cooling_dev: cooling device's related data @@ -184,6 +187,7 @@ * @ptp_data: data of the PTP clock * @time_sync: time sync data. * @ftm_initiator: FTM initiator data + * @last_bt_notif: last received BT Coex notif */ struct iwl_mld { /* Add here fields that need clean up on restart */ @@ -201,19 +205,20 @@ struct iwl_mld { #ifdef CONFIG_IWLWIFI_DEBUGFS __le16 cur_aid; u8 cur_bssid[ETH_ALEN]; + bool ptp_time; #endif } monitor; #ifdef CONFIG_PM_SLEEP bool netdetect; #endif /* CONFIG_PM_SLEEP */ struct ieee80211_vif *p2p_device_vif; - bool bt_is_active; + struct iwl_bt_coex_profile_notif last_bt_notif; ); struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX]; /* And here fields that survive a fw restart */ struct device *dev; struct iwl_trans *trans; - const struct iwl_cfg *cfg; + const struct iwl_rf_cfg *cfg; const struct iwl_fw *fw; struct ieee80211_hw *hw; struct wiphy *wiphy; @@ -241,6 +246,8 @@ struct iwl_mld { ct:1; } radio_kill; + u32 power_budget_mw; + struct mac_address addresses[IWL_MLD_MAX_ADDRESSES]; struct iwl_mld_scan scan; #ifdef CONFIG_PM_SLEEP @@ -266,6 +273,8 @@ struct iwl_mld { u8 mgmt_tx_ant; + bool fw_rates_ver_3; + struct iwl_mld_low_latency low_latency; bool ibss_manager; @@ -286,7 +295,7 @@ struct iwl_mld { memset((void *)&(_ptr)->zeroed_on_hw_restart, 0, \ sizeof((_ptr)->zeroed_on_hw_restart)) -/* Cleanup function for struct iwl_mld_vif, will be called in restart */ +/* Cleanup function for struct iwl_mld, will be called in restart */ static inline void iwl_cleanup_mld(struct iwl_mld *mld) { @@ -410,7 +419,7 @@ iwl_mld_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags, int rate = rate_n_flags & RATE_LEGACY_RATE_MSK; bool is_lb = band == NL80211_BAND_2GHZ; - if (format == RATE_MCS_LEGACY_OFDM_MSK) + if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) return is_lb ? rate + IWL_FIRST_OFDM_RATE : rate; /* CCK is not allowed in 5 GHz */ @@ -482,7 +491,7 @@ iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta, struct ieee80211_rx_status *rx_status, int queue); void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, - const struct iwl_cfg *cfg, const struct iwl_fw *fw, + const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, struct ieee80211_hw *hw, struct dentry *dbgfs_dir); #endif diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index a870e169e265..dba5379ed009 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -52,7 +52,8 @@ static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask) HOW(BT_COEX) \ HOW(CHAN_LOAD) \ HOW(RFI) \ - HOW(FW_REQUEST) + HOW(FW_REQUEST) \ + HOW(INVALID) static const char * iwl_mld_get_emlsr_exit_string(enum iwl_mld_emlsr_exit exit) @@ -325,23 +326,44 @@ static void iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac, struct ieee80211_vif *vif) { - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct iwl_esr_mode_notif *notif = (void *)data; + const struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + enum iwl_mvm_fw_esr_recommendation action; + const struct iwl_esr_mode_notif *notif = NULL; + + if (iwl_fw_lookup_notif_ver(mld_vif->mld->fw, DATA_PATH_GROUP, + ESR_MODE_NOTIF, 0) > 1) { + notif = (void *)data; + action = le32_to_cpu(notif->action); + } else { + const struct iwl_esr_mode_notif_v1 *notif_v1 = (void *)data; + + action = le32_to_cpu(notif_v1->action); + } if (!iwl_mld_vif_has_emlsr_cap(vif)) return; - switch (le32_to_cpu(notif->action)) { + switch (action) { case ESR_RECOMMEND_LEAVE: + if (notif) + IWL_DEBUG_INFO(mld_vif->mld, + "FW recommend leave reason = 0x%x\n", + le32_to_cpu(notif->leave_reason_mask)); + iwl_mld_exit_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_EXIT_FW_REQUEST, iwl_mld_get_primary_link(vif)); break; - case ESR_RECOMMEND_ENTER: case ESR_FORCE_LEAVE: + if (notif) + IWL_DEBUG_INFO(mld_vif->mld, + "FW force leave reason = 0x%x\n", + le32_to_cpu(notif->leave_reason_mask)); + fallthrough; + case ESR_RECOMMEND_ENTER: default: IWL_WARN(mld_vif->mld, "Unexpected EMLSR notification: %d\n", - le32_to_cpu(notif->action)); + action); } } @@ -523,7 +545,7 @@ void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk) } /* Sum up RX and TX MPDUs from the different queues/links */ - for (int q = 0; q < mld->trans->num_rx_queues; q++) { + for (int q = 0; q < mld->trans->info.num_rxqs; q++) { struct iwl_mld_per_q_mpdu_counter *queue_counter = &mld_sta->mpdu_counters[q]; @@ -635,6 +657,42 @@ s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld, #undef RSSI_THRESHOLD } +#define IWL_MLD_BT_COEX_DISABLE_EMLSR_RSSI_THRESH -69 +#define IWL_MLD_BT_COEX_ENABLE_EMLSR_RSSI_THRESH -63 +#define IWL_MLD_BT_COEX_WIFI_LOSS_THRESH 7 + +VISIBLE_IF_IWLWIFI_KUNIT +bool +iwl_mld_bt_allows_emlsr(struct iwl_mld *mld, struct ieee80211_bss_conf *link, + bool check_entry) +{ + int bt_penalty, rssi_thresh; + s32 link_rssi; + + if (WARN_ON_ONCE(!link->bss)) + return false; + + link_rssi = MBM_TO_DBM(link->bss->signal); + rssi_thresh = check_entry ? + IWL_MLD_BT_COEX_ENABLE_EMLSR_RSSI_THRESH : + IWL_MLD_BT_COEX_DISABLE_EMLSR_RSSI_THRESH; + /* No valid RSSI - force to take low rssi */ + if (!link_rssi) + link_rssi = rssi_thresh - 1; + + if (link_rssi > rssi_thresh) + bt_penalty = max(mld->last_bt_notif.wifi_loss_mid_high_rssi[PHY_BAND_24][0], + mld->last_bt_notif.wifi_loss_mid_high_rssi[PHY_BAND_24][1]); + else + bt_penalty = max(mld->last_bt_notif.wifi_loss_low_rssi[PHY_BAND_24][0], + mld->last_bt_notif.wifi_loss_low_rssi[PHY_BAND_24][1]); + + IWL_DEBUG_EHT(mld, "BT penalty for link-id %0X is %d\n", + link->link_id, bt_penalty); + return bt_penalty < IWL_MLD_BT_COEX_WIFI_LOSS_THRESH; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_bt_allows_emlsr); + static u32 iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld, struct ieee80211_vif *vif, @@ -643,13 +701,14 @@ iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld, { struct wiphy *wiphy = mld->wiphy; struct ieee80211_bss_conf *conf; - enum iwl_mld_emlsr_exit ret = 0; + u32 ret = 0; conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]); if (WARN_ON_ONCE(!conf)) - return false; + return IWL_MLD_EMLSR_EXIT_INVALID; - if (link->chandef->chan->band == NL80211_BAND_2GHZ && mld->bt_is_active) + if (link->chandef->chan->band == NL80211_BAND_2GHZ && + !iwl_mld_bt_allows_emlsr(mld, conf, true)) ret |= IWL_MLD_EMLSR_EXIT_BT_COEX; if (link->signal < @@ -731,7 +790,7 @@ iwl_mld_get_min_chan_load_thresh(struct ieee80211_chanctx_conf *chanctx) return 10; } -VISIBLE_IF_IWLWIFI_KUNIT bool +static bool iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, const struct iwl_mld_link_sel_data *a, @@ -772,8 +831,8 @@ iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, if (a->chandef->width <= b->chandef->width) return true; - bw_a = nl80211_chan_width_to_mhz(a->chandef->width); - bw_b = nl80211_chan_width_to_mhz(b->chandef->width); + bw_a = cfg80211_chandef_get_width(a->chandef); + bw_b = cfg80211_chandef_get_width(b->chandef); ratio = bw_a / bw_b; switch (ratio) { @@ -788,10 +847,9 @@ iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, return false; } -EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_channel_load_allows_emlsr); -static bool -iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, +VISIBLE_IF_IWLWIFI_KUNIT u32 +iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif, struct iwl_mld_link_sel_data *a, struct iwl_mld_link_sel_data *b) { @@ -800,12 +858,36 @@ iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, u32 reason_mask = 0; /* Per-link considerations */ - if (iwl_mld_emlsr_disallowed_with_link(mld, vif, a, true) || - iwl_mld_emlsr_disallowed_with_link(mld, vif, b, false)) - return false; - - if (a->chandef->chan->band == b->chandef->chan->band) - reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND; + reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, a, true); + if (reason_mask) + return reason_mask; + + reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, b, false); + if (reason_mask) + return reason_mask; + + if (a->chandef->chan->band == b->chandef->chan->band) { + const struct cfg80211_chan_def *c_low = a->chandef; + const struct cfg80211_chan_def *c_high = b->chandef; + u32 c_low_upper_edge, c_high_lower_edge; + + if (c_low->chan->center_freq > c_high->chan->center_freq) + swap(c_low, c_high); + + c_low_upper_edge = c_low->chan->center_freq + + cfg80211_chandef_get_width(c_low) / 2; + c_high_lower_edge = c_high->chan->center_freq - + cfg80211_chandef_get_width(c_high) / 2; + + if (a->chandef->chan->band == NL80211_BAND_5GHZ && + c_low_upper_edge <= 5330 && c_high_lower_edge >= 5490) { + /* This case is fine - HW/FW can deal with it, there's + * enough separation between the two channels. + */ + } else { + reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND; + } + } if (!iwl_mld_channel_load_allows_emlsr(mld, vif, a, b)) reason_mask |= IWL_MLD_EMLSR_EXIT_CHAN_LOAD; @@ -818,11 +900,11 @@ iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, nl80211_chan_width_to_mhz(a->chandef->width), nl80211_chan_width_to_mhz(b->chandef->width)); iwl_mld_print_emlsr_exit(mld, reason_mask); - return false; } - return true; + return reason_mask; } +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_emlsr_pair_state); /* Calculation is done with fixed-point with a scaling factor of 1/256 */ #define SCALE_FACTOR 256 @@ -850,7 +932,7 @@ unsigned int iwl_mld_get_emlsr_grade(struct iwl_mld *mld, *primary_id = a->link_id; - if (!iwl_mld_valid_emlsr_pair(vif, a, b)) + if (iwl_mld_emlsr_pair_state(vif, a, b)) return 0; primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]); @@ -892,8 +974,11 @@ static void _iwl_mld_select_links(struct iwl_mld *mld, n_data = iwl_mld_set_link_sel_data(mld, vif, data, usable_links, &best_idx); - if (WARN(!n_data, "Couldn't find a valid grade for any link!\n")) + if (!n_data) { + IWL_DEBUG_EHT(mld, + "Couldn't find a valid grade for any link!\n"); return; + } /* Default to selecting the single best link */ best_link = &data[best_idx]; @@ -961,27 +1046,41 @@ static void iwl_mld_emlsr_check_bt_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + const struct iwl_bt_coex_profile_notif zero_notif = {}; struct iwl_mld *mld = mld_vif->mld; struct ieee80211_bss_conf *link; unsigned int link_id; + const struct iwl_bt_coex_profile_notif *notif = &mld->last_bt_notif; - if (!mld->bt_is_active) { - iwl_mld_retry_emlsr(mld, vif); + if (!iwl_mld_vif_has_emlsr_cap(vif)) return; - } - /* BT is turned ON but we are not in EMLSR, nothing to do */ - if (!iwl_mld_emlsr_active(vif)) + /* zeroed structure means that BT is OFF */ + if (!memcmp(notif, &zero_notif, sizeof(*notif))) { + iwl_mld_retry_emlsr(mld, vif); return; - - /* In EMLSR and BT is turned ON */ + } for_each_vif_active_link(vif, link, link_id) { + bool emlsr_active, emlsr_allowed; + if (WARN_ON(!link->chanreq.oper.chan)) continue; - if (link->chanreq.oper.chan->band == NL80211_BAND_2GHZ) { - iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BT_COEX, + if (link->chanreq.oper.chan->band != NL80211_BAND_2GHZ) + continue; + + emlsr_active = iwl_mld_emlsr_active(vif); + emlsr_allowed = iwl_mld_bt_allows_emlsr(mld, link, + !emlsr_active); + if (emlsr_allowed && !emlsr_active) { + iwl_mld_retry_emlsr(mld, vif); + return; + } + + if (!emlsr_allowed && emlsr_active) { + iwl_mld_exit_emlsr(mld, vif, + IWL_MLD_EMLSR_EXIT_BT_COEX, iwl_mld_get_primary_link(vif)); return; } @@ -1074,3 +1173,69 @@ void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif) iwl_mld_int_mlo_scan(mld, vif); } + +static void iwl_mld_ignore_tpt_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + struct iwl_mld_sta *mld_sta; + bool *start = (void *)data; + + /* check_tpt_wk is only used when TPT block isn't set */ + if (mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT || + !IWL_MLD_AUTO_EML_ENABLE || !mld_vif->ap_sta) + return; + + mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta); + + /* We only count for the AP sta in a MLO connection */ + if (!mld_sta->mpdu_counters) + return; + + if (*start) { + wiphy_delayed_work_cancel(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk); + IWL_DEBUG_EHT(mld, "TPT check disabled\n"); + return; + } + + /* Clear the counters so we start from the beginning */ + for (int q = 0; q < mld->trans->info.num_rxqs; q++) { + struct iwl_mld_per_q_mpdu_counter *queue_counter = + &mld_sta->mpdu_counters[q]; + + spin_lock_bh(&queue_counter->lock); + + memset(queue_counter->per_link, 0, + sizeof(queue_counter->per_link)); + + spin_unlock_bh(&queue_counter->lock); + } + + /* Schedule the check in 5 seconds */ + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk, + round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW)); + IWL_DEBUG_EHT(mld, "TPT check enabled\n"); +} + +void iwl_mld_start_ignoring_tpt_updates(struct iwl_mld *mld) +{ + bool start = true; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_ignore_tpt_iter, + &start); +} + +void iwl_mld_stop_ignoring_tpt_updates(struct iwl_mld *mld) +{ + bool start = false; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_ignore_tpt_iter, + &start); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h index 4fb1fdbe3df9..9afa3d6ea649 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h @@ -37,7 +37,7 @@ static inline bool iwl_mld_vif_has_emlsr_cap(struct ieee80211_vif *vif) return ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION && ieee80211_vif_is_mld(vif) && vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP && - !CSR_HW_RFID_IS_CDB(mld_vif->mld->trans->hw_rf_id); + !CSR_HW_RFID_IS_CDB(mld_vif->mld->trans->info.hw_rf_id); } static inline int @@ -158,10 +158,16 @@ struct iwl_mld_link_sel_data { }; #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) -bool iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, - struct ieee80211_vif *vif, - const struct iwl_mld_link_sel_data *a, - const struct iwl_mld_link_sel_data *b); +u32 iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *a, + struct iwl_mld_link_sel_data *b); + +bool iwl_mld_bt_allows_emlsr(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + bool entry_criteria); #endif +void iwl_mld_start_ignoring_tpt_updates(struct iwl_mld *mld); +void iwl_mld_stop_ignoring_tpt_updates(struct iwl_mld *mld); + #endif /* __iwl_mld_mlo_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index fc18cba8aaa8..c0e62d46aba6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -183,47 +183,6 @@ static void iwl_mld_handle_mu_mimo_grp_notif(struct iwl_mld *mld, } static void -iwl_mld_handle_stored_beacon_notif(struct iwl_mld *mld, - struct iwl_rx_packet *pkt) -{ - unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); - struct iwl_stored_beacon_notif *sb = (void *)pkt->data; - struct ieee80211_rx_status rx_status = {}; - struct sk_buff *skb; - u32 size = le32_to_cpu(sb->common.byte_count); - - if (size == 0) - return; - - if (pkt_len < struct_size(sb, data, size)) - return; - - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) { - IWL_ERR(mld, "alloc_skb failed\n"); - return; - } - - /* update rx_status according to the notification's metadata */ - rx_status.mactime = le64_to_cpu(sb->common.tsf); - /* TSF as indicated by the firmware is at INA time */ - rx_status.flag |= RX_FLAG_MACTIME_PLCP_START; - rx_status.device_timestamp = le32_to_cpu(sb->common.system_time); - rx_status.band = - iwl_mld_phy_band_to_nl80211(le16_to_cpu(sb->common.band)); - rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(sb->common.channel), - rx_status.band); - - /* copy the data */ - skb_put_data(skb, sb->data, size); - memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - - /* pass it as regular rx to mac80211 */ - ieee80211_rx_napi(mld->hw, NULL, skb, NULL); -} - -static void iwl_mld_handle_channel_switch_start_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) { @@ -345,12 +304,15 @@ CMD_VERSIONS(session_prot_notif, CMD_VERSIONS(missed_beacon_notif, CMD_VER_ENTRY(5, iwl_missed_beacons_notif)) CMD_VERSIONS(tx_resp_notif, - CMD_VER_ENTRY(8, iwl_tx_resp)) + CMD_VER_ENTRY(8, iwl_tx_resp) + CMD_VER_ENTRY(9, iwl_tx_resp)) CMD_VERSIONS(compressed_ba_notif, CMD_VER_ENTRY(5, iwl_compressed_ba_notif) - CMD_VER_ENTRY(6, iwl_compressed_ba_notif)) + CMD_VER_ENTRY(6, iwl_compressed_ba_notif) + CMD_VER_ENTRY(7, iwl_compressed_ba_notif)) CMD_VERSIONS(tlc_notif, - CMD_VER_ENTRY(3, iwl_tlc_update_notif)) + CMD_VER_ENTRY(3, iwl_tlc_update_notif) + CMD_VER_ENTRY(4, iwl_tlc_update_notif)) CMD_VERSIONS(mu_mimo_grp_notif, CMD_VER_ENTRY(1, iwl_mu_group_mgmt_notif)) CMD_VERSIONS(channel_switch_start_notif, @@ -361,8 +323,6 @@ CMD_VERSIONS(ct_kill_notif, CMD_VER_ENTRY(2, ct_kill_notif)) CMD_VERSIONS(temp_notif, CMD_VER_ENTRY(2, iwl_dts_measurement_notif)) -CMD_VERSIONS(stored_beacon_notif, - CMD_VER_ENTRY(4, iwl_stored_beacon_notif)) CMD_VERSIONS(roc_notif, CMD_VER_ENTRY(1, iwl_roc_notif)) CMD_VERSIONS(probe_resp_data_notif, @@ -378,7 +338,8 @@ CMD_VERSIONS(bt_coex_notif, CMD_VERSIONS(beacon_notification, CMD_VER_ENTRY(6, iwl_extended_beacon_notif)) CMD_VERSIONS(emlsr_mode_notif, - CMD_VER_ENTRY(1, iwl_esr_mode_notif)) + CMD_VER_ENTRY(1, iwl_esr_mode_notif_v1) + CMD_VER_ENTRY(2, iwl_esr_mode_notif)) CMD_VERSIONS(emlsr_trans_fail_notif, CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif)) CMD_VERSIONS(uapsd_misbehaving_ap_notif, @@ -473,8 +434,6 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = { RX_HANDLER_OF_ROC(MAC_CONF_GROUP, ROC_NOTIF, roc_notif) RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF, mu_mimo_grp_notif, RX_HANDLER_SYNC) - RX_HANDLER_NO_OBJECT(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF, - stored_beacon_notif, RX_HANDLER_SYNC) RX_HANDLER_OF_VIF(MAC_CONF_GROUP, PROBE_RESPONSE_DATA_NOTIF, probe_resp_data_notif) RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, CT_KILL_NOTIFICATION, @@ -646,7 +605,7 @@ void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi, struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); - if (unlikely(queue >= mld->trans->num_rx_queues)) + if (unlikely(queue >= mld->trans->info.num_rxqs)) return; if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD))) @@ -707,10 +666,14 @@ void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk) } } -void iwl_mld_purge_async_handlers_list(struct iwl_mld *mld) +void iwl_mld_cancel_async_notifications(struct iwl_mld *mld) { struct iwl_async_handler_entry *entry, *tmp; + lockdep_assert_wiphy(mld->wiphy); + + wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); + spin_lock_bh(&mld->async_handlers_lock); list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) { iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.h b/drivers/net/wireless/intel/iwlwifi/mld/notif.h index 2eaa1d4e138e..adcdd9dec192 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.h @@ -15,7 +15,7 @@ void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi, void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk); -void iwl_mld_purge_async_handlers_list(struct iwl_mld *mld); +void iwl_mld_cancel_async_notifications(struct iwl_mld *mld); enum iwl_mld_object_type { IWL_MLD_OBJECT_TYPE_NONE, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.c b/drivers/net/wireless/intel/iwlwifi/mld/phy.c index 2fbc8090088b..d5a32ee56b92 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.c @@ -50,6 +50,9 @@ static void iwl_mld_chanctx_usage_iter(void *_data, u8 *mac, if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx) continue; + if (vif->type == NL80211_IFTYPE_AP && link_conf->ftm_responder) + data->use_def = true; + if (iwl_mld_chanctx_fils_enabled(vif, data->ctx)) data->use_def = true; } @@ -153,3 +156,43 @@ int iwl_mld_phy_fw_action(struct iwl_mld *mld, return ret; } + +static u32 iwl_mld_get_phy_config(struct iwl_mld *mld) +{ + u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN | + FW_PHY_CFG_RX_CHAIN); + u32 valid_rx_ant = iwl_mld_get_valid_rx_ant(mld); + u32 valid_tx_ant = iwl_mld_get_valid_tx_ant(mld); + + phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS | + valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS; + + return mld->fw->phy_config & phy_config; +} + +int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld) +{ + const struct iwl_tlv_calib_ctrl *default_calib = + &mld->fw->default_calib[IWL_UCODE_REGULAR]; + struct iwl_phy_cfg_cmd_v3 cmd = { + .phy_cfg = cpu_to_le32(iwl_mld_get_phy_config(mld)), + .calib_control.event_trigger = default_calib->event_trigger, + .calib_control.flow_trigger = default_calib->flow_trigger, + .phy_specific_cfg = mld->fwrt.phy_filters, + }; + + IWL_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg); + + return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd); +} + +void iwl_mld_update_phy_chandef(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + struct cfg80211_chan_def *chandef = + iwl_mld_get_chandef_from_chanctx(mld, ctx); + + phy->chandef = *chandef; + iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h index 2212a89321b7..0deaf179f07c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h @@ -52,4 +52,9 @@ iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld, struct ieee80211_chanctx_conf *ctx); u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef); +int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld); + +void iwl_mld_update_phy_chandef(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx); + #endif /* __iwl_mld_phy_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c index 2f16c174b57e..8cc276041360 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -253,6 +253,9 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld, cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); + if (iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) >= 2) + cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK); + /* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */ if (link_conf->beacon_rate && (link_conf->beacon_rate->bitrate == 10 || diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c index d5c3f853d96c..5ee38fc168c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -21,7 +21,7 @@ static int iwl_mld_get_systime(struct iwl_mld *mld, u32 *gp2) { - *gp2 = iwl_read_prph(mld->trans, mld->trans->cfg->gp2_reg_addr); + *gp2 = iwl_read_prph(mld->trans, mld->trans->mac_cfg->base->gp2_reg_addr); if (*gp2 == 0x5a5a5a5a) return -EINVAL; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index a75af8c1e8ab..326c300470ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -63,9 +63,9 @@ void iwl_mld_get_bios_tables(struct iwl_mld *mld) /* we don't fail if the table is not available */ } - ret = iwl_uefi_get_uats_table(mld->trans, &mld->fwrt); - if (ret) - IWL_DEBUG_RADIO(mld, "failed to read UATS table (%d)\n", ret); + iwl_uefi_get_uats_table(mld->trans, &mld->fwrt); + + iwl_bios_get_phy_filters(&mld->fwrt); } static int iwl_mld_geo_sar_init(struct iwl_mld *mld) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.c b/drivers/net/wireless/intel/iwlwifi/mld/roc.c index b87faca23ceb..e85f45bce79a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/roc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.c @@ -31,13 +31,54 @@ iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif) *result = ret; } +struct iwl_mld_roc_iter_data { + enum iwl_roc_activity activity; + struct ieee80211_vif *vif; + bool found; +}; + +static void iwl_mld_find_roc_vif_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_roc_iter_data *roc_data = data; + + if (mld_vif->roc_activity != roc_data->activity) + return; + + /* The FW supports one ROC of each type simultaneously */ + if (WARN_ON(roc_data->found)) { + roc_data->vif = NULL; + return; + } + + roc_data->found = true; + roc_data->vif = vif; +} + +static struct ieee80211_vif * +iwl_mld_find_roc_vif(struct iwl_mld *mld, enum iwl_roc_activity activity) +{ + struct iwl_mld_roc_iter_data roc_data = { + .activity = activity, + .found = false, + }; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_find_roc_vif_iter, + &roc_data); + + return roc_data.vif; +} + int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *channel, int duration, enum ieee80211_roc_type type) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct iwl_mld_int_sta *aux_sta; + struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta; struct iwl_roc_req cmd = { .action = cpu_to_le32(FW_CTXT_ACTION_ADD), }; @@ -49,38 +90,40 @@ int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, lockdep_assert_wiphy(mld->wiphy); - ieee80211_iterate_active_interfaces_mtx(mld->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mld_vif_iter_emlsr_block_roc, - &ret); - if (ret) - return ret; - - /* TODO: task=Hotspot 2.0 */ - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { + if (vif->type != NL80211_IFTYPE_P2P_DEVICE && + vif->type != NL80211_IFTYPE_STATION) { IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n", vif->type); return -EOPNOTSUPP; } - switch (type) { - case IEEE80211_ROC_TYPE_NORMAL: - activity = ROC_ACTIVITY_P2P_DISC; - break; - case IEEE80211_ROC_TYPE_MGMT_TX: - activity = ROC_ACTIVITY_P2P_NEG; - break; - default: - WARN_ONCE(1, "Got an invalid P2P ROC type\n"); - return -EINVAL; + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + switch (type) { + case IEEE80211_ROC_TYPE_NORMAL: + activity = ROC_ACTIVITY_P2P_DISC; + break; + case IEEE80211_ROC_TYPE_MGMT_TX: + activity = ROC_ACTIVITY_P2P_NEG; + break; + default: + WARN_ONCE(1, "Got an invalid P2P ROC type\n"); + return -EINVAL; + } + } else { + activity = ROC_ACTIVITY_HOTSPOT; } - if (WARN_ON(mld_vif->roc_activity != ROC_NUM_ACTIVITIES)) + /* The FW supports one ROC of each type simultaneously */ + if (WARN_ON(iwl_mld_find_roc_vif(mld, activity))) return -EBUSY; - /* No MLO on P2P device */ - aux_sta = &mld_vif->deflink.aux_sta; + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_block_roc, + &ret); + if (ret) + return ret; ret = iwl_mld_add_aux_sta(mld, aux_sta); if (ret) @@ -91,9 +134,6 @@ int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, cmd.channel_info.channel = cpu_to_le32(channel->hw_value); cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band); cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20; - /* TODO: task=Hotspot 2.0, revisit those parameters when we add an ROC - * on the BSS vif - */ cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY); cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); @@ -105,6 +145,7 @@ int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, IWL_ERR(mld, "Couldn't send the ROC_CMD\n"); return ret; } + mld_vif->roc_activity = activity; return 0; @@ -136,9 +177,9 @@ static void iwl_mld_destroy_roc(struct iwl_mld *mld, * we can flush the Tx on the queues */ - iwl_mld_flush_link_sta_txqs(mld, mld_vif->deflink.aux_sta.sta_id); + iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id); - iwl_mld_remove_aux_sta(mld, vif, &vif->bss_conf); + iwl_mld_remove_aux_sta(mld, vif); } int iwl_mld_cancel_roc(struct ieee80211_hw *hw, @@ -156,8 +197,8 @@ int iwl_mld_cancel_roc(struct ieee80211_hw *hw, lockdep_assert_wiphy(mld->wiphy); - /* TODO: task=Hotspot 2.0 */ - if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE)) + if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE && + vif->type != NL80211_IFTYPE_STATION)) return -EOPNOTSUPP; /* No roc activity running it's probably already done */ @@ -192,10 +233,10 @@ void iwl_mld_handle_roc_notif(struct iwl_mld *mld, { const struct iwl_roc_notif *notif = (void *)pkt->data; u32 activity = le32_to_cpu(notif->activity); - /* TODO: task=Hotspot 2.0 - roc can run on BSS */ - struct ieee80211_vif *vif = mld->p2p_device_vif; struct iwl_mld_vif *mld_vif; + struct ieee80211_vif *vif; + vif = iwl_mld_find_roc_vif(mld, activity); if (WARN_ON(!vif)) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c index c4f189bcece2..ce0093d5c638 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -33,17 +33,17 @@ struct iwl_mld_rx_phy_data { u32 gp2_on_air_rise; u16 phy_info; u8 energy_a, energy_b; - u8 channel; }; static void -iwl_mld_fill_phy_data(struct iwl_rx_mpdu_desc *desc, +iwl_mld_fill_phy_data(struct iwl_mld *mld, + struct iwl_rx_mpdu_desc *desc, struct iwl_mld_rx_phy_data *phy_data) { phy_data->phy_info = le16_to_cpu(desc->phy_info); - phy_data->rate_n_flags = le32_to_cpu(desc->v3.rate_n_flags); + phy_data->rate_n_flags = iwl_v3_rate_from_v2_v3(desc->v3.rate_n_flags, + mld->fw_rates_ver_3); phy_data->gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise); - phy_data->channel = desc->v3.channel; phy_data->energy_a = desc->v3.energy_a; phy_data->energy_b = desc->v3.energy_b; phy_data->data0 = desc->v3.phy_data0; @@ -1162,8 +1162,6 @@ static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld, static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, struct iwl_mld_rx_phy_data *phy_data, - struct iwl_rx_mpdu_desc *mpdu_desc, - struct ieee80211_hdr *hdr, int queue) { struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); @@ -1172,47 +1170,15 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK); bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK; - if (WARN_ON_ONCE(phy_data->with_data && (!mpdu_desc || !hdr))) - return; - - /* Keep packets with CRC errors (and with overrun) for monitor mode - * (otherwise the firmware discards them) but mark them as bad. - */ - if (phy_data->with_data && - (!(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_CRC_OK)) || - !(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_OVERRUN_OK)))) { - IWL_DEBUG_RX(mld, "Bad CRC or FIFO: 0x%08X.\n", - le32_to_cpu(mpdu_desc->status)); - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - } - phy_data->info_type = IWL_RX_PHY_INFO_TYPE_NONE; - if (phy_data->with_data && - likely(!(phy_data->phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { - rx_status->mactime = - le64_to_cpu(mpdu_desc->v3.tsf_on_air_rise); - - /* TSF as indicated by the firmware is at INA time */ - rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; - } else { + if (phy_data->phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) phy_data->info_type = le32_get_bits(phy_data->data1, IWL_RX_PHY_DATA1_INFO_TYPE_MASK); - } - - /* management stuff on default queue */ - if (!queue && phy_data->with_data && - unlikely(ieee80211_is_beacon(hdr->frame_control) || - ieee80211_is_probe_resp(hdr->frame_control))) { - rx_status->boottime_ns = ktime_get_boottime_ns(); - - if (mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_ENABLED) - mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_FOUND; - } /* set the preamble flag if appropriate */ - if (format == RATE_MCS_CCK_MSK && + if (format == RATE_MCS_MOD_TYPE_CCK && phy_data->phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE) rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; @@ -1237,7 +1203,7 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, } /* must be before L-SIG data */ - if (format == RATE_MCS_HE_MSK) + if (format == RATE_MCS_MOD_TYPE_HE) iwl_mld_rx_he(mld, skb, phy_data, queue); iwl_mld_decode_lsig(skb, phy_data); @@ -1245,40 +1211,53 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, rx_status->device_timestamp = phy_data->gp2_on_air_rise; /* using TLV format and must be after all fixed len fields */ - if (format == RATE_MCS_EHT_MSK) + if (format == RATE_MCS_MOD_TYPE_EHT) iwl_mld_rx_eht(mld, skb, phy_data, queue); #ifdef CONFIG_IWLWIFI_DEBUGFS - if (unlikely(mld->monitor.on)) + if (unlikely(mld->monitor.on)) { iwl_mld_add_rtap_sniffer_config(mld, skb); + + if (mld->monitor.ptp_time) { + u64 adj_time = + iwl_mld_ptp_get_adj_time(mld, + phy_data->gp2_on_air_rise * + NSEC_PER_USEC); + + rx_status->mactime = div64_u64(adj_time, NSEC_PER_USEC); + rx_status->flag |= RX_FLAG_MACTIME_IS_RTAP_TS64; + rx_status->flag &= ~RX_FLAG_MACTIME; + } + } #endif - if (format != RATE_MCS_CCK_MSK && is_sgi) + if (format != RATE_MCS_MOD_TYPE_CCK && is_sgi) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rate_n_flags & RATE_MCS_LDPC_MSK) rx_status->enc_flags |= RX_ENC_FLAG_LDPC; switch (format) { - case RATE_MCS_HT_MSK: + case RATE_MCS_MOD_TYPE_HT: rx_status->encoding = RX_ENC_HT; rx_status->rate_idx = RATE_HT_MCS_INDEX(rate_n_flags); rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; break; - case RATE_MCS_VHT_MSK: - case RATE_MCS_HE_MSK: - case RATE_MCS_EHT_MSK: - if (format == RATE_MCS_VHT_MSK) { + case RATE_MCS_MOD_TYPE_VHT: + case RATE_MCS_MOD_TYPE_HE: + case RATE_MCS_MOD_TYPE_EHT: + if (format == RATE_MCS_MOD_TYPE_VHT) { rx_status->encoding = RX_ENC_VHT; - } else if (format == RATE_MCS_HE_MSK) { + } else if (format == RATE_MCS_MOD_TYPE_HE) { rx_status->encoding = RX_ENC_HE; rx_status->he_dcm = !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK); - } else if (format == RATE_MCS_EHT_MSK) { + } else if (format == RATE_MCS_MOD_TYPE_EHT) { rx_status->encoding = RX_ENC_EHT; } - rx_status->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; + rx_status->nss = u32_get_bits(rate_n_flags, + RATE_MCS_NSS_MSK) + 1; rx_status->rate_idx = rate_n_flags & RATE_MCS_CODE_MSK; rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; break; @@ -1735,15 +1714,11 @@ static void iwl_mld_rx_update_ampdu_ref(struct iwl_mld *mld, } static void -iwl_mld_fill_rx_status_band_freq(struct iwl_mld_rx_phy_data *phy_data, - struct iwl_rx_mpdu_desc *mpdu_desc, - struct ieee80211_rx_status *rx_status) +iwl_mld_fill_rx_status_band_freq(struct ieee80211_rx_status *rx_status, + u8 band, u8 channel) { - enum nl80211_band band; - - band = BAND_IN_RX_STATUS(mpdu_desc->mac_phy_idx); rx_status->band = iwl_mld_phy_band_to_nl80211(band); - rx_status->freq = ieee80211_channel_to_frequency(phy_data->channel, + rx_status->freq = ieee80211_channel_to_frequency(channel, rx_status->band); } @@ -1758,7 +1733,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, struct sk_buff *skb; size_t mpdu_desc_size = sizeof(*mpdu_desc); bool drop = false; - u8 crypto_len = 0; + u8 crypto_len = 0, band; u32 pkt_len = iwl_rx_packet_payload_len(pkt); u32 mpdu_len; enum iwl_mld_reorder_result reorder_res; @@ -1788,7 +1763,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, hdr = (void *)(pkt->data + mpdu_desc_size); - iwl_mld_fill_phy_data(mpdu_desc, &phy_data); + iwl_mld_fill_phy_data(mld, mpdu_desc, &phy_data); if (mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) { /* If the device inserted padding it means that (it thought) @@ -1802,7 +1777,11 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, rx_status = IEEE80211_SKB_RXCB(skb); /* this is needed early */ - iwl_mld_fill_rx_status_band_freq(&phy_data, mpdu_desc, rx_status); + band = u8_get_bits(mpdu_desc->mac_phy_band, + IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK); + iwl_mld_fill_rx_status_band_freq(rx_status, band, + mpdu_desc->v3.channel); + rcu_read_lock(); @@ -1814,7 +1793,36 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, if (!queue && (phy_data.phy_info & IWL_RX_MPDU_PHY_AMPDU)) iwl_mld_rx_update_ampdu_ref(mld, &phy_data, rx_status); - iwl_mld_rx_fill_status(mld, skb, &phy_data, mpdu_desc, hdr, queue); + /* Keep packets with CRC errors (and with overrun) for monitor mode + * (otherwise the firmware discards them) but mark them as bad. + */ + if (!(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_CRC_OK)) || + !(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_OVERRUN_OK))) { + IWL_DEBUG_RX(mld, "Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(mpdu_desc->status)); + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + } + + if (likely(!(phy_data.phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { + rx_status->mactime = + le64_to_cpu(mpdu_desc->v3.tsf_on_air_rise); + + /* TSF as indicated by the firmware is at INA time */ + rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; + } + + /* management stuff on default queue */ + if (!queue && unlikely(ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control))) { + rx_status->boottime_ns = ktime_get_boottime_ns(); + + if (mld->scan.pass_all_sched_res == + SCHED_SCAN_PASS_ALL_STATE_ENABLED) + mld->scan.pass_all_sched_res = + SCHED_SCAN_PASS_ALL_STATE_FOUND; + } + + iwl_mld_rx_fill_status(mld, skb, &phy_data, queue); if (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue, le32_to_cpu(pkt->len_n_flags), &crypto_len)) @@ -1857,7 +1865,7 @@ void iwl_mld_sync_rx_queues(struct iwl_mld *mld, enum iwl_mld_internal_rxq_notif_type type, const void *notif_payload, u32 notif_payload_size) { - u8 num_rx_queues = mld->trans->num_rx_queues; + u8 num_rx_queues = mld->trans->info.num_rxqs; struct { struct iwl_rxq_sync_cmd sync_cmd; struct iwl_mld_internal_rxq_notif notif; @@ -1956,6 +1964,7 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, struct ieee80211_rx_status *rx_status; struct sk_buff *skb; u32 format, rssi; + u8 channel; if (unlikely(mld->fw_status.in_hw_restart)) return; @@ -1968,14 +1977,16 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, desc = (void *)pkt->data; rssi = le32_to_cpu(desc->rssi); + channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); + phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK); phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK); - phy_data.channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); phy_data.data0 = desc->phy_info[0]; phy_data.data1 = desc->phy_info[1]; phy_data.phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD; phy_data.gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time); - phy_data.rate_n_flags = le32_to_cpu(desc->rate); + phy_data.rate_n_flags = iwl_v3_rate_from_v2_v3(desc->rate, + mld->fw_rates_ver_3); phy_data.with_data = false; BUILD_BUG_ON(sizeof(phy_data.rx_vec) != sizeof(desc->rx_vec)); @@ -2018,13 +2029,13 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, break; } - rx_status->band = phy_data.channel > 14 ? NL80211_BAND_5GHZ : + rx_status->band = channel > 14 ? NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; - rx_status->freq = ieee80211_channel_to_frequency(phy_data.channel, + rx_status->freq = ieee80211_channel_to_frequency(channel, rx_status->band); - iwl_mld_rx_fill_status(mld, skb, &phy_data, NULL, NULL, queue); + iwl_mld_rx_fill_status(mld, skb, &phy_data, queue); /* No more radiotap info should be added after this point. * Mark it as mac header for upper layers to know where @@ -2037,17 +2048,17 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, * may be up to 8 spatial streams. */ switch (format) { - case RATE_MCS_VHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: rx_status->nss = le32_get_bits(desc->rx_vec[0], RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK) + 1; break; - case RATE_MCS_HE_MSK: + case RATE_MCS_MOD_TYPE_HE: rx_status->nss = le32_get_bits(desc->rx_vec[0], RX_NO_DATA_RX_VEC0_HE_NSTS_MSK) + 1; break; - case RATE_MCS_EHT_MSK: + case RATE_MCS_MOD_TYPE_EHT: rx_status->nss = le32_get_bits(desc->rx_vec[2], RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK) + 1; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index 7ec04318ec2f..3fce7cd2d512 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -1920,6 +1920,9 @@ void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, IWL_DEBUG_SCAN(mld, "Scan link is no longer valid\n"); ieee80211_scan_completed(mld->hw, &info); + + /* Scan is over, we can check again the tpt counters */ + iwl_mld_stop_ignoring_tpt_updates(mld); } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_SCHED) { ieee80211_sched_scan_stopped(mld->hw); mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index 332a7aecec2d..8fb51209b4a6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -401,9 +401,11 @@ static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta) static int iwl_mld_send_sta_cmd(struct iwl_mld *mld, const struct iwl_sta_cfg_cmd *cmd) { - int ret = iwl_mld_send_cmd_pdu(mld, - WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), - cmd); + u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD); + int cmd_len = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0) > 1 ? + sizeof(*cmd) : + sizeof(struct iwl_sta_cfg_cmd_v1); + int ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, cmd_len); if (ret) IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret); return ret; @@ -660,7 +662,7 @@ iwl_mld_alloc_dup_data(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta) if (mld->fw_status.in_hw_restart) return 0; - dup_data = kcalloc(mld->trans->num_rx_queues, sizeof(*dup_data), + dup_data = kcalloc(mld->trans->info.num_rxqs, sizeof(*dup_data), GFP_KERNEL); if (!dup_data) return -ENOMEM; @@ -673,7 +675,7 @@ iwl_mld_alloc_dup_data(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta) * This thus allows receiving a packet with seqno 0 and the * retry bit set as the very first packet on a new TID. */ - for (int q = 0; q < mld->trans->num_rx_queues; q++) + for (int q = 0; q < mld->trans->info.num_rxqs; q++) memset(dup_data[q].last_seq, 0xff, sizeof(dup_data[q].last_seq)); mld_sta->dup_data = dup_data; @@ -695,13 +697,13 @@ static void iwl_mld_alloc_mpdu_counters(struct iwl_mld *mld, sta->tdls || !ieee80211_vif_is_mld(vif)) return; - mld_sta->mpdu_counters = kcalloc(mld->trans->num_rx_queues, + mld_sta->mpdu_counters = kcalloc(mld->trans->info.num_rxqs, sizeof(*mld_sta->mpdu_counters), GFP_KERNEL); if (!mld_sta->mpdu_counters) return; - for (int q = 0; q < mld->trans->num_rx_queues; q++) + for (int q = 0; q < mld->trans->info.num_rxqs; q++) spin_lock_init(&mld_sta->mpdu_counters[q].lock); } @@ -947,7 +949,7 @@ static int iwl_mld_allocate_internal_txq(struct iwl_mld *mld, int queue, size; size = max_t(u32, IWL_MGMT_QUEUE_SIZE, - mld->trans->cfg->min_txq_size); + mld->trans->mac_cfg->base->min_txq_size); queue = iwl_trans_txq_alloc(mld->trans, 0, sta_mask, tid, size, IWL_WATCHDOG_DISABLED); @@ -1087,6 +1089,24 @@ int iwl_mld_add_aux_sta(struct iwl_mld *mld, 0, NULL, IWL_MAX_TID_COUNT); } +int iwl_mld_add_mon_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + if (WARN_ON(vif->type != NL80211_IFTYPE_MONITOR)) + return -EINVAL; + + return iwl_mld_add_internal_sta(mld, &mld_link->mon_sta, + STATION_TYPE_BCAST_MGMT, + mld_link->fw_id, NULL, + IWL_MAX_TID_COUNT); +} + static void iwl_mld_remove_internal_sta(struct iwl_mld *mld, struct iwl_mld_int_sta *internal_sta, bool flush, u8 tid) @@ -1140,6 +1160,19 @@ void iwl_mld_remove_mcast_sta(struct iwl_mld *mld, } void iwl_mld_remove_aux_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE && + vif->type != NL80211_IFTYPE_STATION)) + return; + + iwl_mld_remove_internal_sta(mld, &mld_vif->aux_sta, false, + IWL_MAX_TID_COUNT); +} + +void iwl_mld_remove_mon_sta(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link) { @@ -1148,11 +1181,10 @@ void iwl_mld_remove_aux_sta(struct iwl_mld *mld, if (WARN_ON(!mld_link)) return; - /* TODO: Hotspot 2.0 */ - if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE)) + if (WARN_ON(vif->type != NL80211_IFTYPE_MONITOR)) return; - iwl_mld_remove_internal_sta(mld, &mld_link->aux_sta, false, + iwl_mld_remove_internal_sta(mld, &mld_link->mon_sta, false, IWL_MAX_TID_COUNT); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h index ddcffd7b9fde..1897b121aae2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h @@ -247,6 +247,10 @@ int iwl_mld_add_mcast_sta(struct iwl_mld *mld, int iwl_mld_add_aux_sta(struct iwl_mld *mld, struct iwl_mld_int_sta *internal_sta); +int iwl_mld_add_mon_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + void iwl_mld_remove_bcast_sta(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link); @@ -256,6 +260,9 @@ void iwl_mld_remove_mcast_sta(struct iwl_mld *mld, struct ieee80211_bss_conf *link); void iwl_mld_remove_aux_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +void iwl_mld_remove_mon_sta(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index 0715bbc31031..f633cb1cf510 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -191,11 +191,12 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, break; } - if (format == RATE_MCS_CCK_MSK || format == RATE_MCS_LEGACY_OFDM_MSK) { + if (format == RATE_MCS_MOD_TYPE_CCK || + format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) { int rate = u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK); /* add the offset needed to get to the legacy ofdm indices */ - if (format == RATE_MCS_LEGACY_OFDM_MSK) + if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) rate += IWL_FIRST_OFDM_RATE; switch (rate) { @@ -240,7 +241,7 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, rinfo->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; - if (format == RATE_MCS_HT_MSK) + if (format == RATE_MCS_MOD_TYPE_HT) rinfo->mcs = RATE_HT_MCS_INDEX(rate_n_flags); else rinfo->mcs = u32_get_bits(rate_n_flags, RATE_MCS_CODE_MSK); @@ -249,10 +250,10 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; switch (format) { - case RATE_MCS_EHT_MSK: + case RATE_MCS_MOD_TYPE_EHT: rinfo->flags |= RATE_INFO_FLAGS_EHT_MCS; break; - case RATE_MCS_HE_MSK: + case RATE_MCS_MOD_TYPE_HE: gi_ltf = u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK); rinfo->flags |= RATE_INFO_FLAGS_HE_MCS; @@ -293,10 +294,10 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK) rinfo->he_dcm = 1; break; - case RATE_MCS_HT_MSK: + case RATE_MCS_MOD_TYPE_HT: rinfo->flags |= RATE_INFO_FLAGS_MCS; break; - case RATE_MCS_VHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; break; } @@ -467,12 +468,18 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, old_load = phy->avg_channel_load_not_by_us; new_load = le32_to_cpu(per_phy[phy->fw_id].channel_load_not_by_us); - if (IWL_FW_CHECK(phy->mld, new_load > 100, "Invalid channel load %u\n", - new_load)) + + if (IWL_FW_CHECK(phy->mld, + new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD && + new_load > 100, + "Invalid channel load %u\n", new_load)) return; - /* give a weight of 0.5 for the old value */ - phy->avg_channel_load_not_by_us = (new_load >> 1) + (old_load >> 1); + if (new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD) { + /* update giving a weight of 0.5 for the old value */ + phy->avg_channel_load_not_by_us = (new_load >> 1) + + (old_load >> 1); + } iwl_mld_emlsr_check_chan_load(hw, phy, old_load); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile index 36317feb923b..3e2ae6020613 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -iwlmld-tests-y += module.o hcmd.o utils.o link.o rx.o agg.o link-selection.o +iwlmld-tests-y += module.o hcmd.o utils.o link.o rx.o agg.o link-selection.o emlsr_with_bt.o ccflags-y += -I$(src)/../ obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmld-tests.o diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c index 1fd664be1a7c..29b0248cec3d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include <kunit/test.h> #include <kunit/static_stub.h> @@ -474,14 +474,14 @@ static struct iwl_rx_mpdu_desc *setup_mpdu_desc(void) KUNIT_ALLOC_AND_ASSERT(test, mpdu_desc); mpdu_desc->reorder_data |= - cpu_to_le32(FIELD_PREP(IWL_RX_MPDU_REORDER_BAID_MASK, - param->rx_pkt.baid)); + le32_encode_bits(param->rx_pkt.baid, + IWL_RX_MPDU_REORDER_BAID_MASK); mpdu_desc->reorder_data |= - cpu_to_le32(FIELD_PREP(IWL_RX_MPDU_REORDER_SN_MASK, - param->rx_pkt.sn)); + le32_encode_bits(param->rx_pkt.sn, + IWL_RX_MPDU_REORDER_SN_MASK); mpdu_desc->reorder_data |= - cpu_to_le32(FIELD_PREP(IWL_RX_MPDU_REORDER_NSSN_MASK, - param->rx_pkt.nssn)); + le32_encode_bits(param->rx_pkt.nssn, + IWL_RX_MPDU_REORDER_NSSN_MASK); if (param->rx_pkt.old_sn) mpdu_desc->reorder_data |= cpu_to_le32(IWL_RX_MPDU_REORDER_BA_OLD_SN); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c new file mode 100644 index 000000000000..91556ee5c142 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for link selection functions + * + * Copyright (C) 2025 Intel Corporation + */ +#include <kunit/static_stub.h> + +#include "utils.h" +#include "mld.h" +#include "mlo.h" + +static const struct emlsr_with_bt_test_case { + const char *desc; + struct { + struct iwl_bt_coex_profile_notif notif; + s32 signal; + bool check_entry; + } input; + bool emlsr_allowed; +} emlsr_with_bt_cases[] = { + { + .desc = "BT penalty(exit) with low rssi 4.5: emlsr allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {4, 5}, + .notif.wifi_loss_mid_high_rssi[1] = {7, 9}, + .signal = -69, + .check_entry = false, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(exit) from high rssi 5: emlsr allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {7, 9}, + .notif.wifi_loss_mid_high_rssi[1] = {5, 5}, + .signal = -68, + .check_entry = false, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(exit) with low rssi 8: emlsr not allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {7, 9}, + .notif.wifi_loss_mid_high_rssi[1] = {4, 5}, + .signal = -69, + .check_entry = false, + }, + .emlsr_allowed = false, + }, + { + .desc = "BT penalty(exit) from high rssi 9: emlsr not allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {4, 5}, + .notif.wifi_loss_mid_high_rssi[1] = {9, 9}, + .signal = -68, + .check_entry = false, + }, + .emlsr_allowed = false, + }, + { + .desc = "BT penalty(entry) with low rssi 4.5: emlsr allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {4, 5}, + .notif.wifi_loss_mid_high_rssi[1] = {7, 9}, + .signal = -63, + .check_entry = true, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(entry) from high rssi 5: emlsr allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {7, 9}, + .notif.wifi_loss_mid_high_rssi[1] = {5, 5}, + .signal = -62, + .check_entry = false, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(entry) with low rssi 8: emlsr not allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {7, 9}, + .notif.wifi_loss_mid_high_rssi[1] = {4, 5}, + .signal = -63, + .check_entry = false, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(entry) from high rssi 9: emlsr not allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {4, 5}, + .notif.wifi_loss_mid_high_rssi[1] = {9, 9}, + .signal = -62, + .check_entry = true, + }, + .emlsr_allowed = false, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(emlsr_with_bt, emlsr_with_bt_cases, desc); + +static void test_emlsr_with_bt(struct kunit *test) +{ + struct iwl_mld *mld = test->priv; + const struct emlsr_with_bt_test_case *test_param = + (const void *)(test->param_value); + struct ieee80211_vif *vif = + iwlmld_kunit_add_vif(true, NL80211_IFTYPE_STATION); + struct ieee80211_bss_conf *link = iwlmld_kunit_add_link(vif, 1); + bool actual_value = false; + + KUNIT_ALLOC_AND_ASSERT(test, link->bss); + + /* Extract test case parameters */ + link->bss->signal = DBM_TO_MBM(test_param->input.signal); + memcpy(&mld->last_bt_notif, &test_param->input.notif, + sizeof(struct iwl_bt_coex_profile_notif)); + + actual_value = iwl_mld_bt_allows_emlsr(mld, link, + test_param->input.check_entry); + /* Assert that the returned value matches the expected emlsr_allowed */ + KUNIT_EXPECT_EQ(test, actual_value, test_param->emlsr_allowed); +} + +static struct kunit_case emlsr_with_bt_test_cases[] = { + KUNIT_CASE_PARAM(test_emlsr_with_bt, emlsr_with_bt_gen_params), + {}, +}; + +static struct kunit_suite emlsr_with_bt = { + .name = "iwlmld-emlsr-with-bt-tests", + .test_cases = emlsr_with_bt_test_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(emlsr_with_bt); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c index 4e189bf8b3fb..0e3b9417dd63 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include <kunit/test.h> @@ -30,10 +30,10 @@ static void test_hcmd_names_sorted(struct kunit *test) static void test_hcmd_names_for_rx(struct kunit *test) { static struct iwl_trans t = { - .command_groups = iwl_mld_groups, + .conf.command_groups = iwl_mld_groups, }; - t.command_groups_size = global_iwl_mld_goups_size; + t.conf.command_groups_size = global_iwl_mld_goups_size; for (unsigned int i = 0; i < iwl_mld_rx_handlers_num; i++) { const struct iwl_rx_handler *rxh; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c index 295dcfd3f85d..94a037bec1fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c @@ -32,7 +32,7 @@ static const struct link_grading_test_case { .desc = "channel util of 128 (50%)", .input.link = { .link_id = 0, - .chandef = &chandef_2ghz, + .chandef = &chandef_2ghz_20mhz, .active = false, .has_chan_util_elem = true, .chan_util = 128, @@ -43,7 +43,7 @@ static const struct link_grading_test_case { .desc = "channel util of 180 (70%)", .input.link = { .link_id = 0, - .chandef = &chandef_2ghz, + .chandef = &chandef_2ghz_20mhz, .active = false, .has_chan_util_elem = true, .chan_util = 180, @@ -54,7 +54,7 @@ static const struct link_grading_test_case { .desc = "channel util of 180 (70%), channel load by us of 10%", .input.link = { .link_id = 0, - .chandef = &chandef_2ghz, + .chandef = &chandef_2ghz_20mhz, .has_chan_util_elem = true, .chan_util = 180, .active = true, @@ -66,7 +66,7 @@ static const struct link_grading_test_case { .desc = "no channel util element", .input.link = { .link_id = 0, - .chandef = &chandef_2ghz, + .chandef = &chandef_2ghz_20mhz, .active = true, }, .expected_grade = 120, @@ -132,7 +132,7 @@ static void test_link_grading(struct kunit *test) bool active = test_param->input.link.active; u16 valid_links; struct iwl_mld_kunit_link assoc_link = { - .band = test_param->input.link.chandef->chan->band, + .chandef = test_param->input.link.chandef, }; /* If the link is not active, use a different link as the assoc link */ @@ -172,108 +172,150 @@ static struct kunit_suite link_selection = { kunit_test_suite(link_selection); -static const struct channel_load_case { +static const struct link_pair_case { const char *desc; + const struct cfg80211_chan_def *chandef_a, *chandef_b; bool low_latency_vif; u32 chan_load_not_by_us; - enum nl80211_chan_width bw_a; - enum nl80211_chan_width bw_b; bool primary_link_active; - bool expected_result; -} channel_load_cases[] = { + u32 expected_result; +} link_pair_cases[] = { { .desc = "Unequal bandwidth, primary link inactive, EMLSR not allowed", .low_latency_vif = false, .primary_link_active = false, - .bw_a = NL80211_CHAN_WIDTH_40, - .bw_b = NL80211_CHAN_WIDTH_20, - .expected_result = false, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_6ghz_20mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD, }, { .desc = "Equal bandwidths, sufficient channel load, EMLSR allowed", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 11, - .bw_a = NL80211_CHAN_WIDTH_40, - .bw_b = NL80211_CHAN_WIDTH_40, - .expected_result = true, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_6ghz_40mhz, + .expected_result = 0, }, { .desc = "Equal bandwidths, insufficient channel load, EMLSR not allowed", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 6, - .bw_a = NL80211_CHAN_WIDTH_80, - .bw_b = NL80211_CHAN_WIDTH_80, - .expected_result = false, + .chandef_a = &chandef_5ghz_80mhz, + .chandef_b = &chandef_6ghz_80mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD, }, { .desc = "Low latency VIF, sufficient channel load, EMLSR allowed", .low_latency_vif = true, .primary_link_active = true, .chan_load_not_by_us = 6, - .bw_a = NL80211_CHAN_WIDTH_160, - .bw_b = NL80211_CHAN_WIDTH_160, - .expected_result = true, + .chandef_a = &chandef_5ghz_160mhz, + .chandef_b = &chandef_6ghz_160mhz, + .expected_result = 0, }, { .desc = "Different bandwidths (2x ratio), primary link load permits EMLSR", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 30, - .bw_a = NL80211_CHAN_WIDTH_40, - .bw_b = NL80211_CHAN_WIDTH_20, - .expected_result = true, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_6ghz_20mhz, + .expected_result = 0, }, { .desc = "Different bandwidths (4x ratio), primary link load permits EMLSR", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 45, - .bw_a = NL80211_CHAN_WIDTH_80, - .bw_b = NL80211_CHAN_WIDTH_20, - .expected_result = true, + .chandef_a = &chandef_5ghz_80mhz, + .chandef_b = &chandef_6ghz_20mhz, + .expected_result = 0, }, { .desc = "Different bandwidths (16x ratio), primary link load insufficient", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 45, - .bw_a = NL80211_CHAN_WIDTH_320, - .bw_b = NL80211_CHAN_WIDTH_20, - .expected_result = false, + .chandef_a = &chandef_6ghz_320mhz, + .chandef_b = &chandef_5ghz_20mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD, + }, + { + .desc = "Same band not allowed (2.4 GHz)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_2ghz_20mhz, + .chandef_b = &chandef_2ghz_11_20mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND, + }, + { + .desc = "Same band not allowed (5 GHz)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_5ghz_40mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND, + }, + { + .desc = "Same band allowed (5 GHz separated)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_5ghz_120_40mhz, + .expected_result = 0, + }, + { + .desc = "Same band not allowed (6 GHz)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_6ghz_160mhz, + .chandef_b = &chandef_6ghz_221_160mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND, }, }; -KUNIT_ARRAY_PARAM_DESC(channel_load, channel_load_cases, desc); +KUNIT_ARRAY_PARAM_DESC(link_pair, link_pair_cases, desc); -static void test_iwl_mld_channel_load_allows_emlsr(struct kunit *test) +static void test_iwl_mld_link_pair_allows_emlsr(struct kunit *test) { - const struct channel_load_case *params = test->param_value; + const struct link_pair_case *params = test->param_value; struct iwl_mld *mld = test->priv; struct ieee80211_vif *vif; - struct cfg80211_chan_def chandef_a, chandef_b; - struct iwl_mld_link_sel_data a = {.chandef = &chandef_a, - .link_id = 4}; - struct iwl_mld_link_sel_data b = {.chandef = &chandef_b, - .link_id = 5}; + struct ieee80211_bss_conf *link; + /* link A is the primary and link B is the secondary */ + struct iwl_mld_link_sel_data a = { + .chandef = params->chandef_a, + .link_id = 4, + }; + struct iwl_mld_link_sel_data b = { + .chandef = params->chandef_b, + .link_id = 5, + }; struct iwl_mld_kunit_link assoc_link = { + .chandef = params->primary_link_active ? a.chandef : b.chandef, .id = params->primary_link_active ? a.link_id : b.link_id, - .bandwidth = params->primary_link_active ? params->bw_a : params->bw_b, }; - bool result; + u32 result; vif = iwlmld_kunit_setup_mlo_assoc(BIT(a.link_id) | BIT(b.link_id), &assoc_link); - chandef_a.width = params->bw_a; - chandef_b.width = params->bw_b; - if (params->low_latency_vif) iwl_mld_vif_from_mac80211(vif)->low_latency_causes = 1; wiphy_lock(mld->wiphy); + link = wiphy_dereference(mld->wiphy, vif->link_conf[a.link_id]); + KUNIT_ALLOC_AND_ASSERT(test, link->bss); + link = wiphy_dereference(mld->wiphy, vif->link_conf[b.link_id]); + KUNIT_ALLOC_AND_ASSERT(test, link->bss); + /* Simulate channel load */ if (params->primary_link_active) { struct iwl_mld_phy *phy = @@ -282,22 +324,22 @@ static void test_iwl_mld_channel_load_allows_emlsr(struct kunit *test) phy->avg_channel_load_not_by_us = params->chan_load_not_by_us; } - result = iwl_mld_channel_load_allows_emlsr(mld, vif, &a, &b); + result = iwl_mld_emlsr_pair_state(vif, &a, &b); wiphy_unlock(mld->wiphy); KUNIT_EXPECT_EQ(test, result, params->expected_result); } -static struct kunit_case channel_load_criteria_test_cases[] = { - KUNIT_CASE_PARAM(test_iwl_mld_channel_load_allows_emlsr, channel_load_gen_params), +static struct kunit_case link_pair_criteria_test_cases[] = { + KUNIT_CASE_PARAM(test_iwl_mld_link_pair_allows_emlsr, link_pair_gen_params), {} }; -static struct kunit_suite channel_load_criteria_tests = { - .name = "iwlmld_channel_load_allows_emlsr", - .test_cases = channel_load_criteria_test_cases, +static struct kunit_suite link_pair_criteria_tests = { + .name = "iwlmld_link_pair_allows_emlsr", + .test_cases = link_pair_criteria_test_cases, .init = iwlmld_kunit_test_init, }; -kunit_test_suite(channel_load_criteria_tests); +kunit_test_suite(link_pair_criteria_tests); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c index 4a4eaa134bd3..69a0d67858bf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c @@ -63,11 +63,11 @@ static void test_missed_beacon(struct kunit *test) struct iwl_rx_packet *pkt; struct iwl_mld_kunit_link link1 = { .id = 0, - .band = NL80211_BAND_6GHZ, + .chandef = &chandef_6ghz_160mhz, }; struct iwl_mld_kunit_link link2 = { .id = 1, - .band = NL80211_BAND_5GHZ, + .chandef = &chandef_5ghz_80mhz, }; kunit_activate_static_stub(test, ieee80211_connection_loss, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c index 9712ee696509..26cf27be762d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -24,7 +24,7 @@ int iwlmld_kunit_test_init(struct kunit *test) { struct iwl_mld *mld; struct iwl_trans *trans; - const struct iwl_cfg *cfg; + const struct iwl_rf_cfg *cfg; struct iwl_fw *fw; struct ieee80211_hw *hw; @@ -146,7 +146,7 @@ iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id) } struct ieee80211_chanctx_conf * -iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def) +iwlmld_kunit_add_chanctx(const struct cfg80211_chan_def *def) { struct kunit *test = kunit_get_current_test(); struct iwl_mld *mld = test->priv; @@ -346,8 +346,7 @@ iwlmld_kunit_setup_assoc(bool mlo, struct iwl_mld_kunit_link *assoc_link) else link = &vif->bss_conf; - chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->band, - assoc_link->bandwidth); + chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->chandef); wiphy_lock(mld->wiphy); iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); @@ -428,7 +427,7 @@ struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, link = wiphy_dereference(mld->wiphy, vif->link_conf[link2->id]); KUNIT_EXPECT_NOT_NULL(test, link); - chan_ctx = iwlmld_kunit_add_chanctx(link2->band, link2->bandwidth); + chan_ctx = iwlmld_kunit_add_chanctx(link2->chandef); iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); wiphy_unlock(mld->wiphy); @@ -472,3 +471,33 @@ struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif, return iwl_mld_phy_from_mac80211(chanctx); } + +static const struct chandef_case { + const char *desc; + const struct cfg80211_chan_def *chandef; +} chandef_cases[] = { +#define CHANDEF(c, ...) { .desc = "chandef " #c " valid", .chandef = &c, }, + CHANDEF_LIST +#undef CHANDEF +}; + +KUNIT_ARRAY_PARAM_DESC(chandef, chandef_cases, desc); + +static void test_iwl_mld_chandef_valid(struct kunit *test) +{ + const struct chandef_case *params = test->param_value; + + KUNIT_EXPECT_EQ(test, true, cfg80211_chandef_valid(params->chandef)); +} + +static struct kunit_case chandef_test_cases[] = { + KUNIT_CASE_PARAM(test_iwl_mld_chandef_valid, chandef_gen_params), + {} +}; + +static struct kunit_suite chandef_tests = { + .name = "iwlmld_valid_test_chandefs", + .test_cases = chandef_test_cases, +}; + +kunit_test_suite(chandef_tests); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h index d3723653cf1b..edf8eef4e81a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -14,9 +14,8 @@ struct iwl_mld; int iwlmld_kunit_test_init(struct kunit *test); struct iwl_mld_kunit_link { + const struct cfg80211_chan_def *chandef; u8 id; - enum nl80211_band band; - enum nl80211_chan_width bandwidth; }; enum nl80211_iftype; @@ -42,50 +41,57 @@ static struct ieee80211_channel _name = { \ .hw_value = (_freq), \ } -#define CHANDEF(_name, _channel, _freq1, _width) \ -__maybe_unused static struct cfg80211_chan_def _name = { \ - .chan = &(_channel), \ - .center_freq1 = (_freq1), \ - .width = (_width), \ -} - CHANNEL(chan_2ghz, NL80211_BAND_2GHZ, 2412); +CHANNEL(chan_2ghz_11, NL80211_BAND_2GHZ, 2462); CHANNEL(chan_5ghz, NL80211_BAND_5GHZ, 5200); +CHANNEL(chan_5ghz_120, NL80211_BAND_5GHZ, 5600); CHANNEL(chan_6ghz, NL80211_BAND_6GHZ, 6115); +CHANNEL(chan_6ghz_221, NL80211_BAND_6GHZ, 7055); /* Feel free to add more */ +#undef CHANNEL + +#define CHANDEF_LIST \ + CHANDEF(chandef_2ghz_20mhz, chan_2ghz, 2412, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_2ghz_40mhz, chan_2ghz, 2422, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_2ghz_11_20mhz, chan_2ghz_11, 2462, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_5ghz_20mhz, chan_5ghz, 5200, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_5ghz_40mhz, chan_5ghz, 5210, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_5ghz_80mhz, chan_5ghz, 5210, \ + NL80211_CHAN_WIDTH_80) \ + CHANDEF(chandef_5ghz_160mhz, chan_5ghz, 5250, \ + NL80211_CHAN_WIDTH_160) \ + CHANDEF(chandef_5ghz_120_40mhz, chan_5ghz_120, 5610, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_6ghz_20mhz, chan_6ghz, 6115, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_6ghz_40mhz, chan_6ghz, 6125, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_6ghz_80mhz, chan_6ghz, 6145, \ + NL80211_CHAN_WIDTH_80) \ + CHANDEF(chandef_6ghz_160mhz, chan_6ghz, 6185, \ + NL80211_CHAN_WIDTH_160) \ + CHANDEF(chandef_6ghz_320mhz, chan_6ghz, 6105, \ + NL80211_CHAN_WIDTH_320) \ + CHANDEF(chandef_6ghz_221_160mhz, chan_6ghz_221, 6985, \ + NL80211_CHAN_WIDTH_160) \ + /* Feel free to add more */ -CHANDEF(chandef_2ghz, chan_2ghz, 2412, NL80211_CHAN_WIDTH_20); -CHANDEF(chandef_5ghz, chan_5ghz, 5200, NL80211_CHAN_WIDTH_40); -CHANDEF(chandef_6ghz, chan_6ghz, 6115, NL80211_CHAN_WIDTH_160); -/* Feel free to add more */ - -//struct cfg80211_chan_def; +#define CHANDEF(_name, _channel, _freq1, _width) \ +__maybe_unused static const struct cfg80211_chan_def _name = { \ + .chan = &(_channel), \ + .center_freq1 = (_freq1), \ + .width = (_width), \ +}; +CHANDEF_LIST +#undef CHANDEF struct ieee80211_chanctx_conf * -iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def); - -static inline struct ieee80211_chanctx_conf * -iwlmld_kunit_add_chanctx(enum nl80211_band band, enum nl80211_chan_width width) -{ - struct cfg80211_chan_def chandef; - - switch (band) { - case NL80211_BAND_2GHZ: - chandef = chandef_2ghz; - break; - case NL80211_BAND_5GHZ: - chandef = chandef_5ghz; - break; - default: - case NL80211_BAND_6GHZ: - chandef = chandef_6ghz; - break; - } - - chandef.width = width; - - return iwlmld_kunit_add_chanctx_from_def(&chandef); -} +iwlmld_kunit_add_chanctx(const struct cfg80211_chan_def *def); void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, struct ieee80211_bss_conf *link, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c index 1909953a9be9..f8a8c35066be 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c @@ -13,6 +13,9 @@ #include "mld.h" #include "hcmd.h" +#define IWL_MLD_NUM_CTDP_STEPS 20 +#define IWL_MLD_MIN_CTDP_BUDGET_MW 150 + #define IWL_MLD_CT_KILL_DURATION (5 * HZ) void iwl_mld_handle_ct_kill_notif(struct iwl_mld *mld, @@ -116,8 +119,8 @@ free_resp: static int compare_temps(const void *a, const void *b) { - return ((s16)le16_to_cpu(*(__le16 *)a) - - (s16)le16_to_cpu(*(__le16 *)b)); + return ((s16)le16_to_cpu(*(const __le16 *)a) - + (s16)le16_to_cpu(*(const __le16 *)b)); } struct iwl_trip_walk_data { @@ -272,43 +275,27 @@ static void iwl_mld_thermal_zone_register(struct iwl_mld *mld) } } -/* budget in mWatt */ -static const u32 iwl_mld_cdev_budgets[] = { - 2400, /* cooling state 0 */ - 2000, /* cooling state 1 */ - 1800, /* cooling state 2 */ - 1600, /* cooling state 3 */ - 1400, /* cooling state 4 */ - 1200, /* cooling state 5 */ - 1000, /* cooling state 6 */ - 900, /* cooling state 7 */ - 800, /* cooling state 8 */ - 700, /* cooling state 9 */ - 650, /* cooling state 10 */ - 600, /* cooling state 11 */ - 550, /* cooling state 12 */ - 500, /* cooling state 13 */ - 450, /* cooling state 14 */ - 400, /* cooling state 15 */ - 350, /* cooling state 16 */ - 300, /* cooling state 17 */ - 250, /* cooling state 18 */ - 200, /* cooling state 19 */ - 150, /* cooling state 20 */ -}; - int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state, enum iwl_ctdp_cmd_operation op) { struct iwl_ctdp_cmd cmd = { .operation = cpu_to_le32(op), - .budget = cpu_to_le32(iwl_mld_cdev_budgets[state]), .window_size = 0, }; + u32 budget; int ret; lockdep_assert_wiphy(mld->wiphy); + /* Do a linear scale from IWL_MLD_MIN_CTDP_BUDGET_MW to the configured + * maximum in the predefined number of steps. + */ + budget = ((mld->power_budget_mw - IWL_MLD_MIN_CTDP_BUDGET_MW) * + (IWL_MLD_NUM_CTDP_STEPS - 1 - state)) / + (IWL_MLD_NUM_CTDP_STEPS - 1) + + IWL_MLD_MIN_CTDP_BUDGET_MW; + cmd.budget = cpu_to_le32(budget); + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, CTDP_CONFIG_CMD), &cmd); @@ -326,7 +313,7 @@ int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state, static int iwl_mld_tcool_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { - *state = ARRAY_SIZE(iwl_mld_cdev_budgets) - 1; + *state = IWL_MLD_NUM_CTDP_STEPS - 1; return 0; } @@ -354,7 +341,7 @@ static int iwl_mld_tcool_set_cur_state(struct thermal_cooling_device *cdev, goto unlock; } - if (new_state >= ARRAY_SIZE(iwl_mld_cdev_budgets)) { + if (new_state >= IWL_MLD_NUM_CTDP_STEPS) { ret = -EINVAL; goto unlock; } @@ -417,10 +404,50 @@ static void iwl_mld_cooling_device_unregister(struct iwl_mld *mld) } #endif /* CONFIG_THERMAL */ +static u32 iwl_mld_ctdp_get_max_budget(struct iwl_mld *mld) +{ + u64 bios_power_budget = 0; + u32 default_power_budget; + + switch (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id)) { + case IWL_CFG_RF_TYPE_GF: + /* dual-radio devices have a higher budget */ + if (CSR_HW_RFID_IS_CDB(mld->trans->info.hw_rf_id)) + default_power_budget = 5200; + else + default_power_budget = 2880; + break; + case IWL_CFG_RF_TYPE_FM: + default_power_budget = 3450; + break; + case IWL_CFG_RF_TYPE_WH: + case IWL_CFG_RF_TYPE_PE: + default: + default_power_budget = 5550; + break; + } + + iwl_bios_get_pwr_limit(&mld->fwrt, &bios_power_budget); + + /* 32bit in UEFI, 16bit in ACPI; use BIOS value if it is in range */ + if (bios_power_budget && + bios_power_budget != 0xffff && bios_power_budget != 0xffffffff && + bios_power_budget >= IWL_MLD_MIN_CTDP_BUDGET_MW && + bios_power_budget <= default_power_budget) + return (u32)bios_power_budget; + + return default_power_budget; +} + void iwl_mld_thermal_initialize(struct iwl_mld *mld) { + lockdep_assert_not_held(&mld->wiphy->mtx); + wiphy_delayed_work_init(&mld->ct_kill_exit_wk, iwl_mld_exit_ctkill); + mld->power_budget_mw = iwl_mld_ctdp_get_max_budget(mld); + IWL_DEBUG_TEMP(mld, "cTDP power budget: %d mW\n", mld->power_budget_mw); + #ifdef CONFIG_THERMAL iwl_mld_cooling_device_register(mld); iwl_mld_thermal_zone_register(mld); @@ -429,7 +456,9 @@ void iwl_mld_thermal_initialize(struct iwl_mld *mld) void iwl_mld_thermal_exit(struct iwl_mld *mld) { + wiphy_lock(mld->wiphy); wiphy_delayed_work_cancel(mld->wiphy, &mld->ct_kill_exit_wk); + wiphy_unlock(mld->wiphy); #ifdef CONFIG_THERMAL iwl_mld_cooling_device_unregister(mld); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c index f054cc921d9d..a9ca92c0455e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -44,7 +44,7 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, u16 flags = 0; /* STBC flags */ - if (mld->cfg->ht_params->stbc && + if (mld->cfg->ht_params.stbc && (hweight8(iwl_mld_get_valid_tx_ant(mld)) > 1)) { if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] & IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) @@ -56,7 +56,7 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, } /* LDPC */ - if (mld->cfg->ht_params->ldpc && + if (mld->cfg->ht_params.ldpc && ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || (has_vht && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; @@ -658,7 +658,9 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, if (WARN_ON(!mld_link_sta)) return; - mld_link_sta->last_rate_n_flags = le32_to_cpu(notif->rate); + mld_link_sta->last_rate_n_flags = + iwl_v3_rate_from_v2_v3(notif->rate, + mld->fw_rates_ver_3); rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), mld_link_sta->last_rate_n_flags); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c index 543abe72e465..3b4b575aadaa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -76,14 +76,14 @@ static int iwl_mld_allocate_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) */ unsigned int watchdog_timeout = txq->vif->type == NL80211_IFTYPE_AP ? IWL_WATCHDOG_DISABLED : - mld->trans->trans_cfg->base_params->wd_timeout; + mld->trans->mac_cfg->base->wd_timeout; int queue, size; lockdep_assert_wiphy(mld->wiphy); if (tid == IWL_MGMT_TID) size = max_t(u32, IWL_MGMT_QUEUE_SIZE, - mld->trans->cfg->min_txq_size); + mld->trans->mac_cfg->base->min_txq_size); else size = iwl_mld_get_queue_size(mld, txq); @@ -391,9 +391,9 @@ static u32 iwl_mld_mac80211_rate_idx_to_fw(struct iwl_mld *mld, /* Set CCK or OFDM flag */ if (rate_idx <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; + rate_flags |= RATE_MCS_MOD_TYPE_CCK; else - rate_flags |= RATE_MCS_LEGACY_OFDM_MSK; + rate_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM; /* Legacy rates are indexed: * 0 - 3 for CCK and 0 - 7 for OFDM @@ -425,47 +425,40 @@ static u32 iwl_mld_get_inject_tx_rate(struct iwl_mld *mld, struct ieee80211_tx_rate *rate = &info->control.rates[0]; u32 result; - /* we only care about legacy/HT/VHT so far, so we can - * build in v1 and use iwl_new_rate_from_v1() - * FIXME: in newer devices we only support the new rates, build - * the rate_n_flags in the new format here instead of using v1 and - * converting it. - */ - if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { u8 mcs = ieee80211_rate_get_vht_mcs(rate); u8 nss = ieee80211_rate_get_vht_nss(rate); - result = RATE_MCS_VHT_MSK_V1; - result |= u32_encode_bits(mcs, RATE_VHT_MCS_RATE_CODE_MSK); + result = RATE_MCS_MOD_TYPE_VHT; + result |= u32_encode_bits(mcs, RATE_MCS_CODE_MSK); result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - result |= RATE_MCS_SGI_MSK_V1; + result |= RATE_MCS_SGI_MSK; if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1); + result |= RATE_MCS_CHAN_WIDTH_40; else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) - result |= u32_encode_bits(2, RATE_MCS_CHAN_WIDTH_MSK_V1); + result |= RATE_MCS_CHAN_WIDTH_80; else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) - result |= u32_encode_bits(3, RATE_MCS_CHAN_WIDTH_MSK_V1); - - result = iwl_new_rate_from_v1(result); + result |= RATE_MCS_CHAN_WIDTH_160; } else if (rate->flags & IEEE80211_TX_RC_MCS) { - result = RATE_MCS_HT_MSK_V1; - result |= u32_encode_bits(rate->idx, - RATE_HT_MCS_RATE_CODE_MSK_V1 | - RATE_HT_MCS_NSS_MSK_V1); + /* only MCS 0-15 are supported */ + u8 mcs = rate->idx & 7; + u8 nss = rate->idx > 7; + + result = RATE_MCS_MOD_TYPE_HT; + result |= u32_encode_bits(mcs, RATE_MCS_CODE_MSK); + result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - result |= RATE_MCS_SGI_MSK_V1; + result |= RATE_MCS_SGI_MSK; if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1); + result |= RATE_MCS_CHAN_WIDTH_40; if (info->flags & IEEE80211_TX_CTL_LDPC) - result |= RATE_MCS_LDPC_MSK_V1; + result |= RATE_MCS_LDPC_MSK; if (u32_get_bits(info->flags, IEEE80211_TX_CTL_STBC)) result |= RATE_MCS_STBC_MSK; - - result = iwl_new_rate_from_v1(result); } else { result = iwl_mld_mac80211_rate_idx_to_fw(mld, info, rate->idx); } @@ -479,19 +472,23 @@ static u32 iwl_mld_get_inject_tx_rate(struct iwl_mld *mld, return result; } -static u32 iwl_mld_get_tx_rate_n_flags(struct iwl_mld *mld, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, __le16 fc) +static __le32 iwl_mld_get_tx_rate_n_flags(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) { + u32 rate; + if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) - return iwl_mld_get_inject_tx_rate(mld, info, sta, fc); + rate = iwl_mld_get_inject_tx_rate(mld, info, sta, fc); + else + rate = iwl_mld_mac80211_rate_idx_to_fw(mld, info, -1) | + iwl_mld_get_tx_ant(mld, info, sta, fc); - return iwl_mld_mac80211_rate_idx_to_fw(mld, info, -1) | - iwl_mld_get_tx_ant(mld, info, sta, fc); + return iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3); } static void -iwl_mld_fill_tx_cmd_hdr(struct iwl_tx_cmd_gen3 *tx_cmd, +iwl_mld_fill_tx_cmd_hdr(struct iwl_tx_cmd *tx_cmd, struct sk_buff *skb, bool amsdu) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -537,11 +534,11 @@ iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb, struct ieee80211_hdr *hdr = (void *)skb->data; struct iwl_mld_sta *mld_sta = sta ? iwl_mld_sta_from_mac80211(sta) : NULL; - struct iwl_tx_cmd_gen3 *tx_cmd; + struct iwl_tx_cmd *tx_cmd; bool amsdu = ieee80211_is_data_qos(hdr->frame_control) && (*ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_A_MSDU_PRESENT); - u32 rate_n_flags = 0; + __le32 rate_n_flags = 0; u16 flags = 0; dev_tx_cmd->hdr.cmd = TX_CMD; @@ -576,7 +573,7 @@ iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb, tx_cmd->flags = cpu_to_le16(flags); - tx_cmd->rate_n_flags = cpu_to_le32(rate_n_flags); + tx_cmd->rate_n_flags = rate_n_flags; } /* Caller of this need to check that info->control.vif is not NULL */ @@ -641,16 +638,36 @@ iwl_mld_get_tx_queue_id(struct iwl_mld *mld, struct ieee80211_txq *txq, case NL80211_IFTYPE_P2P_DEVICE: mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); - if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES) { - IWL_DEBUG_DROP(mld, "Drop tx outside ROC\n"); + if (mld_vif->roc_activity != ROC_ACTIVITY_P2P_DISC && + mld_vif->roc_activity != ROC_ACTIVITY_P2P_NEG) { + IWL_DEBUG_DROP(mld, + "Drop tx outside ROC with activity %d\n", + mld_vif->roc_activity); return IWL_MLD_INVALID_DROP_TX; } WARN_ON(!ieee80211_is_mgmt(fc)); - return mld_vif->deflink.aux_sta.queue_id; + return mld_vif->aux_sta.queue_id; + case NL80211_IFTYPE_MONITOR: + mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); + return mld_vif->deflink.mon_sta.queue_id; + case NL80211_IFTYPE_STATION: + mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); + + if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) { + IWL_DEBUG_DROP(mld, "Drop tx not off-channel\n"); + return IWL_MLD_INVALID_DROP_TX; + } + + if (mld_vif->roc_activity != ROC_ACTIVITY_HOTSPOT) { + IWL_DEBUG_DROP(mld, "Drop tx outside ROC\n"); + return IWL_MLD_INVALID_DROP_TX; + } + + WARN_ON(!ieee80211_is_mgmt(fc)); + return mld_vif->aux_sta.queue_id; default: - /* TODO: consider monitor (task=monitor) */ WARN_ONCE(1, "Unsupported vif type\n"); break; } @@ -831,7 +848,7 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, * 1 more for the potential data in the header */ if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) > - mld->trans->max_skb_frags) + mld->trans->info.max_skb_frags) num_subframes = 1; if (num_subframes > 1) @@ -977,11 +994,14 @@ void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) rcu_read_unlock(); } -static void iwl_mld_hwrate_to_tx_rate(u32 rate_n_flags, +static void iwl_mld_hwrate_to_tx_rate(struct iwl_mld *mld, + __le32 rate_n_flags_fw, struct ieee80211_tx_info *info) { enum nl80211_band band = info->band; struct ieee80211_tx_rate *tx_rate = &info->status.rates[0]; + u32 rate_n_flags = iwl_v3_rate_from_v2_v3(rate_n_flags_fw, + mld->fw_rates_ver_3); u32 sgi = rate_n_flags & RATE_MCS_SGI_MSK; u32 chan_width = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; @@ -1006,18 +1026,19 @@ static void iwl_mld_hwrate_to_tx_rate(u32 rate_n_flags, } switch (format) { - case RATE_MCS_HT_MSK: + case RATE_MCS_MOD_TYPE_HT: tx_rate->flags |= IEEE80211_TX_RC_MCS; tx_rate->idx = RATE_HT_MCS_INDEX(rate_n_flags); break; - case RATE_MCS_VHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: ieee80211_rate_set_vht(tx_rate, rate_n_flags & RATE_MCS_CODE_MSK, - FIELD_GET(RATE_MCS_NSS_MSK, - rate_n_flags) + 1); + u32_get_bits(rate_n_flags, + RATE_MCS_NSS_MSK) + 1); tx_rate->flags |= IEEE80211_TX_RC_VHT_MCS; break; - case RATE_MCS_HE_MSK: + case RATE_MCS_MOD_TYPE_HE: + case RATE_MCS_MOD_TYPE_EHT: /* mac80211 cannot do this without ieee80211_tx_status_ext() * but it only matters for radiotap */ @@ -1111,8 +1132,7 @@ void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld, iwl_dbg_tlv_time_point(&mld->fwrt, tp, NULL); } - iwl_mld_hwrate_to_tx_rate(le32_to_cpu(tx_resp->initial_rate), - info); + iwl_mld_hwrate_to_tx_rate(mld, tx_resp->initial_rate, info); if (likely(!iwl_mld_time_sync_frame(mld, skb, hdr->addr1))) ieee80211_tx_status_skb(mld->hw, skb); |