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