diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mld')
43 files changed, 1735 insertions, 1400 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 db9e0f04f4b7..3bf36f8f6874 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -124,10 +124,12 @@ void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld, rcu_read_lock(); baid_data = rcu_dereference(mld->fw_id_to_ba[baid]); - if (!IWL_FW_CHECK(mld, !baid_data, - "Got valid BAID %d but not allocated, invalid BAR release!\n", - baid)) + if (!baid_data) { + IWL_DEBUG_HT(mld, + "Got valid BAID %d but not allocated\n", + baid); goto out_unlock; + } if (IWL_FW_CHECK(mld, tid != baid_data->tid || sta_id > mld->fw->ucode_capa.num_stations || @@ -303,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; } @@ -315,7 +322,7 @@ EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_reorder); static void iwl_mld_rx_agg_session_expired(struct timer_list *t) { struct iwl_mld_baid_data *data = - from_timer(data, t, session_timer); + timer_container_of(data, t, session_timer); struct iwl_mld_baid_data __rcu **rcu_ptr = data->rcu_ptr; struct iwl_mld_baid_data *ba_data; struct ieee80211_link_sta *link_sta; @@ -444,7 +451,7 @@ static void iwl_mld_init_reorder_buffer(struct iwl_mld *mld, struct iwl_mld_baid_data *data, u16 ssn) { - for (int i = 0; i < mld->trans->num_rx_queues; i++) { + for (int i = 0; i < mld->trans->info.num_rxqs; i++) { struct iwl_mld_reorder_buffer *reorder_buf = &data->reorder_buf[i]; struct iwl_mld_reorder_buf_entry *entries = @@ -468,7 +475,7 @@ static void iwl_mld_free_reorder_buffer(struct iwl_mld *mld, iwl_mld_sync_rx_queues(mld, IWL_MLD_RXQ_NOTIF_DEL_BA, &delba_data, sizeof(delba_data)); - for (int i = 0; i < mld->trans->num_rx_queues; i++) { + for (int i = 0; i < mld->trans->info.num_rxqs; i++) { struct iwl_mld_reorder_buffer *reorder_buf = &data->reorder_buf[i]; struct iwl_mld_reorder_buf_entry *entries = @@ -530,7 +537,7 @@ int iwl_mld_ampdu_rx_start(struct iwl_mld *mld, struct ieee80211_sta *sta, * before starting the BA session in the firmware */ baid_data = kzalloc(sizeof(*baid_data) + - mld->trans->num_rx_queues * reorder_buf_size, + mld->trans->info.num_rxqs * reorder_buf_size, GFP_KERNEL); if (!baid_data) return -ENOMEM; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c index 571eabd0b511..5c59acc8c4c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ap.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c @@ -11,6 +11,7 @@ #include "tx.h" #include "power.h" #include "key.h" +#include "phy.h" #include "iwl-utils.h" #include "fw/api/sta.h" @@ -269,6 +270,7 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_chanctx_conf *ctx; int ret; if (vif->type == NL80211_IFTYPE_AP) @@ -292,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 @@ -308,15 +321,21 @@ 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. + */ + ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); + iwl_mld_update_phy_chandef(mld, ctx); return 0; rm_bcast: iwl_mld_remove_bcast_sta(mld, vif, link); +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/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 5a7207accd86..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, @@ -774,7 +669,7 @@ iwl_mld_update_ptk_rx_seq(struct iwl_mld *mld, return; for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { - for (int i = 1; i < mld->trans->num_rx_queues; i++) + for (int i = 1; i < mld->trans->info.num_rxqs; i++) memcpy(mld_ptk_pn->q[i].pn[tid], wowlan_status->ptk.aes_seq[tid].ccmp.pn, IEEE80211_CCMP_PN_LEN); @@ -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 @@ -1099,7 +1005,8 @@ iwl_mld_set_netdetect_info(struct iwl_mld *mld, if (!match) return; - netdetect_info->matches[netdetect_info->n_matches++] = match; + netdetect_info->matches[netdetect_info->n_matches] = match; + netdetect_info->n_matches++; /* We inverted the order of the SSIDs in the scan * request, so invert the index here. @@ -1116,9 +1023,11 @@ iwl_mld_set_netdetect_info(struct iwl_mld *mld, for_each_set_bit(j, (unsigned long *)&matches[i].matching_channels[0], - sizeof(matches[i].matching_channels)) - match->channels[match->n_channels++] = + sizeof(matches[i].matching_channels)) { + match->channels[match->n_channels] = netdetect_cfg->channels[j]->center_freq; + match->n_channels++; + } } } @@ -1314,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"); @@ -1344,7 +1260,8 @@ int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld) if (ret) { IWL_ERR(mld, "d3 suspend: trans_d3_suspend failed %d\n", ret); } else { - mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; + /* Async notification might send hcmds, which is not allowed in suspend */ + iwl_mld_cancel_async_notifications(mld); mld->fw_status.in_d3 = true; } @@ -1369,14 +1286,10 @@ int iwl_mld_no_wowlan_resume(struct iwl_mld *mld) IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan resume flow\n"); - mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; mld->fw_status.in_d3 = false; iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); - 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; @@ -1434,7 +1347,7 @@ iwl_mld_suspend_set_ucast_pn(struct iwl_mld *mld, struct ieee80211_sta *sta, ieee80211_get_key_rx_seq(key, tid, &seq); /* and use the internal data for all queues */ - for (int que = 1; que < mld->trans->num_rx_queues; que++) { + for (int que = 1; que < mld->trans->info.num_rxqs; que++) { u8 *cur_pn = mld_ptk_pn->q[que].pn[tid]; if (memcmp(max_pn, cur_pn, IEEE80211_CCMP_PN_LEN) < 0) @@ -1754,7 +1667,7 @@ iwl_mld_send_proto_offload(struct iwl_mld *mld, addrconf_addr_solict_mult(&wowlan_data->target_ipv6_addrs[i], &solicited_addr); - for (j = 0; j < c; j++) + for (j = 0; j < n_nsc && j < c; j++) if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, &solicited_addr) == 0) break; @@ -1895,19 +1808,18 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) int link_id; int ret; bool fw_err = false; - bool keep_connection; lockdep_assert_wiphy(mld->wiphy); IWL_DEBUG_WOWLAN(mld, "Starting the wowlan resume flow\n"); - mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; if (!mld->fw_status.in_d3) { IWL_DEBUG_WOWLAN(mld, "Device_powered_off() was called during wowlan\n"); goto err; } + mld->fw_status.resuming = true; mld->fw_status.in_d3 = false; mld->scan.last_start_time_jiffies = jiffies; @@ -1927,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; @@ -1965,7 +1872,7 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) iwl_mld_process_netdetect_res(mld, bss_vif, &resume_data); mld->netdetect = false; } else { - keep_connection = + bool keep_connection = iwl_mld_process_wowlan_status(mld, bss_vif, resume_data.wowlan_status); @@ -1973,11 +1880,10 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) if (keep_connection) iwl_mld_unblock_emlsr(mld, bss_vif, IWL_MLD_EMLSR_BLOCKED_WOWLAN); + else + ieee80211_resume_disconnect(bss_vif); } - if (!mld->netdetect && !keep_connection) - ieee80211_resume_disconnect(bss_vif); - goto out; err: @@ -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 453ce2ba39d1..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 @@ -396,8 +396,8 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct iwl_mld *mld, char *buf, .data[0] = &cmd, }; struct iwl_dhc_tas_status_resp *resp = NULL; + u32 resp_len = 0; ssize_t pos = 0; - u32 resp_len; u32 status; int ret; @@ -546,6 +546,14 @@ 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); + /* Create a symlink with mac80211. It will be removed when mac80211 * exits (before the opmode exits which removes the target.) */ @@ -949,8 +957,9 @@ void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, snprintf(name, sizeof(name), "%pd", vif->debugfs_dir); snprintf(target, sizeof(target), "../../../%pd3/iwlmld", vif->debugfs_dir); - mld_vif->dbgfs_slink = - debugfs_create_symlink(name, mld->debugfs_dir, target); + if (!mld_vif->dbgfs_slink) + mld_vif->dbgfs_slink = + debugfs_create_symlink(name, mld->debugfs_dir, target); if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && vif->type == NL80211_IFTYPE_STATION) { @@ -996,8 +1005,8 @@ void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, mld_link_dir = debugfs_create_dir("iwlmld", dir); } -static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, - size_t count, void *data) +static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, + size_t count, void *data, bool v3) { struct ieee80211_link_sta *link_sta = data; struct iwl_mld_link_sta *mld_link_sta; @@ -1019,6 +1028,10 @@ static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) return -EIO; + /* input is in FW format (v2 or v3) so convert to v3 */ + rate = iwl_v3_rate_from_v2_v3(cpu_to_le32(rate), v3); + rate = le32_to_cpu(iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3)); + ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE : IWL_TLC_DEBUG_FIXED_RATE, @@ -1032,6 +1045,18 @@ static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, return ret ? : count; } +static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, false); +} + +static ssize_t iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, true); +} + static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, size_t count, void *data) { @@ -1071,6 +1096,7 @@ static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64); LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64); +LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate_v3, 64); void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1078,5 +1104,6 @@ void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, struct dentry *dir) { LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200); + LINK_STA_DEBUGFS_ADD_FILE(fixed_rate_v3, dir, 0200); LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/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 62da137e1024..b372173c4a79 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -8,10 +8,10 @@ #include "fw/api/alive.h" #include "fw/api/scan.h" #include "fw/api/rx.h" +#include "phy.h" #include "fw/dbg.h" #include "fw/pnvm.h" #include "hcmd.h" -#include "iwl-nvm-parse.h" #include "power.h" #include "mcc.h" #include "led.h" @@ -49,7 +49,7 @@ static int iwl_mld_send_rss_cfg_cmd(struct iwl_mld *mld) /* Do not direct RSS traffic to Q 0 which is our fallback queue */ for (int i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++) cmd.indirection_table[i] = - 1 + (i % (mld->trans->num_rx_queues - 1)); + 1 + (i % (mld->trans->info.num_rxqs - 1)); netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key)); return iwl_mld_send_cmd_pdu(mld, RSS_CONFIG_CMD, &cmd); @@ -99,17 +99,23 @@ static void iwl_mld_alive_imr_data(struct iwl_trans *trans, } } +struct iwl_mld_alive_data { + __le32 sku_id[3]; + bool valid; +}; + static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt, void *data) { unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); + unsigned int expected_sz; struct iwl_mld *mld = container_of(notif_wait, struct iwl_mld, notif_wait); struct iwl_trans *trans = mld->trans; u32 version = iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP, UCODE_ALIVE_NTFY, 0); - struct iwl_alive_ntf_v6 *palive; - bool *alive_valid = data; + struct iwl_mld_alive_data *alive_data = data; + struct iwl_alive_ntf *palive; struct iwl_umac_alive *umac; struct iwl_lmac_alive *lmac1; struct iwl_lmac_alive *lmac2 = NULL; @@ -117,7 +123,19 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, u32 umac_error_table; u16 status; - if (version < 6 || version > 7 || pkt_len != sizeof(*palive)) + switch (version) { + case 6: + case 7: + expected_sz = sizeof(struct iwl_alive_ntf_v6); + break; + case 8: + expected_sz = sizeof(struct iwl_alive_ntf); + break; + default: + return false; + } + + if (pkt_len != expected_sz) return false; palive = (void *)pkt->data; @@ -129,12 +147,15 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, lmac2 = &palive->lmac_data[1]; status = le16_to_cpu(palive->status); - trans->sku_id[0] = le32_to_cpu(palive->sku_id.data[0]); - trans->sku_id[1] = le32_to_cpu(palive->sku_id.data[1]); - trans->sku_id[2] = le32_to_cpu(palive->sku_id.data[2]); + BUILD_BUG_ON(sizeof(alive_data->sku_id) != + sizeof(palive->sku_id.data)); + memcpy(alive_data->sku_id, palive->sku_id.data, + sizeof(palive->sku_id.data)); IWL_DEBUG_FW(mld, "Got sku_id: 0x0%x 0x0%x 0x0%x\n", - trans->sku_id[0], trans->sku_id[1], trans->sku_id[2]); + le32_to_cpu(alive_data->sku_id[0]), + le32_to_cpu(alive_data->sku_id[1]), + le32_to_cpu(alive_data->sku_id[2])); lmac_error_event_table = le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr); @@ -147,13 +168,13 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr) & ~FW_ADDR_CACHE_CONTROL; - if (umac_error_table >= trans->cfg->min_umac_error_event_table) + if (umac_error_table >= trans->mac_cfg->base->min_umac_error_event_table) iwl_fw_umac_set_alive_err_table(trans, umac_error_table); else IWL_ERR(mld, "Not valid error log pointer 0x%08X\n", umac_error_table); - *alive_valid = status == IWL_ALIVE_STATUS_OK; + alive_data->valid = status == IWL_ALIVE_STATUS_OK; IWL_DEBUG_FW(mld, "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n", @@ -171,6 +192,10 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, IWL_DEBUG_FW(mld, "FW alive flags 0x%x\n", le16_to_cpu(palive->flags)); + if (version >= 8) + IWL_DEBUG_FW(mld, "platform_id 0x%llx\n", + le64_to_cpu(palive->platform_id)); + iwl_fwrt_update_fw_versions(&mld->fwrt, lmac1, umac); return true; @@ -208,24 +233,22 @@ static void iwl_mld_print_alive_notif_timeout(struct iwl_mld *mld) pc_data->pc_address); } -static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld) +static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld, + struct iwl_mld_alive_data *alive_data) { - const struct fw_img *fw = - iwl_get_ucode_image(mld->fw, IWL_UCODE_REGULAR); static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY }; struct iwl_notification_wait alive_wait; - bool alive_valid = false; int ret; lockdep_assert_wiphy(mld->wiphy); iwl_init_notification_wait(&mld->notif_wait, &alive_wait, alive_cmd, ARRAY_SIZE(alive_cmd), - iwl_alive_fn, &alive_valid); + iwl_alive_fn, alive_data); iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL); - ret = iwl_trans_start_fw(mld->trans, fw, true); + ret = iwl_trans_start_fw(mld->trans, mld->fw, IWL_UCODE_REGULAR, true); if (ret) { iwl_remove_notification(&mld->notif_wait, &alive_wait); return ret; @@ -239,28 +262,26 @@ static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld) iwl_fw_dbg_error_collect(&mld->fwrt, FW_DBG_TRIGGER_ALIVE_TIMEOUT); iwl_mld_print_alive_notif_timeout(mld); - goto alive_failure; + return ret; } - if (!alive_valid) { + if (!alive_data->valid) { IWL_ERR(mld, "Loaded firmware is not valid!\n"); - ret = -EIO; - goto alive_failure; + return -EIO; } - iwl_trans_fw_alive(mld->trans, 0); + iwl_trans_fw_alive(mld->trans); return 0; - -alive_failure: - iwl_trans_stop_device(mld->trans); - return ret; } static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) { struct iwl_notification_wait init_wait; - struct iwl_init_extended_cfg_cmd init_cfg = {}; + struct iwl_init_extended_cfg_cmd init_cfg = { + .init_flags = cpu_to_le32(BIT(IWL_INIT_PHY)), + }; + struct iwl_mld_alive_data alive_data = {}; static const u16 init_complete[] = { INIT_COMPLETE_NOTIF, }; @@ -268,19 +289,15 @@ static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) lockdep_assert_wiphy(mld->wiphy); - ret = iwl_mld_load_fw_wait_alive(mld); + ret = iwl_mld_load_fw_wait_alive(mld, &alive_data); if (ret) return ret; - mld->trans->step_urm = - !!(iwl_read_umac_prph(mld->trans, CNVI_PMU_STEP_FLOW) & - CNVI_PMU_STEP_FLOW_FORCE_URM); - ret = iwl_pnvm_load(mld->trans, &mld->notif_wait, - &mld->fw->ucode_capa); + mld->fw, alive_data.sku_id); if (ret) { IWL_ERR(mld, "Timeout waiting for PNVM load %d\n", ret); - goto init_failure; + return ret; } iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, @@ -298,31 +315,24 @@ static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) if (ret) { IWL_ERR(mld, "Failed to send init config command: %d\n", ret); iwl_remove_notification(&mld->notif_wait, &init_wait); - goto init_failure; + return ret; + } + + ret = iwl_mld_send_phy_cfg_cmd(mld); + if (ret) { + IWL_ERR(mld, "Failed to send PHY config command: %d\n", ret); + iwl_remove_notification(&mld->notif_wait, &init_wait); + return ret; } ret = iwl_wait_notification(&mld->notif_wait, &init_wait, MLD_INIT_COMPLETE_TIMEOUT); if (ret) { IWL_ERR(mld, "Failed to get INIT_COMPLETE %d\n", ret); - goto init_failure; - } - - if (!mld->nvm_data) { - mld->nvm_data = iwl_get_nvm(mld->trans, mld->fw, 0, 0); - if (IS_ERR(mld->nvm_data)) { - ret = PTR_ERR(mld->nvm_data); - mld->nvm_data = NULL; - IWL_ERR(mld, "Failed to read NVM: %d\n", ret); - goto init_failure; - } + return ret; } return 0; - -init_failure: - iwl_trans_stop_device(mld->trans); - return ret; } int iwl_mld_load_fw(struct iwl_mld *mld) @@ -337,15 +347,14 @@ int iwl_mld_load_fw(struct iwl_mld *mld) ret = iwl_mld_run_fw_init_sequence(mld); if (ret) - return ret; - - ret = iwl_mld_init_mcc(mld); - if (ret) - return ret; + goto err; mld->fw_status.running = true; return 0; +err: + iwl_mld_stop_fw(mld); + return ret; } void iwl_mld_stop_fw(struct iwl_mld *mld) @@ -358,6 +367,11 @@ void iwl_mld_stop_fw(struct iwl_mld *mld) iwl_trans_stop_device(mld->trans); + /* HW is stopped, no more coming RX. Cancel all notifications in + * case they were sent just before stopping the HW. + */ + iwl_mld_cancel_async_notifications(mld); + mld->fw_status.running = false; } @@ -519,7 +533,7 @@ int iwl_mld_start_fw(struct iwl_mld *mld) ret = iwl_mld_load_fw(mld); if (IWL_FW_CHECK(mld, ret, "Failed to start firmware %d\n", ret)) { iwl_fw_dbg_error_collect(&mld->fwrt, FW_DBG_TRIGGER_DRIVER); - goto error; + return ret; } IWL_DEBUG_INFO(mld, "uCode started.\n"); @@ -528,6 +542,10 @@ int iwl_mld_start_fw(struct iwl_mld *mld) if (ret) goto error; + ret = iwl_mld_init_mcc(mld); + if (ret) + goto error; + return 0; error: diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index e49e2260ac05..38993d65c052 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -22,9 +22,17 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif) struct iwl_mld *mld = mld_vif->mld; struct iwl_mld_link *link; + mld_vif->emlsr.blocked_reasons &= ~IWL_MLD_EMLSR_BLOCKED_ROC; + + if (mld_vif->aux_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &mld_vif->aux_sta); + /* EMLSR is turned back on during recovery */ vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; + if (mld_vif->roc_activity != ROC_NUM_ACTIVITIES) + ieee80211_remain_on_channel_expired(mld->hw); + mld_vif->roc_activity = ROC_NUM_ACTIVITIES; for_each_mld_vif_valid_link(mld_vif, link) { @@ -47,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); } @@ -103,6 +113,24 @@ static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld, IEEE80211_HE_MAC_CAP2_ACK_EN); } +static void iwl_mld_set_he_support(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd, + int cmd_ver) +{ + if (vif->type == NL80211_IFTYPE_AP) { + if (cmd_ver == 2) + cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1); + else + cmd->wifi_gen.he_ap_support = 1; + } else { + if (cmd_ver == 2) + cmd->wifi_gen_v2.he_support = cpu_to_le16(1); + else + cmd->wifi_gen.he_support = 1; + } +} + /* fill the common part for all interface types */ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, struct ieee80211_vif *vif, @@ -112,6 +140,9 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); struct ieee80211_bss_conf *link_conf; unsigned int link_id; + int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, + MAC_CONFIG_CMD), 0); lockdep_assert_wiphy(mld->wiphy); @@ -138,12 +169,11 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, * and enable both when we have MLO. */ if (ieee80211_vif_is_mld(vif)) { - if (vif->type == NL80211_IFTYPE_AP) - cmd->he_ap_support = cpu_to_le16(1); + iwl_mld_set_he_support(mld, vif, cmd, cmd_ver); + if (cmd_ver == 2) + cmd->wifi_gen_v2.eht_support = cpu_to_le32(1); else - cmd->he_support = cpu_to_le16(1); - - cmd->eht_support = cpu_to_le32(1); + cmd->wifi_gen.eht_support = 1; return; } @@ -151,10 +181,7 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, if (!link_conf->he_support) continue; - if (vif->type == NL80211_IFTYPE_AP) - cmd->he_ap_support = cpu_to_le16(1); - else - cmd->he_support = cpu_to_le16(1); + iwl_mld_set_he_support(mld, vif, cmd, cmd_ver); /* EHT, if supported, was already set above */ break; @@ -226,11 +253,6 @@ static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld, if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); - - if (vif->p2p) - cmd->client.ctwin = - cpu_to_le32(vif->bss_conf.p2p_noa_attr.oppps_ctwindow & - IEEE80211_P2P_OPPPS_CTWINDOW_MASK); } static void iwl_mld_fill_mac_cmd_ap(struct iwl_mld *mld, @@ -365,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 */ @@ -392,7 +425,10 @@ 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); return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index d1d56b081bf6..05dcb63701b1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -10,6 +10,7 @@ #include "link.h" #include "session-protect.h" #include "d3.h" +#include "fw/api/time-event.h" enum iwl_mld_cca_40mhz_wa_status { CCA_40_MHZ_WA_NONE, @@ -59,6 +60,7 @@ enum iwl_mld_emlsr_blocked { * loaded enough to justify EMLSR. * @IWL_MLD_EMLSR_EXIT_RFI: Exit EMLSR due to RFI * @IWL_MLD_EMLSR_EXIT_FW_REQUEST: Exit EMLSR because the FW requested it + * @IWL_MLD_EMLSR_EXIT_INVALID: internal exit reason due to invalid data */ enum iwl_mld_emlsr_exit { IWL_MLD_EMLSR_EXIT_BLOCK = 0x1, @@ -72,6 +74,7 @@ enum iwl_mld_emlsr_exit { IWL_MLD_EMLSR_EXIT_CHAN_LOAD = 0x100, IWL_MLD_EMLSR_EXIT_RFI = 0x200, IWL_MLD_EMLSR_EXIT_FW_REQUEST = 0x400, + IWL_MLD_EMLSR_EXIT_INVALID = 0x800, }; /** @@ -84,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. @@ -102,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; @@ -123,8 +129,6 @@ struct iwl_mld_emlsr { * Only valid for STA. (FIXME: needs to be per link) * @num_associated_stas: number of associated STAs. Relevant only for AP mode. * @ap_ibss_active: whether the AP/IBSS was started - * @roc_activity: the id of the roc_activity running. Relevant for p2p device - * only. Set to %ROC_NUM_ACTIVITIES when not in use. * @cca_40mhz_workaround: When we are connected in 2.4 GHz and 40 MHz, and the * environment is too loaded, we work around this by reconnecting to the * same AP with 20 MHz. This manages the status of the workaround. @@ -132,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. @@ -140,6 +146,10 @@ struct iwl_mld_emlsr { * @use_ps_poll: use ps_poll frames * @disable_bf: disable beacon filter * @dbgfs_slink: debugfs symlink for this interface + * @roc_activity: the id of the roc_activity running. Relevant for STA and + * p2p device only. Set to %ROC_NUM_ACTIVITIES when not in use. + * @aux_sta: station used for remain on channel. Used in P2P device. + * @mlo_scan_start_wk: worker to start a deferred MLO scan */ struct iwl_mld_vif { /* Add here fields that need clean up on restart */ @@ -151,13 +161,13 @@ struct iwl_mld_vif { struct ieee80211_key_conf __rcu *bigtks[2]; u8 num_associated_stas; bool ap_ibss_active; - u32 roc_activity; enum iwl_mld_cca_40mhz_wa_status cca_40mhz_workaround; #ifdef CONFIG_IWLWIFI_DEBUGFS bool beacon_inject_active; #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; @@ -166,7 +176,7 @@ struct iwl_mld_vif { struct iwl_mld_emlsr emlsr; -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP struct iwl_mld_wowlan_data wowlan_data; #endif #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -174,6 +184,10 @@ struct iwl_mld_vif { bool disable_bf; struct dentry *dbgfs_slink; #endif + enum iwl_roc_activity roc_activity; + struct iwl_mld_int_sta aux_sta; + + struct wiphy_delayed_work mlo_scan_start_wk; }; static inline struct iwl_mld_vif * @@ -182,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 82a4979a3af3..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->trans_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,15 +444,11 @@ 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->aux_sta); - - wiphy_delayed_work_init(&mld_link->rx_omi.finished_work, - iwl_mld_omi_bw_finished_work); + iwl_mld_init_internal_sta(&mld_link->mon_sta); return iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link); } @@ -850,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; @@ -863,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, @@ -889,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, @@ -1211,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 42b7bdcbd741..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. - * @aux_sta: station used for remain on channel. Used in P2P device. - * @link_id: over the air link ID + * @mon_sta: station used for TX injection in monitor interface. + * @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,27 +61,18 @@ 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 aux_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; + struct iwl_mld_int_sta mon_sta; /* 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; }; -/* Cleanup function for struct iwl_mld_phy, will be called in restart */ +/* Cleanup function for struct iwl_mld_link, will be called in restart */ static inline void iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link) { @@ -105,8 +88,8 @@ iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link) iwl_mld_free_internal_sta(mld, &link->bcast_sta); if (link->mcast_sta.sta_id != IWL_INVALID_STA) iwl_mld_free_internal_sta(mld, &link->mcast_sta); - if (link->aux_sta.sta_id != IWL_INVALID_STA) - iwl_mld_free_internal_sta(mld, &link->aux_sta); + if (link->mon_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &link->mon_sta); } /* Convert a percentage from [0,100] to [0,255] */ @@ -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 a4a612afb3b3..23362867b400 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c @@ -21,7 +21,7 @@ static bool iwl_mld_calc_low_latency(struct iwl_mld *mld, { struct iwl_mld_low_latency *ll = &mld->low_latency; bool global_low_latency = false; - u8 num_rx_q = mld->trans->num_rx_queues; + u8 num_rx_q = mld->trans->info.num_rxqs; for (int mac_id = 0; mac_id < NUM_MAC_INDEX_DRIVER; mac_id++) { u32 total_vo_vi_pkts = 0; @@ -131,12 +131,12 @@ int iwl_mld_low_latency_init(struct iwl_mld *mld) struct iwl_mld_low_latency *ll = &mld->low_latency; unsigned long ts = jiffies; - ll->pkts_counters = kcalloc(mld->trans->num_rx_queues, + ll->pkts_counters = kcalloc(mld->trans->info.num_rxqs, sizeof(*ll->pkts_counters), GFP_KERNEL); if (!ll->pkts_counters) return -ENOMEM; - for (int q = 0; q < mld->trans->num_rx_queues; q++) + for (int q = 0; q < mld->trans->info.num_rxqs; q++) spin_lock_init(&ll->pkts_counters[q].lock); wiphy_delayed_work_init(&ll->work, iwl_mld_low_latency_wk); @@ -167,7 +167,7 @@ void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld) memset(ll->window_start, 0, sizeof(ll->window_start)); memset(ll->result, 0, sizeof(ll->result)); - for (int q = 0; q < mld->trans->num_rx_queues; q++) + for (int q = 0; q < mld->trans->info.num_rxqs; q++) memset(ll->pkts_counters[q].vo_vi, 0, sizeof(ll->pkts_counters[q].vo_vi)); } @@ -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; @@ -276,7 +273,7 @@ void iwl_mld_low_latency_update_counters(struct iwl_mld *mld, return; if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) || - queue >= mld->trans->num_rx_queues)) + queue >= mld->trans->info.num_rxqs)) return; if (mld->low_latency.stopped) @@ -324,7 +321,7 @@ void iwl_mld_low_latency_restart(struct iwl_mld *mld) ll->window_start[mac] = 0; low_latency |= ll->result[mac]; - for (int q = 0; q < mld->trans->num_rx_queues; q++) { + for (int q = 0; q < mld->trans->info.num_rxqs; q++) { spin_lock_bh(&ll->pkts_counters[q].lock); ll->pkts_counters[q].vo_vi[mac] = 0; spin_unlock_bh(&ll->pkts_counters[q].lock); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 6851064b82da..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 | @@ -243,7 +250,6 @@ static void iwl_mac_hw_set_flags(struct iwl_mld *mld) ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); ieee80211_hw_set(hw, HAS_RATE_CONTROL); ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); - ieee80211_hw_set(hw, DISALLOW_PUNCTURING_5GHZ); ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); ieee80211_hw_set(hw, TDLS_WIDER_BW); @@ -285,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 @@ -305,7 +313,7 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld) wiphy->max_remain_on_channel_duration = 10000; - wiphy->hw_version = mld->trans->hw_id; + wiphy->hw_version = mld->trans->info.hw_id; wiphy->hw_timestamp_max_peers = 1; @@ -351,9 +359,9 @@ static void iwl_mac_hw_set_misc(struct iwl_mld *mld) hw->queues = IEEE80211_NUM_ACS; hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; - hw->netdev_features |= mld->cfg->features; + hw->netdev_features |= mld->trans->mac_cfg->base->features; - hw->max_tx_fragments = mld->trans->max_skb_frags; + hw->max_tx_fragments = mld->trans->info.max_skb_frags; hw->max_listen_interval = IWL_MLD_CONN_LISTEN_INTERVAL; hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL; @@ -376,6 +384,24 @@ static void iwl_mac_hw_set_misc(struct iwl_mld *mld) static int iwl_mld_hw_verify_preconditions(struct iwl_mld *mld) { + int ratecheck; + + /* check for rates version 3 */ + ratecheck = + (iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11) + + (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP, + TLC_MNG_UPDATE_NOTIF, 0) >= 4) + + (iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP, + REPLY_RX_MPDU_CMD, 0) >= 6) + + (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP, + RX_NO_DATA_NOTIF, 0) >= 4) + + (iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, TX_CMD, 0) >= 9); + + if (ratecheck != 0 && ratecheck != 5) { + IWL_ERR(mld, "Firmware has inconsistent rates\n"); + return -EINVAL; + } + /* 11ax is expected to be enabled for all supported devices */ if (WARN_ON(!mld->nvm_data->sku_cap_11ax_enable)) return -EINVAL; @@ -475,8 +501,8 @@ static int iwl_mld_mac80211_start(struct ieee80211_hw *hw) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); - int ret; bool in_d3 = false; + int ret = 0; lockdep_assert_wiphy(mld->wiphy); @@ -491,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 */ @@ -537,18 +570,10 @@ void iwl_mld_mac80211_stop(struct ieee80211_hw *hw, bool suspend) /* if the suspend flow fails the fw is in error. Stop it here, and it * will be started upon wakeup */ - if (!suspend || iwl_mld_no_wowlan_suspend(mld)) + if (!suspend || + (IS_ENABLED(CONFIG_PM_SLEEP) && iwl_mld_no_wowlan_suspend(mld))) iwl_mld_stop_fw(mld); - /* HW is stopped, no more coming RX. OTOH, the worker can't run as the - * wiphy lock is held. Cancel it in case it was scheduled just before - * we stopped the HW. - */ - wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); - - /* Empty out the list, as the worker won't do that */ - iwl_mld_purge_async_handlers_list(mld); - /* Clear in_hw_restart flag when stopping the hw, as mac80211 won't * execute the restart. */ @@ -565,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; } @@ -650,6 +676,7 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw, #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove(iwl_mld_vif_from_mac80211(vif)->dbgfs_slink); + iwl_mld_vif_from_mac80211(vif)->dbgfs_slink = NULL; #endif iwl_mld_rm_vif(mld, vif); @@ -895,9 +922,8 @@ void iwl_mld_change_chanctx(struct ieee80211_hw *hw, return; } update: - phy->chandef = *chandef; - iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY); + iwl_mld_update_phy_chandef(mld, ctx); } static u8 @@ -989,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; @@ -1029,12 +1054,19 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw, iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); if (vif->type == NL80211_IFTYPE_MONITOR) { - /* TODO: task=sniffer add sniffer station */ + ret = iwl_mld_add_mon_sta(mld, vif, link); + if (ret) + goto deactivate_link; + mld->monitor.p80 = iwl_mld_chandef_get_primary_80(&vif->bss_conf.chanreq.oper); } return 0; + +deactivate_link: + if (mld_link->active) + iwl_mld_deactivate_link(mld, link); err: RCU_INIT_POINTER(mld_link->chan_ctx, NULL); return ret; @@ -1060,7 +1092,8 @@ void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mld_deactivate_link(mld, link); - /* TODO: task=sniffer remove sniffer station */ + if (vif->type == NL80211_IFTYPE_MONITOR) + iwl_mld_remove_mon_sta(mld, vif, link); if (n_active > 1) { /* Indicate to mac80211 that EML is disabled */ @@ -1085,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; } @@ -1176,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); } @@ -1256,9 +1276,14 @@ iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw, } static void -iwl_mld_smps_wa(struct iwl_mld *mld, struct ieee80211_vif *vif, bool enable) +iwl_mld_smps_workaround(struct iwl_mld *mld, struct ieee80211_vif *vif, bool enable) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + bool workaround_required = + iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) < 2; + + if (!workaround_required) + return; /* Send the device-level power commands since the * firmware checks the POWER_TABLE_CMD's POWER_SAVE_EN bit to @@ -1305,7 +1330,7 @@ void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw, } if (changes & BSS_CHANGED_PS) { - iwl_mld_smps_wa(mld, vif, vif->cfg.ps); + iwl_mld_smps_workaround(mld, vif, vif->cfg.ps); iwl_mld_update_mac_power(mld, vif, false); } @@ -1318,13 +1343,22 @@ iwl_mld_mac80211_hw_scan(struct ieee80211_hw *hw, struct ieee80211_scan_request *hw_req) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; if (WARN_ON(!hw_req->req.n_channels || hw_req->req.n_channels > mld->fw->ucode_capa.n_scan_channels)) return -EINVAL; - return iwl_mld_regular_scan_start(mld, vif, &hw_req->req, &hw_req->ies); + ret = iwl_mld_regular_scan_start(mld, vif, &hw_req->req, &hw_req->ies); + if (!ret) { + /* We will be busy with scanning, so the counters may not reflect the + * reality. Stop checking the counters until the scan ends + */ + iwl_mld_start_ignoring_tpt_updates(mld); + } + + return ret; } static void @@ -1340,8 +1374,11 @@ iwl_mld_mac80211_cancel_hw_scan(struct ieee80211_hw *hw, * cancel scan before ieee80211_scan_work() could run. * To handle that, simply return if the scan is not running. */ - if (mld->scan.status & IWL_MLD_SCAN_REGULAR) + if (mld->scan.status & IWL_MLD_SCAN_REGULAR) { iwl_mld_scan_stop(mld, IWL_MLD_SCAN_REGULAR, true); + /* Scan is over, we can check again the tpt counters */ + iwl_mld_stop_ignoring_tpt_updates(mld); + } } static int @@ -1376,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) { @@ -1409,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)) @@ -1434,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) @@ -1637,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; @@ -1718,7 +1714,7 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, FW_CTXT_ACTION_MODIFY); if (ret) return ret; - iwl_mld_smps_wa(mld, vif, vif->cfg.ps); + iwl_mld_smps_workaround(mld, vif, vif->cfg.ps); } /* MFP is set by default before the station is authorized. @@ -1761,7 +1757,7 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld, &mld_vif->emlsr.check_tpt_wk); iwl_mld_reset_cca_40mhz_workaround(mld, vif); - iwl_mld_smps_wa(mld, vif, true); + iwl_mld_smps_workaround(mld, vif, true); } /* once we move into assoc state, need to update the FW to @@ -1874,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; @@ -1943,6 +1943,7 @@ static void iwl_mld_sta_rc_update(struct ieee80211_hw *hw, } } +#ifdef CONFIG_PM_SLEEP static void iwl_mld_set_wakeup(struct ieee80211_hw *hw, bool enabled) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); @@ -1994,13 +1995,14 @@ static int iwl_mld_resume(struct ieee80211_hw *hw) return 0; } +#endif static int iwl_mld_alloc_ptk_pn(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta, struct ieee80211_key_conf *key, struct iwl_mld_ptk_pn **ptk_pn) { - u8 num_rx_queues = mld->trans->num_rx_queues; + u8 num_rx_queues = mld->trans->info.num_rxqs; int keyidx = key->keyidx; struct ieee80211_key_seq seq; @@ -2456,15 +2458,17 @@ iwl_mld_change_vif_links(struct ieee80211_hw *hw, added |= BIT(0); for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - if (removed & BIT(i)) + if (removed & BIT(i) && !WARN_ON(!old[i])) iwl_mld_remove_link(mld, old[i]); } for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { if (added & BIT(i)) { link_conf = link_conf_dereference_protected(vif, i); - if (WARN_ON(!link_conf)) - return -EINVAL; + if (!link_conf) { + err = -EINVAL; + goto remove_added_links; + } err = iwl_mld_add_link(mld, link_conf); if (err) @@ -2499,7 +2503,11 @@ remove_added_links: iwl_mld_remove_link(mld, link_conf); } - return err; + if (WARN_ON(!iwl_mld_error_before_recovery(mld))) + return err; + + /* reconfig will fix us anyway */ + return 0; } static int iwl_mld_change_sta_links(struct ieee80211_hw *hw, @@ -2531,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) { @@ -2565,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, @@ -2598,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, @@ -2627,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, @@ -2667,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 daca14e208bd..16bb1b4904f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include <net/cfg80211.h> @@ -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; @@ -158,7 +110,7 @@ iwl_mld_get_regdomain(struct iwl_mld *mld, } IWL_DEBUG_LAR(mld, "MCC update response version: %d\n", resp_ver); - regd = iwl_parse_nvm_mcc_info(mld->trans->dev, mld->cfg, + regd = iwl_parse_nvm_mcc_info(mld->trans, __le32_to_cpu(resp->n_channels), resp->channels, __le16_to_cpu(resp->mcc), @@ -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 d4a99ae64074..7b46ccc306ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -26,6 +26,8 @@ #include "hcmd.h" #include "fw/api/location.h" +#include "iwl-nvm-parse.h" + #define DRV_DESCRIPTION "Intel(R) MLD wireless driver for Linux" MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_LICENSE("GPL"); @@ -60,7 +62,7 @@ static void iwl_mld_hw_set_regulatory(struct iwl_mld *mld) VISIBLE_IF_IWLWIFI_KUNIT void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, - const struct iwl_cfg *cfg, const struct iwl_fw *fw, + const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, struct ieee80211_hw *hw, struct dentry *dbgfs_dir) { mld->dev = trans->dev; @@ -75,6 +77,7 @@ void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, /* Setup async RX handling */ spin_lock_init(&mld->async_handlers_lock); + INIT_LIST_HEAD(&mld->async_handlers_list); wiphy_work_init(&mld->async_handlers_wk, iwl_mld_async_handlers_wk); @@ -191,6 +194,7 @@ static const struct iwl_hcmd_names iwl_mld_system_names[] = { HCMD_NAME(SOC_CONFIGURATION_CMD), HCMD_NAME(INIT_EXTENDED_CFG_CMD), HCMD_NAME(FW_ERROR_RECOVERY_CMD), + HCMD_NAME(RFI_CONFIG_CMD), HCMD_NAME(RFI_GET_FREQ_TABLE_CMD), HCMD_NAME(SYSTEM_STATISTICS_CMD), HCMD_NAME(SYSTEM_STATISTICS_END_NOTIF), @@ -247,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), @@ -286,7 +297,9 @@ static const struct iwl_hcmd_names iwl_mld_statistics_names[] = { * Access is done through binary search */ static const struct iwl_hcmd_names iwl_mld_prot_offload_names[] = { - HCMD_NAME(STORED_BEACON_NTF), + HCMD_NAME(WOWLAN_WAKE_PKT_NOTIFICATION), + HCMD_NAME(WOWLAN_INFO_NOTIFICATION), + HCMD_NAME(D3_END_NOTIFICATION), }; /* Please keep this array *SORTED* by hex value. @@ -303,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), @@ -321,33 +335,40 @@ EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(global_iwl_mld_goups_size); static void iwl_mld_configure_trans(struct iwl_op_mode *op_mode) { - const struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); static const u8 no_reclaim_cmds[] = {TX_CMD}; - struct iwl_trans_config trans_cfg = { - .op_mode = op_mode, - /* Rx is not supported yet, but add it to avoid warnings */ - .rx_buf_size = iwl_amsdu_size_to_rxb_size(), - .command_groups = iwl_mld_groups, - .command_groups_size = ARRAY_SIZE(iwl_mld_groups), - .fw_reset_handshake = true, - .queue_alloc_cmd_ver = - iwl_fw_lookup_cmd_ver(mld->fw, - WIDE_ID(DATA_PATH_GROUP, - SCD_QUEUE_CONFIG_CMD), - 0), - .no_reclaim_cmds = no_reclaim_cmds, - .n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds), - .cb_data_offs = offsetof(struct ieee80211_tx_info, - driver_data[2]), - }; struct iwl_trans *trans = mld->trans; + u32 eckv_value; - trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; - trans->iml = mld->fw->iml; - trans->iml_len = mld->fw->iml_len; - trans->wide_cmd_header = true; + iwl_bios_setup_step(trans, &mld->fwrt); + iwl_uefi_get_step_table(trans); - iwl_trans_configure(trans, &trans_cfg); + if (iwl_bios_get_eckv(&mld->fwrt, &eckv_value)) + IWL_DEBUG_RADIO(mld, "ECKV table doesn't exist in BIOS\n"); + else + trans->conf.ext_32khz_clock_valid = !!eckv_value; + + trans->conf.rx_buf_size = iwl_amsdu_size_to_rxb_size(); + trans->conf.command_groups = iwl_mld_groups; + trans->conf.command_groups_size = ARRAY_SIZE(iwl_mld_groups); + trans->conf.fw_reset_handshake = true; + trans->conf.queue_alloc_cmd_ver = + iwl_fw_lookup_cmd_ver(mld->fw, WIDE_ID(DATA_PATH_GROUP, + SCD_QUEUE_CONFIG_CMD), + 0); + trans->conf.cb_data_offs = offsetof(struct ieee80211_tx_info, + driver_data[2]); + BUILD_BUG_ON(sizeof(no_reclaim_cmds) > + sizeof(trans->conf.no_reclaim_cmds)); + memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds, + sizeof(no_reclaim_cmds)); + trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); + + trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD; + trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_desc); + trans->conf.wide_cmd_header = true; + + iwl_trans_op_mode_enter(trans, op_mode); } /* @@ -358,13 +379,12 @@ iwl_mld_configure_trans(struct iwl_op_mode *op_mode) #define NUM_FW_LOAD_RETRIES 3 static struct iwl_op_mode * -iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) { struct ieee80211_hw *hw; struct iwl_op_mode *op_mode; struct iwl_mld *mld; - u32 eckv_value; int ret; /* Allocate and initialize a new hardware device */ @@ -382,16 +402,13 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_construct_mld(mld, trans, cfg, fw, hw, dbgfs_dir); + /* we'll verify later it matches between commands */ + mld->fw_rates_ver_3 = iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11; + iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir); iwl_mld_get_bios_tables(mld); iwl_uefi_get_sgom_table(trans, &mld->fwrt); - iwl_uefi_get_step_table(trans); - if (iwl_bios_get_eckv(&mld->fwrt, &eckv_value)) - IWL_DEBUG_RADIO(mld, "ECKV table doesn't exist in BIOS\n"); - else - trans->ext_32khz_clock_valid = !!eckv_value; - iwl_bios_setup_step(trans, &mld->fwrt); mld->bios_enable_puncturing = iwl_uefi_get_puncturing(&mld->fwrt); iwl_mld_hw_set_regulatory(mld); @@ -410,13 +427,25 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, break; } + if (!ret) { + mld->nvm_data = iwl_get_nvm(mld->trans, mld->fw, 0, 0); + if (IS_ERR(mld->nvm_data)) { + IWL_ERR(mld, "Failed to read NVM: %d\n", ret); + ret = PTR_ERR(mld->nvm_data); + } + } + if (ret) { wiphy_unlock(mld->wiphy); rtnl_unlock(); - iwl_fw_flush_dumps(&mld->fwrt); - goto free_hw; + goto err; } + /* We are about to stop the FW. Notifications may require an + * operational FW, so handle them all here before we stop. + */ + wiphy_work_flush(mld->wiphy, &mld->async_handlers_wk); + iwl_mld_stop_fw(mld); wiphy_unlock(mld->wiphy); @@ -455,7 +484,8 @@ leds_exit: iwl_mld_leds_exit(mld); free_nvm: kfree(mld->nvm_data); -free_hw: +err: + iwl_trans_op_mode_leave(mld->trans); ieee80211_free_hw(mld->hw); return ERR_PTR(ret); } @@ -468,8 +498,9 @@ iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode) iwl_mld_ptp_remove(mld); iwl_mld_leds_exit(mld); - wiphy_lock(mld->wiphy); iwl_mld_thermal_exit(mld); + + wiphy_lock(mld->wiphy); iwl_mld_low_latency_stop(mld); iwl_mld_deinit_time_sync(mld); wiphy_unlock(mld->wiphy); @@ -483,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); @@ -607,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"); @@ -631,7 +663,8 @@ iwl_mld_nic_error(struct iwl_op_mode *op_mode, * It might not actually be true that we'll restart, but the * setting doesn't matter if we're going to be unbound either. */ - if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT) + if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT && + mld->fw_status.running) mld->fw_status.in_hw_restart = true; } @@ -657,6 +690,13 @@ static bool iwl_mld_sw_reset(struct iwl_op_mode *op_mode, { struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + /* SW reset can happen for TOP error w/o NIC error, so + * also abort scan here and set in_hw_restart, when we + * had a NIC error both were already done. + */ + iwl_mld_report_scan_aborted(mld); + mld->fw_status.in_hw_restart = true; + /* Do restart only in the following conditions are met: * - we consider the FW as running * - The trigger that brought us here is defined as one that requires @@ -685,7 +725,6 @@ static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); wiphy_lock(mld->wiphy); - mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; iwl_mld_stop_fw(mld); mld->fw_status.in_d3 = false; wiphy_unlock(mld->wiphy); @@ -695,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, @@ -709,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 5eceaaf7696d..94dc9da6360d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -116,6 +116,7 @@ * @monitor.ampdu_toggle: the state of the previous packet to track A-MPDU * @monitor.cur_aid: current association id tracked by the sniffer * @monitor.cur_bssid: current bssid tracked by the sniffer + * @monitor.ptp_time: set the Rx mactime using the device's PTP clock time * @monitor.p80: primary channel position relative to he whole bandwidth, in * steps of 80 MHz * @fw_id_to_link_sta: maps a fw id of a sta to the corresponding @@ -149,14 +150,19 @@ * @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 * @radio_kill.hw: radio is killed by hw switch * @radio_kill.ct: radio is killed because the device it too hot + * @power_budget_mw: maximum cTDP power budget as defined for this system and + * device * @addresses: device MAC addresses. * @scan: instance of the scan object + * @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 @@ -174,6 +180,7 @@ * @mcast_filter_cmd: pointer to the multicast filter command. * @mgmt_tx_ant: stores the last TX antenna index; used for setting * TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure). + * @fw_rates_ver_3: FW rates are in version 3 * @low_latency: low-latency manager. * @tzone: thermal zone device's data * @cooling_dev: cooling device's related data @@ -201,6 +208,7 @@ struct iwl_mld { #ifdef CONFIG_IWLWIFI_DEBUGFS __le16 cur_aid; u8 cur_bssid[ETH_ALEN]; + bool ptp_time; #endif } monitor; #ifdef CONFIG_PM_SLEEP @@ -213,7 +221,7 @@ struct iwl_mld { /* And here fields that survive a fw restart */ struct device *dev; struct iwl_trans *trans; - const struct iwl_cfg *cfg; + const struct iwl_rf_cfg *cfg; const struct iwl_fw *fw; struct ieee80211_hw *hw; struct wiphy *wiphy; @@ -231,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; @@ -241,10 +250,14 @@ struct iwl_mld { ct:1; } radio_kill; + u32 power_budget_mw; + 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; @@ -266,6 +279,8 @@ struct iwl_mld { u8 mgmt_tx_ant; + bool fw_rates_ver_3; + struct iwl_mld_low_latency low_latency; bool ibss_manager; @@ -286,7 +301,7 @@ struct iwl_mld { memset((void *)&(_ptr)->zeroed_on_hw_restart, 0, \ sizeof((_ptr)->zeroed_on_hw_restart)) -/* Cleanup function for struct iwl_mld_vif, will be called in restart */ +/* Cleanup function for struct iwl_mld, will be called in restart */ static inline void iwl_cleanup_mld(struct iwl_mld *mld) { @@ -298,11 +313,6 @@ iwl_cleanup_mld(struct iwl_mld *mld) #endif iwl_mld_low_latency_restart_cleanup(mld); - - /* Empty the list of async notification handlers so we won't process - * notifications from the dead fw after the reconfig flow. - */ - iwl_mld_purge_async_handlers_list(mld); } enum iwl_power_scheme { @@ -415,7 +425,7 @@ iwl_mld_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags, int rate = rate_n_flags & RATE_LEGACY_RATE_MSK; bool is_lb = band == NL80211_BAND_2GHZ; - if (format == RATE_MCS_LEGACY_OFDM_MSK) + if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) return is_lb ? rate + IWL_FIRST_OFDM_RATE : rate; /* CCK is not allowed in 5 GHz */ @@ -487,7 +497,7 @@ iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta, struct ieee80211_rx_status *rx_status, int queue); void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, - const struct iwl_cfg *cfg, const struct iwl_fw *fw, + const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, struct ieee80211_hw *hw, struct dentry *dbgfs_dir); #endif diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index a870e169e265..e57f5388fe77 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -52,7 +52,8 @@ static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask) HOW(BT_COEX) \ HOW(CHAN_LOAD) \ HOW(RFI) \ - HOW(FW_REQUEST) + HOW(FW_REQUEST) \ + HOW(INVALID) static const char * iwl_mld_get_emlsr_exit_string(enum iwl_mld_emlsr_exit exit) @@ -286,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); @@ -325,23 +356,44 @@ static void iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac, struct ieee80211_vif *vif) { - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct iwl_esr_mode_notif *notif = (void *)data; + const struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + enum iwl_mvm_fw_esr_recommendation action; + const struct iwl_esr_mode_notif *notif = NULL; + + if (iwl_fw_lookup_notif_ver(mld_vif->mld->fw, DATA_PATH_GROUP, + ESR_MODE_NOTIF, 0) > 1) { + notif = (void *)data; + action = le32_to_cpu(notif->action); + } else { + const struct iwl_esr_mode_notif_v1 *notif_v1 = (void *)data; + + action = le32_to_cpu(notif_v1->action); + } if (!iwl_mld_vif_has_emlsr_cap(vif)) return; - switch (le32_to_cpu(notif->action)) { + switch (action) { case ESR_RECOMMEND_LEAVE: + if (notif) + IWL_DEBUG_INFO(mld_vif->mld, + "FW recommend leave reason = 0x%x\n", + le32_to_cpu(notif->leave_reason_mask)); + iwl_mld_exit_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_EXIT_FW_REQUEST, iwl_mld_get_primary_link(vif)); break; - case ESR_RECOMMEND_ENTER: case ESR_FORCE_LEAVE: + if (notif) + IWL_DEBUG_INFO(mld_vif->mld, + "FW force leave reason = 0x%x\n", + le32_to_cpu(notif->leave_reason_mask)); + fallthrough; + case ESR_RECOMMEND_ENTER: default: IWL_WARN(mld_vif->mld, "Unexpected EMLSR notification: %d\n", - le32_to_cpu(notif->action)); + action); } } @@ -508,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)) @@ -523,7 +577,7 @@ void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk) } /* Sum up RX and TX MPDUs from the different queues/links */ - for (int q = 0; q < mld->trans->num_rx_queues; q++) { + for (int q = 0; q < mld->trans->info.num_rxqs; q++) { struct iwl_mld_per_q_mpdu_counter *queue_counter = &mld_sta->mpdu_counters[q]; @@ -643,11 +697,11 @@ iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld, { struct wiphy *wiphy = mld->wiphy; struct ieee80211_bss_conf *conf; - enum iwl_mld_emlsr_exit ret = 0; + u32 ret = 0; conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]); if (WARN_ON_ONCE(!conf)) - return false; + return IWL_MLD_EMLSR_EXIT_INVALID; if (link->chandef->chan->band == NL80211_BAND_2GHZ && mld->bt_is_active) ret |= IWL_MLD_EMLSR_EXIT_BT_COEX; @@ -731,7 +785,7 @@ iwl_mld_get_min_chan_load_thresh(struct ieee80211_chanctx_conf *chanctx) return 10; } -VISIBLE_IF_IWLWIFI_KUNIT bool +static bool iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, const struct iwl_mld_link_sel_data *a, @@ -772,8 +826,8 @@ iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, if (a->chandef->width <= b->chandef->width) return true; - bw_a = nl80211_chan_width_to_mhz(a->chandef->width); - bw_b = nl80211_chan_width_to_mhz(b->chandef->width); + bw_a = cfg80211_chandef_get_width(a->chandef); + bw_b = cfg80211_chandef_get_width(b->chandef); ratio = bw_a / bw_b; switch (ratio) { @@ -788,10 +842,9 @@ iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, return false; } -EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_channel_load_allows_emlsr); -static bool -iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, +VISIBLE_IF_IWLWIFI_KUNIT u32 +iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif, struct iwl_mld_link_sel_data *a, struct iwl_mld_link_sel_data *b) { @@ -800,12 +853,36 @@ iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, u32 reason_mask = 0; /* Per-link considerations */ - if (iwl_mld_emlsr_disallowed_with_link(mld, vif, a, true) || - iwl_mld_emlsr_disallowed_with_link(mld, vif, b, false)) - return false; - - if (a->chandef->chan->band == b->chandef->chan->band) - reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND; + reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, a, true); + if (reason_mask) + return reason_mask; + + reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, b, false); + if (reason_mask) + return reason_mask; + + if (a->chandef->chan->band == b->chandef->chan->band) { + const struct cfg80211_chan_def *c_low = a->chandef; + const struct cfg80211_chan_def *c_high = b->chandef; + u32 c_low_upper_edge, c_high_lower_edge; + + if (c_low->chan->center_freq > c_high->chan->center_freq) + swap(c_low, c_high); + + c_low_upper_edge = c_low->chan->center_freq + + cfg80211_chandef_get_width(c_low) / 2; + c_high_lower_edge = c_high->chan->center_freq - + cfg80211_chandef_get_width(c_high) / 2; + + if (a->chandef->chan->band == NL80211_BAND_5GHZ && + c_low_upper_edge <= 5330 && c_high_lower_edge >= 5490) { + /* This case is fine - HW/FW can deal with it, there's + * enough separation between the two channels. + */ + } else { + reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND; + } + } if (!iwl_mld_channel_load_allows_emlsr(mld, vif, a, b)) reason_mask |= IWL_MLD_EMLSR_EXIT_CHAN_LOAD; @@ -818,11 +895,11 @@ iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, nl80211_chan_width_to_mhz(a->chandef->width), nl80211_chan_width_to_mhz(b->chandef->width)); iwl_mld_print_emlsr_exit(mld, reason_mask); - return false; } - return true; + return reason_mask; } +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_emlsr_pair_state); /* Calculation is done with fixed-point with a scaling factor of 1/256 */ #define SCALE_FACTOR 256 @@ -850,7 +927,7 @@ unsigned int iwl_mld_get_emlsr_grade(struct iwl_mld *mld, *primary_id = a->link_id; - if (!iwl_mld_valid_emlsr_pair(vif, a, b)) + if (iwl_mld_emlsr_pair_state(vif, a, b)) return 0; primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]); @@ -892,8 +969,11 @@ static void _iwl_mld_select_links(struct iwl_mld *mld, n_data = iwl_mld_set_link_sel_data(mld, vif, data, usable_links, &best_idx); - if (WARN(!n_data, "Couldn't find a valid grade for any link!\n")) + if (!n_data) { + IWL_DEBUG_EHT(mld, + "Couldn't find a valid grade for any link!\n"); return; + } /* Default to selecting the single best link */ best_link = &data[best_idx]; @@ -965,6 +1045,9 @@ static void iwl_mld_emlsr_check_bt_iter(void *_data, u8 *mac, struct ieee80211_bss_conf *link; unsigned int link_id; + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + if (!mld->bt_is_active) { iwl_mld_retry_emlsr(mld, vif); return; @@ -1068,9 +1151,75 @@ 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); } + +static void iwl_mld_ignore_tpt_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + struct iwl_mld_sta *mld_sta; + bool *start = (void *)data; + + /* check_tpt_wk is only used when TPT block isn't set */ + if (mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT || + !IWL_MLD_AUTO_EML_ENABLE || !mld_vif->ap_sta) + return; + + mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta); + + /* We only count for the AP sta in a MLO connection */ + if (!mld_sta->mpdu_counters) + return; + + if (*start) { + wiphy_delayed_work_cancel(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk); + IWL_DEBUG_EHT(mld, "TPT check disabled\n"); + return; + } + + /* Clear the counters so we start from the beginning */ + for (int q = 0; q < mld->trans->info.num_rxqs; q++) { + struct iwl_mld_per_q_mpdu_counter *queue_counter = + &mld_sta->mpdu_counters[q]; + + spin_lock_bh(&queue_counter->lock); + + memset(queue_counter->per_link, 0, + sizeof(queue_counter->per_link)); + + spin_unlock_bh(&queue_counter->lock); + } + + /* Schedule the check in 5 seconds */ + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk, + round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW)); + IWL_DEBUG_EHT(mld, "TPT check enabled\n"); +} + +void iwl_mld_start_ignoring_tpt_updates(struct iwl_mld *mld) +{ + bool start = true; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_ignore_tpt_iter, + &start); +} + +void iwl_mld_stop_ignoring_tpt_updates(struct iwl_mld *mld) +{ + bool start = false; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_ignore_tpt_iter, + &start); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h index 4fb1fdbe3df9..d936589fe39d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h @@ -37,7 +37,7 @@ static inline bool iwl_mld_vif_has_emlsr_cap(struct ieee80211_vif *vif) return ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION && ieee80211_vif_is_mld(vif) && vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP && - !CSR_HW_RFID_IS_CDB(mld_vif->mld->trans->hw_rf_id); + !CSR_HW_RFID_IS_CDB(mld_vif->mld->trans->info.hw_rf_id); } static inline int @@ -157,11 +157,15 @@ 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) -bool iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, - struct ieee80211_vif *vif, - const struct iwl_mld_link_sel_data *a, - const struct iwl_mld_link_sel_data *b); +u32 iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *a, + struct iwl_mld_link_sel_data *b); #endif +void iwl_mld_start_ignoring_tpt_updates(struct iwl_mld *mld); +void iwl_mld_stop_ignoring_tpt_updates(struct iwl_mld *mld); + #endif /* __iwl_mld_mlo_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index fc18cba8aaa8..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 @@ -183,47 +176,6 @@ static void iwl_mld_handle_mu_mimo_grp_notif(struct iwl_mld *mld, } static void -iwl_mld_handle_stored_beacon_notif(struct iwl_mld *mld, - struct iwl_rx_packet *pkt) -{ - unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); - struct iwl_stored_beacon_notif *sb = (void *)pkt->data; - struct ieee80211_rx_status rx_status = {}; - struct sk_buff *skb; - u32 size = le32_to_cpu(sb->common.byte_count); - - if (size == 0) - return; - - if (pkt_len < struct_size(sb, data, size)) - return; - - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) { - IWL_ERR(mld, "alloc_skb failed\n"); - return; - } - - /* update rx_status according to the notification's metadata */ - rx_status.mactime = le64_to_cpu(sb->common.tsf); - /* TSF as indicated by the firmware is at INA time */ - rx_status.flag |= RX_FLAG_MACTIME_PLCP_START; - rx_status.device_timestamp = le32_to_cpu(sb->common.system_time); - rx_status.band = - iwl_mld_phy_band_to_nl80211(le16_to_cpu(sb->common.band)); - rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(sb->common.channel), - rx_status.band); - - /* copy the data */ - skb_put_data(skb, sb->data, size); - memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - - /* pass it as regular rx to mac80211 */ - ieee80211_rx_napi(mld->hw, NULL, skb, NULL); -} - -static void iwl_mld_handle_channel_switch_start_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) { @@ -336,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, @@ -345,12 +299,15 @@ CMD_VERSIONS(session_prot_notif, CMD_VERSIONS(missed_beacon_notif, CMD_VER_ENTRY(5, iwl_missed_beacons_notif)) CMD_VERSIONS(tx_resp_notif, - CMD_VER_ENTRY(8, iwl_tx_resp)) + CMD_VER_ENTRY(8, iwl_tx_resp) + CMD_VER_ENTRY(9, iwl_tx_resp)) CMD_VERSIONS(compressed_ba_notif, CMD_VER_ENTRY(5, iwl_compressed_ba_notif) - CMD_VER_ENTRY(6, iwl_compressed_ba_notif)) + CMD_VER_ENTRY(6, iwl_compressed_ba_notif) + CMD_VER_ENTRY(7, iwl_compressed_ba_notif)) CMD_VERSIONS(tlc_notif, - CMD_VER_ENTRY(3, iwl_tlc_update_notif)) + CMD_VER_ENTRY(3, iwl_tlc_update_notif) + CMD_VER_ENTRY(4, iwl_tlc_update_notif)) CMD_VERSIONS(mu_mimo_grp_notif, CMD_VER_ENTRY(1, iwl_mu_group_mgmt_notif)) CMD_VERSIONS(channel_switch_start_notif, @@ -361,8 +318,6 @@ CMD_VERSIONS(ct_kill_notif, CMD_VER_ENTRY(2, ct_kill_notif)) CMD_VERSIONS(temp_notif, CMD_VER_ENTRY(2, iwl_dts_measurement_notif)) -CMD_VERSIONS(stored_beacon_notif, - CMD_VER_ENTRY(4, iwl_stored_beacon_notif)) CMD_VERSIONS(roc_notif, CMD_VER_ENTRY(1, iwl_roc_notif)) CMD_VERSIONS(probe_resp_data_notif, @@ -378,7 +333,8 @@ CMD_VERSIONS(bt_coex_notif, CMD_VERSIONS(beacon_notification, CMD_VER_ENTRY(6, iwl_extended_beacon_notif)) CMD_VERSIONS(emlsr_mode_notif, - CMD_VER_ENTRY(1, iwl_esr_mode_notif)) + CMD_VER_ENTRY(1, iwl_esr_mode_notif_v1) + CMD_VER_ENTRY(2, iwl_esr_mode_notif)) CMD_VERSIONS(emlsr_trans_fail_notif, CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif)) CMD_VERSIONS(uapsd_misbehaving_ap_notif, @@ -387,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) @@ -405,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 @@ -451,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, @@ -473,8 +432,6 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = { RX_HANDLER_OF_ROC(MAC_CONF_GROUP, ROC_NOTIF, roc_notif) RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF, mu_mimo_grp_notif, RX_HANDLER_SYNC) - RX_HANDLER_NO_OBJECT(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF, - stored_beacon_notif, RX_HANDLER_SYNC) RX_HANDLER_OF_VIF(MAC_CONF_GROUP, PROBE_RESPONSE_DATA_NOTIF, probe_resp_data_notif) RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, CT_KILL_NOTIFICATION, @@ -499,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) }; @@ -646,7 +603,7 @@ void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi, struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); - if (unlikely(queue >= mld->trans->num_rx_queues)) + if (unlikely(queue >= mld->trans->info.num_rxqs)) return; if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD))) @@ -707,10 +664,14 @@ void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk) } } -void iwl_mld_purge_async_handlers_list(struct iwl_mld *mld) +void iwl_mld_cancel_async_notifications(struct iwl_mld *mld) { struct iwl_async_handler_entry *entry, *tmp; + lockdep_assert_wiphy(mld->wiphy); + + wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); + spin_lock_bh(&mld->async_handlers_lock); list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) { iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.h b/drivers/net/wireless/intel/iwlwifi/mld/notif.h index 2eaa1d4e138e..adcdd9dec192 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.h @@ -15,7 +15,7 @@ void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi, void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk); -void iwl_mld_purge_async_handlers_list(struct iwl_mld *mld); +void iwl_mld_cancel_async_notifications(struct iwl_mld *mld); enum iwl_mld_object_type { IWL_MLD_OBJECT_TYPE_NONE, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.c b/drivers/net/wireless/intel/iwlwifi/mld/phy.c index 2fbc8090088b..1d93fb9e4dbf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.c @@ -50,6 +50,9 @@ static void iwl_mld_chanctx_usage_iter(void *_data, u8 *mac, if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx) continue; + if (vif->type == NL80211_IFTYPE_AP && link_conf->ftm_responder) + data->use_def = true; + if (iwl_mld_chanctx_fils_enabled(vif, data->ctx)) data->use_def = true; } @@ -153,3 +156,43 @@ int iwl_mld_phy_fw_action(struct iwl_mld *mld, return ret; } + +static u32 iwl_mld_get_phy_config(struct iwl_mld *mld) +{ + u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN | + FW_PHY_CFG_RX_CHAIN); + u32 valid_rx_ant = iwl_mld_get_valid_rx_ant(mld); + u32 valid_tx_ant = iwl_mld_get_valid_tx_ant(mld); + + phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS | + valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS; + + return mld->fw->phy_config & phy_config; +} + +int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld) +{ + const struct iwl_tlv_calib_ctrl *default_calib = + &mld->fw->default_calib[IWL_UCODE_REGULAR]; + struct iwl_phy_cfg_cmd_v3 cmd = { + .phy_cfg = cpu_to_le32(iwl_mld_get_phy_config(mld)), + .calib_control.event_trigger = default_calib->event_trigger, + .calib_control.flow_trigger = default_calib->flow_trigger, + .phy_specific_cfg = mld->fwrt.phy_filters, + }; + + IWL_DEBUG_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg); + + return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd); +} + +void iwl_mld_update_phy_chandef(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + struct cfg80211_chan_def *chandef = + iwl_mld_get_chandef_from_chanctx(mld, ctx); + + phy->chandef = *chandef; + iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h index 2212a89321b7..0deaf179f07c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h @@ -52,4 +52,9 @@ iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld, struct ieee80211_chanctx_conf *ctx); u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef); +int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld); + +void iwl_mld_update_phy_chandef(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx); + #endif /* __iwl_mld_phy_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c index 2f16c174b57e..f664b277adf7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -253,6 +253,9 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld, cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); + if (iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) >= 2) + cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK); + /* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */ if (link_conf->beacon_rate && (link_conf->beacon_rate->bitrate == 10 || @@ -374,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 d5c3f853d96c..ffeb37a7f830 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -21,7 +21,7 @@ static int iwl_mld_get_systime(struct iwl_mld *mld, u32 *gp2) { - *gp2 = iwl_read_prph(mld->trans, mld->trans->cfg->gp2_reg_addr); + *gp2 = iwl_read_prph(mld->trans, mld->trans->mac_cfg->base->gp2_reg_addr); if (*gp2 == 0x5a5a5a5a) return -EINVAL; @@ -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 a75af8c1e8ab..75d2f5cb23a7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -63,48 +63,25 @@ void iwl_mld_get_bios_tables(struct iwl_mld *mld) /* we don't fail if the table is not available */ } - ret = iwl_uefi_get_uats_table(mld->trans, &mld->fwrt); - if (ret) - IWL_DEBUG_RADIO(mld, "failed to read UATS table (%d)\n", ret); + iwl_uefi_get_uats_table(mld->trans, &mld->fwrt); + + iwl_bios_get_phy_filters(&mld->fwrt); } static int iwl_mld_geo_sar_init(struct iwl_mld *mld) { 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/roc.c b/drivers/net/wireless/intel/iwlwifi/mld/roc.c index b87faca23ceb..e85f45bce79a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/roc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.c @@ -31,13 +31,54 @@ iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif) *result = ret; } +struct iwl_mld_roc_iter_data { + enum iwl_roc_activity activity; + struct ieee80211_vif *vif; + bool found; +}; + +static void iwl_mld_find_roc_vif_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_roc_iter_data *roc_data = data; + + if (mld_vif->roc_activity != roc_data->activity) + return; + + /* The FW supports one ROC of each type simultaneously */ + if (WARN_ON(roc_data->found)) { + roc_data->vif = NULL; + return; + } + + roc_data->found = true; + roc_data->vif = vif; +} + +static struct ieee80211_vif * +iwl_mld_find_roc_vif(struct iwl_mld *mld, enum iwl_roc_activity activity) +{ + struct iwl_mld_roc_iter_data roc_data = { + .activity = activity, + .found = false, + }; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_find_roc_vif_iter, + &roc_data); + + return roc_data.vif; +} + int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *channel, int duration, enum ieee80211_roc_type type) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct iwl_mld_int_sta *aux_sta; + struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta; struct iwl_roc_req cmd = { .action = cpu_to_le32(FW_CTXT_ACTION_ADD), }; @@ -49,38 +90,40 @@ int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, lockdep_assert_wiphy(mld->wiphy); - ieee80211_iterate_active_interfaces_mtx(mld->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mld_vif_iter_emlsr_block_roc, - &ret); - if (ret) - return ret; - - /* TODO: task=Hotspot 2.0 */ - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { + if (vif->type != NL80211_IFTYPE_P2P_DEVICE && + vif->type != NL80211_IFTYPE_STATION) { IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n", vif->type); return -EOPNOTSUPP; } - switch (type) { - case IEEE80211_ROC_TYPE_NORMAL: - activity = ROC_ACTIVITY_P2P_DISC; - break; - case IEEE80211_ROC_TYPE_MGMT_TX: - activity = ROC_ACTIVITY_P2P_NEG; - break; - default: - WARN_ONCE(1, "Got an invalid P2P ROC type\n"); - return -EINVAL; + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + switch (type) { + case IEEE80211_ROC_TYPE_NORMAL: + activity = ROC_ACTIVITY_P2P_DISC; + break; + case IEEE80211_ROC_TYPE_MGMT_TX: + activity = ROC_ACTIVITY_P2P_NEG; + break; + default: + WARN_ONCE(1, "Got an invalid P2P ROC type\n"); + return -EINVAL; + } + } else { + activity = ROC_ACTIVITY_HOTSPOT; } - if (WARN_ON(mld_vif->roc_activity != ROC_NUM_ACTIVITIES)) + /* The FW supports one ROC of each type simultaneously */ + if (WARN_ON(iwl_mld_find_roc_vif(mld, activity))) return -EBUSY; - /* No MLO on P2P device */ - aux_sta = &mld_vif->deflink.aux_sta; + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_block_roc, + &ret); + if (ret) + return ret; ret = iwl_mld_add_aux_sta(mld, aux_sta); if (ret) @@ -91,9 +134,6 @@ int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, cmd.channel_info.channel = cpu_to_le32(channel->hw_value); cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band); cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20; - /* TODO: task=Hotspot 2.0, revisit those parameters when we add an ROC - * on the BSS vif - */ cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY); cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); @@ -105,6 +145,7 @@ int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, IWL_ERR(mld, "Couldn't send the ROC_CMD\n"); return ret; } + mld_vif->roc_activity = activity; return 0; @@ -136,9 +177,9 @@ static void iwl_mld_destroy_roc(struct iwl_mld *mld, * we can flush the Tx on the queues */ - iwl_mld_flush_link_sta_txqs(mld, mld_vif->deflink.aux_sta.sta_id); + iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id); - iwl_mld_remove_aux_sta(mld, vif, &vif->bss_conf); + iwl_mld_remove_aux_sta(mld, vif); } int iwl_mld_cancel_roc(struct ieee80211_hw *hw, @@ -156,8 +197,8 @@ int iwl_mld_cancel_roc(struct ieee80211_hw *hw, lockdep_assert_wiphy(mld->wiphy); - /* TODO: task=Hotspot 2.0 */ - if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE)) + if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE && + vif->type != NL80211_IFTYPE_STATION)) return -EOPNOTSUPP; /* No roc activity running it's probably already done */ @@ -192,10 +233,10 @@ void iwl_mld_handle_roc_notif(struct iwl_mld *mld, { const struct iwl_roc_notif *notif = (void *)pkt->data; u32 activity = le32_to_cpu(notif->activity); - /* TODO: task=Hotspot 2.0 - roc can run on BSS */ - struct ieee80211_vif *vif = mld->p2p_device_vif; struct iwl_mld_vif *mld_vif; + struct ieee80211_vif *vif; + vif = iwl_mld_find_roc_vif(mld, activity); if (WARN_ON(!vif)) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c index c4f189bcece2..b6dedd1ecd4d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -33,17 +33,17 @@ struct iwl_mld_rx_phy_data { u32 gp2_on_air_rise; u16 phy_info; u8 energy_a, energy_b; - u8 channel; }; static void -iwl_mld_fill_phy_data(struct iwl_rx_mpdu_desc *desc, +iwl_mld_fill_phy_data(struct iwl_mld *mld, + struct iwl_rx_mpdu_desc *desc, struct iwl_mld_rx_phy_data *phy_data) { phy_data->phy_info = le16_to_cpu(desc->phy_info); - phy_data->rate_n_flags = le32_to_cpu(desc->v3.rate_n_flags); + phy_data->rate_n_flags = iwl_v3_rate_from_v2_v3(desc->v3.rate_n_flags, + mld->fw_rates_ver_3); phy_data->gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise); - phy_data->channel = desc->v3.channel; phy_data->energy_a = desc->v3.energy_a; phy_data->energy_b = desc->v3.energy_b; phy_data->data0 = desc->v3.phy_data0; @@ -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,10 +1219,11 @@ 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, - struct iwl_mld_rx_phy_data *phy_data, - struct iwl_rx_mpdu_desc *mpdu_desc, +/* 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) { struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); @@ -1172,51 +1232,19 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK); bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK; - if (WARN_ON_ONCE(phy_data->with_data && (!mpdu_desc || !hdr))) - return; - - /* Keep packets with CRC errors (and with overrun) for monitor mode - * (otherwise the firmware discards them) but mark them as bad. - */ - if (phy_data->with_data && - (!(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_CRC_OK)) || - !(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_OVERRUN_OK)))) { - IWL_DEBUG_RX(mld, "Bad CRC or FIFO: 0x%08X.\n", - le32_to_cpu(mpdu_desc->status)); - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - } - phy_data->info_type = IWL_RX_PHY_INFO_TYPE_NONE; - if (phy_data->with_data && - likely(!(phy_data->phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { - rx_status->mactime = - le64_to_cpu(mpdu_desc->v3.tsf_on_air_rise); - - /* TSF as indicated by the firmware is at INA time */ - rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; - } else { + if (phy_data->phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) phy_data->info_type = le32_get_bits(phy_data->data1, IWL_RX_PHY_DATA1_INFO_TYPE_MASK); - } - - /* management stuff on default queue */ - if (!queue && phy_data->with_data && - unlikely(ieee80211_is_beacon(hdr->frame_control) || - ieee80211_is_probe_resp(hdr->frame_control))) { - rx_status->boottime_ns = ktime_get_boottime_ns(); - - if (mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_ENABLED) - mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_FOUND; - } /* set the preamble flag if appropriate */ - if (format == RATE_MCS_CCK_MSK && + if (format == RATE_MCS_MOD_TYPE_CCK && phy_data->phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE) rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; - 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) { @@ -1237,7 +1265,7 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, } /* must be before L-SIG data */ - if (format == RATE_MCS_HE_MSK) + if (format == RATE_MCS_MOD_TYPE_HE) iwl_mld_rx_he(mld, skb, phy_data, queue); iwl_mld_decode_lsig(skb, phy_data); @@ -1245,40 +1273,53 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, rx_status->device_timestamp = phy_data->gp2_on_air_rise; /* using TLV format and must be after all fixed len fields */ - if (format == RATE_MCS_EHT_MSK) + if (format == RATE_MCS_MOD_TYPE_EHT) iwl_mld_rx_eht(mld, skb, phy_data, queue); #ifdef CONFIG_IWLWIFI_DEBUGFS - if (unlikely(mld->monitor.on)) + if (unlikely(mld->monitor.on)) { iwl_mld_add_rtap_sniffer_config(mld, skb); + + if (mld->monitor.ptp_time) { + u64 adj_time = + iwl_mld_ptp_get_adj_time(mld, + phy_data->gp2_on_air_rise * + NSEC_PER_USEC); + + rx_status->mactime = div64_u64(adj_time, NSEC_PER_USEC); + rx_status->flag |= RX_FLAG_MACTIME_IS_RTAP_TS64; + rx_status->flag &= ~RX_FLAG_MACTIME; + } + } #endif - if (format != RATE_MCS_CCK_MSK && is_sgi) + if (format != RATE_MCS_MOD_TYPE_CCK && is_sgi) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rate_n_flags & RATE_MCS_LDPC_MSK) rx_status->enc_flags |= RX_ENC_FLAG_LDPC; switch (format) { - case RATE_MCS_HT_MSK: + case RATE_MCS_MOD_TYPE_HT: rx_status->encoding = RX_ENC_HT; rx_status->rate_idx = RATE_HT_MCS_INDEX(rate_n_flags); rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; break; - case RATE_MCS_VHT_MSK: - case RATE_MCS_HE_MSK: - case RATE_MCS_EHT_MSK: - if (format == RATE_MCS_VHT_MSK) { + case RATE_MCS_MOD_TYPE_VHT: + case RATE_MCS_MOD_TYPE_HE: + case RATE_MCS_MOD_TYPE_EHT: + if (format == RATE_MCS_MOD_TYPE_VHT) { rx_status->encoding = RX_ENC_VHT; - } else if (format == RATE_MCS_HE_MSK) { + } else if (format == RATE_MCS_MOD_TYPE_HE) { rx_status->encoding = RX_ENC_HE; rx_status->he_dcm = !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK); - } else if (format == RATE_MCS_EHT_MSK) { + } else if (format == RATE_MCS_MOD_TYPE_EHT) { rx_status->encoding = RX_ENC_EHT; } - rx_status->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; + rx_status->nss = u32_get_bits(rate_n_flags, + RATE_MCS_NSS_MSK) + 1; rx_status->rate_idx = rate_n_flags & RATE_MCS_CODE_MSK; rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; break; @@ -1735,15 +1776,11 @@ static void iwl_mld_rx_update_ampdu_ref(struct iwl_mld *mld, } static void -iwl_mld_fill_rx_status_band_freq(struct iwl_mld_rx_phy_data *phy_data, - struct iwl_rx_mpdu_desc *mpdu_desc, - struct ieee80211_rx_status *rx_status) +iwl_mld_fill_rx_status_band_freq(struct ieee80211_rx_status *rx_status, + u8 band, u8 channel) { - enum nl80211_band band; - - band = BAND_IN_RX_STATUS(mpdu_desc->mac_phy_idx); rx_status->band = iwl_mld_phy_band_to_nl80211(band); - rx_status->freq = ieee80211_channel_to_frequency(phy_data->channel, + rx_status->freq = ieee80211_channel_to_frequency(channel, rx_status->band); } @@ -1758,7 +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; + 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; @@ -1788,7 +1825,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, hdr = (void *)(pkt->data + mpdu_desc_size); - iwl_mld_fill_phy_data(mpdu_desc, &phy_data); + iwl_mld_fill_phy_data(mld, mpdu_desc, &phy_data); if (mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) { /* If the device inserted padding it means that (it thought) @@ -1802,7 +1839,11 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, rx_status = IEEE80211_SKB_RXCB(skb); /* this is needed early */ - iwl_mld_fill_rx_status_band_freq(&phy_data, mpdu_desc, rx_status); + band = u8_get_bits(mpdu_desc->mac_phy_band, + IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK); + iwl_mld_fill_rx_status_band_freq(rx_status, band, + mpdu_desc->v3.channel); + rcu_read_lock(); @@ -1814,7 +1855,39 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, if (!queue && (phy_data.phy_info & IWL_RX_MPDU_PHY_AMPDU)) iwl_mld_rx_update_ampdu_ref(mld, &phy_data, rx_status); - iwl_mld_rx_fill_status(mld, skb, &phy_data, mpdu_desc, hdr, queue); + /* Keep packets with CRC errors (and with overrun) for monitor mode + * (otherwise the firmware discards them) but mark them as bad. + */ + if (!(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_CRC_OK)) || + !(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_OVERRUN_OK))) { + IWL_DEBUG_RX(mld, "Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(mpdu_desc->status)); + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + } + + if (likely(!(phy_data.phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { + rx_status->mactime = + le64_to_cpu(mpdu_desc->v3.tsf_on_air_rise); + + /* TSF as indicated by the firmware is at INA time */ + rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; + } + + /* management stuff on default queue */ + if (!queue && unlikely(ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control))) { + rx_status->boottime_ns = ktime_get_boottime_ns(); + + if (mld->scan.pass_all_sched_res == + SCHED_SCAN_PASS_ALL_STATE_ENABLED) + mld->scan.pass_all_sched_res = + SCHED_SCAN_PASS_ALL_STATE_FOUND; + } + + 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)) @@ -1857,7 +1930,7 @@ void iwl_mld_sync_rx_queues(struct iwl_mld *mld, enum iwl_mld_internal_rxq_notif_type type, const void *notif_payload, u32 notif_payload_size) { - u8 num_rx_queues = mld->trans->num_rx_queues; + u8 num_rx_queues = mld->trans->info.num_rxqs; struct { struct iwl_rxq_sync_cmd sync_cmd; struct iwl_mld_internal_rxq_notif notif; @@ -1956,6 +2029,7 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, struct ieee80211_rx_status *rx_status; struct sk_buff *skb; u32 format, rssi; + u8 channel; if (unlikely(mld->fw_status.in_hw_restart)) return; @@ -1968,14 +2042,16 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, desc = (void *)pkt->data; rssi = le32_to_cpu(desc->rssi); + channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); + phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK); phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK); - phy_data.channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); phy_data.data0 = desc->phy_info[0]; phy_data.data1 = desc->phy_info[1]; phy_data.phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD; phy_data.gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time); - phy_data.rate_n_flags = le32_to_cpu(desc->rate); + phy_data.rate_n_flags = iwl_v3_rate_from_v2_v3(desc->rate, + mld->fw_rates_ver_3); phy_data.with_data = false; BUILD_BUG_ON(sizeof(phy_data.rx_vec) != sizeof(desc->rx_vec)); @@ -2018,13 +2094,14 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, break; } - rx_status->band = phy_data.channel > 14 ? NL80211_BAND_5GHZ : + rx_status->band = channel > 14 ? NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; - rx_status->freq = ieee80211_channel_to_frequency(phy_data.channel, + rx_status->freq = ieee80211_channel_to_frequency(channel, rx_status->band); - iwl_mld_rx_fill_status(mld, skb, &phy_data, NULL, NULL, queue); + /* 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 @@ -2037,17 +2114,17 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, * may be up to 8 spatial streams. */ switch (format) { - case RATE_MCS_VHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: rx_status->nss = le32_get_bits(desc->rx_vec[0], RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK) + 1; break; - case RATE_MCS_HE_MSK: + case RATE_MCS_MOD_TYPE_HE: rx_status->nss = le32_get_bits(desc->rx_vec[0], RX_NO_DATA_RX_VEC0_HE_NSTS_MSK) + 1; break; - case RATE_MCS_EHT_MSK: + case RATE_MCS_MOD_TYPE_EHT: rx_status->nss = le32_get_bits(desc->rx_vec[2], RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK) + 1; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index 7ec04318ec2f..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); @@ -1920,6 +1957,9 @@ void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, IWL_DEBUG_SCAN(mld, "Scan link is no longer valid\n"); ieee80211_scan_completed(mld->hw, &info); + + /* Scan is over, we can check again the tpt counters */ + iwl_mld_stop_ignoring_tpt_updates(mld); } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_SCHED) { ieee80211_sched_scan_stopped(mld->hw); mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; @@ -2006,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/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index 332a7aecec2d..8fb51209b4a6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -401,9 +401,11 @@ static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta) static int iwl_mld_send_sta_cmd(struct iwl_mld *mld, const struct iwl_sta_cfg_cmd *cmd) { - int ret = iwl_mld_send_cmd_pdu(mld, - WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), - cmd); + u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD); + int cmd_len = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0) > 1 ? + sizeof(*cmd) : + sizeof(struct iwl_sta_cfg_cmd_v1); + int ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, cmd_len); if (ret) IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret); return ret; @@ -660,7 +662,7 @@ iwl_mld_alloc_dup_data(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta) if (mld->fw_status.in_hw_restart) return 0; - dup_data = kcalloc(mld->trans->num_rx_queues, sizeof(*dup_data), + dup_data = kcalloc(mld->trans->info.num_rxqs, sizeof(*dup_data), GFP_KERNEL); if (!dup_data) return -ENOMEM; @@ -673,7 +675,7 @@ iwl_mld_alloc_dup_data(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta) * This thus allows receiving a packet with seqno 0 and the * retry bit set as the very first packet on a new TID. */ - for (int q = 0; q < mld->trans->num_rx_queues; q++) + for (int q = 0; q < mld->trans->info.num_rxqs; q++) memset(dup_data[q].last_seq, 0xff, sizeof(dup_data[q].last_seq)); mld_sta->dup_data = dup_data; @@ -695,13 +697,13 @@ static void iwl_mld_alloc_mpdu_counters(struct iwl_mld *mld, sta->tdls || !ieee80211_vif_is_mld(vif)) return; - mld_sta->mpdu_counters = kcalloc(mld->trans->num_rx_queues, + mld_sta->mpdu_counters = kcalloc(mld->trans->info.num_rxqs, sizeof(*mld_sta->mpdu_counters), GFP_KERNEL); if (!mld_sta->mpdu_counters) return; - for (int q = 0; q < mld->trans->num_rx_queues; q++) + for (int q = 0; q < mld->trans->info.num_rxqs; q++) spin_lock_init(&mld_sta->mpdu_counters[q].lock); } @@ -947,7 +949,7 @@ static int iwl_mld_allocate_internal_txq(struct iwl_mld *mld, int queue, size; size = max_t(u32, IWL_MGMT_QUEUE_SIZE, - mld->trans->cfg->min_txq_size); + mld->trans->mac_cfg->base->min_txq_size); queue = iwl_trans_txq_alloc(mld->trans, 0, sta_mask, tid, size, IWL_WATCHDOG_DISABLED); @@ -1087,6 +1089,24 @@ int iwl_mld_add_aux_sta(struct iwl_mld *mld, 0, NULL, IWL_MAX_TID_COUNT); } +int iwl_mld_add_mon_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + if (WARN_ON(vif->type != NL80211_IFTYPE_MONITOR)) + return -EINVAL; + + return iwl_mld_add_internal_sta(mld, &mld_link->mon_sta, + STATION_TYPE_BCAST_MGMT, + mld_link->fw_id, NULL, + IWL_MAX_TID_COUNT); +} + static void iwl_mld_remove_internal_sta(struct iwl_mld *mld, struct iwl_mld_int_sta *internal_sta, bool flush, u8 tid) @@ -1140,6 +1160,19 @@ void iwl_mld_remove_mcast_sta(struct iwl_mld *mld, } void iwl_mld_remove_aux_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE && + vif->type != NL80211_IFTYPE_STATION)) + return; + + iwl_mld_remove_internal_sta(mld, &mld_vif->aux_sta, false, + IWL_MAX_TID_COUNT); +} + +void iwl_mld_remove_mon_sta(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link) { @@ -1148,11 +1181,10 @@ void iwl_mld_remove_aux_sta(struct iwl_mld *mld, if (WARN_ON(!mld_link)) return; - /* TODO: Hotspot 2.0 */ - if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE)) + if (WARN_ON(vif->type != NL80211_IFTYPE_MONITOR)) return; - iwl_mld_remove_internal_sta(mld, &mld_link->aux_sta, false, + iwl_mld_remove_internal_sta(mld, &mld_link->mon_sta, false, IWL_MAX_TID_COUNT); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h index ddcffd7b9fde..1897b121aae2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h @@ -247,6 +247,10 @@ int iwl_mld_add_mcast_sta(struct iwl_mld *mld, int iwl_mld_add_aux_sta(struct iwl_mld *mld, struct iwl_mld_int_sta *internal_sta); +int iwl_mld_add_mon_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + void iwl_mld_remove_bcast_sta(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link); @@ -256,6 +260,9 @@ void iwl_mld_remove_mcast_sta(struct iwl_mld *mld, struct ieee80211_bss_conf *link); void iwl_mld_remove_aux_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +void iwl_mld_remove_mon_sta(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index 0715bbc31031..cbc64db5eab6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -191,11 +191,12 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, break; } - if (format == RATE_MCS_CCK_MSK || format == RATE_MCS_LEGACY_OFDM_MSK) { + if (format == RATE_MCS_MOD_TYPE_CCK || + format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) { int rate = u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK); /* add the offset needed to get to the legacy ofdm indices */ - if (format == RATE_MCS_LEGACY_OFDM_MSK) + if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) rate += IWL_FIRST_OFDM_RATE; switch (rate) { @@ -240,7 +241,7 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, rinfo->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; - if (format == RATE_MCS_HT_MSK) + if (format == RATE_MCS_MOD_TYPE_HT) rinfo->mcs = RATE_HT_MCS_INDEX(rate_n_flags); else rinfo->mcs = u32_get_bits(rate_n_flags, RATE_MCS_CODE_MSK); @@ -249,10 +250,10 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; switch (format) { - case RATE_MCS_EHT_MSK: + case RATE_MCS_MOD_TYPE_EHT: rinfo->flags |= RATE_INFO_FLAGS_EHT_MCS; break; - case RATE_MCS_HE_MSK: + case RATE_MCS_MOD_TYPE_HE: gi_ltf = u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK); rinfo->flags |= RATE_INFO_FLAGS_HE_MCS; @@ -293,10 +294,10 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK) rinfo->he_dcm = 1; break; - case RATE_MCS_HT_MSK: + case RATE_MCS_MOD_TYPE_HT: rinfo->flags |= RATE_INFO_FLAGS_MCS; break; - case RATE_MCS_VHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; break; } @@ -467,12 +468,18 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, old_load = phy->avg_channel_load_not_by_us; new_load = le32_to_cpu(per_phy[phy->fw_id].channel_load_not_by_us); - if (IWL_FW_CHECK(phy->mld, new_load > 100, "Invalid channel load %u\n", - new_load)) + + if (IWL_FW_CHECK(phy->mld, + new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD && + new_load > 100, + "Invalid channel load %u\n", new_load)) return; - /* give a weight of 0.5 for the old value */ - phy->avg_channel_load_not_by_us = (new_load >> 1) + (old_load >> 1); + if (new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD) { + /* update giving a weight of 0.5 for the old value */ + phy->avg_channel_load_not_by_us = (new_load >> 1) + + (old_load >> 1); + } iwl_mld_emlsr_check_chan_load(hw, phy, old_load); } @@ -501,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/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c index 1fd664be1a7c..29b0248cec3d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include <kunit/test.h> #include <kunit/static_stub.h> @@ -474,14 +474,14 @@ static struct iwl_rx_mpdu_desc *setup_mpdu_desc(void) KUNIT_ALLOC_AND_ASSERT(test, mpdu_desc); mpdu_desc->reorder_data |= - cpu_to_le32(FIELD_PREP(IWL_RX_MPDU_REORDER_BAID_MASK, - param->rx_pkt.baid)); + le32_encode_bits(param->rx_pkt.baid, + IWL_RX_MPDU_REORDER_BAID_MASK); mpdu_desc->reorder_data |= - cpu_to_le32(FIELD_PREP(IWL_RX_MPDU_REORDER_SN_MASK, - param->rx_pkt.sn)); + le32_encode_bits(param->rx_pkt.sn, + IWL_RX_MPDU_REORDER_SN_MASK); mpdu_desc->reorder_data |= - cpu_to_le32(FIELD_PREP(IWL_RX_MPDU_REORDER_NSSN_MASK, - param->rx_pkt.nssn)); + le32_encode_bits(param->rx_pkt.nssn, + IWL_RX_MPDU_REORDER_NSSN_MASK); if (param->rx_pkt.old_sn) mpdu_desc->reorder_data |= cpu_to_le32(IWL_RX_MPDU_REORDER_BA_OLD_SN); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c index 4e189bf8b3fb..0e3b9417dd63 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include <kunit/test.h> @@ -30,10 +30,10 @@ static void test_hcmd_names_sorted(struct kunit *test) static void test_hcmd_names_for_rx(struct kunit *test) { static struct iwl_trans t = { - .command_groups = iwl_mld_groups, + .conf.command_groups = iwl_mld_groups, }; - t.command_groups_size = global_iwl_mld_goups_size; + t.conf.command_groups_size = global_iwl_mld_goups_size; for (unsigned int i = 0; i < iwl_mld_rx_handlers_num; i++) { const struct iwl_rx_handler *rxh; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c index 295dcfd3f85d..766c24db3613 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c @@ -32,7 +32,7 @@ static const struct link_grading_test_case { .desc = "channel util of 128 (50%)", .input.link = { .link_id = 0, - .chandef = &chandef_2ghz, + .chandef = &chandef_2ghz_20mhz, .active = false, .has_chan_util_elem = true, .chan_util = 128, @@ -43,7 +43,7 @@ static const struct link_grading_test_case { .desc = "channel util of 180 (70%)", .input.link = { .link_id = 0, - .chandef = &chandef_2ghz, + .chandef = &chandef_2ghz_20mhz, .active = false, .has_chan_util_elem = true, .chan_util = 180, @@ -54,7 +54,7 @@ static const struct link_grading_test_case { .desc = "channel util of 180 (70%), channel load by us of 10%", .input.link = { .link_id = 0, - .chandef = &chandef_2ghz, + .chandef = &chandef_2ghz_20mhz, .has_chan_util_elem = true, .chan_util = 180, .active = true, @@ -66,7 +66,7 @@ static const struct link_grading_test_case { .desc = "no channel util element", .input.link = { .link_id = 0, - .chandef = &chandef_2ghz, + .chandef = &chandef_2ghz_20mhz, .active = true, }, .expected_grade = 120, @@ -132,7 +132,7 @@ static void test_link_grading(struct kunit *test) bool active = test_param->input.link.active; u16 valid_links; struct iwl_mld_kunit_link assoc_link = { - .band = test_param->input.link.chandef->chan->band, + .chandef = test_param->input.link.chandef, }; /* If the link is not active, use a different link as the assoc link */ @@ -172,103 +172,139 @@ static struct kunit_suite link_selection = { kunit_test_suite(link_selection); -static const struct channel_load_case { +static const struct link_pair_case { const char *desc; + const struct cfg80211_chan_def *chandef_a, *chandef_b; bool low_latency_vif; u32 chan_load_not_by_us; - enum nl80211_chan_width bw_a; - enum nl80211_chan_width bw_b; bool primary_link_active; - bool expected_result; -} channel_load_cases[] = { + u32 expected_result; +} link_pair_cases[] = { { .desc = "Unequal bandwidth, primary link inactive, EMLSR not allowed", .low_latency_vif = false, .primary_link_active = false, - .bw_a = NL80211_CHAN_WIDTH_40, - .bw_b = NL80211_CHAN_WIDTH_20, - .expected_result = false, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_6ghz_20mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD, }, { .desc = "Equal bandwidths, sufficient channel load, EMLSR allowed", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 11, - .bw_a = NL80211_CHAN_WIDTH_40, - .bw_b = NL80211_CHAN_WIDTH_40, - .expected_result = true, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_6ghz_40mhz, + .expected_result = 0, }, { .desc = "Equal bandwidths, insufficient channel load, EMLSR not allowed", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 6, - .bw_a = NL80211_CHAN_WIDTH_80, - .bw_b = NL80211_CHAN_WIDTH_80, - .expected_result = false, + .chandef_a = &chandef_5ghz_80mhz, + .chandef_b = &chandef_6ghz_80mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD, }, { .desc = "Low latency VIF, sufficient channel load, EMLSR allowed", .low_latency_vif = true, .primary_link_active = true, .chan_load_not_by_us = 6, - .bw_a = NL80211_CHAN_WIDTH_160, - .bw_b = NL80211_CHAN_WIDTH_160, - .expected_result = true, + .chandef_a = &chandef_5ghz_160mhz, + .chandef_b = &chandef_6ghz_160mhz, + .expected_result = 0, }, { .desc = "Different bandwidths (2x ratio), primary link load permits EMLSR", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 30, - .bw_a = NL80211_CHAN_WIDTH_40, - .bw_b = NL80211_CHAN_WIDTH_20, - .expected_result = true, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_6ghz_20mhz, + .expected_result = 0, }, { .desc = "Different bandwidths (4x ratio), primary link load permits EMLSR", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 45, - .bw_a = NL80211_CHAN_WIDTH_80, - .bw_b = NL80211_CHAN_WIDTH_20, - .expected_result = true, + .chandef_a = &chandef_5ghz_80mhz, + .chandef_b = &chandef_6ghz_20mhz, + .expected_result = 0, }, { .desc = "Different bandwidths (16x ratio), primary link load insufficient", .low_latency_vif = false, .primary_link_active = true, .chan_load_not_by_us = 45, - .bw_a = NL80211_CHAN_WIDTH_320, - .bw_b = NL80211_CHAN_WIDTH_20, - .expected_result = false, + .chandef_a = &chandef_6ghz_320mhz, + .chandef_b = &chandef_5ghz_20mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD, + }, + { + .desc = "Same band not allowed (2.4 GHz)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_2ghz_20mhz, + .chandef_b = &chandef_2ghz_11_20mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND, + }, + { + .desc = "Same band not allowed (5 GHz)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_5ghz_40mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND, + }, + { + .desc = "Same band allowed (5 GHz separated)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_5ghz_120_40mhz, + .expected_result = 0, + }, + { + .desc = "Same band not allowed (6 GHz)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_6ghz_160mhz, + .chandef_b = &chandef_6ghz_221_160mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND, }, }; -KUNIT_ARRAY_PARAM_DESC(channel_load, channel_load_cases, desc); +KUNIT_ARRAY_PARAM_DESC(link_pair, link_pair_cases, desc); -static void test_iwl_mld_channel_load_allows_emlsr(struct kunit *test) +static void test_iwl_mld_link_pair_allows_emlsr(struct kunit *test) { - const struct channel_load_case *params = test->param_value; + const struct link_pair_case *params = test->param_value; struct iwl_mld *mld = test->priv; struct ieee80211_vif *vif; - struct cfg80211_chan_def chandef_a, chandef_b; - struct iwl_mld_link_sel_data a = {.chandef = &chandef_a, - .link_id = 4}; - struct iwl_mld_link_sel_data b = {.chandef = &chandef_b, - .link_id = 5}; + /* link A is the primary and link B is the secondary */ + struct iwl_mld_link_sel_data a = { + .chandef = params->chandef_a, + .link_id = 4, + }; + struct iwl_mld_link_sel_data b = { + .chandef = params->chandef_b, + .link_id = 5, + }; struct iwl_mld_kunit_link assoc_link = { + .chandef = params->primary_link_active ? a.chandef : b.chandef, .id = params->primary_link_active ? a.link_id : b.link_id, - .bandwidth = params->primary_link_active ? params->bw_a : params->bw_b, }; - bool result; + u32 result; vif = iwlmld_kunit_setup_mlo_assoc(BIT(a.link_id) | BIT(b.link_id), &assoc_link); - chandef_a.width = params->bw_a; - chandef_b.width = params->bw_b; - if (params->low_latency_vif) iwl_mld_vif_from_mac80211(vif)->low_latency_causes = 1; @@ -282,22 +318,22 @@ static void test_iwl_mld_channel_load_allows_emlsr(struct kunit *test) phy->avg_channel_load_not_by_us = params->chan_load_not_by_us; } - result = iwl_mld_channel_load_allows_emlsr(mld, vif, &a, &b); + result = iwl_mld_emlsr_pair_state(vif, &a, &b); wiphy_unlock(mld->wiphy); KUNIT_EXPECT_EQ(test, result, params->expected_result); } -static struct kunit_case channel_load_criteria_test_cases[] = { - KUNIT_CASE_PARAM(test_iwl_mld_channel_load_allows_emlsr, channel_load_gen_params), +static struct kunit_case link_pair_criteria_test_cases[] = { + KUNIT_CASE_PARAM(test_iwl_mld_link_pair_allows_emlsr, link_pair_gen_params), {} }; -static struct kunit_suite channel_load_criteria_tests = { - .name = "iwlmld_channel_load_allows_emlsr", - .test_cases = channel_load_criteria_test_cases, +static struct kunit_suite link_pair_criteria_tests = { + .name = "iwlmld_link_pair_allows_emlsr", + .test_cases = link_pair_criteria_test_cases, .init = iwlmld_kunit_test_init, }; -kunit_test_suite(channel_load_criteria_tests); +kunit_test_suite(link_pair_criteria_tests); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c index 4a4eaa134bd3..69a0d67858bf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c @@ -63,11 +63,11 @@ static void test_missed_beacon(struct kunit *test) struct iwl_rx_packet *pkt; struct iwl_mld_kunit_link link1 = { .id = 0, - .band = NL80211_BAND_6GHZ, + .chandef = &chandef_6ghz_160mhz, }; struct iwl_mld_kunit_link link2 = { .id = 1, - .band = NL80211_BAND_5GHZ, + .chandef = &chandef_5ghz_80mhz, }; kunit_activate_static_stub(test, ieee80211_connection_loss, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c index 9712ee696509..26cf27be762d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -24,7 +24,7 @@ int iwlmld_kunit_test_init(struct kunit *test) { struct iwl_mld *mld; struct iwl_trans *trans; - const struct iwl_cfg *cfg; + const struct iwl_rf_cfg *cfg; struct iwl_fw *fw; struct ieee80211_hw *hw; @@ -146,7 +146,7 @@ iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id) } struct ieee80211_chanctx_conf * -iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def) +iwlmld_kunit_add_chanctx(const struct cfg80211_chan_def *def) { struct kunit *test = kunit_get_current_test(); struct iwl_mld *mld = test->priv; @@ -346,8 +346,7 @@ iwlmld_kunit_setup_assoc(bool mlo, struct iwl_mld_kunit_link *assoc_link) else link = &vif->bss_conf; - chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->band, - assoc_link->bandwidth); + chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->chandef); wiphy_lock(mld->wiphy); iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); @@ -428,7 +427,7 @@ struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, link = wiphy_dereference(mld->wiphy, vif->link_conf[link2->id]); KUNIT_EXPECT_NOT_NULL(test, link); - chan_ctx = iwlmld_kunit_add_chanctx(link2->band, link2->bandwidth); + chan_ctx = iwlmld_kunit_add_chanctx(link2->chandef); iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); wiphy_unlock(mld->wiphy); @@ -472,3 +471,33 @@ struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif, return iwl_mld_phy_from_mac80211(chanctx); } + +static const struct chandef_case { + const char *desc; + const struct cfg80211_chan_def *chandef; +} chandef_cases[] = { +#define CHANDEF(c, ...) { .desc = "chandef " #c " valid", .chandef = &c, }, + CHANDEF_LIST +#undef CHANDEF +}; + +KUNIT_ARRAY_PARAM_DESC(chandef, chandef_cases, desc); + +static void test_iwl_mld_chandef_valid(struct kunit *test) +{ + const struct chandef_case *params = test->param_value; + + KUNIT_EXPECT_EQ(test, true, cfg80211_chandef_valid(params->chandef)); +} + +static struct kunit_case chandef_test_cases[] = { + KUNIT_CASE_PARAM(test_iwl_mld_chandef_valid, chandef_gen_params), + {} +}; + +static struct kunit_suite chandef_tests = { + .name = "iwlmld_valid_test_chandefs", + .test_cases = chandef_test_cases, +}; + +kunit_test_suite(chandef_tests); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h index d3723653cf1b..edf8eef4e81a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -14,9 +14,8 @@ struct iwl_mld; int iwlmld_kunit_test_init(struct kunit *test); struct iwl_mld_kunit_link { + const struct cfg80211_chan_def *chandef; u8 id; - enum nl80211_band band; - enum nl80211_chan_width bandwidth; }; enum nl80211_iftype; @@ -42,50 +41,57 @@ static struct ieee80211_channel _name = { \ .hw_value = (_freq), \ } -#define CHANDEF(_name, _channel, _freq1, _width) \ -__maybe_unused static struct cfg80211_chan_def _name = { \ - .chan = &(_channel), \ - .center_freq1 = (_freq1), \ - .width = (_width), \ -} - CHANNEL(chan_2ghz, NL80211_BAND_2GHZ, 2412); +CHANNEL(chan_2ghz_11, NL80211_BAND_2GHZ, 2462); CHANNEL(chan_5ghz, NL80211_BAND_5GHZ, 5200); +CHANNEL(chan_5ghz_120, NL80211_BAND_5GHZ, 5600); CHANNEL(chan_6ghz, NL80211_BAND_6GHZ, 6115); +CHANNEL(chan_6ghz_221, NL80211_BAND_6GHZ, 7055); /* Feel free to add more */ +#undef CHANNEL + +#define CHANDEF_LIST \ + CHANDEF(chandef_2ghz_20mhz, chan_2ghz, 2412, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_2ghz_40mhz, chan_2ghz, 2422, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_2ghz_11_20mhz, chan_2ghz_11, 2462, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_5ghz_20mhz, chan_5ghz, 5200, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_5ghz_40mhz, chan_5ghz, 5210, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_5ghz_80mhz, chan_5ghz, 5210, \ + NL80211_CHAN_WIDTH_80) \ + CHANDEF(chandef_5ghz_160mhz, chan_5ghz, 5250, \ + NL80211_CHAN_WIDTH_160) \ + CHANDEF(chandef_5ghz_120_40mhz, chan_5ghz_120, 5610, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_6ghz_20mhz, chan_6ghz, 6115, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_6ghz_40mhz, chan_6ghz, 6125, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_6ghz_80mhz, chan_6ghz, 6145, \ + NL80211_CHAN_WIDTH_80) \ + CHANDEF(chandef_6ghz_160mhz, chan_6ghz, 6185, \ + NL80211_CHAN_WIDTH_160) \ + CHANDEF(chandef_6ghz_320mhz, chan_6ghz, 6105, \ + NL80211_CHAN_WIDTH_320) \ + CHANDEF(chandef_6ghz_221_160mhz, chan_6ghz_221, 6985, \ + NL80211_CHAN_WIDTH_160) \ + /* Feel free to add more */ -CHANDEF(chandef_2ghz, chan_2ghz, 2412, NL80211_CHAN_WIDTH_20); -CHANDEF(chandef_5ghz, chan_5ghz, 5200, NL80211_CHAN_WIDTH_40); -CHANDEF(chandef_6ghz, chan_6ghz, 6115, NL80211_CHAN_WIDTH_160); -/* Feel free to add more */ - -//struct cfg80211_chan_def; +#define CHANDEF(_name, _channel, _freq1, _width) \ +__maybe_unused static const struct cfg80211_chan_def _name = { \ + .chan = &(_channel), \ + .center_freq1 = (_freq1), \ + .width = (_width), \ +}; +CHANDEF_LIST +#undef CHANDEF struct ieee80211_chanctx_conf * -iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def); - -static inline struct ieee80211_chanctx_conf * -iwlmld_kunit_add_chanctx(enum nl80211_band band, enum nl80211_chan_width width) -{ - struct cfg80211_chan_def chandef; - - switch (band) { - case NL80211_BAND_2GHZ: - chandef = chandef_2ghz; - break; - case NL80211_BAND_5GHZ: - chandef = chandef_5ghz; - break; - default: - case NL80211_BAND_6GHZ: - chandef = chandef_6ghz; - break; - } - - chandef.width = width; - - return iwlmld_kunit_add_chanctx_from_def(&chandef); -} +iwlmld_kunit_add_chanctx(const struct cfg80211_chan_def *def); void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, struct ieee80211_bss_conf *link, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c index 1909953a9be9..f8a8c35066be 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c @@ -13,6 +13,9 @@ #include "mld.h" #include "hcmd.h" +#define IWL_MLD_NUM_CTDP_STEPS 20 +#define IWL_MLD_MIN_CTDP_BUDGET_MW 150 + #define IWL_MLD_CT_KILL_DURATION (5 * HZ) void iwl_mld_handle_ct_kill_notif(struct iwl_mld *mld, @@ -116,8 +119,8 @@ free_resp: static int compare_temps(const void *a, const void *b) { - return ((s16)le16_to_cpu(*(__le16 *)a) - - (s16)le16_to_cpu(*(__le16 *)b)); + return ((s16)le16_to_cpu(*(const __le16 *)a) - + (s16)le16_to_cpu(*(const __le16 *)b)); } struct iwl_trip_walk_data { @@ -272,43 +275,27 @@ static void iwl_mld_thermal_zone_register(struct iwl_mld *mld) } } -/* budget in mWatt */ -static const u32 iwl_mld_cdev_budgets[] = { - 2400, /* cooling state 0 */ - 2000, /* cooling state 1 */ - 1800, /* cooling state 2 */ - 1600, /* cooling state 3 */ - 1400, /* cooling state 4 */ - 1200, /* cooling state 5 */ - 1000, /* cooling state 6 */ - 900, /* cooling state 7 */ - 800, /* cooling state 8 */ - 700, /* cooling state 9 */ - 650, /* cooling state 10 */ - 600, /* cooling state 11 */ - 550, /* cooling state 12 */ - 500, /* cooling state 13 */ - 450, /* cooling state 14 */ - 400, /* cooling state 15 */ - 350, /* cooling state 16 */ - 300, /* cooling state 17 */ - 250, /* cooling state 18 */ - 200, /* cooling state 19 */ - 150, /* cooling state 20 */ -}; - int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state, enum iwl_ctdp_cmd_operation op) { struct iwl_ctdp_cmd cmd = { .operation = cpu_to_le32(op), - .budget = cpu_to_le32(iwl_mld_cdev_budgets[state]), .window_size = 0, }; + u32 budget; int ret; lockdep_assert_wiphy(mld->wiphy); + /* Do a linear scale from IWL_MLD_MIN_CTDP_BUDGET_MW to the configured + * maximum in the predefined number of steps. + */ + budget = ((mld->power_budget_mw - IWL_MLD_MIN_CTDP_BUDGET_MW) * + (IWL_MLD_NUM_CTDP_STEPS - 1 - state)) / + (IWL_MLD_NUM_CTDP_STEPS - 1) + + IWL_MLD_MIN_CTDP_BUDGET_MW; + cmd.budget = cpu_to_le32(budget); + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, CTDP_CONFIG_CMD), &cmd); @@ -326,7 +313,7 @@ int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state, static int iwl_mld_tcool_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { - *state = ARRAY_SIZE(iwl_mld_cdev_budgets) - 1; + *state = IWL_MLD_NUM_CTDP_STEPS - 1; return 0; } @@ -354,7 +341,7 @@ static int iwl_mld_tcool_set_cur_state(struct thermal_cooling_device *cdev, goto unlock; } - if (new_state >= ARRAY_SIZE(iwl_mld_cdev_budgets)) { + if (new_state >= IWL_MLD_NUM_CTDP_STEPS) { ret = -EINVAL; goto unlock; } @@ -417,10 +404,50 @@ static void iwl_mld_cooling_device_unregister(struct iwl_mld *mld) } #endif /* CONFIG_THERMAL */ +static u32 iwl_mld_ctdp_get_max_budget(struct iwl_mld *mld) +{ + u64 bios_power_budget = 0; + u32 default_power_budget; + + switch (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id)) { + case IWL_CFG_RF_TYPE_GF: + /* dual-radio devices have a higher budget */ + if (CSR_HW_RFID_IS_CDB(mld->trans->info.hw_rf_id)) + default_power_budget = 5200; + else + default_power_budget = 2880; + break; + case IWL_CFG_RF_TYPE_FM: + default_power_budget = 3450; + break; + case IWL_CFG_RF_TYPE_WH: + case IWL_CFG_RF_TYPE_PE: + default: + default_power_budget = 5550; + break; + } + + iwl_bios_get_pwr_limit(&mld->fwrt, &bios_power_budget); + + /* 32bit in UEFI, 16bit in ACPI; use BIOS value if it is in range */ + if (bios_power_budget && + bios_power_budget != 0xffff && bios_power_budget != 0xffffffff && + bios_power_budget >= IWL_MLD_MIN_CTDP_BUDGET_MW && + bios_power_budget <= default_power_budget) + return (u32)bios_power_budget; + + return default_power_budget; +} + void iwl_mld_thermal_initialize(struct iwl_mld *mld) { + lockdep_assert_not_held(&mld->wiphy->mtx); + wiphy_delayed_work_init(&mld->ct_kill_exit_wk, iwl_mld_exit_ctkill); + mld->power_budget_mw = iwl_mld_ctdp_get_max_budget(mld); + IWL_DEBUG_TEMP(mld, "cTDP power budget: %d mW\n", mld->power_budget_mw); + #ifdef CONFIG_THERMAL iwl_mld_cooling_device_register(mld); iwl_mld_thermal_zone_register(mld); @@ -429,7 +456,9 @@ void iwl_mld_thermal_initialize(struct iwl_mld *mld) void iwl_mld_thermal_exit(struct iwl_mld *mld) { + wiphy_lock(mld->wiphy); wiphy_delayed_work_cancel(mld->wiphy, &mld->ct_kill_exit_wk); + wiphy_unlock(mld->wiphy); #ifdef CONFIG_THERMAL iwl_mld_cooling_device_unregister(mld); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c index f054cc921d9d..a9ca92c0455e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -44,7 +44,7 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, u16 flags = 0; /* STBC flags */ - if (mld->cfg->ht_params->stbc && + if (mld->cfg->ht_params.stbc && (hweight8(iwl_mld_get_valid_tx_ant(mld)) > 1)) { if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] & IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) @@ -56,7 +56,7 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, } /* LDPC */ - if (mld->cfg->ht_params->ldpc && + if (mld->cfg->ht_params.ldpc && ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || (has_vht && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; @@ -658,7 +658,9 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, if (WARN_ON(!mld_link_sta)) return; - mld_link_sta->last_rate_n_flags = le32_to_cpu(notif->rate); + mld_link_sta->last_rate_n_flags = + iwl_v3_rate_from_v2_v3(notif->rate, + mld->fw_rates_ver_3); rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), mld_link_sta->last_rate_n_flags); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c index 543abe72e465..3b4b575aadaa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -76,14 +76,14 @@ static int iwl_mld_allocate_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) */ unsigned int watchdog_timeout = txq->vif->type == NL80211_IFTYPE_AP ? IWL_WATCHDOG_DISABLED : - mld->trans->trans_cfg->base_params->wd_timeout; + mld->trans->mac_cfg->base->wd_timeout; int queue, size; lockdep_assert_wiphy(mld->wiphy); if (tid == IWL_MGMT_TID) size = max_t(u32, IWL_MGMT_QUEUE_SIZE, - mld->trans->cfg->min_txq_size); + mld->trans->mac_cfg->base->min_txq_size); else size = iwl_mld_get_queue_size(mld, txq); @@ -391,9 +391,9 @@ static u32 iwl_mld_mac80211_rate_idx_to_fw(struct iwl_mld *mld, /* Set CCK or OFDM flag */ if (rate_idx <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; + rate_flags |= RATE_MCS_MOD_TYPE_CCK; else - rate_flags |= RATE_MCS_LEGACY_OFDM_MSK; + rate_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM; /* Legacy rates are indexed: * 0 - 3 for CCK and 0 - 7 for OFDM @@ -425,47 +425,40 @@ static u32 iwl_mld_get_inject_tx_rate(struct iwl_mld *mld, struct ieee80211_tx_rate *rate = &info->control.rates[0]; u32 result; - /* we only care about legacy/HT/VHT so far, so we can - * build in v1 and use iwl_new_rate_from_v1() - * FIXME: in newer devices we only support the new rates, build - * the rate_n_flags in the new format here instead of using v1 and - * converting it. - */ - if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { u8 mcs = ieee80211_rate_get_vht_mcs(rate); u8 nss = ieee80211_rate_get_vht_nss(rate); - result = RATE_MCS_VHT_MSK_V1; - result |= u32_encode_bits(mcs, RATE_VHT_MCS_RATE_CODE_MSK); + result = RATE_MCS_MOD_TYPE_VHT; + result |= u32_encode_bits(mcs, RATE_MCS_CODE_MSK); result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - result |= RATE_MCS_SGI_MSK_V1; + result |= RATE_MCS_SGI_MSK; if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1); + result |= RATE_MCS_CHAN_WIDTH_40; else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) - result |= u32_encode_bits(2, RATE_MCS_CHAN_WIDTH_MSK_V1); + result |= RATE_MCS_CHAN_WIDTH_80; else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) - result |= u32_encode_bits(3, RATE_MCS_CHAN_WIDTH_MSK_V1); - - result = iwl_new_rate_from_v1(result); + result |= RATE_MCS_CHAN_WIDTH_160; } else if (rate->flags & IEEE80211_TX_RC_MCS) { - result = RATE_MCS_HT_MSK_V1; - result |= u32_encode_bits(rate->idx, - RATE_HT_MCS_RATE_CODE_MSK_V1 | - RATE_HT_MCS_NSS_MSK_V1); + /* only MCS 0-15 are supported */ + u8 mcs = rate->idx & 7; + u8 nss = rate->idx > 7; + + result = RATE_MCS_MOD_TYPE_HT; + result |= u32_encode_bits(mcs, RATE_MCS_CODE_MSK); + result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - result |= RATE_MCS_SGI_MSK_V1; + result |= RATE_MCS_SGI_MSK; if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1); + result |= RATE_MCS_CHAN_WIDTH_40; if (info->flags & IEEE80211_TX_CTL_LDPC) - result |= RATE_MCS_LDPC_MSK_V1; + result |= RATE_MCS_LDPC_MSK; if (u32_get_bits(info->flags, IEEE80211_TX_CTL_STBC)) result |= RATE_MCS_STBC_MSK; - - result = iwl_new_rate_from_v1(result); } else { result = iwl_mld_mac80211_rate_idx_to_fw(mld, info, rate->idx); } @@ -479,19 +472,23 @@ static u32 iwl_mld_get_inject_tx_rate(struct iwl_mld *mld, return result; } -static u32 iwl_mld_get_tx_rate_n_flags(struct iwl_mld *mld, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, __le16 fc) +static __le32 iwl_mld_get_tx_rate_n_flags(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) { + u32 rate; + if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) - return iwl_mld_get_inject_tx_rate(mld, info, sta, fc); + rate = iwl_mld_get_inject_tx_rate(mld, info, sta, fc); + else + rate = iwl_mld_mac80211_rate_idx_to_fw(mld, info, -1) | + iwl_mld_get_tx_ant(mld, info, sta, fc); - return iwl_mld_mac80211_rate_idx_to_fw(mld, info, -1) | - iwl_mld_get_tx_ant(mld, info, sta, fc); + return iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3); } static void -iwl_mld_fill_tx_cmd_hdr(struct iwl_tx_cmd_gen3 *tx_cmd, +iwl_mld_fill_tx_cmd_hdr(struct iwl_tx_cmd *tx_cmd, struct sk_buff *skb, bool amsdu) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -537,11 +534,11 @@ iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb, struct ieee80211_hdr *hdr = (void *)skb->data; struct iwl_mld_sta *mld_sta = sta ? iwl_mld_sta_from_mac80211(sta) : NULL; - struct iwl_tx_cmd_gen3 *tx_cmd; + struct iwl_tx_cmd *tx_cmd; bool amsdu = ieee80211_is_data_qos(hdr->frame_control) && (*ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_A_MSDU_PRESENT); - u32 rate_n_flags = 0; + __le32 rate_n_flags = 0; u16 flags = 0; dev_tx_cmd->hdr.cmd = TX_CMD; @@ -576,7 +573,7 @@ iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb, tx_cmd->flags = cpu_to_le16(flags); - tx_cmd->rate_n_flags = cpu_to_le32(rate_n_flags); + tx_cmd->rate_n_flags = rate_n_flags; } /* Caller of this need to check that info->control.vif is not NULL */ @@ -641,16 +638,36 @@ iwl_mld_get_tx_queue_id(struct iwl_mld *mld, struct ieee80211_txq *txq, case NL80211_IFTYPE_P2P_DEVICE: mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); - if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES) { - IWL_DEBUG_DROP(mld, "Drop tx outside ROC\n"); + if (mld_vif->roc_activity != ROC_ACTIVITY_P2P_DISC && + mld_vif->roc_activity != ROC_ACTIVITY_P2P_NEG) { + IWL_DEBUG_DROP(mld, + "Drop tx outside ROC with activity %d\n", + mld_vif->roc_activity); return IWL_MLD_INVALID_DROP_TX; } WARN_ON(!ieee80211_is_mgmt(fc)); - return mld_vif->deflink.aux_sta.queue_id; + return mld_vif->aux_sta.queue_id; + case NL80211_IFTYPE_MONITOR: + mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); + return mld_vif->deflink.mon_sta.queue_id; + case NL80211_IFTYPE_STATION: + mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); + + if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) { + IWL_DEBUG_DROP(mld, "Drop tx not off-channel\n"); + return IWL_MLD_INVALID_DROP_TX; + } + + if (mld_vif->roc_activity != ROC_ACTIVITY_HOTSPOT) { + IWL_DEBUG_DROP(mld, "Drop tx outside ROC\n"); + return IWL_MLD_INVALID_DROP_TX; + } + + WARN_ON(!ieee80211_is_mgmt(fc)); + return mld_vif->aux_sta.queue_id; default: - /* TODO: consider monitor (task=monitor) */ WARN_ONCE(1, "Unsupported vif type\n"); break; } @@ -831,7 +848,7 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, * 1 more for the potential data in the header */ if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) > - mld->trans->max_skb_frags) + mld->trans->info.max_skb_frags) num_subframes = 1; if (num_subframes > 1) @@ -977,11 +994,14 @@ void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) rcu_read_unlock(); } -static void iwl_mld_hwrate_to_tx_rate(u32 rate_n_flags, +static void iwl_mld_hwrate_to_tx_rate(struct iwl_mld *mld, + __le32 rate_n_flags_fw, struct ieee80211_tx_info *info) { enum nl80211_band band = info->band; struct ieee80211_tx_rate *tx_rate = &info->status.rates[0]; + u32 rate_n_flags = iwl_v3_rate_from_v2_v3(rate_n_flags_fw, + mld->fw_rates_ver_3); u32 sgi = rate_n_flags & RATE_MCS_SGI_MSK; u32 chan_width = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; @@ -1006,18 +1026,19 @@ static void iwl_mld_hwrate_to_tx_rate(u32 rate_n_flags, } switch (format) { - case RATE_MCS_HT_MSK: + case RATE_MCS_MOD_TYPE_HT: tx_rate->flags |= IEEE80211_TX_RC_MCS; tx_rate->idx = RATE_HT_MCS_INDEX(rate_n_flags); break; - case RATE_MCS_VHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: ieee80211_rate_set_vht(tx_rate, rate_n_flags & RATE_MCS_CODE_MSK, - FIELD_GET(RATE_MCS_NSS_MSK, - rate_n_flags) + 1); + u32_get_bits(rate_n_flags, + RATE_MCS_NSS_MSK) + 1); tx_rate->flags |= IEEE80211_TX_RC_VHT_MCS; break; - case RATE_MCS_HE_MSK: + case RATE_MCS_MOD_TYPE_HE: + case RATE_MCS_MOD_TYPE_EHT: /* mac80211 cannot do this without ieee80211_tx_status_ext() * but it only matters for radiotap */ @@ -1111,8 +1132,7 @@ void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld, iwl_dbg_tlv_time_point(&mld->fwrt, tp, NULL); } - iwl_mld_hwrate_to_tx_rate(le32_to_cpu(tx_resp->initial_rate), - info); + iwl_mld_hwrate_to_tx_rate(mld, tx_resp->initial_rate, info); if (likely(!iwl_mld_time_sync_frame(mld, skb, hdr->addr1))) ieee80211_tx_status_skb(mld->hw, skb); |