diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mld')
33 files changed, 657 insertions, 1026 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/Makefile index ece66e7a9be4..c966e573f430 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mld/Makefile @@ -9,8 +9,4 @@ iwlmld-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o iwlmld-$(CONFIG_IWLWIFI_LEDS) += led.o iwlmld-$(CONFIG_PM_SLEEP) += d3.o -# non-upstream things -iwlmld-$(CONFIG_IWL_VENDOR_CMDS) += vendor-cmd.o -iwlmld-$(CONFIG_IWLMVM_AX_SOFTAP_TESTMODE) += ax-softap-testmode.o - subdir-ccflags-y += -I$(src)/../ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index 6b349270481d..3bf36f8f6874 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -305,10 +305,15 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, * already ahead and it will be dropped. * If the last sub-frame is not on this queue - we will get frame * release notification with up to date NSSN. + * If this is the first frame that is stored in the buffer, the head_sn + * may be outdated. Update it based on the last NSSN to make sure it + * will be released when the frame release notification arrives. */ if (!amsdu || last_subframe) iwl_mld_reorder_release_frames(mld, sta, napi, baid_data, buffer, nssn); + else if (buffer->num_stored == 1) + buffer->head_sn = nssn; return IWL_MLD_BUFFERED_SKB; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c index 26511b49d89a..5c59acc8c4c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ap.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c @@ -294,9 +294,20 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, if (ret) return ret; + mld_vif->ap_ibss_active = true; + + if (vif->p2p && mld->p2p_device_vif) { + ret = iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, + FW_CTXT_ACTION_MODIFY); + if (ret) { + mld_vif->ap_ibss_active = false; + goto rm_mcast; + } + } + ret = iwl_mld_add_bcast_sta(mld, vif, link); if (ret) - goto rm_mcast; + goto update_p2p_dev; /* Those keys were configured by the upper layers before starting the * AP. Now that it is started and the bcast and mcast sta were added to @@ -310,12 +321,6 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, iwl_mld_vif_update_low_latency(mld, vif, true, LOW_LATENCY_VIF_TYPE); - mld_vif->ap_ibss_active = true; - - if (vif->p2p && mld->p2p_device_vif) - 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. @@ -326,6 +331,11 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, return 0; rm_bcast: iwl_mld_remove_bcast_sta(mld, vif, link); +update_p2p_dev: + mld_vif->ap_ibss_active = false; + if (vif->p2p && mld->p2p_device_vif) + iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, + FW_CTXT_ACTION_MODIFY); rm_mcast: iwl_mld_remove_mcast_sta(mld, vif, link); return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/coex.c b/drivers/net/wireless/intel/iwlwifi/mld/coex.c index 32c727b3b391..5f262bd43f21 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/coex.c @@ -24,13 +24,17 @@ 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 = (const void *)pkt->data; + const struct iwl_bt_coex_profile_notif *notif = (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)); - mld->last_bt_notif = *notif; + if (bt_is_active == mld->bt_is_active) + return; + 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/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h index 2a59b29b75cb..49accf96f44b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h @@ -40,15 +40,6 @@ #define IWL_MLD_TPT_COUNT_WINDOW (5 * HZ) -/* OMI reduced BW thresholds (channel load percentage) */ -#define IWL_MLD_OMI_ENTER_CHAN_LOAD 10 -#define IWL_MLD_OMI_EXIT_CHAN_LOAD_160 20 -#define IWL_MLD_OMI_EXIT_CHAN_LOAD_320 30 -/* time (in milliseconds) to let AP "settle" the OMI */ -#define IWL_MLD_OMI_AP_SETTLE_DELAY 27 -/* time (in milliseconds) to not enter OMI reduced BW after leaving */ -#define IWL_MLD_OMI_EXIT_PROTECTION 5000 - #define IWL_MLD_DIS_RANDOM_FW_ID false #define IWL_MLD_D3_DEBUG false #define IWL_MLD_NON_TRANSMITTING_AP false diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index c776543cbba5..ed0a0f76f1c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -204,66 +204,6 @@ void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw, } #endif -enum rt_status { - FW_ALIVE, - FW_NEEDS_RESET, - FW_ERROR, -}; - -static enum rt_status iwl_mld_check_err_tables(struct iwl_mld *mld, - struct ieee80211_vif *vif) -{ - u32 err_id; - - /* check for lmac1 error */ - if (iwl_fwrt_read_err_table(mld->trans, - mld->trans->dbg.lmac_error_event_table[0], - &err_id)) { - if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) { - struct cfg80211_wowlan_wakeup wakeup = { - .rfkill_release = true, - }; - ieee80211_report_wowlan_wakeup(vif, &wakeup, - GFP_KERNEL); - - return FW_NEEDS_RESET; - } - return FW_ERROR; - } - - /* check if we have lmac2 set and check for error */ - if (iwl_fwrt_read_err_table(mld->trans, - mld->trans->dbg.lmac_error_event_table[1], - NULL)) - return FW_ERROR; - - /* check for umac error */ - if (iwl_fwrt_read_err_table(mld->trans, - mld->trans->dbg.umac_error_event_table, - NULL)) - return FW_ERROR; - - return FW_ALIVE; -} - -static bool iwl_mld_fw_needs_restart(struct iwl_mld *mld, - struct ieee80211_vif *vif) -{ - enum rt_status rt_status = iwl_mld_check_err_tables(mld, vif); - - if (rt_status == FW_ALIVE) - return false; - - if (rt_status == FW_ERROR) { - IWL_ERR(mld, "FW Error occurred during suspend\n"); - iwl_fwrt_dump_error_logs(&mld->fwrt); - iwl_dbg_tlv_time_point(&mld->fwrt, - IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); - } - - return true; -} - static int iwl_mld_netdetect_config(struct iwl_mld *mld, struct ieee80211_vif *vif, @@ -707,51 +647,6 @@ iwl_mld_set_key_rx_seq(struct ieee80211_key_conf *key, } static void -iwl_mld_d3_update_mcast_key(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct iwl_mld_wowlan_status *wowlan_status, - struct ieee80211_key_conf *key, - struct iwl_mld_mcast_key_data *key_data) -{ - if (key->keyidx != key_data->id && - (key->keyidx < 4 || key->keyidx > 5)) { - IWL_ERR(mld, - "Unexpected keyId mismatch. Old keyId:%d, New keyId:%d\n", - key->keyidx, key_data->id); - return; - } - - /* All installed keys are sent by the FW, even weren't - * rekeyed during D3. - * We remove an existing key if it has the same index as - * a new key and a rekey has occurred during d3 - */ - if (wowlan_status->num_of_gtk_rekeys && key_data->len) { - if (key->keyidx == 4 || key->keyidx == 5) { - struct iwl_mld_vif *mld_vif = - iwl_mld_vif_from_mac80211(vif); - struct iwl_mld_link *mld_link; - int link_id = vif->active_links ? - __ffs(vif->active_links) : 0; - - mld_link = iwl_mld_link_dereference_check(mld_vif, - link_id); - if (WARN_ON(!mld_link)) - return; - - if (mld_link->igtk == key) - mld_link->igtk = NULL; - mld->num_igtks--; - } - - ieee80211_remove_key(key); - return; - } - - iwl_mld_set_key_rx_seq(key, key_data); -} - -static void iwl_mld_update_ptk_rx_seq(struct iwl_mld *mld, struct iwl_mld_wowlan_status *wowlan_status, struct ieee80211_sta *sta, @@ -819,8 +714,7 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, data->gtk_cipher = key->cipher; status_idx = key->keyidx == wowlan_status->gtk[1].id; - iwl_mld_d3_update_mcast_key(data->mld, vif, wowlan_status, key, - &wowlan_status->gtk[status_idx]); + iwl_mld_set_key_rx_seq(key, &wowlan_status->gtk[status_idx]); break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256: @@ -832,9 +726,8 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, return; data->igtk_cipher = key->cipher; - iwl_mld_d3_update_mcast_key(data->mld, vif, - wowlan_status, - key, &wowlan_status->igtk); + if (key->keyidx == wowlan_status->igtk.id) + iwl_mld_set_key_rx_seq(key, &wowlan_status->igtk); } if (key->keyidx == 6 || key->keyidx == 7) { if (WARN_ON(data->bigtk_cipher && @@ -843,9 +736,7 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, data->bigtk_cipher = key->cipher; status_idx = key->keyidx == wowlan_status->bigtk[1].id; - iwl_mld_d3_update_mcast_key(data->mld, vif, - wowlan_status, key, - &wowlan_status->bigtk[status_idx]); + iwl_mld_set_key_rx_seq(key, &wowlan_status->bigtk[status_idx]); } break; default: @@ -855,7 +746,7 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, data->num_keys++; } -static bool +static void iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, struct iwl_mld *mld, struct iwl_mld_mcast_key_data *key_data, @@ -871,6 +762,7 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, .conf.keyidx = key_data->id, }; int link_id = vif->active_links ? __ffs(vif->active_links) : -1; + u8 key[WOWLAN_KEY_MAX_SIZE]; BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP); BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP); @@ -882,7 +774,7 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, BUILD_BUG_ON(sizeof(conf.key) < sizeof(key_data->key)); if (!key_data->len) - return true; + return; switch (cipher) { case WLAN_CIPHER_SUITE_CCMP: @@ -912,9 +804,13 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, } memcpy(conf.conf.key, key_data->key, conf.conf.keylen); - key_config = ieee80211_gtk_rekey_add(vif, &conf.conf, link_id); + + memcpy(key, key_data->key, sizeof(key_data->key)); + + key_config = ieee80211_gtk_rekey_add(vif, key_data->id, key, + sizeof(key), link_id); if (IS_ERR(key_config)) - return false; + return; iwl_mld_set_key_rx_seq(key_config, key_data); @@ -922,13 +818,28 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, if (key_config->keyidx == 4 || key_config->keyidx == 5) { struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); - mld_link->igtk = key_config; - mld->num_igtks++; + + /* If we had more than one rekey, mac80211 will tell us to + * remove the old and add the new so we will update the IGTK in + * drv_set_key + */ + if (mld_link->igtk && mld_link->igtk != key_config) { + /* mark the old IGTK as not in FW */ + mld_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID; + mld_link->igtk = key_config; + } + } + + /* Also keep track of the new BIGTK */ + if ((key_config->keyidx == 6 || key_config->keyidx == 7) && + vif->type == NL80211_IFTYPE_STATION) { + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + rcu_assign_pointer(mld_vif->bigtks[key_config->keyidx - 6], key_config); } - return true; } -static bool +static void iwl_mld_add_all_rekeys(struct ieee80211_vif *vif, struct iwl_mld_wowlan_status *wowlan_status, struct iwl_mld_resume_key_iter_data *key_iter_data, @@ -937,25 +848,20 @@ iwl_mld_add_all_rekeys(struct ieee80211_vif *vif, int i; for (i = 0; i < ARRAY_SIZE(wowlan_status->gtk); i++) - if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, - &wowlan_status->gtk[i], - link_conf, - key_iter_data->gtk_cipher)) - return false; - - if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, - &wowlan_status->igtk, - link_conf, key_iter_data->igtk_cipher)) - return false; + iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, + &wowlan_status->gtk[i], + link_conf, + key_iter_data->gtk_cipher); - for (i = 0; i < ARRAY_SIZE(wowlan_status->bigtk); i++) - if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, - &wowlan_status->bigtk[i], - link_conf, - key_iter_data->bigtk_cipher)) - return false; + iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, + &wowlan_status->igtk, + link_conf, key_iter_data->igtk_cipher); - return true; + for (i = 0; i < ARRAY_SIZE(wowlan_status->bigtk); i++) + iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, + &wowlan_status->bigtk[i], + link_conf, + key_iter_data->bigtk_cipher); } static bool @@ -1317,6 +1223,13 @@ int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld) struct iwl_d3_manager_config d3_cfg_cmd_data = {}; int ret; + if (mld->debug_max_sleep) { + d3_cfg_cmd_data.wakeup_host_timer = + cpu_to_le32(mld->debug_max_sleep); + d3_cfg_cmd_data.wakeup_flags = + cpu_to_le32(IWL_WAKEUP_D3_HOST_TIMER); + } + lockdep_assert_wiphy(mld->wiphy); IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan suspend flow\n"); @@ -1376,10 +1289,7 @@ int iwl_mld_no_wowlan_resume(struct iwl_mld *mld) mld->fw_status.in_d3 = false; iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); - if (iwl_mld_fw_needs_restart(mld, NULL)) - ret = -ENODEV; - else - ret = iwl_mld_wait_d3_notif(mld, &resume_data, false); + ret = iwl_mld_wait_d3_notif(mld, &resume_data, false); if (!ret && (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE)) return -ENODEV; @@ -1909,6 +1819,7 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) goto err; } + mld->fw_status.resuming = true; mld->fw_status.in_d3 = false; mld->scan.last_start_time_jiffies = jiffies; @@ -1928,15 +1839,10 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); - if (iwl_mld_fw_needs_restart(mld, bss_vif)) { - fw_err = true; - goto err; - } - resume_data.wowlan_status = kzalloc(sizeof(*resume_data.wowlan_status), GFP_KERNEL); if (!resume_data.wowlan_status) - return -1; + return -ENOMEM; if (mld->netdetect) resume_data.notifs_expected |= IWL_D3_ND_MATCH_INFO; @@ -1989,6 +1895,8 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) mld->fw_status.in_hw_restart = true; ret = 1; out: + mld->fw_status.resuming = false; + if (resume_data.wowlan_status) { kfree(resume_data.wowlan_status->wake_packet); kfree(resume_data.wowlan_status); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index 352da8aa7898..cc052b0aa53f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -86,7 +86,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mld *mld, char *buf, if (count == 6 && !strcmp(buf, "nolog\n")) { mld->fw_status.do_not_dump_once = true; - set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &mld->trans->status); + iwl_trans_suppress_cmd_error_once(mld->trans); } /* take the return value to make compiler happy - it will @@ -546,6 +546,11 @@ iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) #endif MLD_DEBUGFS_ADD_FILE(inject_packet, debugfs_dir, 0200); +#ifdef CONFIG_PM_SLEEP + debugfs_create_u32("max_sleep", 0600, debugfs_dir, + &mld->debug_max_sleep); +#endif + debugfs_create_bool("rx_ts_ptp", 0600, debugfs_dir, &mld->monitor.ptp_time); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c index f77ba21a174d..3464b3268712 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c @@ -94,7 +94,7 @@ iwl_mld_ftm_set_target_chandef(struct iwl_mld *mld, IWL_ERR(mld, "Unsupported BW in FTM request (%d)\n", peer->chandef.width); return -EINVAL; -} + } /* non EDCA based measurement must use HE preamble */ if (peer->ftm.trigger_based || peer->ftm.non_trigger_based) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c index 9d2c087360e7..b372173c4a79 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -294,7 +294,7 @@ static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) return ret; ret = iwl_pnvm_load(mld->trans, &mld->notif_wait, - &mld->fw->ucode_capa, alive_data.sku_id); + mld->fw, alive_data.sku_id); if (ret) { IWL_ERR(mld, "Timeout waiting for PNVM load %d\n", ret); return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index 235b55e0fe59..38993d65c052 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -55,6 +55,8 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif) ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL); + wiphy_delayed_work_cancel(mld->wiphy, &mld_vif->mlo_scan_start_wk); + CLEANUP_STRUCT(mld_vif); } @@ -385,6 +387,17 @@ int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, return iwl_mld_send_mac_cmd(mld, &cmd); } +static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy, + struct wiphy_work *wk) +{ + struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, + mlo_scan_start_wk.work); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif)); +} + IWL_MLD_ALLOC_FN(vif, vif) /* Constructor function for struct iwl_mld_vif */ @@ -412,6 +425,8 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) iwl_mld_emlsr_prevent_done_wk); wiphy_delayed_work_init(&mld_vif->emlsr.tmp_non_bss_done_wk, iwl_mld_emlsr_tmp_non_bss_done_wk); + wiphy_delayed_work_init(&mld_vif->mlo_scan_start_wk, + iwl_mld_mlo_scan_start_wk); } iwl_mld_init_internal_sta(&mld_vif->aux_sta); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index 49e2ce65557d..05dcb63701b1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -87,6 +87,8 @@ enum iwl_mld_emlsr_exit { * @last_exit_reason: Reason for the last EMLSR exit * @last_exit_ts: Time of the last EMLSR exit (if @last_exit_reason is non-zero) * @exit_repeat_count: Number of times EMLSR was exited for the same reason + * @last_entry_ts: the time of the last EMLSR entry (if iwl_mld_emlsr_active() + * is true) * @unblock_tpt_wk: Unblock EMLSR because the throughput limit was reached * @check_tpt_wk: a worker to check if IWL_MLD_EMLSR_BLOCKED_TPT should be * added, for example if there is no longer enough traffic. @@ -105,6 +107,7 @@ struct iwl_mld_emlsr { enum iwl_mld_emlsr_exit last_exit_reason; unsigned long last_exit_ts; u8 exit_repeat_count; + unsigned long last_entry_ts; ); struct wiphy_work unblock_tpt_wk; @@ -133,6 +136,8 @@ struct iwl_mld_emlsr { * @low_latency_causes: bit flags, indicating the causes for low-latency, * see @iwl_mld_low_latency_cause. * @ps_disabled: indicates that PS is disabled for this interface + * @last_link_activation_time: last time a link was activated, for + * deferring MLO scans (to make them more reliable) * @mld: pointer to the mld structure. * @deflink: default link data, for use in non-MLO, * @link: reference to link data for each valid link, for use in MLO. @@ -144,6 +149,7 @@ struct iwl_mld_emlsr { * @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. + * @mlo_scan_start_wk: worker to start a deferred MLO scan */ struct iwl_mld_vif { /* Add here fields that need clean up on restart */ @@ -161,6 +167,7 @@ struct iwl_mld_vif { #endif u8 low_latency_causes; bool ps_disabled; + time64_t last_link_activation_time; ); /* And here fields that survive a fw restart */ struct iwl_mld *mld; @@ -179,6 +186,8 @@ struct iwl_mld_vif { #endif enum iwl_roc_activity roc_activity; struct iwl_mld_int_sta aux_sta; + + struct wiphy_delayed_work mlo_scan_start_wk; }; static inline struct iwl_mld_vif * @@ -187,6 +196,12 @@ iwl_mld_vif_from_mac80211(struct ieee80211_vif *vif) return (void *)vif->drv_priv; } +static inline struct ieee80211_vif * +iwl_mld_vif_to_mac80211(struct iwl_mld_vif *mld_vif) +{ + return container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); +} + #define iwl_mld_link_dereference_check(mld_vif, link_id) \ rcu_dereference_check((mld_vif)->link[link_id], \ lockdep_is_held(&mld_vif->mld->wiphy->mtx)) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.c b/drivers/net/wireless/intel/iwlwifi/mld/key.c index 0eff13e5ffd5..13462a5ad79a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/key.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.c @@ -129,6 +129,12 @@ static int iwl_mld_add_key_to_fw(struct iwl_mld *mld, u32 sta_mask, bool tkip = key->cipher == WLAN_CIPHER_SUITE_TKIP; int max_key_len = sizeof(cmd.u.add.key); +#ifdef CONFIG_PM_SLEEP + /* If there was a rekey in wowlan, FW already has the key */ + if (mld->fw_status.resuming) + return 0; +#endif + if (WARN_ON(!sta_mask)) return -EINVAL; @@ -160,6 +166,12 @@ static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask, .u.remove.key_flags = cpu_to_le32(key_flags), }; +#ifdef CONFIG_PM_SLEEP + /* If there was a rekey in wowlan, FW already removed the key */ + if (mld->fw_status.resuming) + return; +#endif + if (WARN_ON(!sta_mask)) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index d0f56189ad3f..782fc41aa1c3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -242,27 +242,9 @@ static bool iwl_mld_fill_mu_edca(struct iwl_mld *mld, return true; } -static u8 iwl_mld_sta_rx_bw_to_fw(enum ieee80211_sta_rx_bandwidth bw) -{ - switch (bw) { - default: /* potential future values not supported by this hw/driver */ - case IEEE80211_STA_RX_BW_20: - return IWL_LINK_MODIFY_BW_20; - case IEEE80211_STA_RX_BW_40: - return IWL_LINK_MODIFY_BW_40; - case IEEE80211_STA_RX_BW_80: - return IWL_LINK_MODIFY_BW_80; - case IEEE80211_STA_RX_BW_160: - return IWL_LINK_MODIFY_BW_160; - case IEEE80211_STA_RX_BW_320: - return IWL_LINK_MODIFY_BW_320; - } -} - -static int _iwl_mld_change_link_in_fw(struct iwl_mld *mld, - struct ieee80211_bss_conf *link, - enum ieee80211_sta_rx_bandwidth bw, - u32 changes) +int +iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link, + u32 changes) { struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); struct ieee80211_vif *vif = link->vif; @@ -318,9 +300,6 @@ static int _iwl_mld_change_link_in_fw(struct iwl_mld *mld, cmd.bi = cpu_to_le32(link->beacon_int); cmd.dtim_interval = cpu_to_le32(link->beacon_int * link->dtim_period); - if (changes & LINK_CONTEXT_MODIFY_BANDWIDTH) - cmd.modify_bandwidth = iwl_mld_sta_rx_bw_to_fw(bw); - /* Configure HE parameters only if HE is supported, and only after * the parameters are set in mac80211 (meaning after assoc) */ @@ -382,28 +361,11 @@ send_cmd: return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY); } -int iwl_mld_change_link_in_fw(struct iwl_mld *mld, - struct ieee80211_bss_conf *link, - u32 changes) -{ - if (WARN_ON(changes & LINK_CONTEXT_MODIFY_BANDWIDTH)) - changes &= ~LINK_CONTEXT_MODIFY_BANDWIDTH; - - return _iwl_mld_change_link_in_fw(mld, link, 0, changes); -} - -int iwl_mld_change_link_omi_bw(struct iwl_mld *mld, - struct ieee80211_bss_conf *link, - enum ieee80211_sta_rx_bandwidth bw) -{ - return _iwl_mld_change_link_in_fw(mld, link, bw, - LINK_CONTEXT_MODIFY_BANDWIDTH); -} - int iwl_mld_activate_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link) { struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(link->vif); int ret; lockdep_assert_wiphy(mld->wiphy); @@ -411,13 +373,15 @@ int iwl_mld_activate_link(struct iwl_mld *mld, if (WARN_ON(!mld_link || mld_link->active)) return -EINVAL; - mld_link->rx_omi.exit_ts = jiffies; mld_link->active = true; ret = iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ACTIVE); if (ret) mld_link->active = false; + else + mld_vif->last_link_activation_time = + ktime_get_boottime_seconds(); return ret; } @@ -473,303 +437,6 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link) iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); } -static void iwl_mld_omi_bw_update(struct iwl_mld *mld, - struct ieee80211_bss_conf *link_conf, - struct iwl_mld_link *mld_link, - struct ieee80211_link_sta *link_sta, - enum ieee80211_sta_rx_bandwidth bw, - bool ap_update) -{ - enum ieee80211_sta_rx_bandwidth apply_bw; - - mld_link->rx_omi.desired_bw = bw; - - /* Can't update OMI while already in progress, desired_bw was - * set so on FW notification the worker will see the change - * and apply new the new desired bw. - */ - if (mld_link->rx_omi.bw_in_progress) - return; - - if (bw == IEEE80211_STA_RX_BW_MAX) - apply_bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width); - else - apply_bw = bw; - - if (!ap_update) { - /* The update isn't due to AP tracking after leaving OMI, - * where the AP could increase BW and then we must tell - * it that we can do the increased BW as well, if we did - * update the chandef. - * In this case, if we want MAX, then we will need to send - * a new OMI to the AP if it increases its own bandwidth as - * we can (due to internal and FW limitations, and being - * worried the AP might break) only send to what we're doing - * at the moment. In this case, set last_max_bw; otherwise - * if we really want to decrease our bandwidth set it to 0 - * to indicate no updates are needed if the AP changes. - */ - if (bw != IEEE80211_STA_RX_BW_MAX) - mld_link->rx_omi.last_max_bw = apply_bw; - else - mld_link->rx_omi.last_max_bw = 0; - } else { - /* Otherwise, if we're already trying to do maximum and - * the AP is changing, set last_max_bw to the new max the - * AP is using, we'll only get to this code path if the - * new bandwidth of the AP is bigger than what we sent it - * previously. This avoids repeatedly sending updates if - * it changes bandwidth, only doing it once on an increase. - */ - mld_link->rx_omi.last_max_bw = apply_bw; - } - - if (ieee80211_prepare_rx_omi_bw(link_sta, bw)) { - mld_link->rx_omi.bw_in_progress = apply_bw; - iwl_mld_change_link_omi_bw(mld, link_conf, apply_bw); - } -} - -static void iwl_mld_omi_bw_finished_work(struct wiphy *wiphy, - struct wiphy_work *work) -{ - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); - struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); - struct iwl_mld_link *mld_link = - container_of(work, typeof(*mld_link), rx_omi.finished_work.work); - enum ieee80211_sta_rx_bandwidth desired_bw, switched_to_bw; - struct ieee80211_vif *vif = mld_link->vif; - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct ieee80211_bss_conf *link_conf; - struct ieee80211_link_sta *link_sta; - - if (!mld_vif->ap_sta) - return; - - link_sta = wiphy_dereference(mld->wiphy, - mld_vif->ap_sta->link[mld_link->link_id]); - if (WARN_ON_ONCE(!link_sta)) - return; - - link_conf = link_conf_dereference_protected(vif, link_sta->link_id); - if (WARN_ON_ONCE(!link_conf)) - return; - - if (WARN_ON(!mld_link->rx_omi.bw_in_progress)) - return; - - desired_bw = mld_link->rx_omi.desired_bw; - switched_to_bw = mld_link->rx_omi.bw_in_progress; - - ieee80211_finalize_rx_omi_bw(link_sta); - mld_link->rx_omi.bw_in_progress = 0; - - if (desired_bw != switched_to_bw) - iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, - desired_bw, false); -} - -static struct ieee80211_vif * -iwl_mld_get_omi_bw_reduction_pointers(struct iwl_mld *mld, - struct ieee80211_link_sta **link_sta, - struct iwl_mld_link **mld_link) -{ - struct iwl_mld_vif *mld_vif; - struct ieee80211_vif *vif; - int n_link_stas = 0; - - *link_sta = NULL; - - if (mld->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC) - return NULL; - - vif = iwl_mld_get_bss_vif(mld); - if (!vif) - return NULL; - - for (int i = 0; i < ARRAY_SIZE(mld->fw_id_to_link_sta); i++) { - struct ieee80211_link_sta *tmp; - - tmp = wiphy_dereference(mld->wiphy, mld->fw_id_to_link_sta[i]); - if (IS_ERR_OR_NULL(tmp)) - continue; - - n_link_stas++; - *link_sta = tmp; - } - - /* can't do anything if we have TDLS peers or EMLSR */ - if (n_link_stas != 1) - return NULL; - - mld_vif = iwl_mld_vif_from_mac80211(vif); - *mld_link = iwl_mld_link_dereference_check(mld_vif, - (*link_sta)->link_id); - if (WARN_ON(!*mld_link)) - return NULL; - - return vif; -} - -void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld, - struct ieee80211_bss_conf *link_conf, - enum ieee80211_sta_rx_bandwidth bw) -{ - struct ieee80211_link_sta *link_sta; - struct iwl_mld_link *mld_link; - struct ieee80211_vif *vif; - - vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); - if (!vif) - return; - - if (WARN_ON(link_conf->vif != vif)) - return; - - /* This is 0 if we requested an OMI BW reduction and don't want to - * be sending an OMI when the AP's bandwidth changes. - */ - if (!mld_link->rx_omi.last_max_bw) - return; - - /* We only need to tell the AP if it increases BW over what we last - * told it we were using, if it reduces then our last OMI to it will - * not get used anyway (e.g. we said we want 160 but it's doing 80.) - */ - if (bw < mld_link->rx_omi.last_max_bw) - return; - - iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, true); -} - -void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld, - struct iwl_rx_packet *pkt) -{ - struct ieee80211_link_sta *link_sta; - struct iwl_mld_link *mld_link; - struct ieee80211_vif *vif; - - vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); - if (IWL_FW_CHECK(mld, !vif, "unexpected OMI notification\n")) - return; - - if (IWL_FW_CHECK(mld, !mld_link->rx_omi.bw_in_progress, - "OMI notification when not requested\n")) - return; - - wiphy_delayed_work_queue(mld->hw->wiphy, - &mld_link->rx_omi.finished_work, - msecs_to_jiffies(IWL_MLD_OMI_AP_SETTLE_DELAY)); -} - -void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld) -{ - struct ieee80211_bss_conf *link_conf; - struct ieee80211_link_sta *link_sta; - struct iwl_mld_link *mld_link; - struct ieee80211_vif *vif; - - vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); - if (!vif) - return; - - link_conf = link_conf_dereference_protected(vif, link_sta->link_id); - if (WARN_ON_ONCE(!link_conf)) - return; - - if (!link_conf->he_support) - return; - - mld_link->rx_omi.exit_ts = jiffies; - - iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, - IEEE80211_STA_RX_BW_MAX, false); -} - -void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld) -{ - enum ieee80211_sta_rx_bandwidth bw = IEEE80211_STA_RX_BW_MAX; - struct ieee80211_chanctx_conf *chanctx; - struct ieee80211_bss_conf *link_conf; - struct ieee80211_link_sta *link_sta; - struct cfg80211_chan_def chandef; - struct iwl_mld_link *mld_link; - struct iwl_mld_vif *mld_vif; - struct ieee80211_vif *vif; - struct iwl_mld_phy *phy; - u16 punctured; - int exit_thr; - - /* not allowed in CAM mode */ - if (iwlmld_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) - return; - - /* must have one BSS connection (no P2P), no TDLS, nor EMLSR */ - vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); - if (!vif) - return; - - link_conf = link_conf_dereference_protected(vif, link_sta->link_id); - if (WARN_ON_ONCE(!link_conf)) - return; - - if (!link_conf->he_support) - return; - - chanctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx); - if (WARN_ON(!chanctx)) - return; - - mld_vif = iwl_mld_vif_from_mac80211(vif); - if (!mld_vif->authorized) - goto apply; - - /* must not be in low-latency mode */ - if (iwl_mld_vif_low_latency(mld_vif)) - goto apply; - - chandef = link_conf->chanreq.oper; - - switch (chandef.width) { - case NL80211_CHAN_WIDTH_320: - exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_320; - break; - case NL80211_CHAN_WIDTH_160: - exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_160; - break; - default: - /* since we reduce to 80 MHz, must have more to start with */ - goto apply; - } - - /* not to be done if primary 80 MHz is punctured */ - if (cfg80211_chandef_primary(&chandef, NL80211_CHAN_WIDTH_80, - &punctured) < 0 || - punctured != 0) - goto apply; - - phy = iwl_mld_phy_from_mac80211(chanctx); - - if (phy->channel_load_by_us > exit_thr) { - /* send OMI for max bandwidth */ - goto apply; - } - - if (phy->channel_load_by_us > IWL_MLD_OMI_ENTER_CHAN_LOAD) { - /* no changes between enter/exit thresholds */ - return; - } - - if (time_is_after_jiffies(mld_link->rx_omi.exit_ts + - msecs_to_jiffies(IWL_MLD_OMI_EXIT_PROTECTION))) - return; - - /* reduce bandwidth to 80 MHz to save power */ - bw = IEEE80211_STA_RX_BW_80; -apply: - iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, false); -} - IWL_MLD_ALLOC_FN(link, bss_conf) /* Constructor function for struct iwl_mld_link */ @@ -777,17 +444,12 @@ static int iwl_mld_init_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link, struct iwl_mld_link *mld_link) { - mld_link->vif = link->vif; - mld_link->link_id = link->link_id; + mld_link->average_beacon_energy = 0; 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->mon_sta); - 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); } @@ -851,8 +513,6 @@ void iwl_mld_remove_link(struct iwl_mld *mld, RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); - wiphy_delayed_work_cancel(mld->wiphy, &link->rx_omi.finished_work); - if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links)) return; @@ -864,21 +524,23 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, { const struct iwl_missed_beacons_notif *notif = (const void *)pkt->data; union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt }; - u32 link_id = le32_to_cpu(notif->link_id); + u32 fw_link_id = le32_to_cpu(notif->link_id); u32 missed_bcon = le32_to_cpu(notif->consec_missed_beacons); u32 missed_bcon_since_rx = le32_to_cpu(notif->consec_missed_beacons_since_last_rx); u32 scnd_lnk_bcn_lost = le32_to_cpu(notif->consec_missed_beacons_other_link); struct ieee80211_bss_conf *link_conf = - iwl_mld_fw_id_to_link_conf(mld, link_id); + iwl_mld_fw_id_to_link_conf(mld, fw_link_id); u32 bss_param_ch_cnt_link_id; struct ieee80211_vif *vif; + u8 link_id; if (WARN_ON(!link_conf)) return; vif = link_conf->vif; + link_id = link_conf->link_id; bss_param_ch_cnt_link_id = link_conf->bss_param_ch_cnt_link_id; IWL_DEBUG_INFO(mld, @@ -890,7 +552,7 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, mld->trans->dbg.dump_file_name_ext_valid = true; snprintf(mld->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, - "LinkId_%d_MacType_%d", link_id, + "LinkId_%d_MacType_%d", fw_link_id, iwl_mld_mac80211_iftype_to_fw(vif)); iwl_dbg_tlv_time_point(&mld->fwrt, @@ -1212,3 +874,22 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, return grade; } EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_link_grade); + +void iwl_mld_handle_beacon_filter_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_beacon_filter_notif *notif = (const void *)pkt->data; + u32 link_id = le32_to_cpu(notif->link_id); + struct ieee80211_bss_conf *link_conf = + iwl_mld_fw_id_to_link_conf(mld, link_id); + struct iwl_mld_link *mld_link; + + if (IWL_FW_CHECK(mld, !link_conf, "invalid link ID %d\n", link_id)) + return; + + mld_link = iwl_mld_link_from_mac80211(link_conf); + if (WARN_ON_ONCE(!mld_link)) + return; + + mld_link->average_beacon_energy = le32_to_cpu(notif->average_energy); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index 39f04aae5579..cad2c9426349 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -36,25 +36,17 @@ struct iwl_probe_resp_data { * @he_ru_2mhz_block: 26-tone RU OFDMA transmissions should be blocked. * @igtk: fw can only have one IGTK at a time, whereas mac80211 can have two. * This tracks the one IGTK that currently exists in FW. - * @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. * @mon_sta: station used for TX injection in monitor interface. - * @link_id: over the air link ID + * @average_beacon_energy: average beacon energy for beacons received during + * client connections * @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs, * but higher layers work differently, so we store the keys here for * later installation. * @silent_deactivation: next deactivation needs to be silent. * @probe_resp_data: data from FW notification to store NOA related data to be * inserted into probe response. - * @rx_omi: data for BW reduction with OMI - * @rx_omi.bw_in_progress: update is in progress (indicates target BW) - * @rx_omi.exit_ts: timestamp of last exit - * @rx_omi.finished_work: work for the delayed reaction to the firmware saying - * the change was applied, and for then applying a new mode if it was - * updated while waiting for firmware/AP settle delay. - * @rx_omi.desired_bw: desired bandwidth - * @rx_omi.last_max_bw: last maximum BW used by firmware, for AP BW changes */ struct iwl_mld_link { struct rcu_head rcu_head; @@ -69,22 +61,13 @@ struct iwl_mld_link { struct ieee80211_key_conf *igtk; ); /* And here fields that survive a fw restart */ - struct ieee80211_vif *vif; struct iwl_mld_int_sta bcast_sta; struct iwl_mld_int_sta mcast_sta; struct iwl_mld_int_sta mon_sta; - u8 link_id; - - struct { - struct wiphy_delayed_work finished_work; - unsigned long exit_ts; - enum ieee80211_sta_rx_bandwidth bw_in_progress, - desired_bw, - last_max_bw; - } rx_omi; /* we can only have 2 GTK + 2 IGTK + 2 BIGTK active at a time */ struct ieee80211_key_conf *ap_early_keys[6]; + u32 average_beacon_energy; bool silent_deactivation; struct iwl_probe_resp_data __rcu *probe_resp_data; }; @@ -120,9 +103,6 @@ int iwl_mld_activate_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link); void iwl_mld_deactivate_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link); -int iwl_mld_change_link_omi_bw(struct iwl_mld *mld, - struct ieee80211_bss_conf *link, - enum ieee80211_sta_rx_bandwidth bw); int iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link, u32 changes); void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, @@ -142,12 +122,8 @@ unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf, bool expect_active_link); -void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld, - struct iwl_rx_packet *pkt); -void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld); -void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld); -void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld, - struct ieee80211_bss_conf *link_conf, - enum ieee80211_sta_rx_bandwidth bw); + +void iwl_mld_handle_beacon_filter_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); #endif /* __iwl_mld_link_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c index f7faa87b8ba6..23362867b400 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c @@ -224,9 +224,6 @@ void iwl_mld_vif_update_low_latency(struct iwl_mld *mld, return; } - if (low_latency) - iwl_mld_leave_omi_bw_reduction(mld); - if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_CLIENT) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 4ba050397632..b0bd01914a91 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -4,6 +4,7 @@ */ #include <net/mac80211.h> +#include <linux/fips.h> #include <linux/ip.h> #include "mld.h" @@ -156,6 +157,9 @@ static void iwl_mld_hw_set_security(struct iwl_mld *mld) WLAN_CIPHER_SUITE_BIP_GMAC_256 }; + if (fips_enabled) + return; + hw->wiphy->n_cipher_suites = ARRAY_SIZE(mld_ciphers); hw->wiphy->cipher_suites = mld_ciphers; @@ -180,6 +184,9 @@ static void iwl_mld_hw_set_pm(struct iwl_mld *mld) if (!device_can_wakeup(mld->trans->dev)) return; + if (fips_enabled) + return; + mld->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | WIPHY_WOWLAN_EAP_IDENTITY_REQ | @@ -284,9 +291,11 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld) WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK; + /* For fips_enabled, don't support WiFi7 due to WPA3/MFP requirements */ if (mld->nvm_data->sku_cap_11be_enable && !iwlwifi_mod_params.disable_11ax && - !iwlwifi_mod_params.disable_11be) + !iwlwifi_mod_params.disable_11be && + !fips_enabled) wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; /* the firmware uses u8 for num of iterations, but 0xff is saved for @@ -508,8 +517,15 @@ int iwl_mld_mac80211_start(struct ieee80211_hw *hw) if (in_d3) { /* mac80211 already cleaned up the state, no need for cleanup */ ret = iwl_mld_no_wowlan_resume(mld); - if (ret) + if (ret) { iwl_mld_stop_fw(mld); + /* We're not really restarting in the sense of + * in_hw_restart even if we got an error during + * this. We'll just start again below and have + * nothing to recover, mac80211 will do anyway. + */ + mld->fw_status.in_hw_restart = false; + } } #endif /* CONFIG_PM_SLEEP */ @@ -574,7 +590,8 @@ void iwl_mld_mac80211_stop(struct ieee80211_hw *hw, bool suspend) } static -int iwl_mld_mac80211_config(struct ieee80211_hw *hw, u32 changed) +int iwl_mld_mac80211_config(struct ieee80211_hw *hw, int radio_idx, + u32 changed) { return 0; } @@ -998,10 +1015,9 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw, if (n_active > 1) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - iwl_mld_leave_omi_bw_reduction(mld); - /* Indicate to mac80211 that EML is enabled */ vif->driver_flags |= IEEE80211_VIF_EML_ACTIVE; + mld_vif->emlsr.last_entry_ts = jiffies; if (vif->active_links & BIT(mld_vif->emlsr.selected_links)) mld_vif->emlsr.primary = mld_vif->emlsr.selected_primary; @@ -1102,7 +1118,8 @@ void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, } static -int iwl_mld_mac80211_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +int iwl_mld_mac80211_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, + u32 value) { return 0; } @@ -1193,20 +1210,6 @@ iwl_mld_mac80211_link_info_changed_sta(struct iwl_mld *mld, if (changes & (BSS_CHANGED_CQM | BSS_CHANGED_BEACON_INFO)) iwl_mld_enable_beacon_filter(mld, link_conf, false); - /* If we have used OMI before to reduce bandwidth to 80 MHz and then - * increased to 160 MHz again, and then the AP changes to 320 MHz, it - * will think that we're limited to 160 MHz right now. Update it by - * requesting a new OMI bandwidth. - */ - if (changes & BSS_CHANGED_BANDWIDTH) { - enum ieee80211_sta_rx_bandwidth bw; - - bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width); - - iwl_mld_omi_ap_changed_bw(mld, link_conf, bw); - - } - if (changes & BSS_CHANGED_BANDWIDTH) iwl_mld_retry_emlsr(mld, vif); } @@ -1410,30 +1413,6 @@ iwl_mld_mac80211_sched_scan_stop(struct ieee80211_hw *hw, } static void -iwl_mld_restart_complete_vif(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct ieee80211_bss_conf *link_conf; - struct iwl_mld *mld = data; - int link_id; - - for_each_vif_active_link(vif, link_conf, link_id) { - enum ieee80211_sta_rx_bandwidth bw; - struct iwl_mld_link *mld_link; - - mld_link = wiphy_dereference(mld->wiphy, - mld_vif->link[link_id]); - - if (WARN_ON_ONCE(!mld_link)) - continue; - - bw = mld_link->rx_omi.bw_in_progress; - if (bw) - iwl_mld_change_link_omi_bw(mld, link_conf, bw); - } -} - -static void iwl_mld_mac80211_reconfig_complete(struct ieee80211_hw *hw, enum ieee80211_reconfig_type reconfig_type) { @@ -1443,11 +1422,6 @@ iwl_mld_mac80211_reconfig_complete(struct ieee80211_hw *hw, case IEEE80211_RECONFIG_TYPE_RESTART: mld->fw_status.in_hw_restart = false; iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_END_OF_RECOVERY); - - ieee80211_iterate_interfaces(mld->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mld_restart_complete_vif, mld); - iwl_trans_finish_sw_reset(mld->trans); /* no need to lock, adding in parallel would schedule too */ if (!list_empty(&mld->txqs_to_add)) @@ -1468,7 +1442,7 @@ void iwl_mld_mac80211_mgd_prepare_tx(struct ieee80211_hw *hw, struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); u32 duration = IWL_MLD_SESSION_PROTECTION_ASSOC_TIME_MS; - /* After a successful association the connection is etalibeshed + /* After a successful association the connection is established * and we can rely on the quota to send the disassociation frame. */ if (info->was_assoc) @@ -1671,18 +1645,6 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, return -EBUSY; } - /* - * If this is the first STA (i.e. the AP) it won't do - * anything, otherwise must leave for any new STA on - * any other interface, or for TDLS, etc. - * Need to call this _before_ adding the STA so it can - * look up the one STA to use to ask mac80211 to leave - * OMI; in the unlikely event that adding the new STA - * then fails we'll just re-enter OMI later (via the - * statistics notification handling.) - */ - iwl_mld_leave_omi_bw_reduction(mld); - ret = iwl_mld_add_sta(mld, sta, vif, STATION_TYPE_PEER); if (ret) return ret; @@ -1908,6 +1870,10 @@ iwl_mld_mac80211_ampdu_action(struct ieee80211_hw *hw, switch (action) { case IEEE80211_AMPDU_RX_START: + if (!iwl_enable_rx_ampdu()) { + ret = -EINVAL; + break; + } ret = iwl_mld_ampdu_rx_start(mld, sta, tid, ssn, buf_size, timeout); break; @@ -2573,28 +2539,6 @@ static int iwl_mld_mac80211_tx_last_beacon(struct ieee80211_hw *hw) return mld->ibss_manager; } -#define IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT (5 * HZ) - -static void iwl_mld_vif_iter_emlsr_block_tmp_non_bss(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - int ret; - - if (!iwl_mld_vif_has_emlsr_cap(vif)) - return; - - ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, - IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS, - iwl_mld_get_primary_link(vif)); - if (ret) - return; - - wiphy_delayed_work_queue(mld_vif->mld->wiphy, - &mld_vif->emlsr.tmp_non_bss_done_wk, - IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT); -} - static void iwl_mld_prep_add_interface(struct ieee80211_hw *hw, enum nl80211_iftype type) { @@ -2607,10 +2551,7 @@ static void iwl_mld_prep_add_interface(struct ieee80211_hw *hw, type == NL80211_IFTYPE_P2P_CLIENT)) return; - ieee80211_iterate_active_interfaces_mtx(mld->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mld_vif_iter_emlsr_block_tmp_non_bss, - NULL); + iwl_mld_emlsr_block_tmp_non_bss(mld); } static int iwl_mld_set_hw_timestamp(struct ieee80211_hw *hw, @@ -2640,6 +2581,23 @@ static int iwl_mld_start_pmsr(struct ieee80211_hw *hw, return iwl_mld_ftm_start(mld, vif, request); } +static enum ieee80211_neg_ttlm_res +iwl_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_neg_ttlm *neg_ttlm) +{ + u16 map; + + /* Verify all TIDs are mapped to the same links set */ + map = neg_ttlm->downlink[0]; + for (int i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) { + if (neg_ttlm->downlink[i] != neg_ttlm->uplink[i] || + neg_ttlm->uplink[i] != map) + return NEG_TTLM_RES_REJECT; + } + + return NEG_TTLM_RES_ACCEPT; +} + const struct ieee80211_ops iwl_mld_hw_ops = { .tx = iwl_mld_mac80211_tx, .start = iwl_mld_mac80211_start, @@ -2669,6 +2627,7 @@ const struct ieee80211_ops iwl_mld_hw_ops = { .mgd_complete_tx = iwl_mld_mac_mgd_complete_tx, .sta_state = iwl_mld_mac80211_sta_state, .sta_statistics = iwl_mld_mac80211_sta_statistics, + .get_survey = iwl_mld_mac80211_get_survey, .flush = iwl_mld_mac80211_flush, .flush_sta = iwl_mld_mac80211_flush_sta, .ampdu_action = iwl_mld_mac80211_ampdu_action, @@ -2709,4 +2668,5 @@ const struct ieee80211_ops iwl_mld_hw_ops = { .prep_add_interface = iwl_mld_prep_add_interface, .set_hw_timestamp = iwl_mld_set_hw_timestamp, .start_pmsr = iwl_mld_start_pmsr, + .can_neg_ttlm = iwl_mld_can_neg_ttlm, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c index 19cb562e7a73..16bb1b4904f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -15,7 +15,7 @@ /* It is the caller's responsibility to free the pointer returned here */ static struct iwl_mcc_update_resp_v8 * -iwl_mld_parse_mcc_update_resp_v8(const struct iwl_rx_packet *pkt) +iwl_mld_copy_mcc_resp(const struct iwl_rx_packet *pkt) { const struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (const void *)pkt->data; int n_channels = __le32_to_cpu(mcc_resp_v8->n_channels); @@ -34,41 +34,9 @@ iwl_mld_parse_mcc_update_resp_v8(const struct iwl_rx_packet *pkt) /* It is the caller's responsibility to free the pointer returned here */ static struct iwl_mcc_update_resp_v8 * -iwl_mld_parse_mcc_update_resp_v5_v6(const struct iwl_rx_packet *pkt) -{ - const struct iwl_mcc_update_resp_v4 *mcc_resp_v4 = (const void *)pkt->data; - struct iwl_mcc_update_resp_v8 *resp_cp; - int n_channels = __le32_to_cpu(mcc_resp_v4->n_channels); - int resp_len; - - if (iwl_rx_packet_payload_len(pkt) != - struct_size(mcc_resp_v4, channels, n_channels)) - return ERR_PTR(-EINVAL); - - resp_len = struct_size(resp_cp, channels, n_channels); - resp_cp = kzalloc(resp_len, GFP_KERNEL); - if (!resp_cp) - return ERR_PTR(-ENOMEM); - - resp_cp->status = mcc_resp_v4->status; - resp_cp->mcc = mcc_resp_v4->mcc; - resp_cp->cap = cpu_to_le32(le16_to_cpu(mcc_resp_v4->cap)); - resp_cp->source_id = mcc_resp_v4->source_id; - resp_cp->geo_info = mcc_resp_v4->geo_info; - resp_cp->n_channels = mcc_resp_v4->n_channels; - memcpy(resp_cp->channels, mcc_resp_v4->channels, - n_channels * sizeof(__le32)); - - return resp_cp; -} - -/* It is the caller's responsibility to free the pointer returned here */ -static struct iwl_mcc_update_resp_v8 * iwl_mld_update_mcc(struct iwl_mld *mld, const char *alpha2, enum iwl_mcc_source src_id) { - int resp_ver = iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, - MCC_UPDATE_CMD, 0); struct iwl_mcc_update_cmd mcc_update_cmd = { .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]), .source_id = (u8)src_id, @@ -93,23 +61,7 @@ iwl_mld_update_mcc(struct iwl_mld *mld, const char *alpha2, pkt = cmd.resp_pkt; - /* For Wifi-7 radios, we get version 8 - * For Wifi-6E radios, we get version 6 - * For Wifi-6 radios, we get version 5, but 5, 6, and 4 are compatible. - */ - switch (resp_ver) { - case 5: - case 6: - resp_cp = iwl_mld_parse_mcc_update_resp_v5_v6(pkt); - break; - case 8: - resp_cp = iwl_mld_parse_mcc_update_resp_v8(pkt); - break; - default: - IWL_FW_CHECK_FAILED(mld, "Unknown MCC_UPDATE_CMD version %d\n", resp_ver); - resp_cp = ERR_PTR(-EINVAL); - } - + resp_cp = iwl_mld_copy_mcc_resp(pkt); if (IS_ERR(resp_cp)) goto exit; @@ -177,11 +129,15 @@ iwl_mld_get_regdomain(struct iwl_mld *mld, mld->mcc_src = resp->source_id; - if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing, - le16_to_cpu(resp->mcc))) - ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING); - else - __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, mld->hw->flags); + /* FM is the earliest supported and later always do puncturing */ + if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM) { + if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing, + le16_to_cpu(resp->mcc))) + ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING); + else + __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, + mld->hw->flags); + } out: kfree(resp); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 1774bb84dd3f..7b46ccc306ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -251,16 +251,23 @@ static const struct iwl_hcmd_names iwl_mld_data_path_names[] = { HCMD_NAME(TLC_MNG_CONFIG_CMD), HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD), HCMD_NAME(SCD_QUEUE_CONFIG_CMD), - HCMD_NAME(OMI_SEND_STATUS_NOTIF), HCMD_NAME(ESR_MODE_NOTIF), HCMD_NAME(MONITOR_NOTIF), HCMD_NAME(TLC_MNG_UPDATE_NOTIF), + HCMD_NAME(BEACON_FILTER_IN_NOTIF), HCMD_NAME(MU_GROUP_MGMT_NOTIF), }; /* Please keep this array *SORTED* by hex value. * Access is done through binary search */ +static const struct iwl_hcmd_names iwl_mld_scan_names[] = { + HCMD_NAME(CHANNEL_SURVEY_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ static const struct iwl_hcmd_names iwl_mld_location_names[] = { HCMD_NAME(TOF_RANGE_REQ_CMD), HCMD_NAME(TOF_RANGE_RESPONSE_NOTIF), @@ -309,6 +316,7 @@ const struct iwl_hcmd_arr iwl_mld_groups[] = { [SYSTEM_GROUP] = HCMD_ARR(iwl_mld_system_names), [MAC_CONF_GROUP] = HCMD_ARR(iwl_mld_mac_conf_names), [DATA_PATH_GROUP] = HCMD_ARR(iwl_mld_data_path_names), + [SCAN_GROUP] = HCMD_ARR(iwl_mld_scan_names), [LOCATION_GROUP] = HCMD_ARR(iwl_mld_location_names), [REGULATORY_AND_NVM_GROUP] = HCMD_ARR(iwl_mld_reg_and_nvm_names), [DEBUG_GROUP] = HCMD_ARR(iwl_mld_debug_names), @@ -357,7 +365,7 @@ iwl_mld_configure_trans(struct iwl_op_mode *op_mode) 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.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_desc); trans->conf.wide_cmd_header = true; iwl_trans_op_mode_enter(trans, op_mode); @@ -506,6 +514,7 @@ iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode) kfree(mld->nvm_data); kfree(mld->scan.cmd); + kfree(mld->channel_survey); kfree(mld->error_recovery_buf); kfree(mld->mcast_filter_cmd); @@ -630,7 +639,7 @@ iwl_mld_nic_error(struct iwl_op_mode *op_mode, enum iwl_fw_error_type type) { struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); - bool trans_dead = test_bit(STATUS_TRANS_DEAD, &mld->trans->status); + bool trans_dead = iwl_trans_is_dead(mld->trans); if (type == IWL_ERR_TYPE_CMD_QUEUE_FULL) IWL_ERR(mld, "Command queue full!\n"); @@ -725,6 +734,17 @@ static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) {} #endif +static void iwl_mld_dump(struct iwl_op_mode *op_mode) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + struct iwl_fw_runtime *fwrt = &mld->fwrt; + + if (!iwl_trans_fw_running(fwrt->trans)) + return; + + iwl_dbg_tlv_time_point(fwrt, IWL_FW_INI_TIME_POINT_USER_TRIGGER, NULL); +} + static const struct iwl_op_mode_ops iwl_mld_ops = { .start = iwl_op_mode_mld_start, .stop = iwl_op_mode_mld_stop, @@ -739,6 +759,7 @@ static const struct iwl_op_mode_ops iwl_mld_ops = { .sw_reset = iwl_mld_sw_reset, .time_point = iwl_mld_time_point, .device_powered_off = pm_sleep_ptr(iwl_mld_device_powered_off), + .dump = iwl_mld_dump, }; struct iwl_mld_mod_params iwlmld_mod_params = { diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h index 1a2c44f44eff..94dc9da6360d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -127,6 +127,7 @@ * 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 @@ -149,6 +150,7 @@ * @running: true if the firmware is running * @do_not_dump_once: true if firmware dump must be prevented once * @in_d3: indicates FW is in suspend mode and should be resumed + * @resuming: indicates the driver is resuming from wowlan * @in_hw_restart: indicates that we are currently in restart flow. * rather than restarted. Should be unset upon restart. * @radio_kill: bitmap of radio kill status @@ -158,7 +160,9 @@ * device * @addresses: device MAC addresses. * @scan: instance of the scan object + * @channel_survey: channel survey information collected during scan * @wowlan: WoWLAN support data. + * @debug_max_sleep: maximum sleep time in D3 (for debug purposes) * @led: the led device * @mcc_src: the source id of the MCC, comes from the firmware * @bios_enable_puncturing: is puncturing enabled by bios @@ -187,7 +191,6 @@ * @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 */ @@ -212,7 +215,7 @@ struct iwl_mld { bool netdetect; #endif /* CONFIG_PM_SLEEP */ struct ieee80211_vif *p2p_device_vif; - struct iwl_bt_coex_profile_notif last_bt_notif; + bool bt_is_active; ); struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX]; /* And here fields that survive a fw restart */ @@ -236,6 +239,7 @@ struct iwl_mld { do_not_dump_once:1, #ifdef CONFIG_PM_SLEEP in_d3:1, + resuming:1, #endif in_hw_restart:1; @@ -250,8 +254,10 @@ struct iwl_mld { struct mac_address addresses[IWL_MLD_MAX_ADDRESSES]; struct iwl_mld_scan scan; + struct iwl_mld_survey *channel_survey; #ifdef CONFIG_PM_SLEEP struct wiphy_wowlan_support wowlan; + u32 debug_max_sleep; #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index dba5379ed009..e57f5388fe77 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -287,6 +287,36 @@ int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif, return _iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, true); } +#define IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT (10 * HZ) + +static void iwl_mld_vif_iter_emlsr_block_tmp_non_bss(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + + ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS, + iwl_mld_get_primary_link(vif)); + if (ret) + return; + + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.tmp_non_bss_done_wk, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT); +} + +void iwl_mld_emlsr_block_tmp_non_bss(struct iwl_mld *mld) +{ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_block_tmp_non_bss, + NULL); +} + static void _iwl_mld_select_links(struct iwl_mld *mld, struct ieee80211_vif *vif); @@ -530,10 +560,12 @@ void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk) /* * TPT is unblocked, need to check if the TPT criteria is still met. * - * If EMLSR is active, then we also need to check the secondar link - * requirements. + * If EMLSR is active for at least 5 seconds, then we also + * need to check the secondary link requirements. */ - if (iwl_mld_emlsr_active(vif)) { + if (iwl_mld_emlsr_active(vif) && + time_is_before_jiffies(mld_vif->emlsr.last_entry_ts + + IWL_MLD_TPT_COUNT_WINDOW)) { sec_link_id = iwl_mld_get_other_link(vif, iwl_mld_get_primary_link(vif)); sec_link = iwl_mld_link_dereference_check(mld_vif, sec_link_id); if (WARN_ON_ONCE(!sec_link)) @@ -657,42 +689,6 @@ 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, @@ -707,8 +703,7 @@ iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld, if (WARN_ON_ONCE(!conf)) return IWL_MLD_EMLSR_EXIT_INVALID; - if (link->chandef->chan->band == NL80211_BAND_2GHZ && - !iwl_mld_bt_allows_emlsr(mld, conf, true)) + if (link->chandef->chan->band == NL80211_BAND_2GHZ && mld->bt_is_active) ret |= IWL_MLD_EMLSR_EXIT_BT_COEX; if (link->signal < @@ -1046,41 +1041,30 @@ 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 (!iwl_mld_vif_has_emlsr_cap(vif)) return; - /* zeroed structure means that BT is OFF */ - if (!memcmp(notif, &zero_notif, sizeof(*notif))) { + if (!mld->bt_is_active) { iwl_mld_retry_emlsr(mld, vif); return; } - for_each_vif_active_link(vif, link, link_id) { - bool emlsr_active, emlsr_allowed; + /* BT is turned ON but we are not in EMLSR, nothing to do */ + if (!iwl_mld_emlsr_active(vif)) + return; - if (WARN_ON(!link->chanreq.oper.chan)) - continue; + /* In EMLSR and BT is turned ON */ - if (link->chanreq.oper.chan->band != NL80211_BAND_2GHZ) + for_each_vif_active_link(vif, link, link_id) { + if (WARN_ON(!link->chanreq.oper.chan)) 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, + if (link->chanreq.oper.chan->band == NL80211_BAND_2GHZ) { + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BT_COEX, iwl_mld_get_primary_link(vif)); return; } @@ -1167,8 +1151,8 @@ void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - if (!iwl_mld_vif_has_emlsr_cap(vif) || iwl_mld_emlsr_active(vif) || - mld_vif->emlsr.blocked_reasons) + if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif) || + iwl_mld_emlsr_active(vif) || mld_vif->emlsr.blocked_reasons) return; iwl_mld_int_mlo_scan(mld, vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h index 9afa3d6ea649..d936589fe39d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h @@ -157,14 +157,12 @@ struct iwl_mld_link_sel_data { u16 grade; }; +void iwl_mld_emlsr_block_tmp_non_bss(struct iwl_mld *mld); + #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) 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); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index c0e62d46aba6..f17aeca4fae6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -78,13 +78,6 @@ static bool iwl_mld_cancel_##name##_notif(struct iwl_mld *mld, \ u8: (notif)->id_member); \ } -static bool iwl_mld_always_cancel(struct iwl_mld *mld, - struct iwl_rx_packet *pkt, - u32 obj_id) -{ - return true; -} - /* Currently only defined for the RX_HANDLER_SIZES options. Use this for * notifications that belong to a specific object, and that should be * canceled when the object is removed @@ -295,6 +288,8 @@ CMD_VERSIONS(scan_complete_notif, CMD_VER_ENTRY(1, iwl_umac_scan_complete)) CMD_VERSIONS(scan_iter_complete_notif, CMD_VER_ENTRY(2, iwl_umac_scan_iter_complete_notif)) +CMD_VERSIONS(channel_survey_notif, + CMD_VER_ENTRY(1, iwl_umac_scan_channel_survey_notif)) CMD_VERSIONS(mfuart_notif, CMD_VER_ENTRY(2, iwl_mfuart_load_notif)) CMD_VERSIONS(update_mcc, @@ -348,9 +343,8 @@ CMD_VERSIONS(time_msmt_notif, CMD_VER_ENTRY(1, iwl_time_msmt_notify)) CMD_VERSIONS(time_sync_confirm_notif, CMD_VER_ENTRY(1, iwl_time_msmt_cfm_notify)) -CMD_VERSIONS(omi_status_notif, - CMD_VER_ENTRY(1, iwl_omi_send_status_notif)) -CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(9, iwl_tof_range_rsp_ntfy)) +CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(10, iwl_tof_range_rsp_ntfy)) +CMD_VERSIONS(beacon_filter_notif, CMD_VER_ENTRY(2, iwl_beacon_filter_notif)) DEFINE_SIMPLE_CANCELLATION(session_prot, iwl_session_prot_notif, mac_link_id) DEFINE_SIMPLE_CANCELLATION(tlc, iwl_tlc_update_notif, sta_id) @@ -366,8 +360,8 @@ DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif, mac_id) DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif, mac_id) -#define iwl_mld_cancel_omi_status_notif iwl_mld_always_cancel DEFINE_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id) +DEFINE_SIMPLE_CANCELLATION(beacon_filter, iwl_beacon_filter_notif, link_id) /** * DOC: Handlers for fw notifications @@ -412,6 +406,10 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = { RX_HANDLER_NO_VAL(LEGACY_GROUP, MATCH_FOUND_NOTIFICATION, match_found_notif, RX_HANDLER_SYNC) + RX_HANDLER_NO_OBJECT(SCAN_GROUP, CHANNEL_SURVEY_NOTIF, + channel_survey_notif, + RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_NOTIF, stats_oper_notif, RX_HANDLER_ASYNC) RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF, @@ -458,8 +456,8 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = { RX_HANDLER_NO_OBJECT(LEGACY_GROUP, WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION, time_sync_confirm_notif, RX_HANDLER_ASYNC) - RX_HANDLER_OF_LINK(DATA_PATH_GROUP, OMI_SEND_STATUS_NOTIF, - omi_status_notif) + RX_HANDLER_OF_LINK(DATA_PATH_GROUP, BEACON_FILTER_IN_NOTIF, + beacon_filter_notif) RX_HANDLER_OF_FTM_REQ(LOCATION_GROUP, TOF_RANGE_RESPONSE_NOTIF, ftm_resp_notif) }; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.c b/drivers/net/wireless/intel/iwlwifi/mld/phy.c index d5a32ee56b92..1d93fb9e4dbf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.c @@ -181,7 +181,7 @@ int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld) .phy_specific_cfg = mld->fwrt.phy_filters, }; - IWL_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg); + IWL_DEBUG_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg); return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c index 8cc276041360..f664b277adf7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -377,23 +377,15 @@ int iwl_mld_set_tx_power(struct iwl_mld *mld, u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ? IWL_DEV_MAX_TX_POWER : 8 * tx_power; struct iwl_dev_tx_power_cmd cmd = { - /* Those fields sit on the same place for v9 and v10 */ .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK), .common.pwr_restriction = cpu_to_le16(u_tx_power), }; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, - IWL_FW_CMD_VER_UNKNOWN); - int len = sizeof(cmd.common); + int len = sizeof(cmd.common) + sizeof(cmd.v10); if (WARN_ON(!mld_link)) return -ENODEV; cmd.common.link_id = cpu_to_le32(mld_link->fw_id); - if (cmd_ver == 10) - len += sizeof(cmd.v10); - else if (cmd_ver == 9) - len += sizeof(cmd.v9); - return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c index 5ee38fc168c1..ffeb37a7f830 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -299,18 +299,18 @@ void iwl_mld_ptp_init(struct iwl_mld *mld) PTR_ERR(mld->ptp_data.ptp_clock)); mld->ptp_data.ptp_clock = NULL; } else { - IWL_INFO(mld, "Registered PHC clock: %s, with index: %d\n", - mld->ptp_data.ptp_clock_info.name, - ptp_clock_index(mld->ptp_data.ptp_clock)); + IWL_DEBUG_INFO(mld, "Registered PHC clock: %s, with index: %d\n", + mld->ptp_data.ptp_clock_info.name, + ptp_clock_index(mld->ptp_data.ptp_clock)); } } void iwl_mld_ptp_remove(struct iwl_mld *mld) { if (mld->ptp_data.ptp_clock) { - IWL_INFO(mld, "Unregistering PHC clock: %s, with index: %d\n", - mld->ptp_data.ptp_clock_info.name, - ptp_clock_index(mld->ptp_data.ptp_clock)); + IWL_DEBUG_INFO(mld, "Unregistering PHC clock: %s, with index: %d\n", + mld->ptp_data.ptp_clock_info.name, + ptp_clock_index(mld->ptp_data.ptp_clock)); ptp_clock_unregister(mld->ptp_data.ptp_clock); mld->ptp_data.ptp_clock = NULL; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index 326c300470ea..75d2f5cb23a7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -71,40 +71,17 @@ void iwl_mld_get_bios_tables(struct iwl_mld *mld) static int iwl_mld_geo_sar_init(struct iwl_mld *mld) { u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD); - union iwl_geo_tx_power_profiles_cmd cmd; - u16 len; - u32 n_bands; - __le32 sk = cpu_to_le32(0); - int ret; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, - IWL_FW_CMD_VER_UNKNOWN); - - BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, ops) != - offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, ops)); - - cmd.v4.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES); - /* Only set to South Korea if the table revision is 1 */ - if (mld->fwrt.geo_rev == 1) - sk = cpu_to_le32(1); - - if (cmd_ver == 5) { - len = sizeof(cmd.v5); - n_bands = ARRAY_SIZE(cmd.v5.table[0]); - cmd.v5.table_revision = sk; - } else if (cmd_ver == 4) { - len = sizeof(cmd.v4); - n_bands = ARRAY_SIZE(cmd.v4.table[0]); - cmd.v4.table_revision = sk; - } else { - return -EOPNOTSUPP; - } + __le32 sk = cpu_to_le32(mld->fwrt.geo_rev == 1 ? 1 : 0); + union iwl_geo_tx_power_profiles_cmd cmd = { + .v5.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES), + .v5.table_revision = sk, + }; + int ret; - BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, table) != - offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, table)); - /* the table is at the same position for all versions, so set use v4 */ - ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v4.table[0][0], - n_bands, BIOS_GEO_MAX_PROFILE_NUM); + ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v5.table[0][0], + ARRAY_SIZE(cmd.v5.table[0]), + BIOS_GEO_MAX_PROFILE_NUM); /* It is a valid scenario to not support SAR, or miss wgds table, * but in that case there is no need to send the command. @@ -112,7 +89,7 @@ static int iwl_mld_geo_sar_init(struct iwl_mld *mld) if (ret) return 0; - return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len); + return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, sizeof(cmd.v5)); } int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b) @@ -120,37 +97,20 @@ int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b) u32 cmd_id = REDUCE_TX_POWER_CMD; struct iwl_dev_tx_power_cmd cmd = { .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), + .v10.flags = cpu_to_le32(mld->fwrt.reduced_power_flags), }; - __le16 *per_chain; int ret; - u16 len = sizeof(cmd.common); - u32 n_subbands; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, - IWL_FW_CMD_VER_UNKNOWN); - - if (cmd_ver == 10) { - len += sizeof(cmd.v10); - n_subbands = IWL_NUM_SUB_BANDS_V2; - per_chain = &cmd.v10.per_chain[0][0][0]; - cmd.v10.flags = - cpu_to_le32(mld->fwrt.reduced_power_flags); - } else if (cmd_ver == 9) { - len += sizeof(cmd.v9); - n_subbands = IWL_NUM_SUB_BANDS_V1; - per_chain = &cmd.v9.per_chain[0][0]; - } else { - return -EOPNOTSUPP; - } /* TODO: CDB - support IWL_NUM_CHAIN_TABLES_V2 */ - ret = iwl_sar_fill_profile(&mld->fwrt, per_chain, - IWL_NUM_CHAIN_TABLES, - n_subbands, prof_a, prof_b); + ret = iwl_sar_fill_profile(&mld->fwrt, &cmd.v10.per_chain[0][0][0], + IWL_NUM_CHAIN_TABLES, IWL_NUM_SUB_BANDS_V2, + prof_a, prof_b); /* return on error or if the profile is disabled (positive number) */ if (ret) return ret; - return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len); + return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, + sizeof(cmd.common) + sizeof(cmd.v10)); } int iwl_mld_init_sar(struct iwl_mld *mld) @@ -238,34 +198,50 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) struct iwl_lari_config_change_cmd cmd = { .config_bitmap = iwl_get_lari_config_bitmap(fwrt), }; + bool has_raw_dsm_capa = fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE); int ret; u32 value; ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_11AX_ENABLEMENT, &value); - if (!ret) + if (!ret) { + if (!has_raw_dsm_capa) + value &= DSM_11AX_ALLOW_BITMAP; cmd.oem_11ax_allow_bitmap = cpu_to_le32(value); + } ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value); - if (!ret) - cmd.oem_unii4_allow_bitmap = - cpu_to_le32(value &= DSM_UNII4_ALLOW_BITMAP); + if (!ret) { + if (!has_raw_dsm_capa) + value &= DSM_UNII4_ALLOW_BITMAP; + cmd.oem_unii4_allow_bitmap = cpu_to_le32(value); + } ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value); - if (!ret) + if (!ret) { + if (!has_raw_dsm_capa) + value &= CHAN_STATE_ACTIVE_BITMAP_CMD_V12; cmd.chan_state_active_bitmap = cpu_to_le32(value); + } ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_6E, &value); if (!ret) cmd.oem_uhb_allow_bitmap = cpu_to_le32(value); ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value); - if (!ret) + if (!ret) { + if (!has_raw_dsm_capa) + value &= DSM_FORCE_DISABLE_CHANNELS_ALLOWED_BITMAP; cmd.force_disable_channels_bitmap = cpu_to_le32(value); + } ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD, &value); - if (!ret) + if (!ret) { + if (!has_raw_dsm_capa) + value &= DSM_EDT_ALLOWED_BITMAP; cmd.edt_bitmap = cpu_to_le32(value); + } ret = iwl_bios_get_wbem(fwrt, &value); if (!ret) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c index ce0093d5c638..b6dedd1ecd4d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -143,7 +143,55 @@ void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, } EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211); -static void iwl_mld_fill_signal(struct iwl_mld *mld, +static bool iwl_mld_used_average_energy(struct iwl_mld *mld, int link_id, + struct ieee80211_hdr *hdr, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_bss_conf *link_conf; + struct iwl_mld_link *mld_link; + + if (unlikely(!hdr || link_id < 0)) + return false; + + if (likely(!ieee80211_is_beacon(hdr->frame_control))) + return false; + + /* + * if link ID is >= valid ones then that means the RX + * was on the AUX link and no correction is needed + */ + if (link_id >= mld->fw->ucode_capa.num_links) + return false; + + /* for the link conf lookup */ + guard(rcu)(); + + link_conf = rcu_dereference(mld->fw_id_to_bss_conf[link_id]); + if (!link_conf) + return false; + + mld_link = iwl_mld_link_from_mac80211(link_conf); + if (!mld_link) + return false; + + /* + * If we know the link by link ID then the frame was + * received for the link, so by filtering it means it + * was from the AP the link is connected to. + */ + + /* skip also in case we don't have it (yet) */ + if (!mld_link->average_beacon_energy) + return false; + + IWL_DEBUG_STATS(mld, "energy override by average %d\n", + mld_link->average_beacon_energy); + rx_status->signal = -mld_link->average_beacon_energy; + return true; +} + +static void iwl_mld_fill_signal(struct iwl_mld *mld, int link_id, + struct ieee80211_hdr *hdr, struct ieee80211_rx_status *rx_status, struct iwl_mld_rx_phy_data *phy_data) { @@ -159,9 +207,11 @@ static void iwl_mld_fill_signal(struct iwl_mld *mld, IWL_DEBUG_STATS(mld, "energy in A %d B %d, and max %d\n", energy_a, energy_b, max_energy); + if (iwl_mld_used_average_energy(mld, link_id, hdr, rx_status)) + return; + rx_status->signal = max_energy; - rx_status->chains = - (rate_n_flags & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS; + rx_status->chains = u32_get_bits(rate_n_flags, RATE_MCS_ANT_AB_MSK); rx_status->chain_signal[0] = energy_a; rx_status->chain_signal[1] = energy_b; } @@ -1039,6 +1089,15 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb, rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; } + /* update aggregation data for monitor sake on default queue */ + if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) && + (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) { + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->data0 & + cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) iwl_mld_decode_eht_phy_data(mld, phy_data, rx_status, eht, usig); @@ -1160,7 +1219,10 @@ static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld, } #endif -static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, +/* Note: hdr can be NULL */ +static void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id, + struct ieee80211_hdr *hdr, + struct sk_buff *skb, struct iwl_mld_rx_phy_data *phy_data, int queue) { @@ -1182,7 +1244,7 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, phy_data->phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE) rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; - iwl_mld_fill_signal(mld, rx_status, phy_data); + iwl_mld_fill_signal(mld, link_id, hdr, rx_status, phy_data); /* This may be overridden by iwl_mld_rx_he() to HE_RU */ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { @@ -1733,7 +1795,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, band; + u8 crypto_len = 0, band, link_id; u32 pkt_len = iwl_rx_packet_payload_len(pkt); u32 mpdu_len; enum iwl_mld_reorder_result reorder_res; @@ -1822,7 +1884,10 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, SCHED_SCAN_PASS_ALL_STATE_FOUND; } - iwl_mld_rx_fill_status(mld, skb, &phy_data, queue); + link_id = u8_get_bits(mpdu_desc->mac_phy_band, + IWL_RX_MPDU_MAC_PHY_BAND_LINK_MASK); + + iwl_mld_rx_fill_status(mld, link_id, hdr, 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)) @@ -2035,7 +2100,8 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, rx_status->freq = ieee80211_channel_to_frequency(channel, rx_status->band); - iwl_mld_rx_fill_status(mld, skb, &phy_data, queue); + /* link ID is ignored for NULL header */ + iwl_mld_rx_fill_status(mld, -1, NULL, 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 diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index 3fce7cd2d512..62f97a18a16c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -4,6 +4,8 @@ */ #include <linux/crc32.h> +#include "iwl-utils.h" + #include "mld.h" #include "scan.h" #include "hcmd.h" @@ -359,7 +361,7 @@ iwl_mld_scan_fits(struct iwl_mld *mld, int n_ssids, struct ieee80211_scan_ies *ies, int n_channels) { return ((n_ssids <= PROBE_OPTION_MAX) && - (n_channels <= mld->fw->ucode_capa.n_scan_channels) & + (n_channels <= mld->fw->ucode_capa.n_scan_channels) && (ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] + ies->len[NL80211_BAND_5GHZ] + ies->len[NL80211_BAND_6GHZ] <= iwl_mld_scan_max_template_size())); @@ -482,7 +484,9 @@ iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld, static u8 iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld, struct iwl_mld_scan_params *params, - struct ieee80211_vif *vif, u16 gen_flags) + struct ieee80211_vif *vif, + enum iwl_mld_scan_status scan_status, + u16 gen_flags) { u8 flags = 0; @@ -494,6 +498,17 @@ iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld, if (params->scan_6ghz) flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT; + /* For AP interfaces, request survey data for regular scans and if + * it is supported. For non-AP interfaces, EBS will be enabled and + * the results may be missing information for some channels. + */ + if (scan_status == IWL_MLD_SCAN_REGULAR && + ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP && + gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE && + iwl_fw_lookup_notif_ver(mld->fw, SCAN_GROUP, + CHANNEL_SURVEY_NOTIF, 0) >= 1) + flags |= IWL_UMAC_SCAN_GEN_FLAGS2_COLLECT_CHANNEL_STATS; + return flags; } @@ -544,6 +559,7 @@ iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld, u16 gen_flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif, scan_status); u8 gen_flags2 = iwl_mld_scan_get_cmd_gen_flags2(mld, params, vif, + scan_status, gen_flags); IWL_DEBUG_SCAN(mld, "General: flags=0x%x, flags2=0x%x\n", @@ -1752,6 +1768,15 @@ int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif, struct cfg80211_scan_request *req, struct ieee80211_scan_ies *ies) { + /* Clear survey data when starting the first part of a regular scan */ + if (req->first_part && mld->channel_survey) + memset(mld->channel_survey->channels, 0, + sizeof(mld->channel_survey->channels[0]) * + mld->channel_survey->n_channels); + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + iwl_mld_emlsr_block_tmp_non_bss(mld); + return _iwl_mld_single_scan_start(mld, vif, req, ies, IWL_MLD_SCAN_REGULAR); } @@ -1800,17 +1825,20 @@ static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld, IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret); } +#define IWL_MLD_MLO_SCAN_BLOCKOUT_TIME 5 /* seconds */ + void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif) { struct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS]; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); unsigned long usable_links = ieee80211_vif_usable_links(vif); size_t n_channels = 0; u8 link_id; lockdep_assert_wiphy(mld->wiphy); - if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif) || - hweight16(vif->valid_links) == 1) + if (!IWL_MLD_AUTO_EML_ENABLE || !vif->cfg.assoc || + !ieee80211_vif_is_mld(vif) || hweight16(vif->valid_links) == 1) return; if (mld->scan.status & IWL_MLD_SCAN_INT_MLO) { @@ -1818,6 +1846,15 @@ void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif) return; } + if (mld_vif->last_link_activation_time > ktime_get_boottime_seconds() - + IWL_MLD_MLO_SCAN_BLOCKOUT_TIME) { + /* timing doesn't matter much, so use the blockout time */ + wiphy_delayed_work_queue(mld->wiphy, + &mld_vif->mlo_scan_start_wk, + IWL_MLD_MLO_SCAN_BLOCKOUT_TIME); + return; + } + for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_bss_conf *link_conf = link_conf_dereference_check(vif, link_id); @@ -2009,3 +2046,136 @@ int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld) return 0; } + +static int iwl_mld_chanidx_from_phy(struct iwl_mld *mld, + enum nl80211_band band, + u16 phy_chan_num) +{ + struct ieee80211_supported_band *sband = mld->wiphy->bands[band]; + + if (WARN_ON_ONCE(!sband)) + return -EINVAL; + + for (int chan_idx = 0; chan_idx < sband->n_channels; chan_idx++) { + struct ieee80211_channel *channel = &sband->channels[chan_idx]; + + if (channel->hw_value == phy_chan_num) + return chan_idx; + } + + return -EINVAL; +} + +void iwl_mld_handle_channel_survey_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_umac_scan_channel_survey_notif *notif = + (void *)pkt->data; + struct iwl_mld_survey_channel *info; + enum nl80211_band band; + int chan_idx; + + if (!mld->channel_survey) { + size_t n_channels = 0; + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (!mld->wiphy->bands[band]) + continue; + + n_channels += mld->wiphy->bands[band]->n_channels; + } + + mld->channel_survey = kzalloc(struct_size(mld->channel_survey, + channels, n_channels), + GFP_KERNEL); + + if (!mld->channel_survey) + return; + + mld->channel_survey->n_channels = n_channels; + n_channels = 0; + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (!mld->wiphy->bands[band]) + continue; + + mld->channel_survey->bands[band] = + &mld->channel_survey->channels[n_channels]; + n_channels += mld->wiphy->bands[band]->n_channels; + } + } + + band = iwl_mld_phy_band_to_nl80211(le32_to_cpu(notif->band)); + chan_idx = iwl_mld_chanidx_from_phy(mld, band, + le32_to_cpu(notif->channel)); + if (WARN_ON_ONCE(chan_idx < 0)) + return; + + IWL_DEBUG_SCAN(mld, "channel survey received for freq %d\n", + mld->wiphy->bands[band]->channels[chan_idx].center_freq); + + info = &mld->channel_survey->bands[band][chan_idx]; + + /* Times are all in ms */ + info->time = le32_to_cpu(notif->active_time); + info->time_busy = le32_to_cpu(notif->busy_time); + info->noise = + iwl_average_neg_dbm(notif->noise, ARRAY_SIZE(notif->noise)); +} + +int iwl_mld_mac80211_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int curr_idx = 0; + + if (!mld->channel_survey) + return -ENOENT; + + /* Iterate bands/channels to find the requested index. + * Logically this returns the entry with index "idx" from a flattened + * survey result array that only contains channels with information. + * The current index into this flattened array is tracked in curr_idx. + */ + for (enum nl80211_band band = 0; band < NUM_NL80211_BANDS; band++) { + struct ieee80211_supported_band *sband = + mld->wiphy->bands[band]; + + if (!sband) + continue; + + for (int per_band_idx = 0; + per_band_idx < sband->n_channels; + per_band_idx++) { + struct iwl_mld_survey_channel *info = + &mld->channel_survey->bands[band][per_band_idx]; + + /* Skip entry entirely, it was not reported/scanned, + * do not increase curr_idx for this entry. + */ + if (!info->time) + continue; + + /* Search did not reach the requested entry yet, + * increment curr_idx and continue. + */ + if (idx != curr_idx) { + curr_idx++; + continue; + } + + /* Found (the next) channel to report */ + survey->channel = &sband->channels[per_band_idx]; + survey->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; + survey->time = info->time; + survey->time_busy = info->time_busy; + survey->noise = info->noise; + if (survey->noise < 0) + survey->filled |= SURVEY_INFO_NOISE_DBM; + + return 0; + } + } + + return -ENOENT; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h index 3ae940d55065..69110f0cfc8e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h @@ -30,6 +30,12 @@ void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); +int iwl_mld_mac80211_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey); + +void iwl_mld_handle_channel_survey_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + #define WFA_TPC_IE_LEN 9 static inline int iwl_mld_scan_max_template_size(void) @@ -130,7 +136,38 @@ struct iwl_mld_scan { void *cmd; unsigned long last_6ghz_passive_jiffies; unsigned long last_start_time_jiffies; - unsigned long last_mlo_scan_time; + u64 last_mlo_scan_time; +}; + +/** + * struct iwl_mld_survey_channel - per-channel survey information + * + * Driver version of &struct survey_info with just the data we want to report. + * + * @time: time in ms the radio was on the channel + * @time_busy: time in ms the channel was sensed busy + * @noise: channel noise in dBm + */ +struct iwl_mld_survey_channel { + u32 time; + u32 time_busy; + s8 noise; +}; + +/** + * struct iwl_mld_survey - survey information + * + * Survey information for all available channels. + * + * @bands: per-band array for per-channel survey data, points into @channels + * @n_channels: Number of @channels entries that are allocated + * @channels: per-channel information + */ +struct iwl_mld_survey { + struct iwl_mld_survey_channel *bands[NUM_NL80211_BANDS]; + + int n_channels; + struct iwl_mld_survey_channel channels[] __counted_by(n_channels); }; #endif /* __iwl_mld_scan_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index f633cb1cf510..cbc64db5eab6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -508,8 +508,6 @@ void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld, iwl_mld_process_per_link_stats(mld, stats->per_link, curr_ts_usec); iwl_mld_process_per_sta_stats(mld, stats->per_sta); iwl_mld_process_per_phy_stats(mld, stats->per_phy); - - iwl_mld_check_omi_bw_reduction(mld); } void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile index 3e2ae6020613..36317feb923b 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 emlsr_with_bt.o +iwlmld-tests-y += module.o hcmd.o utils.o link.o rx.o agg.o link-selection.o ccflags-y += -I$(src)/../ obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmld-tests.o 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 deleted file mode 100644 index 91556ee5c142..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c +++ /dev/null @@ -1,140 +0,0 @@ -// 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/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c index 94a037bec1fa..766c24db3613 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c @@ -287,7 +287,6 @@ static void test_iwl_mld_link_pair_allows_emlsr(struct kunit *test) const struct link_pair_case *params = test->param_value; struct iwl_mld *mld = test->priv; struct ieee80211_vif *vif; - 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, @@ -311,11 +310,6 @@ static void test_iwl_mld_link_pair_allows_emlsr(struct kunit *test) 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 = |