From ad25ee36f00172f7d53242dc77c69fff7ced0755 Mon Sep 17 00:00:00 2001 From: Xingyuan Mo Date: Sun, 17 Dec 2023 13:29:01 +0200 Subject: [PATCH 001/378] wifi: ath10k: fix NULL pointer dereference in ath10k_wmi_tlv_op_pull_mgmt_tx_compl_ev() We should check whether the WMI_TLV_TAG_STRUCT_MGMT_TX_COMPL_EVENT tlv is present before accessing it, otherwise a null pointer deference error will occur. Fixes: dc405152bb64 ("ath10k: handle mgmt tx completion event") Signed-off-by: Xingyuan Mo Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20231208043433.271449-1-hdthky0@gmail.com --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 6b6aa3c36744..0ce08e9a0a3d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -851,6 +851,10 @@ ath10k_wmi_tlv_op_pull_mgmt_tx_compl_ev(struct ath10k *ar, struct sk_buff *skb, } ev = tb[WMI_TLV_TAG_STRUCT_MGMT_TX_COMPL_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } arg->desc_id = ev->desc_id; arg->status = ev->status; From f5e6c0c4b0877e0ec0221df6c0041c0f39b6ce0f Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Sun, 17 Dec 2023 13:29:02 +0200 Subject: [PATCH 002/378] wifi: ath11k: refactor ath11k_wmi_tlv_parse_alloc() Since 'ath11k_wmi_tlv_parse_alloc()' always operates on 'skb->data, skb->len' tuple, it may be simplified to pass the only 'skb' argument instead (which also implies refactoring of 'ath11k_pull_bcn_tx_status_ev()' and 'ath11k_pull_chan_info_ev()' in the same way). Compile tested only. Signed-off-by: Dmitry Antipov Signed-off-by: Kalle Valo Link: https://msgid.link/20231214161117.75145-1-dmantipov@yandex.ru --- drivers/net/wireless/ath/ath11k/testmode.c | 2 +- drivers/net/wireless/ath/ath11k/wmi.c | 73 +++++++++++----------- drivers/net/wireless/ath/ath11k/wmi.h | 4 +- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c index 43bb23265d34..302d66092b97 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.c +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -198,7 +198,7 @@ static void ath11k_tm_wmi_event_segmented(struct ath11k_base *ab, u32 cmd_id, u16 length; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse ftm event tlv: %d\n", ret); diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 8a65fa04b48d..89514899fcd5 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -238,8 +238,8 @@ static int ath11k_wmi_tlv_parse(struct ath11k_base *ar, const void **tb, (void *)tb); } -const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr, - size_t len, gfp_t gfp) +const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, + struct sk_buff *skb, gfp_t gfp) { const void **tb; int ret; @@ -248,7 +248,7 @@ const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr, if (!tb) return ERR_PTR(-ENOMEM); - ret = ath11k_wmi_tlv_parse(ab, tb, ptr, len); + ret = ath11k_wmi_tlv_parse(ab, tb, skb->data, skb->len); if (ret) { kfree(tb); return ERR_PTR(ret); @@ -3930,7 +3930,7 @@ ath11k_wmi_obss_color_collision_event(struct ath11k_base *ab, struct sk_buff *sk struct ath11k_vif *arvif; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5003,7 +5003,7 @@ static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buf const struct wmi_vdev_start_resp_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5102,7 +5102,7 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory channel list\n"); - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5278,7 +5278,7 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory ext channel list\n"); - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5634,7 +5634,7 @@ static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff * const struct wmi_peer_delete_resp_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5666,7 +5666,7 @@ static int ath11k_pull_vdev_del_resp_ev(struct ath11k_base *ab, const struct wmi_vdev_delete_resp_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5686,15 +5686,15 @@ static int ath11k_pull_vdev_del_resp_ev(struct ath11k_base *ab, return 0; } -static int ath11k_pull_bcn_tx_status_ev(struct ath11k_base *ab, void *evt_buf, - u32 len, u32 *vdev_id, - u32 *tx_status) +static int ath11k_pull_bcn_tx_status_ev(struct ath11k_base *ab, + struct sk_buff *skb, + u32 *vdev_id, u32 *tx_status) { const void **tb; const struct wmi_bcn_tx_status_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5722,7 +5722,7 @@ static int ath11k_pull_vdev_stopped_param_tlv(struct ath11k_base *ab, struct sk_ const struct wmi_vdev_stopped_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5876,7 +5876,7 @@ static int ath11k_pull_mgmt_tx_compl_param_tlv(struct ath11k_base *ab, const struct wmi_mgmt_tx_compl_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6052,7 +6052,7 @@ static int ath11k_pull_scan_ev(struct ath11k_base *ab, struct sk_buff *skb, const struct wmi_scan_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6085,7 +6085,7 @@ static int ath11k_pull_peer_sta_kickout_ev(struct ath11k_base *ab, struct sk_buf const struct wmi_peer_sta_kickout_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6112,7 +6112,7 @@ static int ath11k_pull_roam_ev(struct ath11k_base *ab, struct sk_buff *skb, const struct wmi_roam_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6153,14 +6153,14 @@ static int freq_to_idx(struct ath11k *ar, int freq) return idx; } -static int ath11k_pull_chan_info_ev(struct ath11k_base *ab, u8 *evt_buf, - u32 len, struct wmi_chan_info_event *ch_info_ev) +static int ath11k_pull_chan_info_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_chan_info_event *ch_info_ev) { const void **tb; const struct wmi_chan_info_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6199,7 +6199,7 @@ ath11k_pull_pdev_bss_chan_info_ev(struct ath11k_base *ab, struct sk_buff *skb, const struct wmi_pdev_bss_chan_info_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6239,7 +6239,7 @@ ath11k_pull_vdev_install_key_compl_ev(struct ath11k_base *ab, struct sk_buff *sk const struct wmi_vdev_install_key_compl_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6270,7 +6270,7 @@ static int ath11k_pull_peer_assoc_conf_ev(struct ath11k_base *ab, struct sk_buff const struct wmi_peer_assoc_conf_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6995,7 +6995,7 @@ static int ath11k_reg_11d_new_cc_event(struct ath11k_base *ab, struct sk_buff *s const void **tb; int ret, i; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -7384,8 +7384,7 @@ static void ath11k_bcn_tx_status_event(struct ath11k_base *ab, struct sk_buff *s struct ath11k_vif *arvif; u32 vdev_id, tx_status; - if (ath11k_pull_bcn_tx_status_ev(ab, skb->data, skb->len, - &vdev_id, &tx_status) != 0) { + if (ath11k_pull_bcn_tx_status_ev(ab, skb, &vdev_id, &tx_status) != 0) { ath11k_warn(ab, "failed to extract bcn tx status"); return; } @@ -7416,7 +7415,7 @@ static void ath11k_wmi_event_peer_sta_ps_state_chg(struct ath11k_base *ab, enum ath11k_wmi_peer_ps_state peer_previous_ps_state; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -7884,7 +7883,7 @@ static void ath11k_chan_info_event(struct ath11k_base *ab, struct sk_buff *skb) /* HW channel counters frequency value in hertz */ u32 cc_freq_hz = ab->cc_freq_hz; - if (ath11k_pull_chan_info_ev(ab, skb->data, skb->len, &ch_info_ev) != 0) { + if (ath11k_pull_chan_info_ev(ab, skb, &ch_info_ev) != 0) { ath11k_warn(ab, "failed to extract chan info event"); return; } @@ -8216,7 +8215,7 @@ static void ath11k_pdev_ctl_failsafe_check_event(struct ath11k_base *ab, const struct wmi_pdev_ctl_failsafe_chk_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8281,7 +8280,7 @@ ath11k_wmi_pdev_csa_switch_count_status_event(struct ath11k_base *ab, const u32 *vdev_ids; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8315,7 +8314,7 @@ ath11k_wmi_pdev_dfs_radar_detected_event(struct ath11k_base *ab, struct sk_buff struct ath11k *ar; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8369,7 +8368,7 @@ ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab, const struct wmi_pdev_temperature_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8409,7 +8408,7 @@ static void ath11k_fils_discovery_event(struct ath11k_base *ab, const struct wmi_fils_discovery_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, @@ -8441,7 +8440,7 @@ static void ath11k_probe_resp_tx_status_event(struct ath11k_base *ab, const struct wmi_probe_resp_tx_status_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, @@ -8567,7 +8566,7 @@ static void ath11k_wmi_twt_add_dialog_event(struct ath11k_base *ab, const struct wmi_twt_add_dialog_event *ev; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, @@ -8604,7 +8603,7 @@ static void ath11k_wmi_gtk_offload_status_event(struct ath11k_base *ab, u64 replay_ctr; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath11k_warn(ab, "failed to parse tlv: %d\n", ret); diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index ff0a9a92beeb..b1b4ee804b7b 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -6295,8 +6295,8 @@ enum wmi_sta_keepalive_method { #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 -const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr, - size_t len, gfp_t gfp); +const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, + struct sk_buff *skb, gfp_t gfp); int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, u32 cmd_id); struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); From 504130491026be9cf19f9de562172d340a06a2bb Mon Sep 17 00:00:00 2001 From: Wenli Looi Date: Sun, 17 Dec 2023 13:29:02 +0200 Subject: [PATCH 003/378] wifi: ath9k: delete some unused/duplicate macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rate macros are for AR9002 and not correct for AR9003. The AGC 3 macros are unused and have incorrect values, at least for QCN5502, where AR_AGC3_BASE should be 0x2de00. This change does not appear to affect the final binary. Signed-off-by: Wenli Looi Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://msgid.link/20230629231625.951744-3-wlooi@ucalgary.ca --- drivers/net/wireless/ath/ath9k/ar9003_phy.h | 9 --------- drivers/net/wireless/ath/ath9k/reg_aic.h | 4 ---- 2 files changed, 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 57e2b4c89125..ad72a30b67c3 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -851,8 +851,6 @@ #define AR_PHY_TXGAIN_FORCED_TXBB1DBGAIN 0x0000000e #define AR_PHY_TXGAIN_FORCED_TXBB1DBGAIN_S 1 -#define AR_PHY_POWER_TX_RATE1 0x9934 -#define AR_PHY_POWER_TX_RATE2 0x9938 #define AR_PHY_POWER_TX_RATE_MAX 0x993c #define AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE 0x00000040 #define PHY_AGC_CLR 0x10000000 @@ -1041,13 +1039,6 @@ #define AR_PHY_TX_IQCAL_STATUS_B2_FAILED 0x00000001 -/* - * AGC 3 Register Map - */ -#define AR_AGC3_BASE 0xce00 - -#define AR_PHY_RSSI_3 (AR_AGC3_BASE + 0x180) - /* GLB Registers */ #define AR_GLB_BASE 0x20000 #define AR_GLB_GPIO_CONTROL (AR_GLB_BASE) diff --git a/drivers/net/wireless/ath/ath9k/reg_aic.h b/drivers/net/wireless/ath/ath9k/reg_aic.h index 955147ab48a2..f50994910eae 100644 --- a/drivers/net/wireless/ath/ath9k/reg_aic.h +++ b/drivers/net/wireless/ath/ath9k/reg_aic.h @@ -17,10 +17,6 @@ #ifndef REG_AIC_H #define REG_AIC_H -#define AR_SM_BASE 0xa200 -#define AR_SM1_BASE 0xb200 -#define AR_AGC_BASE 0x9e00 - #define AR_PHY_AIC_CTRL_0_B0 (AR_SM_BASE + 0x4b0) #define AR_PHY_AIC_CTRL_1_B0 (AR_SM_BASE + 0x4b4) #define AR_PHY_AIC_CTRL_2_B0 (AR_SM_BASE + 0x4b8) From 27ce06d018cec6a5042069fb21b000753dd147d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Sun, 17 Dec 2023 13:29:03 +0200 Subject: [PATCH 004/378] wifi: ath9k: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Jeff Johnson Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://msgid.link/20231117093056.873834-11-u.kleine-koenig@pengutronix.de --- drivers/net/wireless/ath/ath9k/ahb.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 9bfaadfa6c00..1a6697b6e3b4 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -144,7 +144,7 @@ static int ath_ahb_probe(struct platform_device *pdev) return ret; } -static int ath_ahb_remove(struct platform_device *pdev) +static void ath_ahb_remove(struct platform_device *pdev) { struct ieee80211_hw *hw = platform_get_drvdata(pdev); @@ -155,13 +155,11 @@ static int ath_ahb_remove(struct platform_device *pdev) free_irq(sc->irq, sc); ieee80211_free_hw(sc->hw); } - - return 0; } static struct platform_driver ath_ahb_driver = { .probe = ath_ahb_probe, - .remove = ath_ahb_remove, + .remove_new = ath_ahb_remove, .driver = { .name = "ath9k", }, From d6b27eb997ef9a2aa51633b3111bc4a04748e6d3 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Sun, 17 Dec 2023 13:29:03 +0200 Subject: [PATCH 005/378] wifi: ath9k: fix LNA selection in ath_ant_try_scan() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 'ath_ant_try_scan()', (most likely) the 2nd LNA's signal strength should be used in comparison against RSSI when selecting first LNA as the main one. Compile tested only. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Dmitry Antipov Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://msgid.link/20231211172502.25202-1-dmantipov@yandex.ru --- drivers/net/wireless/ath/ath9k/antenna.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c index 988222cea9df..acc84e6711b0 100644 --- a/drivers/net/wireless/ath/ath9k/antenna.c +++ b/drivers/net/wireless/ath/ath9k/antenna.c @@ -643,7 +643,7 @@ static void ath_ant_try_scan(struct ath_ant_comb *antcomb, conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; } else if (antcomb->rssi_sub > - antcomb->rssi_lna1) { + antcomb->rssi_lna2) { /* set to A-B */ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; From d2eb318f4b6be1176e87ac3f9f8cc976be1c014b Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 17 Dec 2023 13:29:03 +0200 Subject: [PATCH 006/378] wifi: ath10k: use flexible array in struct wmi_host_mem_chunks Currently struct wmi_host_mem_chunks defines: struct host_memory_chunk items[1]; Per the guidance in [1] this should be a flexible array. However there is a documented requirement: some fw revisions require at least 1 chunk regardless of count To satisfy this requirement, follow the guidance from [2] and wrap the array in a union which contains both the flexible array and a single instance of the underlying struct. Since the footprint of the struct is unchanged, no additional driver changes are required. No functional changes, compile tested only. [1] https://docs.kernel.org/process/deprecated.html#zero-length-and-one-element-arrays [2] https://lore.kernel.org/linux-wireless/202308301529.AC90A9EF98@keescook/ Signed-off-by: Jeff Johnson Reviewed-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://msgid.link/20231213-wmi_host_mem_chunks_flexarray-v1-1-92922d92fa2c@quicinc.com --- drivers/net/wireless/ath/ath10k/wmi.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 9146df98fcee..833ce0251a2c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3069,7 +3069,10 @@ struct host_memory_chunk { struct wmi_host_mem_chunks { __le32 count; /* some fw revisions require at least 1 chunk regardless of count */ - struct host_memory_chunk items[1]; + union { + struct host_memory_chunk item; + DECLARE_FLEX_ARRAY(struct host_memory_chunk, items); + }; } __packed; struct wmi_init_cmd { From 72ca7c4073ac126be1c2341644838ef5f146b36b Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 17 Dec 2023 13:29:03 +0200 Subject: [PATCH 007/378] wifi: ath10k: use flexible arrays for WMI start scan TLVs Currently ath10k defines the following struct: struct wmi_start_scan_tlvs { u8 tlvs[0]; } __packed; Per the guidance in [1] this should be a flexible array. However, a direct replace to u8 tlvs[] results in the compilation error: flexible array member in a struct with no named members This is because C99 6.7.2.1 (16) requires that a structure containing a flexible array member must have more than one named member. So rather than defining a separate struct wmi_start_scan_tlvs which contains the flexible tlvs[] array, just define the tlvs[] array where struct wmi_start_scan_tlvs is being used. No functional changes, compile tested only. [1] https://docs.kernel.org/process/deprecated.html#zero-length-and-one-element-arrays Signed-off-by: Jeff Johnson Reviewed-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://msgid.link/20231213-wmi_host_mem_chunks_flexarray-v1-2-92922d92fa2c@quicinc.com --- drivers/net/wireless/ath/ath10k/wmi.c | 8 ++++---- drivers/net/wireless/ath/ath10k/wmi.h | 11 ++--------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 88befe92f95d..4d5aadbc7159 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -6927,14 +6927,14 @@ void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn, } static void -ath10k_wmi_put_start_scan_tlvs(struct wmi_start_scan_tlvs *tlvs, +ath10k_wmi_put_start_scan_tlvs(u8 *tlvs, const struct wmi_start_scan_arg *arg) { struct wmi_ie_data *ie; struct wmi_chan_list *channels; struct wmi_ssid_list *ssids; struct wmi_bssid_list *bssids; - void *ptr = tlvs->tlvs; + void *ptr = tlvs; int i; if (arg->n_channels) { @@ -7012,7 +7012,7 @@ ath10k_wmi_op_gen_start_scan(struct ath10k *ar, cmd = (struct wmi_start_scan_cmd *)skb->data; ath10k_wmi_put_start_scan_common(&cmd->common, arg); - ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); + ath10k_wmi_put_start_scan_tlvs(cmd->tlvs, arg); cmd->burst_duration_ms = __cpu_to_le32(0); @@ -7041,7 +7041,7 @@ ath10k_wmi_10x_op_gen_start_scan(struct ath10k *ar, cmd = (struct wmi_10x_start_scan_cmd *)skb->data; ath10k_wmi_put_start_scan_common(&cmd->common, arg); - ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); + ath10k_wmi_put_start_scan_tlvs(cmd->tlvs, arg); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi 10x start scan\n"); return skb; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 833ce0251a2c..52a409ff94e7 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3218,23 +3218,16 @@ struct wmi_start_scan_common { __le32 scan_ctrl_flags; } __packed; -struct wmi_start_scan_tlvs { - /* TLV parameters. These includes channel list, ssid list, bssid list, - * extra ies. - */ - u8 tlvs[0]; -} __packed; - struct wmi_start_scan_cmd { struct wmi_start_scan_common common; __le32 burst_duration_ms; - struct wmi_start_scan_tlvs tlvs; + u8 tlvs[]; } __packed; /* This is the definition from 10.X firmware branch */ struct wmi_10x_start_scan_cmd { struct wmi_start_scan_common common; - struct wmi_start_scan_tlvs tlvs; + u8 tlvs[]; } __packed; struct wmi_ssid_arg { From 26eb704a46f89d50a58ccb22b317b73ee85fa233 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 17 Dec 2023 13:29:03 +0200 Subject: [PATCH 008/378] wifi: ath10k: remove struct wmi_pdev_chanlist_update_event Currently struct wmi_pdev_chanlist_update_event defines: struct wmi_channel channel_list[1]; Per the guidance in [1] this should be a flexible array. However during conversion it was discovered that this struct is not used, so just remove the entire struct. No functional changes, compile tested only. [1] https://docs.kernel.org/process/deprecated.html#zero-length-and-one-element-arrays Signed-off-by: Jeff Johnson Reviewed-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://msgid.link/20231213-wmi_host_mem_chunks_flexarray-v1-3-92922d92fa2c@quicinc.com --- drivers/net/wireless/ath/ath10k/wmi.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 52a409ff94e7..37a7d421bd86 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4256,13 +4256,6 @@ struct wmi_peer_sta_ps_state_chg_event { __le32 peer_ps_state; } __packed; -struct wmi_pdev_chanlist_update_event { - /* number of channels */ - __le32 num_chan; - /* array of channels */ - struct wmi_channel channel_list[1]; -} __packed; - #define WMI_MAX_DEBUG_MESG (sizeof(u32) * 32) struct wmi_debug_mesg_event { From b0c0794b05ec07a150bd39be3d43854a9dd93cad Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 17 Dec 2023 13:29:03 +0200 Subject: [PATCH 009/378] wifi: ath10k: remove unused template structs Currently both the wmi_bcn_tmpl_cmd and wmi_prb_tmpl_cmd structs define: u8 data[1]; Per the guidance in [1] both instances of this should be flexible arrays. However during conversion it was discovered that neither of these structs are actually used, so just remove them. No functional changes, compile tested only. [1] https://docs.kernel.org/process/deprecated.html#zero-length-and-one-element-arrays Signed-off-by: Jeff Johnson Reviewed-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://msgid.link/20231213-wmi_host_mem_chunks_flexarray-v1-4-92922d92fa2c@quicinc.com --- drivers/net/wireless/ath/ath10k/wmi.h | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 37a7d421bd86..e16410e348ca 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -5782,30 +5782,6 @@ struct wmi_bcn_prb_info { /* app IE */ } __packed; -struct wmi_bcn_tmpl_cmd { - /* unique id identifying the VDEV, generated by the caller */ - __le32 vdev_id; - /* TIM IE offset from the beginning of the template. */ - __le32 tim_ie_offset; - /* beacon probe capabilities and IEs */ - struct wmi_bcn_prb_info bcn_prb_info; - /* beacon buffer length */ - __le32 buf_len; - /* variable length data */ - u8 data[1]; -} __packed; - -struct wmi_prb_tmpl_cmd { - /* unique id identifying the VDEV, generated by the caller */ - __le32 vdev_id; - /* beacon probe capabilities and IEs */ - struct wmi_bcn_prb_info bcn_prb_info; - /* beacon buffer length */ - __le32 buf_len; - /* Variable length data */ - u8 data[1]; -} __packed; - enum wmi_sta_ps_mode { /* enable power save for the given STA VDEV */ WMI_STA_PS_MODE_DISABLED = 0, From cb188e862c1ce195ce8beaac10b554c33847f4c8 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 17 Dec 2023 13:29:03 +0200 Subject: [PATCH 010/378] wifi: ath10k: use flexible array in struct wmi_tdls_peer_capabilities Currently struct wmi_tdls_peer_capabilities defines: struct wmi_channel peer_chan_list[1]; Per the guidance in [1] this should be a flexible array, and at one point Gustavo was trying to fix this [2], but had questions about the correct behavior when the associated peer_chan_len is 0. I have been unable to determine if firmware requires that at least one record be present even if peer_chan_len is 0. But since that is the current behavior, follow the example from [3] and replace the one-element array with a union that contains both a flexible array and a single instance of the array element. This results in a struct that has the same footprint as the original, so no other driver changes are required. No functional changes, compile tested only. [1] https://docs.kernel.org/process/deprecated.html#zero-length-and-one-element-arrays [2] https://lore.kernel.org/linux-wireless/626ae2e7-66f8-423b-b17f-e75c1a6d29b3@embeddedor.com/ [3] https://lore.kernel.org/linux-wireless/202308301529.AC90A9EF98@keescook/ Signed-off-by: Jeff Johnson Reviewed-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://msgid.link/20231213-wmi_host_mem_chunks_flexarray-v1-5-92922d92fa2c@quicinc.com --- drivers/net/wireless/ath/ath10k/wmi.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index e16410e348ca..b64b6e214bae 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -7162,7 +7162,13 @@ struct wmi_tdls_peer_capabilities { __le32 is_peer_responder; __le32 pref_offchan_num; __le32 pref_offchan_bw; - struct wmi_channel peer_chan_list[1]; + union { + /* to match legacy implementation allocate room for + * at least one record even if peer_chan_len is 0 + */ + struct wmi_channel peer_chan_min_allocation; + DECLARE_FLEX_ARRAY(struct wmi_channel, peer_chan_list); + }; } __packed; struct wmi_10_4_tdls_peer_update_cmd { From 6b9923f1f6d1b57a2ae932540a6273a0face54fe Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 17 Dec 2023 13:29:03 +0200 Subject: [PATCH 011/378] wifi: ath10k: remove duplicate memset() in 10.4 TDLS peer update In [1] it was identified that in ath10k_wmi_10_4_gen_tdls_peer_update() the memset(skb->data, 0, sizeof(*cmd)) is unnecessary since function ath10k_wmi_alloc_skb() already zeroes skb->data, so remove it. No functional changes, compile tested only. [1] https://lore.kernel.org/linux-wireless/626ae2e7-66f8-423b-b17f-e75c1a6d29b3@embeddedor.com/ Signed-off-by: Jeff Johnson Reviewed-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://msgid.link/20231213-wmi_host_mem_chunks_flexarray-v1-6-92922d92fa2c@quicinc.com --- drivers/net/wireless/ath/ath10k/wmi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 4d5aadbc7159..0cfd9484c45e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -8918,8 +8918,6 @@ ath10k_wmi_10_4_gen_tdls_peer_update(struct ath10k *ar, if (!skb) return ERR_PTR(-ENOMEM); - memset(skb->data, 0, sizeof(*cmd)); - cmd = (struct wmi_10_4_tdls_peer_update_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(arg->vdev_id); ether_addr_copy(cmd->peer_macaddr.addr, arg->addr); From f4c2a9d62213cf7004db5c74ce12d26d63a23629 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Sun, 17 Dec 2023 13:29:03 +0200 Subject: [PATCH 012/378] wifi: ath12k: add string type to search board data in board-2.bin for WCN7850 Currently ath12k only supports string type with bus, chip id and board id such as "bus=ahb,qmi-chip-id=1,qmi-board-id=4" for AHB bus chip and "bus=pci,qmi-chip-id=0,qmi-board-id=255" for PCI bus chip in board-2.bin. For WCN7850, it is not enough to distinguish all different chips. Add a new string type which includes bus, chip id, board id, vendor, device, subsystem-vendor and subsystem-device for WCN7850. ath12k will first load board-2.bin and searches in it for the board data with the above parameters. If matched with one board data, ath12k downloads it to firmware. And if not, downloads board.bin instead. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Wen Gong Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231216060140.30611-2-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 27 ++++++++++++++++++++------ drivers/net/wireless/ath/ath12k/core.h | 13 +++++++++++++ drivers/net/wireless/ath/ath12k/pci.c | 10 ++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 6c01b282fcd3..b8027f76a02e 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -114,11 +114,26 @@ static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name, scnprintf(variant, sizeof(variant), ",variant=%s", ab->qmi.target.bdf_ext); - scnprintf(name, name_len, - "bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s", - ath12k_bus_str(ab->hif.bus), - ab->qmi.target.chip_id, - ab->qmi.target.board_id, variant); + switch (ab->id.bdf_search) { + case ATH12K_BDF_SEARCH_BUS_AND_BOARD: + scnprintf(name, name_len, + "bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x,qmi-chip-id=%d,qmi-board-id=%d%s", + ath12k_bus_str(ab->hif.bus), + ab->id.vendor, ab->id.device, + ab->id.subsystem_vendor, + ab->id.subsystem_device, + ab->qmi.target.chip_id, + ab->qmi.target.board_id, + variant); + break; + default: + scnprintf(name, name_len, + "bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s", + ath12k_bus_str(ab->hif.bus), + ab->qmi.target.chip_id, + ab->qmi.target.board_id, variant); + break; + } ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot using board name '%s'\n", name); @@ -356,7 +371,7 @@ int ath12k_core_fetch_board_data_api_1(struct ath12k_base *ab, return 0; } -#define BOARD_NAME_SIZE 100 +#define BOARD_NAME_SIZE 200 int ath12k_core_fetch_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd) { char boardname[BOARD_NAME_SIZE]; diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 8458dc292821..385fda03e913 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -55,6 +55,11 @@ #define ATH12K_RECONFIGURE_TIMEOUT_HZ (10 * HZ) #define ATH12K_RECOVER_START_TIMEOUT_HZ (20 * HZ) +enum ath12k_bdf_search { + ATH12K_BDF_SEARCH_DEFAULT, + ATH12K_BDF_SEARCH_BUS_AND_BOARD, +}; + enum wme_ac { WME_AC_BE, WME_AC_BK, @@ -793,6 +798,14 @@ struct ath12k_base { /* true means radio is on */ bool rfkill_radio_on; + struct { + enum ath12k_bdf_search bdf_search; + u32 vendor; + u32 device; + u32 subsystem_vendor; + u32 subsystem_device; + } id; + /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index f0d2e2d8719c..76438b3afd55 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1310,6 +1310,15 @@ static int ath12k_pci_probe(struct pci_dev *pdev, goto err_free_core; } + ath12k_dbg(ab, ATH12K_DBG_BOOT, "pci probe %04x:%04x %04x:%04x\n", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + ab->id.vendor = pdev->vendor; + ab->id.device = pdev->device; + ab->id.subsystem_vendor = pdev->subsystem_vendor; + ab->id.subsystem_device = pdev->subsystem_device; + switch (pci_dev->device) { case QCN9274_DEVICE_ID: ab_pci->msi_config = &ath12k_msi_config[0]; @@ -1333,6 +1342,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, } break; case WCN7850_DEVICE_ID: + ab->id.bdf_search = ATH12K_BDF_SEARCH_BUS_AND_BOARD; ab_pci->msi_config = &ath12k_msi_config[0]; ab->static_window_map = false; ab_pci->pci_ops = &ath12k_pci_ops_wcn7850; From 7173972a2eb1107ae3dc076f1cd27abce132e027 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Sun, 17 Dec 2023 13:29:04 +0200 Subject: [PATCH 013/378] wifi: ath12k: add fallback board name without variant while searching board-2.bin Currently a variant value read from DT or SMBIOS is considered while searching board-2.bin, this may fail because not all board-2.bin files contains that symbol. Add fallback board name which removes variant value and searches again in board-2.bin when fails to increase boot up success rate. dmesg log after this patch: [169547.248472] ath12k_pci 0000:05:00.0: boot using board name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262,variant=test' [169547.248565] ath12k_pci 0000:05:00.0: boot firmware request ath12k/WCN7850/hw2.0/board-2.bin size 180324 [169547.248568] ath12k_pci 0000:05:00.0: board name [169547.248570] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 [169547.248571] ath12k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, [169547.248572] ath12k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor [169547.248574] ath12k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- [169547.248575] ath12k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- [169547.248576] ath12k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo [169547.248577] ath12k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 32 ard-id=262 [169547.248578] ath12k_pci 0000:05:00.0: board name [169547.248579] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 [169547.248581] ath12k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, [169547.248582] ath12k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor [169547.248583] ath12k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- [169547.248584] ath12k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- [169547.248585] ath12k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo [169547.248587] ath12k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 36 ard-id=266 [169547.248588] ath12k_pci 0000:05:00.0: board name [169547.248589] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 [169547.248590] ath12k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, [169547.248591] ath12k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor [169547.248592] ath12k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- [169547.248594] ath12k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- [169547.248595] ath12k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 31 38 2c 71 6d 69 2d 62 chip-id=18,qmi-b [169547.248596] ath12k_pci 0000:05:00.0: 00000060: 6f 61 72 64 2d 69 64 3d 32 36 36 oard-id=266 [169547.248597] ath12k_pci 0000:05:00.0: failed to fetch board data for bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262,variant=test from ath12k/WCN7850/hw2.0/board-2.bin [169547.248476] ath12k_pci 0000:05:00.0: boot using board name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' [169547.248634] ath12k_pci 0000:05:00.0: boot firmware request ath12k/WCN7850/hw2.0/board-2.bin size 180324 [169547.248636] ath12k_pci 0000:05:00.0: board name [169547.248637] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 [169547.248638] ath12k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, [169547.248639] ath12k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor [169547.248641] ath12k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- [169547.248642] ath12k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- [169547.248643] ath12k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo [169547.248645] ath12k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 32 ard-id=262 [169547.248646] ath12k_pci 0000:05:00.0: boot found match for name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' [169547.248647] ath12k_pci 0000:05:00.0: boot found board data for 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' [169547.248649] ath12k_pci 0000:05:00.0: using board api 2 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Wen Gong Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231216060140.30611-3-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 48 ++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index b8027f76a02e..672aa174704b 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -104,13 +104,13 @@ int ath12k_core_resume(struct ath12k_base *ab) return 0; } -static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name, - size_t name_len) +static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, + size_t name_len, bool with_variant) { /* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */ char variant[9 + ATH12K_QMI_BDF_EXT_STR_LENGTH] = { 0 }; - if (ab->qmi.target.bdf_ext[0] != '\0') + if (with_variant && ab->qmi.target.bdf_ext[0] != '\0') scnprintf(variant, sizeof(variant), ",variant=%s", ab->qmi.target.bdf_ext); @@ -140,6 +140,18 @@ static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name, return 0; } +static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name, + size_t name_len) +{ + return __ath12k_core_create_board_name(ab, name, name_len, true); +} + +static int ath12k_core_create_fallback_board_name(struct ath12k_base *ab, char *name, + size_t name_len) +{ + return __ath12k_core_create_board_name(ab, name, name_len, false); +} + const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab, const char *file) { @@ -343,7 +355,7 @@ static int ath12k_core_fetch_board_data_api_n(struct ath12k_base *ab, out: if (!bd->data || !bd->len) { - ath12k_err(ab, + ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to fetch board data for %s from %s\n", boardname, filepath); ret = -ENODATA; @@ -374,11 +386,14 @@ int ath12k_core_fetch_board_data_api_1(struct ath12k_base *ab, #define BOARD_NAME_SIZE 200 int ath12k_core_fetch_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd) { - char boardname[BOARD_NAME_SIZE]; + char boardname[BOARD_NAME_SIZE], fallback_boardname[BOARD_NAME_SIZE]; + char *filename, filepath[100]; int bd_api; int ret; - ret = ath12k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); + filename = ATH12K_BOARD_API2_FILE; + + ret = ath12k_core_create_board_name(ab, boardname, sizeof(boardname)); if (ret) { ath12k_err(ab, "failed to create board name: %d", ret); return ret; @@ -389,10 +404,29 @@ int ath12k_core_fetch_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd) if (!ret) goto success; + ret = ath12k_core_create_fallback_board_name(ab, fallback_boardname, + sizeof(fallback_boardname)); + if (ret) { + ath12k_err(ab, "failed to create fallback board name: %d", ret); + return ret; + } + + ret = ath12k_core_fetch_board_data_api_n(ab, bd, fallback_boardname); + if (!ret) + goto success; + bd_api = 1; ret = ath12k_core_fetch_board_data_api_1(ab, bd, ATH12K_DEFAULT_BOARD_FILE); if (ret) { - ath12k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n", + ath12k_core_create_firmware_path(ab, filename, + filepath, sizeof(filepath)); + ath12k_err(ab, "failed to fetch board data for %s from %s\n", + boardname, filepath); + if (memcmp(boardname, fallback_boardname, strlen(boardname))) + ath12k_err(ab, "failed to fetch board data for %s from %s\n", + fallback_boardname, filepath); + + ath12k_err(ab, "failed to fetch board.bin from %s\n", ab->hw_params->fw.dir); return ret; } From 97474e5f54243be13d72cbda50624f0114213c7d Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Sun, 17 Dec 2023 13:29:04 +0200 Subject: [PATCH 014/378] wifi: ath12k: remove unused ATH12K_BD_IE_BOARD_EXT Currently ATH12K_BD_IE_BOARD_EXT is not used, so remove it. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Wen Gong Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231216060140.30611-4-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/hw.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index d2622bfef942..130d99f7e426 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -245,7 +245,6 @@ enum ath12k_bd_ie_board_type { enum ath12k_bd_ie_type { /* contains sub IEs of enum ath12k_bd_ie_board_type */ ATH12K_BD_IE_BOARD = 0, - ATH12K_BD_IE_BOARD_EXT = 1, }; struct ath12k_hw_regs { From 511207452221a94242664be47c4ceb63d5c6b293 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Sun, 17 Dec 2023 13:29:04 +0200 Subject: [PATCH 015/378] wifi: ath12k: add support to search regdb data in board-2.bin for WCN7850 Currently ath12k only downloads the same regdb.bin file for all WCN7850 chips, actually ath12k needs to distinguish all different WCN7850 chips. This is to re-use the string type which includes bus, chip id, board id, vendor, device, subsystem-vendor, subsystem-device and variant for WCN7850 to distinguish different regdb in board-2.bin. ath12k will first load board-2.bin and search in it for the regdb data with the above parameters. If matched with one regdb data, download it to firmware. And if not, download regdb.bin instead. Add enum value ATH12K_BD_IE_REGDB and enum type ath12k_bd_ie_regdb_type to distinguish regdb data and board data since they are in the same file board-2.bin. Test log: [ 3833.091948] ath12k_pci 0000:05:00.0: boot using board name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' [ 3833.092072] ath12k_pci 0000:05:00.0: boot firmware request ath12k/WCN7850/hw2.0/board-2.bin size 205316 [ 3833.092079] ath12k_pci 0000:05:00.0: board name [ 3833.092083] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip [ 3833.092088] ath12k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 31 -id=1 [ 3833.092091] ath12k_pci 0000:05:00.0: board name [ 3833.092095] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip [ 3833.092099] ath12k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 32 -id=2 [ 3833.092102] ath12k_pci 0000:05:00.0: board name [ 3833.092105] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip [ 3833.092109] ath12k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 33 -id=3 [ 3833.092112] ath12k_pci 0000:05:00.0: board name [ 3833.092116] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 [ 3833.092119] ath12k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, [ 3833.092123] ath12k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor [ 3833.092126] ath12k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- [ 3833.092130] ath12k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- [ 3833.092133] ath12k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo [ 3833.092137] ath12k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 36 2c 76 61 72 69 61 ard-id=266,varia [ 3833.092140] ath12k_pci 0000:05:00.0: 00000070: 6e 74 3d 48 50 5f 47 38 5f 4c 61 6e 63 69 61 31 nt=HP_G8_Lancia1 [ 3833.092144] ath12k_pci 0000:05:00.0: 00000080: 35 5 [ 3833.092147] ath12k_pci 0000:05:00.0: board name [ 3833.092150] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 [ 3833.092154] ath12k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, [ 3833.092157] ath12k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor [ 3833.092161] ath12k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- [ 3833.092165] ath12k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- [ 3833.092168] ath12k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo [ 3833.092172] ath12k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 36 ard-id=266 [ 3833.092206] ath12k_pci 0000:05:00.0: board name [ 3833.092209] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 [ 3833.092213] ath12k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, [ 3833.092216] ath12k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor [ 3833.092220] ath12k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- [ 3833.092223] ath12k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- [ 3833.092227] ath12k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo [ 3833.092230] ath12k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 32 ard-id=262 [ 3833.092234] ath12k_pci 0000:05:00.0: boot found match regdb data for name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' [ 3833.092238] ath12k_pci 0000:05:00.0: board name [ 3833.092241] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip [ 3833.092245] ath12k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 31 31 -id=11 [ 3833.092248] ath12k_pci 0000:05:00.0: board name [ 3833.092251] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip [ 3833.092255] ath12k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 32 32 -id=22 [ 3833.092258] ath12k_pci 0000:05:00.0: board name [ 3833.092261] ath12k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip [ 3833.092265] ath12k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 33 33 -id=33 [ 3833.092268] ath12k_pci 0000:05:00.0: boot found regdb data for 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' [ 3833.092272] ath12k_pci 0000:05:00.0: fetched regdb Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Wen Gong Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231216060140.30611-5-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 96 +++++++++++++++++++------- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/hw.h | 19 +++++ drivers/net/wireless/ath/ath12k/qmi.c | 3 +- 4 files changed, 92 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 672aa174704b..80fce8099bdf 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -186,7 +186,9 @@ static int ath12k_core_parse_bd_ie_board(struct ath12k_base *ab, struct ath12k_board_data *bd, const void *buf, size_t buf_len, const char *boardname, - int bd_ie_type) + int ie_id, + int name_id, + int data_id) { const struct ath12k_fw_ie *hdr; bool name_match_found; @@ -196,7 +198,7 @@ static int ath12k_core_parse_bd_ie_board(struct ath12k_base *ab, name_match_found = false; - /* go through ATH12K_BD_IE_BOARD_ elements */ + /* go through ATH12K_BD_IE_BOARD_/ATH12K_BD_IE_REGDB_ elements */ while (buf_len > sizeof(struct ath12k_fw_ie)) { hdr = buf; board_ie_id = le32_to_cpu(hdr->id); @@ -207,48 +209,50 @@ static int ath12k_core_parse_bd_ie_board(struct ath12k_base *ab, buf += sizeof(*hdr); if (buf_len < ALIGN(board_ie_len, 4)) { - ath12k_err(ab, "invalid ATH12K_BD_IE_BOARD length: %zu < %zu\n", + ath12k_err(ab, "invalid %s length: %zu < %zu\n", + ath12k_bd_ie_type_str(ie_id), buf_len, ALIGN(board_ie_len, 4)); ret = -EINVAL; goto out; } - switch (board_ie_id) { - case ATH12K_BD_IE_BOARD_NAME: + if (board_ie_id == name_id) { ath12k_dbg_dump(ab, ATH12K_DBG_BOOT, "board name", "", board_ie_data, board_ie_len); if (board_ie_len != strlen(boardname)) - break; + goto next; ret = memcmp(board_ie_data, boardname, strlen(boardname)); if (ret) - break; + goto next; name_match_found = true; ath12k_dbg(ab, ATH12K_DBG_BOOT, - "boot found match for name '%s'", + "boot found match %s for name '%s'", + ath12k_bd_ie_type_str(ie_id), boardname); - break; - case ATH12K_BD_IE_BOARD_DATA: + } else if (board_ie_id == data_id) { if (!name_match_found) /* no match found */ - break; + goto next; ath12k_dbg(ab, ATH12K_DBG_BOOT, - "boot found board data for '%s'", boardname); + "boot found %s for '%s'", + ath12k_bd_ie_type_str(ie_id), + boardname); bd->data = board_ie_data; bd->len = board_ie_len; ret = 0; goto out; - default: - ath12k_warn(ab, "unknown ATH12K_BD_IE_BOARD found: %d\n", + } else { + ath12k_warn(ab, "unknown %s id found: %d\n", + ath12k_bd_ie_type_str(ie_id), board_ie_id); - break; } - +next: /* jump over the padding */ board_ie_len = ALIGN(board_ie_len, 4); @@ -265,7 +269,10 @@ static int ath12k_core_parse_bd_ie_board(struct ath12k_base *ab, static int ath12k_core_fetch_board_data_api_n(struct ath12k_base *ab, struct ath12k_board_data *bd, - const char *boardname) + const char *boardname, + int ie_id_match, + int name_id, + int data_id) { size_t len, magic_len; const u8 *data; @@ -330,22 +337,23 @@ static int ath12k_core_fetch_board_data_api_n(struct ath12k_base *ab, goto err; } - switch (ie_id) { - case ATH12K_BD_IE_BOARD: + if (ie_id == ie_id_match) { ret = ath12k_core_parse_bd_ie_board(ab, bd, data, ie_len, boardname, - ATH12K_BD_IE_BOARD); + ie_id_match, + name_id, + data_id); if (ret == -ENOENT) /* no match found, continue */ - break; + goto next; else if (ret) /* there was an error, bail out */ goto err; /* either found or error, so stop searching */ goto out; } - +next: /* jump over the padding */ ie_len = ALIGN(ie_len, 4); @@ -356,7 +364,8 @@ static int ath12k_core_fetch_board_data_api_n(struct ath12k_base *ab, out: if (!bd->data || !bd->len) { ath12k_dbg(ab, ATH12K_DBG_BOOT, - "failed to fetch board data for %s from %s\n", + "failed to fetch %s for %s from %s\n", + ath12k_bd_ie_type_str(ie_id_match), boardname, filepath); ret = -ENODATA; goto err; @@ -400,7 +409,10 @@ int ath12k_core_fetch_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd) } bd_api = 2; - ret = ath12k_core_fetch_board_data_api_n(ab, bd, boardname); + ret = ath12k_core_fetch_board_data_api_n(ab, bd, boardname, + ATH12K_BD_IE_BOARD, + ATH12K_BD_IE_BOARD_NAME, + ATH12K_BD_IE_BOARD_DATA); if (!ret) goto success; @@ -411,7 +423,10 @@ int ath12k_core_fetch_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd) return ret; } - ret = ath12k_core_fetch_board_data_api_n(ab, bd, fallback_boardname); + ret = ath12k_core_fetch_board_data_api_n(ab, bd, fallback_boardname, + ATH12K_BD_IE_BOARD, + ATH12K_BD_IE_BOARD_NAME, + ATH12K_BD_IE_BOARD_DATA); if (!ret) goto success; @@ -436,6 +451,37 @@ int ath12k_core_fetch_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd) return 0; } +int ath12k_core_fetch_regdb(struct ath12k_base *ab, struct ath12k_board_data *bd) +{ + char boardname[BOARD_NAME_SIZE]; + int ret; + + ret = ath12k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); + if (ret) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "failed to create board name for regdb: %d", ret); + goto exit; + } + + ret = ath12k_core_fetch_board_data_api_n(ab, bd, boardname, + ATH12K_BD_IE_REGDB, + ATH12K_BD_IE_REGDB_NAME, + ATH12K_BD_IE_REGDB_DATA); + if (!ret) + goto exit; + + ret = ath12k_core_fetch_board_data_api_1(ab, bd, ATH12K_REGDB_FILE_NAME); + if (ret) + ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to fetch %s from %s\n", + ATH12K_REGDB_FILE_NAME, ab->hw_params->fw.dir); + +exit: + if (!ret) + ath12k_dbg(ab, ATH12K_DBG_BOOT, "fetched regdb\n"); + + return ret; +} + static void ath12k_core_stop(struct ath12k_base *ab) { if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags)) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 385fda03e913..ba0a30f1ea29 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -823,6 +823,7 @@ int ath12k_core_fetch_board_data_api_1(struct ath12k_base *ab, int ath12k_core_fetch_bdf(struct ath12k_base *ath12k, struct ath12k_board_data *bd); void ath12k_core_free_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd); +int ath12k_core_fetch_regdb(struct ath12k_base *ab, struct ath12k_board_data *bd); int ath12k_core_check_dt(struct ath12k_base *ath12k); int ath12k_core_check_smbios(struct ath12k_base *ab); void ath12k_core_halt(struct ath12k *ar); diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index 130d99f7e426..fa8230def22b 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -242,9 +242,16 @@ enum ath12k_bd_ie_board_type { ATH12K_BD_IE_BOARD_DATA = 1, }; +enum ath12k_bd_ie_regdb_type { + ATH12K_BD_IE_REGDB_NAME = 0, + ATH12K_BD_IE_REGDB_DATA = 1, +}; + enum ath12k_bd_ie_type { /* contains sub IEs of enum ath12k_bd_ie_board_type */ ATH12K_BD_IE_BOARD = 0, + /* contains sub IEs of enum ath12k_bd_ie_regdb_type */ + ATH12K_BD_IE_REGDB = 1, }; struct ath12k_hw_regs { @@ -314,6 +321,18 @@ struct ath12k_hw_regs { u32 hal_reo_status_ring_base; }; +static inline const char *ath12k_bd_ie_type_str(enum ath12k_bd_ie_type type) +{ + switch (type) { + case ATH12K_BD_IE_BOARD: + return "board data"; + case ATH12K_BD_IE_REGDB: + return "regdb data"; + } + + return "unknown"; +} + int ath12k_hw_init(struct ath12k_base *ab); #endif diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 77a132f6bbd1..f29291064e20 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -2423,8 +2423,7 @@ static int ath12k_qmi_load_bdf_qmi(struct ath12k_base *ab, break; case ATH12K_QMI_BDF_TYPE_REGDB: - ret = ath12k_core_fetch_board_data_api_1(ab, &bd, - ATH12K_REGDB_FILE_NAME); + ret = ath12k_core_fetch_regdb(ab, &bd); if (ret) { ath12k_warn(ab, "qmi failed to load regdb bin:\n"); goto out; From 52f8c45fa36df0754b4562b7b535be8475304ebe Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Sun, 17 Dec 2023 13:29:04 +0200 Subject: [PATCH 016/378] wifi: ath12k: support default regdb while searching board-2.bin for WCN7850 Sometimes board-2.bin does not have the regdb data which matches the parameters such as vendor, device, subsystem-vendor, subsystem-device etc. Add default regdb data with only 'bus=%s' into board-2.bin for WCN7850, then ath12k uses 'bus=pci' to search regdb data in board-2.bin for WCN7850. [ 46.114895] ath12k_pci 0000:03:00.0: boot using board name 'bus=pci,vendor=17cb,device=1107,subsystem-vendor=17cb,subsystem-device=3378,qmi-chip-id=2,qmi-board-id=260' [ 46.118167] ath12k_pci 0000:03:00.0: boot firmware request ath12k/WCN7850/hw2.0/board-2.bin size 380280 [ 46.118173] ath12k_pci 0000:03:00.0: board name [ 46.118176] ath12k_pci 0000:03:00.0: 00000000: 62 75 73 3d 70 63 69 bus=pci [ 46.118179] ath12k_pci 0000:03:00.0: failed to fetch regdb data for bus=pci,vendor=17cb,device=1107,subsystem-vendor=17cb,subsystem-device=3378,qmi-chip-id=2,qmi-board-id=260 from ath12k/WCN7850/hw2.0/board-2.bin [ 46.118239] ath12k_pci 0000:03:00.0: boot using board name 'bus=pci' [ 46.119842] ath12k_pci 0000:03:00.0: boot firmware request ath12k/WCN7850/hw2.0/board-2.bin size 380280 [ 46.119847] ath12k_pci 0000:03:00.0: board name [ 46.119849] ath12k_pci 0000:03:00.0: 00000000: 62 75 73 3d 70 63 69 bus=pci [ 46.119852] ath12k_pci 0000:03:00.0: boot found match regdb data for name 'bus=pci' [ 46.119855] ath12k_pci 0000:03:00.0: boot found regdb data for 'bus=pci' [ 46.119857] ath12k_pci 0000:03:00.0: fetched regdb Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231216060140.30611-6-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 53 +++++++++++++++++++------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 80fce8099bdf..d73e2d33a41e 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -105,7 +105,8 @@ int ath12k_core_resume(struct ath12k_base *ab) } static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, - size_t name_len, bool with_variant) + size_t name_len, bool with_variant, + bool bus_type_mode) { /* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */ char variant[9 + ATH12K_QMI_BDF_EXT_STR_LENGTH] = { 0 }; @@ -116,15 +117,20 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, switch (ab->id.bdf_search) { case ATH12K_BDF_SEARCH_BUS_AND_BOARD: - scnprintf(name, name_len, - "bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x,qmi-chip-id=%d,qmi-board-id=%d%s", - ath12k_bus_str(ab->hif.bus), - ab->id.vendor, ab->id.device, - ab->id.subsystem_vendor, - ab->id.subsystem_device, - ab->qmi.target.chip_id, - ab->qmi.target.board_id, - variant); + if (bus_type_mode) + scnprintf(name, name_len, + "bus=%s", + ath12k_bus_str(ab->hif.bus)); + else + scnprintf(name, name_len, + "bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x,qmi-chip-id=%d,qmi-board-id=%d%s", + ath12k_bus_str(ab->hif.bus), + ab->id.vendor, ab->id.device, + ab->id.subsystem_vendor, + ab->id.subsystem_device, + ab->qmi.target.chip_id, + ab->qmi.target.board_id, + variant); break; default: scnprintf(name, name_len, @@ -143,13 +149,19 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, true); + return __ath12k_core_create_board_name(ab, name, name_len, true, false); } static int ath12k_core_create_fallback_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, false); + return __ath12k_core_create_board_name(ab, name, name_len, false, false); +} + +static int ath12k_core_create_bus_type_board_name(struct ath12k_base *ab, char *name, + size_t name_len) +{ + return __ath12k_core_create_board_name(ab, name, name_len, false, true); } const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab, @@ -453,7 +465,7 @@ int ath12k_core_fetch_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd) int ath12k_core_fetch_regdb(struct ath12k_base *ab, struct ath12k_board_data *bd) { - char boardname[BOARD_NAME_SIZE]; + char boardname[BOARD_NAME_SIZE], default_boardname[BOARD_NAME_SIZE]; int ret; ret = ath12k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); @@ -470,6 +482,21 @@ int ath12k_core_fetch_regdb(struct ath12k_base *ab, struct ath12k_board_data *bd if (!ret) goto exit; + ret = ath12k_core_create_bus_type_board_name(ab, default_boardname, + BOARD_NAME_SIZE); + if (ret) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "failed to create default board name for regdb: %d", ret); + goto exit; + } + + ret = ath12k_core_fetch_board_data_api_n(ab, bd, default_boardname, + ATH12K_BD_IE_REGDB, + ATH12K_BD_IE_REGDB_NAME, + ATH12K_BD_IE_REGDB_DATA); + if (!ret) + goto exit; + ret = ath12k_core_fetch_board_data_api_1(ab, bd, ATH12K_REGDB_FILE_NAME); if (ret) ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to fetch %s from %s\n", From e7ab40b733094dfc50dad58bbce81f544af1d8cc Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 17 Dec 2023 09:26:46 -0800 Subject: [PATCH 017/378] wifi: ath12k: Make QMI message rules const Commit ff6d365898d4 ("soc: qcom: qmi: use const for struct qmi_elem_info") allows QMI message encoding/decoding rules to be const, so do that for ath12k. Compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20231217-ath12k-qmi_elem_info-const-v1-1-7ebb0de0a2b6@quicinc.com --- drivers/net/wireless/ath/ath12k/qmi.c | 64 +++++++++++++-------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index f29291064e20..536856234f3b 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -17,7 +17,7 @@ #define PLATFORM_CAP_PCIE_GLOBAL_RESET 0x08 #define ATH12K_QMI_MAX_CHUNK_SIZE 2097152 -static struct qmi_elem_info wlfw_host_mlo_chip_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_host_mlo_chip_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -61,7 +61,7 @@ static struct qmi_elem_info wlfw_host_mlo_chip_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -511,7 +511,7 @@ static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -528,7 +528,7 @@ static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -753,7 +753,7 @@ static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -789,7 +789,7 @@ static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -821,7 +821,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -863,7 +863,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -890,7 +890,7 @@ static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -930,7 +930,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -957,7 +957,7 @@ static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -975,7 +975,7 @@ static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -983,7 +983,7 @@ static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1009,7 +1009,7 @@ static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1026,7 +1026,7 @@ static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1042,7 +1042,7 @@ static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_dev_mem_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_dev_mem_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -1068,7 +1068,7 @@ static struct qmi_elem_info qmi_wlanfw_dev_mem_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1094,7 +1094,7 @@ static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1348,7 +1348,7 @@ static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -1483,7 +1483,7 @@ static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1501,7 +1501,7 @@ static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -1525,7 +1525,7 @@ static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1542,7 +1542,7 @@ static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1595,7 +1595,7 @@ static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1630,7 +1630,7 @@ static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_2_BYTE, .elem_len = 1, @@ -1654,7 +1654,7 @@ static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_shadow_reg_v3_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_shadow_reg_v3_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1671,7 +1671,7 @@ static struct qmi_elem_info qmi_wlanfw_shadow_reg_v3_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1706,7 +1706,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1724,7 +1724,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1862,7 +1862,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1879,14 +1879,14 @@ static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, }, }; -static struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, From aaf244141ed7195a9a56e03c2367f4a9d0b727a8 Mon Sep 17 00:00:00 2001 From: Zhenghao Gu Date: Wed, 20 Dec 2023 20:33:08 +0200 Subject: [PATCH 018/378] wifi: ath11k: fix IOMMU errors on buffer rings virt_to_phys() doesn't work on systems with IOMMU enabled, which have non-identity physical-to-IOVA mappings. It leads to IO_PAGE_FAULTs like this: [IO_PAGE_FAULT domain=0x0023 address=0x1cce00000 flags=0x0020] And no association to the AP can be established. This patch changes that to dma_map_single(), which works correctly. Even virt_to_phys() documentation says device drivers should not use it: This function does not give bus mappings for DMA transfers. In almost all conceivable cases a device driver should not be using this function Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Signed-off-by: Zhenghao Gu Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20231212031914.47339-1-imguzh@gmail.com --- drivers/net/wireless/ath/ath11k/dp.c | 20 +++++++++++++++++--- drivers/net/wireless/ath/ath11k/hal.c | 19 +++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index 8975dc57ad77..1a62407e5a9f 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -104,11 +104,14 @@ void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) if (!ring->vaddr_unaligned) return; - if (ring->cached) + if (ring->cached) { + dma_unmap_single(ab->dev, ring->paddr_unaligned, ring->size, + DMA_FROM_DEVICE); kfree(ring->vaddr_unaligned); - else + } else { dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned, ring->paddr_unaligned); + } ring->vaddr_unaligned = NULL; } @@ -249,7 +252,18 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, if (cached) { ring->vaddr_unaligned = kzalloc(ring->size, GFP_KERNEL); - ring->paddr_unaligned = virt_to_phys(ring->vaddr_unaligned); + if (!ring->vaddr_unaligned) + return -ENOMEM; + + ring->paddr_unaligned = dma_map_single(ab->dev, + ring->vaddr_unaligned, + ring->size, + DMA_FROM_DEVICE); + if (dma_mapping_error(ab->dev, ring->paddr_unaligned)) { + kfree(ring->vaddr_unaligned); + ring->vaddr_unaligned = NULL; + return -ENOMEM; + } } } diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c index c060c4b5c0cc..f3d04568c221 100644 --- a/drivers/net/wireless/ath/ath11k/hal.c +++ b/drivers/net/wireless/ath/ath11k/hal.c @@ -626,15 +626,30 @@ u32 *ath11k_hal_srng_dst_peek(struct ath11k_base *ab, struct hal_srng *srng) return NULL; } +static u32 *ath11k_hal_srng_dst_peek_with_dma(struct ath11k_base *ab, + struct hal_srng *srng, dma_addr_t *paddr) +{ + lockdep_assert_held(&srng->lock); + + if (srng->u.dst_ring.tp != srng->u.dst_ring.cached_hp) { + *paddr = srng->ring_base_paddr + + sizeof(*srng->ring_base_vaddr) * srng->u.dst_ring.tp; + return srng->ring_base_vaddr + srng->u.dst_ring.tp; + } + + return NULL; +} + static void ath11k_hal_srng_prefetch_desc(struct ath11k_base *ab, struct hal_srng *srng) { + dma_addr_t desc_paddr; u32 *desc; /* prefetch only if desc is available */ - desc = ath11k_hal_srng_dst_peek(ab, srng); + desc = ath11k_hal_srng_dst_peek_with_dma(ab, srng, &desc_paddr); if (likely(desc)) { - dma_sync_single_for_cpu(ab->dev, virt_to_phys(desc), + dma_sync_single_for_cpu(ab->dev, desc_paddr, (srng->entry_size * sizeof(u32)), DMA_FROM_DEVICE); prefetch(desc); From fba97a777dcb90896ab1dc32e796d85bb7bbcd69 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 Dec 2023 12:10:18 +0300 Subject: [PATCH 019/378] wifi: ath12k: refactor ath12k_wmi_tlv_parse_alloc() Since 'ath12k_wmi_tlv_parse_alloc()' always operates on 'skb->data, skb->len' tuple, it may be simplified to pass the only 'skb' argument instead (which implies refactoring of 'ath12k_pull_bcn_tx_status_ev()', 'ath12k_pull_chan_info_ev()' and 'ath12k_pull_pdev_temp_ev()' in the same way). This is an ath12k counterpart of the recently submitted ath11k patch and compile tested only as well. Signed-off-by: Dmitry Antipov Signed-off-by: Kalle Valo Link: https://msgid.link/20231219091022.70861-1-dmantipov@yandex.ru --- drivers/net/wireless/ath/ath12k/wmi.c | 69 +++++++++++++-------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 11cc3005c0f9..553d2566b3f7 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -359,8 +359,8 @@ static int ath12k_wmi_tlv_parse(struct ath12k_base *ar, const void **tb, } static const void ** -ath12k_wmi_tlv_parse_alloc(struct ath12k_base *ab, const void *ptr, - size_t len, gfp_t gfp) +ath12k_wmi_tlv_parse_alloc(struct ath12k_base *ab, + struct sk_buff *skb, gfp_t gfp) { const void **tb; int ret; @@ -369,7 +369,7 @@ ath12k_wmi_tlv_parse_alloc(struct ath12k_base *ab, const void *ptr, if (!tb) return ERR_PTR(-ENOMEM); - ret = ath12k_wmi_tlv_parse(ab, tb, ptr, len); + ret = ath12k_wmi_tlv_parse(ab, tb, skb->data, skb->len); if (ret) { kfree(tb); return ERR_PTR(ret); @@ -4374,7 +4374,7 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf const struct wmi_vdev_start_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -4452,7 +4452,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel list\n"); - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -4738,7 +4738,7 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * const struct wmi_peer_delete_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -4770,7 +4770,7 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab, const struct wmi_vdev_delete_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -4790,15 +4790,15 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab, return 0; } -static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab, void *evt_buf, - u32 len, u32 *vdev_id, - u32 *tx_status) +static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab, + struct sk_buff *skb, + u32 *vdev_id, u32 *tx_status) { const void **tb; const struct wmi_bcn_tx_status_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -4826,7 +4826,7 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_ const struct wmi_vdev_stopped_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -4970,7 +4970,7 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, const struct wmi_mgmt_tx_compl_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5141,7 +5141,7 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_scan_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5174,7 +5174,7 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf const struct wmi_peer_sta_kickout_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5201,7 +5201,7 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_roam_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5245,14 +5245,14 @@ static int freq_to_idx(struct ath12k *ar, int freq) return idx; } -static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, u8 *evt_buf, - u32 len, struct wmi_chan_info_event *ch_info_ev) +static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, + struct wmi_chan_info_event *ch_info_ev) { const void **tb; const struct wmi_chan_info_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5291,7 +5291,7 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_pdev_bss_chan_info_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5331,7 +5331,7 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk const struct wmi_vdev_install_key_compl_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5362,7 +5362,7 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff const struct wmi_peer_assoc_conf_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5384,13 +5384,13 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff } static int -ath12k_pull_pdev_temp_ev(struct ath12k_base *ab, u8 *evt_buf, - u32 len, const struct wmi_pdev_temperature_event *ev) +ath12k_pull_pdev_temp_ev(struct ath12k_base *ab, struct sk_buff *skb, + const struct wmi_pdev_temperature_event *ev) { const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5725,8 +5725,7 @@ static void ath12k_bcn_tx_status_event(struct ath12k_base *ab, struct sk_buff *s { u32 vdev_id, tx_status; - if (ath12k_pull_bcn_tx_status_ev(ab, skb->data, skb->len, - &vdev_id, &tx_status) != 0) { + if (ath12k_pull_bcn_tx_status_ev(ab, skb, &vdev_id, &tx_status) != 0) { ath12k_warn(ab, "failed to extract bcn tx status"); return; } @@ -6110,7 +6109,7 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb) /* HW channel counters frequency value in hertz */ u32 cc_freq_hz = ab->cc_freq_hz; - if (ath12k_pull_chan_info_ev(ab, skb->data, skb->len, &ch_info_ev) != 0) { + if (ath12k_pull_chan_info_ev(ab, skb, &ch_info_ev) != 0) { ath12k_warn(ab, "failed to extract chan info event"); return; } @@ -6395,7 +6394,7 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, const struct wmi_pdev_ctl_failsafe_chk_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6460,7 +6459,7 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, const u32 *vdev_ids; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6494,7 +6493,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff struct ath12k *ar; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6546,7 +6545,7 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, struct ath12k *ar; struct wmi_pdev_temperature_event ev = {0}; - if (ath12k_pull_pdev_temp_ev(ab, skb->data, skb->len, &ev) != 0) { + if (ath12k_pull_pdev_temp_ev(ab, skb, &ev) != 0) { ath12k_warn(ab, "failed to extract pdev temperature event"); return; } @@ -6573,7 +6572,7 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab, const struct wmi_fils_discovery_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, @@ -6603,7 +6602,7 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, const struct wmi_probe_resp_tx_status_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, @@ -6635,7 +6634,7 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); From e3d373ec4f02bf41379d91707e3e3f2a46464cd7 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:57 +0200 Subject: [PATCH 020/378] wifi: ath11k: add support to select 6 GHz regulatory type There are 3 types of regulatory rules for AP mode and 6 type for station mode. Add wmi_vdev_type and ieee80211_ap_reg_power to select the exact reg rules. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-2-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/reg.c | 70 +++++++++++++++++++++------ drivers/net/wireless/ath/ath11k/reg.h | 6 ++- drivers/net/wireless/ath/ath11k/wmi.c | 3 +- 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index b4fd4d2107c7..78b99ab10c63 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -618,25 +618,68 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, *rule_idx = i; } +enum wmi_reg_6ghz_ap_type +ath11k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type) +{ + switch (power_type) { + case IEEE80211_REG_LPI_AP: + return WMI_REG_INDOOR_AP; + case IEEE80211_REG_SP_AP: + return WMI_REG_STANDARD_POWER_AP; + case IEEE80211_REG_VLP_AP: + return WMI_REG_VERY_LOW_POWER_AP; + default: + return WMI_REG_MAX_AP_TYPE; + } +} + struct ieee80211_regdomain * ath11k_reg_build_regd(struct ath11k_base *ab, - struct cur_regulatory_info *reg_info, bool intersect) + struct cur_regulatory_info *reg_info, bool intersect, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type) { struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL; - struct cur_reg_rule *reg_rule; + struct cur_reg_rule *reg_rule, *reg_rule_6ghz; u8 i = 0, j = 0, k = 0; u8 num_rules; u16 max_bw; - u32 flags; + u32 flags, reg_6ghz_number, max_bw_6ghz; char alpha2[3]; num_rules = reg_info->num_5ghz_reg_rules + reg_info->num_2ghz_reg_rules; - /* FIXME: Currently taking reg rules for 6 GHz only from Indoor AP mode list. - * This can be updated after complete 6 GHz regulatory support is added. - */ - if (reg_info->is_ext_reg_event) - num_rules += reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP]; + if (reg_info->is_ext_reg_event) { + if (vdev_type == WMI_VDEV_TYPE_STA) { + enum wmi_reg_6ghz_ap_type ap_type; + + ap_type = ath11k_reg_ap_pwr_convert(power_type); + + if (ap_type == WMI_REG_MAX_AP_TYPE) + ap_type = WMI_REG_INDOOR_AP; + + reg_6ghz_number = reg_info->num_6ghz_rules_client + [ap_type][WMI_REG_DEFAULT_CLIENT]; + + if (reg_6ghz_number == 0) { + ap_type = WMI_REG_INDOOR_AP; + reg_6ghz_number = reg_info->num_6ghz_rules_client + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } + + reg_rule_6ghz = reg_info->reg_rules_6ghz_client_ptr + [ap_type][WMI_REG_DEFAULT_CLIENT]; + max_bw_6ghz = reg_info->max_bw_6ghz_client + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } else { + reg_6ghz_number = reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP]; + reg_rule_6ghz = + reg_info->reg_rules_6ghz_ap_ptr[WMI_REG_INDOOR_AP]; + max_bw_6ghz = reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP]; + } + + num_rules += reg_6ghz_number; + } if (!num_rules) goto ret; @@ -683,13 +726,10 @@ ath11k_reg_build_regd(struct ath11k_base *ab, * per other BW rule flags we pass from here */ flags = NL80211_RRF_AUTO_BW; - } else if (reg_info->is_ext_reg_event && - reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP] && - (k < reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP])) { - reg_rule = reg_info->reg_rules_6ghz_ap_ptr[WMI_REG_INDOOR_AP] + - k++; - max_bw = min_t(u16, reg_rule->max_bw, - reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP]); + } else if (reg_info->is_ext_reg_event && reg_6ghz_number && + k < reg_6ghz_number) { + reg_rule = reg_rule_6ghz + k++; + max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz); flags = NL80211_RRF_AUTO_BW; } else { break; diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h index f28902f85e41..989b27b16bea 100644 --- a/drivers/net/wireless/ath/ath11k/reg.h +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -34,7 +34,11 @@ void ath11k_reg_free(struct ath11k_base *ab); void ath11k_regd_update_work(struct work_struct *work); struct ieee80211_regdomain * ath11k_reg_build_regd(struct ath11k_base *ab, - struct cur_regulatory_info *reg_info, bool intersect); + struct cur_regulatory_info *reg_info, bool intersect, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type); int ath11k_regd_update(struct ath11k *ar); int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait); +enum wmi_reg_6ghz_ap_type +ath11k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type); #endif diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 89514899fcd5..58882829351e 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -7151,7 +7151,8 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, !ath11k_reg_is_world_alpha((char *)reg_info->alpha2)) intersect = true; - regd = ath11k_reg_build_regd(ab, reg_info, intersect); + regd = ath11k_reg_build_regd(ab, reg_info, intersect, + WMI_VDEV_TYPE_AP, IEEE80211_REG_LPI_AP); if (!regd) { ath11k_warn(ab, "failed to build regd from reg_info\n"); goto fallback; From 7004bdceef605e5c1c5ab4aaf282002ad7523ddd Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:57 +0200 Subject: [PATCH 021/378] wifi: ath11k: store cur_regulatory_info for each radio The regulatory info of WMI_REG_CHAN_LIST_CC_EXT_EVENTID is not saved in ath11k now, the info should be saved in ath11k. Save the info for each radio and support switch regulatory rules dynamically. As mac.c will also call ath11k_reg_handle_chan_list() in next patches move the function to reg.c. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Acked-by: Jeff Johnson Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-3-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/core.h | 1 + drivers/net/wireless/ath/ath11k/reg.c | 179 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/reg.h | 5 + drivers/net/wireless/ath/ath11k/wmi.c | 148 +++----------------- drivers/net/wireless/ath/ath11k/wmi.h | 1 + 5 files changed, 207 insertions(+), 127 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 7e3b6779f4e9..cc91f7d3ca8e 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -922,6 +922,7 @@ struct ath11k_base { * This may or may not be used during the runtime */ struct ieee80211_regdomain *new_regd[MAX_RADIOS]; + struct cur_regulatory_info *reg_info_store; /* Current DFS Regulatory */ enum ath11k_dfs_region dfs_region; diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index 78b99ab10c63..adcd9063a59c 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -798,6 +798,159 @@ ath11k_reg_build_regd(struct ath11k_base *ab, return new_regd; } +static bool ath11k_reg_is_world_alpha(char *alpha) +{ + if (alpha[0] == '0' && alpha[1] == '0') + return true; + + if (alpha[0] == 'n' && alpha[1] == 'a') + return true; + + return false; +} + +static enum wmi_vdev_type ath11k_reg_get_ar_vdev_type(struct ath11k *ar) +{ + struct ath11k_vif *arvif; + + /* Currently each struct ath11k maps to one struct ieee80211_hw/wiphy + * and one struct ieee80211_regdomain, so it could only store one group + * reg rules. It means multi-interface concurrency in the same ath11k is + * not support for the regdomain. So get the vdev type of the first entry + * now. After concurrency support for the regdomain, this should change. + */ + arvif = list_first_entry_or_null(&ar->arvifs, struct ath11k_vif, list); + if (arvif) + return arvif->vdev_type; + + return WMI_VDEV_TYPE_UNSPEC; +} + +int ath11k_reg_handle_chan_list(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, + enum ieee80211_ap_reg_power power_type) +{ + struct ieee80211_regdomain *regd; + bool intersect = false; + int pdev_idx; + struct ath11k *ar; + enum wmi_vdev_type vdev_type; + + ath11k_dbg(ab, ATH11K_DBG_WMI, "event reg handle chan list"); + + if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { + /* In case of failure to set the requested ctry, + * fw retains the current regd. We print a failure info + * and return from here. + */ + ath11k_warn(ab, "Failed to set the requested Country regulatory setting\n"); + return -EINVAL; + } + + pdev_idx = reg_info->phy_id; + + /* Avoid default reg rule updates sent during FW recovery if + * it is already available + */ + spin_lock(&ab->base_lock); + if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) && + ab->default_regd[pdev_idx]) { + spin_unlock(&ab->base_lock); + goto retfail; + } + spin_unlock(&ab->base_lock); + + if (pdev_idx >= ab->num_radios) { + /* Process the event for phy0 only if single_pdev_only + * is true. If pdev_idx is valid but not 0, discard the + * event. Otherwise, it goes to fallback. In either case + * ath11k_reg_reset_info() needs to be called to avoid + * memory leak issue. + */ + ath11k_reg_reset_info(reg_info); + + if (ab->hw_params.single_pdev_only && + pdev_idx < ab->hw_params.num_rxmda_per_pdev) + return 0; + goto fallback; + } + + /* Avoid multiple overwrites to default regd, during core + * stop-start after mac registration. + */ + if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && + !memcmp((char *)ab->default_regd[pdev_idx]->alpha2, + (char *)reg_info->alpha2, 2)) + goto retfail; + + /* Intersect new rules with default regd if a new country setting was + * requested, i.e a default regd was already set during initialization + * and the regd coming from this event has a valid country info. + */ + if (ab->default_regd[pdev_idx] && + !ath11k_reg_is_world_alpha((char *) + ab->default_regd[pdev_idx]->alpha2) && + !ath11k_reg_is_world_alpha((char *)reg_info->alpha2)) + intersect = true; + + ar = ab->pdevs[pdev_idx].ar; + vdev_type = ath11k_reg_get_ar_vdev_type(ar); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi handle chan list power type %d vdev type %d intersect %d\n", + power_type, vdev_type, intersect); + + regd = ath11k_reg_build_regd(ab, reg_info, intersect, vdev_type, power_type); + if (!regd) { + ath11k_warn(ab, "failed to build regd from reg_info\n"); + goto fallback; + } + + if (power_type == IEEE80211_REG_UNSET_AP) { + ath11k_reg_reset_info(&ab->reg_info_store[pdev_idx]); + ab->reg_info_store[pdev_idx] = *reg_info; + } + + spin_lock(&ab->base_lock); + if (ab->default_regd[pdev_idx]) { + /* The initial rules from FW after WMI Init is to build + * the default regd. From then on, any rules updated for + * the pdev could be due to user reg changes. + * Free previously built regd before assigning the newly + * generated regd to ar. NULL pointer handling will be + * taken care by kfree itself. + */ + ar = ab->pdevs[pdev_idx].ar; + kfree(ab->new_regd[pdev_idx]); + ab->new_regd[pdev_idx] = regd; + queue_work(ab->workqueue, &ar->regd_update_work); + } else { + /* This regd would be applied during mac registration and is + * held constant throughout for regd intersection purpose + */ + ab->default_regd[pdev_idx] = regd; + } + ab->dfs_region = reg_info->dfs_region; + spin_unlock(&ab->base_lock); + + return 0; + +fallback: + /* Fallback to older reg (by sending previous country setting + * again if fw has succeeded and we failed to process here. + * The Regdomain should be uniform across driver and fw. Since the + * FW has processed the command and sent a success status, we expect + * this function to succeed as well. If it doesn't, CTRY needs to be + * reverted at the fw and the old SCAN_CHAN_LIST cmd needs to be sent. + */ + /* TODO: This is rare, but still should also be handled */ + WARN_ON(1); + +retfail: + + return -EINVAL; +} + void ath11k_regd_update_work(struct work_struct *work) { struct ath11k *ar = container_of(work, struct ath11k, @@ -821,10 +974,36 @@ void ath11k_reg_init(struct ath11k *ar) ar->hw->wiphy->reg_notifier = ath11k_reg_notifier; } +void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info) +{ + int i, j; + + if (!reg_info) + return; + + kfree(reg_info->reg_rules_2ghz_ptr); + kfree(reg_info->reg_rules_5ghz_ptr); + + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { + kfree(reg_info->reg_rules_6ghz_ap_ptr[i]); + + for (j = 0; j < WMI_REG_MAX_CLIENT_TYPE; j++) + kfree(reg_info->reg_rules_6ghz_client_ptr[i][j]); + } + + memset(reg_info, 0, sizeof(*reg_info)); +} + void ath11k_reg_free(struct ath11k_base *ab) { int i; + for (i = 0; i < ab->num_radios; i++) + ath11k_reg_reset_info(&ab->reg_info_store[i]); + + kfree(ab->reg_info_store); + ab->reg_info_store = NULL; + for (i = 0; i < ab->hw_params.max_radios; i++) { kfree(ab->default_regd[i]); kfree(ab->new_regd[i]); diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h index 989b27b16bea..64edb794260a 100644 --- a/drivers/net/wireless/ath/ath11k/reg.h +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -30,6 +30,7 @@ enum ath11k_dfs_region { /* ATH11K Regulatory API's */ void ath11k_reg_init(struct ath11k *ar); +void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info); void ath11k_reg_free(struct ath11k_base *ab); void ath11k_regd_update_work(struct work_struct *work); struct ieee80211_regdomain * @@ -41,4 +42,8 @@ int ath11k_regd_update(struct ath11k *ar); int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait); enum wmi_reg_6ghz_ap_type ath11k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type); +int ath11k_reg_handle_chan_list(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, + enum ieee80211_ap_reg_power power_type); + #endif diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 58882829351e..7e604d4b9a77 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -4749,6 +4749,14 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc, soc->pdevs[0].pdev_id = 0; } + if (!soc->reg_info_store) { + soc->reg_info_store = kcalloc(soc->num_radios, + sizeof(*soc->reg_info_store), + GFP_ATOMIC); + if (!soc->reg_info_store) + return -ENOMEM; + } + return 0; } @@ -7060,32 +7068,15 @@ static void ath11k_wmi_htc_tx_complete(struct ath11k_base *ab, wake_up(&wmi->tx_ce_desc_wq); } -static bool ath11k_reg_is_world_alpha(char *alpha) -{ - if (alpha[0] == '0' && alpha[1] == '0') - return true; - - if (alpha[0] == 'n' && alpha[1] == 'a') - return true; - - return false; -} - -static int ath11k_reg_chan_list_event(struct ath11k_base *ab, - struct sk_buff *skb, +static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb, enum wmi_reg_chan_list_cmd_type id) { - struct cur_regulatory_info *reg_info = NULL; - struct ieee80211_regdomain *regd = NULL; - bool intersect = false; - int ret = 0, pdev_idx, i, j; - struct ath11k *ar; + struct cur_regulatory_info *reg_info; + int ret; reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC); - if (!reg_info) { - ret = -ENOMEM; - goto fallback; - } + if (!reg_info) + return -ENOMEM; if (id == WMI_REG_CHAN_LIST_CC_ID) ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info); @@ -7093,119 +7084,22 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info); if (ret) { - ath11k_warn(ab, "failed to extract regulatory info from received event\n"); - goto fallback; - } - - ath11k_dbg(ab, ATH11K_DBG_WMI, "event reg chan list id %d", id); - - if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { - /* In case of failure to set the requested ctry, - * fw retains the current regd. We print a failure info - * and return from here. - */ - ath11k_warn(ab, "Failed to set the requested Country regulatory setting\n"); + ath11k_warn(ab, "failed to extract regulatory info\n"); goto mem_free; } - pdev_idx = reg_info->phy_id; - - /* Avoid default reg rule updates sent during FW recovery if - * it is already available - */ - spin_lock(&ab->base_lock); - if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) && - ab->default_regd[pdev_idx]) { - spin_unlock(&ab->base_lock); + ret = ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_UNSET_AP); + if (ret) { + ath11k_warn(ab, "failed to process regulatory info %d\n", ret); goto mem_free; } - spin_unlock(&ab->base_lock); - if (pdev_idx >= ab->num_radios) { - /* Process the event for phy0 only if single_pdev_only - * is true. If pdev_idx is valid but not 0, discard the - * event. Otherwise, it goes to fallback. - */ - if (ab->hw_params.single_pdev_only && - pdev_idx < ab->hw_params.num_rxmda_per_pdev) - goto mem_free; - else - goto fallback; - } + kfree(reg_info); + return 0; - /* Avoid multiple overwrites to default regd, during core - * stop-start after mac registration. - */ - if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && - !memcmp((char *)ab->default_regd[pdev_idx]->alpha2, - (char *)reg_info->alpha2, 2)) - goto mem_free; - - /* Intersect new rules with default regd if a new country setting was - * requested, i.e a default regd was already set during initialization - * and the regd coming from this event has a valid country info. - */ - if (ab->default_regd[pdev_idx] && - !ath11k_reg_is_world_alpha((char *) - ab->default_regd[pdev_idx]->alpha2) && - !ath11k_reg_is_world_alpha((char *)reg_info->alpha2)) - intersect = true; - - regd = ath11k_reg_build_regd(ab, reg_info, intersect, - WMI_VDEV_TYPE_AP, IEEE80211_REG_LPI_AP); - if (!regd) { - ath11k_warn(ab, "failed to build regd from reg_info\n"); - goto fallback; - } - - spin_lock(&ab->base_lock); - if (ab->default_regd[pdev_idx]) { - /* The initial rules from FW after WMI Init is to build - * the default regd. From then on, any rules updated for - * the pdev could be due to user reg changes. - * Free previously built regd before assigning the newly - * generated regd to ar. NULL pointer handling will be - * taken care by kfree itself. - */ - ar = ab->pdevs[pdev_idx].ar; - kfree(ab->new_regd[pdev_idx]); - ab->new_regd[pdev_idx] = regd; - queue_work(ab->workqueue, &ar->regd_update_work); - } else { - /* This regd would be applied during mac registration and is - * held constant throughout for regd intersection purpose - */ - ab->default_regd[pdev_idx] = regd; - } - ab->dfs_region = reg_info->dfs_region; - spin_unlock(&ab->base_lock); - - goto mem_free; - -fallback: - /* Fallback to older reg (by sending previous country setting - * again if fw has succeeded and we failed to process here. - * The Regdomain should be uniform across driver and fw. Since the - * FW has processed the command and sent a success status, we expect - * this function to succeed as well. If it doesn't, CTRY needs to be - * reverted at the fw and the old SCAN_CHAN_LIST cmd needs to be sent. - */ - /* TODO: This is rare, but still should also be handled */ - WARN_ON(1); mem_free: - if (reg_info) { - kfree(reg_info->reg_rules_2ghz_ptr); - kfree(reg_info->reg_rules_5ghz_ptr); - if (reg_info->is_ext_reg_event) { - for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) - kfree(reg_info->reg_rules_6ghz_ap_ptr[i]); - - for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) - for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) - kfree(reg_info->reg_rules_6ghz_client_ptr[j][i]); - } - kfree(reg_info); - } + ath11k_reg_reset_info(reg_info); + kfree(reg_info); return ret; } diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index b1b4ee804b7b..a466d2bb4bb8 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -4951,6 +4951,7 @@ struct ath11k_targ_cap { }; enum wmi_vdev_type { + WMI_VDEV_TYPE_UNSPEC = 0, WMI_VDEV_TYPE_AP = 1, WMI_VDEV_TYPE_STA = 2, WMI_VDEV_TYPE_IBSS = 3, From cf2df0080bd59cb97a1519ddefaf59788febdaa5 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 11 Jan 2024 15:56:57 +0200 Subject: [PATCH 022/378] wifi: ath11k: fix a possible dead lock caused by ab->base_lock spin_lock()/spin_unlock() are used in ath11k_reg_chan_list_event() to acquire/release ab->base_lock. For now this is safe because that function is only called in soft IRQ context. But ath11k_reg_chan_list_event() will be called from process context in an upcoming patch, and this can result in a deadlock if ab->base_lock is acquired in process context and then soft IRQ occurs on the same CPU and tries to acquire that lock. Fix it by using spin_lock_bh() and spin_unlock_bh() instead. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Fixes: 69a0fcf8a9f2 ("ath11k: Avoid reg rules update during firmware recovery") Signed-off-by: Baochen Qiang Signed-off-by: Wen Gong Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-4-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/reg.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index adcd9063a59c..d4fd3509e608 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -852,13 +852,13 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab, /* Avoid default reg rule updates sent during FW recovery if * it is already available */ - spin_lock(&ab->base_lock); + spin_lock_bh(&ab->base_lock); if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) && ab->default_regd[pdev_idx]) { - spin_unlock(&ab->base_lock); + spin_unlock_bh(&ab->base_lock); goto retfail; } - spin_unlock(&ab->base_lock); + spin_unlock_bh(&ab->base_lock); if (pdev_idx >= ab->num_radios) { /* Process the event for phy0 only if single_pdev_only @@ -911,7 +911,7 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab, ab->reg_info_store[pdev_idx] = *reg_info; } - spin_lock(&ab->base_lock); + spin_lock_bh(&ab->base_lock); if (ab->default_regd[pdev_idx]) { /* The initial rules from FW after WMI Init is to build * the default regd. From then on, any rules updated for @@ -931,7 +931,7 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab, ab->default_regd[pdev_idx] = regd; } ab->dfs_region = reg_info->dfs_region; - spin_unlock(&ab->base_lock); + spin_unlock_bh(&ab->base_lock); return 0; From 17144d32e90708cd5532055f3d9894ced9ac45a2 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:57 +0200 Subject: [PATCH 023/378] wifi: ath11k: update regulatory rules when interface added There are two power types for 6 GHz regulatory, one is AP, another is client. When firmware boots up, WMI_REG_CHAN_LIST_CC_EXT_EVENTID is sent from firmware at an early stage, the interface mode is not decided at this point, then ath11k select reg rules of AP type as default. After interface is created, it is exactly decided the interface type such as AP/mesh point/station. Then ath11k need to update reg rules to the exact power type matched to the interface type. The client power type is used for station interface, and AP power type is used for AP/mesh point interface. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Acked-by: Jeff Johnson Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-5-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 8 ++++++++ drivers/net/wireless/ath/ath11k/wmi.c | 6 ++++++ drivers/net/wireless/ath/ath11k/wmi.h | 1 + 3 files changed, 15 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index db241589424d..3f52428433d1 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6949,6 +6949,14 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, ret); } + if (ath11k_wmi_supports_6ghz_cc_ext(ar)) { + struct cur_regulatory_info *reg_info; + + reg_info = &ab->reg_info_store[ar->pdev_idx]; + ath11k_dbg(ab, ATH11K_DBG_MAC, "interface added to change reg rules\n"); + ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_LPI_AP); + } + mutex_unlock(&ar->conf_mutex); return 0; diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 7e604d4b9a77..5c6f3bdcb4af 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -9687,3 +9687,9 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar, return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); } + +bool ath11k_wmi_supports_6ghz_cc_ext(struct ath11k *ar) +{ + return test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT, + ar->ab->wmi_ab.svc_map) && ar->supports_6ghz; +} diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index a466d2bb4bb8..528756198a7a 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -6480,5 +6480,6 @@ int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_va int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar); int ath11k_wmi_sta_keepalive(struct ath11k *ar, const struct wmi_sta_keepalive_arg *arg); +bool ath11k_wmi_supports_6ghz_cc_ext(struct ath11k *ar); #endif From 1329beb56297021042788223e666f6ccd2e11a0a Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:58 +0200 Subject: [PATCH 024/378] wifi: ath11k: update regulatory rules when connect to AP on 6 GHz band for station When station connect to AP on 6 GHz band, it needs switch the regulatory rules according to the regulatory info sub field in HE operation element. Switch to the power type which AP used for station interface. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Acked-by: Jeff Johnson Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-6-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 3f52428433d1..0b2b79ca5b1d 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -7619,6 +7619,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); int ret; struct peer_create_params param; + struct cur_regulatory_info *reg_info; + enum ieee80211_ap_reg_power power_type; mutex_lock(&ar->conf_mutex); @@ -7626,6 +7628,22 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, "chanctx assign ptr %p vdev_id %i\n", ctx, arvif->vdev_id); + if (ath11k_wmi_supports_6ghz_cc_ext(ar) && + ctx->def.chan->band == NL80211_BAND_6GHZ && + arvif->vdev_type == WMI_VDEV_TYPE_STA) { + reg_info = &ab->reg_info_store[ar->pdev_idx]; + power_type = vif->bss_conf.power_type; + + ath11k_dbg(ab, ATH11K_DBG_MAC, "chanctx power type %d\n", power_type); + + if (power_type == IEEE80211_REG_UNSET_AP) { + ret = -EINVAL; + goto out; + } + + ath11k_reg_handle_chan_list(ab, reg_info, power_type); + } + /* for QCA6390 bss peer must be created before vdev_start */ if (ab->hw_params.vdev_start_delay && arvif->vdev_type != WMI_VDEV_TYPE_AP && From 28f64d368b21c551d5faf33687e7228531256d10 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:58 +0200 Subject: [PATCH 025/378] wifi: ath11k: save power spectral density(PSD) of regulatory rule Save the power spectral density (PSD) report from firmware to struct ieee80211_reg_rule. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Acked-by: Jeff Johnson Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-7-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/reg.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index d4fd3509e608..737fcd450d4b 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -425,6 +425,11 @@ static void ath11k_reg_intersect_rules(struct ieee80211_reg_rule *rule1, /* Use the flags of both the rules */ new_rule->flags = rule1->flags | rule2->flags; + if ((rule1->flags & NL80211_RRF_PSD) && (rule2->flags & NL80211_RRF_PSD)) + new_rule->psd = min_t(s8, rule1->psd, rule2->psd); + else + new_rule->flags &= ~NL80211_RRF_PSD; + /* To be safe, lts use the max cac timeout of both rules */ new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms, rule2->dfs_cac_ms); @@ -527,13 +532,14 @@ ath11k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw) static void ath11k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq, u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr, - u32 reg_flags) + s8 psd, u32 reg_flags) { reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq); reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq); reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw); reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain); reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr); + reg_rule->psd = psd; reg_rule->flags = reg_flags; } @@ -563,7 +569,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, reg_rule->start_freq, ETSI_WEATHER_RADAR_BAND_LOW, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); ath11k_dbg(ab, ATH11K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", @@ -584,7 +590,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, ath11k_reg_update_rule(regd->reg_rules + i, start_freq, end_freq, bw, reg_rule->ant_gain, - reg_rule->reg_power, flags); + reg_rule->reg_power, reg_rule->psd_eirp, flags); regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT; @@ -605,7 +611,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, ETSI_WEATHER_RADAR_BAND_HIGH, reg_rule->end_freq, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); ath11k_dbg(ab, ATH11K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", @@ -731,6 +737,8 @@ ath11k_reg_build_regd(struct ath11k_base *ab, reg_rule = reg_rule_6ghz + k++; max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz); flags = NL80211_RRF_AUTO_BW; + if (reg_rule->psd_flag) + flags |= NL80211_RRF_PSD; } else { break; } @@ -742,7 +750,7 @@ ath11k_reg_build_regd(struct ath11k_base *ab, reg_rule->start_freq, reg_rule->end_freq, max_bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); /* Update dfs cac timeout if the dfs domain is ETSI and the * new rule covers weather radar band. From 6f4e235be6550f67791616d88682fb99f4e670e4 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:58 +0200 Subject: [PATCH 026/378] wifi: ath11k: add parse of transmit power envelope element MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The transmit power envelope element has some fields for power, ath11k should parse it according to IEEE Std 802.11ax™‐2021. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Acked-by: Jeff Johnson Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-8-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/core.h | 39 +++++ drivers/net/wireless/ath/ath11k/mac.c | 188 +++++++++++++++++++++++++ 2 files changed, 227 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index cc91f7d3ca8e..9a398de5dc44 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -314,6 +314,43 @@ struct ath11k_rekey_data { bool enable_offload; }; +/** + * struct ath11k_chan_power_info - TPE containing power info per channel chunk + * @chan_cfreq: channel center freq (MHz) + * e.g. + * channel 37/20 MHz, it is 6135 + * channel 37/40 MHz, it is 6125 + * channel 37/80 MHz, it is 6145 + * channel 37/160 MHz, it is 6185 + * @tx_power: transmit power (dBm) + */ +struct ath11k_chan_power_info { + u16 chan_cfreq; + s8 tx_power; +}; + +/** + * struct ath11k_reg_tpc_power_info - regulatory TPC power info + * @is_psd_power: is PSD power or not + * @eirp_power: Maximum EIRP power (dBm), valid only if power is PSD + * @ap_power_type: type of power (SP/LPI/VLP) + * @num_pwr_levels: number of power levels + * @reg_max: Array of maximum TX power (dBm) per PSD value + * @ap_constraint_power: AP constraint power (dBm) + * @tpe: TPE values processed from TPE IE + * @chan_power_info: power info to send to firmware + */ +struct ath11k_reg_tpc_power_info { + bool is_psd_power; + u8 eirp_power; + enum wmi_reg_6ghz_ap_type ap_power_type; + u8 num_pwr_levels; + u8 reg_max[IEEE80211_MAX_NUM_PWR_LEVEL]; + u8 ap_constraint_power; + s8 tpe[IEEE80211_MAX_NUM_PWR_LEVEL]; + struct ath11k_chan_power_info chan_power_info[IEEE80211_MAX_NUM_PWR_LEVEL]; +}; + struct ath11k_vif { u32 vdev_id; enum wmi_vdev_type vdev_type; @@ -369,6 +406,8 @@ struct ath11k_vif { struct ath11k_arp_ns_offload arp_ns_offload; struct ath11k_rekey_data rekey_data; + struct ath11k_reg_tpc_power_info reg_tpc_info; + #ifdef CONFIG_ATH11K_DEBUGFS struct dentry *debugfs_twt; #endif /* CONFIG_ATH11K_DEBUGFS */ diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 0b2b79ca5b1d..4be5b2a35ec9 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -7608,6 +7608,192 @@ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, return 0; } +static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt) +{ + switch (txpwr_intrprt) { + /* Refer "Table 9-276-Meaning of Maximum Transmit Power Count subfield + * if the Maximum Transmit Power Interpretation subfield is 0 or 2" of + * "IEEE Std 802.11ax 2021". + */ + case IEEE80211_TPE_LOCAL_EIRP: + case IEEE80211_TPE_REG_CLIENT_EIRP: + txpwr_cnt = txpwr_cnt <= 3 ? txpwr_cnt : 3; + txpwr_cnt = txpwr_cnt + 1; + break; + /* Refer "Table 9-277-Meaning of Maximum Transmit Power Count subfield + * if Maximum Transmit Power Interpretation subfield is 1 or 3" of + * "IEEE Std 802.11ax 2021". + */ + case IEEE80211_TPE_LOCAL_EIRP_PSD: + case IEEE80211_TPE_REG_CLIENT_EIRP_PSD: + txpwr_cnt = txpwr_cnt <= 4 ? txpwr_cnt : 4; + txpwr_cnt = txpwr_cnt ? (BIT(txpwr_cnt - 1)) : 1; + break; + } + + return txpwr_cnt; +} + +static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def) +{ + if (chan_def->chan->flags & IEEE80211_CHAN_PSD) { + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20: + return 1; + case NL80211_CHAN_WIDTH_40: + return 2; + case NL80211_CHAN_WIDTH_80: + return 4; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + return 8; + default: + return 1; + } + } else { + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20: + return 1; + case NL80211_CHAN_WIDTH_40: + return 2; + case NL80211_CHAN_WIDTH_80: + return 3; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + return 4; + default: + return 1; + } + } +} + +static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + struct ieee80211_tx_pwr_env *single_tpe; + enum wmi_reg_6ghz_client_type client_type; + struct cur_regulatory_info *reg_info; + int i; + u8 pwr_count, pwr_interpret, pwr_category; + u8 psd_index = 0, non_psd_index = 0, local_tpe_count = 0, reg_tpe_count = 0; + bool use_local_tpe, non_psd_set = false, psd_set = false; + + reg_info = &ab->reg_info_store[ar->pdev_idx]; + client_type = reg_info->client_type; + + for (i = 0; i < bss_conf->tx_pwr_env_num; i++) { + single_tpe = &bss_conf->tx_pwr_env[i]; + pwr_category = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_CATEGORY); + pwr_interpret = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + + if (pwr_category == client_type) { + if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP || + pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) + local_tpe_count++; + else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP || + pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) + reg_tpe_count++; + } + } + + if (!reg_tpe_count && !local_tpe_count) { + ath11k_warn(ab, + "no transmit power envelope match client power type %d\n", + client_type); + return; + } else if (!reg_tpe_count) { + use_local_tpe = true; + } else { + use_local_tpe = false; + } + + for (i = 0; i < bss_conf->tx_pwr_env_num; i++) { + single_tpe = &bss_conf->tx_pwr_env[i]; + pwr_category = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_CATEGORY); + pwr_interpret = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + + if (pwr_category != client_type) + continue; + + /* get local transmit power envelope */ + if (use_local_tpe) { + if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP) { + non_psd_index = i; + non_psd_set = true; + } else if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) { + psd_index = i; + psd_set = true; + } + /* get regulatory transmit power envelope */ + } else { + if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP) { + non_psd_index = i; + non_psd_set = true; + } else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) { + psd_index = i; + psd_set = true; + } + } + } + + if (non_psd_set && !psd_set) { + single_tpe = &bss_conf->tx_pwr_env[non_psd_index]; + pwr_count = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_COUNT); + pwr_interpret = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + arvif->reg_tpc_info.is_psd_power = false; + arvif->reg_tpc_info.eirp_power = 0; + + arvif->reg_tpc_info.num_pwr_levels = + ath11k_mac_get_tpe_count(pwr_interpret, pwr_count); + + for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) { + ath11k_dbg(ab, ATH11K_DBG_MAC, + "non PSD power[%d] : %d\n", + i, single_tpe->tx_power[i]); + arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2; + } + } + + if (psd_set) { + single_tpe = &bss_conf->tx_pwr_env[psd_index]; + pwr_count = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_COUNT); + pwr_interpret = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + arvif->reg_tpc_info.is_psd_power = true; + + if (pwr_count == 0) { + ath11k_dbg(ab, ATH11K_DBG_MAC, + "TPE PSD power : %d\n", single_tpe->tx_power[0]); + arvif->reg_tpc_info.num_pwr_levels = + ath11k_mac_get_num_pwr_levels(&ctx->def); + + for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) + arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[0] / 2; + } else { + arvif->reg_tpc_info.num_pwr_levels = + ath11k_mac_get_tpe_count(pwr_interpret, pwr_count); + + for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) { + ath11k_dbg(ab, ATH11K_DBG_MAC, + "TPE PSD power[%d] : %d\n", + i, single_tpe->tx_power[i]); + arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2; + } + } + } +} + static int ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -7642,6 +7828,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, } ath11k_reg_handle_chan_list(ab, reg_info, power_type); + + ath11k_mac_parse_tx_pwr_env(ar, vif, ctx); } /* for QCA6390 bss peer must be created before vdev_start */ From 46f20de2c4f8faadea12679a3edb2082f35dcf1e Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:58 +0200 Subject: [PATCH 027/378] wifi: ath11k: save max transmit power in vdev start response event from firmware Save the max transmit power received in the vdev start response event from firmware. A subsequent patch will use this to calculate the final power value for WMI_VDEV_SET_TPC_POWER_CMDID. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Acked-by: Jeff Johnson Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-9-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/core.h | 1 + drivers/net/wireless/ath/ath11k/wmi.c | 3 ++- drivers/net/wireless/ath/ath11k/wmi.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 9a398de5dc44..12fa5b28520f 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -778,6 +778,7 @@ struct ath11k { /* protected by conf_mutex */ bool ps_state_enable; bool ps_timekeeper_enable; + s8 max_allowed_tx_power; }; struct ath11k_band_cap { diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 5c6f3bdcb4af..3a3a2b697119 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -5036,6 +5036,7 @@ static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buf vdev_rsp->mac_id = ev->mac_id; vdev_rsp->cfgd_tx_streams = ev->cfgd_tx_streams; vdev_rsp->cfgd_rx_streams = ev->cfgd_rx_streams; + vdev_rsp->max_allowed_tx_power = ev->max_allowed_tx_power; kfree(tb); return 0; @@ -7257,7 +7258,7 @@ static void ath11k_vdev_start_resp_event(struct ath11k_base *ab, struct sk_buff } ar->last_wmi_vdev_start_status = 0; - + ar->max_allowed_tx_power = vdev_start_resp.max_allowed_tx_power; status = vdev_start_resp.status; if (WARN_ON_ONCE(status)) { diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 528756198a7a..7f6287f22cf5 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -4119,6 +4119,7 @@ struct wmi_vdev_start_resp_event { }; u32 cfgd_tx_streams; u32 cfgd_rx_streams; + s32 max_allowed_tx_power; } __packed; /* VDEV start response status codes */ From 92425f788feede9bf152ecf3fb7a264942ee7719 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:58 +0200 Subject: [PATCH 028/378] wifi: ath11k: fill parameters for vdev set tpc power WMI command Prepare the parameters which are needed for WMI command WMI_VDEV_SET_TPC_POWER_CMDID. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Acked-by: Jeff Johnson Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-10-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 284 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/mac.h | 5 +- 2 files changed, 288 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 4be5b2a35ec9..f812a6f44b82 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -7667,6 +7667,290 @@ static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def) } } +static u16 ath11k_mac_get_6ghz_start_frequency(struct cfg80211_chan_def *chan_def) +{ + u16 diff_seq; + + /* It is to get the lowest channel number's center frequency of the chan. + * For example, + * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1 + * with center frequency 5955, its diff is 5965 - 5955 = 10. + * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1 + * with center frequency 5955, its diff is 5985 - 5955 = 30. + * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1 + * with center frequency 5955, its diff is 6025 - 5955 = 70. + */ + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_160: + diff_seq = 70; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + diff_seq = 30; + break; + case NL80211_CHAN_WIDTH_40: + diff_seq = 10; + break; + default: + diff_seq = 0; + } + + return chan_def->center_freq1 - diff_seq; +} + +static u16 ath11k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def, + u16 start_seq, u8 seq) +{ + u16 seg_seq; + + /* It is to get the center frequency of the specific bandwidth. + * start_seq means the lowest channel number's center frequency. + * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz&80P80. + * For example, + * lowest channel is 1, its center frequency 5955, + * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0. + * lowest channel is 1, its center frequency 5955, + * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10. + * lowest channel is 1, its center frequency 5955, + * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30. + * lowest channel is 1, its center frequency 5955, + * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70. + */ + if (chan_def->width == NL80211_CHAN_WIDTH_80P80 && seq == 3) + return chan_def->center_freq2; + + seg_seq = 10 * (BIT(seq) - 1); + return seg_seq + start_seq; +} + +static void ath11k_mac_get_psd_channel(struct ath11k *ar, + u16 step_freq, + u16 *start_freq, + u16 *center_freq, + u8 i, + struct ieee80211_channel **temp_chan, + s8 *tx_power) +{ + /* It is to get the center frequency for each 20 MHz. + * For example, if the chan is 160 MHz and center frequency is 6025, + * then it include 8 channels, they are 1/5/9/13/17/21/25/29, + * channel number 1's center frequency is 5955, it is parameter start_freq. + * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels. + * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7, + * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095, + * the gap is 20 for each channel, parameter step_freq means the gap. + * after get the center frequency of each channel, it is easy to find the + * struct ieee80211_channel of it and get the max_reg_power. + */ + *center_freq = *start_freq + i * step_freq; + *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq); + *tx_power = (*temp_chan)->max_reg_power; +} + +static void ath11k_mac_get_eirp_power(struct ath11k *ar, + u16 *start_freq, + u16 *center_freq, + u8 i, + struct ieee80211_channel **temp_chan, + struct cfg80211_chan_def *def, + s8 *tx_power) +{ + /* It is to get the center frequency for 20 MHz/40 MHz/80 MHz/ + * 160 MHz&80P80 bandwidth, and then plus 10 to the center frequency, + * it is the center frequency of a channel number. + * For example, when configured channel number is 1. + * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975, + * then it is channel number 5. + * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995, + * then it is channel number 9. + * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035, + * then it is channel number 17. + * after get the center frequency of each channel, it is easy to find the + * struct ieee80211_channel of it and get the max_reg_power. + */ + *center_freq = ath11k_mac_get_seg_freq(def, *start_freq, i); + + /* For the 20 MHz, its center frequency is same with same channel */ + if (i != 0) + *center_freq += 10; + + *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq); + *tx_power = (*temp_chan)->max_reg_power; +} + +void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + struct ath11k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info; + struct ieee80211_channel *chan, *temp_chan; + u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction; + bool is_psd_power = false, is_tpe_present = false; + s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL], + psd_power, tx_power, eirp_power; + u16 start_freq, center_freq; + + chan = ctx->def.chan; + start_freq = ath11k_mac_get_6ghz_start_frequency(&ctx->def); + pwr_reduction = bss_conf->pwr_reduction; + + if (arvif->reg_tpc_info.num_pwr_levels) { + is_tpe_present = true; + num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels; + } else { + num_pwr_levels = ath11k_mac_get_num_pwr_levels(&ctx->def); + } + + for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) { + /* STA received TPE IE*/ + if (is_tpe_present) { + /* local power is PSD power*/ + if (chan->flags & IEEE80211_CHAN_PSD) { + /* Connecting AP is psd power */ + if (reg_tpc_info->is_psd_power) { + is_psd_power = true; + ath11k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + psd_power = temp_chan->psd; + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = + min_t(s8, + psd_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + /* Connecting AP is not psd power */ + } else { + ath11k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + psd_power = temp_chan->psd; + /* convert psd power to EIRP power based + * on channel width + */ + tx_power = + min_t(s8, tx_power, + psd_power + 13 + pwr_lvl_idx * 3); + max_tx_power[pwr_lvl_idx] = + min_t(s8, + tx_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + } + /* local power is not PSD power */ + } else { + /* Connecting AP is psd power */ + if (reg_tpc_info->is_psd_power) { + is_psd_power = true; + ath11k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = + reg_tpc_info->tpe[pwr_lvl_idx]; + /* Connecting AP is not psd power */ + } else { + ath11k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + max_tx_power[pwr_lvl_idx] = + min_t(s8, + tx_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + } + } + /* STA not received TPE IE */ + } else { + /* local power is PSD power*/ + if (chan->flags & IEEE80211_CHAN_PSD) { + is_psd_power = true; + ath11k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + psd_power = temp_chan->psd; + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = psd_power; + } else { + ath11k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + max_tx_power[pwr_lvl_idx] = tx_power; + } + } + + if (is_psd_power) { + /* If AP local power constraint is present */ + if (pwr_reduction) + eirp_power = eirp_power - pwr_reduction; + + /* If firmware updated max tx power is non zero, then take + * the min of firmware updated ap tx power + * and max power derived from above mentioned parameters. + */ + ath11k_dbg(ab, ATH11K_DBG_MAC, + "eirp power : %d firmware report power : %d\n", + eirp_power, ar->max_allowed_tx_power); + /* Firmware reports lower max_allowed_tx_power during vdev + * start response. In case of 6 GHz, firmware is not aware + * of EIRP power unless driver sets EIRP power through WMI + * TPC command. So radio which does not support idle power + * save can set maximum calculated EIRP power directly to + * firmware through TPC command without min comparison with + * vdev start response's max_allowed_tx_power. + */ + if (ar->max_allowed_tx_power && ab->hw_params.idle_ps) + eirp_power = min_t(s8, + eirp_power, + ar->max_allowed_tx_power); + } else { + /* If AP local power constraint is present */ + if (pwr_reduction) + max_tx_power[pwr_lvl_idx] = + max_tx_power[pwr_lvl_idx] - pwr_reduction; + /* If firmware updated max tx power is non zero, then take + * the min of firmware updated ap tx power + * and max power derived from above mentioned parameters. + */ + if (ar->max_allowed_tx_power && ab->hw_params.idle_ps) + max_tx_power[pwr_lvl_idx] = + min_t(s8, + max_tx_power[pwr_lvl_idx], + ar->max_allowed_tx_power); + } + reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq; + reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power = + max_tx_power[pwr_lvl_idx]; + } + + reg_tpc_info->num_pwr_levels = num_pwr_levels; + reg_tpc_info->is_psd_power = is_psd_power; + reg_tpc_info->eirp_power = eirp_power; + reg_tpc_info->ap_power_type = + ath11k_reg_ap_pwr_convert(vif->bss_conf.power_type); +} + static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx) diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 0dfdeed5177b..f5800fbecff8 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_MAC_H @@ -176,4 +176,7 @@ int ath11k_mac_wait_tx_complete(struct ath11k *ar); int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif, enum wmi_sta_keepalive_method method, u32 interval); +void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx); #endif From f8a573bd5f3b1c272c431831add259deabfd9948 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:58 +0200 Subject: [PATCH 029/378] wifi: ath11k: add WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT service bit Firmware advertises support for SERVICE_EXT_TPC_REG via a WMI service bit. Add the definition of this service bit so that a subsequent patch can check whether or not firmware supports this service. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Acked-by: Jeff Johnson Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-11-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/wmi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 7f6287f22cf5..9ed802194a5b 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2114,6 +2114,7 @@ enum wmi_tlv_service { /* The second 128 bits */ WMI_MAX_EXT_SERVICE = 256, WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL = 265, + WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT = 280, WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281, WMI_TLV_SERVICE_BIOS_SAR_SUPPORT = 326, WMI_TLV_SERVICE_SUPPORT_11D_FOR_HOST_SCAN = 357, From ed0a61dcb2d3936cf15dfc04341c4d0fc297919f Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:59 +0200 Subject: [PATCH 030/378] wifi: ath11k: add handler for WMI_VDEV_SET_TPC_POWER_CMDID Add the handler for WMI_VDEV_SET_TPC_POWER_CMDID, it is for 6 GHz band. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-12-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/wmi.c | 64 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/wmi.h | 57 ++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 3a3a2b697119..688ee20528a0 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -2379,6 +2379,70 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, return ret; } +int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar, + u32 vdev_id, + struct ath11k_reg_tpc_power_info *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_set_tpc_power_cmd *cmd; + struct wmi_vdev_ch_power_info *ch; + struct sk_buff *skb; + struct wmi_tlv *tlv; + u8 *ptr; + int i, ret, len, array_len; + + array_len = sizeof(*ch) * param->num_pwr_levels; + len = sizeof(*cmd) + TLV_HDR_SIZE + array_len; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + ptr = skb->data; + + cmd = (struct wmi_vdev_set_tpc_power_cmd *)ptr; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_SET_TPC_POWER_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->psd_power = param->is_psd_power; + cmd->eirp_power = param->eirp_power; + cmd->power_type_6ghz = param->ap_power_type; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "tpc vdev id %d is psd power %d eirp power %d 6 ghz power type %d\n", + vdev_id, param->is_psd_power, param->eirp_power, param->ap_power_type); + + ptr += sizeof(*cmd); + tlv = (struct wmi_tlv *)ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, array_len); + + ptr += TLV_HDR_SIZE; + ch = (struct wmi_vdev_ch_power_info *)ptr; + + for (i = 0; i < param->num_pwr_levels; i++, ch++) { + ch->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_CH_POWER_INFO) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*ch) - TLV_HDR_SIZE); + + ch->chan_cfreq = param->chan_power_info[i].chan_cfreq; + ch->tx_power = param->chan_power_info[i].tx_power; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tpc chan freq %d TX power %d\n", + ch->chan_cfreq, ch->tx_power); + } + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_SET_TPC_POWER_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n"); + dev_kfree_skb(skb); + return ret; + } + + return 0; +} + int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, struct scan_cancel_param *param) { diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 9ed802194a5b..6dcd14700570 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -15,6 +15,7 @@ struct ath11k; struct ath11k_fw_stats; struct ath11k_fw_dbglog; struct ath11k_vif; +struct ath11k_reg_tpc_power_info; #define PSOC_HOST_MAX_NUM_SS (8) @@ -327,6 +328,22 @@ enum wmi_tlv_cmd_id { WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID, WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID, WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID, + WMI_VDEV_SET_ARP_STAT_CMDID, + WMI_VDEV_GET_ARP_STAT_CMDID, + WMI_VDEV_GET_TX_POWER_CMDID, + WMI_VDEV_LIMIT_OFFCHAN_CMDID, + WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID, + WMI_VDEV_CHAINMASK_CONFIG_CMDID, + WMI_VDEV_GET_BCN_RECEPTION_STATS_CMDID, + WMI_VDEV_GET_MWS_COEX_INFO_CMDID, + WMI_VDEV_DELETE_ALL_PEER_CMDID, + WMI_VDEV_BSS_MAX_IDLE_TIME_CMDID, + WMI_VDEV_AUDIO_SYNC_TRIGGER_CMDID, + WMI_VDEV_AUDIO_SYNC_QTIMER_CMDID, + WMI_VDEV_SET_PCL_CMDID, + WMI_VDEV_GET_BIG_DATA_CMDID, + WMI_VDEV_GET_BIG_DATA_P2_CMDID, + WMI_VDEV_SET_TPC_POWER_CMDID, WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER), WMI_PEER_DELETE_CMDID, WMI_PEER_FLUSH_TIDS_CMDID, @@ -1880,6 +1897,8 @@ enum wmi_tlv_tag { WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, + WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5, + WMI_TAG_VDEV_CH_POWER_INFO, WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD, WMI_TAG_MAX @@ -3169,6 +3188,41 @@ struct wlan_ssid { u8 ssid[WLAN_SSID_MAX_LEN]; }; +struct wmi_vdev_ch_power_info { + u32 tlv_header; + + /* Channel center frequency (MHz) */ + u32 chan_cfreq; + + /* Unit: dBm, either PSD/EIRP power for this frequency or + * incremental for non-PSD BW + */ + u32 tx_power; +} __packed; + +struct wmi_vdev_set_tpc_power_cmd { + u32 tlv_header; + u32 vdev_id; + + /* Value: 0 or 1, is PSD power or not */ + u32 psd_power; + + /* Maximum EIRP power (dBm units), valid only if power is PSD */ + u32 eirp_power; + + /* Type: WMI_6GHZ_REG_TYPE, used for halphy CTL lookup */ + u32 power_type_6ghz; + + /* This fixed_param TLV is followed by the below TLVs: + * num_pwr_levels of wmi_vdev_ch_power_info + * For PSD power, it is the PSD/EIRP power of the frequency (20 MHz chunks). + * For non-PSD power, the power values are for 20, 40, and till + * BSS BW power levels. + * The num_pwr_levels will be checked by sw how many elements present + * in the variable-length array. + */ +} __packed; + #define WMI_IE_BITMAP_SIZE 8 /* prefix used by scan requestor ids on the host */ @@ -6483,5 +6537,8 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar); int ath11k_wmi_sta_keepalive(struct ath11k *ar, const struct wmi_sta_keepalive_arg *arg); bool ath11k_wmi_supports_6ghz_cc_ext(struct ath11k *ar); +int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar, + u32 vdev_id, + struct ath11k_reg_tpc_power_info *param); #endif From 74ef2d05ede63fd6416aa635aa8972dff901325f Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 11 Jan 2024 15:56:59 +0200 Subject: [PATCH 031/378] wifi: ath11k: use WMI_VDEV_SET_TPC_POWER_CMDID when EXT_TPC_REG_SUPPORT for 6 GHz When station is connected to a 6 GHz AP, it has 2 ways to configure the power limit to firmware. The first way is to send 2 WMI commands WMI_PDEV_PARAM_TXPOWER_LIMIT2G/WMI_PDEV_PARAM_TXPOWER_LIMIT5G to firmware, the second way is to send WMI_VDEV_SET_TPC_POWER_CMDID to firmware which include more parameters for power control. When firmware supports SERVICE_EXT_TPC_REG, it means firmware supports WMI_VDEV_SET_TPC_POWER_CMDID, then ath11k selects the second way. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Wen Gong Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://msgid.link/20231218085844.2658-13-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index f812a6f44b82..bf4a97967177 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -3397,6 +3397,18 @@ static int ath11k_mac_config_obss_pd(struct ath11k *ar, return 0; } +static bool ath11k_mac_supports_station_tpc(struct ath11k *ar, + struct ath11k_vif *arvif, + const struct cfg80211_chan_def *chandef) +{ + return ath11k_wmi_supports_6ghz_cc_ext(ar) && + test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map) && + arvif->vdev_type == WMI_VDEV_TYPE_STA && + arvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE && + chandef->chan && + chandef->chan->band == NL80211_BAND_6GHZ; +} + static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -3596,7 +3608,6 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_TXPOWER) { ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev_id %i txpower %d\n", arvif->vdev_id, info->txpower); - arvif->txpower = info->txpower; ath11k_mac_txpower_recalc(ar); } @@ -7285,6 +7296,15 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, return ret; } + /* TODO: For now we only set TPC power here. However when + * channel changes, say CSA, it should be updated again. + */ + if (ath11k_mac_supports_station_tpc(ar, arvif, chandef)) { + ath11k_mac_fill_reg_tpc_info(ar, arvif->vif, &arvif->chanctx); + ath11k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id, + &arvif->reg_tpc_info); + } + if (!restart) ar->num_started_vdevs++; @@ -8112,7 +8132,7 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, } ath11k_reg_handle_chan_list(ab, reg_info, power_type); - + arvif->chanctx = *ctx; ath11k_mac_parse_tx_pwr_env(ar, vif, ctx); } From 59cf57ab3deea979ffc0a6f7c22f4331e59d32f0 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 11 Jan 2024 10:05:30 -0800 Subject: [PATCH 032/378] wifi: ath12k: Remove unnecessary struct qmi_txn initializers Currently most of the ath12k QMI messaging functions define their struct qmi_txn variables with a {} initializer. However, all of these functions subsequently call qmi_txn_init(), and the very first thing that function does is zero the struct. Hence, the initializers are unnecessary. Since these consume code space and cpu cycles, remove them. No functional changes, compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240111-qmi-cleanup-v2-1-53343af953d5@quicinc.com --- drivers/net/wireless/ath/ath12k/qmi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 536856234f3b..180e86c2a10c 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -1921,7 +1921,7 @@ static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) { struct qmi_wlanfw_host_cap_req_msg_v01 req; struct qmi_wlanfw_host_cap_resp_msg_v01 resp; - struct qmi_txn txn = {}; + struct qmi_txn txn; int ret = 0; memset(&req, 0, sizeof(req)); @@ -2069,7 +2069,7 @@ static int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) { struct qmi_wlanfw_respond_mem_req_msg_v01 *req; struct qmi_wlanfw_respond_mem_resp_msg_v01 resp; - struct qmi_txn txn = {}; + struct qmi_txn txn; int ret = 0, i; bool delayed; @@ -2210,7 +2210,7 @@ static int ath12k_qmi_request_target_cap(struct ath12k_base *ab) { struct qmi_wlanfw_cap_req_msg_v01 req; struct qmi_wlanfw_cap_resp_msg_v01 resp; - struct qmi_txn txn = {}; + struct qmi_txn txn; unsigned int board_id = ATH12K_BOARD_ID_DEFAULT; int ret = 0; int r; @@ -2311,7 +2311,7 @@ static int ath12k_qmi_load_file_target_mem(struct ath12k_base *ab, { struct qmi_wlanfw_bdf_download_req_msg_v01 *req; struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; - struct qmi_txn txn = {}; + struct qmi_txn txn; const u8 *temp = data; int ret; u32 remaining = len; @@ -2547,7 +2547,7 @@ static int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; struct qmi_wlanfw_m3_info_req_msg_v01 req; struct qmi_wlanfw_m3_info_resp_msg_v01 resp; - struct qmi_txn txn = {}; + struct qmi_txn txn; int ret = 0; memset(&req, 0, sizeof(req)); @@ -2598,7 +2598,7 @@ static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab, { struct qmi_wlanfw_wlan_mode_req_msg_v01 req; struct qmi_wlanfw_wlan_mode_resp_msg_v01 resp; - struct qmi_txn txn = {}; + struct qmi_txn txn; int ret = 0; memset(&req, 0, sizeof(req)); @@ -2651,7 +2651,7 @@ static int ath12k_qmi_wlanfw_wlan_cfg_send(struct ath12k_base *ab) struct qmi_wlanfw_wlan_cfg_resp_msg_v01 resp; struct ce_pipe_config *ce_cfg; struct service_to_pipe *svc_cfg; - struct qmi_txn txn = {}; + struct qmi_txn txn; int ret = 0, pipe_num; ce_cfg = (struct ce_pipe_config *)ab->qmi.ce_cfg.tgt_ce; From 2e82b5f09a97f1b98b885470c81c1248bec103af Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 11 Jan 2024 10:05:31 -0800 Subject: [PATCH 033/378] wifi: ath12k: Add missing qmi_txn_cancel() calls Per the QMI documentation "A client calling qmi_txn_init() must call either qmi_txn_wait() or qmi_txn_cancel() to free up the allocated resources." Unfortunately, in most of the ath12k messaging functions, when qmi_send_request() fails, the function returns without performing the necessary cleanup. So update those functions to call qmi_txn_cancel() when qmi_send_request() fails. No functional changes, compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240111-qmi-cleanup-v2-2-53343af953d5@quicinc.com --- drivers/net/wireless/ath/ath12k/qmi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 180e86c2a10c..1f2df2e3fbce 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1977,6 +1977,7 @@ static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN, qmi_wlanfw_host_cap_req_msg_v01_ei, &req); if (ret < 0) { + qmi_txn_cancel(&txn); ath12k_warn(ab, "Failed to send host capability request,err = %d\n", ret); goto out; } @@ -2040,6 +2041,7 @@ static int ath12k_qmi_fw_ind_register_send(struct ath12k_base *ab) QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN, qmi_wlanfw_ind_register_req_msg_v01_ei, req); if (ret < 0) { + qmi_txn_cancel(&txn); ath12k_warn(ab, "Failed to send indication register request, err = %d\n", ret); goto out; @@ -2114,6 +2116,7 @@ static int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN, qmi_wlanfw_respond_mem_req_msg_v01_ei, req); if (ret < 0) { + qmi_txn_cancel(&txn); ath12k_warn(ab, "qmi failed to respond memory request, err = %d\n", ret); goto out; @@ -2229,6 +2232,7 @@ static int ath12k_qmi_request_target_cap(struct ath12k_base *ab) QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN, qmi_wlanfw_cap_req_msg_v01_ei, &req); if (ret < 0) { + qmi_txn_cancel(&txn); ath12k_warn(ab, "qmi failed to send target cap request, err = %d\n", ret); goto out; @@ -2572,6 +2576,7 @@ static int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN, qmi_wlanfw_m3_info_req_msg_v01_ei, &req); if (ret < 0) { + qmi_txn_cancel(&txn); ath12k_warn(ab, "qmi failed to send M3 information request, err = %d\n", ret); goto out; @@ -2618,6 +2623,7 @@ static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab, QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN, qmi_wlanfw_wlan_mode_req_msg_v01_ei, &req); if (ret < 0) { + qmi_txn_cancel(&txn); ath12k_warn(ab, "qmi failed to send mode request, mode: %d, err = %d\n", mode, ret); goto out; @@ -2709,6 +2715,7 @@ static int ath12k_qmi_wlanfw_wlan_cfg_send(struct ath12k_base *ab) QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN, qmi_wlanfw_wlan_cfg_req_msg_v01_ei, req); if (ret < 0) { + qmi_txn_cancel(&txn); ath12k_warn(ab, "qmi failed to send wlan config request, err = %d\n", ret); goto out; From 6d2b0a066941c5d9c56c79d95c91dcec2fd7a7fa Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 11 Jan 2024 10:05:32 -0800 Subject: [PATCH 034/378] wifi: ath12k: Use initializers for QMI message buffers Currently most of the QMI messaging functions use memset() to zero out the QMI message buffers. Prefer to use a {} initializer to allow the compiler to generate optimized code and avoid the function call overhead. No functional changes, compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240111-qmi-cleanup-v2-3-53343af953d5@quicinc.com --- drivers/net/wireless/ath/ath12k/qmi.c | 40 ++++++++------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 1f2df2e3fbce..c4c7f31a91cd 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1919,14 +1919,11 @@ static void ath12k_host_cap_parse_mlo(struct qmi_wlanfw_host_cap_req_msg_v01 *re static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) { - struct qmi_wlanfw_host_cap_req_msg_v01 req; - struct qmi_wlanfw_host_cap_resp_msg_v01 resp; + struct qmi_wlanfw_host_cap_req_msg_v01 req = {}; + struct qmi_wlanfw_host_cap_resp_msg_v01 resp = {}; struct qmi_txn txn; int ret = 0; - memset(&req, 0, sizeof(req)); - memset(&resp, 0, sizeof(resp)); - req.num_clients_valid = 1; req.num_clients = 1; req.mem_cfg_mode = ab->qmi.target_mem_mode; @@ -2070,7 +2067,7 @@ static int ath12k_qmi_fw_ind_register_send(struct ath12k_base *ab) static int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) { struct qmi_wlanfw_respond_mem_req_msg_v01 *req; - struct qmi_wlanfw_respond_mem_resp_msg_v01 resp; + struct qmi_wlanfw_respond_mem_resp_msg_v01 resp = {}; struct qmi_txn txn; int ret = 0, i; bool delayed; @@ -2079,8 +2076,6 @@ static int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) if (!req) return -ENOMEM; - memset(&resp, 0, sizeof(resp)); - /* Some targets by default request a block of big contiguous * DMA memory, it's hard to allocate from kernel. So host returns * failure to firmware and firmware then request multiple blocks of @@ -2090,7 +2085,6 @@ static int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) delayed = true; ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi delays mem_request %d\n", ab->qmi.mem_seg_count); - memset(req, 0, sizeof(*req)); } else { delayed = false; req->mem_seg_len = ab->qmi.mem_seg_count; @@ -2211,17 +2205,14 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) static int ath12k_qmi_request_target_cap(struct ath12k_base *ab) { - struct qmi_wlanfw_cap_req_msg_v01 req; - struct qmi_wlanfw_cap_resp_msg_v01 resp; + struct qmi_wlanfw_cap_req_msg_v01 req = {}; + struct qmi_wlanfw_cap_resp_msg_v01 resp = {}; struct qmi_txn txn; unsigned int board_id = ATH12K_BOARD_ID_DEFAULT; int ret = 0; int r; int i; - memset(&req, 0, sizeof(req)); - memset(&resp, 0, sizeof(resp)); - ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_cap_resp_msg_v01_ei, &resp); if (ret < 0) @@ -2314,7 +2305,7 @@ static int ath12k_qmi_load_file_target_mem(struct ath12k_base *ab, const u8 *data, u32 len, u8 type) { struct qmi_wlanfw_bdf_download_req_msg_v01 *req; - struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; + struct qmi_wlanfw_bdf_download_resp_msg_v01 resp = {}; struct qmi_txn txn; const u8 *temp = data; int ret; @@ -2323,7 +2314,6 @@ static int ath12k_qmi_load_file_target_mem(struct ath12k_base *ab, req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; - memset(&resp, 0, sizeof(resp)); while (remaining) { req->valid = 1; @@ -2549,14 +2539,11 @@ static void ath12k_qmi_m3_free(struct ath12k_base *ab) static int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) { struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; - struct qmi_wlanfw_m3_info_req_msg_v01 req; - struct qmi_wlanfw_m3_info_resp_msg_v01 resp; + struct qmi_wlanfw_m3_info_req_msg_v01 req = {}; + struct qmi_wlanfw_m3_info_resp_msg_v01 resp = {}; struct qmi_txn txn; int ret = 0; - memset(&req, 0, sizeof(req)); - memset(&resp, 0, sizeof(resp)); - ret = ath12k_qmi_m3_load(ab); if (ret) { ath12k_err(ab, "failed to load m3 firmware: %d", ret); @@ -2601,14 +2588,11 @@ static int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab, u32 mode) { - struct qmi_wlanfw_wlan_mode_req_msg_v01 req; - struct qmi_wlanfw_wlan_mode_resp_msg_v01 resp; + struct qmi_wlanfw_wlan_mode_req_msg_v01 req = {}; + struct qmi_wlanfw_wlan_mode_resp_msg_v01 resp = {}; struct qmi_txn txn; int ret = 0; - memset(&req, 0, sizeof(req)); - memset(&resp, 0, sizeof(resp)); - req.mode = mode; req.hw_debug_valid = 1; req.hw_debug = 0; @@ -2654,7 +2638,7 @@ static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab, static int ath12k_qmi_wlanfw_wlan_cfg_send(struct ath12k_base *ab) { struct qmi_wlanfw_wlan_cfg_req_msg_v01 *req; - struct qmi_wlanfw_wlan_cfg_resp_msg_v01 resp; + struct qmi_wlanfw_wlan_cfg_resp_msg_v01 resp = {}; struct ce_pipe_config *ce_cfg; struct service_to_pipe *svc_cfg; struct qmi_txn txn; @@ -2667,8 +2651,6 @@ static int ath12k_qmi_wlanfw_wlan_cfg_send(struct ath12k_base *ab) if (!req) return -ENOMEM; - memset(&resp, 0, sizeof(resp)); - req->host_version_valid = 1; strscpy(req->host_version, ATH12K_HOST_VERSION_STRING, sizeof(req->host_version)); From eaf9f17b861b665a0bc8dff120ae3c9028642e6d Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:38 +0200 Subject: [PATCH 035/378] wifi: ath12k: relocate ath12k_dp_pdev_pre_alloc() call Currently, the data path pdev pre alloc and mac allocate are called separately from the core start procedure. The data path pdev pre alloc can be called from the mac allocate procedure itself since initialization related to pdev happens in the mac allocate procedure. So move the caller of DP pdev pre alloc from the core start procedure to the mac allocate procedure. This change helps in the future to easily decouple the mac allocate procedure from core start handling in order to support MLO in multi chip. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo Link: https://msgid.link/20231206034920.1037449-2-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 2 -- drivers/net/wireless/ath/ath12k/mac.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index d73e2d33a41e..ababe7f01b5b 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -714,8 +714,6 @@ static int ath12k_core_start(struct ath12k_base *ab, ath12k_dp_cc_config(ab); - ath12k_dp_pdev_pre_alloc(ab); - ret = ath12k_dp_rx_pdev_reo_setup(ab); if (ret) { ath12k_err(ab, "failed to initialize reo destination rings: %d\n", ret); diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 88cec54c6c2e..49d56f5d8896 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7667,6 +7667,8 @@ int ath12k_mac_allocate(struct ath12k_base *ab) clear_bit(ATH12K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); } + ath12k_dp_pdev_pre_alloc(ab); + return 0; err_free_mac: From 8a742a79f90e3d3f0378b030110eccac8d28b655 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:38 +0200 Subject: [PATCH 036/378] wifi: ath12k: refactor ath12k_mac_allocate() and ath12k_mac_destroy() Currently, the MAC allocation and destroy helper functions are tightly coupled with the link/radio (ar) structure. In the future, to support single/Multi link operations, need to refactor these helper functions across the core and mac sub modules, so that it can be easy to scale these functions to support single/Multi link operations. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo Link: https://msgid.link/20231206034920.1037449-3-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 150 +++++++++++++++----------- 1 file changed, 88 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 49d56f5d8896..09a1a2edf842 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7607,77 +7607,47 @@ int ath12k_mac_register(struct ath12k_base *ab) return ret; } -int ath12k_mac_allocate(struct ath12k_base *ab) +static void ath12k_mac_setup(struct ath12k *ar) { - struct ieee80211_hw *hw; - struct ath12k *ar; - struct ath12k_pdev *pdev; - int ret; - int i; + struct ath12k_base *ab = ar->ab; + struct ath12k_pdev *pdev = ar->pdev; + u8 pdev_idx = ar->pdev_idx; - if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) - return 0; + ar->lmac_id = ath12k_hw_get_mac_from_pdev_id(ab->hw_params, pdev_idx); - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - hw = ieee80211_alloc_hw(sizeof(struct ath12k), &ath12k_ops); - if (!hw) { - ath12k_warn(ab, "failed to allocate mac80211 hw device\n"); - ret = -ENOMEM; - goto err_free_mac; - } + ar->wmi = &ab->wmi_ab.wmi[pdev_idx]; + /* FIXME: wmi[0] is already initialized during attach, + * Should we do this again? + */ + ath12k_wmi_pdev_attach(ab, pdev_idx); - ar = hw->priv; - ar->hw = hw; - ar->ab = ab; - ar->pdev = pdev; - ar->pdev_idx = i; - ar->lmac_id = ath12k_hw_get_mac_from_pdev_id(ab->hw_params, i); + ar->cfg_tx_chainmask = pdev->cap.tx_chain_mask; + ar->cfg_rx_chainmask = pdev->cap.rx_chain_mask; + ar->num_tx_chains = hweight32(pdev->cap.tx_chain_mask); + ar->num_rx_chains = hweight32(pdev->cap.rx_chain_mask); - ar->wmi = &ab->wmi_ab.wmi[i]; - /* FIXME: wmi[0] is already initialized during attach, - * Should we do this again? - */ - ath12k_wmi_pdev_attach(ab, i); + spin_lock_init(&ar->data_lock); + INIT_LIST_HEAD(&ar->arvifs); + INIT_LIST_HEAD(&ar->ppdu_stats_info); + mutex_init(&ar->conf_mutex); + init_completion(&ar->vdev_setup_done); + init_completion(&ar->vdev_delete_done); + init_completion(&ar->peer_assoc_done); + init_completion(&ar->peer_delete_done); + init_completion(&ar->install_key_done); + init_completion(&ar->bss_survey_done); + init_completion(&ar->scan.started); + init_completion(&ar->scan.completed); - ar->cfg_tx_chainmask = pdev->cap.tx_chain_mask; - ar->cfg_rx_chainmask = pdev->cap.rx_chain_mask; - ar->num_tx_chains = hweight32(pdev->cap.tx_chain_mask); - ar->num_rx_chains = hweight32(pdev->cap.rx_chain_mask); + INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work); + INIT_WORK(&ar->regd_update_work, ath12k_regd_update_work); - pdev->ar = ar; - spin_lock_init(&ar->data_lock); - INIT_LIST_HEAD(&ar->arvifs); - INIT_LIST_HEAD(&ar->ppdu_stats_info); - mutex_init(&ar->conf_mutex); - init_completion(&ar->vdev_setup_done); - init_completion(&ar->vdev_delete_done); - init_completion(&ar->peer_assoc_done); - init_completion(&ar->peer_delete_done); - init_completion(&ar->install_key_done); - init_completion(&ar->bss_survey_done); - init_completion(&ar->scan.started); - init_completion(&ar->scan.completed); - - INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work); - INIT_WORK(&ar->regd_update_work, ath12k_regd_update_work); - - INIT_WORK(&ar->wmi_mgmt_tx_work, ath12k_mgmt_over_wmi_tx_work); - skb_queue_head_init(&ar->wmi_mgmt_tx_queue); - clear_bit(ATH12K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); - } - - ath12k_dp_pdev_pre_alloc(ab); - - return 0; - -err_free_mac: - ath12k_mac_destroy(ab); - - return ret; + INIT_WORK(&ar->wmi_mgmt_tx_work, ath12k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + clear_bit(ATH12K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); } -void ath12k_mac_destroy(struct ath12k_base *ab) +static void ath12k_mac_hw_destroy(struct ath12k_base *ab) { struct ath12k *ar; struct ath12k_pdev *pdev; @@ -7693,3 +7663,59 @@ void ath12k_mac_destroy(struct ath12k_base *ab) pdev->ar = NULL; } } + +static int ath12k_mac_hw_allocate(struct ath12k_base *ab) +{ + struct ieee80211_hw *hw; + struct ath12k *ar; + struct ath12k_pdev *pdev; + int ret; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + hw = ieee80211_alloc_hw(sizeof(struct ath12k), &ath12k_ops); + if (!hw) { + ath12k_warn(ab, "failed to allocate mac80211 hw device\n"); + ret = -ENOMEM; + goto err_free_mac; + } + + ar = hw->priv; + ar->hw = hw; + ar->ab = ab; + ar->pdev = pdev; + ar->pdev_idx = i; + pdev->ar = ar; + + ath12k_mac_setup(ar); + } + + return 0; + +err_free_mac: + ath12k_mac_hw_destroy(ab); + + return ret; +} + +void ath12k_mac_destroy(struct ath12k_base *ab) +{ + ath12k_mac_hw_destroy(ab); +} + +int ath12k_mac_allocate(struct ath12k_base *ab) +{ + int ret; + + if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) + return 0; + + ret = ath12k_mac_hw_allocate(ab); + if (ret) + return ret; + + ath12k_dp_pdev_pre_alloc(ab); + + return 0; +} From d2b7a6e5fa1c92deabe77aa57ef16f599f3b4247 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:39 +0200 Subject: [PATCH 037/378] wifi: ath12k: refactor ath12k_mac_setup_channels_rates() Currently, the MAC setup helper function is accessing the mac80211 hw data. In the future, to support single/multi link operation, need to decouple the mac80211 hw data from this helper function so that it can be easy to scale these functions to support single/multi link operations. So remove the mac80211 hw access from the ath12k_mac_setup_channels_rates() helper function. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo Link: https://msgid.link/20231206034920.1037449-4-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 09a1a2edf842..33e03208479d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7158,9 +7158,9 @@ static u32 ath12k_get_phy_id(struct ath12k *ar, u32 band) } static int ath12k_mac_setup_channels_rates(struct ath12k *ar, - u32 supported_bands) + u32 supported_bands, + struct ieee80211_supported_band *bands[]) { - struct ieee80211_hw *hw = ar->hw; struct ieee80211_supported_band *band; struct ath12k_wmi_hal_reg_capabilities_ext_arg *reg_cap; void *channels; @@ -7186,7 +7186,7 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->channels = channels; band->n_bitrates = ath12k_g_rates_size; band->bitrates = ath12k_g_rates; - hw->wiphy->bands[NL80211_BAND_2GHZ] = band; + bands[NL80211_BAND_2GHZ] = band; if (ar->ab->hw_params->single_pdev_only) { phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_2G_CAP); @@ -7213,7 +7213,7 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->channels = channels; band->n_bitrates = ath12k_a_rates_size; band->bitrates = ath12k_a_rates; - hw->wiphy->bands[NL80211_BAND_6GHZ] = band; + bands[NL80211_BAND_6GHZ] = band; ath12k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); @@ -7235,7 +7235,7 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->channels = channels; band->n_bitrates = ath12k_a_rates_size; band->bitrates = ath12k_a_rates; - hw->wiphy->bands[NL80211_BAND_5GHZ] = band; + bands[NL80211_BAND_5GHZ] = band; if (ar->ab->hw_params->single_pdev_only) { phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5G_CAP); @@ -7414,7 +7414,8 @@ static int __ath12k_mac_register(struct ath12k *ar) SET_IEEE80211_DEV(hw, ab->dev); ret = ath12k_mac_setup_channels_rates(ar, - cap->supported_bands); + cap->supported_bands, + hw->wiphy->bands); if (ret) goto err; From d786c9f5fe3472ac102160c113a7bba8ec79a6c6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:39 +0200 Subject: [PATCH 038/378] wifi: ath12k: refactor ath12k_mac_register() and ath12k_mac_unregister() Currently, the mac80211 hw registration procedure is tightly coupled with the handling of link/radio (ar). Define a new helper function to separate the link/radio handling from the mac80211 hw registration procedure for improved code readability. Also, it can be easy to scale these functionality to support single/multi link operation in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo Link: https://msgid.link/20231206034920.1037449-5-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 183 ++++++++++++++------------ 1 file changed, 102 insertions(+), 81 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 33e03208479d..4fc338bace8d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7349,7 +7349,17 @@ static const struct wiphy_iftype_ext_capab ath12k_iftypes_ext_capa[] = { }, }; -static void __ath12k_mac_unregister(struct ath12k *ar) +static void ath12k_mac_cleanup_unregister(struct ath12k *ar) +{ + idr_for_each(&ar->txmgmt_idr, ath12k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + + kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); + kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels); +} + +static void ath12k_mac_hw_unregister(struct ath12k *ar) { struct ieee80211_hw *hw = ar->hw; struct wiphy *wiphy = hw->wiphy; @@ -7358,12 +7368,7 @@ static void __ath12k_mac_unregister(struct ath12k *ar) ieee80211_unregister_hw(hw); - idr_for_each(&ar->txmgmt_idr, ath12k_mac_tx_mgmt_pending_free, ar); - idr_destroy(&ar->txmgmt_idr); - - kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); - kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); - kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels); + ath12k_mac_cleanup_unregister(ar); kfree(wiphy->iface_combinations[0].limits); kfree(wiphy->iface_combinations); @@ -7371,28 +7376,41 @@ static void __ath12k_mac_unregister(struct ath12k *ar) SET_IEEE80211_DEV(hw, NULL); } -void ath12k_mac_unregister(struct ath12k_base *ab) +static int ath12k_mac_setup_register(struct ath12k *ar, + u32 *ht_cap, + struct ieee80211_supported_band *bands[]) { - struct ath12k *ar; - struct ath12k_pdev *pdev; - int i; + struct ath12k_pdev_cap *cap = &ar->pdev->cap; + int ret; - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; - if (!ar) - continue; + init_waitqueue_head(&ar->txmgmt_empty_waitq); + idr_init(&ar->txmgmt_idr); + spin_lock_init(&ar->txmgmt_idr_lock); - __ath12k_mac_unregister(ar); - } + ath12k_pdev_caps_update(ar); + + ret = ath12k_mac_setup_channels_rates(ar, + cap->supported_bands, + bands); + if (ret) + return ret; + + ath12k_mac_setup_ht_vht_cap(ar, cap, ht_cap); + ath12k_mac_setup_sband_iftype_data(ar, cap); + + ar->max_num_stations = TARGET_NUM_STATIONS; + ar->max_num_peers = TARGET_NUM_PEERS_PDEV; + + return 0; } -static int __ath12k_mac_register(struct ath12k *ar) +static int ath12k_mac_hw_register(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; struct ieee80211_hw *hw = ar->hw; struct wiphy *wiphy = hw->wiphy; - struct ath12k_pdev_cap *cap = &ar->pdev->cap; + struct ath12k_pdev *pdev = ar->pdev; + struct ath12k_pdev_cap *cap = &pdev->cap; static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, @@ -7407,25 +7425,24 @@ static int __ath12k_mac_register(struct ath12k *ar) int ret; u32 ht_cap = 0; - ath12k_pdev_caps_update(ar); + if (ab->pdevs_macaddr_valid) { + ether_addr_copy(ar->mac_addr, pdev->mac_addr); + } else { + ether_addr_copy(ar->mac_addr, ab->mac_addr); + ar->mac_addr[4] += ar->pdev_idx; + } + + ret = ath12k_mac_setup_register(ar, &ht_cap, hw->wiphy->bands); + if (ret) + goto out; SET_IEEE80211_PERM_ADDR(hw, ar->mac_addr); - SET_IEEE80211_DEV(hw, ab->dev); - ret = ath12k_mac_setup_channels_rates(ar, - cap->supported_bands, - hw->wiphy->bands); - if (ret) - goto err; - - ath12k_mac_setup_ht_vht_cap(ar, cap, &ht_cap); - ath12k_mac_setup_sband_iftype_data(ar, cap); - ret = ath12k_mac_setup_iface_combinations(ar); if (ret) { ath12k_err(ar->ab, "failed to setup interface combinations: %d\n", ret); - goto err_free_channels; + goto err_setup_unregister; } wiphy->available_antennas_rx = cap->rx_chain_mask; @@ -7484,9 +7501,6 @@ static int __ath12k_mac_register(struct ath12k *ar) wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | NL80211_FEATURE_AP_SCAN; - ar->max_num_stations = TARGET_NUM_STATIONS; - ar->max_num_peers = TARGET_NUM_PEERS_PDEV; - wiphy->max_ap_assoc_sta = ar->max_num_stations; hw->queues = ATH12K_HW_MAX_QUEUES; @@ -7553,58 +7567,14 @@ static int __ath12k_mac_register(struct ath12k *ar) kfree(wiphy->iface_combinations[0].limits); kfree(wiphy->iface_combinations); -err_free_channels: +err_setup_unregister: kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels); -err: SET_IEEE80211_DEV(hw, NULL); - return ret; -} - -int ath12k_mac_register(struct ath12k_base *ab) -{ - struct ath12k *ar; - struct ath12k_pdev *pdev; - int i; - int ret; - - if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) - return 0; - - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; - if (ab->pdevs_macaddr_valid) { - ether_addr_copy(ar->mac_addr, pdev->mac_addr); - } else { - ether_addr_copy(ar->mac_addr, ab->mac_addr); - ar->mac_addr[4] += i; - } - - ret = __ath12k_mac_register(ar); - if (ret) - goto err_cleanup; - - init_waitqueue_head(&ar->txmgmt_empty_waitq); - idr_init(&ar->txmgmt_idr); - spin_lock_init(&ar->txmgmt_idr_lock); - } - - /* Initialize channel counters frequency value in hertz */ - ab->cc_freq_hz = 320000; - ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; - - return 0; - -err_cleanup: - for (i = i - 1; i >= 0; i--) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; - __ath12k_mac_unregister(ar); - } +out: return ret; } @@ -7648,6 +7618,57 @@ static void ath12k_mac_setup(struct ath12k *ar) clear_bit(ATH12K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); } +int ath12k_mac_register(struct ath12k_base *ab) +{ + struct ath12k *ar; + struct ath12k_pdev *pdev; + int i; + int ret; + + if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) + return 0; + + /* Initialize channel counters frequency value in hertz */ + ab->cc_freq_hz = 320000; + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + + ret = ath12k_mac_hw_register(ar); + if (ret) + goto err_cleanup; + } + + return 0; + +err_cleanup: + for (i = i - 1; i >= 0; i--) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + ath12k_mac_hw_unregister(ar); + } + + return ret; +} + +void ath12k_mac_unregister(struct ath12k_base *ab) +{ + struct ath12k *ar; + struct ath12k_pdev *pdev; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar) + continue; + + ath12k_mac_hw_unregister(ar); + } +} + static void ath12k_mac_hw_destroy(struct ath12k_base *ab) { struct ath12k *ar; From 3e141f0034d573f133fc3e73508949ba64461a4a Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:40 +0200 Subject: [PATCH 039/378] wifi: ath12k: refactor ath12k_mac_op_config() To support single wiphy abstraction, introduce link/radio specific helper function in the mac80211 callback config(). This way, the callback can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-2-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 4fc338bace8d..06ecbb382b63 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -1083,9 +1083,9 @@ static int ath12k_mac_monitor_stop(struct ath12k *ar) return ret; } -static int ath12k_mac_op_config(struct ieee80211_hw *hw, u32 changed) +static int ath12k_mac_config(struct ath12k *ar, u32 changed) { - struct ath12k *ar = hw->priv; + struct ieee80211_hw *hw = ar->hw; struct ieee80211_conf *conf = &hw->conf; int ret = 0; @@ -1122,6 +1122,19 @@ static int ath12k_mac_op_config(struct ieee80211_hw *hw, u32 changed) return ret; } +static int ath12k_mac_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath12k *ar = hw->priv; + int ret; + + ret = ath12k_mac_config(ar, changed); + if (ret) + ath12k_warn(ar->ab, "failed to update config pdev idx %d: %d\n", + ar->pdev_idx, ret); + + return ret; +} + static int ath12k_mac_setup_bcn_tmpl(struct ath12k_vif *arvif) { struct ath12k *ar = arvif->ar; From ce20a10fdff41afbbc119e91cf3e2378d81e2eb0 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:40 +0200 Subject: [PATCH 040/378] wifi: ath12k: refactor ath12k_bss_assoc() To support single wiphy abstraction, introduce link/radio specific helper function in the mac80211 callback bss_info_change(). This way, the callback can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-3-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 43 ++++++++++++++++----------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 06ecbb382b63..58bd850bb86e 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -2279,12 +2279,11 @@ static int ath12k_setup_peer_smps(struct ath12k *ar, struct ath12k_vif *arvif, ath12k_smps_map[smps]); } -static void ath12k_bss_assoc(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, +static void ath12k_bss_assoc(struct ath12k *ar, + struct ath12k_vif *arvif, struct ieee80211_bss_conf *bss_conf) { - struct ath12k *ar = hw->priv; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_vif *vif = arvif->vif; struct ath12k_wmi_peer_assoc_arg peer_arg; struct ieee80211_sta *ap_sta; struct ath12k_peer *peer; @@ -2374,11 +2373,9 @@ static void ath12k_bss_assoc(struct ieee80211_hw *hw, arvif->vdev_id, ret); } -static void ath12k_bss_disassoc(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static void ath12k_bss_disassoc(struct ath12k *ar, + struct ath12k_vif *arvif) { - struct ath12k *ar = hw->priv; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); int ret; lockdep_assert_held(&ar->conf_mutex); @@ -2504,13 +2501,12 @@ static int ath12k_mac_fils_discovery(struct ath12k_vif *arvif, return ret; } -static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u64 changed) +static void ath12k_mac_bss_info_changed(struct ath12k *ar, + struct ath12k_vif *arvif, + struct ieee80211_bss_conf *info, + u64 changed) { - struct ath12k *ar = hw->priv; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_vif *vif = arvif->vif; struct cfg80211_chan_def def; u32 param_id, param_value; enum nl80211_band band; @@ -2523,7 +2519,7 @@ static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, u8 rateidx; u32 rate; - mutex_lock(&ar->conf_mutex); + lockdep_assert_held(&ar->conf_mutex); if (changed & BSS_CHANGED_BEACON_INT) { arvif->beacon_interval = info->beacon_int; @@ -2679,9 +2675,9 @@ static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ASSOC) { if (vif->cfg.assoc) - ath12k_bss_assoc(hw, vif, info); + ath12k_bss_assoc(ar, arvif, info); else - ath12k_bss_disassoc(hw, vif); + ath12k_bss_disassoc(ar, arvif); } if (changed & BSS_CHANGED_TXPOWER) { @@ -2783,6 +2779,19 @@ static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_EHT_PUNCTURING) arvif->punct_bitmap = info->eht_puncturing; +} + +static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct ath12k *ar = hw->priv; + struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + + mutex_lock(&ar->conf_mutex); + + ath12k_mac_bss_info_changed(ar, arvif, info, changed); mutex_unlock(&ar->conf_mutex); } From 00c9b1a6d21d03b8fb74f1029c370dccce294dd5 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:40 +0200 Subject: [PATCH 041/378] wifi: ath12k: refactor ath12k_mac_op_conf_tx() To support single wiphy abstraction, introduce link/radio specific helper function in the mac80211 callback conf_tx(). This way, the callback can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-4-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 41 ++++++++++++++++++--------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 58bd850bb86e..37ae22d27ccb 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3986,10 +3986,10 @@ static void ath12k_mac_op_sta_rc_update(struct ieee80211_hw *hw, ieee80211_queue_work(hw, &arsta->update_wk); } -static int ath12k_conf_tx_uapsd(struct ath12k *ar, struct ieee80211_vif *vif, +static int ath12k_conf_tx_uapsd(struct ath12k_vif *arvif, u16 ac, bool enable) { - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k *ar = arvif->ar; u32 value; int ret; @@ -4043,17 +4043,16 @@ static int ath12k_conf_tx_uapsd(struct ath12k *ar, struct ieee80211_vif *vif, return ret; } -static int ath12k_mac_op_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - unsigned int link_id, u16 ac, - const struct ieee80211_tx_queue_params *params) +static int ath12k_mac_conf_tx(struct ath12k_vif *arvif, + unsigned int link_id, u16 ac, + const struct ieee80211_tx_queue_params *params) { - struct ath12k *ar = hw->priv; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); struct wmi_wmm_params_arg *p = NULL; + struct ath12k *ar = arvif->ar; + struct ath12k_base *ab = ar->ab; int ret; - mutex_lock(&ar->conf_mutex); + lockdep_assert_held(&ar->conf_mutex); switch (ac) { case IEEE80211_AC_VO: @@ -4083,17 +4082,33 @@ static int ath12k_mac_op_conf_tx(struct ieee80211_hw *hw, ret = ath12k_wmi_send_wmm_update_cmd(ar, arvif->vdev_id, &arvif->wmm_params); if (ret) { - ath12k_warn(ar->ab, "failed to set wmm params: %d\n", ret); + ath12k_warn(ab, "pdev idx %d failed to set wmm params: %d\n", + ar->pdev_idx, ret); goto exit; } - ret = ath12k_conf_tx_uapsd(ar, vif, ac, params->uapsd); - + ret = ath12k_conf_tx_uapsd(arvif, ac, params->uapsd); if (ret) - ath12k_warn(ar->ab, "failed to set sta uapsd: %d\n", ret); + ath12k_warn(ab, "pdev idx %d failed to set sta uapsd: %d\n", + ar->pdev_idx, ret); exit: + return ret; +} + +static int ath12k_mac_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct ath12k *ar = hw->priv; + struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + int ret; + + mutex_lock(&ar->conf_mutex); + ret = ath12k_mac_conf_tx(arvif, link_id, ac, params); mutex_unlock(&ar->conf_mutex); + return ret; } From e1e275a699061fba965b5653a8f03d550bb5fe6e Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:40 +0200 Subject: [PATCH 042/378] wifi: ath12k: refactor ath12k_mac_op_start() To support single wiphy abstraction, introduce link/radio specific helper function in the mac80211 callback start(). This way, the callback can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-5-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 31 ++++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 37ae22d27ccb..d48161b43a4a 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -5081,14 +5081,12 @@ static void ath12k_mac_wait_reconfigure(struct ath12k_base *ab) ATH12K_RECONFIGURE_TIMEOUT_HZ); } -static int ath12k_mac_op_start(struct ieee80211_hw *hw) +static int ath12k_mac_start(struct ath12k *ar) { - struct ath12k *ar = hw->priv; struct ath12k_base *ab = ar->ab; struct ath12k_pdev *pdev = ar->pdev; int ret; - ath12k_mac_drain_tx(ar); mutex_lock(&ar->conf_mutex); switch (ar->state) { @@ -5111,14 +5109,14 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw) 1, pdev->pdev_id); if (ret) { - ath12k_err(ar->ab, "failed to enable PMF QOS: (%d\n", ret); + ath12k_err(ab, "failed to enable PMF QOS: (%d\n", ret); goto err; } ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 1, pdev->pdev_id); if (ret) { - ath12k_err(ar->ab, "failed to enable dynamic bw: %d\n", ret); + ath12k_err(ab, "failed to enable dynamic bw: %d\n", ret); goto err; } @@ -5148,7 +5146,7 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw) 1, pdev->pdev_id); if (ret) { - ath12k_err(ar->ab, "failed to enable MESH MCAST ENABLE: (%d\n", ret); + ath12k_err(ab, "failed to enable MESH MCAST ENABLE: (%d\n", ret); goto err; } @@ -5174,7 +5172,7 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw) } if (ret == -ENOTSUPP) - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + ath12k_dbg(ab, ATH12K_DBG_MAC, "monitor status config is not yet supported"); /* Configure the hash seed for hash based reo dest ring selection */ @@ -5196,7 +5194,6 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw) &ab->pdevs[ar->pdev_idx]); return 0; - err: ar->state = ATH12K_STATE_OFF; mutex_unlock(&ar->conf_mutex); @@ -5204,6 +5201,24 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw) return ret; } +static int ath12k_mac_op_start(struct ieee80211_hw *hw) +{ + struct ath12k *ar = hw->priv; + struct ath12k_base *ab = ar->ab; + int ret; + + ath12k_mac_drain_tx(ar); + + ret = ath12k_mac_start(ar); + if (ret) { + ath12k_err(ab, "fail to start mac operations in pdev idx %d ret %d\n", + ar->pdev_idx, ret); + return ret; + } + + return 0; +} + int ath12k_mac_rfkill_config(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; From 3bbc9c7429ff9e9c79c36add3742f7292d135b98 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:40 +0200 Subject: [PATCH 043/378] wifi: ath12k: refactor ath12k_mac_op_stop() To support single wiphy abstraction, introduce link/radio specific helper function in the mac80211 callback stop(). This way, the callback can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-6-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index d48161b43a4a..b6ee5527f350 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -5276,14 +5276,11 @@ int ath12k_mac_rfkill_enable_radio(struct ath12k *ar, bool enable) return 0; } -static void ath12k_mac_op_stop(struct ieee80211_hw *hw) +static void ath12k_mac_stop(struct ath12k *ar) { - struct ath12k *ar = hw->priv; struct htt_ppdu_stats_info *ppdu_stats, *tmp; int ret; - ath12k_mac_drain_tx(ar); - mutex_lock(&ar->conf_mutex); ret = ath12k_mac_config_mon_status_default(ar, false); if (ret && (ret != -ENOTSUPP)) @@ -5312,6 +5309,15 @@ static void ath12k_mac_op_stop(struct ieee80211_hw *hw) atomic_set(&ar->num_pending_mgmt_tx, 0); } +static void ath12k_mac_op_stop(struct ieee80211_hw *hw) +{ + struct ath12k *ar = hw->priv; + + ath12k_mac_drain_tx(ar); + + ath12k_mac_stop(ar); +} + static u8 ath12k_mac_get_vdev_stats_id(struct ath12k_vif *arvif) { From 92b30bb397869f94724b59d85f16e8de00fa9bc4 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:40 +0200 Subject: [PATCH 044/378] wifi: ath12k: refactor ath12k_mac_op_update_vif_offload() To support single wiphy abstraction, introduce link/radio specific helper function in the mac80211 callback update_vif_offload(). This way, the callback can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-7-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index b6ee5527f350..bc6746abebf0 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -5434,12 +5434,11 @@ static int ath12k_set_he_mu_sounding_mode(struct ath12k *ar, return ret; } -static void ath12k_mac_op_update_vif_offload(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static void ath12k_mac_update_vif_offload(struct ath12k_vif *arvif) { - struct ath12k *ar = hw->priv; + struct ieee80211_vif *vif = arvif->vif; + struct ath12k *ar = arvif->ar; struct ath12k_base *ab = ar->ab; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); u32 param_id, param_value; int ret; @@ -5481,6 +5480,14 @@ static void ath12k_mac_op_update_vif_offload(struct ieee80211_hw *hw, } } +static void ath12k_mac_op_update_vif_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + + ath12k_mac_update_vif_offload(arvif); +} + static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -5584,7 +5591,7 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, list_add(&arvif->list, &ar->arvifs); spin_unlock_bh(&ar->data_lock); - ath12k_mac_op_update_vif_offload(hw, vif); + ath12k_mac_update_vif_offload(arvif); nss = hweight32(ar->cfg_tx_chainmask) ? : 1; ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, From d629b0c149c913d633ada52357069b8ac7fc81c6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:41 +0200 Subject: [PATCH 045/378] wifi: ath12k: refactor ath12k_mac_op_configure_filter() To support single wiphy abstraction, introduce link/radio specific helper function in the mac80211 callback configure_filter(). This way, the callback can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-8-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 28 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index bc6746abebf0..ef9e3558f5eb 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -5831,19 +5831,15 @@ static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw, FIF_PROBE_REQ | \ FIF_FCSFAIL) -static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) +static void ath12k_mac_configure_filter(struct ath12k *ar, + unsigned int total_flags) { - struct ath12k *ar = hw->priv; bool reset_flag; int ret; - mutex_lock(&ar->conf_mutex); + lockdep_assert_held(&ar->conf_mutex); - *total_flags &= SUPPORTED_FILTERS; - ar->filter_flags = *total_flags; + ar->filter_flags = total_flags; /* For monitor mode */ reset_flag = !(ar->filter_flags & FIF_BCN_PRBRESP_PROMISC); @@ -5858,9 +5854,23 @@ static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, ath12k_warn(ar->ab, "fail to set monitor filter: %d\n", ret); } + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "total_flags:0x%x, reset_flag:%d\n", - *total_flags, reset_flag); + total_flags, reset_flag); +} + +static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct ath12k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + *total_flags &= SUPPORTED_FILTERS; + ath12k_mac_configure_filter(ar, *total_flags); mutex_unlock(&ar->conf_mutex); } From 5b1b5dbfd6a6590606d7dbb99b0708be100a4acb Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:41 +0200 Subject: [PATCH 046/378] wifi: ath12k: refactor ath12k_mac_op_ampdu_action() To support single wiphy abstraction, introduce link/radio specific helper function in the mac80211 callback ampdu_action(). This way, the callback can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-9-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index ef9e3558f5eb..895bf9d19457 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -5901,14 +5901,13 @@ static int ath12k_mac_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx return ret; } -static int ath12k_mac_op_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_ampdu_params *params) +static int ath12k_mac_ampdu_action(struct ath12k_vif *arvif, + struct ieee80211_ampdu_params *params) { - struct ath12k *ar = hw->priv; + struct ath12k *ar = arvif->ar; int ret = -EINVAL; - mutex_lock(&ar->conf_mutex); + lockdep_assert_held(&ar->conf_mutex); switch (params->action) { case IEEE80211_AMPDU_RX_START: @@ -5929,8 +5928,25 @@ static int ath12k_mac_op_ampdu_action(struct ieee80211_hw *hw, break; } + return ret; +} + +static int ath12k_mac_op_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + struct ath12k *ar = hw->priv; + struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + int ret = -EINVAL; + + mutex_lock(&ar->conf_mutex); + ret = ath12k_mac_ampdu_action(arvif, params); mutex_unlock(&ar->conf_mutex); + if (ret) + ath12k_warn(ar->ab, "pdev idx %d unable to perform ampdu action %d ret %d\n", + ar->pdev_idx, params->action, ret); + return ret; } From b33dcbe8d53d1e57cfc9bc035f43a27af5b6a973 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:41 +0200 Subject: [PATCH 047/378] wifi: ath12k: refactor ath12k_mac_op_flush() To support single wiphy abstraction, introduce link/radio specific helper function in the mac80211 callback flush(). This way, the callback can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-10-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 895bf9d19457..ce916bf2dcc9 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -6644,15 +6644,10 @@ static int ath12k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) return -EOPNOTSUPP; } -static void ath12k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop) +static void ath12k_mac_flush(struct ath12k *ar) { - struct ath12k *ar = hw->priv; long time_left; - if (drop) - return; - time_left = wait_event_timeout(ar->dp.tx_empty_waitq, (atomic_read(&ar->dp.num_tx_pending) == 0), ATH12K_FLUSH_TIMEOUT); @@ -6667,6 +6662,17 @@ static void ath12k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *v time_left); } +static void ath12k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct ath12k *ar = hw->priv; + + if (drop) + return; + + ath12k_mac_flush(ar); +} + static int ath12k_mac_bitrate_mask_num_ht_rates(struct ath12k *ar, enum nl80211_band band, From 5bdfb8c9db223009d15a2379b87d16cc800d439a Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Sun, 14 Jan 2024 17:02:41 +0200 Subject: [PATCH 048/378] wifi: ath12k: ath12k_start_vdev_delay(): convert to use ar To support single wiphy abstraction, remove the mac80211 hw data dependency from the start vdev delay function. This way, this function can be extended to handle multiple link/radio in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240103063731.3356060-11-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index ce916bf2dcc9..a90c0309230a 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -241,8 +241,8 @@ static const u32 ath12k_smps_map[] = { [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, }; -static int ath12k_start_vdev_delay(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); +static int ath12k_start_vdev_delay(struct ath12k *ar, + struct ath12k_vif *arvif); static const char *ath12k_mac_phymode_str(enum wmi_phy_mode mode) { @@ -3718,7 +3718,7 @@ static int ath12k_mac_station_add(struct ath12k *ar, if (ab->hw_params->vdev_start_delay && !arvif->is_started && arvif->vdev_type != WMI_VDEV_TYPE_AP) { - ret = ath12k_start_vdev_delay(ar->hw, vif); + ret = ath12k_start_vdev_delay(ar, arvif); if (ret) { ath12k_warn(ab, "failed to delay vdev start: %d\n", ret); goto free_peer; @@ -6411,12 +6411,11 @@ static void ath12k_mac_op_change_chanctx(struct ieee80211_hw *hw, mutex_unlock(&ar->conf_mutex); } -static int ath12k_start_vdev_delay(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int ath12k_start_vdev_delay(struct ath12k *ar, + struct ath12k_vif *arvif) { - struct ath12k *ar = hw->priv; struct ath12k_base *ab = ar->ab; - struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_vif *vif = arvif->vif; int ret; if (WARN_ON(arvif->is_started)) From 9666ad011992b51da2a4623d20084a363a3a095e Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 14 Jan 2024 17:02:42 +0200 Subject: [PATCH 049/378] wifi: ath11k: document HAL_RX_BUF_RBM_SW4_BM Commit 7636c9a6e7d7 ("wifi: ath11k: Add multi TX ring support for WCN6750") added HAL_RX_BUF_RBM_SW4_BM to enum hal_rx_buf_return_buf_manager. However, as flagged by the kernel-doc script, the documentation was not updated: drivers/net/wireless/ath/ath11k/hal.h:689: warning: Enum value 'HAL_RX_BUF_RBM_SW4_BM' not described in enum 'hal_rx_buf_return_buf_manager' So update the documentation. No functional changes, compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240111-document-hal_rx_buf_rbm_sw4_bm-v1-1-ad277e8ab3cc@quicinc.com --- drivers/net/wireless/ath/ath11k/hal.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h index 80447f488954..65e8f244ebb9 100644 --- a/drivers/net/wireless/ath/ath11k/hal.h +++ b/drivers/net/wireless/ath/ath11k/hal.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_HAL_H @@ -674,6 +674,7 @@ struct hal_srng_config { * @HAL_RX_BUF_RBM_SW1_BM: For Tx completion -- returned to host * @HAL_RX_BUF_RBM_SW2_BM: For Tx completion -- returned to host * @HAL_RX_BUF_RBM_SW3_BM: For Rx release -- returned to host + * @HAL_RX_BUF_RBM_SW4_BM: For Tx completion -- returned to host */ enum hal_rx_buf_return_buf_manager { From 76fece36f17a535c75ebc83d026bacad1263fd37 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Tue, 16 Jan 2024 14:33:07 +0200 Subject: [PATCH 050/378] wifi: ath12k: refactor QMI MLO host capability helper function Currently, QMI MLO host capability parameters are specific to the WCN7850 platform. To make use of this helper function across all the platforms, move the platform specific MLO capability parameter to the HW param configuration. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240113001659.1022465-2-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/hw.c | 9 +++++++ drivers/net/wireless/ath/ath12k/hw.h | 3 +++ drivers/net/wireless/ath/ath12k/qmi.c | 34 +++++++++++++++++++-------- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index de60d988d860..cbb6e2b6d826 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -914,6 +914,9 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .rfkill_on_level = 0, .rddm_size = 0, + + .def_num_link = 0, + .max_mlo_peer = 256, }, { .name = "wcn7850 hw2.0", @@ -978,6 +981,9 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .rfkill_on_level = 1, .rddm_size = 0x780000, + + .def_num_link = 2, + .max_mlo_peer = 32, }, { .name = "qcn9274 hw2.0", @@ -1040,6 +1046,9 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .rfkill_on_level = 0, .rddm_size = 0, + + .def_num_link = 0, + .max_mlo_peer = 256, }, }; diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index fa8230def22b..0c3b416ae150 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -192,6 +192,9 @@ struct ath12k_hw_params { u32 rfkill_on_level; u32 rddm_size; + + u8 def_num_link; + u16 max_mlo_peer; }; struct ath12k_hw_ops { diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index c4c7f31a91cd..916dec626702 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1893,8 +1893,16 @@ static const struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { }, }; -static void ath12k_host_cap_parse_mlo(struct qmi_wlanfw_host_cap_req_msg_v01 *req) +static void ath12k_host_cap_parse_mlo(struct ath12k_base *ab, + struct qmi_wlanfw_host_cap_req_msg_v01 *req) { + struct wlfw_host_mlo_chip_info_s_v01 *info; + u8 hw_link_id = 0; + int i; + + if (!ab->hw_params->def_num_link) + return; + req->mlo_capable_valid = 1; req->mlo_capable = 1; req->mlo_chip_id_valid = 1; @@ -1905,16 +1913,22 @@ static void ath12k_host_cap_parse_mlo(struct qmi_wlanfw_host_cap_req_msg_v01 *re /* Max peer number generally won't change for the same device * but needs to be synced with host driver. */ - req->max_mlo_peer = 32; + req->max_mlo_peer = ab->hw_params->max_mlo_peer; req->mlo_num_chips_valid = 1; req->mlo_num_chips = 1; + + info = &req->mlo_chip_info[0]; + info->chip_id = 0; + info->num_local_links = ab->hw_params->def_num_link; + + for (i = 0; i < info->num_local_links; i++) { + info->hw_link_id[i] = hw_link_id; + info->valid_mlo_link_id[i] = 1; + + hw_link_id++; + } + req->mlo_chip_info_valid = 1; - req->mlo_chip_info[0].chip_id = 0; - req->mlo_chip_info[0].num_local_links = 2; - req->mlo_chip_info[0].hw_link_id[0] = 0; - req->mlo_chip_info[0].hw_link_id[1] = 1; - req->mlo_chip_info[0].valid_mlo_link_id[0] = 1; - req->mlo_chip_info[0].valid_mlo_link_id[1] = 1; } static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) @@ -1960,10 +1974,10 @@ static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) */ req.nm_modem |= SLEEP_CLOCK_SELECT_INTERNAL_BIT; req.nm_modem |= PLATFORM_CAP_PCIE_GLOBAL_RESET; - - ath12k_host_cap_parse_mlo(&req); } + ath12k_host_cap_parse_mlo(ab, &req); + ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_host_cap_resp_msg_v01_ei, &resp); if (ret < 0) From 53a65445c144f99a6769788d0c04b64cdbb497d4 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Tue, 16 Jan 2024 14:33:07 +0200 Subject: [PATCH 051/378] wifi: ath12k: add QMI PHY capability learn support Currently, the number of PHY is learned from the firmware service ready event message. However, on the QCN9274 platform, number of PHY is a variable parameter. To enable MLO capability in the QMI host capability request message, the driver needs the PHY count information earlier than the firmware service ready event. Therefore, a new QMI message, "PHY capability message", is introduced to retrieve this information. This message allows the driver to fill in the MLO parameter in the QMI host capability request message. If the new QMI PHY capability message fails, the default configuration in the HW params will be used. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240113001659.1022465-3-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 1 + drivers/net/wireless/ath/ath12k/qmi.c | 124 ++++++++++++++++++++++++- drivers/net/wireless/ath/ath12k/qmi.h | 17 ++++ 3 files changed, 140 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index ababe7f01b5b..6cab747ff858 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1174,6 +1174,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, ab->dev = dev; ab->hif.bus = bus; + ab->qmi.num_radios = U8_MAX; return ab; diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 916dec626702..d20d08023c81 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -528,6 +528,67 @@ static const struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { }, }; +static const struct qmi_elem_info qmi_wlanfw_phy_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_phy_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + num_phy_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + num_phy), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + board_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + board_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + static const struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, @@ -1900,8 +1961,12 @@ static void ath12k_host_cap_parse_mlo(struct ath12k_base *ab, u8 hw_link_id = 0; int i; - if (!ab->hw_params->def_num_link) + if (!ab->qmi.num_radios || ab->qmi.num_radios == U8_MAX) { + ath12k_dbg(ab, ATH12K_DBG_QMI, + "skip QMI MLO cap due to invalid num_radio %d\n", + ab->qmi.num_radios); return; + } req->mlo_capable_valid = 1; req->mlo_capable = 1; @@ -1919,7 +1984,7 @@ static void ath12k_host_cap_parse_mlo(struct ath12k_base *ab, info = &req->mlo_chip_info[0]; info->chip_id = 0; - info->num_local_links = ab->hw_params->def_num_link; + info->num_local_links = ab->qmi.num_radios; for (i = 0; i < info->num_local_links; i++) { info->hw_link_id[i] = hw_link_id; @@ -2008,6 +2073,59 @@ static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) return ret; } +static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) +{ + struct qmi_wlanfw_phy_cap_req_msg_v01 req = {}; + struct qmi_wlanfw_phy_cap_resp_msg_v01 resp = {}; + struct qmi_txn txn; + int ret; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_phy_cap_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_PHY_CAP_REQ_V01, + QMI_WLANFW_PHY_CAP_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_phy_cap_req_msg_v01_ei, &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + ath12k_warn(ab, "failed to send phy capability request: %d\n", ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH12K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) + goto out; + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ret = -EOPNOTSUPP; + goto out; + } + + if (!resp.num_phy_valid) { + ret = -ENODATA; + goto out; + } + + ab->qmi.num_radios = resp.num_phy; + + ath12k_dbg(ab, ATH12K_DBG_QMI, "phy capability resp valid %d num_phy %d valid %d board_id %d\n", + resp.num_phy_valid, resp.num_phy, + resp.board_id_valid, resp.board_id); + + return; + +out: + /* If PHY capability not advertised then rely on default num link */ + ab->qmi.num_radios = ab->hw_params->def_num_link; + + ath12k_dbg(ab, ATH12K_DBG_QMI, + "no valid response from PHY capability, choose default num_phy %d\n", + ab->qmi.num_radios); +} + static int ath12k_qmi_fw_ind_register_send(struct ath12k_base *ab) { struct qmi_wlanfw_ind_register_req_msg_v01 *req; @@ -2794,6 +2912,8 @@ static int ath12k_qmi_event_server_arrive(struct ath12k_qmi *qmi) struct ath12k_base *ab = qmi->ab; int ret; + ath12k_qmi_phy_cap_send(ab); + ret = ath12k_qmi_fw_ind_register_send(ab); if (ret < 0) { ath12k_warn(ab, "qmi failed to send FW indication QMI:%d\n", ret); diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index e25bbaa125e8..bfed22c310be 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -141,6 +141,7 @@ struct ath12k_qmi { u32 target_mem_mode; bool target_mem_delayed; u8 cal_done; + u8 num_radios; struct target_info target; struct m3_mem_region m3_mem; unsigned int service_ins_id; @@ -251,6 +252,22 @@ struct qmi_wlanfw_host_cap_resp_msg_v01 { struct qmi_response_type_v01 resp; }; +#define QMI_WLANFW_PHY_CAP_REQ_MSG_V01_MAX_LEN 0 +#define QMI_WLANFW_PHY_CAP_REQ_V01 0x0057 +#define QMI_WLANFW_PHY_CAP_RESP_MSG_V01_MAX_LEN 18 +#define QMI_WLANFW_PHY_CAP_RESP_V01 0x0057 + +struct qmi_wlanfw_phy_cap_req_msg_v01 { +}; + +struct qmi_wlanfw_phy_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 num_phy_valid; + u8 num_phy; + u8 board_id_valid; + u32 board_id; +}; + #define QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN 54 #define QMI_WLANFW_IND_REGISTER_REQ_V01 0x0020 #define QMI_WLANFW_IND_REGISTER_RESP_MSG_V01_MAX_LEN 18 From 49b88e5f3fa114ed187f876082aa5bc4349f5bc7 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Wed, 17 Jan 2024 13:34:29 +0530 Subject: [PATCH 052/378] wifi: ath12k: replace ENOTSUPP with EOPNOTSUPP ENOTSUPP is not a standard error code, don't use it. Replace with EOPNOTSUPP instead. No functional changes, compile tested only. Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240117080431.2907471-2-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/dp_tx.c | 6 +++--- drivers/net/wireless/ath/ath12k/hal_rx.c | 4 ++-- drivers/net/wireless/ath/ath12k/mac.c | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 62f9cdbb811c..05d5f14cdfa1 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -151,7 +151,7 @@ int ath12k_dp_tx(struct ath12k *ar, struct ath12k_vif *arvif, if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && !ieee80211_is_data(hdr->frame_control)) - return -ENOTSUPP; + return -EOPNOTSUPP; pool_id = skb_get_queue_mapping(skb) & (ATH12K_HW_MAX_QUEUES - 1); @@ -837,7 +837,7 @@ int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab) if (dp->htt_tgt_ver_major != HTT_TARGET_VERSION_MAJOR) { ath12k_err(ab, "unsupported htt major version %d supported version is %d\n", dp->htt_tgt_ver_major, HTT_TARGET_VERSION_MAJOR); - return -ENOTSUPP; + return -EOPNOTSUPP; } return 0; diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c index 4f25eb9f7745..4fc08d4f85b5 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.c +++ b/drivers/net/wireless/ath/ath12k/hal_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "debug.h" @@ -247,7 +247,7 @@ int ath12k_hal_reo_cmd_send(struct ath12k_base *ab, struct hal_srng *srng, case HAL_REO_CMD_UNBLOCK_CACHE: case HAL_REO_CMD_FLUSH_TIMEOUT_LIST: ath12k_warn(ab, "Unsupported reo command %d\n", type); - ret = -ENOTSUPP; + ret = -EOPNOTSUPP; break; default: ath12k_warn(ab, "Unknown reo command %d\n", type); diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index a90c0309230a..d453e4165228 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -5055,7 +5055,7 @@ void ath12k_mac_drain_tx(struct ath12k *ar) static int ath12k_mac_config_mon_status_default(struct ath12k *ar, bool enable) { - return -ENOTSUPP; + return -EOPNOTSUPP; /* TODO: Need to support new monitor mode */ } @@ -5165,13 +5165,13 @@ static int ath12k_mac_start(struct ath12k *ar) * such as rssi, rx_duration. */ ret = ath12k_mac_config_mon_status_default(ar, true); - if (ret && (ret != -ENOTSUPP)) { + if (ret && (ret != -EOPNOTSUPP)) { ath12k_err(ab, "failed to configure monitor status ring with default rx_filter: (%d)\n", ret); goto err; } - if (ret == -ENOTSUPP) + if (ret == -EOPNOTSUPP) ath12k_dbg(ab, ATH12K_DBG_MAC, "monitor status config is not yet supported"); @@ -5283,7 +5283,7 @@ static void ath12k_mac_stop(struct ath12k *ar) mutex_lock(&ar->conf_mutex); ret = ath12k_mac_config_mon_status_default(ar, false); - if (ret && (ret != -ENOTSUPP)) + if (ret && (ret != -EOPNOTSUPP)) ath12k_err(ar->ab, "failed to clear rx_filter for monitor status ring: (%d)\n", ret); From 3422402bacd0322875be2e2a97a98e67d30c1b14 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Wed, 17 Jan 2024 13:34:30 +0530 Subject: [PATCH 053/378] wifi: ath11k: replace ENOTSUPP with EOPNOTSUPP ENOTSUPP is not a standard error code, don't use it. Replace with EOPNOTSUPP instead. No functional changes, compile tested only. Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240117080431.2907471-3-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath11k/dp_tx.c | 6 +++--- drivers/net/wireless/ath/ath11k/hal_rx.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index c1072e66e3e8..272b1c35f98d 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -103,7 +103,7 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, if (unlikely(!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && !ieee80211_is_data(hdr->frame_control))) - return -ENOTSUPP; + return -EOPNOTSUPP; pool_id = skb_get_queue_mapping(skb) & (ATH11K_HW_MAX_QUEUES - 1); @@ -1018,7 +1018,7 @@ int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab) if (dp->htt_tgt_ver_major != HTT_TARGET_VERSION_MAJOR) { ath11k_err(ab, "unsupported htt major version %d supported version is %d\n", dp->htt_tgt_ver_major, HTT_TARGET_VERSION_MAJOR); - return -ENOTSUPP; + return -EOPNOTSUPP; } return 0; diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index e758ee8e17c9..8f7dd43dc1bd 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "debug.h" @@ -246,7 +246,7 @@ int ath11k_hal_reo_cmd_send(struct ath11k_base *ab, struct hal_srng *srng, case HAL_REO_CMD_UNBLOCK_CACHE: case HAL_REO_CMD_FLUSH_TIMEOUT_LIST: ath11k_warn(ab, "Unsupported reo command %d\n", type); - ret = -ENOTSUPP; + ret = -EOPNOTSUPP; break; default: ath11k_warn(ab, "Unknown reo command %d\n", type); From bc2ef64931c263104532723e9d8d923cfb357ab6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Wed, 17 Jan 2024 13:34:31 +0530 Subject: [PATCH 054/378] wifi: ath10k: replace ENOTSUPP with EOPNOTSUPP ENOTSUPP is not a standard error code, don't use it. Replace with EOPNOTSUPP instead. No functional changes, compile tested only. Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240117080431.2907471-4-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath10k/core.c | 4 ++-- drivers/net/wireless/ath/ath10k/htt.c | 3 ++- drivers/net/wireless/ath/ath10k/mac.c | 6 +++--- drivers/net/wireless/ath/ath10k/pci.c | 10 +++++----- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 7 ++++--- drivers/net/wireless/ath/ath10k/wmi.c | 12 ++++++------ 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 0032f8aa892f..9ce6f49ab261 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -3613,7 +3613,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, default: ath10k_err(ar, "unsupported core hardware revision %d\n", hw_rev); - ret = -ENOTSUPP; + ret = -EOPNOTSUPP; goto err_free_mac; } diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 907e1e13871a..dbaf262cd7c1 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -381,7 +382,7 @@ static int ath10k_htt_verify_version(struct ath10k_htt *htt) htt->target_version_major != 3) { ath10k_err(ar, "unsupported htt major version %d. supported versions are 2 and 3\n", htt->target_version_major); - return -ENOTSUPP; + return -EOPNOTSUPP; } return 0; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 090bcf148d0c..fc503db2fd8e 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "mac.h" @@ -4056,7 +4056,7 @@ static int ath10k_mac_tx(struct ath10k *ar, !(skb_cb->flags & ATH10K_SKB_F_RAW_TX)) { WARN_ON_ONCE(1); ieee80211_free_txskb(hw, skb); - return -ENOTSUPP; + return -EOPNOTSUPP; } } @@ -7065,7 +7065,7 @@ static int ath10k_mac_set_tid_config(struct ath10k *ar, struct ieee80211_sta *st if (sta) { if (!sta->wme) - return -ENOTSUPP; + return -EOPNOTSUPP; arsta = (struct ath10k_sta *)sta->drv_priv; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 3de2de6d44bc..5c34b156b4ff 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -889,7 +889,7 @@ static u32 ath10k_pci_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); if (WARN_ON_ONCE(!ar_pci->targ_cpu_to_ce_addr)) - return -ENOTSUPP; + return -EOPNOTSUPP; return ar_pci->targ_cpu_to_ce_addr(ar, addr); } @@ -2668,7 +2668,7 @@ static int ath10k_pci_safe_chip_reset(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); if (!ar_pci->pci_soft_reset) - return -ENOTSUPP; + return -EOPNOTSUPP; return ar_pci->pci_soft_reset(ar); } @@ -2808,7 +2808,7 @@ static int ath10k_pci_chip_reset(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); if (WARN_ON(!ar_pci->pci_hard_reset)) - return -ENOTSUPP; + return -EOPNOTSUPP; return ar_pci->pci_hard_reset(ar); } @@ -3594,7 +3594,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, break; default: WARN_ON(1); - return -ENOTSUPP; + return -EOPNOTSUPP; } ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev, ATH10K_BUS_PCI, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 0ce08e9a0a3d..aed97fd121ba 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3,6 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" #include "debug.h" @@ -1351,7 +1352,7 @@ static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar, __le32_to_cpu(ev->abi.abi_ver_ns1) != WMI_TLV_ABI_VER_NS1 || __le32_to_cpu(ev->abi.abi_ver_ns2) != WMI_TLV_ABI_VER_NS2 || __le32_to_cpu(ev->abi.abi_ver_ns3) != WMI_TLV_ABI_VER_NS3) { - return -ENOTSUPP; + return -EOPNOTSUPP; } arg->min_tx_power = ev->hw_min_tx_power; @@ -2123,9 +2124,9 @@ static int ath10k_wmi_tlv_op_get_vdev_subtype(struct ath10k *ar, case WMI_VDEV_SUBTYPE_MESH_11S: return WMI_TLV_VDEV_SUBTYPE_MESH_11S; case WMI_VDEV_SUBTYPE_MESH_NON_11S: - return -ENOTSUPP; + return -EOPNOTSUPP; } - return -ENOTSUPP; + return -EOPNOTSUPP; } static struct sk_buff * diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 0cfd9484c45e..9e2f0a50aaea 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -8733,9 +8733,9 @@ int ath10k_wmi_op_get_vdev_subtype(struct ath10k *ar, return WMI_VDEV_SUBTYPE_LEGACY_PROXY_STA; case WMI_VDEV_SUBTYPE_MESH_11S: case WMI_VDEV_SUBTYPE_MESH_NON_11S: - return -ENOTSUPP; + return -EOPNOTSUPP; } - return -ENOTSUPP; + return -EOPNOTSUPP; } static int ath10k_wmi_10_2_4_op_get_vdev_subtype(struct ath10k *ar, @@ -8755,9 +8755,9 @@ static int ath10k_wmi_10_2_4_op_get_vdev_subtype(struct ath10k *ar, case WMI_VDEV_SUBTYPE_MESH_11S: return WMI_VDEV_SUBTYPE_10_2_4_MESH_11S; case WMI_VDEV_SUBTYPE_MESH_NON_11S: - return -ENOTSUPP; + return -EOPNOTSUPP; } - return -ENOTSUPP; + return -EOPNOTSUPP; } static int ath10k_wmi_10_4_op_get_vdev_subtype(struct ath10k *ar, @@ -8779,7 +8779,7 @@ static int ath10k_wmi_10_4_op_get_vdev_subtype(struct ath10k *ar, case WMI_VDEV_SUBTYPE_MESH_NON_11S: return WMI_VDEV_SUBTYPE_10_4_MESH_NON_11S; } - return -ENOTSUPP; + return -EOPNOTSUPP; } static struct sk_buff * From 60b9376583217c8726be0a57d9ff71d52e9ce261 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 29 Nov 2023 10:04:12 +0800 Subject: [PATCH 055/378] wifi: ath12k: fix wrong definitions of hal_reo_update_rx_queue Some fields of hal_reo_update_rx_queue structure are wrongly defined, so fix it. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20231129020414.56425-2-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/hal_desc.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h index 6c17adc6d60b..ec204939e50c 100644 --- a/drivers/net/wireless/ath/ath12k/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/hal_desc.h @@ -2500,13 +2500,13 @@ struct hal_rx_reo_queue { #define HAL_REO_UPD_RX_QUEUE_INFO1_PN_HANDLE_ENABLE BIT(30) #define HAL_REO_UPD_RX_QUEUE_INFO1_IGNORE_AMPDU_FLG BIT(31) -#define HAL_REO_UPD_RX_QUEUE_INFO2_BA_WINDOW_SIZE GENMASK(7, 0) -#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_SIZE GENMASK(9, 8) -#define HAL_REO_UPD_RX_QUEUE_INFO2_SVLD BIT(10) -#define HAL_REO_UPD_RX_QUEUE_INFO2_SSN GENMASK(22, 11) -#define HAL_REO_UPD_RX_QUEUE_INFO2_SEQ_2K_ERR BIT(23) -#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_ERR BIT(24) -#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_VALID BIT(25) +#define HAL_REO_UPD_RX_QUEUE_INFO2_BA_WINDOW_SIZE GENMASK(9, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_SIZE GENMASK(11, 10) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SVLD BIT(12) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SSN GENMASK(24, 13) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SEQ_2K_ERR BIT(25) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_ERR BIT(26) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_VALID BIT(27) struct hal_reo_update_rx_queue { struct hal_reo_cmd_hdr cmd; From b0970f50839ecbb71d43914fe88ea7eeb3416a21 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 17 Jan 2024 14:05:30 +0200 Subject: [PATCH 056/378] wifi: ath12k: add support for BA1024 Currently the maximum block ACK window size supported is 256. This results in that, when connected to an AP which supports larger BA sizes like BA512 or BA1024, only BA256 is established, leading to a lower peak throughput. So add support for BA1024, this is done by allocating a larger REO queue and advertising IEEE80211_MAX_AMPDU_BUF_EHT support to MAC80211. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20231129020414.56425-3-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/dp.h | 2 +- drivers/net/wireless/ath/ath12k/hal_desc.h | 6 ++++++ drivers/net/wireless/ath/ath12k/hal_rx.c | 11 ++++++++--- drivers/net/wireless/ath/ath12k/mac.c | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 1df3cdd46140..f8fc72a178ef 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -150,7 +150,7 @@ struct ath12k_pdev_dp { #define DP_RX_HASH_ENABLE 1 /* Enable hash based Rx steering */ -#define DP_BA_WIN_SZ_MAX 256 +#define DP_BA_WIN_SZ_MAX 1024 #define DP_TCL_NUM_RING_MAX 4 diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h index ec204939e50c..63340256d3f6 100644 --- a/drivers/net/wireless/ath/ath12k/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/hal_desc.h @@ -2517,6 +2517,12 @@ struct hal_reo_update_rx_queue { __le32 pn[4]; } __packed; +struct hal_rx_reo_queue_1k { + struct hal_desc_header desc_hdr; + __le32 rx_bitmap_1023_288[23]; + __le32 reserved[8]; +} __packed; + #define HAL_REO_UNBLOCK_CACHE_INFO0_UNBLK_CACHE BIT(0) #define HAL_REO_UNBLOCK_CACHE_INFO0_RESOURCE_IDX GENMASK(2, 1) diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c index 4fc08d4f85b5..f7c1aaa3b5d4 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.c +++ b/drivers/net/wireless/ath/ath12k/hal_rx.c @@ -688,23 +688,28 @@ void ath12k_hal_reo_update_rx_reo_queue_status(struct ath12k_base *ab, u32 ath12k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid) { - u32 num_ext_desc; + u32 num_ext_desc, num_1k_desc = 0; if (ba_window_size <= 1) { if (tid != HAL_DESC_REO_NON_QOS_TID) num_ext_desc = 1; else num_ext_desc = 0; + } else if (ba_window_size <= 105) { num_ext_desc = 1; } else if (ba_window_size <= 210) { num_ext_desc = 2; - } else { + } else if (ba_window_size <= 256) { num_ext_desc = 3; + } else { + num_ext_desc = 10; + num_1k_desc = 1; } return sizeof(struct hal_rx_reo_queue) + - (num_ext_desc * sizeof(struct hal_rx_reo_queue_ext)); + (num_ext_desc * sizeof(struct hal_rx_reo_queue_ext)) + + (num_1k_desc * sizeof(struct hal_rx_reo_queue_1k)); } void ath12k_hal_reo_qdesc_setup(struct hal_rx_reo_queue *qdesc, diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index d453e4165228..408f1fada337 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7602,7 +7602,7 @@ static int ath12k_mac_hw_register(struct ath12k *ar) hw->queues = ATH12K_HW_MAX_QUEUES; wiphy->tx_queue_len = ATH12K_QUEUE_LEN; hw->offchannel_tx_hw_queue = ATH12K_HW_MAX_QUEUES - 1; - hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_EHT; hw->vif_data_size = sizeof(struct ath12k_vif); hw->sta_data_size = sizeof(struct ath12k_sta); From 955df16f2a4c3023753680925e71e099818c2329 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 17 Jan 2024 14:05:39 +0200 Subject: [PATCH 057/378] wifi: ath12k: change MAC buffer ring size to 2048 For WCN7850, there is a SRNG named MAC buffer ring, i.e., dp->rx_mac_buf_ring. During initialization, it is setup by host and then under control of firmware. During RX process, firmware fetches buffers from dp->rx_refill_buf_ring to fill that MAC buffer ring, and those buffers are taken by RXDMA to carry real WLAN frames received from air. Currently a low RX throughput is observed. Checking firmware log, lots of errors are reported by MAC buffer ring, complaining that it is running out of buffers, which further indicates that RXDMA is suffering from starvation. Currently the size of dp->rx_mac_buf_ring is configured as 1024. After changing it to 2048, those error messages are reduced, and a 6.4% increase is seen in peak throughput. Note that 2048 is an empirical value. It is chosen here because the RX throughput meets our expectation after the change. This change only applies to WCN7850 since other chips don't have a MAC buffer ring. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20231129020414.56425-4-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/dp.h | 1 + drivers/net/wireless/ath/ath12k/dp_rx.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index f8fc72a178ef..0a10cd362356 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -170,6 +170,7 @@ struct ath12k_pdev_dp { #define DP_REO_CMD_RING_SIZE 128 #define DP_REO_STATUS_RING_SIZE 2048 #define DP_RXDMA_BUF_RING_SIZE 4096 +#define DP_RX_MAC_BUF_RING_SIZE 2048 #define DP_RXDMA_REFILL_RING_SIZE 2048 #define DP_RXDMA_ERR_DST_RING_SIZE 1024 #define DP_RXDMA_MON_STATUS_RING_SIZE 1024 diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 1ee83f765929..07430289023f 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -4086,7 +4086,7 @@ int ath12k_dp_rx_alloc(struct ath12k_base *ab) ret = ath12k_dp_srng_setup(ab, &dp->rx_mac_buf_ring[i], HAL_RXDMA_BUF, 1, - i, 1024); + i, DP_RX_MAC_BUF_RING_SIZE); if (ret) { ath12k_warn(ab, "failed to setup rx_mac_buf_ring %d\n", i); From b856f023b40fa7735ca19de433fc1307d69c36d0 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Thu, 18 Jan 2024 06:33:19 +0530 Subject: [PATCH 058/378] wifi: ath12k: Refactor the mac80211 hw access from link/radio Currently, mac80211 hardware accesses link/radio structure directly in multiple locations. Introduce helper function to avoid this direct access, as this change will facilitate refactoring for Multi-link operation support. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo Link: https://msgid.link/20240118010320.3918136-2-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 10 +++-- drivers/net/wireless/ath/ath12k/core.h | 6 ++- drivers/net/wireless/ath/ath12k/dp_mon.c | 4 +- drivers/net/wireless/ath/ath12k/dp_rx.c | 6 +-- drivers/net/wireless/ath/ath12k/dp_tx.c | 4 +- drivers/net/wireless/ath/ath12k/mac.c | 51 ++++++++++++++---------- drivers/net/wireless/ath/ath12k/reg.c | 6 +-- drivers/net/wireless/ath/ath12k/wmi.c | 17 ++++---- 8 files changed, 60 insertions(+), 44 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 6cab747ff858..d21618ca70b9 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -879,6 +879,7 @@ static void ath12k_rfkill_work(struct work_struct *work) { struct ath12k_base *ab = container_of(work, struct ath12k_base, rfkill_work); struct ath12k *ar; + struct ieee80211_hw *hw; bool rfkill_radio_on; int i; @@ -891,8 +892,9 @@ static void ath12k_rfkill_work(struct work_struct *work) if (!ar) continue; + hw = ath12k_ar_to_hw(ar); ath12k_mac_rfkill_enable_radio(ar, rfkill_radio_on); - wiphy_rfkill_set_hw_state(ar->hw->wiphy, !rfkill_radio_on); + wiphy_rfkill_set_hw_state(hw->wiphy, !rfkill_radio_on); } } @@ -936,7 +938,7 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) if (!ar || ar->state == ATH12K_STATE_OFF) continue; - ieee80211_stop_queues(ar->hw); + ieee80211_stop_queues(ath12k_ar_to_hw(ar)); ath12k_mac_drain_tx(ar); complete(&ar->scan.started); complete(&ar->scan.completed); @@ -976,7 +978,7 @@ static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) case ATH12K_STATE_ON: ar->state = ATH12K_STATE_RESTARTING; ath12k_core_halt(ar); - ieee80211_restart_hw(ar->hw); + ieee80211_restart_hw(ath12k_ar_to_hw(ar)); break; case ATH12K_STATE_OFF: ath12k_warn(ab, diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index ba0a30f1ea29..10a292b28d4d 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_CORE_H @@ -896,4 +896,8 @@ static inline const char *ath12k_bus_str(enum ath12k_bus bus) return "unknown"; } +static inline struct ieee80211_hw *ath12k_ar_to_hw(struct ath12k *ar) +{ + return ar->hw; +} #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index be4b39f5fa80..2432ab605c85 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "dp_mon.h" @@ -1130,7 +1130,7 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED)) rx_status->flag |= RX_FLAG_8023; - ieee80211_rx_napi(ar->hw, pubsta, msdu, napi); + ieee80211_rx_napi(ath12k_ar_to_hw(ar), pubsta, msdu, napi); } static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id, diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 07430289023f..a31c24b54851 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -2458,7 +2458,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED)) rx_status->flag |= RX_FLAG_8023; - ieee80211_rx_napi(ar->hw, pubsta, msdu, napi); + ieee80211_rx_napi(ath12k_ar_to_hw(ar), pubsta, msdu, napi); } static int ath12k_dp_rx_process_msdu(struct ath12k *ar, @@ -2844,7 +2844,7 @@ static int ath12k_dp_rx_h_verify_tkip_mic(struct ath12k *ar, struct ath12k_peer ath12k_dp_rx_h_ppdu(ar, rx_desc, rxs); ath12k_dp_rx_h_undecap(ar, msdu, rx_desc, HAL_ENCRYPT_TYPE_TKIP_MIC, rxs, true); - ieee80211_rx(ar->hw, msdu); + ieee80211_rx(ath12k_ar_to_hw(ar), msdu); return -EINVAL; } diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 05d5f14cdfa1..d4db94112deb 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -401,7 +401,7 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, } } - ieee80211_tx_status_skb(ar->hw, msdu); + ieee80211_tx_status_skb(ath12k_ar_to_hw(ar), msdu); } static void @@ -498,7 +498,7 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, * Might end up reporting it out-of-band from HTT stats. */ - ieee80211_tx_status_skb(ar->hw, msdu); + ieee80211_tx_status_skb(ath12k_ar_to_hw(ar), msdu); exit: rcu_read_unlock(); diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 408f1fada337..27c5337a764e 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -542,7 +542,7 @@ struct ath12k_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id) arvif_iter.vdev_id = vdev_id; flags = IEEE80211_IFACE_ITER_RESUME_ALL; - ieee80211_iterate_active_interfaces_atomic(ar->hw, + ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar), flags, ath12k_get_arvif_iter, &arvif_iter); @@ -1040,7 +1040,7 @@ static int ath12k_mac_monitor_start(struct ath12k *ar) if (ar->monitor_started) return 0; - ieee80211_iter_chan_contexts_atomic(ar->hw, + ieee80211_iter_chan_contexts_atomic(ath12k_ar_to_hw(ar), ath12k_mac_get_any_chandef_iter, &chandef); if (!chandef) @@ -1085,7 +1085,7 @@ static int ath12k_mac_monitor_stop(struct ath12k *ar) static int ath12k_mac_config(struct ath12k *ar, u32 changed) { - struct ieee80211_hw *hw = ar->hw; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); struct ieee80211_conf *conf = &hw->conf; int ret = 0; @@ -1139,7 +1139,7 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_vif *arvif) { struct ath12k *ar = arvif->ar; struct ath12k_base *ab = ar->ab; - struct ieee80211_hw *hw = ar->hw; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); struct ieee80211_vif *vif = arvif->vif; struct ieee80211_mutable_offsets offs = {}; struct sk_buff *bcn; @@ -1227,6 +1227,7 @@ static void ath12k_peer_assoc_h_basic(struct ath12k *ar, struct ath12k_wmi_peer_assoc_arg *arg) { struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); u32 aid; lockdep_assert_held(&ar->conf_mutex); @@ -1241,7 +1242,7 @@ static void ath12k_peer_assoc_h_basic(struct ath12k *ar, arg->peer_associd = aid; arg->auth_flag = true; /* TODO: STA WAR in ath10k for listen interval required? */ - arg->peer_listen_intval = ar->hw->conf.listen_interval; + arg->peer_listen_intval = hw->conf.listen_interval; arg->peer_nss = 1; arg->peer_caps = vif->bss_conf.assoc_capability; } @@ -1255,6 +1256,7 @@ static void ath12k_peer_assoc_h_crypto(struct ath12k *ar, struct cfg80211_chan_def def; struct cfg80211_bss *bss; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); const u8 *rsnie = NULL; const u8 *wpaie = NULL; @@ -1263,7 +1265,7 @@ static void ath12k_peer_assoc_h_crypto(struct ath12k *ar, if (WARN_ON(ath12k_mac_vif_chan(vif, &def))) return; - bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid, NULL, 0, + bss = cfg80211_get_bss(hw->wiphy, def.chan, info->bssid, NULL, 0, IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); if (arvif->rsnie_present || arvif->wpaie_present) { @@ -1283,7 +1285,7 @@ static void ath12k_peer_assoc_h_crypto(struct ath12k *ar, ies->data, ies->len); rcu_read_unlock(); - cfg80211_put_bss(ar->hw->wiphy, bss); + cfg80211_put_bss(hw->wiphy, bss); } /* FIXME: base on RSN IE/WPA IE is a correct idea? */ @@ -1317,6 +1319,7 @@ static void ath12k_peer_assoc_h_rates(struct ath12k *ar, struct cfg80211_chan_def def; const struct ieee80211_supported_band *sband; const struct ieee80211_rate *rates; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); enum nl80211_band band; u32 ratemask; u8 rate; @@ -1328,7 +1331,7 @@ static void ath12k_peer_assoc_h_rates(struct ath12k *ar, return; band = def.chan->band; - sband = ar->hw->wiphy->bands[band]; + sband = hw->wiphy->bands[band]; ratemask = sta->deflink.supp_rates[band]; ratemask &= arvif->bitrate_mask.control[band].legacy; rates = sband->bitrates; @@ -2423,6 +2426,7 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar, struct cfg80211_chan_def *def) { struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); const struct ieee80211_supported_band *sband; u8 basic_rate_idx; int hw_rate_code; @@ -2432,7 +2436,7 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar, lockdep_assert_held(&ar->conf_mutex); - sband = ar->hw->wiphy->bands[def->chan->band]; + sband = hw->wiphy->bands[def->chan->band]; basic_rate_idx = ffs(vif->bss_conf.basic_rates) - 1; bitrate = sband->bitrates[basic_rate_idx].bitrate; @@ -2459,6 +2463,7 @@ static int ath12k_mac_fils_discovery(struct ath12k_vif *arvif, struct ieee80211_bss_conf *info) { struct ath12k *ar = arvif->ar; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); struct sk_buff *tmpl; int ret; u32 interval; @@ -2467,7 +2472,7 @@ static int ath12k_mac_fils_discovery(struct ath12k_vif *arvif, if (info->fils_discovery.max_interval) { interval = info->fils_discovery.max_interval; - tmpl = ieee80211_get_fils_discovery_tmpl(ar->hw, arvif->vif); + tmpl = ieee80211_get_fils_discovery_tmpl(hw, arvif->vif); if (tmpl) ret = ath12k_wmi_fils_discovery_tmpl(ar, arvif->vdev_id, tmpl); @@ -2475,7 +2480,7 @@ static int ath12k_mac_fils_discovery(struct ath12k_vif *arvif, unsol_bcast_probe_resp_enabled = 1; interval = info->unsol_bcast_probe_resp_interval; - tmpl = ieee80211_get_unsol_bcast_probe_resp_tmpl(ar->hw, + tmpl = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, arvif->vif); if (tmpl) ret = ath12k_wmi_probe_resp_tmpl(ar, arvif->vdev_id, @@ -2798,6 +2803,8 @@ static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, void __ath12k_mac_scan_finish(struct ath12k *ar) { + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); + lockdep_assert_held(&ar->data_lock); switch (ar->scan.state) { @@ -2806,7 +2813,7 @@ void __ath12k_mac_scan_finish(struct ath12k *ar) case ATH12K_SCAN_RUNNING: case ATH12K_SCAN_ABORTING: if (ar->scan.is_roc && ar->scan.roc_notify) - ieee80211_remain_on_channel_expired(ar->hw); + ieee80211_remain_on_channel_expired(hw); fallthrough; case ATH12K_SCAN_STARTING: if (!ar->scan.is_roc) { @@ -2817,7 +2824,7 @@ void __ath12k_mac_scan_finish(struct ath12k *ar) ATH12K_SCAN_STARTING)), }; - ieee80211_scan_completed(ar->hw, &info); + ieee80211_scan_completed(hw, &info); } ar->scan.state = ATH12K_SCAN_IDLE; @@ -3036,7 +3043,7 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw, } /* Add a margin to account for event/command processing */ - ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, + ieee80211_queue_delayed_work(ath12k_ar_to_hw(ar), &ar->scan.timeout, msecs_to_jiffies(arg.max_scan_time + ATH12K_MAC_SCAN_TIMEOUT_MSECS)); @@ -4819,7 +4826,7 @@ static void ath12k_mgmt_over_wmi_tx_drop(struct ath12k *ar, struct sk_buff *skb) { int num_mgmt; - ieee80211_free_txskb(ar->hw, skb); + ieee80211_free_txskb(ath12k_ar_to_hw(ar), skb); num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx); @@ -4996,7 +5003,7 @@ static int ath12k_mac_mgmt_tx(struct ath12k *ar, struct sk_buff *skb, skb_queue_tail(q, skb); atomic_inc(&ar->num_pending_mgmt_tx); - ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work); + ieee80211_queue_work(ath12k_ar_to_hw(ar), &ar->wmi_mgmt_tx_work); return 0; } @@ -6357,7 +6364,7 @@ ath12k_mac_update_active_vif_chan(struct ath12k *ar, struct ieee80211_chanctx_conf *ctx) { struct ath12k_mac_change_chanctx_arg arg = { .ctx = ctx }; - struct ieee80211_hw *hw = ar->hw; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); lockdep_assert_held(&ar->conf_mutex); @@ -6874,7 +6881,7 @@ static void ath12k_mac_set_bitrate_mask_iter(void *data, arsta->changed |= IEEE80211_RC_SUPP_RATES_CHANGED; spin_unlock_bh(&ar->data_lock); - ieee80211_queue_work(ar->hw, &arsta->update_wk); + ieee80211_queue_work(ath12k_ar_to_hw(ar), &arsta->update_wk); } static void ath12k_mac_disable_peer_fixed_rate(void *data, @@ -7350,7 +7357,7 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, static int ath12k_mac_setup_iface_combinations(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; - struct ieee80211_hw *hw = ar->hw; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); struct wiphy *wiphy = hw->wiphy; struct ieee80211_iface_combination *combinations; struct ieee80211_iface_limit *limits; @@ -7457,7 +7464,7 @@ static void ath12k_mac_cleanup_unregister(struct ath12k *ar) static void ath12k_mac_hw_unregister(struct ath12k *ar) { - struct ieee80211_hw *hw = ar->hw; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); struct wiphy *wiphy = hw->wiphy; cancel_work_sync(&ar->regd_update_work); @@ -7503,7 +7510,7 @@ static int ath12k_mac_setup_register(struct ath12k *ar, static int ath12k_mac_hw_register(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; - struct ieee80211_hw *hw = ar->hw; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); struct wiphy *wiphy = hw->wiphy; struct ath12k_pdev *pdev = ar->pdev; struct ath12k_pdev_cap *cap = &pdev->cap; @@ -7777,7 +7784,7 @@ static void ath12k_mac_hw_destroy(struct ath12k_base *ab) if (!ar) continue; - ieee80211_free_hw(ar->hw); + ieee80211_free_hw(ath12k_ar_to_hw(ar)); pdev->ar = NULL; } } diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index f924bc13ccff..88e71d171355 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include "core.h" @@ -95,7 +95,7 @@ int ath12k_reg_update_chan_list(struct ath12k *ar) struct ieee80211_supported_band **bands; struct ath12k_wmi_scan_chan_list_arg *arg; struct ieee80211_channel *channel; - struct ieee80211_hw *hw = ar->hw; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); struct ath12k_wmi_channel_arg *ch; enum nl80211_band band; int num_channels = 0; @@ -199,7 +199,7 @@ static void ath12k_copy_regd(struct ieee80211_regdomain *regd_orig, int ath12k_regd_update(struct ath12k *ar, bool init) { - struct ieee80211_hw *hw = ar->hw; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); struct ieee80211_regdomain *regd, *regd_copy = NULL; int ret, regd_len, pdev_id; struct ath12k_base *ab; diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 553d2566b3f7..4d41c335ef34 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -4948,7 +4948,7 @@ static int wmi_process_mgmt_tx_comp(struct ath12k *ar, u32 desc_id, if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && !status) info->flags |= IEEE80211_TX_STAT_ACK; - ieee80211_tx_status_irqsafe(ar->hw, msdu); + ieee80211_tx_status_irqsafe(ath12k_ar_to_hw(ar), msdu); num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx); @@ -5076,6 +5076,8 @@ static void ath12k_wmi_event_scan_bss_chan(struct ath12k *ar) static void ath12k_wmi_event_scan_foreign_chan(struct ath12k *ar, u32 freq) { + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); + lockdep_assert_held(&ar->data_lock); switch (ar->scan.state) { @@ -5087,7 +5089,7 @@ static void ath12k_wmi_event_scan_foreign_chan(struct ath12k *ar, u32 freq) break; case ATH12K_SCAN_RUNNING: case ATH12K_SCAN_ABORTING: - ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq); + ar->scan_channel = ieee80211_get_channel(hw->wiphy, freq); break; } } @@ -5226,13 +5228,14 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, static int freq_to_idx(struct ath12k *ar, int freq) { struct ieee80211_supported_band *sband; + struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); int band, ch, idx = 0; for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { if (!ar->mac.sbands[band].channels) continue; - sband = ar->hw->wiphy->bands[band]; + sband = hw->wiphy->bands[band]; if (!sband) continue; @@ -5863,7 +5866,7 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) status->freq, status->band, status->signal, status->rate_idx); - ieee80211_rx_ni(ar->hw, skb); + ieee80211_rx_ni(ath12k_ar_to_hw(ar), skb); exit: rcu_read_unlock(); @@ -6036,7 +6039,7 @@ static void ath12k_peer_sta_kickout_event(struct ath12k_base *ab, struct sk_buff goto exit; } - sta = ieee80211_find_sta_by_ifaddr(ar->hw, + sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar), arg.mac_addr, NULL); if (!sta) { ath12k_warn(ab, "Spurious quick kickout for STA %pM\n", @@ -6530,7 +6533,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff if (ar->dfs_block_radar_events) ath12k_info(ab, "DFS Radar detected, but ignored as requested\n"); else - ieee80211_radar_detected(ar->hw); + ieee80211_radar_detected(ath12k_ar_to_hw(ar)); exit: rcu_read_unlock(); From 6db6e70a17f6fb3f2cfae31bb212a2179d0f6e6b Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Thu, 18 Jan 2024 07:58:56 +0200 Subject: [PATCH 059/378] wifi: ath12k: Introduce the container for mac80211 hw To support multi link operation, we need to combine all the link/pdev under a single wiphy. This avoids the overhead of synchronization across multiple hardware instances in both the cfg80211 and mac80211 layers. Currently, each link/pdev is registered as separate wiphy, tightly coupled with link/pdev/radio (ar) structure. To enable single wiphy registration within the chip, we decouple the wiphy data entity from the link/pdev/radio (ar) structure and move it under the chip (ab) structure with a new data container (ath12k_hw) structure. This approach improves scalability for future multi link operation support. mac80211 hw private data structure diagram ------------------------------------------ Now After +---------------------+ +---------------------+ |mac80211 hw priv data| |mac80211 hw priv data| | | | | | | | | | | | | | | | ath12k_hw (ah) | | | | | | | +-------------------> | | | ath12k (ar) | | +-------------+ | | | | | | | | | | | ath12k (ar) | | | | | | | | | | | | | | | | | +-------------+ | | | | | | | | | +---------------------+ +---------------------+ Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo Link: https://msgid.link/20240118010320.3918136-3-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 10 +- drivers/net/wireless/ath/ath12k/core.h | 37 ++- drivers/net/wireless/ath/ath12k/mac.c | 421 +++++++++++++++++-------- drivers/net/wireless/ath/ath12k/mac.h | 4 +- drivers/net/wireless/ath/ath12k/reg.c | 3 +- 5 files changed, 337 insertions(+), 138 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index d21618ca70b9..1baad3302157 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -923,6 +923,7 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) { struct ath12k *ar; struct ath12k_pdev *pdev; + struct ath12k_hw *ah; int i; spin_lock_bh(&ab->base_lock); @@ -932,13 +933,20 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) if (ab->is_reset) set_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags); + for (i = 0; i < ab->num_hw; i++) { + if (!ab->ah[i]) + continue; + + ah = ab->ah[i]; + ieee80211_stop_queues(ah->hw); + } + for (i = 0; i < ab->num_radios; i++) { pdev = &ab->pdevs[i]; ar = pdev->ar; if (!ar || ar->state == ATH12K_STATE_OFF) continue; - ieee80211_stop_queues(ath12k_ar_to_hw(ar)); ath12k_mac_drain_tx(ar); complete(&ar->scan.started); complete(&ar->scan.completed); diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 10a292b28d4d..a56fc74366b4 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -473,7 +473,7 @@ struct ath12k_per_peer_tx_stats { struct ath12k { struct ath12k_base *ab; struct ath12k_pdev *pdev; - struct ieee80211_hw *hw; + struct ath12k_hw *ah; struct ath12k_wmi_pdev *wmi; struct ath12k_pdev_dp dp; u8 mac_addr[ETH_ALEN]; @@ -537,6 +537,7 @@ struct ath12k { /* pdev_idx starts from 0 whereas pdev->pdev_id starts with 1 */ u8 pdev_idx; u8 lmac_id; + u8 hw_link_id; struct completion peer_assoc_done; struct completion peer_delete_done; @@ -596,6 +597,13 @@ struct ath12k { int monitor_vdev_id; }; +struct ath12k_hw { + struct ieee80211_hw *hw; + + u8 num_radio; + struct ath12k radio[] __aligned(sizeof(void *)); +}; + struct ath12k_band_cap { u32 phy_id; u32 max_bw_supported; @@ -729,6 +737,16 @@ struct ath12k_base { u8 fw_pdev_count; struct ath12k_pdev __rcu *pdevs_active[MAX_RADIOS]; + + /* Holds information of wiphy (hw) registration. + * + * In Multi/Single Link Operation case, all pdevs are registered as + * a single wiphy. In other (legacy/Non-MLO) cases, each pdev is + * registered as separate wiphys. + */ + struct ath12k_hw *ah[MAX_RADIOS]; + u8 num_hw; + struct ath12k_wmi_hal_reg_capabilities_ext_arg hal_reg_cap[MAX_RADIOS]; unsigned long long free_vdev_map; unsigned long long free_vdev_stats_id_map; @@ -810,6 +828,11 @@ struct ath12k_base { u8 drv_priv[] __aligned(sizeof(void *)); }; +struct ath12k_pdev_map { + struct ath12k_base *ab; + u8 pdev_idx; +}; + int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab); int ath12k_core_pre_init(struct ath12k_base *ab); int ath12k_core_init(struct ath12k_base *ath12k); @@ -896,8 +919,18 @@ static inline const char *ath12k_bus_str(enum ath12k_bus bus) return "unknown"; } +static inline struct ath12k_hw *ath12k_hw_to_ah(struct ieee80211_hw *hw) +{ + return hw->priv; +} + +static inline struct ath12k *ath12k_ah_to_ar(struct ath12k_hw *ah) +{ + return ah->radio; +} + static inline struct ieee80211_hw *ath12k_ar_to_hw(struct ath12k *ar) { - return ar->hw; + return ar->ah->hw; } #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 27c5337a764e..fe600dbef875 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1124,9 +1124,12 @@ static int ath12k_mac_config(struct ath12k *ar, u32 changed) static int ath12k_mac_op_config(struct ieee80211_hw *hw, u32 changed) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; int ret; + ar = ath12k_ah_to_ar(ah); + ret = ath12k_mac_config(ar, changed); if (ret) ath12k_warn(ar->ab, "failed to update config pdev idx %d: %d\n", @@ -2791,9 +2794,12 @@ static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_bss_conf *info, u64 changed) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + ar = ath12k_ah_to_ar(ah); + mutex_lock(&ar->conf_mutex); ath12k_mac_bss_info_changed(ar, arvif, info, changed); @@ -2969,13 +2975,16 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *hw_req) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); struct cfg80211_scan_request *req = &hw_req->req; struct ath12k_wmi_scan_req_arg arg = {}; int ret; int i; + ar = ath12k_ah_to_ar(ah); + mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); @@ -3054,13 +3063,17 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw, kfree(arg.extraie.ptr); mutex_unlock(&ar->conf_mutex); + return ret; } static void ath12k_mac_op_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + + ar = ath12k_ah_to_ar(ah); mutex_lock(&ar->conf_mutex); ath12k_scan_abort(ar); @@ -3188,8 +3201,9 @@ static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { - struct ath12k *ar = hw->priv; - struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + struct ath12k_base *ab; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); struct ath12k_peer *peer; struct ath12k_sta *arsta; @@ -3204,6 +3218,9 @@ static int ath12k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, key->cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256) return 1; + ar = ath12k_ah_to_ar(ah); + ab = ar->ab; + if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags)) return 1; @@ -3779,7 +3796,8 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); struct ath12k_sta *arsta = ath12k_sta_to_arsta(sta); struct ath12k_peer *peer; @@ -3790,6 +3808,8 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, new_state == IEEE80211_STA_NOTEXIST)) cancel_work_sync(&arsta->update_wk); + ar = ath12k_ah_to_ar(ah); + mutex_lock(&ar->conf_mutex); if (old_state == IEEE80211_STA_NOTEXIST && @@ -3885,6 +3905,7 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, } mutex_unlock(&ar->conf_mutex); + return ret; } @@ -3892,7 +3913,8 @@ static int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); int ret; s16 txpwr; @@ -3908,6 +3930,8 @@ static int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, if (txpwr > ATH12K_TX_POWER_MAX_VAL || txpwr < ATH12K_TX_POWER_MIN_VAL) return -EINVAL; + ar = ath12k_ah_to_ar(ah); + mutex_lock(&ar->conf_mutex); ret = ath12k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, @@ -3928,12 +3952,15 @@ static void ath12k_mac_op_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u32 changed) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; struct ath12k_sta *arsta = ath12k_sta_to_arsta(sta); struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); struct ath12k_peer *peer; u32 bw, smps; + ar = ath12k_ah_to_ar(ah); + spin_lock_bh(&ar->ab->base_lock); peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr); @@ -4108,10 +4135,13 @@ static int ath12k_mac_op_conf_tx(struct ieee80211_hw *hw, unsigned int link_id, u16 ac, const struct ieee80211_tx_queue_params *params) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); int ret; + ar = ath12k_ah_to_ar(ah); + mutex_lock(&ar->conf_mutex); ret = ath12k_mac_conf_tx(arvif, link_id, ac, params); mutex_unlock(&ar->conf_mutex); @@ -5013,10 +5043,10 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb); - struct ath12k *ar = hw->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = info->control.vif; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k *ar = arvif->ar; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; u32 info_flags = info->flags; @@ -5210,7 +5240,8 @@ static int ath12k_mac_start(struct ath12k *ar) static int ath12k_mac_op_start(struct ieee80211_hw *hw) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah); struct ath12k_base *ab = ar->ab; int ret; @@ -5318,7 +5349,8 @@ static void ath12k_mac_stop(struct ath12k *ar) static void ath12k_mac_op_stop(struct ieee80211_hw *hw) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah); ath12k_mac_drain_tx(ar); @@ -5498,8 +5530,9 @@ static void ath12k_mac_op_update_vif_offload(struct ieee80211_hw *hw, static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { - struct ath12k *ar = hw->priv; - struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + struct ath12k_base *ab; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); struct ath12k_wmi_vdev_create_arg vdev_arg = {0}; struct ath12k_wmi_peer_create_arg peer_param; @@ -5511,6 +5544,9 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + ar = ath12k_ah_to_ar(ah); + ab = ar->ab; + mutex_lock(&ar->conf_mutex); if (vif->type == NL80211_IFTYPE_AP && @@ -5757,12 +5793,16 @@ static void ath12k_mac_vif_unref(struct ath12k_dp *dp, struct ieee80211_vif *vif static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); - struct ath12k_base *ab = ar->ab; + struct ath12k_base *ab; unsigned long time_left; int ret; + ar = ath12k_ah_to_ar(ah); + ab = ar->ab; + mutex_lock(&ar->conf_mutex); ath12k_dbg(ab, ATH12K_DBG_MAC, "mac remove interface (vdev %d)\n", @@ -5872,7 +5912,10 @@ static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, unsigned int *total_flags, u64 multicast) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + + ar = ath12k_ah_to_ar(ah); mutex_lock(&ar->conf_mutex); @@ -5884,7 +5927,10 @@ static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, static int ath12k_mac_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + + ar = ath12k_ah_to_ar(ah); mutex_lock(&ar->conf_mutex); @@ -5898,9 +5944,12 @@ static int ath12k_mac_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 * static int ath12k_mac_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; int ret; + ar = ath12k_ah_to_ar(ah); + mutex_lock(&ar->conf_mutex); ret = __ath12k_set_antenna(ar, tx_ant, rx_ant); mutex_unlock(&ar->conf_mutex); @@ -5942,10 +5991,13 @@ static int ath12k_mac_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); int ret = -EINVAL; + ar = ath12k_ah_to_ar(ah); + mutex_lock(&ar->conf_mutex); ret = ath12k_mac_ampdu_action(arvif, params); mutex_unlock(&ar->conf_mutex); @@ -5960,8 +6012,12 @@ static int ath12k_mac_op_ampdu_action(struct ieee80211_hw *hw, static int ath12k_mac_op_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { - struct ath12k *ar = hw->priv; - struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + struct ath12k_base *ab; + + ar = ath12k_ah_to_ar(ah); + ab = ar->ab; ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx add freq %u width %d ptr %pK\n", @@ -5984,8 +6040,12 @@ static int ath12k_mac_op_add_chanctx(struct ieee80211_hw *hw, static void ath12k_mac_op_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { - struct ath12k *ar = hw->priv; - struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + struct ath12k_base *ab; + + ar = ath12k_ah_to_ar(ah); + ab = ar->ab; ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx remove freq %u width %d ptr %pK\n", @@ -6393,8 +6453,12 @@ static void ath12k_mac_op_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) { - struct ath12k *ar = hw->priv; - struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + struct ath12k_base *ab; + + ar = ath12k_ah_to_ar(ah); + ab = ar->ab; mutex_lock(&ar->conf_mutex); @@ -6456,12 +6520,16 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { - struct ath12k *ar = hw->priv; - struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + struct ath12k_base *ab; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); int ret; struct ath12k_wmi_peer_create_arg param; + ar = ath12k_ah_to_ar(ah); + ab = ar->ab; + mutex_lock(&ar->conf_mutex); ath12k_dbg(ab, ATH12K_DBG_MAC, @@ -6535,11 +6603,15 @@ ath12k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { - struct ath12k *ar = hw->priv; - struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + struct ath12k_base *ab; struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); int ret; + ar = ath12k_ah_to_ar(ah); + ab = ar->ab; + mutex_lock(&ar->conf_mutex); ath12k_dbg(ab, ATH12K_DBG_MAC, @@ -6587,7 +6659,10 @@ ath12k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, int n_vifs, enum ieee80211_chanctx_switch_mode mode) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + + ar = ath12k_ah_to_ar(ah); mutex_lock(&ar->conf_mutex); @@ -6629,10 +6704,15 @@ ath12k_set_vdev_param_to_all_vifs(struct ath12k *ar, int param, u32 value) */ static int ath12k_mac_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { - struct ath12k *ar = hw->priv; - int param_id = WMI_VDEV_PARAM_RTS_THRESHOLD; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + int param_id = WMI_VDEV_PARAM_RTS_THRESHOLD, ret; - return ath12k_set_vdev_param_to_all_vifs(ar, param_id, value); + ar = ath12k_ah_to_ar(ah); + + ret = ath12k_set_vdev_param_to_all_vifs(ar, param_id, value); + + return ret; } static int ath12k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) @@ -6671,7 +6751,8 @@ static void ath12k_mac_flush(struct ath12k *ar) static void ath12k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah); if (drop) return; @@ -6929,8 +7010,10 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC); sgi = mask->control[band].gi; - if (sgi == NL80211_TXRATE_FORCE_LGI) - return -EINVAL; + if (sgi == NL80211_TXRATE_FORCE_LGI) { + ret = -EINVAL; + goto out; + } /* mac80211 doesn't support sending a fixed HT/VHT MCS alone, rather it * requires passing at least one of used basic rates along with them. @@ -6946,7 +7029,7 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, if (ret) { ath12k_warn(ar->ab, "failed to get single legacy rate for vdev %i: %d\n", arvif->vdev_id, ret); - return ret; + goto out; } ieee80211_iterate_stations_atomic(hw, ath12k_mac_disable_peer_fixed_rate, @@ -6991,7 +7074,8 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, */ ath12k_warn(ar->ab, "Setting more than one MCS Value in bitrate mask not supported\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } ieee80211_iterate_stations_atomic(hw, @@ -7018,6 +7102,7 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, mutex_unlock(&ar->conf_mutex); +out: return ret; } @@ -7025,14 +7110,18 @@ static void ath12k_mac_op_reconfig_complete(struct ieee80211_hw *hw, enum ieee80211_reconfig_type reconfig_type) { - struct ath12k *ar = hw->priv; - struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; + struct ath12k_base *ab; struct ath12k_vif *arvif; int recovery_count; if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) return; + ar = ath12k_ah_to_ar(ah); + ab = ar->ab; + mutex_lock(&ar->conf_mutex); if (ar->state == ATH12K_STATE_RESTARTED) { @@ -7116,7 +7205,8 @@ ath12k_mac_update_bss_chan_survey(struct ath12k *ar, static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar; struct ieee80211_supported_band *sband; struct survey_info *ar_survey; int ret = 0; @@ -7124,6 +7214,8 @@ static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, if (idx >= ATH12K_NUM_CHANS) return -ENOENT; + ar = ath12k_ah_to_ar(ah); + ar_survey = &ar->survey[idx]; mutex_lock(&ar->conf_mutex); @@ -7155,6 +7247,7 @@ static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, exit: mutex_unlock(&ar->conf_mutex); + return ret; } @@ -7354,20 +7447,44 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, return 0; } -static int ath12k_mac_setup_iface_combinations(struct ath12k *ar) +static u16 ath12k_mac_get_ifmodes(struct ath12k_hw *ah) { - struct ath12k_base *ab = ar->ab; - struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); - struct wiphy *wiphy = hw->wiphy; + struct ath12k *ar = ath12k_ah_to_ar(ah); + u16 interface_modes = U16_MAX; + + interface_modes &= ar->ab->hw_params->interface_modes; + + return interface_modes == U16_MAX ? 0 : interface_modes; +} + +static bool ath12k_mac_is_iface_mode_enable(struct ath12k_hw *ah, + enum nl80211_iftype type) +{ + struct ath12k *ar = ath12k_ah_to_ar(ah); + u16 interface_modes, mode; + bool is_enable = true; + + mode = BIT(type); + + interface_modes = ar->ab->hw_params->interface_modes; + if (!(interface_modes & mode)) + is_enable = false; + + return is_enable; +} + +static int ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah) +{ + struct wiphy *wiphy = ah->hw->wiphy; struct ieee80211_iface_combination *combinations; struct ieee80211_iface_limit *limits; int n_limits, max_interfaces; bool ap, mesh; - ap = ab->hw_params->interface_modes & BIT(NL80211_IFTYPE_AP); + ap = ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_AP); mesh = IS_ENABLED(CONFIG_MAC80211_MESH) && - ab->hw_params->interface_modes & BIT(NL80211_IFTYPE_MESH_POINT); + ath12k_mac_is_iface_mode_enable(ah, NL80211_IFTYPE_MESH_POINT); combinations = kzalloc(sizeof(*combinations), GFP_KERNEL); if (!combinations) @@ -7462,10 +7579,11 @@ static void ath12k_mac_cleanup_unregister(struct ath12k *ar) kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels); } -static void ath12k_mac_hw_unregister(struct ath12k *ar) +static void ath12k_mac_hw_unregister(struct ath12k_hw *ah) { - struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); + struct ieee80211_hw *hw = ah->hw; struct wiphy *wiphy = hw->wiphy; + struct ath12k *ar = ath12k_ah_to_ar(ah); cancel_work_sync(&ar->regd_update_work); @@ -7507,13 +7625,14 @@ static int ath12k_mac_setup_register(struct ath12k *ar, return 0; } -static int ath12k_mac_hw_register(struct ath12k *ar) +static int ath12k_mac_hw_register(struct ath12k_hw *ah) { - struct ath12k_base *ab = ar->ab; - struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); + struct ieee80211_hw *hw = ah->hw; struct wiphy *wiphy = hw->wiphy; - struct ath12k_pdev *pdev = ar->pdev; - struct ath12k_pdev_cap *cap = &pdev->cap; + struct ath12k *ar = ath12k_ah_to_ar(ah); + struct ath12k_base *ab = ar->ab; + struct ath12k_pdev *pdev; + struct ath12k_pdev_cap *cap; static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, @@ -7528,30 +7647,34 @@ static int ath12k_mac_hw_register(struct ath12k *ar) int ret; u32 ht_cap = 0; - if (ab->pdevs_macaddr_valid) { + pdev = ar->pdev; + + if (ab->pdevs_macaddr_valid) ether_addr_copy(ar->mac_addr, pdev->mac_addr); - } else { + else ether_addr_copy(ar->mac_addr, ab->mac_addr); - ar->mac_addr[4] += ar->pdev_idx; - } ret = ath12k_mac_setup_register(ar, &ht_cap, hw->wiphy->bands); if (ret) goto out; - SET_IEEE80211_PERM_ADDR(hw, ar->mac_addr); - SET_IEEE80211_DEV(hw, ab->dev); + wiphy->max_ap_assoc_sta = ar->max_num_stations; - ret = ath12k_mac_setup_iface_combinations(ar); - if (ret) { - ath12k_err(ar->ab, "failed to setup interface combinations: %d\n", ret); - goto err_setup_unregister; - } + cap = &pdev->cap; wiphy->available_antennas_rx = cap->rx_chain_mask; wiphy->available_antennas_tx = cap->tx_chain_mask; - wiphy->interface_modes = ab->hw_params->interface_modes; + SET_IEEE80211_PERM_ADDR(hw, ar->mac_addr); + SET_IEEE80211_DEV(hw, ab->dev); + + ret = ath12k_mac_setup_iface_combinations(ah); + if (ret) { + ath12k_err(ab, "failed to setup interface combinations: %d\n", ret); + goto err_cleanup_unregister; + } + + wiphy->interface_modes = ath12k_mac_get_ifmodes(ah); if (wiphy->bands[NL80211_BAND_2GHZ] && wiphy->bands[NL80211_BAND_5GHZ] && @@ -7604,8 +7727,6 @@ static int ath12k_mac_hw_register(struct ath12k *ar) wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | NL80211_FEATURE_AP_SCAN; - wiphy->max_ap_assoc_sta = ar->max_num_stations; - hw->queues = ATH12K_HW_MAX_QUEUES; wiphy->tx_queue_len = ATH12K_QUEUE_LEN; hw->offchannel_tx_hw_queue = ATH12K_HW_MAX_QUEUES - 1; @@ -7642,7 +7763,7 @@ static int ath12k_mac_hw_register(struct ath12k *ar) ret = ieee80211_register_hw(hw); if (ret) { - ath12k_err(ar->ab, "ieee80211 registration failed: %d\n", ret); + ath12k_err(ab, "ieee80211 registration failed: %d\n", ret); goto err_free_if_combs; } @@ -7670,14 +7791,12 @@ static int ath12k_mac_hw_register(struct ath12k *ar) kfree(wiphy->iface_combinations[0].limits); kfree(wiphy->iface_combinations); -err_setup_unregister: - kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); - kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); - kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels); - - SET_IEEE80211_DEV(hw, NULL); +err_cleanup_unregister: + ath12k_mac_cleanup_unregister(ar); out: + SET_IEEE80211_DEV(hw, NULL); + return ret; } @@ -7723,8 +7842,7 @@ static void ath12k_mac_setup(struct ath12k *ar) int ath12k_mac_register(struct ath12k_base *ab) { - struct ath12k *ar; - struct ath12k_pdev *pdev; + struct ath12k_hw *ah; int i; int ret; @@ -7735,22 +7853,23 @@ int ath12k_mac_register(struct ath12k_base *ab) ab->cc_freq_hz = 320000; ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; + for (i = 0; i < ab->num_hw; i++) { + ah = ab->ah[i]; - ret = ath12k_mac_hw_register(ar); + ret = ath12k_mac_hw_register(ah); if (ret) - goto err_cleanup; + goto err; } return 0; -err_cleanup: +err: for (i = i - 1; i >= 0; i--) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; - ath12k_mac_hw_unregister(ar); + ah = ab->ah[i]; + if (!ah) + continue; + + ath12k_mac_hw_unregister(ah); } return ret; @@ -7758,89 +7877,125 @@ int ath12k_mac_register(struct ath12k_base *ab) void ath12k_mac_unregister(struct ath12k_base *ab) { - struct ath12k *ar; - struct ath12k_pdev *pdev; + struct ath12k_hw *ah; int i; - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; - if (!ar) + for (i = ab->num_hw - 1; i >= 0; i--) { + ah = ab->ah[i]; + if (!ah) continue; - ath12k_mac_hw_unregister(ar); + ath12k_mac_hw_unregister(ah); } } -static void ath12k_mac_hw_destroy(struct ath12k_base *ab) +static void ath12k_mac_hw_destroy(struct ath12k_hw *ah) { - struct ath12k *ar; - struct ath12k_pdev *pdev; - int i; - - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - ar = pdev->ar; - if (!ar) - continue; - - ieee80211_free_hw(ath12k_ar_to_hw(ar)); - pdev->ar = NULL; - } + ieee80211_free_hw(ah->hw); } -static int ath12k_mac_hw_allocate(struct ath12k_base *ab) +static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_base *ab, + struct ath12k_pdev_map *pdev_map, + u8 num_pdev_map) { struct ieee80211_hw *hw; struct ath12k *ar; struct ath12k_pdev *pdev; - int ret; + struct ath12k_hw *ah; int i; + u8 pdev_idx; - for (i = 0; i < ab->num_radios; i++) { - pdev = &ab->pdevs[i]; - hw = ieee80211_alloc_hw(sizeof(struct ath12k), &ath12k_ops); - if (!hw) { - ath12k_warn(ab, "failed to allocate mac80211 hw device\n"); - ret = -ENOMEM; - goto err_free_mac; - } + hw = ieee80211_alloc_hw(struct_size(ah, radio, num_pdev_map), + &ath12k_ops); + if (!hw) + return NULL; - ar = hw->priv; - ar->hw = hw; + ah = ath12k_hw_to_ah(hw); + ah->hw = hw; + ah->num_radio = num_pdev_map; + + for (i = 0; i < num_pdev_map; i++) { + ab = pdev_map[i].ab; + pdev_idx = pdev_map[i].pdev_idx; + pdev = &ab->pdevs[pdev_idx]; + + ar = ath12k_ah_to_ar(ah); + ar->ah = ah; ar->ab = ab; + ar->hw_link_id = i; ar->pdev = pdev; - ar->pdev_idx = i; + ar->pdev_idx = pdev_idx; pdev->ar = ar; ath12k_mac_setup(ar); } - return 0; - -err_free_mac: - ath12k_mac_hw_destroy(ab); - - return ret; + return ah; } void ath12k_mac_destroy(struct ath12k_base *ab) { - ath12k_mac_hw_destroy(ab); + struct ath12k_pdev *pdev; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + if (!pdev->ar) + continue; + + pdev->ar = NULL; + } + + for (i = 0; i < ab->num_hw; i++) { + if (!ab->ah[i]) + continue; + + ath12k_mac_hw_destroy(ab->ah[i]); + ab->ah[i] = NULL; + } } int ath12k_mac_allocate(struct ath12k_base *ab) { - int ret; + struct ath12k_hw *ah; + struct ath12k_pdev_map pdev_map[MAX_RADIOS]; + int ret, i, j; + u8 radio_per_hw; if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) return 0; - ret = ath12k_mac_hw_allocate(ab); - if (ret) - return ret; + ab->num_hw = ab->num_radios; + radio_per_hw = 1; + + for (i = 0; i < ab->num_hw; i++) { + for (j = 0; j < radio_per_hw; j++) { + pdev_map[j].ab = ab; + pdev_map[j].pdev_idx = (i * radio_per_hw) + j; + } + + ah = ath12k_mac_hw_allocate(ab, pdev_map, radio_per_hw); + if (!ah) { + ath12k_warn(ab, "failed to allocate mac80211 hw device for hw_idx %d\n", + i); + goto err; + } + + ab->ah[i] = ah; + } ath12k_dp_pdev_pre_alloc(ab); return 0; + +err: + for (i = i - 1; i >= 0; i--) { + if (!ab->ah[i]) + continue; + + ath12k_mac_hw_destroy(ab->ah[i]); + ab->ah[i] = NULL; + } + + return ret; } diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 7c63bb628adc..3f5e1be0dff9 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_MAC_H @@ -12,6 +12,8 @@ struct ath12k; struct ath12k_base; +struct ath12k_hw; +struct ath12k_pdev_map; struct ath12k_generic_iter { struct ath12k *ar; diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index 88e71d171355..a164f15d1e2b 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -48,7 +48,8 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath12k_wmi_init_country_arg arg; - struct ath12k *ar = hw->priv; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah); int ret; ath12k_dbg(ar->ab, ATH12K_DBG_REG, From 9f9df1a2535f6c890242b80d8538bd90ae540647 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 18 Jan 2024 07:53:48 +0200 Subject: [PATCH 060/378] wifi: ath12k: add support for collecting firmware log Currently there is no way to collect firmware log because firmware does not send it to host. Also host does not handle WMI_DIAG_EVENTID which is used by firmware to upload firmware log. So add support for it by firstly enabling firmware log upload via a QMI message, and secondly processing WMI DIAG event to expose it to userspace via trace event. This change applies to both WCN7850 and QCN9274. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240115023726.2866-1-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/qmi.c | 93 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/qmi.h | 17 ++++- drivers/net/wireless/ath/ath12k/trace.h | 29 +++++++- drivers/net/wireless/ath/ath12k/wmi.c | 9 +++ 4 files changed, 146 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index d20d08023c81..69f56400367c 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1954,6 +1954,50 @@ static const struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { }, }; +static const struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_ini_req_msg_v01, + enable_fwlog_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_ini_req_msg_v01, + enable_fwlog), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_wlan_ini_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + static void ath12k_host_cap_parse_mlo(struct ath12k_base *ab, struct qmi_wlanfw_host_cap_req_msg_v01 *req) { @@ -2853,6 +2897,49 @@ static int ath12k_qmi_wlanfw_wlan_cfg_send(struct ath12k_base *ab) return ret; } +static int ath12k_qmi_wlanfw_wlan_ini_send(struct ath12k_base *ab) +{ + struct qmi_wlanfw_wlan_ini_resp_msg_v01 resp = {}; + struct qmi_wlanfw_wlan_ini_req_msg_v01 req = {}; + struct qmi_txn txn; + int ret; + + req.enable_fwlog_valid = true; + req.enable_fwlog = 1; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_wlan_ini_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + ATH12K_QMI_WLANFW_WLAN_INI_REQ_V01, + QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_ini_req_msg_v01_ei, &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + ath12k_warn(ab, "failed to send QMI wlan ini request: %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH12K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath12k_warn(ab, "failed to receive QMI wlan ini request: %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath12k_warn(ab, "QMI wlan ini response failure: %d %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } + +out: + return ret; +} + void ath12k_qmi_firmware_stop(struct ath12k_base *ab) { int ret; @@ -2869,6 +2956,12 @@ int ath12k_qmi_firmware_start(struct ath12k_base *ab, { int ret; + ret = ath12k_qmi_wlanfw_wlan_ini_send(ab); + if (ret < 0) { + ath12k_warn(ab, "qmi failed to send wlan fw ini: %d\n", ret); + return ret; + } + ret = ath12k_qmi_wlanfw_wlan_cfg_send(ab); if (ret < 0) { ath12k_warn(ab, "qmi failed to send wlan cfg:%d\n", ret); diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index bfed22c310be..828ab2bf037d 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_QMI_H @@ -576,6 +576,21 @@ struct qmi_wlanfw_wlan_cfg_resp_msg_v01 { struct qmi_response_type_v01 resp; }; +#define ATH12K_QMI_WLANFW_WLAN_INI_REQ_V01 0x002F +#define ATH12K_QMI_WLANFW_WLAN_INI_RESP_V01 0x002F +#define QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_WLAN_INI_RESP_MSG_V01_MAX_LEN 7 + +struct qmi_wlanfw_wlan_ini_req_msg_v01 { + /* Must be set to true if enable_fwlog is being passed */ + u8 enable_fwlog_valid; + u8 enable_fwlog; +}; + +struct qmi_wlanfw_wlan_ini_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + int ath12k_qmi_firmware_start(struct ath12k_base *ab, u32 mode); void ath12k_qmi_firmware_stop(struct ath12k_base *ab); diff --git a/drivers/net/wireless/ath/ath12k/trace.h b/drivers/net/wireless/ath/ath12k/trace.h index f72096684b74..240737e1542d 100644 --- a/drivers/net/wireless/ath/ath12k/trace.h +++ b/drivers/net/wireless/ath/ath12k/trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) @@ -140,6 +140,33 @@ TRACE_EVENT(ath12k_htt_rxdesc, ) ); +TRACE_EVENT(ath12k_wmi_diag, + TP_PROTO(struct ath12k_base *ab, const void *data, size_t len), + + TP_ARGS(ab, data, len), + + TP_STRUCT__entry( + __string(device, dev_name(ab->dev)) + __string(driver, dev_driver_string(ab->dev)) + __field(u16, len) + __dynamic_array(u8, data, len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ab->dev)); + __assign_str(driver, dev_driver_string(ab->dev)); + __entry->len = len; + memcpy(__get_dynamic_array(data), data, len); + ), + + TP_printk( + "%s %s tlv diag len %d", + __get_str(driver), + __get_str(device), + __entry->len + ) +); + #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ /* we don't want to use include/trace/events */ diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 4d41c335ef34..2fa724e5851a 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -6664,6 +6664,12 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, kfree(tb); } +static void +ath12k_wmi_diag_event(struct ath12k_base *ab, struct sk_buff *skb) +{ + trace_ath12k_wmi_diag(ab, skb->data, skb->len); +} + static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -6774,6 +6780,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_VDEV_DELETE_RESP_EVENTID: ath12k_vdev_delete_resp_event(ab, skb); break; + case WMI_DIAG_EVENTID: + ath12k_wmi_diag_event(ab, skb); + break; /* TODO: Add remaining events */ default: ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id); From 1779487e72e0390d240df271be0005397d0110f3 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 18 Jan 2024 08:12:53 -0800 Subject: [PATCH 061/378] wifi: ath10k: add missing wmi_10_4_feature_mask documentation Currently kernel-doc reports the following issues: drivers/net/wireless/ath/ath10k/wmi.h:3033: warning: Enum value 'WMI_10_4_EXT_PEER_TID_CONFIGS_SUPPORT' not described in enum 'wmi_10_4_feature_mask' drivers/net/wireless/ath/ath10k/wmi.h:3033: warning: Enum value 'WMI_10_4_REPORT_AIRTIME' not described in enum 'wmi_10_4_feature_mask' Update the kernel-doc for enum wmi_10_4_feature_mask to add the missing documentation. No functional changes, compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240118-ath10k-kerneldoc-v1-1-99c7e8d95aad@quicinc.com --- drivers/net/wireless/ath/ath10k/wmi.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index b64b6e214bae..2379501225a4 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _WMI_H_ @@ -3008,8 +3008,11 @@ enum wmi_coex_version { * @WMI_10_4_TDLS_UAPSD_SLEEP_STA: TDLS sleep sta support enable/disable * @WMI_10_4_TDLS_CONN_TRACKER_IN_HOST_MODE: TDLS connection tracker in host * enable/disable - * @WMI_10_4_TDLS_EXPLICIT_MODE_ONLY:Explicit TDLS mode enable/disable + * @WMI_10_4_TDLS_EXPLICIT_MODE_ONLY: Explicit TDLS mode enable/disable * @WMI_10_4_TX_DATA_ACK_RSSI: Enable DATA ACK RSSI if firmware is capable + * @WMI_10_4_EXT_PEER_TID_CONFIGS_SUPPORT: Firmware supports Extended Peer + * TID configuration for QoS related settings + * @WMI_10_4_REPORT_AIRTIME: Firmware supports transmit airtime reporting */ enum wmi_10_4_feature_mask { WMI_10_4_LTEU_SUPPORT = BIT(0), From 5f813b0447feef3a4883b66e600c7317a4d7d76b Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 18 Jan 2024 08:12:54 -0800 Subject: [PATCH 062/378] wifi: ath10k: correctly document enum wmi_tlv_tx_pause_id Currently kernel-doc reports the issue: drivers/net/wireless/ath/ath10k/wmi-tlv.h:2363: warning: cannot understand function prototype: 'enum wmi_tlv_tx_pause_id ' Update the enum wmi_tlv_tx_pause_id documentation to fix this issue. No functional changes, compile tested only. Signed-off-by: Jeff Johnson Acked-by: Randy Dunlap Signed-off-by: Kalle Valo Link: https://msgid.link/20240118-ath10k-kerneldoc-v1-2-99c7e8d95aad@quicinc.com --- drivers/net/wireless/ath/ath10k/wmi-tlv.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 83a8f07a687f..8a2f87d0a3a3 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _WMI_TLV_H #define _WMI_TLV_H @@ -2343,7 +2343,7 @@ struct wmi_tlv_adaptive_qcs { } __packed; /** - * wmi_tlv_tx_pause_id - firmware tx queue pause reason types + * enum wmi_tlv_tx_pause_id - firmware tx queue pause reason types * * @WMI_TLV_TX_PAUSE_ID_MCC: used for by multi-channel firmware scheduler. * Only vdev_map is valid. From 75dd17fdef110d99a2613d1284d2863cfc7cf6e9 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 18 Jan 2024 08:12:55 -0800 Subject: [PATCH 063/378] wifi: ath10k: fix htt_q_state_conf & htt_q_state kernel-doc Currently kernel-doc reports: drivers/net/wireless/ath/ath10k/htt.h:1488: warning: cannot understand function prototype: 'struct htt_q_state_conf ' drivers/net/wireless/ath/ath10k/htt.h:1542: warning: cannot understand function prototype: 'struct htt_q_state ' Update the kernel-doc for these two structs to resolve the warnings. No functional changes, compile tested only. Signed-off-by: Jeff Johnson Acked-by: Randy Dunlap Signed-off-by: Kalle Valo Link: https://msgid.link/20240118-ath10k-kerneldoc-v1-3-99c7e8d95aad@quicinc.com --- drivers/net/wireless/ath/ath10k/htt.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 4a9270e2a4c8..eb0ce2f49315 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2021, 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021, 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _HTT_H_ @@ -1474,15 +1474,19 @@ enum htt_q_depth_type { #define HTT_TX_Q_STATE_ENTRY_MULTIPLIER 0 /** - * htt_q_state_conf - part of htt_frag_desc_bank_cfg for host q state config + * struct htt_q_state_conf - part of htt_frag_desc_bank_cfg for host q state config * * Defines host q state format and behavior. See htt_q_state. * + * @paddr: Queue physical address + * @num_peers: Number of supported peers + * @num_tids: Number of supported TIDs * @record_size: Defines the size of each host q entry in bytes. In practice * however firmware (at least 10.4.3-00191) ignores this host * configuration value and uses hardcoded value of 1. * @record_multiplier: This is valid only when q depth type is MSDUs. It * defines the exponent for the power of 2 multiplication. + * @pad: struct padding for 32-bit alignment */ struct htt_q_state_conf { __le32 paddr; @@ -1518,7 +1522,7 @@ struct htt_frag_desc_bank_cfg64 { #define HTT_TX_Q_STATE_ENTRY_EXP_LSB 6 /** - * htt_q_state - shared between host and firmware via DMA + * struct htt_q_state - shared between host and firmware via DMA * * This structure is used for the host to expose it's software queue state to * firmware so that its rate control can schedule fetch requests for optimized From c80cc5cfefbaca35e019a26f31faa11031c13665 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 18 Jan 2024 08:12:56 -0800 Subject: [PATCH 064/378] wifi: ath10k: Fix htt_data_tx_completion kernel-doc warning Currently kernel-doc reports: drivers/net/wireless/ath/ath10k/htt.h:911: warning: Cannot understand * @brief target -> host TX completion indication message definition on line 911 - I thought it was a doc line This is because even though struct htt_data_tx_completion uses the kernel-doc marker "/**", it doesn't actual use kernel-doc syntax for the documentation. Rather than try to update this legacy driver documentation to use kernel-doc style, just replace the comment marker. No functional changes, compile tested only. Signed-off-by: Jeff Johnson Acked-by: Randy Dunlap Signed-off-by: Kalle Valo Link: https://msgid.link/20240118-ath10k-kerneldoc-v1-4-99c7e8d95aad@quicinc.com --- drivers/net/wireless/ath/ath10k/htt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index eb0ce2f49315..603f6de62b0a 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -906,7 +906,7 @@ struct htt_data_tx_completion_ext { __le16 msdus_rssi[]; } __packed; -/** +/* * @brief target -> host TX completion indication message definition * * @details From f020c30299323c206b9041dd478ac040dd828ec0 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 18 Jan 2024 08:12:57 -0800 Subject: [PATCH 065/378] wifi: ath10k: Fix enum ath10k_fw_crash_dump_type kernel-doc The kernel-doc script currently reports: drivers/net/wireless/ath/ath10k/coredump.h:27: warning: Enum value 'ATH10K_FW_CRASH_DUMP_REGISTERS' not described in enum 'ath10k_fw_crash_dump_type' drivers/net/wireless/ath/ath10k/coredump.h:27: warning: Enum value 'ATH10K_FW_CRASH_DUMP_CE_DATA' not described in enum 'ath10k_fw_crash_dump_type' drivers/net/wireless/ath/ath10k/coredump.h:27: warning: Enum value 'ATH10K_FW_CRASH_DUMP_RAM_DATA' not described in enum 'ath10k_fw_crash_dump_type' drivers/net/wireless/ath/ath10k/coredump.h:27: warning: Enum value 'ATH10K_FW_CRASH_DUMP_MAX' not described in enum 'ath10k_fw_crash_dump_type' drivers/net/wireless/ath/ath10k/coredump.h:27: warning: Excess enum value 'ATH10K_FW_CRASH_DUMP_REGDUMP' description in 'ath10k_fw_crash_dump_type' Fix these issues with the enum ath10k_fw_crash_dump_type kernel-doc. No functional changes, compile tested only. Signed-off-by: Jeff Johnson Acked-by: Randy Dunlap Signed-off-by: Kalle Valo Link: https://msgid.link/20240118-ath10k-kerneldoc-v1-5-99c7e8d95aad@quicinc.com --- drivers/net/wireless/ath/ath10k/coredump.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/coredump.h b/drivers/net/wireless/ath/ath10k/coredump.h index e5ef0352e319..8d274e0f374b 100644 --- a/drivers/net/wireless/ath/ath10k/coredump.h +++ b/drivers/net/wireless/ath/ath10k/coredump.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _COREDUMP_H_ @@ -13,7 +13,11 @@ /** * enum ath10k_fw_crash_dump_type - types of data in the dump file - * @ATH10K_FW_CRASH_DUMP_REGDUMP: Register crash dump in binary format + * @ATH10K_FW_CRASH_DUMP_REGISTERS: Register crash dump in binary format + * @ATH10K_FW_CRASH_DUMP_CE_DATA: Copy Engine crash dump data + * @ATH10K_FW_CRASH_DUMP_RAM_DATA: RAM crash dump data, contains multiple + * struct ath10k_dump_ram_data_hdr + * @ATH10K_FW_CRASH_DUMP_MAX: Maximum enumeration */ enum ath10k_fw_crash_dump_type { ATH10K_FW_CRASH_DUMP_REGISTERS = 0, From 67a48d937fac917947540c9f89630d472cd61fcb Mon Sep 17 00:00:00 2001 From: Sriram R Date: Wed, 17 Jan 2024 11:56:28 +0530 Subject: [PATCH 066/378] wifi: ath12k: Fix issues in channel list update Currently, the logic used to select the 6 GHz band is incorrect, which may cause 6 GHz supported channels to not be updated properly. This is because the 6 GHz Max frequency supported by the driver is being compared to the Max frequency supported on the board. If in some cases, the 6 GHz Max frequency supported on the board is less than the defined 6 GHz Max frequency, all 6 GHz channels are disabled. To address this, compare the max frequency supported by the board to the defined 6 GHz Minimum frequency by the driver. Similarly, when a dual mac card supports both 6 GHz and 5 GHz radios, if the 5 GHz radio gets enumerated first before 6 GHz, the checks in ath12k_mac_setup_channels_rates() can cause the 5 GHz channels which were enabled earlier to get disabled when the 6 GHz channel list is updated. This is because the Min 6 GHz frequency defined in the driver is 5945 MHz, which should be 5925 MHz since channel 2 is not considered currently, but the firmware can pass 5925 MHz as the minimum. Hence, update the Min frequency supported by the driver to 5925 MHz. In addition, ensure that the channel list update to firmware updates only the channels that the current radio (ar) supports rather than considering the wiphy support. This would be required when multiple pdevs are supported in a wiphy and they support different ranges of frequencies or bands as in single wiphy support. Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00188-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Sriram R Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240117062628.8260-1-quic_srirrama@quicinc.com --- drivers/net/wireless/ath/ath12k/core.h | 2 +- drivers/net/wireless/ath/ath12k/mac.c | 2 +- drivers/net/wireless/ath/ath12k/reg.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index a56fc74366b4..5c6c1e2eddb6 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -425,7 +425,7 @@ struct ath12k_sta { }; #define ATH12K_MIN_5G_FREQ 4150 -#define ATH12K_MIN_6G_FREQ 5945 +#define ATH12K_MIN_6G_FREQ 5925 #define ATH12K_MAX_6G_FREQ 7115 #define ATH12K_NUM_CHANS 100 #define ATH12K_MAX_5G_CHAN 173 diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index fe600dbef875..a27480a69b27 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7394,7 +7394,7 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, } if (supported_bands & WMI_HOST_WLAN_5G_CAP) { - if (reg_cap->high_5ghz_chan >= ATH12K_MAX_6G_FREQ) { + if (reg_cap->high_5ghz_chan >= ATH12K_MIN_6G_FREQ) { channels = kmemdup(ath12k_6ghz_channels, sizeof(ath12k_6ghz_channels), GFP_KERNEL); if (!channels) { diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index a164f15d1e2b..f308e9a6ed55 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -104,7 +104,7 @@ int ath12k_reg_update_chan_list(struct ath12k *ar) bands = hw->wiphy->bands; for (band = 0; band < NUM_NL80211_BANDS; band++) { - if (!bands[band]) + if (!(ar->mac.sbands[band].channels && bands[band])) continue; for (i = 0; i < bands[band]->n_channels; i++) { @@ -130,7 +130,7 @@ int ath12k_reg_update_chan_list(struct ath12k *ar) ch = arg->channel; for (band = 0; band < NUM_NL80211_BANDS; band++) { - if (!bands[band]) + if (!(ar->mac.sbands[band].channels && bands[band])) continue; for (i = 0; i < bands[band]->n_channels; i++) { From dbd73acb22d85873ecca51fdb33a0e50d11e8021 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 23 Jan 2024 09:52:01 +0800 Subject: [PATCH 067/378] wifi: ath11k: enable 36 bit mask for stream DMA Currently 32 bit DMA mask is used, telling kernel to get us an DMA address under 4GB when mapping a buffer. This results in a very high CPU overhead in the case where IOMMU is disabled and more than 4GB system memory is installed. The reason is, with more than 4GB memory installed, kernel is likely to allocate a buffer whose physical address is above 4GB. While with IOMMU disabled, kernel has to involve SWIOTLB to map/unmap that buffer, which consumes lots of CPU cycles. We did hit an issue caused by the reason mentioned above: in a system that disables IOMMU and gets 8GB memory installed, a total of 40.5% CPU usage is observed in throughput test. CPU profiling shows nearly 60% of CPU cycles are consumed by SWIOTLB. By enabling 36 bit DMA mask, we can bypass SWIOTLB for any buffer whose physical address is below 64GB. There are two types of DMA mask within struct device, named dma_mask and coherent_dma_mask. Here we only enable 36 bit for dma_mask, because firmware crashes if coherent_dma_mask is also enabled, due to some unknown hardware limitations. This is acceptable because coherent_dma_mask is used for mapping a consistent DMA buffer, which generally does not happen in a hot path. With this change, the total CPU usage mentioned in above issue drops to 18.9%. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240123015201.28939-1-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mhi.c | 4 ++-- drivers/net/wireless/ath/ath11k/pci.c | 19 +++++++++++++++---- drivers/net/wireless/ath/ath11k/pci.h | 3 ++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index 6835c14b82cc..e6e69bb91103 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -423,7 +423,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci) goto free_controller; } else { mhi_ctrl->iova_start = 0; - mhi_ctrl->iova_stop = 0xFFFFFFFF; + mhi_ctrl->iova_stop = ab_pci->dma_mask; } mhi_ctrl->rddm_size = RDDM_DUMP_SIZE; diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 09e65c5e55c4..d667d5e06a66 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -18,7 +18,8 @@ #include "qmi.h" #define ATH11K_PCI_BAR_NUM 0 -#define ATH11K_PCI_DMA_MASK 32 +#define ATH11K_PCI_DMA_MASK 36 +#define ATH11K_PCI_COHERENT_DMA_MASK 32 #define TCSR_SOC_HW_VERSION 0x0224 #define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(11, 8) @@ -526,14 +527,24 @@ static int ath11k_pci_claim(struct ath11k_pci *ab_pci, struct pci_dev *pdev) goto disable_device; } - ret = dma_set_mask_and_coherent(&pdev->dev, - DMA_BIT_MASK(ATH11K_PCI_DMA_MASK)); + ret = dma_set_mask(&pdev->dev, + DMA_BIT_MASK(ATH11K_PCI_DMA_MASK)); if (ret) { ath11k_err(ab, "failed to set pci dma mask to %d: %d\n", ATH11K_PCI_DMA_MASK, ret); goto release_region; } + ab_pci->dma_mask = DMA_BIT_MASK(ATH11K_PCI_DMA_MASK); + + ret = dma_set_coherent_mask(&pdev->dev, + DMA_BIT_MASK(ATH11K_PCI_COHERENT_DMA_MASK)); + if (ret) { + ath11k_err(ab, "failed to set pci coherent dma mask to %d: %d\n", + ATH11K_PCI_COHERENT_DMA_MASK, ret); + goto release_region; + } + pci_set_master(pdev); ab->mem_len = pci_resource_len(pdev, ATH11K_PCI_BAR_NUM); diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h index e9a01f344ec6..6be73333d90b 100644 --- a/drivers/net/wireless/ath/ath11k/pci.h +++ b/drivers/net/wireless/ath/ath11k/pci.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022,2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH11K_PCI_H #define _ATH11K_PCI_H @@ -72,6 +72,7 @@ struct ath11k_pci { /* enum ath11k_pci_flags */ unsigned long flags; u16 link_ctl; + u64 dma_mask; }; static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab) From 171203f0c4093dfe7e73ceb6c38033595f329f56 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 23 Jan 2024 10:56:57 +0800 Subject: [PATCH 068/378] wifi: ath11k: remove invalid peer create logic In ath11k_mac_op_assign_vif_chanctx(), there is a logic to create peer using ar->mac_addr for a STA vdev. This is invalid because a STA vdev should have a peer created using AP's MAC address. Besides, if we run into that logic, it means a peer has already been created earlier, we should not create it again. So remove it. This is found during code review. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240123025700.2929-2-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index bf4a97967177..33ab66afb97f 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -8108,7 +8108,6 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ath11k_base *ab = ar->ab; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); int ret; - struct peer_create_params param; struct cur_regulatory_info *reg_info; enum ieee80211_ap_reg_power power_type; @@ -8151,21 +8150,6 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, goto out; } - if (ab->hw_params.vdev_start_delay && - arvif->vdev_type != WMI_VDEV_TYPE_AP && - arvif->vdev_type != WMI_VDEV_TYPE_MONITOR) { - param.vdev_id = arvif->vdev_id; - param.peer_type = WMI_PEER_TYPE_DEFAULT; - param.peer_addr = ar->mac_addr; - - ret = ath11k_peer_create(ar, arvif, NULL, ¶m); - if (ret) { - ath11k_warn(ab, "failed to create peer after vdev start delay: %d", - ret); - goto out; - } - } - if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ret = ath11k_mac_monitor_start(ar); if (ret) { From 629642fa8b25b8dfecefc9e2177a44c009858da7 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 23 Jan 2024 10:56:58 +0800 Subject: [PATCH 069/378] wifi: ath11k: rename ath11k_start_vdev_delay() Rename ath11k_start_vdev_delay() as ath11k_mac_start_vdev_delay() to follow naming convention. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240123025700.2929-3-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 33ab66afb97f..9d041065f81c 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -255,8 +255,8 @@ static const u32 ath11k_smps_map[] = { [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, }; -static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); +static int ath11k_mac_start_vdev_delay(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy) { @@ -4987,7 +4987,7 @@ static int ath11k_mac_station_add(struct ath11k *ar, if (ab->hw_params.vdev_start_delay && !arvif->is_started && arvif->vdev_type != WMI_VDEV_TYPE_AP) { - ret = ath11k_start_vdev_delay(ar->hw, vif); + ret = ath11k_mac_start_vdev_delay(ar->hw, vif); if (ret) { ath11k_warn(ab, "failed to delay vdev start: %d\n", ret); goto free_tx_stats; @@ -7581,8 +7581,8 @@ static void ath11k_mac_op_change_chanctx(struct ieee80211_hw *hw, mutex_unlock(&ar->conf_mutex); } -static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int ath11k_mac_start_vdev_delay(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct ath11k *ar = hw->priv; struct ath11k_base *ab = ar->ab; From ce59902e56ea0477ad9bef0067d0e47b6c4d707d Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 23 Jan 2024 10:56:59 +0800 Subject: [PATCH 070/378] wifi: ath11k: avoid forward declaration of ath11k_mac_start_vdev_delay() Currently ath11k_mac_start_vdev_delay() needs a forward declaration because it is defined after where it is called. Avoid this by re-arranging ath11k_mac_station_add() and ath11k_mac_op_sta_state(). No functional changes. Compile tested only. Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240123025700.2929-4-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 459 +++++++++++++------------- 1 file changed, 228 insertions(+), 231 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 9d041065f81c..324bbb377eac 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -255,9 +255,6 @@ static const u32 ath11k_smps_map[] = { [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, }; -static int ath11k_mac_start_vdev_delay(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); - enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy) { enum nl80211_he_ru_alloc ret; @@ -4917,100 +4914,6 @@ static void ath11k_mac_dec_num_stations(struct ath11k_vif *arvif, ar->num_stations--; } -static int ath11k_mac_station_add(struct ath11k *ar, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct ath11k_base *ab = ar->ab; - struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); - struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); - struct peer_create_params peer_param; - int ret; - - lockdep_assert_held(&ar->conf_mutex); - - ret = ath11k_mac_inc_num_stations(arvif, sta); - if (ret) { - ath11k_warn(ab, "refusing to associate station: too many connected already (%d)\n", - ar->max_num_stations); - goto exit; - } - - arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); - if (!arsta->rx_stats) { - ret = -ENOMEM; - goto dec_num_station; - } - - peer_param.vdev_id = arvif->vdev_id; - peer_param.peer_addr = sta->addr; - peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; - - ret = ath11k_peer_create(ar, arvif, sta, &peer_param); - if (ret) { - ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - goto free_rx_stats; - } - - ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - - if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) { - arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL); - if (!arsta->tx_stats) { - ret = -ENOMEM; - goto free_peer; - } - } - - if (ieee80211_vif_is_mesh(vif)) { - ath11k_dbg(ab, ATH11K_DBG_MAC, - "setting USE_4ADDR for mesh STA %pM\n", sta->addr); - ret = ath11k_wmi_set_peer_param(ar, sta->addr, - arvif->vdev_id, - WMI_PEER_USE_4ADDR, 1); - if (ret) { - ath11k_warn(ab, "failed to set mesh STA %pM 4addr capability: %d\n", - sta->addr, ret); - goto free_tx_stats; - } - } - - ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); - if (ret) { - ath11k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", - sta->addr, arvif->vdev_id, ret); - goto free_tx_stats; - } - - if (ab->hw_params.vdev_start_delay && - !arvif->is_started && - arvif->vdev_type != WMI_VDEV_TYPE_AP) { - ret = ath11k_mac_start_vdev_delay(ar->hw, vif); - if (ret) { - ath11k_warn(ab, "failed to delay vdev start: %d\n", ret); - goto free_tx_stats; - } - } - - ewma_avg_rssi_init(&arsta->avg_rssi); - return 0; - -free_tx_stats: - kfree(arsta->tx_stats); - arsta->tx_stats = NULL; -free_peer: - ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); -free_rx_stats: - kfree(arsta->rx_stats); - arsta->rx_stats = NULL; -dec_num_station: - ath11k_mac_dec_num_stations(arvif, sta); -exit: - return ret; -} - static u32 ath11k_mac_ieee80211_sta_bw_to_wmi(struct ath11k *ar, struct ieee80211_sta *sta) { @@ -5039,140 +4942,6 @@ static u32 ath11k_mac_ieee80211_sta_bw_to_wmi(struct ath11k *ar, return bw; } -static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - enum ieee80211_sta_state old_state, - enum ieee80211_sta_state new_state) -{ - struct ath11k *ar = hw->priv; - struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); - struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); - struct ath11k_peer *peer; - int ret = 0; - - /* cancel must be done outside the mutex to avoid deadlock */ - if ((old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST)) { - cancel_work_sync(&arsta->update_wk); - cancel_work_sync(&arsta->set_4addr_wk); - } - - mutex_lock(&ar->conf_mutex); - - if (old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) { - memset(arsta, 0, sizeof(*arsta)); - arsta->arvif = arvif; - arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED; - INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk); - INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk); - - ret = ath11k_mac_station_add(ar, vif, sta); - if (ret) - ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - } else if ((old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST)) { - bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay && - vif->type == NL80211_IFTYPE_STATION; - - ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); - - if (!skip_peer_delete) { - ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); - if (ret) - ath11k_warn(ar->ab, - "Failed to delete peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - else - ath11k_dbg(ar->ab, - ATH11K_DBG_MAC, - "Removed peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - } - - ath11k_mac_dec_num_stations(arvif, sta); - mutex_lock(&ar->ab->tbl_mtx_lock); - spin_lock_bh(&ar->ab->base_lock); - peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); - if (skip_peer_delete && peer) { - peer->sta = NULL; - } else if (peer && peer->sta == sta) { - ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", - vif->addr, arvif->vdev_id); - ath11k_peer_rhash_delete(ar->ab, peer); - peer->sta = NULL; - list_del(&peer->list); - kfree(peer); - ar->num_peers--; - } - spin_unlock_bh(&ar->ab->base_lock); - mutex_unlock(&ar->ab->tbl_mtx_lock); - - kfree(arsta->tx_stats); - arsta->tx_stats = NULL; - - kfree(arsta->rx_stats); - arsta->rx_stats = NULL; - } else if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_ASSOC && - (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_MESH_POINT || - vif->type == NL80211_IFTYPE_ADHOC)) { - ret = ath11k_station_assoc(ar, vif, sta, false); - if (ret) - ath11k_warn(ar->ab, "Failed to associate station: %pM\n", - sta->addr); - - spin_lock_bh(&ar->data_lock); - /* Set arsta bw and prev bw */ - arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); - arsta->bw_prev = arsta->bw; - spin_unlock_bh(&ar->data_lock); - } else if (old_state == IEEE80211_STA_ASSOC && - new_state == IEEE80211_STA_AUTHORIZED) { - spin_lock_bh(&ar->ab->base_lock); - - peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); - if (peer) - peer->is_authorized = true; - - spin_unlock_bh(&ar->ab->base_lock); - - if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) { - ret = ath11k_wmi_set_peer_param(ar, sta->addr, - arvif->vdev_id, - WMI_PEER_AUTHORIZE, - 1); - if (ret) - ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", - sta->addr, arvif->vdev_id, ret); - } - } else if (old_state == IEEE80211_STA_AUTHORIZED && - new_state == IEEE80211_STA_ASSOC) { - spin_lock_bh(&ar->ab->base_lock); - - peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); - if (peer) - peer->is_authorized = false; - - spin_unlock_bh(&ar->ab->base_lock); - } else if (old_state == IEEE80211_STA_ASSOC && - new_state == IEEE80211_STA_AUTH && - (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_MESH_POINT || - vif->type == NL80211_IFTYPE_ADHOC)) { - ret = ath11k_station_disassoc(ar, vif, sta); - if (ret) - ath11k_warn(ar->ab, "Failed to disassociate station: %pM\n", - sta->addr); - } - - mutex_unlock(&ar->conf_mutex); - return ret; -} - static int ath11k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) @@ -9610,6 +9379,234 @@ static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw, return 0; } +static int ath11k_mac_station_add(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); + struct peer_create_params peer_param; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath11k_mac_inc_num_stations(arvif, sta); + if (ret) { + ath11k_warn(ab, "refusing to associate station: too many connected already (%d)\n", + ar->max_num_stations); + goto exit; + } + + arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); + if (!arsta->rx_stats) { + ret = -ENOMEM; + goto dec_num_station; + } + + peer_param.vdev_id = arvif->vdev_id; + peer_param.peer_addr = sta->addr; + peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; + + ret = ath11k_peer_create(ar, arvif, sta, &peer_param); + if (ret) { + ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + goto free_rx_stats; + } + + ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + + if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) { + arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL); + if (!arsta->tx_stats) { + ret = -ENOMEM; + goto free_peer; + } + } + + if (ieee80211_vif_is_mesh(vif)) { + ath11k_dbg(ab, ATH11K_DBG_MAC, + "setting USE_4ADDR for mesh STA %pM\n", sta->addr); + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_USE_4ADDR, 1); + if (ret) { + ath11k_warn(ab, "failed to set mesh STA %pM 4addr capability: %d\n", + sta->addr, ret); + goto free_tx_stats; + } + } + + ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); + if (ret) { + ath11k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", + sta->addr, arvif->vdev_id, ret); + goto free_tx_stats; + } + + if (ab->hw_params.vdev_start_delay && + !arvif->is_started && + arvif->vdev_type != WMI_VDEV_TYPE_AP) { + ret = ath11k_mac_start_vdev_delay(ar->hw, vif); + if (ret) { + ath11k_warn(ab, "failed to delay vdev start: %d\n", ret); + goto free_tx_stats; + } + } + + ewma_avg_rssi_init(&arsta->avg_rssi); + return 0; + +free_tx_stats: + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; +free_peer: + ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); +free_rx_stats: + kfree(arsta->rx_stats); + arsta->rx_stats = NULL; +dec_num_station: + ath11k_mac_dec_num_stations(arvif, sta); +exit: + return ret; +} + +static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); + struct ath11k_peer *peer; + int ret = 0; + + /* cancel must be done outside the mutex to avoid deadlock */ + if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) { + cancel_work_sync(&arsta->update_wk); + cancel_work_sync(&arsta->set_4addr_wk); + } + + mutex_lock(&ar->conf_mutex); + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + memset(arsta, 0, sizeof(*arsta)); + arsta->arvif = arvif; + arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED; + INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk); + INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk); + + ret = ath11k_mac_station_add(ar, vif, sta); + if (ret) + ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + } else if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) { + bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay && + vif->type == NL80211_IFTYPE_STATION; + + ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); + + if (!skip_peer_delete) { + ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); + if (ret) + ath11k_warn(ar->ab, + "Failed to delete peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath11k_dbg(ar->ab, + ATH11K_DBG_MAC, + "Removed peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + } + + ath11k_mac_dec_num_stations(arvif, sta); + mutex_lock(&ar->ab->tbl_mtx_lock); + spin_lock_bh(&ar->ab->base_lock); + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); + if (skip_peer_delete && peer) { + peer->sta = NULL; + } else if (peer && peer->sta == sta) { + ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", + vif->addr, arvif->vdev_id); + ath11k_peer_rhash_delete(ar->ab, peer); + peer->sta = NULL; + list_del(&peer->list); + kfree(peer); + ar->num_peers--; + } + spin_unlock_bh(&ar->ab->base_lock); + mutex_unlock(&ar->ab->tbl_mtx_lock); + + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; + + kfree(arsta->rx_stats); + arsta->rx_stats = NULL; + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || + vif->type == NL80211_IFTYPE_ADHOC)) { + ret = ath11k_station_assoc(ar, vif, sta, false); + if (ret) + ath11k_warn(ar->ab, "Failed to associate station: %pM\n", + sta->addr); + + spin_lock_bh(&ar->data_lock); + /* Set arsta bw and prev bw */ + arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); + arsta->bw_prev = arsta->bw; + spin_unlock_bh(&ar->data_lock); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) { + spin_lock_bh(&ar->ab->base_lock); + + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); + if (peer) + peer->is_authorized = true; + + spin_unlock_bh(&ar->ab->base_lock); + + if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) { + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_AUTHORIZE, + 1); + if (ret) + ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", + sta->addr, arvif->vdev_id, ret); + } + } else if (old_state == IEEE80211_STA_AUTHORIZED && + new_state == IEEE80211_STA_ASSOC) { + spin_lock_bh(&ar->ab->base_lock); + + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); + if (peer) + peer->is_authorized = false; + + spin_unlock_bh(&ar->ab->base_lock); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || + vif->type == NL80211_IFTYPE_ADHOC)) { + ret = ath11k_station_disassoc(ar, vif, sta); + if (ret) + ath11k_warn(ar->ab, "Failed to disassociate station: %pM\n", + sta->addr); + } + + mutex_unlock(&ar->conf_mutex); + return ret; +} + static const struct ieee80211_ops ath11k_ops = { .tx = ath11k_mac_op_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, From 9d5f28c1366f48efae7b1df0f622285519e74dce Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 23 Jan 2024 10:57:00 +0800 Subject: [PATCH 071/378] wifi: ath11k: fix connection failure due to unexpected peer delete Currently ath11k_mac_op_unassign_vif_chanctx() deletes peer but ath11k_mac_op_assign_vif_chanctx() doesn't create it. This results in connection failure if MAC80211 calls drv_unassign_vif_chanctx() and drv_assign_vif_chanctx() during AUTH and ASSOC, see below log: [ 102.372431] wlan0: authenticated [ 102.372585] ath11k_pci 0000:01:00.0: wlan0: disabling HT/VHT/HE as WMM/QoS is not supported by the AP [ 102.372593] ath11k_pci 0000:01:00.0: mac chanctx unassign ptr ffff895084638598 vdev_id 0 [ 102.372808] ath11k_pci 0000:01:00.0: WMI vdev stop id 0x0 [ 102.383114] ath11k_pci 0000:01:00.0: vdev stopped for vdev id 0 [ 102.384689] ath11k_pci 0000:01:00.0: WMI peer delete vdev_id 0 peer_addr 20:e5:2a:21:c4:51 [ 102.396676] ath11k_pci 0000:01:00.0: htt peer unmap vdev 0 peer 20:e5:2a:21:c4:51 id 3 [ 102.396711] ath11k_pci 0000:01:00.0: peer delete resp for vdev id 0 addr 20:e5:2a:21:c4:51 [ 102.396722] ath11k_pci 0000:01:00.0: mac removed peer 20:e5:2a:21:c4:51 vdev 0 after vdev stop [ 102.396780] ath11k_pci 0000:01:00.0: mac chanctx assign ptr ffff895084639c18 vdev_id 0 [ 102.400628] wlan0: associate with 20:e5:2a:21:c4:51 (try 1/3) [ 102.508864] wlan0: associate with 20:e5:2a:21:c4:51 (try 2/3) [ 102.612815] wlan0: associate with 20:e5:2a:21:c4:51 (try 3/3) [ 102.720846] wlan0: association with 20:e5:2a:21:c4:51 timed out The peer delete logic in ath11k_mac_op_unassign_vif_chanctx() is introduced by commit b4a0f54156ac ("ath11k: move peer delete after vdev stop of station for QCA6390 and WCN6855") to fix firmware crash issue caused by unexpected vdev stop/peer delete sequence. Actually for a STA interface peer should be deleted in ath11k_mac_op_sta_state() when STA's state changes from IEEE80211_STA_NONE to IEEE80211_STA_NOTEXIST, which also coincides with current peer creation design that peer is created during IEEE80211_STA_NOTEXIST -> IEEE80211_STA_NONE transition. So move peer delete back to ath11k_mac_op_sta_state(), also stop vdev before deleting peer to fix the firmware crash issue mentioned there. In this way the connection failure mentioned here is also fixed. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Fixes: b4a0f54156ac ("ath11k: move peer delete after vdev stop of station for QCA6390 and WCN6855") Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240123025700.2929-5-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 139 ++++++++++++++++---------- 1 file changed, 85 insertions(+), 54 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 324bbb377eac..4a6ab45e9386 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -7397,6 +7397,30 @@ static int ath11k_mac_start_vdev_delay(struct ieee80211_hw *hw, return 0; } +static int ath11k_mac_stop_vdev_early(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + int ret; + + if (WARN_ON(!arvif->is_started)) + return -EBUSY; + + ret = ath11k_mac_vdev_stop(arvif); + if (ret) { + ath11k_warn(ab, "failed to stop vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + arvif->is_started = false; + + /* TODO: Setup ps and cts/rts protection */ + return 0; +} + static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt) { switch (txpwr_intrprt) { @@ -7931,15 +7955,17 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, goto out; } - ret = ath11k_mac_vdev_start(arvif, ctx); - if (ret) { - ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", - arvif->vdev_id, vif->addr, - ctx->def.chan->center_freq, ret); - goto out; - } + if (!arvif->is_started) { + ret = ath11k_mac_vdev_start(arvif, ctx); + if (ret) { + ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", + arvif->vdev_id, vif->addr, + ctx->def.chan->center_freq, ret); + goto out; + } - arvif->is_started = true; + arvif->is_started = true; + } if (arvif->vdev_type != WMI_VDEV_TYPE_MONITOR && test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) { @@ -7979,8 +8005,6 @@ ath11k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, "chanctx unassign ptr %p vdev_id %i\n", ctx, arvif->vdev_id); - WARN_ON(!arvif->is_started); - if (ab->hw_params.vdev_start_delay && arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { spin_lock_bh(&ab->base_lock); @@ -8004,24 +8028,13 @@ ath11k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, return; } - ret = ath11k_mac_vdev_stop(arvif); - if (ret) - ath11k_warn(ab, "failed to stop vdev %i: %d\n", - arvif->vdev_id, ret); - - arvif->is_started = false; - - if (ab->hw_params.vdev_start_delay && - arvif->vdev_type == WMI_VDEV_TYPE_STA) { - ret = ath11k_peer_delete(ar, arvif->vdev_id, arvif->bssid); + if (arvif->is_started) { + ret = ath11k_mac_vdev_stop(arvif); if (ret) - ath11k_warn(ar->ab, - "failed to delete peer %pM for vdev %d: %d\n", - arvif->bssid, arvif->vdev_id, ret); - else - ath11k_dbg(ar->ab, ATH11K_DBG_MAC, - "removed peer %pM vdev %d after vdev stop\n", - arvif->bssid, arvif->vdev_id); + ath11k_warn(ab, "failed to stop vdev %i: %d\n", + arvif->vdev_id, ret); + + arvif->is_started = false; } if (ab->hw_params.vdev_start_delay && @@ -9473,6 +9486,46 @@ static int ath11k_mac_station_add(struct ath11k *ar, return ret; } +static int ath11k_mac_station_remove(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); + int ret; + + if (ab->hw_params.vdev_start_delay && + arvif->is_started && + arvif->vdev_type != WMI_VDEV_TYPE_AP) { + ret = ath11k_mac_stop_vdev_early(ar->hw, vif); + if (ret) { + ath11k_warn(ab, "failed to do early vdev stop: %d\n", ret); + return ret; + } + } + + ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); + + ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); + if (ret) + ath11k_warn(ab, "Failed to delete peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath11k_dbg(ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + + ath11k_mac_dec_num_stations(arvif, sta); + + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; + + kfree(arsta->rx_stats); + arsta->rx_stats = NULL; + + return ret; +} + static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -9508,31 +9561,15 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, sta->addr, arvif->vdev_id); } else if ((old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST)) { - bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay && - vif->type == NL80211_IFTYPE_STATION; + ret = ath11k_mac_station_remove(ar, vif, sta); + if (ret) + ath11k_warn(ar->ab, "Failed to remove station: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); - ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); - - if (!skip_peer_delete) { - ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); - if (ret) - ath11k_warn(ar->ab, - "Failed to delete peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - else - ath11k_dbg(ar->ab, - ATH11K_DBG_MAC, - "Removed peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - } - - ath11k_mac_dec_num_stations(arvif, sta); mutex_lock(&ar->ab->tbl_mtx_lock); spin_lock_bh(&ar->ab->base_lock); peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); - if (skip_peer_delete && peer) { - peer->sta = NULL; - } else if (peer && peer->sta == sta) { + if (peer && peer->sta == sta) { ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", vif->addr, arvif->vdev_id); ath11k_peer_rhash_delete(ar->ab, peer); @@ -9543,12 +9580,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, } spin_unlock_bh(&ar->ab->base_lock); mutex_unlock(&ar->ab->tbl_mtx_lock); - - kfree(arsta->tx_stats); - arsta->tx_stats = NULL; - - kfree(arsta->rx_stats); - arsta->rx_stats = NULL; } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC && (vif->type == NL80211_IFTYPE_AP || From 7f78840cf4d4ac9ab36ddf574a025a45835375d0 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Wed, 10 Jan 2024 08:42:06 +0300 Subject: [PATCH 072/378] wifi: wireless: avoid strlen() in cfg80211_michael_mic_failure() In 'cfg80211_michael_mic_failure()', avoid extra call to 'strlen()' by using the value returned by 'sprintf()'. Compile tested only. Signed-off-by: Dmitry Antipov Link: https://msgid.link/20240110054246.371651-1-dmantipov@yandex.ru Signed-off-by: Johannes Berg --- net/wireless/mlme.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index f635a8b6ca2e..43ba7891e2a3 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -241,12 +241,12 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, char *buf = kmalloc(128, gfp); if (buf) { - sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" - "keyid=%d %scast addr=%pM)", key_id, - key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", - addr); memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = strlen(buf); + wrqu.data.length = + sprintf(buf, "MLME-MICHAELMICFAILURE." + "indication(keyid=%d %scast addr=%pM)", + key_id, key_type == NL80211_KEYTYPE_GROUP + ? "broad" : "uni", addr); wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); kfree(buf); } From 4d1d6b3f45999b1ddde53831d639a67e2655285f Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 2 Jan 2024 21:35:32 +0200 Subject: [PATCH 073/378] wifi: cfg80211: add RNR with reporting AP information If the reporting AP is part of the same MLD, then an entry in the RNR is required in order to discover it again from the BSS generated from the per-STA profile in the Multi-Link Probe Response. We need this because we do not have a direct concept of an MLD AP and just do the lookup from one to the other on the fly if needed. As such, we need to ensure that this lookup will work both ways. Fixes: 2481b5da9c6b ("wifi: cfg80211: handle BSS data contained in ML probe responses") Signed-off-by: Benjamin Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.4cb3dbb1d84f.I7c74edec83c5d7598cdd578929fd0876d67aef7f@changeid [roll in off-by-one fix and test updates from Benjamin] Signed-off-by: Johannes Berg --- net/wireless/scan.c | 135 ++++++++++++++++++++++++++++++++++++-- net/wireless/tests/scan.c | 36 +++++++++- 2 files changed, 163 insertions(+), 8 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 2249b1a89d1c..01618c4059f4 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -2617,6 +2617,103 @@ cfg80211_tbtt_info_for_mld_ap(const u8 *ie, size_t ielen, u8 mld_id, u8 link_id, return 0; } +static struct element * +cfg80211_gen_reporter_rnr(struct cfg80211_bss *source_bss, bool is_mbssid, + bool same_mld, u8 link_id, u8 bss_change_count, + gfp_t gfp) +{ + const struct cfg80211_bss_ies *ies; + struct ieee80211_neighbor_ap_info ap_info; + struct ieee80211_tbtt_info_ge_11 tbtt_info; + u32 short_ssid; + const struct element *elem; + struct element *res; + + /* + * We only generate the RNR to permit ML lookups. For that we do not + * need an entry for the corresponding transmitting BSS, lets just skip + * it even though it would be easy to add. + */ + if (!same_mld) + return NULL; + + /* We could use tx_data->ies if we change cfg80211_calc_short_ssid */ + rcu_read_lock(); + ies = rcu_dereference(source_bss->ies); + + ap_info.tbtt_info_len = offsetofend(typeof(tbtt_info), mld_params); + ap_info.tbtt_info_hdr = + u8_encode_bits(IEEE80211_TBTT_INFO_TYPE_TBTT, + IEEE80211_AP_INFO_TBTT_HDR_TYPE) | + u8_encode_bits(0, IEEE80211_AP_INFO_TBTT_HDR_COUNT); + + ap_info.channel = ieee80211_frequency_to_channel(source_bss->channel->center_freq); + + /* operating class */ + elem = cfg80211_find_elem(WLAN_EID_SUPPORTED_REGULATORY_CLASSES, + ies->data, ies->len); + if (elem && elem->datalen >= 1) { + ap_info.op_class = elem->data[0]; + } else { + struct cfg80211_chan_def chandef; + + /* The AP is not providing us with anything to work with. So + * make up a somewhat reasonable operating class, but don't + * bother with it too much as no one will ever use the + * information. + */ + cfg80211_chandef_create(&chandef, source_bss->channel, + NL80211_CHAN_NO_HT); + + if (!ieee80211_chandef_to_operating_class(&chandef, + &ap_info.op_class)) + goto out_unlock; + } + + /* Just set TBTT offset and PSD 20 to invalid/unknown */ + tbtt_info.tbtt_offset = 255; + tbtt_info.psd_20 = IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED; + + memcpy(tbtt_info.bssid, source_bss->bssid, ETH_ALEN); + if (cfg80211_calc_short_ssid(ies, &elem, &short_ssid)) + goto out_unlock; + + rcu_read_unlock(); + + tbtt_info.short_ssid = cpu_to_le32(short_ssid); + + tbtt_info.bss_params = IEEE80211_RNR_TBTT_PARAMS_SAME_SSID; + + if (is_mbssid) { + tbtt_info.bss_params |= IEEE80211_RNR_TBTT_PARAMS_MULTI_BSSID; + tbtt_info.bss_params |= IEEE80211_RNR_TBTT_PARAMS_TRANSMITTED_BSSID; + } + + tbtt_info.mld_params.mld_id = 0; + tbtt_info.mld_params.params = + le16_encode_bits(link_id, IEEE80211_RNR_MLD_PARAMS_LINK_ID) | + le16_encode_bits(bss_change_count, + IEEE80211_RNR_MLD_PARAMS_BSS_CHANGE_COUNT); + + res = kzalloc(struct_size(res, data, + sizeof(ap_info) + ap_info.tbtt_info_len), + gfp); + if (!res) + return NULL; + + /* Copy the data */ + res->id = WLAN_EID_REDUCED_NEIGHBOR_REPORT; + res->datalen = sizeof(ap_info) + ap_info.tbtt_info_len; + memcpy(res->data, &ap_info, sizeof(ap_info)); + memcpy(res->data + sizeof(ap_info), &tbtt_info, ap_info.tbtt_info_len); + + return res; + +out_unlock: + rcu_read_unlock(); + return NULL; +} + static void cfg80211_parse_ml_elem_sta_data(struct wiphy *wiphy, struct cfg80211_inform_single_bss_data *tx_data, @@ -2630,13 +2727,14 @@ cfg80211_parse_ml_elem_sta_data(struct wiphy *wiphy, .source_bss = source_bss, .bss_source = BSS_SOURCE_STA_PROFILE, }; + struct element *reporter_rnr = NULL; struct ieee80211_multi_link_elem *ml_elem; struct cfg80211_mle *mle; u16 control; u8 ml_common_len; - u8 *new_ie; + u8 *new_ie = NULL; struct cfg80211_bss *bss; - int mld_id; + u8 mld_id, reporter_link_id, bss_change_count; u16 seen_links = 0; const u8 *pos; u8 i; @@ -2658,8 +2756,14 @@ cfg80211_parse_ml_elem_sta_data(struct wiphy *wiphy, ml_common_len = ml_elem->variable[0]; - /* length + MLD MAC address + link ID info + BSS Params Change Count */ - pos = ml_elem->variable + 1 + 6 + 1 + 1; + /* length + MLD MAC address */ + pos = ml_elem->variable + 1 + 6; + + reporter_link_id = pos[0]; + pos += 1; + + bss_change_count = pos[0]; + pos += 1; if (u16_get_bits(control, IEEE80211_MLC_BASIC_PRES_MED_SYNC_DELAY)) pos += 2; @@ -2690,10 +2794,21 @@ cfg80211_parse_ml_elem_sta_data(struct wiphy *wiphy, if (!mle) return; + /* No point in doing anything if there is no per-STA profile */ + if (!mle->sta_prof[0]) + goto out; + new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp); if (!new_ie) goto out; + reporter_rnr = cfg80211_gen_reporter_rnr(source_bss, + u16_get_bits(control, + IEEE80211_MLC_BASIC_PRES_MLD_ID), + mld_id == 0, reporter_link_id, + bss_change_count, + gfp); + for (i = 0; i < ARRAY_SIZE(mle->sta_prof) && mle->sta_prof[i]; i++) { const struct ieee80211_neighbor_ap_info *ap_info; enum nl80211_band band; @@ -2803,7 +2918,16 @@ cfg80211_parse_ml_elem_sta_data(struct wiphy *wiphy, data.ielen += sizeof(*ml_elem) + ml_common_len; - /* TODO: Add an RNR containing only the reporting AP */ + if (reporter_rnr && (use_for & NL80211_BSS_USE_FOR_NORMAL)) { + if (data.ielen + sizeof(struct element) + + reporter_rnr->datalen > IEEE80211_MAX_DATA_LEN) + continue; + + memcpy(new_ie + data.ielen, reporter_rnr, + sizeof(struct element) + reporter_rnr->datalen); + data.ielen += sizeof(struct element) + + reporter_rnr->datalen; + } bss = cfg80211_inform_single_bss_data(wiphy, &data, gfp); if (!bss) @@ -2812,6 +2936,7 @@ cfg80211_parse_ml_elem_sta_data(struct wiphy *wiphy, } out: + kfree(reporter_rnr); kfree(new_ie); kfree(mle); } diff --git a/net/wireless/tests/scan.c b/net/wireless/tests/scan.c index 77854161cd22..f9ea44aee995 100644 --- a/net/wireless/tests/scan.c +++ b/net/wireless/tests/scan.c @@ -2,7 +2,7 @@ /* * KUnit tests for inform_bss functions * - * Copyright (C) 2023 Intel Corporation + * Copyright (C) 2023-2024 Intel Corporation */ #include #include @@ -406,9 +406,27 @@ static struct inform_bss_ml_sta_case { const char *desc; int mld_id; bool sta_prof_vendor_elems; + bool include_oper_class; } inform_bss_ml_sta_cases[] = { - { .desc = "no_mld_id", .mld_id = 0, .sta_prof_vendor_elems = false }, - { .desc = "mld_id_eq_1", .mld_id = 1, .sta_prof_vendor_elems = true }, + { + .desc = "zero_mld_id", + .mld_id = 0, + .sta_prof_vendor_elems = false, + }, { + .desc = "zero_mld_id_with_oper_class", + .mld_id = 0, + .sta_prof_vendor_elems = false, + .include_oper_class = true, + }, { + .desc = "mld_id_eq_1", + .mld_id = 1, + .sta_prof_vendor_elems = true, + }, { + .desc = "mld_id_eq_1_with_oper_class", + .mld_id = 1, + .sta_prof_vendor_elems = true, + .include_oper_class = true, + }, }; KUNIT_ARRAY_PARAM_DESC(inform_bss_ml_sta, inform_bss_ml_sta_cases, desc) @@ -515,6 +533,12 @@ static void test_inform_bss_ml_sta(struct kunit *test) skb_put_u8(input, 4); skb_put_data(input, "TEST", 4); + if (params->include_oper_class) { + skb_put_u8(input, WLAN_EID_SUPPORTED_REGULATORY_CLASSES); + skb_put_u8(input, 1); + skb_put_u8(input, 81); + } + skb_put_u8(input, WLAN_EID_REDUCED_NEIGHBOR_REPORT); skb_put_u8(input, sizeof(rnr)); skb_put_data(input, &rnr, sizeof(rnr)); @@ -582,15 +606,21 @@ static void test_inform_bss_ml_sta(struct kunit *test) KUNIT_EXPECT_EQ(test, ies->tsf, tsf + le64_to_cpu(sta_prof.tsf_offset)); /* Resulting length should be: * SSID (inherited) + RNR (inherited) + vendor element(s) + + * operating class (if requested) + + * generated RNR (if MLD ID == 0) + * MLE common info + MLE header and control */ if (params->sta_prof_vendor_elems) KUNIT_EXPECT_EQ(test, ies->len, 6 + 2 + sizeof(rnr) + 2 + 160 + 2 + 165 + + (params->include_oper_class ? 3 : 0) + + (!params->mld_id ? 22 : 0) + mle_basic_common_info.var_len + 5); else KUNIT_EXPECT_EQ(test, ies->len, 6 + 2 + sizeof(rnr) + 2 + 155 + + (params->include_oper_class ? 3 : 0) + + (!params->mld_id ? 22 : 0) + mle_basic_common_info.var_len + 5); rcu_read_unlock(); From 83e897a961b801536dd1d736e9ede5b1ddb1c188 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Tue, 2 Jan 2024 21:35:33 +0200 Subject: [PATCH 074/378] wifi: ieee80211: add definitions for negotiated TID to Link map Add the relevant definitions and structures for TID to Link mapping negotiation request/response/teardown according to P802.11be_D4.0. Signed-off-by: Ayala Beker Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.9ef2b866c8c7.Ieaf7dadea9961e0edc55d19c99f0f9fbae591de6@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 83c4d060a559..eafa70e5ba94 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1454,6 +1454,20 @@ struct ieee80211_mgmt { u8 max_tod_error; u8 max_toa_error; } __packed wnm_timing_msr; + struct { + u8 action_code; + u8 dialog_token; + u8 variable[]; + } __packed ttlm_req; + struct { + u8 action_code; + u8 dialog_token; + u8 status_code; + u8 variable[]; + } __packed ttlm_res; + struct { + u8 action_code; + } __packed ttlm_tear_down; } u; } __packed action; DECLARE_FLEX_ARRAY(u8, body); /* Generic frame body */ @@ -3357,6 +3371,8 @@ enum ieee80211_statuscode { WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER = 109, WLAN_STATUS_SAE_HASH_TO_ELEMENT = 126, WLAN_STATUS_SAE_PK = 127, + WLAN_STATUS_DENIED_TID_TO_LINK_MAPPING = 133, + WLAN_STATUS_PREF_TID_TO_LINK_MAPPING_SUGGESTED = 134, }; @@ -3682,6 +3698,7 @@ enum ieee80211_category { WLAN_CATEGORY_UNPROT_DMG = 20, WLAN_CATEGORY_VHT = 21, WLAN_CATEGORY_S1G = 22, + WLAN_CATEGORY_PROTECTED_EHT = 37, WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126, WLAN_CATEGORY_VENDOR_SPECIFIC = 127, }; @@ -3745,6 +3762,13 @@ enum ieee80211_unprotected_wnm_actioncode { WLAN_UNPROTECTED_WNM_ACTION_TIMING_MEASUREMENT_RESPONSE = 1, }; +/* Protected EHT action codes */ +enum ieee80211_protected_eht_actioncode { + WLAN_PROTECTED_EHT_ACTION_TTLM_REQ = 0, + WLAN_PROTECTED_EHT_ACTION_TTLM_RES = 1, + WLAN_PROTECTED_EHT_ACTION_TTLM_TEARDOWN = 2, +}; + /* Security key length */ enum ieee80211_key_len { WLAN_KEY_LEN_WEP40 = 5, @@ -4845,6 +4869,10 @@ struct ieee80211_multi_link_elem { #define IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS 0x000f #define IEEE80211_MLD_CAP_OP_SRS_SUPPORT 0x0010 #define IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP 0x0060 +#define IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_NO_SUPP 0 +#define IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME 1 +#define IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_RESERVED 2 +#define IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_DIFF 3 #define IEEE80211_MLD_CAP_OP_FREQ_SEP_TYPE_IND 0x0f80 #define IEEE80211_MLD_CAP_OP_AAR_SUPPORT 0x1000 From 8f500fbc6c655976c8062b1f1e55bd0b3095d6c2 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Tue, 2 Jan 2024 21:35:34 +0200 Subject: [PATCH 075/378] wifi: mac80211: process and save negotiated TID to Link mapping request An MLD may send TID-to-Link mapping request frame to negotiate TID to link mapping with a peer MLD. Support handling negotiated TID-to-Link mapping request frame by parsing the frame, asking the driver whether it supports the received mapping or not, and sending a TID-to-Link mapping response to the AP MLD. Theoretically, links that became inactive due to the received TID-to-Link mapping request, can be selected to be activated but this would require tearing down the negotiated TID-to-Link mapping, which is still not supported. Signed-off-by: Ayala Beker Reviewed-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.0bc1a24fcc9d.Ie72e47dc6f8c77d4a2f0947b775ef6367fe0edac@changeid Signed-off-by: Johannes Berg --- include/net/mac80211.h | 46 ++++- net/mac80211/driver-ops.h | 19 +++ net/mac80211/ieee80211_i.h | 2 + net/mac80211/iface.c | 12 ++ net/mac80211/main.c | 3 +- net/mac80211/mlme.c | 335 +++++++++++++++++++++++++++++++++++-- net/mac80211/rx.c | 14 ++ net/mac80211/trace.h | 52 ++++++ 8 files changed, 465 insertions(+), 18 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d400fe2e8668..6490b92d5cc1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -342,6 +342,7 @@ struct ieee80211_vif_chanctx_switch { * status changed. * @BSS_CHANGED_EHT_PUNCTURING: The channel puncturing bitmap changed. * @BSS_CHANGED_MLD_VALID_LINKS: MLD valid links status changed. + * @BSS_CHANGED_MLD_TTLM: TID to link mapping was changed */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -378,6 +379,7 @@ enum ieee80211_bss_change { BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = 1<<31, BSS_CHANGED_EHT_PUNCTURING = BIT_ULL(32), BSS_CHANGED_MLD_VALID_LINKS = BIT_ULL(33), + BSS_CHANGED_MLD_TTLM = BIT_ULL(34), /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -1845,6 +1847,35 @@ struct ieee80211_vif_cfg { u8 ap_addr[ETH_ALEN] __aligned(2); }; +#define IEEE80211_TTLM_NUM_TIDS 8 + +/** + * struct ieee80211_neg_ttlm - negotiated TID to link map info + * + * @downlink: bitmap of active links per TID for downlink, or 0 if mapping for + * this TID is not included. + * @uplink: bitmap of active links per TID for uplink, or 0 if mapping for this + * TID is not included. + * @valid: info is valid or not. + */ +struct ieee80211_neg_ttlm { + u16 downlink[IEEE80211_TTLM_NUM_TIDS]; + u16 uplink[IEEE80211_TTLM_NUM_TIDS]; + bool valid; +}; + +/** + * enum ieee80211_neg_ttlm_res - return value for negotiated TTLM handling + * @NEG_TTLM_RES_ACCEPT: accept the request + * @NEG_TTLM_RES_REJECT: reject the request + * @NEG_TTLM_RES_SUGGEST_PREFERRED: reject and suggest a new mapping + */ +enum ieee80211_neg_ttlm_res { + NEG_TTLM_RES_ACCEPT, + NEG_TTLM_RES_REJECT, + NEG_TTLM_RES_SUGGEST_PREFERRED +}; + /** * struct ieee80211_vif - per-interface data * @@ -1863,6 +1894,11 @@ struct ieee80211_vif_cfg { * API calls meant for that purpose. * @dormant_links: bitmap of valid but disabled links, or 0 for non-MLO. * Must be a subset of valid_links. + * @suspended_links: subset of dormant_links representing links that are + * suspended. + * 0 for non-MLO. + * @neg_ttlm: negotiated TID to link mapping info. + * see &struct ieee80211_neg_ttlm. * @addr: address of this interface * @p2p: indicates whether this AP or STA interface is a p2p * interface, i.e. a GO or p2p-sta respectively @@ -1900,7 +1936,8 @@ struct ieee80211_vif { struct ieee80211_vif_cfg cfg; struct ieee80211_bss_conf bss_conf; struct ieee80211_bss_conf __rcu *link_conf[IEEE80211_MLD_MAX_NUM_LINKS]; - u16 valid_links, active_links, dormant_links; + u16 valid_links, active_links, dormant_links, suspended_links; + struct ieee80211_neg_ttlm neg_ttlm; u8 addr[ETH_ALEN] __aligned(2); bool p2p; @@ -4293,6 +4330,10 @@ struct ieee80211_prep_tx_info { * flow offloading for flows originating from the vif. * Note that the driver must not assume that the vif driver_data is valid * at this point, since the callback can be called during netdev teardown. + * @can_neg_ttlm: for managed interface, requests the driver to determine + * if the requested TID-To-Link mapping can be accepted or not. + * If it's not accepted the driver may suggest a preferred mapping and + * modify @ttlm parameter with the suggested TID-to-Link mapping. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -4673,6 +4714,9 @@ struct ieee80211_ops { struct net_device *dev, enum tc_setup_type type, void *type_data); + enum ieee80211_neg_ttlm_res + (*can_neg_ttlm)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_neg_ttlm *ttlm); }; /** diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index eb482fb8c3af..e20c64edb880 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1695,4 +1695,23 @@ int drv_change_sta_links(struct ieee80211_local *local, struct ieee80211_sta *sta, u16 old_links, u16 new_links); +static inline enum ieee80211_neg_ttlm_res +drv_can_neg_ttlm(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_neg_ttlm *neg_ttlm) +{ + enum ieee80211_neg_ttlm_res res = NEG_TTLM_RES_REJECT; + + might_sleep(); + if (!check_sdata_in_driver(sdata)) + return -EIO; + + trace_drv_can_neg_ttlm(local, sdata, neg_ttlm); + if (local->ops->can_neg_ttlm) + res = local->ops->can_neg_ttlm(&local->hw, &sdata->vif, + neg_ttlm); + trace_drv_neg_ttlm_res(local, sdata, res, neg_ttlm); + + return res; +} #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0b2b53550bd9..38755f6f6fa0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2603,6 +2603,8 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, const struct ieee80211_eht_cap_elem *eht_cap_ie_elem, u8 eht_cap_len, struct link_sta_info *link_sta); +void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len); void ieee80211_check_wbrf_support(struct ieee80211_local *local); void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e4e7c0b38cb6..4a87d2d336ae 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1546,6 +1546,18 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, default: break; } + } else if (ieee80211_is_action(mgmt->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_PROTECTED_EHT) { + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + switch (mgmt->u.action.u.ttlm_req.action_code) { + case WLAN_PROTECTED_EHT_ACTION_TTLM_REQ: + ieee80211_process_neg_ttlm_req(sdata, mgmt, + skb->len); + break; + default: + break; + } + } } else if (ieee80211_is_ext(mgmt->frame_control)) { if (sdata->vif.type == NL80211_IFTYPE_STATION) ieee80211_sta_rx_queued_ext(sdata, skb); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f2ece7793573..13c417eda281 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -208,7 +208,8 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) BSS_CHANGED_IBSS |\ BSS_CHANGED_ARP_FILTER |\ BSS_CHANGED_SSID |\ - BSS_CHANGED_MLD_VALID_LINKS) + BSS_CHANGED_MLD_VALID_LINKS |\ + BSS_CHANGED_MLD_TTLM) void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, u64 changed) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 073105deb424..b56f7de3b53f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1318,8 +1318,6 @@ static void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata, cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EML_CAPA); skb_put_data(skb, &eml_capa, sizeof(eml_capa)); } - /* need indication from userspace to support this */ - mld_capa_ops &= ~cpu_to_le16(IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP); skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops)); for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { @@ -5890,6 +5888,56 @@ static void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata, TU_TO_JIFFIES(delay)); } +static int ieee80211_ttlm_set_links(struct ieee80211_sub_if_data *sdata, + u16 active_links, u16 dormant_links, + u16 suspended_links) +{ + u64 changed = 0; + int ret; + + if (!active_links) { + ret = -EINVAL; + goto out; + } + + /* If there is an active negotiated TTLM, it should be discarded by + * the new negotiated/advertised TTLM. + */ + if (sdata->vif.neg_ttlm.valid) { + memset(&sdata->vif.neg_ttlm, 0, sizeof(sdata->vif.neg_ttlm)); + sdata->vif.suspended_links = 0; + changed = BSS_CHANGED_MLD_TTLM; + } + + if (sdata->vif.active_links != active_links) { + ret = ieee80211_set_active_links(&sdata->vif, active_links); + if (ret) { + sdata_info(sdata, "Failed to set TTLM active links\n"); + goto out; + } + } + + ret = ieee80211_vif_set_links(sdata, sdata->vif.valid_links, + dormant_links); + if (ret) { + sdata_info(sdata, "Failed to set TTLM dormant links\n"); + goto out; + } + + changed |= BSS_CHANGED_MLD_VALID_LINKS; + sdata->vif.suspended_links = suspended_links; + if (sdata->vif.suspended_links) + changed |= BSS_CHANGED_MLD_TTLM; + + ieee80211_vif_cfg_change_notify(sdata, changed); + +out: + if (ret) + ieee80211_disconnect(&sdata->vif, false); + + return ret; +} + static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy, struct wiphy_work *work) { @@ -5897,30 +5945,19 @@ static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.ttlm_work.work); - int ret; new_active_links = sdata->u.mgd.ttlm_info.map & sdata->vif.valid_links; new_dormant_links = ~sdata->u.mgd.ttlm_info.map & sdata->vif.valid_links; - if (!new_active_links) { - ieee80211_disconnect(&sdata->vif, false); - return; - } ieee80211_vif_set_links(sdata, sdata->vif.valid_links, 0); - new_active_links = BIT(ffs(new_active_links) - 1); - ieee80211_set_active_links(&sdata->vif, new_active_links); - - ret = ieee80211_vif_set_links(sdata, sdata->vif.valid_links, - new_dormant_links); + if (ieee80211_ttlm_set_links(sdata, new_active_links, new_dormant_links, + 0)) + return; sdata->u.mgd.ttlm_info.active = true; sdata->u.mgd.ttlm_info.switch_time = 0; - - if (!ret) - ieee80211_vif_cfg_change_notify(sdata, - BSS_CHANGED_MLD_VALID_LINKS); } static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data) @@ -6437,6 +6474,272 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, kfree(elems); } +static void ieee80211_apply_neg_ttlm(struct ieee80211_sub_if_data *sdata, + struct ieee80211_neg_ttlm neg_ttlm) +{ + u16 new_active_links, new_dormant_links, new_suspended_links, map = 0; + u8 i; + + for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) + map |= neg_ttlm.downlink[i] | neg_ttlm.uplink[i]; + + /* If there is an active TTLM, unset previously suspended links */ + if (sdata->vif.neg_ttlm.valid) + sdata->vif.dormant_links &= ~sdata->vif.suspended_links; + + /* exclude links that are already disabled by advertised TTLM */ + new_active_links = + map & sdata->vif.valid_links & ~sdata->vif.dormant_links; + new_suspended_links = + (~map & sdata->vif.valid_links) & ~sdata->vif.dormant_links; + new_dormant_links = sdata->vif.dormant_links | new_suspended_links; + if (ieee80211_ttlm_set_links(sdata, new_active_links, + new_dormant_links, new_suspended_links)) + return; + + sdata->vif.neg_ttlm = neg_ttlm; + sdata->vif.neg_ttlm.valid = true; +} + +static void +ieee80211_neg_ttlm_add_suggested_map(struct sk_buff *skb, + struct ieee80211_neg_ttlm *neg_ttlm) +{ + u8 i, direction[IEEE80211_TTLM_MAX_CNT]; + + if (memcmp(neg_ttlm->downlink, neg_ttlm->uplink, + sizeof(neg_ttlm->downlink))) { + direction[0] = IEEE80211_TTLM_DIRECTION_DOWN; + direction[1] = IEEE80211_TTLM_DIRECTION_UP; + } else { + direction[0] = IEEE80211_TTLM_DIRECTION_BOTH; + } + + for (i = 0; i < ARRAY_SIZE(direction); i++) { + u8 tid, len, map_ind = 0, *len_pos, *map_ind_pos, *pos; + __le16 map; + + len = sizeof(struct ieee80211_ttlm_elem) + 1 + 1; + + pos = skb_put(skb, len + 2); + *pos++ = WLAN_EID_EXTENSION; + len_pos = pos++; + *pos++ = WLAN_EID_EXT_TID_TO_LINK_MAPPING; + *pos++ = direction[i]; + map_ind_pos = pos++; + for (tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) { + map = direction[i] == IEEE80211_TTLM_DIRECTION_UP ? + cpu_to_le16(neg_ttlm->uplink[tid]) : + cpu_to_le16(neg_ttlm->downlink[tid]); + if (!map) + continue; + + len += 2; + map_ind |= BIT(tid); + skb_put_data(skb, &map, sizeof(map)); + } + + *map_ind_pos = map_ind; + *len_pos = len; + + if (direction[i] == IEEE80211_TTLM_DIRECTION_BOTH) + break; + } +} + +static void +ieee80211_send_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, + enum ieee80211_neg_ttlm_res ttlm_res, + u8 dialog_token, + struct ieee80211_neg_ttlm *neg_ttlm) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.ttlm_res); + int ttlm_max_len = 2 + 1 + sizeof(struct ieee80211_ttlm_elem) + 1 + + 2 * 2 * IEEE80211_TTLM_NUM_TIDS; + + skb = dev_alloc_skb(local->tx_headroom + hdr_len + ttlm_max_len); + if (!skb) + return; + + skb_reserve(skb, local->tx_headroom); + mgmt = skb_put_zero(skb, hdr_len); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + memcpy(mgmt->da, sdata->vif.cfg.ap_addr, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); + + mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_EHT; + mgmt->u.action.u.ttlm_res.action_code = + WLAN_PROTECTED_EHT_ACTION_TTLM_RES; + mgmt->u.action.u.ttlm_res.dialog_token = dialog_token; + switch (ttlm_res) { + default: + WARN_ON(1); + fallthrough; + case NEG_TTLM_RES_REJECT: + mgmt->u.action.u.ttlm_res.status_code = + WLAN_STATUS_DENIED_TID_TO_LINK_MAPPING; + break; + case NEG_TTLM_RES_ACCEPT: + mgmt->u.action.u.ttlm_res.status_code = WLAN_STATUS_SUCCESS; + break; + case NEG_TTLM_RES_SUGGEST_PREFERRED: + mgmt->u.action.u.ttlm_res.status_code = + WLAN_STATUS_PREF_TID_TO_LINK_MAPPING_SUGGESTED; + ieee80211_neg_ttlm_add_suggested_map(skb, neg_ttlm); + break; + } + + ieee80211_tx_skb(sdata, skb); +} + +static int +ieee80211_parse_neg_ttlm(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_ttlm_elem *ttlm, + struct ieee80211_neg_ttlm *neg_ttlm, + u8 *direction) +{ + u8 control, link_map_presence, map_size, tid; + u8 *pos; + + /* The element size was already validated in + * ieee80211_tid_to_link_map_size_ok() + */ + pos = (void *)ttlm->optional; + + control = ttlm->control; + + /* mapping switch time and expected duration fields are not expected + * in case of negotiated TTLM + */ + if (control & (IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT | + IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT)) { + mlme_dbg(sdata, + "Invalid TTLM element in negotiated TTLM request\n"); + return -EINVAL; + } + + if (control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) { + for (tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) { + neg_ttlm->downlink[tid] = sdata->vif.valid_links; + neg_ttlm->uplink[tid] = sdata->vif.valid_links; + } + *direction = IEEE80211_TTLM_DIRECTION_BOTH; + return 0; + } + + *direction = u8_get_bits(control, IEEE80211_TTLM_CONTROL_DIRECTION); + if (*direction != IEEE80211_TTLM_DIRECTION_DOWN && + *direction != IEEE80211_TTLM_DIRECTION_UP && + *direction != IEEE80211_TTLM_DIRECTION_BOTH) + return -EINVAL; + + link_map_presence = *pos; + pos++; + + if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE) + map_size = 1; + else + map_size = 2; + + for (tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) { + u16 map; + + if (link_map_presence & BIT(tid)) { + map = ieee80211_get_ttlm(map_size, pos); + if (!map) { + mlme_dbg(sdata, + "No active links for TID %d", tid); + return -EINVAL; + } + } else { + map = 0; + } + + switch (*direction) { + case IEEE80211_TTLM_DIRECTION_BOTH: + neg_ttlm->downlink[tid] = map; + neg_ttlm->uplink[tid] = map; + break; + case IEEE80211_TTLM_DIRECTION_DOWN: + neg_ttlm->downlink[tid] = map; + break; + case IEEE80211_TTLM_DIRECTION_UP: + neg_ttlm->uplink[tid] = map; + break; + default: + return -EINVAL; + } + pos += map_size; + } + return 0; +} + +void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) +{ + u8 dialog_token, direction[IEEE80211_TTLM_MAX_CNT] = {}, i; + size_t ies_len; + enum ieee80211_neg_ttlm_res ttlm_res = NEG_TTLM_RES_ACCEPT; + struct ieee802_11_elems *elems = NULL; + struct ieee80211_neg_ttlm neg_ttlm = {}; + + BUILD_BUG_ON(ARRAY_SIZE(direction) != ARRAY_SIZE(elems->ttlm)); + + if (!ieee80211_vif_is_mld(&sdata->vif)) + return; + + dialog_token = mgmt->u.action.u.ttlm_req.dialog_token; + ies_len = len - offsetof(struct ieee80211_mgmt, + u.action.u.ttlm_req.variable); + elems = ieee802_11_parse_elems(mgmt->u.action.u.ttlm_req.variable, + ies_len, true, NULL); + if (!elems) { + ttlm_res = NEG_TTLM_RES_REJECT; + goto out; + } + + for (i = 0; i < elems->ttlm_num; i++) { + if (ieee80211_parse_neg_ttlm(sdata, elems->ttlm[i], + &neg_ttlm, &direction[i]) || + (direction[i] == IEEE80211_TTLM_DIRECTION_BOTH && + elems->ttlm_num != 1)) { + ttlm_res = NEG_TTLM_RES_REJECT; + goto out; + } + } + + if (!elems->ttlm_num || + (elems->ttlm_num == 2 && direction[0] == direction[1])) { + ttlm_res = NEG_TTLM_RES_REJECT; + goto out; + } + + for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) { + if ((neg_ttlm.downlink[i] && + (neg_ttlm.downlink[i] & ~sdata->vif.valid_links)) || + (neg_ttlm.uplink[i] && + (neg_ttlm.uplink[i] & ~sdata->vif.valid_links))) { + ttlm_res = NEG_TTLM_RES_REJECT; + goto out; + } + } + + ttlm_res = drv_can_neg_ttlm(sdata->local, sdata, &neg_ttlm); + + if (ttlm_res != NEG_TTLM_RES_ACCEPT) + goto out; + + ieee80211_apply_neg_ttlm(sdata, neg_ttlm); +out: + kfree(elems); + ieee80211_send_neg_ttlm_res(sdata, ttlm_res, dialog_token, &neg_ttlm); +} + void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 0bf72928ccfc..75eb3e55eaec 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3763,6 +3763,20 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) break; } break; + case WLAN_CATEGORY_PROTECTED_EHT: + switch (mgmt->u.action.u.ttlm_req.action_code) { + case WLAN_PROTECTED_EHT_ACTION_TTLM_REQ: + if (sdata->vif.type != NL80211_IFTYPE_STATION) + break; + + if (len < offsetofend(typeof(*mgmt), + u.action.u.ttlm_req)) + goto invalid; + goto queue; + default: + break; + } + break; } return RX_CONTINUE; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 06835ed4c44f..1c0c46b11c6d 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -3088,6 +3088,58 @@ TRACE_EVENT(stop_queue, ) ); +TRACE_EVENT(drv_can_neg_ttlm, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_neg_ttlm *neg_ttlm), + + TP_ARGS(local, sdata, neg_ttlm), + + TP_STRUCT__entry(LOCAL_ENTRY + VIF_ENTRY + __array(u16, downlink, sizeof(u16) * 8) + __array(u16, uplink, sizeof(u16) * 8) + ), + + TP_fast_assign(LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->downlink, neg_ttlm->downlink, + sizeof(neg_ttlm->downlink)); + memcpy(__entry->uplink, neg_ttlm->uplink, + sizeof(neg_ttlm->uplink)); + ), + + TP_printk(LOCAL_PR_FMT ", " VIF_PR_FMT, LOCAL_PR_ARG, VIF_PR_ARG) +); + +TRACE_EVENT(drv_neg_ttlm_res, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum ieee80211_neg_ttlm_res res, + struct ieee80211_neg_ttlm *neg_ttlm), + + TP_ARGS(local, sdata, res, neg_ttlm), + + TP_STRUCT__entry(LOCAL_ENTRY + VIF_ENTRY + __field(u32, res) + __array(u16, downlink, sizeof(u16) * 8) + __array(u16, uplink, sizeof(u16) * 8) + ), + + TP_fast_assign(LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->res = res; + memcpy(__entry->downlink, neg_ttlm->downlink, + sizeof(neg_ttlm->downlink)); + memcpy(__entry->uplink, neg_ttlm->uplink, + sizeof(neg_ttlm->uplink)); + ), + + TP_printk(LOCAL_PR_FMT VIF_PR_FMT " response: %d\n ", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->res + ) +); #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH From 03d5110241eb3a7e25030e75b546f6f7a62d3007 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Tue, 2 Jan 2024 21:35:35 +0200 Subject: [PATCH 076/378] wifi: mac80211_hwsim: handle TID to link mapping neg request Accept the request if all TIDs are mapped to the same link set, otherwise reject it. Signed-off-by: Ayala Beker Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.dfa8e132d0cd.I5fbec1fef933980819ea39c1227f37d307ab1145@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index a84340c2075f..e144cd30ae3f 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -2738,6 +2738,24 @@ static int mac80211_hwsim_get_survey(struct ieee80211_hw *hw, int idx, return 0; } +static enum ieee80211_neg_ttlm_res +mac80211_hwsim_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_neg_ttlm *neg_ttlm) +{ + u32 i; + + /* For testing purposes, accept if all TIDs are mapped to the same links + * set, otherwise reject. + */ + for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) { + if (neg_ttlm->downlink[i] != neg_ttlm->uplink[i] || + neg_ttlm->downlink[i] != neg_ttlm->downlink[0]) + return NEG_TTLM_RES_REJECT; + } + + return NEG_TTLM_RES_ACCEPT; +} + #ifdef CONFIG_NL80211_TESTMODE /* * This section contains example code for using netlink @@ -3903,6 +3921,7 @@ static const struct ieee80211_ops mac80211_hwsim_mlo_ops = { .change_vif_links = mac80211_hwsim_change_vif_links, .change_sta_links = mac80211_hwsim_change_sta_links, .sta_state = mac80211_hwsim_sta_state, + .can_neg_ttlm = mac80211_hwsim_can_neg_ttlm, }; struct hwsim_new_radio_params { From 9362fabcede336d2e780c6ae44eff0f882349dd2 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Tue, 2 Jan 2024 21:35:36 +0200 Subject: [PATCH 077/378] wifi: mac80211_hwsim: handle BSS_CHANGED_MLD_TTLM Same as in BSS_CHANGED_VALID_LINKS, set the active links to all the usable links. Signed-off-by: Ayala Beker Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.3c28da3534e9.I76846c5dd693f930d4828e411c734639708b5a1a@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index e144cd30ae3f..e080d1131f76 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -2462,7 +2462,7 @@ static void mac80211_hwsim_vif_info_changed(struct ieee80211_hw *hw, } if (vif->type == NL80211_IFTYPE_STATION && - changed & BSS_CHANGED_MLD_VALID_LINKS) { + changed & (BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM)) { u16 usable_links = ieee80211_vif_usable_links(vif); if (vif->active_links != usable_links) From f7660b3f584aadd25dde18aa1902488577a15863 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Tue, 2 Jan 2024 21:35:37 +0200 Subject: [PATCH 078/378] wifi: mac80211: add support for negotiated TTLM request Update neg_ttlm and active_links according to the new mapping, and send a negotiated TID-to-link map request with the new mapping. Signed-off-by: Ayala Beker Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.eeb385d771df.I2a5441c14421de884dbd93d1624ce7bb2c944833@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 37 ++++++++++++ include/net/mac80211.h | 7 ++- net/mac80211/cfg.c | 12 ++++ net/mac80211/ieee80211_i.h | 8 +++ net/mac80211/iface.c | 4 ++ net/mac80211/mlme.c | 114 +++++++++++++++++++++++++++++++++++++ net/mac80211/rx.c | 8 +++ 7 files changed, 188 insertions(+), 2 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index eafa70e5ba94..f0c068446c79 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -5024,6 +5024,43 @@ static inline u16 ieee80211_mle_get_eml_cap(const u8 *data) return get_unaligned_le16(common); } +/** + * ieee80211_mle_get_mld_capa_op - returns the MLD capabilities and operations. + * @data: pointer to the multi link EHT IE + * + * The element is assumed to be of the correct type (BASIC) and big enough, + * this must be checked using ieee80211_mle_type_ok(). + * + * If the MLD capabilities and operations field is not present, 0 will be + * returned. + */ +static inline u16 ieee80211_mle_get_mld_capa_op(const u8 *data) +{ + const struct ieee80211_multi_link_elem *mle = (const void *)data; + u16 control = le16_to_cpu(mle->control); + const u8 *common = mle->variable; + + /* + * common points now at the beginning of + * ieee80211_mle_basic_common_info + */ + common += sizeof(struct ieee80211_mle_basic_common_info); + + if (!(control & IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP)) + return 0; + + if (control & IEEE80211_MLC_BASIC_PRES_LINK_ID) + common += 1; + if (control & IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT) + common += 1; + if (control & IEEE80211_MLC_BASIC_PRES_MED_SYNC_DELAY) + common += 2; + if (control & IEEE80211_MLC_BASIC_PRES_EML_CAPA) + common += 2; + + return get_unaligned_le16(common); +} + /** * ieee80211_mle_size_ok - validate multi-link element size * @data: pointer to the element data diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 6490b92d5cc1..84cc66dd93c1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1810,9 +1810,11 @@ enum ieee80211_offload_flags { * @ps: power-save mode (STA only). This flag is NOT affected by * offchannel/dynamic_ps operations. * @aid: association ID number, valid only when @assoc is true - * @eml_cap: EML capabilities as described in P802.11be_D2.2 Figure 9-1002k. + * @eml_cap: EML capabilities as described in P802.11be_D4.1 Figure 9-1001j. * @eml_med_sync_delay: Medium Synchronization delay as described in - * P802.11be_D2.2 Figure 9-1002j. + * P802.11be_D4.1 Figure 9-1001i. + * @mld_capa_op: MLD Capabilities and Operations per P802.11be_D4.1 + * Figure 9-1001k * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The * may filter ARP queries targeted for other addresses than listed here. * The driver must allow ARP queries targeted for all address listed here @@ -1837,6 +1839,7 @@ struct ieee80211_vif_cfg { u16 aid; u16 eml_cap; u16 eml_med_sync_delay; + u16 mld_capa_op; __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; int arp_addr_cnt; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 489dd97f5172..1e8da6372da2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4966,6 +4966,17 @@ static int ieee80211_set_hw_timestamp(struct wiphy *wiphy, return local->ops->set_hw_timestamp(&local->hw, &sdata->vif, hwts); } +static int +ieee80211_set_ttlm(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ttlm_params *params) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + return ieee80211_req_neg_ttlm(sdata, params); +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -5078,4 +5089,5 @@ const struct cfg80211_ops mac80211_config_ops = { .mod_link_station = ieee80211_mod_link_station, .del_link_station = ieee80211_del_link_station, .set_hw_timestamp = ieee80211_set_hw_timestamp, + .set_ttlm = ieee80211_set_ttlm, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 38755f6f6fa0..79a5067ce82b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -579,6 +579,10 @@ struct ieee80211_if_managed { /* TID-to-link mapping support */ struct wiphy_delayed_work ttlm_work; struct ieee80211_adv_ttlm_info ttlm_info; + + /* dialog token enumerator for neg TTLM request */ + u8 dialog_token_alloc; + struct wiphy_delayed_work neg_ttlm_timeout_work; }; struct ieee80211_if_ibss { @@ -2605,6 +2609,10 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, struct link_sta_info *link_sta); void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); +void ieee80211_process_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len); +int ieee80211_req_neg_ttlm(struct ieee80211_sub_if_data *sdata, + struct cfg80211_ttlm_params *params); void ieee80211_check_wbrf_support(struct ieee80211_local *local); void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 4a87d2d336ae..df314222c2c9 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1554,6 +1554,10 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, ieee80211_process_neg_ttlm_req(sdata, mgmt, skb->len); break; + case WLAN_PROTECTED_EHT_ACTION_TTLM_RES: + ieee80211_process_neg_ttlm_res(sdata, mgmt, + skb->len); + break; default: break; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b56f7de3b53f..5279af09fd53 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -46,6 +46,8 @@ #define IEEE80211_ADV_TTLM_SAFETY_BUFFER_MS msecs_to_jiffies(100) #define IEEE80211_ADV_TTLM_ST_UNDERFLOW 0xff00 +#define IEEE80211_NEG_TTLM_REQ_TIMEOUT (HZ / 5) + static int max_nullfunc_tries = 2; module_param(max_nullfunc_tries, int, 0644); MODULE_PARM_DESC(max_nullfunc_tries, @@ -3087,6 +3089,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(&sdata->u.mgd.ttlm_info, 0, sizeof(sdata->u.mgd.ttlm_info)); wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work); + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, + &ifmgd->neg_ttlm_timeout_work); ieee80211_vif_set_links(sdata, 0, 0); } @@ -4984,6 +4988,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ieee80211_mle_get_eml_cap(eht_ml_elem->data + 1); sdata->vif.cfg.eml_med_sync_delay = ieee80211_mle_get_eml_med_sync_delay(eht_ml_elem->data + 1); + sdata->vif.cfg.mld_capa_op = + ieee80211_mle_get_mld_capa_op(eht_ml_elem->data + 1); } } @@ -6501,6 +6507,19 @@ static void ieee80211_apply_neg_ttlm(struct ieee80211_sub_if_data *sdata, sdata->vif.neg_ttlm.valid = true; } +static void ieee80211_neg_ttlm_timeout_work(struct wiphy *wiphy, + struct wiphy_work *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.mgd.neg_ttlm_timeout_work.work); + + sdata_info(sdata, + "No negotiated TTLM response from AP, disconnecting.\n"); + + __ieee80211_disconnect(sdata); +} + static void ieee80211_neg_ttlm_add_suggested_map(struct sk_buff *skb, struct ieee80211_neg_ttlm *neg_ttlm) @@ -6547,6 +6566,74 @@ ieee80211_neg_ttlm_add_suggested_map(struct sk_buff *skb, } } +static void +ieee80211_send_neg_ttlm_req(struct ieee80211_sub_if_data *sdata, + struct ieee80211_neg_ttlm *neg_ttlm, + u8 dialog_token) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.ttlm_req); + int ttlm_max_len = 2 + 1 + sizeof(struct ieee80211_ttlm_elem) + 1 + + 2 * 2 * IEEE80211_TTLM_NUM_TIDS; + + skb = dev_alloc_skb(local->tx_headroom + hdr_len + ttlm_max_len); + if (!skb) + return; + + skb_reserve(skb, local->tx_headroom); + mgmt = skb_put_zero(skb, hdr_len); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + memcpy(mgmt->da, sdata->vif.cfg.ap_addr, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); + + mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_EHT; + mgmt->u.action.u.ttlm_req.action_code = + WLAN_PROTECTED_EHT_ACTION_TTLM_REQ; + mgmt->u.action.u.ttlm_req.dialog_token = dialog_token; + ieee80211_neg_ttlm_add_suggested_map(skb, neg_ttlm); + ieee80211_tx_skb(sdata, skb); +} + +int ieee80211_req_neg_ttlm(struct ieee80211_sub_if_data *sdata, + struct cfg80211_ttlm_params *params) +{ + struct ieee80211_neg_ttlm neg_ttlm = {}; + u8 i; + + if (!ieee80211_vif_is_mld(&sdata->vif) || + !(sdata->vif.cfg.mld_capa_op & + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP)) + return -EINVAL; + + for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) { + if ((params->dlink[i] & ~sdata->vif.valid_links) || + (params->ulink[i] & ~sdata->vif.valid_links)) + return -EINVAL; + + neg_ttlm.downlink[i] = params->dlink[i]; + neg_ttlm.uplink[i] = params->ulink[i]; + } + + if (drv_can_neg_ttlm(sdata->local, sdata, &neg_ttlm) != + NEG_TTLM_RES_ACCEPT) + return -EINVAL; + + ieee80211_apply_neg_ttlm(sdata, neg_ttlm); + sdata->u.mgd.dialog_token_alloc++; + ieee80211_send_neg_ttlm_req(sdata, &sdata->vif.neg_ttlm, + sdata->u.mgd.dialog_token_alloc); + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, + &sdata->u.mgd.neg_ttlm_timeout_work); + wiphy_delayed_work_queue(sdata->local->hw.wiphy, + &sdata->u.mgd.neg_ttlm_timeout_work, + IEEE80211_NEG_TTLM_REQ_TIMEOUT); + return 0; +} + static void ieee80211_send_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, enum ieee80211_neg_ttlm_res ttlm_res, @@ -6740,6 +6827,29 @@ void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata, ieee80211_send_neg_ttlm_res(sdata, ttlm_res, dialog_token, &neg_ttlm); } +void ieee80211_process_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) +{ + if (!ieee80211_vif_is_mld(&sdata->vif) || + mgmt->u.action.u.ttlm_req.dialog_token != + sdata->u.mgd.dialog_token_alloc) + return; + + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, + &sdata->u.mgd.neg_ttlm_timeout_work); + + /* MLD station sends a TID to link mapping request, mainly to handle + * BTM (BSS transition management) request, in which case it needs to + * restrict the active links set. + * In this case it's not expected that the MLD AP will reject the + * negotiated TTLM request. + * This can be better implemented in the future, to handle request + * rejections. + */ + if (mgmt->u.action.u.ttlm_res.status_code != WLAN_STATUS_SUCCESS) + __ieee80211_disconnect(sdata); +} + void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { @@ -7369,6 +7479,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ieee80211_sta_handle_tspec_ac_params_wk); wiphy_delayed_work_init(&ifmgd->ttlm_work, ieee80211_tid_to_link_map_work); + wiphy_delayed_work_init(&ifmgd->neg_ttlm_timeout_work, + ieee80211_neg_ttlm_timeout_work); ifmgd->flags = 0; ifmgd->powersave = sdata->wdev.ps; @@ -8459,6 +8571,8 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ml_reconf_work); wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work); + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, + &ifmgd->neg_ttlm_timeout_work); if (ifmgd->assoc_data) ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 75eb3e55eaec..615795c4b052 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3773,6 +3773,14 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) u.action.u.ttlm_req)) goto invalid; goto queue; + case WLAN_PROTECTED_EHT_ACTION_TTLM_RES: + if (sdata->vif.type != NL80211_IFTYPE_STATION) + break; + + if (len < offsetofend(typeof(*mgmt), + u.action.u.ttlm_res)) + goto invalid; + goto queue; default: break; } From 34b5ff4617fa1145eec5a1c62fa1ce6ac2fb0b28 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 2 Jan 2024 21:35:38 +0200 Subject: [PATCH 079/378] wifi: mac80211_hwsim: Declare support for negotiated TTLM Advertise support for negotiated TTLM in AP mode for testing purposes. In addition, declare support for some extended capabilities that are globally advertised by mac80211. Signed-off-by: Ilan Peer Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.3f54382f8449.I42b2f7c52f7574448cc8da3ad3db45075e4e0baa@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index e080d1131f76..2b7c9368e96c 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -4984,6 +4984,29 @@ static void mac80211_hwsim_sband_capab(struct ieee80211_supported_band *sband) BIT(NL80211_IFTYPE_MESH_POINT) | \ BIT(NL80211_IFTYPE_OCB)) +static const u8 iftypes_ext_capa_ap[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF | + WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB, + [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB, + [9] = WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT, +}; + +#define MAC80211_HWSIM_MLD_CAPA_OPS FIELD_PREP_CONST( \ + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) + +static const struct wiphy_iftype_ext_capab mac80211_hwsim_iftypes_ext_capa[] = { + { + .iftype = NL80211_IFTYPE_AP, + .extended_capabilities = iftypes_ext_capa_ap, + .extended_capabilities_mask = iftypes_ext_capa_ap, + .extended_capabilities_len = sizeof(iftypes_ext_capa_ap), + .mld_capa_and_ops = MAC80211_HWSIM_MLD_CAPA_OPS, + }, +}; + static int mac80211_hwsim_new_radio(struct genl_info *info, struct hwsim_new_radio_params *param) { @@ -5178,6 +5201,10 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); ieee80211_hw_set(hw, CONNECTION_MONITOR); ieee80211_hw_set(hw, AP_LINK_PS); + + hw->wiphy->iftype_ext_capab = mac80211_hwsim_iftypes_ext_capa; + hw->wiphy->num_iftype_ext_capab = + ARRAY_SIZE(mac80211_hwsim_iftypes_ext_capa); } else { ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); ieee80211_hw_set(hw, PS_NULLFUNC_STACK); From 2518e89d5b1913c360f8e4cd9fc6eda6146b8800 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 2 Jan 2024 21:35:39 +0200 Subject: [PATCH 080/378] wifi: cfg80211: add support for SPP A-MSDUs Add SPP (signaling and payload protected) AMSDU support. Since userspace has to build the RSNX element, add an extended feature flag to indicate that this is supported. In order to avoid downgrade/mismatch attacks, add a flag to the assoc command on the station side, so that we can be sure that the value of the flag comes from the same RSNX element that will be validated by the supplicant against the 4-way-handshake. If we just pulled the data out of a beacon/probe response, we could theoretically look an RSNX element from a different frame, with a different value for this flag, than the supplicant is using to validate in the 4-way-handshake. Note that this patch is only geared towards software crypto implementations or hardware ones that can perfectly implement SPP A-MSDUs, i.e. are able to switch the AAD construction on the fly for each TX/RX frame. For more limited hardware implementations, more capability advertisement would be required, e.g. if the hardware has no way to switch this on the fly but has only a global configuration that must apply to all stations. The driver could of course *reject* mismatches, but the supplicant must know so it can do things like not negotiating SPP A-MSDUs on a T-DLS link when connected to an AP that doesn't support it, or similar. Signed-off-by: Johannes Berg Signed-off-by: Daniel Gabay Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.fadac8df7030.I9240aebcba1be49636a73c647ed0af862713fc6f@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 14 ++++++++++++++ net/wireless/nl80211.c | 24 +++++++++++++++++++++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index cf79656ce09c..56bce924bec6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3059,6 +3059,7 @@ struct cfg80211_assoc_link { * @CONNECT_REQ_MLO_SUPPORT: Userspace indicates support for handling MLD links. * Drivers shall disable MLO features for the current association if this * flag is not set. + * @ASSOC_REQ_SPP_AMSDU: SPP A-MSDUs will be used on this connection (if any) */ enum cfg80211_assoc_req_flags { ASSOC_REQ_DISABLE_HT = BIT(0), @@ -3068,6 +3069,7 @@ enum cfg80211_assoc_req_flags { ASSOC_REQ_DISABLE_HE = BIT(4), ASSOC_REQ_DISABLE_EHT = BIT(5), CONNECT_REQ_MLO_SUPPORT = BIT(6), + ASSOC_REQ_SPP_AMSDU = BIT(7), }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1ccdcae24372..3e239df3528f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2851,6 +2851,10 @@ enum nl80211_commands { * mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element) * in Draft P802.11be_D4.0. * + * @NL80211_ATTR_ASSOC_SPP_AMSDU: flag attribute used with + * %NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs + * are used on this connection + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3394,6 +3398,8 @@ enum nl80211_attrs { NL80211_ATTR_MLO_TTLM_DLINK, NL80211_ATTR_MLO_TTLM_ULINK, + NL80211_ATTR_ASSOC_SPP_AMSDU, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3534,6 +3540,7 @@ enum nl80211_iftype { * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a * previously added station into associated state + * @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -3546,6 +3553,7 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_AUTHENTICATED, NL80211_STA_FLAG_TDLS_PEER, NL80211_STA_FLAG_ASSOCIATED, + NL80211_STA_FLAG_SPP_AMSDU, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, @@ -6520,6 +6528,11 @@ enum nl80211_feature_flags { * DFS master on the same channel as described in FCC-594280 D01 * (Section B.3). This, for example, allows P2P GO and P2P clients to * operate on DFS channels as long as there's a concurrent BSS connection. + * + * @NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT: The driver has support for SPP + * (signaling and payload protected) A-MSDUs and this shall be advertised + * in the RSNXE. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -6594,6 +6607,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_OWE_OFFLOAD, NL80211_EXT_FEATURE_OWE_OFFLOAD_AP, NL80211_EXT_FEATURE_DFS_CONCURRENT, + NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b09700400d09..b267317aa33c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -821,6 +821,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA] = { .type = NLA_FLAG }, [NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), [NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), + [NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -6874,7 +6875,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy, return -EINVAL; /* When you run into this, adjust the code below for the new flag */ - BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7); + BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 8); switch (statype) { case CFG80211_STA_MESH_PEER_KERNEL: @@ -6934,6 +6935,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy, params->link_sta_params.he_capa || params->link_sta_params.eht_capa) return -EINVAL; + if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) + return -EINVAL; } if (statype != CFG80211_STA_AP_CLIENT && @@ -6957,7 +6960,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy, BIT(NL80211_STA_FLAG_ASSOCIATED) | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | - BIT(NL80211_STA_FLAG_MFP))) + BIT(NL80211_STA_FLAG_MFP) | + BIT(NL80211_STA_FLAG_SPP_AMSDU))) return -EINVAL; /* but authenticated/associated only if driver handles it */ @@ -7516,7 +7520,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; /* When you run into this, adjust the code below for the new flag */ - BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7); + BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 8); switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_AP: @@ -7540,6 +7544,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.sta_flags_mask & auth_assoc) return -EINVAL; + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT) && + params.sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) + return -EINVAL; + /* Older userspace, or userspace wanting to be compatible with * !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth * and assoc flags in the mask, but assumes the station will be @@ -11102,6 +11111,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) sizeof(req.s1g_capa)); } + if (nla_get_flag(info->attrs[NL80211_ATTR_ASSOC_SPP_AMSDU])) { + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT)) { + GENL_SET_ERR_MSG(info, "SPP A-MSDUs not supported"); + return -EINVAL; + } + req.flags |= ASSOC_REQ_SPP_AMSDU; + } + req.link_id = nl80211_link_id_or_invalid(info->attrs); if (info->attrs[NL80211_ATTR_MLO_LINKS]) { From 3b220ed8b2172fd8edb22a62a5c3a9a9c9f2b6bd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 2 Jan 2024 21:35:40 +0200 Subject: [PATCH 081/378] wifi: mac80211: add support for SPP A-MSDUs If software crypto is used, simply add support for SPP A-MSDUs (and use it whenever enabled as required by the cfg80211 API). If hardware crypto is used, leave it up to the driver to set the NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT flag and then check sta->spp_amsdu or the IEEE80211_KEY_FLAG_SPP_AMSDU key flag. Signed-off-by: Johannes Berg Signed-off-by: Daniel Gabay Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.b8ada4514e2b.I1ac25d5f158165b5a88062a5a5e4c4fbeecf9a5d@changeid Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 +++++ net/mac80211/cfg.c | 3 +++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/key.c | 4 ++++ net/mac80211/main.c | 5 ++++- net/mac80211/mlme.c | 4 ++++ net/mac80211/wpa.c | 33 +++++++++++++++++++++++---------- 7 files changed, 44 insertions(+), 11 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 84cc66dd93c1..8d6ae22c09bf 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2081,6 +2081,8 @@ static inline bool lockdep_vif_wiphy_mutex_held(struct ieee80211_vif *vif) * @IEEE80211_KEY_FLAG_GENERATE_MMIE: This flag should be set by the driver * for a AES_CMAC key to indicate that it requires sequence number * generation only + * @IEEE80211_KEY_FLAG_SPP_AMSDU: SPP A-MSDUs can be used with this key + * (set by mac80211 from the sta->spp_amsdu flag) */ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(0), @@ -2094,6 +2096,7 @@ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_PUT_MIC_SPACE = BIT(8), IEEE80211_KEY_FLAG_NO_AUTO_TX = BIT(9), IEEE80211_KEY_FLAG_GENERATE_MMIE = BIT(10), + IEEE80211_KEY_FLAG_SPP_AMSDU = BIT(11), }; /** @@ -2392,6 +2395,7 @@ struct ieee80211_link_sta { * would be assigned to link[link_id] where link_id is the id assigned * by the AP. * @valid_links: bitmap of valid links, or 0 for non-MLO + * @spp_amsdu: indicates whether the STA uses SPP A-MSDU or not. */ struct ieee80211_sta { u8 addr[ETH_ALEN]; @@ -2405,6 +2409,7 @@ struct ieee80211_sta { bool tdls_initiator; bool mfp; bool mlo; + bool spp_amsdu; u8 max_amsdu_subframes; struct ieee80211_sta_aggregates *cur; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1e8da6372da2..dd237081dbe3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1942,6 +1942,9 @@ static int sta_apply_parameters(struct ieee80211_local *local, clear_sta_flag(sta, WLAN_STA_TDLS_PEER); } + if (mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) + sta->sta.spp_amsdu = set & BIT(NL80211_STA_FLAG_SPP_AMSDU); + /* mark TDLS channel switch support, if the AP allows it */ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && !sdata->deflink.u.mgd.tdls_chan_switch_prohibited && diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 79a5067ce82b..eb32174984c3 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -441,6 +441,7 @@ struct ieee80211_mgd_assoc_data { bool timeout_started; bool comeback; /* whether the AP has requested association comeback */ bool s1g; + bool spp_amsdu; unsigned int assoc_link_id; diff --git a/net/mac80211/key.c b/net/mac80211/key.c index af74d7f9d94d..a2cce62c97b7 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -925,6 +925,10 @@ int ieee80211_key_link(struct ieee80211_key *key, */ key->color = atomic_inc_return(&key_color); + /* keep this flag for easier access later */ + if (sta && sta->sta.spp_amsdu) + key->conf.flags |= IEEE80211_KEY_FLAG_SPP_AMSDU; + increment_tailroom_need_count(sdata); ret = ieee80211_key_replace(sdata, link, sta, pairwise, old_key, key); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 13c417eda281..d48fa1147c14 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -735,8 +735,11 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT); } - if (!ops->set_key) + if (!ops->set_key) { wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT); + } wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5279af09fd53..fba596d81280 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -5132,6 +5132,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (WARN_ON(!sta)) goto out_err; + sta->sta.spp_amsdu = assoc_data->spp_amsdu; + if (ieee80211_vif_is_mld(&sdata->vif)) { for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { if (!assoc_data->link[link_id].bss) @@ -8216,6 +8218,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, assoc_data->wmm = bss->wmm_used && (local->hw.queues >= IEEE80211_NUM_ACS); + assoc_data->spp_amsdu = req->flags & ASSOC_REQ_SPP_AMSDU; + /* * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. * We still associate in non-HT mode (11a/b/g) if any one of these diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 94dae7cb6dbd..e40529b8c5c9 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -315,7 +315,7 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) * Calculate AAD for CCMP/GCMP, returning qos_tid since we * need that in CCMP also for b_0. */ -static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad) +static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad, bool spp_amsdu) { struct ieee80211_hdr *hdr = (void *)skb->data; __le16 mask_fc; @@ -340,7 +340,14 @@ static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad) len_a += 6; if (ieee80211_is_data_qos(hdr->frame_control)) { - qos_tid = ieee80211_get_tid(hdr); + qos_tid = *ieee80211_get_qos_ctl(hdr); + + if (spp_amsdu) + qos_tid &= IEEE80211_QOS_CTL_TID_MASK | + IEEE80211_QOS_CTL_A_MSDU_PRESENT; + else + qos_tid &= IEEE80211_QOS_CTL_TID_MASK; + mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_ORDER); len_a += 2; } else { @@ -369,10 +376,11 @@ static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad) return qos_tid; } -static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad) +static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, + bool spp_amsdu) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - u8 qos_tid = ccmp_gcmp_aad(skb, aad); + u8 qos_tid = ccmp_gcmp_aad(skb, aad, spp_amsdu); /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC * mode authentication are not allowed to collide, yet both are derived @@ -479,7 +487,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb, return 0; pos += IEEE80211_CCMP_HDR_LEN; - ccmp_special_blocks(skb, pn, b_0, aad); + ccmp_special_blocks(skb, pn, b_0, aad, + key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU); return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, skb_put(skb, mic_len)); } @@ -557,7 +566,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx, u8 aad[2 * AES_BLOCK_SIZE]; u8 b_0[AES_BLOCK_SIZE]; /* hardware didn't decrypt/verify MIC */ - ccmp_special_blocks(skb, pn, b_0, aad); + ccmp_special_blocks(skb, pn, b_0, aad, + key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU); if (ieee80211_aes_ccm_decrypt( key->u.ccmp.tfm, b_0, aad, @@ -581,7 +591,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx, return RX_CONTINUE; } -static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad) +static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad, + bool spp_amsdu) { struct ieee80211_hdr *hdr = (void *)skb->data; @@ -591,7 +602,7 @@ static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad) j_0[14] = 0; j_0[AES_BLOCK_SIZE - 1] = 0x01; - ccmp_gcmp_aad(skb, aad); + ccmp_gcmp_aad(skb, aad, spp_amsdu); } static inline void gcmp_pn2hdr(u8 *hdr, const u8 *pn, int key_id) @@ -680,7 +691,8 @@ static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) return 0; pos += IEEE80211_GCMP_HDR_LEN; - gcmp_special_blocks(skb, pn, j_0, aad); + gcmp_special_blocks(skb, pn, j_0, aad, + key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU); return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len, skb_put(skb, IEEE80211_GCMP_MIC_LEN)); } @@ -753,7 +765,8 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx) u8 aad[2 * AES_BLOCK_SIZE]; u8 j_0[AES_BLOCK_SIZE]; /* hardware didn't decrypt/verify MIC */ - gcmp_special_blocks(skb, pn, j_0, aad); + gcmp_special_blocks(skb, pn, j_0, aad, + key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU); if (ieee80211_aes_gcm_decrypt( key->u.gcmp.tfm, j_0, aad, From 6a19031da91515ebcc4ddaae87914bbcccede75e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 2 Jan 2024 21:35:41 +0200 Subject: [PATCH 082/378] wifi: mac80211_hwsim: advertise AP-side EMLSR/EMLMR capa Advertise EMLSR and EMLMR capability on the AP side to be a better compliant AP MLD. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.dc8786efa787.Ic460c13a91d770c208ac16d0b3e94941bab9b8eb@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 2b7c9368e96c..304dc96fc3bb 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -5003,6 +5003,8 @@ static const struct wiphy_iftype_ext_capab mac80211_hwsim_iftypes_ext_capa[] = { .extended_capabilities = iftypes_ext_capa_ap, .extended_capabilities_mask = iftypes_ext_capa_ap, .extended_capabilities_len = sizeof(iftypes_ext_capa_ap), + .eml_capabilities = IEEE80211_EML_CAP_EMLSR_SUPP | + IEEE80211_EML_CAP_EMLMR_SUPPORT, .mld_capa_and_ops = MAC80211_HWSIM_MLD_CAPA_OPS, }, }; From a8b652604e39ff73b8b7f91e7c29a18904eb8a7c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 2 Jan 2024 21:35:43 +0200 Subject: [PATCH 083/378] wifi: mac80211: take EML/MLD capa from assoc response The association response is more likely to be correct than a random scan result, which really also should be correct, but we generally prefer to take data from the association response, so do that here as well. Also reset the data so it doesn't hang around from an old connection to a non-MLO connection, drivers would hopefully not look at it, but less surprise this way. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.1d10f1d1dbab.I545e955675e2269a52496a22ae7822d95b40235e@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index fba596d81280..576ba6b25db9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3086,9 +3086,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(sdata->vif.bss_conf.tx_pwr_env, 0, sizeof(sdata->vif.bss_conf.tx_pwr_env)); + sdata->vif.cfg.eml_cap = 0; + sdata->vif.cfg.eml_med_sync_delay = 0; + sdata->vif.cfg.mld_capa_op = 0; + memset(&sdata->u.mgd.ttlm_info, 0, sizeof(sdata->u.mgd.ttlm_info)); wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work); + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->neg_ttlm_timeout_work); ieee80211_vif_set_links(sdata, 0, 0); @@ -4981,16 +4986,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, eht_ml_elem && ieee80211_mle_type_ok(eht_ml_elem->data + 1, IEEE80211_ML_CONTROL_TYPE_BASIC, - eht_ml_elem->datalen - 1)) { + eht_ml_elem->datalen - 1)) supports_mlo = true; - - sdata->vif.cfg.eml_cap = - ieee80211_mle_get_eml_cap(eht_ml_elem->data + 1); - sdata->vif.cfg.eml_med_sync_delay = - ieee80211_mle_get_eml_med_sync_delay(eht_ml_elem->data + 1); - sdata->vif.cfg.mld_capa_op = - ieee80211_mle_get_mld_capa_op(eht_ml_elem->data + 1); - } } /* Allow VHT if at least one channel on the sband supports 80 MHz */ @@ -5432,6 +5429,13 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, assoc_data->ap_addr); goto abandon_assoc; } + + sdata->vif.cfg.eml_cap = + ieee80211_mle_get_eml_cap((const void *)elems->ml_basic); + sdata->vif.cfg.eml_med_sync_delay = + ieee80211_mle_get_eml_med_sync_delay((const void *)elems->ml_basic); + sdata->vif.cfg.mld_capa_op = + ieee80211_mle_get_mld_capa_op((const void *)elems->ml_basic); } sdata->vif.cfg.aid = aid; From ccb964b4ab1663ce92f389b72c052fc47a0ffdb9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 2 Jan 2024 21:35:44 +0200 Subject: [PATCH 084/378] wifi: cfg80211: validate MLO connections better When going into an MLO connection, validate that the link IDs match what userspace indicated, and that the AP MLD addresses and capabilities are all matching between the links. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240102213313.ff83c034cb9a.I9962db0bfa8c73b37b8d5b59a3fad7f02f2129ae@changeid [roll in extra fix from Miri to actually check the return value] Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 24 +++++++ net/wireless/core.h | 3 +- net/wireless/mlme.c | 136 ++++++++++++++++++++++++++++++++++---- net/wireless/nl80211.c | 3 +- net/wireless/sme.c | 3 +- 5 files changed, 152 insertions(+), 17 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index f0c068446c79..a70388ae3a7b 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -4935,6 +4935,30 @@ static inline u8 ieee80211_mle_common_size(const u8 *data) return sizeof(*mle) + common + mle->variable[0]; } +/** + * ieee80211_mle_get_link_id - returns the link ID + * @data: the basic multi link element + * + * The element is assumed to be of the correct type (BASIC) and big enough, + * this must be checked using ieee80211_mle_type_ok(). + * + * If the BSS link ID can't be found, -1 will be returned + */ +static inline int ieee80211_mle_get_link_id(const u8 *data) +{ + const struct ieee80211_multi_link_elem *mle = (const void *)data; + u16 control = le16_to_cpu(mle->control); + const u8 *common = mle->variable; + + /* common points now at the beginning of ieee80211_mle_basic_common_info */ + common += sizeof(struct ieee80211_mle_basic_common_info); + + if (!(control & IEEE80211_MLC_BASIC_PRES_LINK_ID)) + return -1; + + return *common; +} + /** * ieee80211_mle_get_bss_param_ch_cnt - returns the BSS parameter change count * @mle: the basic multi link element diff --git a/net/wireless/core.h b/net/wireless/core.h index 13657a85cf61..30434551b377 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -362,7 +362,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct cfg80211_auth_request *req); int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_assoc_request *req); + struct cfg80211_assoc_request *req, + struct netlink_ext_ack *extack); int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 43ba7891e2a3..4052041a19ea 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -4,7 +4,7 @@ * * Copyright (c) 2009, Jouni Malinen * Copyright (c) 2015 Intel Deutschland GmbH - * Copyright (C) 2019-2020, 2022-2023 Intel Corporation + * Copyright (C) 2019-2020, 2022-2024 Intel Corporation */ #include @@ -325,27 +325,135 @@ void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, p1[i] &= p2[i]; } +static int +cfg80211_mlme_check_mlo_compat(const struct ieee80211_multi_link_elem *mle_a, + const struct ieee80211_multi_link_elem *mle_b, + struct netlink_ext_ack *extack) +{ + const struct ieee80211_mle_basic_common_info *common_a, *common_b; + + common_a = (const void *)mle_a->variable; + common_b = (const void *)mle_b->variable; + + if (memcmp(common_a->mld_mac_addr, common_b->mld_mac_addr, ETH_ALEN)) { + NL_SET_ERR_MSG(extack, "AP MLD address mismatch"); + return -EINVAL; + } + + if (ieee80211_mle_get_eml_med_sync_delay((const u8 *)mle_a) != + ieee80211_mle_get_eml_med_sync_delay((const u8 *)mle_b)) { + NL_SET_ERR_MSG(extack, "link EML medium sync delay mismatch"); + return -EINVAL; + } + + if (ieee80211_mle_get_eml_cap((const u8 *)mle_a) != + ieee80211_mle_get_eml_cap((const u8 *)mle_b)) { + NL_SET_ERR_MSG(extack, "link EML capabilities mismatch"); + return -EINVAL; + } + + if (ieee80211_mle_get_mld_capa_op((const u8 *)mle_a) != + ieee80211_mle_get_mld_capa_op((const u8 *)mle_b)) { + NL_SET_ERR_MSG(extack, "link MLD capabilities/ops mismatch"); + return -EINVAL; + } + + return 0; +} + +static int cfg80211_mlme_check_mlo(struct net_device *dev, + struct cfg80211_assoc_request *req, + struct netlink_ext_ack *extack) +{ + const struct ieee80211_multi_link_elem *mles[ARRAY_SIZE(req->links)] = {}; + int i; + + if (req->link_id < 0) + return 0; + + if (!req->links[req->link_id].bss) { + NL_SET_ERR_MSG(extack, "no BSS for assoc link"); + return -EINVAL; + } + + rcu_read_lock(); + for (i = 0; i < ARRAY_SIZE(req->links); i++) { + const struct cfg80211_bss_ies *ies; + const struct element *ml; + + if (!req->links[i].bss) + continue; + + if (ether_addr_equal(req->links[i].bss->bssid, dev->dev_addr)) { + NL_SET_ERR_MSG(extack, "BSSID must not be our address"); + req->links[i].error = -EINVAL; + goto error; + } + + ies = rcu_dereference(req->links[i].bss->ies); + ml = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_MULTI_LINK, + ies->data, ies->len); + if (!ml) { + NL_SET_ERR_MSG(extack, "MLO BSS w/o ML element"); + req->links[i].error = -EINVAL; + goto error; + } + + if (!ieee80211_mle_type_ok(ml->data + 1, + IEEE80211_ML_CONTROL_TYPE_BASIC, + ml->datalen - 1)) { + NL_SET_ERR_MSG(extack, "BSS with invalid ML element"); + req->links[i].error = -EINVAL; + goto error; + } + + mles[i] = (const void *)(ml->data + 1); + + if (ieee80211_mle_get_link_id((const u8 *)mles[i]) != i) { + NL_SET_ERR_MSG(extack, "link ID mismatch"); + req->links[i].error = -EINVAL; + goto error; + } + } + + if (WARN_ON(!mles[req->link_id])) + goto error; + + for (i = 0; i < ARRAY_SIZE(req->links); i++) { + if (i == req->link_id || !req->links[i].bss) + continue; + + if (WARN_ON(!mles[i])) + goto error; + + if (cfg80211_mlme_check_mlo_compat(mles[req->link_id], mles[i], + extack)) { + req->links[i].error = -EINVAL; + goto error; + } + } + + rcu_read_unlock(); + return 0; +error: + rcu_read_unlock(); + return -EINVAL; +} + /* Note: caller must cfg80211_put_bss() regardless of result */ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_assoc_request *req) + struct cfg80211_assoc_request *req, + struct netlink_ext_ack *extack) { struct wireless_dev *wdev = dev->ieee80211_ptr; - int err, i, j; + int err; lockdep_assert_wiphy(wdev->wiphy); - for (i = 1; i < ARRAY_SIZE(req->links); i++) { - if (!req->links[i].bss) - continue; - for (j = 0; j < i; j++) { - if (req->links[i].bss == req->links[j].bss) - return -EINVAL; - } - - if (ether_addr_equal(req->links[i].bss->bssid, dev->dev_addr)) - return -EINVAL; - } + err = cfg80211_mlme_check_mlo(dev, req, extack); + if (err) + return err; if (wdev->connected && (!req->prev_bssid || diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b267317aa33c..0809f721f045 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -11245,7 +11245,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) struct nlattr *link; int rem = 0; - err = cfg80211_mlme_assoc(rdev, dev, &req); + err = cfg80211_mlme_assoc(rdev, dev, &req, + info->extack); if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) { dev->ieee80211_ptr->conn_owner_nlportid = diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 195c8532734b..82e3ce42206c 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -209,7 +209,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev, if (!req.bss) { err = -ENOENT; } else { - err = cfg80211_mlme_assoc(rdev, wdev->netdev, &req); + err = cfg80211_mlme_assoc(rdev, wdev->netdev, + &req, NULL); cfg80211_put_bss(&rdev->wiphy, req.bss); } From 2b3e35d98bcaad9218ec3d4ab91e93022f76b6dd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 11 Jan 2024 18:17:39 +0200 Subject: [PATCH 085/378] wifi: mac80211_hwsim: advertise 15 simultaneous links Advertise MLD capabilities and operations in AP mode that say that up to 15 links are supported simultaneously. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://msgid.link/20240111181514.52a1d48b67e6.Ie459df742944d24d6401683d54d2f3ac44834803@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 304dc96fc3bb..403d892c0468 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -4993,9 +4993,11 @@ static const u8 iftypes_ext_capa_ap[] = { [9] = WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT, }; -#define MAC80211_HWSIM_MLD_CAPA_OPS FIELD_PREP_CONST( \ - IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ - IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) +#define MAC80211_HWSIM_MLD_CAPA_OPS \ + FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \ + FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS, \ + IEEE80211_MLD_MAX_NUM_LINKS - 1) static const struct wiphy_iftype_ext_capab mac80211_hwsim_iftypes_ext_capa[] = { { From d1155f2873cfd387ffb7f5423edbfb2bb22eaf32 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 11 Jan 2024 18:17:40 +0200 Subject: [PATCH 086/378] wifi: mac80211: simplify ieee80211_config_bw() prototype The only user of this function passes a lot of pointers directly from the parsed elements, so it's simpler to just pass the entire elements parsing struct. This also shows that the ht_cap is actually unused. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240111181514.f0653cd5e7dd.I8bd5ee848074029a9f0495c95e4339546ad8fe15@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 576ba6b25db9..6fa69ad3ad4f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -489,15 +489,15 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, } static int ieee80211_config_bw(struct ieee80211_link_data *link, - const struct ieee80211_ht_cap *ht_cap, - const struct ieee80211_vht_cap *vht_cap, - const struct ieee80211_ht_operation *ht_oper, - const struct ieee80211_vht_operation *vht_oper, - const struct ieee80211_he_operation *he_oper, - const struct ieee80211_eht_operation *eht_oper, - const struct ieee80211_s1g_oper_ie *s1g_oper, + struct ieee802_11_elems *elems, const u8 *bssid, u64 *changed) { + const struct ieee80211_vht_cap *vht_cap = elems->vht_cap_elem; + const struct ieee80211_ht_operation *ht_oper = elems->ht_operation; + const struct ieee80211_vht_operation *vht_oper = elems->vht_operation; + const struct ieee80211_he_operation *he_oper = elems->he_operation; + const struct ieee80211_eht_operation *eht_oper = elems->eht_operation; + const struct ieee80211_s1g_oper_ie *s1g_oper = elems->s1g_oper; struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -6433,11 +6433,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, changed |= ieee80211_recalc_twt_req(sdata, sband, link, link_sta, elems); - if (ieee80211_config_bw(link, elems->ht_cap_elem, - elems->vht_cap_elem, elems->ht_operation, - elems->vht_operation, elems->he_operation, - elems->eht_operation, - elems->s1g_oper, bssid, &changed)) { + if (ieee80211_config_bw(link, elems, bssid, &changed)) { sdata_info(sdata, "failed to follow AP %pM bandwidth change, disconnect\n", bssid); From f73ef56c941248ae8b1e19862e11a7bb517b9af1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 11 Jan 2024 18:17:41 +0200 Subject: [PATCH 087/378] wifi: mac80211: remove extra element parsing We already parse all the BSS elements into elems, there's really no need to separately find EHT/ML again. Remove the extra code. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240111181514.c4a55da9f778.I112b1ef00904c4183ac7644800f8daa8a4449875@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6fa69ad3ad4f..45be270eaab7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4962,32 +4962,12 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, (IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT)) && he_oper) { - const struct cfg80211_bss_ies *cbss_ies; - const struct element *eht_ml_elem; - const u8 *eht_oper_ie; - - cbss_ies = rcu_dereference(cbss->ies); - eht_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_EHT_OPERATION, - cbss_ies->data, cbss_ies->len); - if (eht_oper_ie && eht_oper_ie[1] >= - 1 + sizeof(struct ieee80211_eht_operation)) - eht_oper = (void *)(eht_oper_ie + 3); - else - eht_oper = NULL; + eht_oper = elems->eht_operation; if (!ieee80211_verify_sta_eht_mcs_support(sdata, sband, eht_oper)) *conn_flags |= IEEE80211_CONN_DISABLE_EHT; - eht_ml_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_MULTI_LINK, - cbss_ies->data, cbss_ies->len); - - /* data + 1 / datalen - 1 since it's an extended element */ - if (!(*conn_flags & IEEE80211_CONN_DISABLE_EHT) && - eht_ml_elem && - ieee80211_mle_type_ok(eht_ml_elem->data + 1, - IEEE80211_ML_CONTROL_TYPE_BASIC, - eht_ml_elem->datalen - 1)) - supports_mlo = true; + supports_mlo = elems->ml_basic; } /* Allow VHT if at least one channel on the sband supports 80 MHz */ From 6593c7aec7fa15d59af4fbefdbb3d46d27d6e3ed Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 11 Jan 2024 18:17:42 +0200 Subject: [PATCH 088/378] wifi: mac80211: simplify HE capability access For verifying the required HE capabilities are supported locally, we access the HE capability element of the AP. Simplify that access, we've already parsed and validated it when parsing elements. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240111181514.2ef62b43caeb.I8baa604dd3f3399e08b86c99395a2c6a1185d35d@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 45be270eaab7..5b1bc84760d5 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4547,41 +4547,17 @@ static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link, static bool ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata, - const struct cfg80211_bss_ies *ies, + const struct ieee80211_he_cap_elem *he_cap, const struct ieee80211_he_operation *he_op) { - const struct element *he_cap_elem; - const struct ieee80211_he_cap_elem *he_cap; struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp; u16 mcs_80_map_tx, mcs_80_map_rx; u16 ap_min_req_set; - int mcs_nss_size; int nss; - he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, - ies->data, ies->len); - - if (!he_cap_elem) + if (!he_cap) return false; - /* invalid HE IE */ - if (he_cap_elem->datalen < 1 + sizeof(*he_cap)) { - sdata_info(sdata, - "Invalid HE elem, Disable HE\n"); - return false; - } - - /* skip one byte ext_tag_id */ - he_cap = (void *)(he_cap_elem->data + 1); - mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap); - - /* invalid HE IE */ - if (he_cap_elem->datalen < 1 + sizeof(*he_cap) + mcs_nss_size) { - sdata_info(sdata, - "Invalid HE elem with nss size, Disable HE\n"); - return false; - } - /* mcs_nss is right after he_cap info */ he_mcs_nss_supp = (void *)(he_cap + 1); @@ -4946,7 +4922,9 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, } } - if (!ieee80211_verify_peer_he_mcs_support(sdata, ies, he_oper) || + if (!ieee80211_verify_peer_he_mcs_support(sdata, + (void *)elems->he_cap, + he_oper) || !ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper)) *conn_flags |= IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT; From f04d2c247e0407ff8219395bef11ddc0837b7397 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 11 Jan 2024 18:17:43 +0200 Subject: [PATCH 089/378] wifi: mac80211: disallow drivers with HT wider than HE To simplify the code in the next patch, disallow drivers supporting 40 MHz in HT but not HE, since we'd otherwise have to track local maximum bandwidth per mode there. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240111181514.da15fe3214d2.I4df51ad2f4c844615c168bf9bdb498925b3c77d4@changeid Signed-off-by: Johannes Berg --- net/mac80211/main.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index d48fa1147c14..e05bcc35bc1e 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1119,8 +1119,26 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) supp_vht = supp_vht || sband->vht_cap.vht_supported; for_each_sband_iftype_data(sband, i, iftd) { + u8 he_40_mhz_cap; + supp_he = supp_he || iftd->he_cap.has_he; supp_eht = supp_eht || iftd->eht_cap.has_eht; + + if (sband->band == NL80211_BAND_2GHZ) + he_40_mhz_cap = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + else + he_40_mhz_cap = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + + /* currently no support for HE client where HT has 40 MHz but not HT */ + if (iftd->he_cap.has_he && + iftd->types_mask & (BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT)) && + sband->ht_cap.ht_supported && + sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + !(iftd->he_cap.he_cap_elem.phy_cap_info[0] & he_40_mhz_cap)) + return -EINVAL; } /* HT, VHT, HE require QoS, thus >= 4 queues */ From bc8a0fac8677f729ab17176023be1a2653e8b9c6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 11 Jan 2024 18:17:45 +0200 Subject: [PATCH 090/378] wifi: mac80211: don't set bss_conf in parsing When parsing 6 GHz operation, don't set the bss_conf values. We only commit to that later in association, so move the code there. Also clear it later. While at it, handle IEEE80211_6GHZ_CTRL_REG_VLP_AP. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240111181514.c2da4bc515e8.I219ca40e15c0fbaff0e7c3e83ca4b92ecbc1f8ae@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 27 +++++++++++++++++++++++++++ net/mac80211/util.c | 21 +-------------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5b1bc84760d5..99188bd84799 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3081,6 +3081,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec)); wiphy_delayed_work_cancel(local->hw.wiphy, &ifmgd->tx_tspec_wk); + sdata->vif.bss_conf.power_type = IEEE80211_REG_UNSET_AP; sdata->vif.bss_conf.pwr_reduction = 0; sdata->vif.bss_conf.tx_pwr_env_num = 0; memset(sdata->vif.bss_conf.tx_pwr_env, 0, @@ -4236,12 +4237,38 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, if (elems->he_operation && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && elems->he_cap) { + const struct ieee80211_he_6ghz_oper *he_6ghz_oper; + ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, elems->he_cap, elems->he_cap_len, elems->he_6ghz_capa, link_sta); + he_6ghz_oper = ieee80211_he_6ghz_oper(elems->he_operation); + + if (is_6ghz && he_6ghz_oper) { + switch (u8_get_bits(he_6ghz_oper->control, + IEEE80211_HE_6GHZ_OPER_CTRL_REG_INFO)) { + case IEEE80211_6GHZ_CTRL_REG_LPI_AP: + bss_conf->power_type = IEEE80211_REG_LPI_AP; + break; + case IEEE80211_6GHZ_CTRL_REG_SP_AP: + bss_conf->power_type = IEEE80211_REG_SP_AP; + break; + case IEEE80211_6GHZ_CTRL_REG_VLP_AP: + bss_conf->power_type = IEEE80211_REG_VLP_AP; + break; + default: + bss_conf->power_type = IEEE80211_REG_UNSET_AP; + break; + } + } else if (is_6ghz) { + link_info(link, + "HE 6 GHz operation missing (on %d MHz), expect issues\n", + bss_conf->chandef.chan->center_freq); + } + bss_conf->he_support = link_sta->pub->he_cap.has_he; if (elems->rsnx && elems->rsnx_len && (elems->rsnx[0] & WLAN_RSNX_CAPA_PROTECTED_TWT) && diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 643c54855be6..685b55a053f3 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3845,7 +3845,6 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, const struct ieee80211_sta_eht_cap *eht_cap; struct cfg80211_chan_def he_chandef = *chandef; const struct ieee80211_he_6ghz_oper *he_6ghz_oper; - struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; bool support_80_80, support_160, support_320; u8 he_phy_cap, eht_phy_cap; u32 freq; @@ -3881,13 +3880,8 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, eht_oper = NULL; he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper); - - if (!he_6ghz_oper) { - sdata_info(sdata, - "HE 6GHz operation missing (on %d MHz), expect issues\n", - chandef->chan->center_freq); + if (!he_6ghz_oper) return false; - } /* * The EHT operation IE does not contain the primary channel so the @@ -3898,19 +3892,6 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, NL80211_BAND_6GHZ); he_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq); - switch (u8_get_bits(he_6ghz_oper->control, - IEEE80211_HE_6GHZ_OPER_CTRL_REG_INFO)) { - case IEEE80211_6GHZ_CTRL_REG_LPI_AP: - bss_conf->power_type = IEEE80211_REG_LPI_AP; - break; - case IEEE80211_6GHZ_CTRL_REG_SP_AP: - bss_conf->power_type = IEEE80211_REG_SP_AP; - break; - default: - bss_conf->power_type = IEEE80211_REG_UNSET_AP; - break; - } - if (!eht_oper || !(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) { switch (u8_get_bits(he_6ghz_oper->control, From e10322810ce0d0d4a5a319458c4e1e052c6fe9be Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 11 Jan 2024 18:17:46 +0200 Subject: [PATCH 091/378] wifi: mac80211: use deflink and fix typo in link ID check This does not change anything effectively, but it is closer to what the code is trying to achieve here. i.e. select the link data if it is an MLD and fall back to using the deflink otherwise. Fixes: 0f99f0878350 ("wifi: mac80211: Print local link address during authentication") Signed-off-by: Benjamin Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://msgid.link/20240111181514.4c4b1c40eb3c.I2771621dee328c618536596b7e56232df42a79c8@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 99188bd84799..cc9a8eaffa6b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7869,10 +7869,10 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, if (err) goto err_clear; - if (req->link_id > 0) + if (req->link_id >= 0) link = sdata_dereference(sdata->link[req->link_id], sdata); else - link = sdata_dereference(sdata->link[0], sdata); + link = &sdata->deflink; if (WARN_ON(!link)) { err = -ENOLINK; From d60277ac3fc9c9f7887ff813c98d0a71ac2abde2 Mon Sep 17 00:00:00 2001 From: Michael-CY Lee Date: Tue, 23 Jan 2024 13:47:52 +0800 Subject: [PATCH 092/378] wifi: mac80211: apply duration for SW scan This patch makes duration in scan request be applicable when using SW scan, but only accepts durations greater than the default value for the following reasons: 1. Most APs have a beacoon interval of 100ms. 2. Sending and receiving probe require some delay. 3. Setting channel to HW also requires some delays Signed-off-by: Michael-CY Lee Link: https://msgid.link/20240123054752.22833-1-michael-cy.lee@mediatek.com Signed-off-by: Johannes Berg --- net/mac80211/scan.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 645355e5f1bc..8adcb23262f5 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -671,7 +671,10 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, * After sending probe requests, wait for probe responses * on the channel. */ - *next_delay = IEEE80211_CHANNEL_TIME; + *next_delay = msecs_to_jiffies(scan_req->duration) > + IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME ? + msecs_to_jiffies(scan_req->duration) - IEEE80211_PROBE_DELAY : + IEEE80211_CHANNEL_TIME; local->next_scan_state = SCAN_DECISION; } @@ -994,7 +997,10 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, */ if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) || !scan_req->n_ssids) { - *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; + *next_delay = msecs_to_jiffies(scan_req->duration) > + IEEE80211_PASSIVE_CHANNEL_TIME ? + msecs_to_jiffies(scan_req->duration) : + IEEE80211_PASSIVE_CHANNEL_TIME; local->next_scan_state = SCAN_DECISION; if (scan_req->n_ssids) set_bit(SCAN_BEACON_WAIT, &local->scanning); From cf74ce02e394109e16efcb4f15a8e885e9a834a7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Jan 2024 20:08:09 +0200 Subject: [PATCH 093/378] wifi: iwlwifi: add kunit test for devinfo ordering We used to have a test built into the code for this internally, but now we can put that into kunit and let everyone run it, to verify the devinfo table ordering if it's changed. Signed-off-by: Johannes Berg Reviewed-by: Benjamin Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240123200528.a4a8af7c091f.I0fb09083317b331168b99b8db39656a126a5cc4d@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/Kconfig | 9 ++++ drivers/net/wireless/intel/iwlwifi/Makefile | 2 + .../net/wireless/intel/iwlwifi/iwl-config.h | 10 ++++ drivers/net/wireless/intel/iwlwifi/iwl-drv.h | 9 ++++ drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 11 +++- .../net/wireless/intel/iwlwifi/tests/Makefile | 7 +++ .../wireless/intel/iwlwifi/tests/devinfo.c | 54 +++++++++++++++++++ .../net/wireless/intel/iwlwifi/tests/module.c | 10 ++++ 8 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/tests/Makefile create mode 100644 drivers/net/wireless/intel/iwlwifi/tests/devinfo.c create mode 100644 drivers/net/wireless/intel/iwlwifi/tests/module.c diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig index 20971304fdef..4b04865fc2c9 100644 --- a/drivers/net/wireless/intel/iwlwifi/Kconfig +++ b/drivers/net/wireless/intel/iwlwifi/Kconfig @@ -46,6 +46,15 @@ config IWLWIFI if IWLWIFI +config IWLWIFI_KUNIT_TESTS + tristate + depends on KUNIT + default KUNIT_ALL_TESTS + help + Enable this option for iwlwifi kunit tests. + + If unsure, say N. + config IWLWIFI_LEDS bool depends on LEDS_CLASS=y || LEDS_CLASS=MAC80211 diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index b983982aee45..3a2a25333d36 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -33,4 +33,6 @@ obj-$(CONFIG_IWLDVM) += dvm/ obj-$(CONFIG_IWLMVM) += mvm/ obj-$(CONFIG_IWLMEI) += mei/ +obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += tests/ + CFLAGS_iwl-devtrace.o := -I$(src) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index ae6f1cd4d660..b3c6847cccf1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -12,6 +12,7 @@ #include #include #include "iwl-csr.h" +#include "iwl-drv.h" enum iwl_device_family { IWL_DEVICE_FAMILY_UNDEFINED, @@ -471,6 +472,15 @@ struct iwl_dev_info { const char *name; }; +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +extern const struct iwl_dev_info iwl_dev_info_table[]; +extern const unsigned int iwl_dev_info_table_size; +const struct iwl_dev_info * +iwl_pci_find_dev_info(u16 device, u16 subsystem_device, + u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb, + u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step); +#endif + /* * This list declares the config structures for all devices. */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h index 3d1a27ba35c6..6a1d31892417 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h @@ -6,6 +6,7 @@ #ifndef __iwl_drv_h__ #define __iwl_drv_h__ #include +#include /* for all modules */ #define DRV_NAME "iwlwifi" @@ -89,6 +90,14 @@ void iwl_drv_stop(struct iwl_drv *drv); #define IWL_EXPORT_SYMBOL(sym) #endif +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +#define EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(sym) EXPORT_SYMBOL_IF_KUNIT(sym) +#define VISIBLE_IF_IWLWIFI_KUNIT +#else +#define EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(sym) +#define VISIBLE_IF_IWLWIFI_KUNIT static +#endif + /* max retry for init flow */ #define IWL_MAX_INIT_RETRY 2 diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 2c9b98c8184b..cbae9503f4ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -526,7 +526,7 @@ MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, \ IWL_CFG_ANY, _cfg, _name) -static const struct iwl_dev_info iwl_dev_info_table[] = { +VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { #if IS_ENABLED(CONFIG_IWLMVM) /* 9000 */ IWL_DEV_INFO(0x2526, 0x1550, iwl9260_2ac_cfg, iwl9260_killer_1550_name), @@ -1117,6 +1117,12 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { iwl_cfg_sc, iwl_sc_name), #endif /* CONFIG_IWLMVM */ }; +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table); + +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +const unsigned int iwl_dev_info_table_size = ARRAY_SIZE(iwl_dev_info_table); +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table_size); +#endif /* * Read rf id and cdb info from prph register and store it @@ -1236,7 +1242,7 @@ static int map_crf_id(struct iwl_trans *iwl_trans) /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 -static const struct iwl_dev_info * +VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info * iwl_pci_find_dev_info(u16 device, u16 subsystem_device, u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb, u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step) @@ -1299,6 +1305,7 @@ iwl_pci_find_dev_info(u16 device, u16 subsystem_device, return NULL; } +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_pci_find_dev_info); static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { diff --git a/drivers/net/wireless/intel/iwlwifi/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/tests/Makefile new file mode 100644 index 000000000000..5658471bdf0a --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/tests/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +iwlwifi-tests-y += module.o devinfo.o + +ccflags-y += -I$(srctree)/$(src)/../ + +obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlwifi-tests.o diff --git a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c new file mode 100644 index 000000000000..7aa47fce6e2d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for the iwlwifi device info table + * + * Copyright (C) 2023 Intel Corporation + */ +#include +#include "iwl-drv.h" +#include "iwl-config.h" + +MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); + +static void iwl_pci_print_dev_info(const char *pfx, const struct iwl_dev_info *di) +{ + printk(KERN_DEBUG "%sdev=%.4x,subdev=%.4x,mac_type=%.4x,mac_step=%.4x,rf_type=%.4x,cdb=%d,jacket=%d,rf_id=%.2x,no_160=%d,cores=%.2x\n", + pfx, di->device, di->subdevice, di->mac_type, di->mac_step, + di->rf_type, di->cdb, di->jacket, di->rf_id, di->no_160, + di->cores); +} + +static void devinfo_table_order(struct kunit *test) +{ + int idx; + + for (idx = 0; idx < iwl_dev_info_table_size; idx++) { + const struct iwl_dev_info *di = &iwl_dev_info_table[idx]; + const struct iwl_dev_info *ret; + + ret = iwl_pci_find_dev_info(di->device, di->subdevice, + di->mac_type, di->mac_step, + di->rf_type, di->cdb, + di->jacket, di->rf_id, + di->no_160, di->cores, di->rf_step); + if (ret != di) { + iwl_pci_print_dev_info("searched: ", di); + iwl_pci_print_dev_info("found: ", ret); + KUNIT_FAIL(test, + "unusable entry at index %d (found index %d instead)\n", + idx, (int)(ret - iwl_dev_info_table)); + } + } +} + +static struct kunit_case devinfo_test_cases[] = { + KUNIT_CASE(devinfo_table_order), + {} +}; + +static struct kunit_suite iwlwifi_devinfo = { + .name = "iwlwifi-devinfo", + .test_cases = devinfo_test_cases, +}; + +kunit_test_suite(iwlwifi_devinfo); diff --git a/drivers/net/wireless/intel/iwlwifi/tests/module.c b/drivers/net/wireless/intel/iwlwifi/tests/module.c new file mode 100644 index 000000000000..0c54f818e5a7 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/tests/module.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Module boilerplate for the iwlwifi kunit module. + * + * Copyright (C) 2023 Intel Corporation + */ +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("kunit tests for iwlwifi"); From 099a47dbe71b758e6875d0297467f2b8d23014df Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Tue, 23 Jan 2024 20:08:10 +0200 Subject: [PATCH 094/378] wifi: iwlwifi: Add support for new 802.11be device Add support for the new 802.11be device with limites capabilities: - 320 MHz isn't supported - MCSs 12 and 13 are not supported Signed-off-by: Mukesh Sisodiya Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240123200528.8529bd2acedf.I25dccb7bbeb21b8df2123fad51dde7fcf137a508@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 1 + drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 13 +++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-config.h | 4 ++++ .../net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 16 ++++++++++++++-- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 2 ++ drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 7 ++++++- 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 82da957adcf6..21fdaf8e0e0e 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -153,6 +153,7 @@ const struct iwl_cfg_trans_params iwl_bz_trans_cfg = { }; const char iwl_bz_name[] = "Intel(R) TBD Bz device"; +const char iwl_mtp_name[] = "Intel(R) Wi-Fi 7 BE202 160MHz"; const struct iwl_cfg iwl_cfg_bz = { .fw_name_mac = "bz", diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index 650e4bde9c17..d467ec0e3552 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -12,6 +12,8 @@ #include "fw/api/alive.h" #include "fw/uefi.h" +#define IWL_PNVM_REDUCED_CAP_BIT BIT(25) + struct iwl_pnvm_section { __le32 offset; const u8 data[]; @@ -173,6 +175,7 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, while (len >= sizeof(*tlv)) { u32 tlv_len, tlv_type; + u32 rf_type; len -= sizeof(*tlv); tlv = (const void *)data; @@ -201,6 +204,16 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, data += sizeof(*tlv) + ALIGN(tlv_len, 4); len -= ALIGN(tlv_len, 4); + trans->reduced_cap_sku = false; + rf_type = CSR_HW_RFID_TYPE(trans->hw_rf_id); + if ((trans->sku_id[0] & IWL_PNVM_REDUCED_CAP_BIT) && + rf_type == IWL_CFG_RF_TYPE_FM) + trans->reduced_cap_sku = true; + + IWL_DEBUG_FW(trans, + "Reduced SKU device %d\n", + trans->reduced_cap_sku); + if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) && trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) && trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index b3c6847cccf1..97e73443316a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -443,6 +443,9 @@ struct iwl_cfg { #define IWL_CFG_NO_160 0x1 #define IWL_CFG_160 0x0 +#define IWL_CFG_NO_320 0x1 +#define IWL_CFG_320 0x0 + #define IWL_CFG_CORES_BT 0x0 #define IWL_CFG_CORES_BT_GNSS 0x5 @@ -536,6 +539,7 @@ extern const char iwl_ax221_name[]; extern const char iwl_ax231_name[]; extern const char iwl_ax411_name[]; extern const char iwl_bz_name[]; +extern const char iwl_mtp_name[]; extern const char iwl_sc_name[]; #if IS_ENABLED(CONFIG_IWLDVM) extern const struct iwl_cfg iwl5300_agn_cfg; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 402896988686..3f62f10a7c37 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -892,8 +892,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, bool is_ap = iftype_data->types_mask & BIT(NL80211_IFTYPE_AP); bool no_320; - no_320 = !trans->trans_cfg->integrated && - trans->pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB; + no_320 = (!trans->trans_cfg->integrated && + trans->pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB) || + trans->reduced_cap_sku; if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be) iftype_data->eht_cap.has_eht = false; @@ -1059,6 +1060,17 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->he_cap.he_cap_elem.phy_cap_info[7] &= ~IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; } + + if (trans->reduced_cap_sku) { + memset(&iftype_data->eht_cap.eht_mcs_nss_supp.bw._320, 0, + sizeof(iftype_data->eht_cap.eht_mcs_nss_supp.bw._320)); + iftype_data->eht_cap.eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss = 0; + iftype_data->eht_cap.eht_mcs_nss_supp.bw._160.rx_tx_mcs13_max_nss = 0; + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[8] &= + ~IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA; + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] &= + ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK; + } } static void iwl_init_he_hw_capab(struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 5789a8735976..9e26c9eb6d83 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1068,6 +1068,7 @@ struct iwl_trans_txqs { * @pcie_link_speed: current PCIe link speed (%PCI_EXP_LNKSTA_CLS_*), * only valid for discrete (not integrated) NICs * @invalid_tx_cmd: invalid TX command buffer + * @reduced_cap_sku: reduced capability supported SKU */ struct iwl_trans { bool csme_own; @@ -1090,6 +1091,7 @@ struct iwl_trans { u32 hw_id; char hw_id_str[52]; u32 sku_id[3]; + bool reduced_cap_sku; u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index cbae9503f4ba..42680d8469f5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1008,8 +1008,13 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_320, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_gl, iwl_bz_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_NO_320, IWL_CFG_ANY, IWL_CFG_NO_CDB, + iwl_cfg_gl, iwl_mtp_name), /* SoF with JF2 */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, From 47cde0942959ca11855e1aa5d66209d8625c2a2b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Jan 2024 20:08:12 +0200 Subject: [PATCH 095/378] wifi: iwlwifi: make TB reallocation a debug message There's no need to print this, it's a known issue and the workaround works just fine. Make the reallocation message just a debug message. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240123200528.329d5f2ee7f7.I0bfc6dde17fe2c738129f3aba746c6cba57589f9@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/queue/tx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/queue/tx.c b/drivers/net/wireless/intel/iwlwifi/queue/tx.c index ca74b1b63cac..ba0419bc1765 100644 --- a/drivers/net/wireless/intel/iwlwifi/queue/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/queue/tx.c @@ -271,9 +271,10 @@ static int iwl_txq_gen2_set_tb_with_wa(struct iwl_trans *trans, meta = NULL; goto unmap; } - IWL_WARN(trans, - "TB bug workaround: copied %d bytes from 0x%llx to 0x%llx\n", - len, (unsigned long long)oldphys, (unsigned long long)phys); + IWL_DEBUG_TX(trans, + "TB bug workaround: copied %d bytes from 0x%llx to 0x%llx\n", + len, (unsigned long long)oldphys, + (unsigned long long)phys); ret = 0; unmap: From 84ec2d2e960f33edcee7c47118e59dde72826843 Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Fri, 26 Jan 2024 09:00:30 +0200 Subject: [PATCH 096/378] wifi: iwlwifi: disable 160 MHz based on subsystem device ID The driver should not send 160 MHz BW support for 5 GHz band in HE if PCI subsystem device ID indicates no 160 MHz support. Signed-off-by: Mukesh Sisodiya Reviewed-by: Mordechay Goodstein Signed-off-by: Miri Korenblit Link: https://msgid.link/20240126085924.77c248ce6986.I558e8d0cf19dc862b1c4124df78a4cb690095bb2@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 4 ++++ drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 3 ++- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 3f62f10a7c37..67c7cda073e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1061,6 +1061,10 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, ~IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; } + if (trans->no_160) + iftype_data->he_cap.he_cap_elem.phy_cap_info[0] &= + ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + if (trans->reduced_cap_sku) { memset(&iftype_data->eht_cap.eht_mcs_nss_supp.bw._320, 0, sizeof(iftype_data->eht_cap.eht_mcs_nss_supp.bw._320)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 9e26c9eb6d83..e3b76c682d76 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1069,6 +1069,7 @@ struct iwl_trans_txqs { * only valid for discrete (not integrated) NICs * @invalid_tx_cmd: invalid TX command buffer * @reduced_cap_sku: reduced capability supported SKU + * @no_160: device not supporting 160 MHz */ struct iwl_trans { bool csme_own; @@ -1092,7 +1093,7 @@ struct iwl_trans { char hw_id_str[52]; u32 sku_id[3]; bool reduced_cap_sku; - + u8 no_160; u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size; bool pm_support; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 42680d8469f5..c80b02503b41 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1394,6 +1394,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (dev_info) { iwl_trans->cfg = dev_info->cfg; iwl_trans->name = dev_info->name; + iwl_trans->no_160 = dev_info->no_160 == IWL_CFG_NO_160; } #if IS_ENABLED(CONFIG_IWLMVM) From de0c2cdcb7eb8e08f3886b433277472d97af0f6e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Jan 2024 20:08:14 +0200 Subject: [PATCH 097/378] wifi: iwlwifi: mvm: limit EHT 320 MHz MCS for STEP URM If the STEP (the interface between MAC and PHY) is in URM (a lower speed mode) then we cannot use 320 MHz MCS > 9. Therefore, limit the MCS in our capabilities in this case. Note that this also limits the TX/rate scaling since that takes both TX and RX capabilities into account. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240123200528.02bae683b7fc.Id5efbb71d45da02c8c4e211d20396637ddd44da8@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 5 +++++ drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 3 +++ drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 4 +++- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 5 +++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 67c7cda073e8..8e6ce484db87 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1061,6 +1061,11 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, ~IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; } + if (trans->step_urm) { + iftype_data->eht_cap.eht_mcs_nss_supp.bw._320.rx_tx_mcs11_max_nss = 0; + iftype_data->eht_cap.eht_mcs_nss_supp.bw._320.rx_tx_mcs13_max_nss = 0; + } + if (trans->no_160) iftype_data->he_cap.he_cap_elem.phy_cap_info[0] &= ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index dd32c287b983..c1c7d44f421b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -374,6 +374,9 @@ enum { #define CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR 0xA29938 #define CNVI_SCU_SEQ_DATA_DW9 0xA27488 +#define CNVI_PMU_STEP_FLOW 0xA2D588 +#define CNVI_PMU_STEP_FLOW_FORCE_URM BIT(2) + #define PREG_AUX_BUS_WPROT_0 0xA04CC0 /* device family 9000 WPROT register */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index e3b76c682d76..3047ffc24415 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1070,6 +1070,7 @@ struct iwl_trans_txqs { * @invalid_tx_cmd: invalid TX command buffer * @reduced_cap_sku: reduced capability supported SKU * @no_160: device not supporting 160 MHz + * @step_urm: STEP is in URM, no support for MCS>9 in 320 MHz */ struct iwl_trans { bool csme_own; @@ -1093,7 +1094,8 @@ struct iwl_trans { char hw_id_str[52]; u32 sku_id[3]; bool reduced_cap_sku; - u8 no_160; + u8 no_160:1, step_urm:1; + u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size; bool pm_support; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 1252084662c6..b6acf4ade552 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -677,6 +677,11 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm) iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, NULL); + if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_BZ) + mvm->trans->step_urm = !!(iwl_read_umac_prph(mvm->trans, + CNVI_PMU_STEP_FLOW) & + CNVI_PMU_STEP_FLOW_FORCE_URM); + /* Send init config command to mark that we are sending NVM access * commands */ From dfdfe4be183b27b278d78225d48b98b3c1ca6285 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Jan 2024 20:08:15 +0200 Subject: [PATCH 098/378] wifi: iwlwifi: remove retry loops in start There's either the pldr_sync case, in which case we didn't want or do the retry loops anyway, or things will just continue to fail. Remove the retry loop that was added in a previous attempt to address the issue that was later (though still a bit broken) addressed by the pldr_sync case. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240123200528.f80a88a18799.I48f21eda090f4cc675f40e99eef69a986d21b500@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 28 ++++++------------- drivers/net/wireless/intel/iwlwifi/iwl-drv.h | 3 -- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 10 +------ 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index ffe2670720c9..8d562d0e87e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1423,35 +1423,25 @@ _iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) const struct iwl_op_mode_ops *ops = op->ops; struct dentry *dbgfs_dir = NULL; struct iwl_op_mode *op_mode = NULL; - int retry, max_retry = !!iwlwifi_mod_params.fw_restart * IWL_MAX_INIT_RETRY; /* also protects start/stop from racing against each other */ lockdep_assert_held(&iwlwifi_opmode_table_mtx); - for (retry = 0; retry <= max_retry; retry++) { - #ifdef CONFIG_IWLWIFI_DEBUGFS - drv->dbgfs_op_mode = debugfs_create_dir(op->name, - drv->dbgfs_drv); - dbgfs_dir = drv->dbgfs_op_mode; + drv->dbgfs_op_mode = debugfs_create_dir(op->name, + drv->dbgfs_drv); + dbgfs_dir = drv->dbgfs_op_mode; #endif - op_mode = ops->start(drv->trans, drv->trans->cfg, - &drv->fw, dbgfs_dir); - - if (op_mode) - return op_mode; - - if (test_bit(STATUS_TRANS_DEAD, &drv->trans->status)) - break; - - IWL_ERR(drv, "retry init count %d\n", retry); + op_mode = ops->start(drv->trans, drv->trans->cfg, + &drv->fw, dbgfs_dir); + if (op_mode) + return op_mode; #ifdef CONFIG_IWLWIFI_DEBUGFS - debugfs_remove_recursive(drv->dbgfs_op_mode); - drv->dbgfs_op_mode = NULL; + debugfs_remove_recursive(drv->dbgfs_op_mode); + drv->dbgfs_op_mode = NULL; #endif - } return NULL; } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h index 6a1d31892417..1549ff429549 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h @@ -98,9 +98,6 @@ void iwl_drv_stop(struct iwl_drv *drv); #define VISIBLE_IF_IWLWIFI_KUNIT static #endif -/* max retry for init flow */ -#define IWL_MAX_INIT_RETRY 2 - #define FW_NAME_PRE_BUFSIZE 64 struct iwl_trans; const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 7f13dff04b26..6bbcf4092f52 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1195,14 +1195,12 @@ int iwl_mvm_mac_start(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; - int retry, max_retry = 0; mutex_lock(&mvm->mutex); /* we are starting the mac not in error flow, and restart is enabled */ if (!test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) && iwlwifi_mod_params.fw_restart) { - max_retry = IWL_MAX_INIT_RETRY; /* * This will prevent mac80211 recovery flows to trigger during * init failures @@ -1210,13 +1208,7 @@ int iwl_mvm_mac_start(struct ieee80211_hw *hw) set_bit(IWL_MVM_STATUS_STARTING, &mvm->status); } - for (retry = 0; retry <= max_retry; retry++) { - ret = __iwl_mvm_mac_start(mvm); - if (!ret || mvm->pldr_sync) - break; - - IWL_ERR(mvm, "mac start retry %d\n", retry); - } + ret = __iwl_mvm_mac_start(mvm); clear_bit(IWL_MVM_STATUS_STARTING, &mvm->status); mutex_unlock(&mvm->mutex); From 6c8ce23854b66db94d88e0957e531cb074806c16 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 23 Jan 2024 20:08:16 +0200 Subject: [PATCH 099/378] wifi: iwlwifi: change link id in time event to s8 Link ID in time event data is -1 when the time event is cleared. Change the type of the link ID in the time event data structure and in the affected function from unsigned to signed. Fixes: 135065837310 ("wifi: iwlwifi: support link_id in SESSION_PROTECTION cmd") Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240123200528.50d4941f946c.Iea990b118c69bc3e1eb61c1d134c9d470b3a17ac@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 40627961b834..997f0395b97a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -121,7 +121,7 @@ struct iwl_mvm_time_event_data { * if the te is in the time event list or not (when id == TE_MAX) */ u32 id; - u8 link_id; + s8 link_id; }; /* Power management */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 218fdf1ed530..aceab96bcb97 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -692,7 +692,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, /* Determine whether mac or link id should be used, and validate the link id */ static int iwl_mvm_get_session_prot_id(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 link_id) + s8 link_id) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ver = iwl_fw_lookup_cmd_ver(mvm->fw, @@ -716,7 +716,7 @@ static int iwl_mvm_get_session_prot_id(struct iwl_mvm *mvm, static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 id, u32 link_id) + u32 id, s8 link_id) { int mac_link_id = iwl_mvm_get_session_prot_id(mvm, vif, link_id); struct iwl_mvm_session_prot_cmd cmd = { @@ -745,7 +745,7 @@ static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif = te_data->vif; struct iwl_mvm_vif *mvmvif; enum nl80211_iftype iftype; - unsigned int link_id; + s8 link_id; if (!vif) return false; @@ -1296,7 +1296,7 @@ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; const u16 notif[] = { WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF) }; struct iwl_notification_wait wait_notif; - int mac_link_id = iwl_mvm_get_session_prot_id(mvm, vif, link_id); + int mac_link_id = iwl_mvm_get_session_prot_id(mvm, vif, (s8)link_id); struct iwl_mvm_session_prot_cmd cmd = { .id_and_color = cpu_to_le32(mac_link_id), .action = cpu_to_le32(FW_CTXT_ACTION_ADD), From 77b8b078440eb136efd03427404134e1d88dca50 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Jan 2024 20:08:17 +0200 Subject: [PATCH 100/378] wifi: iwlwifi: nvm-parse: advertise common packet padding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should - at least for now - advertise common nominal packet padding of 16µs instead of the more specific PPE thresholds. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240123200528.4312e176dfdc.Ide75980ff57257a31e86e6ac5948a8f97aaab577@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 8e6ce484db87..a7152d65eefa 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -696,10 +696,11 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI, .phy_cap_info[5] = + FIELD_PREP_CONST(IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK, + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US) | IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT, + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP, .phy_cap_info[6] = IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP, @@ -733,6 +734,9 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { /* * PPE thresholds for NSS = 2, and RU index bitmap set * to 0xc. + * Note: just for stating what we want, not present in + * the transmitted data due to not including + * IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT. */ .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } }, @@ -801,7 +805,8 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI, .phy_cap_info[5] = - IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT, + FIELD_PREP_CONST(IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK, + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US), }, /* For all MCS and bandwidth, set 2 NSS for both Tx and @@ -829,6 +834,9 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { /* * PPE thresholds for NSS = 2, and RU index bitmap set * to 0xc. + * Note: just for stating what we want, not present in + * the transmitted data due to not including + * IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT. */ .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } }, From 22d9987c79cb8d1d04625cde5f63fc01abae2b6a Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 23 Jan 2024 20:08:18 +0200 Subject: [PATCH 101/378] wifi: iwlwifi: skip affinity setting on non-SMP Without SMP the function is just a stub that returns an error code. Add a compile time check for CONFIG_SMP in the interest of not logging an error if setting affinity is not possible anyway. Signed-off-by: Benjamin Berg Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240123200528.ed9094390731.Ic4e5e019c01fd4231b99cf4919af5d19d6353869@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 63e13577aff8..b5756e168f49 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1718,6 +1718,7 @@ iwl_pcie_set_interrupt_capa(struct pci_dev *pdev, static void iwl_pcie_irq_set_affinity(struct iwl_trans *trans) { +#if defined(CONFIG_SMP) int iter_rx_q, i, ret, cpu, offset; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1738,6 +1739,7 @@ static void iwl_pcie_irq_set_affinity(struct iwl_trans *trans) "Failed to set affinity mask for IRQ %d\n", trans_pcie->msix_entries[i].vector); } +#endif } static int iwl_pcie_init_msix_handler(struct pci_dev *pdev, From 38d84aaed52832945887ae8618bed68c89a3bf81 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 23 Jan 2024 20:08:20 +0200 Subject: [PATCH 102/378] wifi: iwlwifi: mvm: introduce PHY_CONTEXT_CMD_API_VER_5 This command version adds two news fields: sbb_bandwidth and sbb_ctrl_channel_loc They will be populated later. Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240123200528.82ab4140fff9.Icfba4819fe0b7ac8219ab671c632e25f5fbbaf6f@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h | 16 ++++++++++++++-- .../net/wireless/intel/iwlwifi/mvm/phy-ctxt.c | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h index 306ed88de463..205d0413e626 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h @@ -142,6 +142,8 @@ struct iwl_phy_context_cmd_v1 { * @lmac_id: the lmac id the phy context belongs to * @ci: channel info * @rxchain_info: ??? + * @sbb_bandwidth: 0 disabled, 1 - 40Mhz ... 4 - 320MHz + * @sbb_ctrl_channel_loc: location of the control channel * @dsp_cfg_flags: set to 0 * @reserved: reserved to align to 64 bit */ @@ -152,9 +154,19 @@ struct iwl_phy_context_cmd { /* PHY_CONTEXT_DATA_API_S_VER_3, PHY_CONTEXT_DATA_API_S_VER_4 */ struct iwl_fw_channel_info ci; __le32 lmac_id; - __le32 rxchain_info; /* reserved in _VER_4 */ + union { + __le32 rxchain_info; /* reserved in _VER_4 */ + struct { /* used for _VER_5 */ + u8 sbb_bandwidth; + u8 sbb_ctrl_channel_loc; + __le16 reserved; + } v5; + }; __le32 dsp_cfg_flags; __le32 reserved; -} __packed; /* PHY_CONTEXT_CMD_API_VER_3, PHY_CONTEXT_CMD_API_VER_4 */ +} __packed; /* PHY_CONTEXT_CMD_API_VER_3, + * PHY_CONTEXT_CMD_API_VER_4, + * PHY_CONTEXT_CMD_API_VER_5 + */ #endif /* __iwl_fw_api_phy_ctxt_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index 334d1f59f6e4..8bf778503b74 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -204,7 +204,7 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, int ret; int ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1); - if (ver == 3 || ver == 4) { + if (ver >= 3 && ver <= 5) { struct iwl_phy_context_cmd cmd = {}; /* Set the command header fields */ From 289f57bbef09b0a2385a5187274e37d76031de14 Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Tue, 23 Jan 2024 20:08:21 +0200 Subject: [PATCH 103/378] wifi: iwlwifi: bump FW API to 87 for AX/BZ/SC devices Start supporting API version 87 for new devices. Signed-off-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240123200528.14cc41da34c4.Ic867f979504c60c21c8182e9adccec9ffbadfe5b@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/cfg/ax210.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c index 134635c70ce8..02b727687fb8 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_AX210_UCODE_API_MAX 86 +#define IWL_AX210_UCODE_API_MAX 87 /* Lowest firmware API version supported */ #define IWL_AX210_UCODE_API_MIN 59 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 21fdaf8e0e0e..20799a0fbc07 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_BZ_UCODE_API_MAX 86 +#define IWL_BZ_UCODE_API_MAX 87 /* Lowest firmware API version supported */ #define IWL_BZ_UCODE_API_MIN 80 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index 80eb9b499538..51b8f50d8795 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_SC_UCODE_API_MAX 86 +#define IWL_SC_UCODE_API_MAX 87 /* Lowest firmware API version supported */ #define IWL_SC_UCODE_API_MIN 82 From c4d32f2745c75c9041937767f0329f6f1051778b Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 23 Jan 2024 20:08:22 +0200 Subject: [PATCH 104/378] wifi: iwlwifi: implement can_activate_links callback This callback checks if a given bitmap of active_links will be supported by the driver or not. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Link: https://msgid.link/20240123200528.a26fd48bfe3d.I03ae6b4c7fd24e8701660a68cec9403dc3469a0e@changeid Signed-off-by: Johannes Berg --- .../wireless/intel/iwlwifi/mvm/mld-mac80211.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 61170173f917..449229ced3bb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -253,9 +253,6 @@ __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, if (!rcu_access_pointer(link_conf->chanctx_conf)) n_active++; - if (n_active > iwl_mvm_max_active_links(mvm, vif)) - return -EOPNOTSUPP; - if (WARN_ON_ONCE(!mvmvif->link[link_id])) return -EINVAL; @@ -1121,17 +1118,12 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) { struct iwl_mvm_vif_link_info *new_link[IEEE80211_MLD_MAX_NUM_LINKS] = {}; - unsigned int n_active = iwl_mvm_mld_count_active_links(vif); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u16 removed = old_links & ~new_links; u16 added = new_links & ~old_links; int err, i; - if (hweight16(new_links) > 1 && - n_active > iwl_mvm_max_active_links(mvm, vif)) - return -EOPNOTSUPP; - for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { int r; @@ -1223,6 +1215,15 @@ iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw, return ret; } +static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 desired_links) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + return hweight16(desired_links) <= iwl_mvm_max_active_links(mvm, vif); +} + const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .tx = iwl_mvm_mac_tx, .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, @@ -1317,4 +1318,5 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .change_vif_links = iwl_mvm_mld_change_vif_links, .change_sta_links = iwl_mvm_mld_change_sta_links, + .can_activate_links = iwl_mvm_mld_can_activate_links, }; From fdccafad7e9b49e75fb484c5aa29954d34f3c63a Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 23 Jan 2024 20:08:23 +0200 Subject: [PATCH 105/378] wifi: iwlwifi: add support for a wiphy_work rx handler The wiphy_work infra ensures that the entire worker will run with the wiphy mutex. It is useful to have RX handlers running as a wiphy_work, when we don't want the handler to run in parallel with mac80211 work (to avoid races). For example - BT notification can disable eSR starting from the next patch. In ieee80211_set_active_links we first check that eSR is allowed, (drv_can_activate_links) and then activate it. If the BT notif was received after drv_can_activate_links (which returned true), and before the activation - eSR will be activated when it shouldn't. If BT notif is handled with the wiphy mutex, it can't run in parallel to ieee80211_set_active_links, which also holds that mutex. Add the necessary infrastructure here, for use in the next commit. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Reviewed-by: Johannes Berg Link: https://msgid.link/20240123200528.ce83d16cdec8.I35ef53fa23f58b9ec17924099238b61deafcecd7@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 61 +++++++++++++++---- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 6bbcf4092f52..406956574f52 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1342,6 +1342,7 @@ void iwl_mvm_mac_stop(struct ieee80211_hw *hw) * discover that its list is now empty. */ cancel_work_sync(&mvm->async_handlers_wk); + wiphy_work_cancel(hw->wiphy, &mvm->async_handlers_wiphy_wk); } struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 997f0395b97a..af5c8b4bb5a6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -848,6 +848,9 @@ struct iwl_mvm { spinlock_t async_handlers_lock; struct work_struct async_handlers_wk; + /* For async rx handlers that require the wiphy lock */ + struct wiphy_work async_handlers_wiphy_wk; + struct work_struct roc_done_wk; unsigned long init_status; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index adbbe19aeae5..38a84a54ff78 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -267,11 +267,15 @@ static void iwl_mvm_rx_thermal_dual_chain_req(struct iwl_mvm *mvm, * it will be called from a worker with mvm->mutex held. * @RX_HANDLER_ASYNC_UNLOCKED : in case the handler needs to lock the * mutex itself, it will be called from a worker without mvm->mutex held. + * @RX_HANDLER_ASYNC_LOCKED_WIPHY: If the handler needs to hold the wiphy lock + * and mvm->mutex. Will be handled with the wiphy_work queue infra + * instead of regular work queue. */ enum iwl_rx_handler_context { RX_HANDLER_SYNC, RX_HANDLER_ASYNC_LOCKED, RX_HANDLER_ASYNC_UNLOCKED, + RX_HANDLER_ASYNC_LOCKED_WIPHY, }; /** @@ -673,6 +677,8 @@ static const struct iwl_hcmd_arr iwl_mvm_groups[] = { /* this forward declaration can avoid to export the function */ static void iwl_mvm_async_handlers_wk(struct work_struct *wk); +static void iwl_mvm_async_handlers_wiphy_wk(struct wiphy *wiphy, + struct wiphy_work *work); static u32 iwl_mvm_min_backoff(struct iwl_mvm *mvm) { @@ -1265,6 +1271,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, INIT_LIST_HEAD(&mvm->add_stream_txqs); spin_lock_init(&mvm->add_stream_lock); + wiphy_work_init(&mvm->async_handlers_wiphy_wk, + iwl_mvm_async_handlers_wiphy_wk); init_waitqueue_head(&mvm->rx_sync_waitq); mvm->queue_sync_state = 0; @@ -1551,35 +1559,62 @@ void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm) spin_unlock_bh(&mvm->async_handlers_lock); } -static void iwl_mvm_async_handlers_wk(struct work_struct *wk) +/* + * This function receives a bitmap of rx async handler contexts + * (&iwl_rx_handler_context) to handle, and runs only them + */ +static void iwl_mvm_async_handlers_by_context(struct iwl_mvm *mvm, + u8 contexts) { - struct iwl_mvm *mvm = - container_of(wk, struct iwl_mvm, async_handlers_wk); struct iwl_async_handler_entry *entry, *tmp; LIST_HEAD(local_list); - /* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */ - /* - * Sync with Rx path with a lock. Remove all the entries from this list, - * add them to a local one (lock free), and then handle them. + * Sync with Rx path with a lock. Remove all the entries of the + * wanted contexts from this list, add them to a local one (lock free), + * and then handle them. */ spin_lock_bh(&mvm->async_handlers_lock); - list_splice_init(&mvm->async_handlers_list, &local_list); + list_for_each_entry_safe(entry, tmp, &mvm->async_handlers_list, list) { + if (!(BIT(entry->context) & contexts)) + continue; + list_del(&entry->list); + list_add_tail(&entry->list, &local_list); + } spin_unlock_bh(&mvm->async_handlers_lock); list_for_each_entry_safe(entry, tmp, &local_list, list) { - if (entry->context == RX_HANDLER_ASYNC_LOCKED) + if (entry->context != RX_HANDLER_ASYNC_UNLOCKED) mutex_lock(&mvm->mutex); entry->fn(mvm, &entry->rxb); iwl_free_rxb(&entry->rxb); list_del(&entry->list); - if (entry->context == RX_HANDLER_ASYNC_LOCKED) + if (entry->context != RX_HANDLER_ASYNC_UNLOCKED) mutex_unlock(&mvm->mutex); kfree(entry); } } +static void iwl_mvm_async_handlers_wiphy_wk(struct wiphy *wiphy, + struct wiphy_work *wk) +{ + struct iwl_mvm *mvm = + container_of(wk, struct iwl_mvm, async_handlers_wiphy_wk); + u8 contexts = BIT(RX_HANDLER_ASYNC_LOCKED_WIPHY); + + iwl_mvm_async_handlers_by_context(mvm, contexts); +} + +static void iwl_mvm_async_handlers_wk(struct work_struct *wk) +{ + struct iwl_mvm *mvm = + container_of(wk, struct iwl_mvm, async_handlers_wk); + u8 contexts = BIT(RX_HANDLER_ASYNC_LOCKED) | + BIT(RX_HANDLER_ASYNC_UNLOCKED); + + iwl_mvm_async_handlers_by_context(mvm, contexts); +} + static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { @@ -1659,7 +1694,11 @@ static void iwl_mvm_rx_common(struct iwl_mvm *mvm, spin_lock(&mvm->async_handlers_lock); list_add_tail(&entry->list, &mvm->async_handlers_list); spin_unlock(&mvm->async_handlers_lock); - schedule_work(&mvm->async_handlers_wk); + if (rx_h->context == RX_HANDLER_ASYNC_LOCKED_WIPHY) + wiphy_work_queue(mvm->hw->wiphy, + &mvm->async_handlers_wiphy_wk); + else + schedule_work(&mvm->async_handlers_wk); break; } } From a923ff876f4b6133a093482a6d465cde3bc2e65c Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 25 Jan 2024 14:55:47 -0800 Subject: [PATCH 106/378] Revert "nl80211/cfg80211: Specify band specific min RSSI thresholds with sched scan" This *mostly* reverts commit 1e1b11b6a111 ("nl80211/cfg80211: Specify band specific min RSSI thresholds with sched scan"). During the review of a new patch [1] it was observed that the functionality being modified was not actually being used by any in-tree driver. Further research determined that the functionality was originally introduced to support a new Android interface, but that interface was subsequently abandoned. Since the functionality has apparently never been used, remove it. However, to mantain the sanctity of the UABI, keep the nl80211.h assignments, but clearly mark them as obsolete. Cc: Lin Ma Cc: Vamsi Krishna Link: https://lore.kernel.org/linux-wireless/20240119151201.8670-1-linma@zju.edu.cn/ [1] Signed-off-by: Jeff Johnson Link: https://msgid.link/20240125-for-next-v1-1-fd79e01c6c09@quicinc.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 ------ include/uapi/linux/nl80211.h | 16 +++-------- net/wireless/nl80211.c | 55 ------------------------------------ 3 files changed, 4 insertions(+), 75 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 56bce924bec6..51b9e6fa12f8 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2695,19 +2695,11 @@ static inline void get_random_mask_addr(u8 *buf, const u8 *addr, const u8 *mask) * @bssid: BSSID to be matched; may be all-zero BSSID in case of SSID match * or no match (RSSI only) * @rssi_thold: don't report scan results below this threshold (in s32 dBm) - * @per_band_rssi_thold: Minimum rssi threshold for each band to be applied - * for filtering out scan results received. Drivers advertise this support - * of band specific rssi based filtering through the feature capability - * %NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD. These band - * specific rssi thresholds take precedence over rssi_thold, if specified. - * If not specified for any band, it will be assigned with rssi_thold of - * corresponding matchset. */ struct cfg80211_match_set { struct cfg80211_ssid ssid; u8 bssid[ETH_ALEN]; s32 rssi_thold; - s32 per_band_rssi_thold[NUM_NL80211_BANDS]; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 3e239df3528f..853ac538a686 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4463,14 +4463,7 @@ enum nl80211_reg_rule_attr { * value as specified by &struct nl80211_bss_select_rssi_adjust. * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching * (this cannot be used together with SSID). - * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the - * band specific minimum rssi thresholds for the bands defined in - * enum nl80211_band. The minimum rssi threshold value(s32) specific to a - * band shall be encapsulated in attribute with type value equals to one - * of the NL80211_BAND_* defined in enum nl80211_band. For example, the - * minimum rssi threshold value for 2.4GHZ band shall be encapsulated - * within an attribute of type NL80211_BAND_2GHZ. And one or more of such - * attributes will be nested within this attribute. + * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Obsolete * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -4483,7 +4476,7 @@ enum nl80211_sched_scan_match_attr { NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST, NL80211_SCHED_SCAN_MATCH_ATTR_BSSID, - NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, + NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, /* obsolete */ /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, @@ -6418,8 +6411,7 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching * (set/del PMKSA operations) in AP mode. * - * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports - * filtering of sched scan results using band specific RSSI thresholds. + * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Obsolete * * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power * to a station. @@ -6574,7 +6566,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS, NL80211_EXT_FEATURE_AP_PMKSA_CACHING, - NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, + NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, /* obsolete */ NL80211_EXT_FEATURE_EXT_KEY_ID, NL80211_EXT_FEATURE_STA_TX_PWR, NL80211_EXT_FEATURE_SAE_OFFLOAD, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0809f721f045..e4f41f86e295 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -906,23 +906,12 @@ nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { [NL80211_REKEY_DATA_AKM] = { .type = NLA_U32 }, }; -static const struct nla_policy -nl80211_match_band_rssi_policy[NUM_NL80211_BANDS] = { - [NL80211_BAND_2GHZ] = { .type = NLA_S32 }, - [NL80211_BAND_5GHZ] = { .type = NLA_S32 }, - [NL80211_BAND_6GHZ] = { .type = NLA_S32 }, - [NL80211_BAND_60GHZ] = { .type = NLA_S32 }, - [NL80211_BAND_LC] = { .type = NLA_S32 }, -}; - static const struct nla_policy nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY, .len = IEEE80211_MAX_SSID_LEN }, [NL80211_SCHED_SCAN_MATCH_ATTR_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 }, - [NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI] = - NLA_POLICY_NESTED(nl80211_match_band_rssi_policy), }; static const struct nla_policy @@ -9490,41 +9479,6 @@ nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans, return 0; } -static int -nl80211_parse_sched_scan_per_band_rssi(struct wiphy *wiphy, - struct cfg80211_match_set *match_sets, - struct nlattr *tb_band_rssi, - s32 rssi_thold) -{ - struct nlattr *attr; - int i, tmp, ret = 0; - - if (!wiphy_ext_feature_isset(wiphy, - NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD)) { - if (tb_band_rssi) - ret = -EOPNOTSUPP; - else - for (i = 0; i < NUM_NL80211_BANDS; i++) - match_sets->per_band_rssi_thold[i] = - NL80211_SCAN_RSSI_THOLD_OFF; - return ret; - } - - for (i = 0; i < NUM_NL80211_BANDS; i++) - match_sets->per_band_rssi_thold[i] = rssi_thold; - - nla_for_each_nested(attr, tb_band_rssi, tmp) { - enum nl80211_band band = nla_type(attr); - - if (band < 0 || band >= NUM_NL80211_BANDS) - return -EINVAL; - - match_sets->per_band_rssi_thold[band] = nla_get_s32(attr); - } - - return 0; -} - static struct cfg80211_sched_scan_request * nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, struct nlattr **attrs, int max_match_sets) @@ -9799,15 +9753,6 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, if (rssi) request->match_sets[i].rssi_thold = nla_get_s32(rssi); - - /* Parse per band RSSI attribute */ - err = nl80211_parse_sched_scan_per_band_rssi(wiphy, - &request->match_sets[i], - tb[NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI], - request->match_sets[i].rssi_thold); - if (err) - goto out_free; - i++; } From 28b3df1fe6ba2cb439ba109f095aa841fef3a54f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 26 Jan 2024 10:34:17 +0200 Subject: [PATCH 107/378] kunit: add wireless unit tests Add cfg80211, mac80211 and iwlwifi to the all_tests config so that the unit tests (enabled by KUNIT_ALL_TESTS) will be run by default. Signed-off-by: Johannes Berg --- tools/testing/kunit/configs/all_tests.config | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/testing/kunit/configs/all_tests.config b/tools/testing/kunit/configs/all_tests.config index 3bf506d4a63c..12da4c493867 100644 --- a/tools/testing/kunit/configs/all_tests.config +++ b/tools/testing/kunit/configs/all_tests.config @@ -27,6 +27,11 @@ CONFIG_MCTP=y CONFIG_INET=y CONFIG_MPTCP=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_WLAN_VENDOR_INTEL=y +CONFIG_IWLWIFI=y + CONFIG_DAMON=y CONFIG_DAMON_VADDR=y CONFIG_DAMON_PADDR=y From 26f0dc8a705ae182eaa126cef0a9870d1a87a5ac Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 28 Jan 2024 10:30:56 +0100 Subject: [PATCH 108/378] wifi: brcmfmac: add linefeed at end of file The following sparse warning was reported: drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c:432:49: warning: no newline at end of file Fixes: 31343230abb1 ("wifi: brcmfmac: export firmware interface functions") Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/all/20240125165128.7e43a1f3@kernel.org/ Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://msgid.link/20240128093057.164791-2-arend.vanspriel@broadcom.com --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c index bc1c6b5a6e31..6385a7db7f7d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c @@ -429,4 +429,4 @@ s32 brcmf_fil_xtlv_data_get(struct brcmf_if *ifp, const char *name, u16 id, mutex_unlock(&drvr->proto_block); return err; } -BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_get); \ No newline at end of file +BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_xtlv_data_get); From 2a71528427c635f0a8bff704b2e62ce81c641d6f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 28 Jan 2024 10:30:57 +0100 Subject: [PATCH 109/378] wifi: brcmfmac: fix copyright year mentioned in platform_data header The driver found its inception a little after the year 201. According git blame output it was added in 2016 so lets go with that. Fixes: 4d7928959832 ("brcmfmac: switch to new platform data") Reported-by: Dmitry Antipov Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://msgid.link/20240128093057.164791-3-arend.vanspriel@broadcom.com --- include/linux/platform_data/brcmfmac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/platform_data/brcmfmac.h b/include/linux/platform_data/brcmfmac.h index f922a192fe58..ec99b7b73d1d 100644 --- a/include/linux/platform_data/brcmfmac.h +++ b/include/linux/platform_data/brcmfmac.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 201 Broadcom Corporation + * Copyright (c) 2016 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above From 57b9426952c46f8d0aa7fad27fe55403fb28974f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 21 Jan 2024 15:18:23 +0800 Subject: [PATCH 110/378] wifi: rtw89: pci: update SER timer unit and timeout time Be higher resolution of SER timer unit from 32ms to 16ms to detect abnormal situation more accurately, and set hardware watchdog timer to 4ms. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240121071826.10159-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci_be.c | 5 +++++ drivers/net/wireless/realtek/rtw89/reg.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index 629ffa4bee91..5c9e39357773 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -105,6 +105,10 @@ static void rtw89_pci_ctrl_trxdma_pcie_be(struct rtw89_dev *rtwdev, val |= B_BE_STOP_AXI_MST; rtw89_write32(rtwdev, R_BE_HAXI_INIT_CFG1, val); + + if (io_en == MAC_AX_PCIE_ENABLE) + rtw89_write32_mask(rtwdev, R_BE_HAXI_MST_WDT_TIMEOUT_SEL_V1, + B_BE_HAXI_MST_WDT_TIMEOUT_SEL_MASK, 4); } static void rtw89_pci_clr_idx_all_be(struct rtw89_dev *rtwdev) @@ -257,6 +261,7 @@ static void rtw89_pci_ser_setting_be(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_BE_PL1_DBG_INFO, 0x0); rtw89_write32_set(rtwdev, R_BE_FWS1IMR, B_BE_PCIE_SER_TIMEOUT_INDIC_EN); rtw89_write32_set(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN); + rtw89_write32_mask(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_TIMER_UNIT_MASK, 1); val32 = rtw89_read32(rtwdev, R_BE_REG_PL1_MASK); val32 |= B_BE_SER_PMU_IMR | B_BE_SER_L1SUB_IMR | B_BE_SER_PM_MASTER_IMR | diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index acc96d30d085..ec2559bde092 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -5800,6 +5800,9 @@ #define B_BE_STOP_CH1 BIT(1) #define B_BE_STOP_CH0 BIT(0) +#define R_BE_HAXI_MST_WDT_TIMEOUT_SEL_V1 0xB02C +#define B_BE_HAXI_MST_WDT_TIMEOUT_SEL_MASK GENMASK(4, 0) + #define R_BE_HAXI_IDCT_MSK 0xB0B8 #define B_BE_HAXI_RRESP_ERR_IDCT_MSK BIT(7) #define B_BE_HAXI_BRESP_ERR_IDCT_MSK BIT(6) From 26cdaee43dc5f9c9d0c5429b365b8f094afad717 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 21 Jan 2024 15:18:24 +0800 Subject: [PATCH 111/378] wifi: rtw89: pci: interrupt v2 refine IMR for SER During SER (system error recovery), expect to deal with only ISR related to halt. So, refine IMR configuration. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240121071826.10159-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 9943ed856248..8227dc55e818 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -705,7 +705,7 @@ void rtw89_pci_recognize_intrs_v2(struct rtw89_dev *rtwdev, rtw89_read32(rtwdev, R_BE_HISR0) & rtwpci->halt_c2h_intrs : 0; isrs->isrs[0] = isrs->ind_isrs & B_BE_HCI_AXIDMA_INT ? rtw89_read32(rtwdev, R_BE_HAXI_HISR00) & rtwpci->intrs[0] : 0; - isrs->isrs[1] = rtw89_read32(rtwdev, R_BE_PCIE_DMA_ISR); + isrs->isrs[1] = rtw89_read32(rtwdev, R_BE_PCIE_DMA_ISR) & rtwpci->intrs[1]; if (isrs->halt_c2h_isrs) rtw89_write32(rtwdev, R_BE_HISR0, isrs->halt_c2h_isrs); @@ -3452,8 +3452,7 @@ static void rtw89_pci_recovery_intr_mask_v2(struct rtw89_dev *rtwdev) rtwpci->ind_intrs = B_BE_HS0_IND_INT_EN0; rtwpci->halt_c2h_intrs = B_BE_HALT_C2H_INT_EN | B_BE_WDT_TIMEOUT_INT_EN; rtwpci->intrs[0] = 0; - rtwpci->intrs[1] = B_BE_PCIE_RX_RX0P2_IMR0_V1 | - B_BE_PCIE_RX_RPQ0_IMR0_V1; + rtwpci->intrs[1] = 0; } static void rtw89_pci_default_intr_mask_v2(struct rtw89_dev *rtwdev) From 0bc7d1d4e63cf31ff1b4396b0e2f0e3c76828d26 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 21 Jan 2024 15:18:25 +0800 Subject: [PATCH 112/378] wifi: rtw89: pci: validate RX tag for RXQ and RPQ PCI RX ring is a kind of read/write index ring, and DMA and ring index are asynchronous, so suddenly driver gets newer index ahead before DMA. To resolve this rare situation, we use a RX tag as helpers to make sure DMA is done. The RX tag is a 13-bit value, and range is from 1 ~ 0x1FFF, but 0 isn't used so should be skipped. Only enable this validation to coming WiFi 7 chips, because existing chips use different design and don't really meet this situation. Add missed rx_ring_eq_is_full for 8851BE by the way. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240121071826.10159-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.c | 60 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/pci.h | 4 +- .../net/wireless/realtek/rtw89/rtw8851be.c | 2 + .../net/wireless/realtek/rtw89/rtw8852ae.c | 1 + .../net/wireless/realtek/rtw89/rtw8852be.c | 1 + .../net/wireless/realtek/rtw89/rtw8852ce.c | 1 + .../net/wireless/realtek/rtw89/rtw8922ae.c | 1 + 7 files changed, 63 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 8227dc55e818..b51ec3cbc715 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -155,8 +155,8 @@ static void rtw89_pci_sync_skb_for_device(struct rtw89_dev *rtwdev, DMA_FROM_DEVICE); } -static int rtw89_pci_rxbd_info_update(struct rtw89_dev *rtwdev, - struct sk_buff *skb) +static void rtw89_pci_rxbd_info_update(struct rtw89_dev *rtwdev, + struct sk_buff *skb) { struct rtw89_pci_rxbd_info *rxbd_info; struct rtw89_pci_rx_info *rx_info = RTW89_PCI_RX_SKB_CB(skb); @@ -166,10 +166,58 @@ static int rtw89_pci_rxbd_info_update(struct rtw89_dev *rtwdev, rx_info->ls = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_LS); rx_info->len = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_WRITE_SIZE); rx_info->tag = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_TAG); +} + +static int rtw89_pci_validate_rx_tag(struct rtw89_dev *rtwdev, + struct rtw89_pci_rx_ring *rx_ring, + struct sk_buff *skb) +{ + struct rtw89_pci_rx_info *rx_info = RTW89_PCI_RX_SKB_CB(skb); + const struct rtw89_pci_info *info = rtwdev->pci_info; + u32 target_rx_tag; + + if (!info->check_rx_tag) + return 0; + + /* valid range is 1 ~ 0x1FFF */ + if (rx_ring->target_rx_tag == 0) + target_rx_tag = 1; + else + target_rx_tag = rx_ring->target_rx_tag; + + if (rx_info->tag != target_rx_tag) { + rtw89_debug(rtwdev, RTW89_DBG_UNEXP, "mismatch RX tag 0x%x 0x%x\n", + rx_info->tag, target_rx_tag); + return -EAGAIN; + } return 0; } +static +int rtw89_pci_sync_skb_for_device_and_validate_rx_info(struct rtw89_dev *rtwdev, + struct rtw89_pci_rx_ring *rx_ring, + struct sk_buff *skb) +{ + struct rtw89_pci_rx_info *rx_info = RTW89_PCI_RX_SKB_CB(skb); + int rx_tag_retry = 100; + int ret; + + do { + rtw89_pci_sync_skb_for_cpu(rtwdev, skb); + rtw89_pci_rxbd_info_update(rtwdev, skb); + + ret = rtw89_pci_validate_rx_tag(rtwdev, rx_ring, skb); + if (ret != -EAGAIN) + break; + } while (rx_tag_retry--); + + /* update target rx_tag for next RX */ + rx_ring->target_rx_tag = rx_info->tag + 1; + + return ret; +} + static void rtw89_pci_ctrl_txdma_ch_pcie(struct rtw89_dev *rtwdev, bool enable) { const struct rtw89_pci_info *info = rtwdev->pci_info; @@ -259,9 +307,8 @@ static u32 rtw89_pci_rxbd_deliver_skbs(struct rtw89_dev *rtwdev, skb_idx = rtw89_pci_get_rx_skb_idx(rtwdev, bd_ring); skb = rx_ring->buf[skb_idx]; - rtw89_pci_sync_skb_for_cpu(rtwdev, skb); - ret = rtw89_pci_rxbd_info_update(rtwdev, skb); + ret = rtw89_pci_sync_skb_for_device_and_validate_rx_info(rtwdev, rx_ring, skb); if (ret) { rtw89_err(rtwdev, "failed to update %d RXBD info: %d\n", bd_ring->wp, ret); @@ -549,9 +596,8 @@ static u32 rtw89_pci_release_tx_skbs(struct rtw89_dev *rtwdev, skb_idx = rtw89_pci_get_rx_skb_idx(rtwdev, bd_ring); skb = rx_ring->buf[skb_idx]; - rtw89_pci_sync_skb_for_cpu(rtwdev, skb); - ret = rtw89_pci_rxbd_info_update(rtwdev, skb); + ret = rtw89_pci_sync_skb_for_device_and_validate_rx_info(rtwdev, rx_ring, skb); if (ret) { rtw89_err(rtwdev, "failed to update %d RXBD info: %d\n", bd_ring->wp, ret); @@ -1550,6 +1596,7 @@ static void rtw89_pci_reset_trx_rings(struct rtw89_dev *rtwdev) bd_ring->rp = 0; rx_ring->diliver_skb = NULL; rx_ring->diliver_desc.ready = false; + rx_ring->target_rx_tag = 0; rtw89_write16(rtwdev, addr_num, bd_ring->len); rtw89_write32(rtwdev, addr_desa_l, bd_ring->dma); @@ -3213,6 +3260,7 @@ static int rtw89_pci_alloc_rx_ring(struct rtw89_dev *rtwdev, rx_ring->buf_sz = buf_sz; rx_ring->diliver_skb = NULL; rx_ring->diliver_desc.ready = false; + rx_ring->target_rx_tag = 0; for (i = 0; i < len; i++) { skb = dev_alloc_skb(buf_sz); diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 1fb7c209fa0d..0543221b4c58 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -1235,6 +1235,7 @@ struct rtw89_pci_info { enum mac_ax_pcie_func_ctrl io_rcy_en; enum mac_ax_io_rcy_tmr io_rcy_tmr; bool rx_ring_eq_is_full; + bool check_rx_tag; u32 init_cfg_reg; u32 txhci_en_bit; @@ -1277,7 +1278,7 @@ struct rtw89_pci_tx_data { struct rtw89_pci_rx_info { dma_addr_t dma; - u32 fs:1, ls:1, tag:11, len:14; + u32 fs:1, ls:1, tag:13, len:14; }; #define RTW89_PCI_TXBD_OPTION_LS BIT(14) @@ -1406,6 +1407,7 @@ struct rtw89_pci_rx_ring { u32 buf_sz; struct sk_buff *diliver_skb; struct rtw89_rx_desc_info diliver_desc; + u32 target_rx_tag:13; }; struct rtw89_pci_isrs { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c index ade69bd30fc8..ca1374a71727 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c @@ -25,6 +25,8 @@ static const struct rtw89_pci_info rtw8851b_pci_info = { .autok_en = MAC_AX_PCIE_DISABLE, .io_rcy_en = MAC_AX_PCIE_DISABLE, .io_rcy_tmr = MAC_AX_IO_RCY_ANA_TMR_6MS, + .rx_ring_eq_is_full = false, + .check_rx_tag = false, .init_cfg_reg = R_AX_PCIE_INIT_CFG1, .txhci_en_bit = B_AX_TXHCI_EN, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c index f1e890bde049..7c6ffedb77e2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c @@ -26,6 +26,7 @@ static const struct rtw89_pci_info rtw8852a_pci_info = { .io_rcy_en = MAC_AX_PCIE_DISABLE, .io_rcy_tmr = MAC_AX_IO_RCY_ANA_TMR_6MS, .rx_ring_eq_is_full = false, + .check_rx_tag = false, .init_cfg_reg = R_AX_PCIE_INIT_CFG1, .txhci_en_bit = B_AX_TXHCI_EN, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c index 920b20bbcfb7..ed71364e6437 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c @@ -26,6 +26,7 @@ static const struct rtw89_pci_info rtw8852b_pci_info = { .io_rcy_en = MAC_AX_PCIE_DISABLE, .io_rcy_tmr = MAC_AX_IO_RCY_ANA_TMR_6MS, .rx_ring_eq_is_full = false, + .check_rx_tag = false, .init_cfg_reg = R_AX_PCIE_INIT_CFG1, .txhci_en_bit = B_AX_TXHCI_EN, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c index 4592de3dbd94..583ea673a4f5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c @@ -35,6 +35,7 @@ static const struct rtw89_pci_info rtw8852c_pci_info = { .io_rcy_en = MAC_AX_PCIE_ENABLE, .io_rcy_tmr = MAC_AX_IO_RCY_ANA_TMR_6MS, .rx_ring_eq_is_full = false, + .check_rx_tag = false, .init_cfg_reg = R_AX_HAXI_INIT_CFG1, .txhci_en_bit = B_AX_TXHCI_EN_V1, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c index 7b3d98d2c402..9f46fb166105 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c @@ -26,6 +26,7 @@ static const struct rtw89_pci_info rtw8922a_pci_info = { .io_rcy_en = MAC_AX_PCIE_ENABLE, .io_rcy_tmr = MAC_AX_IO_RCY_ANA_TMR_DEF, .rx_ring_eq_is_full = true, + .check_rx_tag = true, .init_cfg_reg = R_BE_HAXI_INIT_CFG1, .txhci_en_bit = B_BE_TXDMA_EN, From c108b4a50dd7650941d4f4ec5c161655a73711db Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 21 Jan 2024 15:18:26 +0800 Subject: [PATCH 113/378] wifi: rtw89: pci: enlarge RX DMA buffer to consider size of RX descriptor Hardware puts RX descriptor and packet in RX DMA buffer, so it could be over one buffer size if packet size is 11454, and then it will be split into two segments. WiFi 7 chips use larger size of RX descriptor, so enlarge DMA buffer size according to RX descriptor to have better performance and simple flow. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240121071826.10159-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 0543221b4c58..532f78eaf6df 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -997,7 +997,7 @@ #define RTW89_PCI_TXWD_NUM_MAX 512 #define RTW89_PCI_TXWD_PAGE_SIZE 128 #define RTW89_PCI_ADDRINFO_MAX 4 -#define RTW89_PCI_RX_BUF_SIZE 11460 +#define RTW89_PCI_RX_BUF_SIZE (11454 + 40) /* +40 for rtw89_rxdesc_long_v2 */ #define RTW89_PCI_POLL_BDRAM_RST_CNT 100 #define RTW89_PCI_MULTITAG 8 From f8a7840e98a440f466954c0b9eed99a9f064a564 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 24 Jan 2024 11:36:32 +0800 Subject: [PATCH 114/378] wifi: rtw89: 8922a: hook handlers of TX/RX descriptors to chip_ops Hook implemented handlers to chip_ops, and fill packet frequency and signal strength to RX status from RX PPDU status packet. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240124033637.12330-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index f34e2a8bff07..9c7465d0715b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1250,6 +1250,39 @@ static void rtw8922a_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en, } } +static void rtw8922a_fill_freq_with_ppdu(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu, + struct ieee80211_rx_status *status) +{ + u8 chan_idx = phy_ppdu->chan_idx; + enum nl80211_band band; + u8 ch; + + if (chan_idx == 0) + return; + + rtw89_decode_chan_idx(rtwdev, chan_idx, &ch, &band); + status->freq = ieee80211_channel_to_frequency(ch, band); + status->band = band; +} + +static void rtw8922a_query_ppdu(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu, + struct ieee80211_rx_status *status) +{ + u8 path; + u8 *rx_power = phy_ppdu->rssi; + + status->signal = + RTW89_RSSI_RAW_TO_DBM(max(rx_power[RF_PATH_A], rx_power[RF_PATH_B])); + for (path = 0; path < rtwdev->chip->rf_path_num; path++) { + status->chains |= BIT(path); + status->chain_signal[path] = RTW89_RSSI_RAW_TO_DBM(rx_power[path]); + } + if (phy_ppdu->valid) + rtw8922a_fill_freq_with_ppdu(rtwdev, phy_ppdu, status); +} + static int rtw8922a_mac_enable_bb_rf(struct rtw89_dev *rtwdev) { rtw89_write8_set(rtwdev, R_BE_FEN_RST_ENABLE, @@ -1291,10 +1324,14 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .set_txpwr_ctrl = rtw8922a_set_txpwr_ctrl, .init_txpwr_unit = NULL, .ctrl_btg_bt_rx = rtw8922a_ctrl_btg_bt_rx, + .query_ppdu = rtw8922a_query_ppdu, .ctrl_nbtg_bt_tx = rtw8922a_ctrl_nbtg_bt_tx, .set_txpwr_ul_tb_offset = NULL, .pwr_on_func = rtw8922a_pwr_on_func, .pwr_off_func = rtw8922a_pwr_off_func, + .query_rxdesc = rtw89_core_query_rxdesc_v2, + .fill_txdesc = rtw89_core_fill_txdesc_v2, + .fill_txdesc_fwcmd = rtw89_core_fill_txdesc_fwcmd_v2, .h2c_dctl_sec_cam = rtw89_fw_h2c_dctl_sec_cam_v2, .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl_g7, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl_g7, From b16daa62125e3f841a135c37dfd996cdf7e7960d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 24 Jan 2024 11:36:33 +0800 Subject: [PATCH 115/378] wifi: rtw89: 8922a: implement {stop,resume}_sch_tx and cfg_ppdu To set TX/RX path or set channel, we need these helpers to stop TX and restore settings. The sch_tx stands for scheduler TX channel, and the cfg_ppdu is to stop reporting PPDU status, so we should stop them during setting. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240124033637.12330-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 5 +- drivers/net/wireless/realtek/rtw89/mac.h | 14 ++- drivers/net/wireless/realtek/rtw89/mac_be.c | 96 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/reg.h | 44 +++++++++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 + 5 files changed, 158 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index eb94e832e154..d1d16564a686 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5205,7 +5205,8 @@ bool rtw89_mac_get_txpwr_cr_ax(struct rtw89_dev *rtwdev, return false; } -int rtw89_mac_cfg_ppdu_status(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable) +static +int rtw89_mac_cfg_ppdu_status_ax(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable) { u32 reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_PPDU_STAT, mac_idx); int ret; @@ -5228,7 +5229,6 @@ int rtw89_mac_cfg_ppdu_status(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable) return 0; } -EXPORT_SYMBOL(rtw89_mac_cfg_ppdu_status); void rtw89_mac_update_rts_threshold(struct rtw89_dev *rtwdev, u8 mac_idx) { @@ -6179,6 +6179,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .bf_assoc = rtw89_mac_bf_assoc_ax, .typ_fltr_opt = rtw89_mac_typ_fltr_opt_ax, + .cfg_ppdu_status = rtw89_mac_cfg_ppdu_status_ax, .dle_mix_cfg = dle_mix_cfg_ax, .chk_dle_rdy = chk_dle_rdy_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 181d03d1f78a..66bac01eb067 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -913,6 +913,7 @@ struct rtw89_mac_gen_def { enum rtw89_machdr_frame_type type, enum rtw89_mac_fwd_target fwd_target, u8 mac_idx); + int (*cfg_ppdu_status)(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable); int (*dle_mix_cfg)(struct rtw89_dev *rtwdev, const struct rtw89_dle_mem *cfg); int (*chk_dle_rdy)(struct rtw89_dev *rtwdev, bool wde_or_ple); @@ -1138,9 +1139,20 @@ int rtw89_mac_stop_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, u32 *tx_en, enum rtw89_sch_tx_sel sel); int rtw89_mac_stop_sch_tx_v1(struct rtw89_dev *rtwdev, u8 mac_idx, u32 *tx_en, enum rtw89_sch_tx_sel sel); +int rtw89_mac_stop_sch_tx_v2(struct rtw89_dev *rtwdev, u8 mac_idx, + u32 *tx_en, enum rtw89_sch_tx_sel sel); int rtw89_mac_resume_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en); int rtw89_mac_resume_sch_tx_v1(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en); -int rtw89_mac_cfg_ppdu_status(struct rtw89_dev *rtwdev, u8 mac_ids, bool enable); +int rtw89_mac_resume_sch_tx_v2(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en); + +static inline +int rtw89_mac_cfg_ppdu_status(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + + return mac->cfg_ppdu_status(rtwdev, mac_idx, enable); +} + void rtw89_mac_update_rts_threshold(struct rtw89_dev *rtwdev, u8 mac_idx); void rtw89_mac_flush_txq(struct rtw89_dev *rtwdev, u32 queues, bool drop); int rtw89_mac_coex_init(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex *coex); diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 4befbe06cd15..f03d05a2f857 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1718,6 +1718,101 @@ static int trx_init_be(struct rtw89_dev *rtwdev) return 0; } +static int rtw89_set_hw_sch_tx_en_v2(struct rtw89_dev *rtwdev, u8 mac_idx, + u32 tx_en, u32 tx_en_mask) +{ + u32 reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_CTN_DRV_TXEN, mac_idx); + u32 val; + int ret; + + ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL); + if (ret) + return ret; + + val = rtw89_read32(rtwdev, reg); + val = (val & ~tx_en_mask) | (tx_en & tx_en_mask); + rtw89_write32(rtwdev, reg, val); + + return 0; +} + +int rtw89_mac_stop_sch_tx_v2(struct rtw89_dev *rtwdev, u8 mac_idx, + u32 *tx_en, enum rtw89_sch_tx_sel sel) +{ + int ret; + + *tx_en = rtw89_read32(rtwdev, + rtw89_mac_reg_by_idx(rtwdev, R_BE_CTN_DRV_TXEN, mac_idx)); + + switch (sel) { + case RTW89_SCH_TX_SEL_ALL: + ret = rtw89_set_hw_sch_tx_en_v2(rtwdev, mac_idx, 0, + B_BE_CTN_TXEN_ALL_MASK); + if (ret) + return ret; + break; + case RTW89_SCH_TX_SEL_HIQ: + ret = rtw89_set_hw_sch_tx_en_v2(rtwdev, mac_idx, + 0, B_BE_CTN_TXEN_HGQ); + if (ret) + return ret; + break; + case RTW89_SCH_TX_SEL_MG0: + ret = rtw89_set_hw_sch_tx_en_v2(rtwdev, mac_idx, + 0, B_BE_CTN_TXEN_MGQ); + if (ret) + return ret; + break; + case RTW89_SCH_TX_SEL_MACID: + ret = rtw89_set_hw_sch_tx_en_v2(rtwdev, mac_idx, 0, + B_BE_CTN_TXEN_ALL_MASK); + if (ret) + return ret; + break; + default: + return 0; + } + + return 0; +} +EXPORT_SYMBOL(rtw89_mac_stop_sch_tx_v2); + +int rtw89_mac_resume_sch_tx_v2(struct rtw89_dev *rtwdev, u8 mac_idx, u32 tx_en) +{ + int ret; + + ret = rtw89_set_hw_sch_tx_en_v2(rtwdev, mac_idx, tx_en, + B_BE_CTN_TXEN_ALL_MASK); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(rtw89_mac_resume_sch_tx_v2); + +static +int rtw89_mac_cfg_ppdu_status_be(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable) +{ + u32 reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_PPDU_STAT, mac_idx); + int ret; + + ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL); + if (ret) + return ret; + + if (!enable) { + rtw89_write32_clr(rtwdev, reg, B_BE_PPDU_STAT_RPT_EN); + return 0; + } + + rtw89_write32_mask(rtwdev, R_BE_HW_PPDU_STATUS, B_BE_FWD_PPDU_STAT_MASK, 3); + rtw89_write32(rtwdev, reg, B_BE_PPDU_STAT_RPT_EN | B_BE_PPDU_MAC_INFO | + B_BE_APP_RX_CNT_RPT | B_BE_APP_PLCP_HDR_RPT | + B_BE_PPDU_STAT_RPT_CRC32 | B_BE_PPDU_STAT_RPT_DMA); + + return 0; +} + static bool rtw89_mac_get_txpwr_cr_be(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u32 reg_base, u32 *cr) @@ -2239,6 +2334,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .bf_assoc = rtw89_mac_bf_assoc_be, .typ_fltr_opt = rtw89_mac_typ_fltr_opt_be, + .cfg_ppdu_status = rtw89_mac_cfg_ppdu_status_be, .dle_mix_cfg = dle_mix_cfg_be, .chk_dle_rdy = chk_dle_rdy_be, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index ec2559bde092..cdead0132d66 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -5502,6 +5502,14 @@ #define B_BE_DROP_NONDMA_PPDU BIT(2) #define B_BE_APPEND_FCS BIT(0) +#define R_BE_HW_PPDU_STATUS 0x9C30 +#define B_BE_FWD_RPKTTYPE_MASK GENMASK(31, 26) +#define B_BE_FWD_PPDU_PRTID_MASK GENMASK(25, 23) +#define B_BE_FWD_PPDU_FW_RLS BIT(22) +#define B_BE_FWD_PPDU_QUEID_MASK GENMASK(21, 16) +#define B_BE_FWD_OTHER_RPKT_MASK GENMASK(15, 8) +#define B_BE_FWD_PPDU_STAT_MASK GENMASK(7, 0) + #define R_BE_CUT_AMSDU_CTRL 0x9C94 #define B_BE_EN_CUT_AMSDU BIT(31) #define B_BE_CUT_AMSDU_CHKLEN_EN BIT(30) @@ -6143,6 +6151,28 @@ #define B_BE_SET_MUEDCATIMER_TF_0 BIT(4) #define B_BE_MUEDCA_EN_0 BIT(0) +#define R_BE_CTN_DRV_TXEN 0x10398 +#define R_BE_CTN_DRV_TXEN_C1 0x14398 +#define B_BE_CTN_TXEN_TWT_3 BIT(17) +#define B_BE_CTN_TXEN_TWT_2 BIT(16) +#define B_BE_CTN_TXEN_TWT_1 BIT(15) +#define B_BE_CTN_TXEN_TWT_0 BIT(14) +#define B_BE_CTN_TXEN_ULQ BIT(13) +#define B_BE_CTN_TXEN_BCNQ BIT(12) +#define B_BE_CTN_TXEN_HGQ BIT(11) +#define B_BE_CTN_TXEN_CPUMGQ BIT(10) +#define B_BE_CTN_TXEN_MGQ1 BIT(9) +#define B_BE_CTN_TXEN_MGQ BIT(8) +#define B_BE_CTN_TXEN_VO_1 BIT(7) +#define B_BE_CTN_TXEN_VI_1 BIT(6) +#define B_BE_CTN_TXEN_BK_1 BIT(5) +#define B_BE_CTN_TXEN_BE_1 BIT(4) +#define B_BE_CTN_TXEN_VO_0 BIT(3) +#define B_BE_CTN_TXEN_VI_0 BIT(2) +#define B_BE_CTN_TXEN_BK_0 BIT(1) +#define B_BE_CTN_TXEN_BE_0 BIT(0) +#define B_BE_CTN_TXEN_ALL_MASK GENMASK(17, 0) + #define R_BE_TB_CHK_CCA_NAV 0x103AC #define R_BE_TB_CHK_CCA_NAV_C1 0x143AC #define B_BE_TB_CHK_TX_NAV BIT(15) @@ -7144,6 +7174,20 @@ #define S_BE_BACAM_RST_ENT 1 #define S_BE_BACAM_RST_ALL 2 +#define R_BE_PPDU_STAT 0x11440 +#define R_BE_PPDU_STAT_C1 0x15440 +#define B_BE_STAT_IORST BIT(13) +#define B_BE_STAT_GCKDIS BIT(12) +#define B_BE_PPDU_STAT_WR_BW_MASK GENMASK(11, 10) +#define B_BE_PPDU_STAT_RPT_TRIG BIT(8) +#define B_BE_PPDU_STAT_RPT_DMA BIT(6) +#define B_BE_PPDU_STAT_RPT_CRC32 BIT(5) +#define B_BE_PPDU_STAT_RPT_ADDR BIT(4) +#define B_BE_APP_PLCP_HDR_RPT BIT(3) +#define B_BE_APP_RX_CNT_RPT BIT(2) +#define B_BE_PPDU_MAC_INFO BIT(1) +#define B_BE_PPDU_STAT_RPT_EN BIT(0) + #define R_BE_RX_SR_CTRL 0x1144A #define R_BE_RX_SR_CTRL_C1 0x1544A #define B_BE_SR_OP_MODE_MASK GENMASK(5, 4) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 9c7465d0715b..beac7bf0faff 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1332,6 +1332,8 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .query_rxdesc = rtw89_core_query_rxdesc_v2, .fill_txdesc = rtw89_core_fill_txdesc_v2, .fill_txdesc_fwcmd = rtw89_core_fill_txdesc_fwcmd_v2, + .stop_sch_tx = rtw89_mac_stop_sch_tx_v2, + .resume_sch_tx = rtw89_mac_resume_sch_tx_v2, .h2c_dctl_sec_cam = rtw89_fw_h2c_dctl_sec_cam_v2, .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl_g7, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl_g7, From 1ba63a8a752a76ebe4b26d80c6d25bd04484a9eb Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 24 Jan 2024 11:36:34 +0800 Subject: [PATCH 116/378] wifi: rtw89: 8922a: add chip_ops::cfg_txrx_path This function is to set TX/RX path. Especially for 1SS rate, it can select to TX on one or two antenna. Before this operation, stop hardware to prevent transmitting/receiving unexpected packets. After that, restore settings and reset hardware to prevent it stays on abnormal state. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240124033637.12330-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 24 ++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 279 ++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8922a_rfk.c | 33 +++ .../net/wireless/realtek/rtw89/rtw8922a_rfk.h | 12 + 4 files changed, 348 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index cdead0132d66..d7217f2a26aa 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -7686,9 +7686,23 @@ #define B_SWSI_READ_ADDR_ADDR_V1 GENMASK(7, 0) #define B_SWSI_READ_ADDR_PATH_V1 GENMASK(10, 8) #define B_SWSI_READ_ADDR_V1 GENMASK(10, 0) +#define R_BRK_R 0x0418 +#define B_VHTMCS_LMT GENMASK(22, 21) +#define B_HTMCS_LMT GENMASK(9, 8) +#define R_BRK_EHT 0x0474 +#define B_RXEHT_NSS_MAX GENMASK(4, 2) +#define R_BRK_RXEHT 0x0478 +#define B_RXEHT_N_USER_MAX GENMASK(31, 24) +#define B_RXEHTTB_NSS_MAX GENMASK(16, 14) #define R_EN_SND_WO_NDP 0x047c #define R_EN_SND_WO_NDP_C1 0x147c #define B_EN_SND_WO_NDP BIT(1) +#define R_BRK_HE 0x0480 +#define B_TB_NSS_MAX GENMASK(25, 23) +#define B_NSS_MAX GENMASK(16, 14) +#define B_N_USR_MAX GENMASK(13, 6) +#define R_RXCCA_BE1 0x0520 +#define B_RXCCA_BE1_DIS BIT(0) #define R_UPD_CLK_ADC 0x0700 #define B_UPD_CLK_ADC_VAL GENMASK(26, 25) #define B_UPD_CLK_ADC_ON BIT(24) @@ -7735,6 +7749,7 @@ #define B_PMAC_RXMOD_MSK GENMASK(7, 4) #define R_MAC_SEL 0x09A4 #define B_MAC_SEL_OFDM_TRI_FILTER BIT(31) +#define B_MAC_SEL GENMASK(19, 17) #define B_MAC_SEL_PWR_EN BIT(16) #define B_MAC_SEL_DPD_EN BIT(10) #define B_MAC_SEL_MOD GENMASK(4, 2) @@ -7828,6 +7843,8 @@ #define R_CLK_GCK 0x1008 #define B_CLK_GCK GENMASK(24, 0) #define R_EDCCA_RPT_SEL_BE 0x10CC +#define R_ADC_FIFO_V1 0x10FC +#define B_ADC_FIFO_EN_V1 GENMASK(31, 24) #define R_S0_HW_SI_DIS 0x1200 #define B_S0_HW_SI_DIS_W_R_TRIG GENMASK(30, 28) #define R_P0_RXCK 0x12A0 @@ -8937,6 +8954,11 @@ #define R_IQK_DPK_PRST 0xE4AC #define R_IQK_DPK_PRST_C1 0xE5AC #define B_IQK_DPK_PRST BIT(27) +#define R_TXPWR_RSTA 0xE60C +#define B_TXPWR_RSTA BIT(16) +#define R_TSSI_PWR_P0 0xE610 +#define R_TSSI_PWR_P1 0xE710 +#define B_TSSI_CONT_EN BIT(3) #define R_TSSI_MAP_OFST_P0 0xE620 #define R_TSSI_MAP_OFST_P1 0xE720 #define B_TSSI_MAP_OFST_OFDM GENMASK(17, 9) @@ -8949,6 +8971,8 @@ #define R_TXAGC_REF1_P0 0xE62C #define R_TXAGC_REF1_P1 0xE72C #define B_TXAGC_REF1_CCK_CW GENMASK(8, 0) +#define R_TXPWR_RSTB 0xE70C +#define B_TXPWR_RSTB BIT(16) /* WiFi CPU local domain */ #define R_AX_WDT_CTRL 0x0040 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index beac7bf0faff..2481f983b426 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -9,12 +9,15 @@ #include "phy.h" #include "reg.h" #include "rtw8922a.h" +#include "rtw8922a_rfk.h" #define RTW8922A_FW_FORMAT_MAX 0 #define RTW8922A_FW_BASENAME "rtw89/rtw8922a_fw" #define RTW8922A_MODULE_FIRMWARE \ RTW8922A_FW_BASENAME ".bin" +#define HE_N_USER_MAX_8922A 4 + static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_pcie[] = { {2, 1641, grp_0}, /* ACH 0 */ {2, 1641, grp_0}, /* ACH 1 */ @@ -1049,10 +1052,167 @@ static void rtw8922a_bb_postinit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph rtw89_phy_write32_idx(rtwdev, R_UDP_COEEF, B_UDP_COEEF, 0x1, phy_idx); } +static void rtw8922a_bb_reset_en(struct rtw89_dev *rtwdev, enum rtw89_band band, + bool en, enum rtw89_phy_idx phy_idx) +{ + if (en) { + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 1, phy_idx); + if (band == RTW89_BAND_2G) + rtw89_phy_write32_idx(rtwdev, R_RXCCA_BE1, + B_RXCCA_BE1_DIS, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PD_CTRL, B_PD_HIT_DIS, 0x0, phy_idx); + } else { + rtw89_phy_write32_idx(rtwdev, R_RXCCA_BE1, B_RXCCA_BE1_DIS, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PD_CTRL, B_PD_HIT_DIS, 0x1, phy_idx); + fsleep(1); + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 0, phy_idx); + } +} + +static int rtw8922a_ctrl_tx_path_tmac(struct rtw89_dev *rtwdev, + enum rtw89_rf_path tx_path, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_reg2_def path_com_cr[] = { + {0x11A00, 0x21C86900}, + {0x11A04, 0x00E4E433}, + {0x11A08, 0x39390CC9}, + {0x11A0C, 0x4E433240}, + {0x11A10, 0x90CC900E}, + {0x11A14, 0x00240393}, + {0x11A18, 0x201C8600}, + }; + int ret = 0; + u32 reg; + int i; + + rtw89_phy_write32_idx(rtwdev, R_MAC_SEL, B_MAC_SEL, 0x0, phy_idx); + + if (phy_idx == RTW89_PHY_1 && !rtwdev->dbcc_en) + return 0; + + if (tx_path == RF_PATH_A) { + path_com_cr[0].data = 0x21C82900; + path_com_cr[1].data = 0x00E4E431; + path_com_cr[2].data = 0x39390C49; + path_com_cr[3].data = 0x4E431240; + path_com_cr[4].data = 0x90C4900E; + path_com_cr[6].data = 0x201C8200; + } else if (tx_path == RF_PATH_B) { + path_com_cr[0].data = 0x21C04900; + path_com_cr[1].data = 0x00E4E032; + path_com_cr[2].data = 0x39380C89; + path_com_cr[3].data = 0x4E032240; + path_com_cr[4].data = 0x80C8900E; + path_com_cr[6].data = 0x201C0400; + } else if (tx_path == RF_PATH_AB) { + path_com_cr[0].data = 0x21C86900; + path_com_cr[1].data = 0x00E4E433; + path_com_cr[2].data = 0x39390CC9; + path_com_cr[3].data = 0x4E433240; + path_com_cr[4].data = 0x90CC900E; + path_com_cr[6].data = 0x201C8600; + } else { + ret = -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(path_com_cr); i++) { + reg = rtw89_mac_reg_by_idx(rtwdev, path_com_cr[i].addr, phy_idx); + rtw89_write32(rtwdev, reg, path_com_cr[i].data); + } + + return ret; +} + static void rtw8922a_bb_reset(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { } +static int rtw8922a_cfg_rx_nss_limit(struct rtw89_dev *rtwdev, u8 rx_nss, + enum rtw89_phy_idx phy_idx) +{ + if (rx_nss == 1) { + rtw89_phy_write32_idx(rtwdev, R_BRK_R, B_HTMCS_LMT, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_R, B_VHTMCS_LMT, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_HE, B_N_USR_MAX, + HE_N_USER_MAX_8922A, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_HE, B_NSS_MAX, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_HE, B_TB_NSS_MAX, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_EHT, B_RXEHT_NSS_MAX, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_RXEHT, B_RXEHTTB_NSS_MAX, 0, + phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_RXEHT, B_RXEHT_N_USER_MAX, + HE_N_USER_MAX_8922A, phy_idx); + } else if (rx_nss == 2) { + rtw89_phy_write32_idx(rtwdev, R_BRK_R, B_HTMCS_LMT, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_R, B_VHTMCS_LMT, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_HE, B_N_USR_MAX, + HE_N_USER_MAX_8922A, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_HE, B_NSS_MAX, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_HE, B_TB_NSS_MAX, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_EHT, B_RXEHT_NSS_MAX, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_RXEHT, B_RXEHTTB_NSS_MAX, 1, + phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BRK_RXEHT, B_RXEHT_N_USER_MAX, + HE_N_USER_MAX_8922A, phy_idx); + } else { + return -EINVAL; + } + + return 0; +} + +static void rtw8922a_tssi_reset(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { + if (phy_idx == RTW89_PHY_0) { + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTA, B_TXPWR_RSTA, 0x0); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTA, B_TXPWR_RSTA, 0x1); + } else { + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB, B_TXPWR_RSTB, 0x0); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB, B_TXPWR_RSTB, 0x1); + } + } else { + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTA, B_TXPWR_RSTA, 0x0); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTA, B_TXPWR_RSTA, 0x1); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB, B_TXPWR_RSTB, 0x0); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB, B_TXPWR_RSTB, 0x1); + } +} + +static int rtw8922a_ctrl_rx_path_tmac(struct rtw89_dev *rtwdev, + enum rtw89_rf_path rx_path, + enum rtw89_phy_idx phy_idx) +{ + u8 rx_nss = (rx_path == RF_PATH_AB) ? 2 : 1; + + /* Set to 0 first to avoid abnormal EDCCA report */ + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_ANT_RX_SG0, 0x0, phy_idx); + + if (rx_path == RF_PATH_A) { + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_ANT_RX_SG0, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_RX_1RCCA, 1, phy_idx); + rtw8922a_cfg_rx_nss_limit(rtwdev, rx_nss, phy_idx); + rtw8922a_tssi_reset(rtwdev, rx_path, phy_idx); + } else if (rx_path == RF_PATH_B) { + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_ANT_RX_SG0, 0x2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_RX_1RCCA, 2, phy_idx); + rtw8922a_cfg_rx_nss_limit(rtwdev, rx_nss, phy_idx); + rtw8922a_tssi_reset(rtwdev, rx_path, phy_idx); + } else if (rx_path == RF_PATH_AB) { + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_ANT_RX_SG0, 0x3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_RX_1RCCA, 3, phy_idx); + rtw8922a_cfg_rx_nss_limit(rtwdev, rx_nss, phy_idx); + rtw8922a_tssi_reset(rtwdev, rx_path, phy_idx); + } else { + return -EINVAL; + } + + return 0; +} + static int rtw8922a_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode) { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); @@ -1130,6 +1290,85 @@ static void rtw8922a_set_channel(struct rtw89_dev *rtwdev, rtw8922a_set_channel_bb(rtwdev, chan, phy_idx); } +static void rtw8922a_dfs_en_idx(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, enum rtw89_rf_path path, + bool en) +{ + u32 path_ofst = (path == RF_PATH_B) ? 0x100 : 0x0; + + if (en) + rtw89_phy_write32_idx(rtwdev, 0x2800 + path_ofst, BIT(1), 1, + phy_idx); + else + rtw89_phy_write32_idx(rtwdev, 0x2800 + path_ofst, BIT(1), 0, + phy_idx); +} + +static void rtw8922a_dfs_en(struct rtw89_dev *rtwdev, bool en, + enum rtw89_phy_idx phy_idx) +{ + rtw8922a_dfs_en_idx(rtwdev, phy_idx, RF_PATH_A, en); + rtw8922a_dfs_en_idx(rtwdev, phy_idx, RF_PATH_B, en); +} + +static void rtw8922a_adc_en_path(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path, bool en) +{ + u32 val; + + val = rtw89_phy_read32_mask(rtwdev, R_ADC_FIFO_V1, B_ADC_FIFO_EN_V1); + + if (en) { + if (path == RF_PATH_A) + val &= ~0x1; + else + val &= ~0x2; + } else { + if (path == RF_PATH_A) + val |= 0x1; + else + val |= 0x2; + } + + rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO_V1, B_ADC_FIFO_EN_V1, val); +} + +static void rtw8922a_adc_en(struct rtw89_dev *rtwdev, bool en, u8 phy_idx) +{ + if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { + if (phy_idx == RTW89_PHY_0) + rtw8922a_adc_en_path(rtwdev, RF_PATH_A, en); + else + rtw8922a_adc_en_path(rtwdev, RF_PATH_B, en); + } else { + rtw8922a_adc_en_path(rtwdev, RF_PATH_A, en); + rtw8922a_adc_en_path(rtwdev, RF_PATH_B, en); + } +} + +static +void rtw8922a_hal_reset(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, enum rtw89_mac_idx mac_idx, + enum rtw89_band band, u32 *tx_en, bool enter) +{ + if (enter) { + rtw89_chip_stop_sch_tx(rtwdev, mac_idx, tx_en, RTW89_SCH_TX_SEL_ALL); + rtw89_mac_cfg_ppdu_status(rtwdev, mac_idx, false); + rtw8922a_dfs_en(rtwdev, false, phy_idx); + rtw8922a_tssi_cont_en_phyidx(rtwdev, false, phy_idx); + rtw8922a_adc_en(rtwdev, false, phy_idx); + fsleep(40); + rtw8922a_bb_reset_en(rtwdev, band, false, phy_idx); + } else { + rtw89_mac_cfg_ppdu_status(rtwdev, mac_idx, true); + rtw8922a_adc_en(rtwdev, true, phy_idx); + rtw8922a_dfs_en(rtwdev, true, phy_idx); + rtw8922a_tssi_cont_en_phyidx(rtwdev, true, phy_idx); + rtw8922a_bb_reset_en(rtwdev, band, true, phy_idx); + rtw89_chip_resume_sch_tx(rtwdev, mac_idx, *tx_en); + } +} + static void rtw8922a_set_txpwr_ref(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { @@ -1188,6 +1427,19 @@ static void rtw8922a_set_txpwr_ctrl(struct rtw89_dev *rtwdev, rtw8922a_set_txpwr_ref(rtwdev, phy_idx); } +static void rtw8922a_ctrl_trx_path(struct rtw89_dev *rtwdev, + enum rtw89_rf_path tx_path, u8 tx_nss, + enum rtw89_rf_path rx_path, u8 rx_nss) +{ + enum rtw89_phy_idx phy_idx; + + for (phy_idx = RTW89_PHY_0; phy_idx <= RTW89_PHY_1; phy_idx++) { + rtw8922a_ctrl_tx_path_tmac(rtwdev, tx_path, phy_idx); + rtw8922a_ctrl_rx_path_tmac(rtwdev, rx_path, phy_idx); + rtw8922a_cfg_rx_nss_limit(rtwdev, rx_nss, phy_idx); + } +} + static void rtw8922a_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en, enum rtw89_phy_idx phy_idx) { @@ -1250,6 +1502,32 @@ static void rtw8922a_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en, } } +static void rtw8922a_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + enum rtw89_band band = chan->band_type; + struct rtw89_hal *hal = &rtwdev->hal; + u8 ntx_path = RF_PATH_AB; + u32 tx_en0, tx_en1; + + if (hal->antenna_tx == RF_A) + ntx_path = RF_PATH_A; + else if (hal->antenna_tx == RF_B) + ntx_path = RF_PATH_B; + + rtw8922a_hal_reset(rtwdev, RTW89_PHY_0, RTW89_MAC_0, band, &tx_en0, true); + if (rtwdev->dbcc_en) + rtw8922a_hal_reset(rtwdev, RTW89_PHY_1, RTW89_MAC_1, band, + &tx_en1, true); + + rtw8922a_ctrl_trx_path(rtwdev, ntx_path, 2, RF_PATH_AB, 2); + + rtw8922a_hal_reset(rtwdev, RTW89_PHY_0, RTW89_MAC_0, band, &tx_en0, false); + if (rtwdev->dbcc_en) + rtw8922a_hal_reset(rtwdev, RTW89_PHY_1, RTW89_MAC_1, band, + &tx_en0, false); +} + static void rtw8922a_fill_freq_with_ppdu(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct ieee80211_rx_status *status) @@ -1326,6 +1604,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .ctrl_btg_bt_rx = rtw8922a_ctrl_btg_bt_rx, .query_ppdu = rtw8922a_query_ppdu, .ctrl_nbtg_bt_tx = rtw8922a_ctrl_nbtg_bt_tx, + .cfg_txrx_path = rtw8922a_bb_cfg_txrx_path, .set_txpwr_ul_tb_offset = NULL, .pwr_on_func = rtw8922a_pwr_on_func, .pwr_off_func = rtw8922a_pwr_off_func, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c new file mode 100644 index 000000000000..e0e8048db739 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2023 Realtek Corporation + */ + +#include "debug.h" +#include "phy.h" +#include "reg.h" +#include "rtw8922a.h" +#include "rtw8922a_rfk.h" + +static void rtw8922a_tssi_cont_en(struct rtw89_dev *rtwdev, bool en, + enum rtw89_rf_path path) +{ + static const u32 tssi_trk_man[2] = {R_TSSI_PWR_P0, R_TSSI_PWR_P1}; + + if (en) + rtw89_phy_write32_mask(rtwdev, tssi_trk_man[path], B_TSSI_CONT_EN, 0); + else + rtw89_phy_write32_mask(rtwdev, tssi_trk_man[path], B_TSSI_CONT_EN, 1); +} + +void rtw8922a_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx) +{ + if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { + if (phy_idx == RTW89_PHY_0) + rtw8922a_tssi_cont_en(rtwdev, en, RF_PATH_A); + else + rtw8922a_tssi_cont_en(rtwdev, en, RF_PATH_B); + } else { + rtw8922a_tssi_cont_en(rtwdev, en, RF_PATH_A); + rtw8922a_tssi_cont_en(rtwdev, en, RF_PATH_B); + } +} diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h new file mode 100644 index 000000000000..fbd22de269e2 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2023 Realtek Corporation + */ + +#ifndef __RTW89_8922A_RFK_H__ +#define __RTW89_8922A_RFK_H__ + +#include "core.h" + +void rtw8922a_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx); + +#endif From 88d1f9b22fab815dd8c27ccb06f30d4814eaa11a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 24 Jan 2024 11:36:35 +0800 Subject: [PATCH 117/378] wifi: rtw89: 8922a: add RF read/write v2 Implement indirect interface v2 to read/write RF registers via PHY registers for 8922A. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240124033637.12330-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 125 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 4 + drivers/net/wireless/realtek/rtw89/reg.h | 15 +++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 + 4 files changed, 146 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 7880fbaee092..f661be2f1287 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -796,6 +796,71 @@ u32 rtw89_phy_read_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, } EXPORT_SYMBOL(rtw89_phy_read_rf_v1); +static u32 rtw89_phy_read_full_rf_v2_a(struct rtw89_dev *rtwdev, + enum rtw89_rf_path rf_path, u32 addr) +{ + static const u16 r_addr_ofst[2] = {0x2C24, 0x2D24}; + static const u16 addr_ofst[2] = {0x2ADC, 0x2BDC}; + bool busy, done; + int ret; + u32 val; + + rtw89_phy_write32_mask(rtwdev, addr_ofst[rf_path], B_HWSI_ADD_CTL_MASK, 0x1); + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, busy, !busy, + 1, 3800, false, + rtwdev, r_addr_ofst[rf_path], B_HWSI_VAL_BUSY); + if (ret) { + rtw89_warn(rtwdev, "poll HWSI is busy\n"); + return INV_RF_DATA; + } + + rtw89_phy_write32_mask(rtwdev, addr_ofst[rf_path], B_HWSI_ADD_MASK, addr); + rtw89_phy_write32_mask(rtwdev, addr_ofst[rf_path], B_HWSI_ADD_RD, 0x1); + udelay(2); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, done, done, + 1, 3800, false, + rtwdev, r_addr_ofst[rf_path], B_HWSI_VAL_RDONE); + if (ret) { + rtw89_warn(rtwdev, "read HWSI is busy\n"); + val = INV_RF_DATA; + goto out; + } + + val = rtw89_phy_read32_mask(rtwdev, r_addr_ofst[rf_path], RFREG_MASK); +out: + rtw89_phy_write32_mask(rtwdev, addr_ofst[rf_path], B_HWSI_ADD_POLL_MASK, 0); + + return val; +} + +static u32 rtw89_phy_read_rf_v2_a(struct rtw89_dev *rtwdev, + enum rtw89_rf_path rf_path, u32 addr, u32 mask) +{ + u32 val; + + val = rtw89_phy_read_full_rf_v2_a(rtwdev, rf_path, addr); + + return (val & mask) >> __ffs(mask); +} + +u32 rtw89_phy_read_rf_v2(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask) +{ + bool ad_sel = u32_get_bits(addr, RTW89_RF_ADDR_ADSEL_MASK); + + if (rf_path >= rtwdev->chip->rf_path_num) { + rtw89_err(rtwdev, "unsupported rf path (%d)\n", rf_path); + return INV_RF_DATA; + } + + if (ad_sel) + return rtw89_phy_read_rf(rtwdev, rf_path, addr, mask); + else + return rtw89_phy_read_rf_v2_a(rtwdev, rf_path, addr, mask); +} +EXPORT_SYMBOL(rtw89_phy_read_rf_v2); + bool rtw89_phy_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data) { @@ -875,6 +940,66 @@ bool rtw89_phy_write_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, } EXPORT_SYMBOL(rtw89_phy_write_rf_v1); +static +bool rtw89_phy_write_full_rf_v2_a(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 data) +{ + static const u32 addr_is_idle[2] = {0x2C24, 0x2D24}; + static const u32 addr_ofst[2] = {0x2AE0, 0x2BE0}; + bool busy; + u32 val; + int ret; + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, busy, !busy, + 1, 3800, false, + rtwdev, addr_is_idle[rf_path], BIT(29)); + if (ret) { + rtw89_warn(rtwdev, "[%s] HWSI is busy\n", __func__); + return false; + } + + val = u32_encode_bits(addr, B_HWSI_DATA_ADDR) | + u32_encode_bits(data, B_HWSI_DATA_VAL); + + rtw89_phy_write32(rtwdev, addr_ofst[rf_path], val); + + return true; +} + +static +bool rtw89_phy_write_rf_a_v2(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data) +{ + u32 val; + + if (mask == RFREG_MASK) { + val = data; + } else { + val = rtw89_phy_read_full_rf_v2_a(rtwdev, rf_path, addr); + val &= ~mask; + val |= (data << __ffs(mask)) & mask; + } + + return rtw89_phy_write_full_rf_v2_a(rtwdev, rf_path, addr, val); +} + +bool rtw89_phy_write_rf_v2(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data) +{ + bool ad_sel = u32_get_bits(addr, RTW89_RF_ADDR_ADSEL_MASK); + + if (rf_path >= rtwdev->chip->rf_path_num) { + rtw89_err(rtwdev, "unsupported rf path (%d)\n", rf_path); + return INV_RF_DATA; + } + + if (ad_sel) + return rtw89_phy_write_rf(rtwdev, rf_path, addr, mask, data); + else + return rtw89_phy_write_rf_a_v2(rtwdev, rf_path, addr, mask, data); +} +EXPORT_SYMBOL(rtw89_phy_write_rf_v2); + static bool rtw89_chip_rf_v1(struct rtw89_dev *rtwdev) { return rtwdev->chip->ops->write_rf == rtw89_phy_write_rf_v1; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index c05f724a84ce..13903ca1eaa9 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -781,10 +781,14 @@ u32 rtw89_phy_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask); u32 rtw89_phy_read_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask); +u32 rtw89_phy_read_rf_v2(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask); bool rtw89_phy_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data); bool rtw89_phy_write_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data); +bool rtw89_phy_write_rf_v2(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data); void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev); void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio); void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index d7217f2a26aa..95c06b98192c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -7997,6 +7997,21 @@ #define B_AFEDAC1 GENMASK(2, 0) #define R_IQKDPK_HC 0x2AB8 #define B_IQKDPK_HC BIT(28) +#define R_HWSI_ADD0 0x2ADC +#define R_HWSI_ADD1 0x2BDC +#define B_HWSI_ADD_MASK GENMASK(11, 4) +#define B_HWSI_ADD_CTL_MASK GENMASK(2, 0) +#define B_HWSI_ADD_RD BIT(2) +#define B_HWSI_ADD_POLL_MASK GENMASK(1, 0) +#define B_HWSI_ADD_RUN BIT(1) +#define B_HWSI_ADD_BUSY BIT(0) +#define R_HWSI_DATA 0x2AE0 +#define B_HWSI_DATA_VAL GENMASK(27, 8) +#define B_HWSI_DATA_ADDR GENMASK(7, 0) +#define R_HWSI_VAL0 0x2C24 +#define R_HWSI_VAL1 0x2D24 +#define B_HWSI_VAL_RDONE BIT(31) +#define B_HWSI_VAL_BUSY BIT(29) #define R_P1_EN_SOUND_WO_NDP 0x2D7C #define B_P1_EN_SOUND_WO_NDP BIT(1) #define R_EDCCA_RPT_A_BE 0x2E38 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 2481f983b426..1c5ceb5564c3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1594,6 +1594,8 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .bb_postinit = rtw8922a_bb_postinit, .bb_reset = rtw8922a_bb_reset, .bb_sethw = rtw8922a_bb_sethw, + .read_rf = rtw89_phy_read_rf_v2, + .write_rf = rtw89_phy_write_rf_v2, .set_channel = rtw8922a_set_channel, .read_efuse = rtw8922a_read_efuse, .read_phycap = rtw8922a_read_phycap, From 1de97cd362c4080aab595b84bf0bff3ebc702446 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 24 Jan 2024 11:38:02 +0800 Subject: [PATCH 118/378] wifi: rtw89: 8922a: add chip_ops to get thermal value Get thermal value as a clue to do RF calibration if the delta is larger than a threshold, but 8922A doesn't need this, so we only read out the value when debugging to reduce IO. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240124033802.12508-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 95c06b98192c..addf11fc76d4 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -7468,6 +7468,7 @@ #define RR_LUTWD0_LB GENMASK(5, 0) #define RR_TM 0x42 #define RR_TM_TRI BIT(19) +#define RR_TM_VAL_V1 GENMASK(7, 0) #define RR_TM_VAL GENMASK(6, 1) #define RR_TM2 0x43 #define RR_TM2_OFF GENMASK(19, 16) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 1c5ceb5564c3..6de3c0f8d83a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1528,6 +1528,27 @@ static void rtw8922a_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) &tx_en0, false); } +static u8 rtw8922a_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + int th; + + /* read thermal only if debugging */ + if (!rtw89_debug_is_enabled(rtwdev, RTW89_DBG_CFO | RTW89_DBG_RFK_TRACK)) + return 80; + + rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x1); + rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x0); + rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x1); + + fsleep(200); + + th = rtw89_read_rf(rtwdev, rf_path, RR_TM, RR_TM_VAL_V1); + th += (s8)info->thermal_trim[rf_path]; + + return clamp_t(int, th, 0, U8_MAX); +} + static void rtw8922a_fill_freq_with_ppdu(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct ieee80211_rx_status *status) @@ -1603,6 +1624,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .set_txpwr = rtw8922a_set_txpwr, .set_txpwr_ctrl = rtw8922a_set_txpwr_ctrl, .init_txpwr_unit = NULL, + .get_thermal = rtw8922a_get_thermal, .ctrl_btg_bt_rx = rtw8922a_ctrl_btg_bt_rx, .query_ppdu = rtw8922a_query_ppdu, .ctrl_nbtg_bt_tx = rtw8922a_ctrl_nbtg_bt_tx, From a6c759c8962b11fdc62ced6f70a3f6ed0a50e033 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 24 Jan 2024 11:38:13 +0800 Subject: [PATCH 119/378] wifi: rtw89: 8922a: set chip_ops FEM and GPIO to NULL The chip_ops::fem_setup is to get if a hardware module type contains PA and LNA that will affect how RF calibrations do. The chip_ops::rfe_gpio is to set GPIO PIN MUX according to hardware module type too. 8922A doesn't have these special module types yet, so leave them as NULL. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240124033813.12562-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 6de3c0f8d83a..aefad3f2e612 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1620,6 +1620,8 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .set_channel = rtw8922a_set_channel, .read_efuse = rtw8922a_read_efuse, .read_phycap = rtw8922a_read_phycap, + .fem_setup = NULL, + .rfe_gpio = NULL, .power_trim = rtw8922a_power_trim, .set_txpwr = rtw8922a_set_txpwr, .set_txpwr_ctrl = rtw8922a_set_txpwr_ctrl, From b5d7020134d91ccb9ae763f738aaa466e37ac25a Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 26 Jan 2024 14:33:50 +0800 Subject: [PATCH 120/378] wifi: rtw89: update scan C2H messages for wifi 7 IC Add definition and parsing for wifi 7 extended fields. These fields include hardware index which is current reporting, timestamp and self defined sequences for debug purposes. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240126063356.17857-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.h | 5 +++++ drivers/net/wireless/realtek/rtw89/mac.c | 24 ++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index a3df701bdc6e..2c94d82d384c 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3309,6 +3309,11 @@ struct rtw89_c2h_scanofld { #define RTW89_C2H_SCANOFLD_W5_TX_FAIL GENMASK(3, 0) #define RTW89_C2H_SCANOFLD_W5_AIR_DENSITY GENMASK(7, 4) #define RTW89_C2H_SCANOFLD_W5_BAND GENMASK(25, 24) +#define RTW89_C2H_SCANOFLD_W5_MAC_IDX BIT(26) +#define RTW89_C2H_SCANOFLD_W6_SW_DEF GENMASK(7, 0) +#define RTW89_C2H_SCANOFLD_W6_EXPECT_PERIOD GENMASK(15, 8) +#define RTW89_C2H_SCANOFLD_W6_FW_DEF GENMASK(23, 16) +#define RTW89_C2H_SCANOFLD_W7_REPORT_TSF GENMASK(31, 0) #define RTW89_GET_MAC_C2H_MCC_RCV_ACK_GROUP(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(1, 0)) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index d1d16564a686..247c566b851a 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4686,8 +4686,9 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif); struct rtw89_chan new; - u8 reason, status, tx_fail, band, actual_period; - u32 last_chan = rtwdev->scan_info.last_chan_idx; + u8 reason, status, tx_fail, band, actual_period, expect_period; + u32 last_chan = rtwdev->scan_info.last_chan_idx, report_tsf; + u8 mac_idx, sw_def, fw_def; u16 chan; int ret; @@ -4700,15 +4701,29 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb, reason = le32_get_bits(c2h->w2, RTW89_C2H_SCANOFLD_W2_RSN); band = le32_get_bits(c2h->w5, RTW89_C2H_SCANOFLD_W5_BAND); actual_period = le32_get_bits(c2h->w2, RTW89_C2H_SCANOFLD_W2_PERIOD); + mac_idx = le32_get_bits(c2h->w5, RTW89_C2H_SCANOFLD_W5_MAC_IDX); + if (!(rtwdev->chip->support_bands & BIT(NL80211_BAND_6GHZ))) band = chan > 14 ? RTW89_BAND_5G : RTW89_BAND_2G; rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, - "band: %d, chan: %d, reason: %d, status: %d, tx_fail: %d, actual: %d\n", - band, chan, reason, status, tx_fail, actual_period); + "mac_idx[%d] band: %d, chan: %d, reason: %d, status: %d, tx_fail: %d, actual: %d\n", + mac_idx, band, chan, reason, status, tx_fail, actual_period); + + if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) { + sw_def = le32_get_bits(c2h->w6, RTW89_C2H_SCANOFLD_W6_SW_DEF); + expect_period = le32_get_bits(c2h->w6, RTW89_C2H_SCANOFLD_W6_EXPECT_PERIOD); + fw_def = le32_get_bits(c2h->w6, RTW89_C2H_SCANOFLD_W6_FW_DEF); + report_tsf = le32_get_bits(c2h->w7, RTW89_C2H_SCANOFLD_W7_REPORT_TSF); + + rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, + "sw_def: %d, fw_def: %d, tsf: %x, expect: %d\n", + sw_def, fw_def, report_tsf, expect_period); + } switch (reason) { + case RTW89_SCAN_LEAVE_OP_NOTIFY: case RTW89_SCAN_LEAVE_CH_NOTIFY: if (rtw89_is_op_chan(rtwdev, band, chan)) { rtw89_mac_enable_beacon_for_ap_vifs(rtwdev, false); @@ -4727,6 +4742,7 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb, rtw89_hw_scan_complete(rtwdev, vif, rtwdev->scan_info.abort); } break; + case RTW89_SCAN_ENTER_OP_NOTIFY: case RTW89_SCAN_ENTER_CH_NOTIFY: if (rtw89_is_op_chan(rtwdev, band, chan)) { rtw89_assign_entity_chan(rtwdev, rtwvif->sub_entity_idx, From ac54faf507e5d2776a7dca2c13665745d4490cd5 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 26 Jan 2024 14:33:51 +0800 Subject: [PATCH 121/378] wifi: rtw89: debug: add FW log component for scan This allows scan related logs when FW log debug mode is on. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240126063356.17857-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 8 ++++++-- drivers/net/wireless/realtek/rtw89/fw.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index e49360e29faf..43fe9333d3e7 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1829,10 +1829,14 @@ int rtw89_fw_h2c_init_ba_cam_users(struct rtw89_dev *rtwdev, u8 users, int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable) { struct sk_buff *skb; - u32 comp = enable ? BIT(RTW89_FW_LOG_COMP_INIT) | BIT(RTW89_FW_LOG_COMP_TASK) | - BIT(RTW89_FW_LOG_COMP_PS) | BIT(RTW89_FW_LOG_COMP_ERROR) : 0; + u32 comp = 0; int ret; + if (enable) + comp = BIT(RTW89_FW_LOG_COMP_INIT) | BIT(RTW89_FW_LOG_COMP_TASK) | + BIT(RTW89_FW_LOG_COMP_PS) | BIT(RTW89_FW_LOG_COMP_ERROR) | + BIT(RTW89_FW_LOG_COMP_SCAN); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_LOG_CFG_LEN); if (!skb) { rtw89_err(rtwdev, "failed to alloc skb for fw log cfg\n"); diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 2c94d82d384c..cb5b7bf88177 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -149,6 +149,7 @@ enum rtw89_fw_log_comp { RTW89_FW_LOG_COMP_TWT, RTW89_FW_LOG_COMP_RF, RTW89_FW_LOG_COMP_MCC = 20, + RTW89_FW_LOG_COMP_SCAN = 28, }; enum rtw89_pkt_offload_op { From a412920b70199c07504ea9e937b00f07916a541a Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 26 Jan 2024 14:33:52 +0800 Subject: [PATCH 122/378] wifi: rtw89: prepare scan leaf functions for wifi 7 ICs The channel field slightly differs between WiFi 6 and WiFi 7. So we prepare some required functions in advance, this doesn't change existing wifi 6 ICs behavior. This H2C prepares the channel list to be scanned for. With layout as the following: +--------+--------------------+-------------------+-----------------------+ | header | number of channels | channel info size | channel_info * number | +--------+--------------------+-------------------+-----------------------+ Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240126063356.17857-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 220 +++++++++++++++++++- drivers/net/wireless/realtek/rtw89/fw.h | 79 ++++++- drivers/net/wireless/realtek/rtw89/mac.c | 2 + drivers/net/wireless/realtek/rtw89/mac.h | 3 + drivers/net/wireless/realtek/rtw89/mac_be.c | 2 + 5 files changed, 302 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 43fe9333d3e7..ea308244b303 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -4078,6 +4078,102 @@ int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int ch_num, return 0; } +int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num, + struct list_head *chan_list) +{ + struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; + struct rtw89_h2c_chinfo_elem_be *elem; + struct rtw89_mac_chinfo_be *ch_info; + struct rtw89_h2c_chinfo *h2c; + struct sk_buff *skb; + unsigned int cond; + int skb_len; + int ret; + + static_assert(sizeof(*elem) == RTW89_MAC_CHINFO_SIZE); + + skb_len = struct_size(h2c, elem, ch_num); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, skb_len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c scan list\n"); + return -ENOMEM; + } + + skb_put(skb, sizeof(*h2c)); + h2c = (struct rtw89_h2c_chinfo *)skb->data; + + h2c->ch_num = ch_num; + h2c->elem_size = sizeof(*elem) / 4; /* in unit of 4 bytes */ + h2c->arg = u8_encode_bits(RTW89_PHY_0, RTW89_H2C_CHINFO_ARG_MAC_IDX_MASK); + + list_for_each_entry(ch_info, chan_list, list) { + elem = (struct rtw89_h2c_chinfo_elem_be *)skb_put(skb, sizeof(*elem)); + + elem->w0 = le32_encode_bits(ch_info->period, RTW89_H2C_CHINFO_BE_W0_PERIOD) | + le32_encode_bits(ch_info->dwell_time, RTW89_H2C_CHINFO_BE_W0_DWELL) | + le32_encode_bits(ch_info->central_ch, + RTW89_H2C_CHINFO_BE_W0_CENTER_CH) | + le32_encode_bits(ch_info->pri_ch, RTW89_H2C_CHINFO_BE_W0_PRI_CH); + + elem->w1 = le32_encode_bits(ch_info->bw, RTW89_H2C_CHINFO_BE_W1_BW) | + le32_encode_bits(ch_info->ch_band, RTW89_H2C_CHINFO_BE_W1_CH_BAND) | + le32_encode_bits(ch_info->dfs_ch, RTW89_H2C_CHINFO_BE_W1_DFS) | + le32_encode_bits(ch_info->pause_data, + RTW89_H2C_CHINFO_BE_W1_PAUSE_DATA) | + le32_encode_bits(ch_info->tx_null, RTW89_H2C_CHINFO_BE_W1_TX_NULL) | + le32_encode_bits(ch_info->rand_seq_num, + RTW89_H2C_CHINFO_BE_W1_RANDOM) | + le32_encode_bits(ch_info->notify_action, + RTW89_H2C_CHINFO_BE_W1_NOTIFY) | + le32_encode_bits(ch_info->probe_id != 0xff ? 1 : 0, + RTW89_H2C_CHINFO_BE_W1_PROBE) | + le32_encode_bits(ch_info->leave_crit, + RTW89_H2C_CHINFO_BE_W1_EARLY_LEAVE_CRIT) | + le32_encode_bits(ch_info->chkpt_timer, + RTW89_H2C_CHINFO_BE_W1_CHKPT_TIMER); + + elem->w2 = le32_encode_bits(ch_info->leave_time, + RTW89_H2C_CHINFO_BE_W2_EARLY_LEAVE_TIME) | + le32_encode_bits(ch_info->leave_th, + RTW89_H2C_CHINFO_BE_W2_EARLY_LEAVE_TH) | + le32_encode_bits(ch_info->tx_pkt_ctrl, + RTW89_H2C_CHINFO_BE_W2_TX_PKT_CTRL); + + elem->w3 = le32_encode_bits(ch_info->pkt_id[0], RTW89_H2C_CHINFO_BE_W3_PKT0) | + le32_encode_bits(ch_info->pkt_id[1], RTW89_H2C_CHINFO_BE_W3_PKT1) | + le32_encode_bits(ch_info->pkt_id[2], RTW89_H2C_CHINFO_BE_W3_PKT2) | + le32_encode_bits(ch_info->pkt_id[3], RTW89_H2C_CHINFO_BE_W3_PKT3); + + elem->w4 = le32_encode_bits(ch_info->pkt_id[4], RTW89_H2C_CHINFO_BE_W4_PKT4) | + le32_encode_bits(ch_info->pkt_id[5], RTW89_H2C_CHINFO_BE_W4_PKT5) | + le32_encode_bits(ch_info->pkt_id[6], RTW89_H2C_CHINFO_BE_W4_PKT6) | + le32_encode_bits(ch_info->pkt_id[7], RTW89_H2C_CHINFO_BE_W4_PKT7); + + elem->w5 = le32_encode_bits(ch_info->sw_def, RTW89_H2C_CHINFO_BE_W5_SW_DEF) | + le32_encode_bits(ch_info->fw_probe0_ssids, + RTW89_H2C_CHINFO_BE_W5_FW_PROBE0_SSIDS); + + elem->w6 = le32_encode_bits(ch_info->fw_probe0_shortssids, + RTW89_H2C_CHINFO_BE_W6_FW_PROBE0_SHORTSSIDS) | + le32_encode_bits(ch_info->fw_probe0_bssids, + RTW89_H2C_CHINFO_BE_W6_FW_PROBE0_BSSIDS); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD, + H2C_FUNC_ADD_SCANOFLD_CH, 1, 1, skb_len); + + cond = RTW89_SCANOFLD_WAIT_COND_ADD_CH; + + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_FW, "failed to add scan ofld ch\n"); + return ret; + } + + return 0; +} + int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev, struct rtw89_scan_option *option, struct rtw89_vif *rtwvif) @@ -4769,8 +4865,66 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, } } -static int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev, - struct rtw89_vif *rtwvif, bool connected) +static void rtw89_hw_scan_add_chan_be(struct rtw89_dev *rtwdev, int chan_type, + int ssid_num, + struct rtw89_mac_chinfo_be *ch_info) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + struct cfg80211_scan_request *req = rtwvif->scan_req; + struct rtw89_pktofld_info *info; + u8 band, probe_count = 0, i; + + ch_info->notify_action = RTW89_SCANOFLD_DEBUG_MASK; + ch_info->dfs_ch = chan_type == RTW89_CHAN_DFS; + ch_info->bw = RTW89_SCAN_WIDTH; + ch_info->tx_null = false; + ch_info->pause_data = false; + ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE; + + if (ssid_num) { + band = rtw89_hw_to_nl80211_band(ch_info->ch_band); + + list_for_each_entry(info, &scan_info->pkt_list[band], list) { + if (info->channel_6ghz && + ch_info->pri_ch != info->channel_6ghz) + continue; + ch_info->pkt_id[probe_count++] = info->id; + if (probe_count >= RTW89_SCANOFLD_MAX_SSID) + break; + } + } + + if (ch_info->ch_band == RTW89_BAND_6G) { + if ((ssid_num == 1 && req->ssids[0].ssid_len == 0) || + !ch_info->is_psc) { + ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE; + if (!req->duration_mandatory) + ch_info->period -= RTW89_DWELL_TIME_6G; + } + } + + for (i = probe_count; i < RTW89_SCANOFLD_MAX_SSID; i++) + ch_info->pkt_id[i] = RTW89_SCANOFLD_PKT_NONE; + + switch (chan_type) { + case RTW89_CHAN_DFS: + if (ch_info->ch_band != RTW89_BAND_6G) + ch_info->period = + max_t(u8, ch_info->period, RTW89_DFS_CHAN_TIME); + ch_info->dwell_time = RTW89_DWELL_TIME; + break; + case RTW89_CHAN_ACTIVE: + break; + default: + rtw89_warn(rtwdev, "Channel type out of bound\n"); + break; + } +} + +int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool connected) { struct cfg80211_scan_request *req = rtwvif->scan_req; struct rtw89_mac_chinfo *ch_info, *tmp; @@ -4846,9 +5000,69 @@ static int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev, return ret; } +int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool connected) +{ + struct cfg80211_scan_request *req = rtwvif->scan_req; + struct rtw89_mac_chinfo_be *ch_info, *tmp; + struct ieee80211_channel *channel; + struct list_head chan_list; + enum rtw89_chan_type type; + int list_len, ret; + bool random_seq; + u32 idx; + + random_seq = !!(req->flags & NL80211_SCAN_FLAG_RANDOM_SN); + INIT_LIST_HEAD(&chan_list); + + for (idx = rtwdev->scan_info.last_chan_idx, list_len = 0; + idx < req->n_channels && list_len < RTW89_SCAN_LIST_LIMIT; + idx++, list_len++) { + channel = req->channels[idx]; + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + goto out; + } + + if (req->duration_mandatory) + ch_info->period = req->duration; + else if (channel->band == NL80211_BAND_6GHZ) + ch_info->period = RTW89_CHANNEL_TIME_6G + RTW89_DWELL_TIME_6G; + else + ch_info->period = RTW89_CHANNEL_TIME; + + ch_info->ch_band = rtw89_nl80211_to_hw_band(channel->band); + ch_info->central_ch = channel->hw_value; + ch_info->pri_ch = channel->hw_value; + ch_info->rand_seq_num = random_seq; + ch_info->is_psc = cfg80211_channel_is_psc(channel); + + if (channel->flags & (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR)) + type = RTW89_CHAN_DFS; + else + type = RTW89_CHAN_ACTIVE; + rtw89_hw_scan_add_chan_be(rtwdev, type, req->n_ssids, ch_info); + + list_add_tail(&ch_info->list, &chan_list); + } + + rtwdev->scan_info.last_chan_idx = idx; + ret = rtw89_fw_h2c_scan_list_offload_be(rtwdev, list_len, &chan_list); + +out: + list_for_each_entry_safe(ch_info, tmp, &chan_list, list) { + list_del(&ch_info->list); + kfree(ch_info); + } + + return ret; +} + static int rtw89_hw_scan_prehandle(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool connected) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; int ret; ret = rtw89_hw_scan_update_probe_req(rtwdev, rtwvif); @@ -4856,7 +5070,7 @@ static int rtw89_hw_scan_prehandle(struct rtw89_dev *rtwdev, rtw89_err(rtwdev, "Update probe request failed\n"); goto out; } - ret = rtw89_hw_scan_add_chan_list(rtwdev, rtwvif, connected); + ret = mac->add_chan_list(rtwdev, rtwvif, connected); out: return ret; } diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index cb5b7bf88177..132809ea05bf 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -297,6 +297,34 @@ struct rtw89_mac_chinfo { bool is_psc; }; +struct rtw89_mac_chinfo_be { + u8 period; + u8 dwell_time; + u8 central_ch; + u8 pri_ch; + u8 bw:3; + u8 ch_band:2; + u8 dfs_ch:1; + u8 pause_data:1; + u8 tx_null:1; + u8 rand_seq_num:1; + u8 notify_action:5; + u8 probe_id; + u8 leave_crit; + u8 chkpt_timer; + u8 leave_time; + u8 leave_th; + u16 tx_pkt_ctrl; + u8 pkt_id[RTW89_SCANOFLD_MAX_SSID]; + u8 sw_def; + u16 fw_probe0_ssids; + u16 fw_probe0_shortssids; + u16 fw_probe0_bssids; + + struct list_head list; + bool is_psc; +}; + struct rtw89_scan_option { bool enable; bool target_ch_mode; @@ -2708,14 +2736,57 @@ struct rtw89_h2c_chinfo_elem { #define RTW89_H2C_CHINFO_W3_PKT7 GENMASK(31, 24) #define RTW89_H2C_CHINFO_W4_POWER_IDX GENMASK(15, 0) +struct rtw89_h2c_chinfo_elem_be { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; + __le32 w4; + __le32 w5; + __le32 w6; +} __packed; + +#define RTW89_H2C_CHINFO_BE_W0_PERIOD GENMASK(7, 0) +#define RTW89_H2C_CHINFO_BE_W0_DWELL GENMASK(15, 8) +#define RTW89_H2C_CHINFO_BE_W0_CENTER_CH GENMASK(23, 16) +#define RTW89_H2C_CHINFO_BE_W0_PRI_CH GENMASK(31, 24) +#define RTW89_H2C_CHINFO_BE_W1_BW GENMASK(2, 0) +#define RTW89_H2C_CHINFO_BE_W1_CH_BAND GENMASK(4, 3) +#define RTW89_H2C_CHINFO_BE_W1_DFS BIT(5) +#define RTW89_H2C_CHINFO_BE_W1_PAUSE_DATA BIT(6) +#define RTW89_H2C_CHINFO_BE_W1_TX_NULL BIT(7) +#define RTW89_H2C_CHINFO_BE_W1_RANDOM BIT(8) +#define RTW89_H2C_CHINFO_BE_W1_NOTIFY GENMASK(13, 9) +#define RTW89_H2C_CHINFO_BE_W1_PROBE BIT(14) +#define RTW89_H2C_CHINFO_BE_W1_EARLY_LEAVE_CRIT GENMASK(17, 15) +#define RTW89_H2C_CHINFO_BE_W1_CHKPT_TIMER GENMASK(31, 24) +#define RTW89_H2C_CHINFO_BE_W2_EARLY_LEAVE_TIME GENMASK(7, 0) +#define RTW89_H2C_CHINFO_BE_W2_EARLY_LEAVE_TH GENMASK(15, 8) +#define RTW89_H2C_CHINFO_BE_W2_TX_PKT_CTRL GENMASK(31, 16) +#define RTW89_H2C_CHINFO_BE_W3_PKT0 GENMASK(7, 0) +#define RTW89_H2C_CHINFO_BE_W3_PKT1 GENMASK(15, 8) +#define RTW89_H2C_CHINFO_BE_W3_PKT2 GENMASK(23, 16) +#define RTW89_H2C_CHINFO_BE_W3_PKT3 GENMASK(31, 24) +#define RTW89_H2C_CHINFO_BE_W4_PKT4 GENMASK(7, 0) +#define RTW89_H2C_CHINFO_BE_W4_PKT5 GENMASK(15, 8) +#define RTW89_H2C_CHINFO_BE_W4_PKT6 GENMASK(23, 16) +#define RTW89_H2C_CHINFO_BE_W4_PKT7 GENMASK(31, 24) +#define RTW89_H2C_CHINFO_BE_W5_SW_DEF GENMASK(7, 0) +#define RTW89_H2C_CHINFO_BE_W5_FW_PROBE0_SSIDS GENMASK(31, 16) +#define RTW89_H2C_CHINFO_BE_W6_FW_PROBE0_SHORTSSIDS GENMASK(15, 0) +#define RTW89_H2C_CHINFO_BE_W6_FW_PROBE0_BSSIDS GENMASK(31, 16) + struct rtw89_h2c_chinfo { u8 ch_num; u8 elem_size; + u8 arg; u8 rsvd0; - u8 rsvd1; struct rtw89_h2c_chinfo_elem elem[] __counted_by(ch_num); } __packed; +#define RTW89_H2C_CHINFO_ARG_MAC_IDX_MASK BIT(0) +#define RTW89_H2C_CHINFO_ARG_APPEND_MASK BIT(1) + struct rtw89_h2c_scanofld { __le32 w0; __le32 w1; @@ -3933,6 +4004,8 @@ int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, struct sk_buff *skb_ofld); int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int ch_num, struct list_head *chan_list); +int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num, + struct list_head *chan_list); int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev, struct rtw89_scan_option *opt, struct rtw89_vif *vif); @@ -3975,6 +4048,10 @@ void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, bool enable); void rtw89_hw_scan_abort(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif); +int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool connected); +int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool connected); int rtw89_fw_h2c_trigger_cpu_exception(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_pkt_drop(struct rtw89_dev *rtwdev, const struct rtw89_pkt_drop_params *params); diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 247c566b851a..0bb38f401760 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -6225,5 +6225,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .dump_err_status = rtw89_mac_dump_err_status_ax, .is_txq_empty = mac_is_txq_empty_ax, + + .add_chan_list = rtw89_hw_scan_add_chan_list, }; EXPORT_SYMBOL(rtw89_mac_gen_ax); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 66bac01eb067..7d7fbd18491e 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -953,6 +953,9 @@ struct rtw89_mac_gen_def { enum mac_ax_err_info err); bool (*is_txq_empty)(struct rtw89_dev *rtwdev); + + int (*add_chan_list)(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool connected); }; extern const struct rtw89_mac_gen_def rtw89_mac_gen_ax; diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index f03d05a2f857..2213451740e0 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2364,5 +2364,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .dump_err_status = rtw89_mac_dump_err_status_be, .is_txq_empty = mac_is_txq_empty_be, + + .add_chan_list = rtw89_hw_scan_add_chan_list_be, }; EXPORT_SYMBOL(rtw89_mac_gen_be); From 4ba24331c973eb1df0d3b67b0e3f8b7cde7765a7 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 26 Jan 2024 14:33:53 +0800 Subject: [PATCH 123/378] wifi: rtw89: 8922a: add ieee80211_ops::hw_scan This adds support for hardware scan after FW version 0.34.35. Currently we only support scanning on single hardware band and support of dual band scan will be added in the future. Adjust the current flow to make driver compatible with different generation ICs. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240126063356.17857-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 39 +++++ drivers/net/wireless/realtek/rtw89/fw.c | 178 +++++++++++++++++++- drivers/net/wireless/realtek/rtw89/fw.h | 86 +++++++++- drivers/net/wireless/realtek/rtw89/mac.c | 15 +- drivers/net/wireless/realtek/rtw89/mac.h | 3 + drivers/net/wireless/realtek/rtw89/mac_be.c | 1 + 6 files changed, 314 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c86b46e7964f..30cc77ac78c5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3258,6 +3258,45 @@ enum rtw89_mlo_dbcc_mode { DBCC_LEGACY = 0xffffffff, }; +enum rtw89_scan_be_operation { + RTW89_SCAN_OP_STOP, + RTW89_SCAN_OP_START, + RTW89_SCAN_OP_SETPARM, + RTW89_SCAN_OP_GETRPT, + RTW89_SCAN_OP_NUM +}; + +enum rtw89_scan_be_mode { + RTW89_SCAN_MODE_SA, + RTW89_SCAN_MODE_MACC, + RTW89_SCAN_MODE_NUM +}; + +enum rtw89_scan_be_opmode { + RTW89_SCAN_OPMODE_NONE, + RTW89_SCAN_OPMODE_TBTT, + RTW89_SCAN_OPMODE_INTV, + RTW89_SCAN_OPMODE_CNT, + RTW89_SCAN_OPMODE_NUM, +}; + +struct rtw89_scan_option { + bool enable; + bool target_ch_mode; + u8 num_macc_role; + u8 num_opch; + u8 repeat; + u16 norm_pd; + u16 slow_pd; + u16 norm_cy; + u8 opch_end; + u64 prohib_chan; + enum rtw89_phy_idx band; + enum rtw89_scan_be_operation operation; + enum rtw89_scan_be_mode scan_mode; + enum rtw89_mlo_dbcc_mode mlo_mode; +}; + enum rtw89_qta_mode { RTW89_QTA_SCC, RTW89_QTA_DLFW, diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index ea308244b303..7a0671e4ef2c 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -459,6 +459,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 56, 10, BEACON_FILTER), __CFG_FW_FEAT(RTL8922A, ge, 0, 34, 30, 0, CRASH_TRIGGER), __CFG_FW_FEAT(RTL8922A, ge, 0, 34, 11, 0, MACID_PAUSE_SLEEP), + __CFG_FW_FEAT(RTL8922A, ge, 0, 34, 35, 0, SCAN_OFFLOAD), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, @@ -4236,6 +4237,169 @@ int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev, return 0; } +static void rtw89_scan_get_6g_disabled_chan(struct rtw89_dev *rtwdev, + struct rtw89_scan_option *option) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + u8 i, idx; + + sband = rtwdev->hw->wiphy->bands[NL80211_BAND_6GHZ]; + + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + if (chan->flags & IEEE80211_CHAN_DISABLED) { + idx = (chan->hw_value - 1) / 4; + option->prohib_chan |= BIT(idx); + } + } +} + +int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, + struct rtw89_scan_option *option, + struct rtw89_vif *rtwvif) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; + struct rtw89_h2c_scanofld_be_macc_role *macc_role; + struct rtw89_chan *op = &scan_info->op_chan; + struct rtw89_h2c_scanofld_be_opch *opch; + struct rtw89_h2c_scanofld_be *h2c; + struct sk_buff *skb; + u8 macc_role_size = sizeof(*macc_role) * option->num_macc_role; + u8 opch_size = sizeof(*opch) * option->num_opch; + u8 probe_id[NUM_NL80211_BANDS]; + unsigned int cond; + void *ptr; + int ret; + u32 len; + u8 i; + + rtw89_scan_get_6g_disabled_chan(rtwdev, option); + + len = sizeof(*h2c) + macc_role_size + opch_size; + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c scan offload\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_scanofld_be *)skb->data; + ptr = skb->data; + + h2c->w0 = le32_encode_bits(option->operation, RTW89_H2C_SCANOFLD_BE_W0_OP) | + le32_encode_bits(option->scan_mode, + RTW89_H2C_SCANOFLD_BE_W0_SCAN_MODE) | + le32_encode_bits(option->repeat, RTW89_H2C_SCANOFLD_BE_W0_REPEAT) | + le32_encode_bits(true, RTW89_H2C_SCANOFLD_BE_W0_NOTIFY_END) | + le32_encode_bits(true, RTW89_H2C_SCANOFLD_BE_W0_LEARN_CH) | + le32_encode_bits(rtwvif->mac_id, RTW89_H2C_SCANOFLD_BE_W0_MACID) | + le32_encode_bits(rtwvif->port, RTW89_H2C_SCANOFLD_BE_W0_PORT) | + le32_encode_bits(option->band, RTW89_H2C_SCANOFLD_BE_W0_BAND); + + h2c->w1 = le32_encode_bits(option->num_macc_role, RTW89_H2C_SCANOFLD_BE_W1_NUM_MACC_ROLE) | + le32_encode_bits(option->num_opch, RTW89_H2C_SCANOFLD_BE_W1_NUM_OP) | + le32_encode_bits(option->norm_pd, RTW89_H2C_SCANOFLD_BE_W1_NORM_PD); + + h2c->w2 = le32_encode_bits(option->slow_pd, RTW89_H2C_SCANOFLD_BE_W2_SLOW_PD) | + le32_encode_bits(option->norm_cy, RTW89_H2C_SCANOFLD_BE_W2_NORM_CY) | + le32_encode_bits(option->opch_end, RTW89_H2C_SCANOFLD_BE_W2_OPCH_END); + + h2c->w3 = le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_W3_NUM_SSID) | + le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_W3_NUM_SHORT_SSID) | + le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_W3_NUM_BSSID) | + le32_encode_bits(probe_id[NL80211_BAND_2GHZ], RTW89_H2C_SCANOFLD_BE_W3_PROBEID); + + h2c->w4 = le32_encode_bits(probe_id[NL80211_BAND_5GHZ], + RTW89_H2C_SCANOFLD_BE_W4_PROBE_5G) | + le32_encode_bits(probe_id[NL80211_BAND_6GHZ], + RTW89_H2C_SCANOFLD_BE_W4_PROBE_6G) | + le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_W4_DELAY_START); + + h2c->w5 = le32_encode_bits(option->mlo_mode, RTW89_H2C_SCANOFLD_BE_W5_MLO_MODE); + + h2c->w6 = le32_encode_bits(option->prohib_chan, + RTW89_H2C_SCANOFLD_BE_W6_CHAN_PROHIB_LOW); + h2c->w7 = le32_encode_bits(option->prohib_chan >> 32, + RTW89_H2C_SCANOFLD_BE_W7_CHAN_PROHIB_HIGH); + ptr += sizeof(*h2c); + + for (i = 0; i < option->num_macc_role; i++) { + macc_role = (struct rtw89_h2c_scanofld_be_macc_role *)&h2c->role[i]; + macc_role->w0 = + le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_MACC_ROLE_W0_BAND) | + le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_MACC_ROLE_W0_PORT) | + le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_MACC_ROLE_W0_MACID) | + le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_MACC_ROLE_W0_OPCH_END); + ptr += sizeof(*macc_role); + } + + for (i = 0; i < option->num_opch; i++) { + opch = ptr; + opch->w0 = le32_encode_bits(rtwvif->mac_id, + RTW89_H2C_SCANOFLD_BE_OPCH_W0_MACID) | + le32_encode_bits(option->band, + RTW89_H2C_SCANOFLD_BE_OPCH_W0_BAND) | + le32_encode_bits(rtwvif->port, + RTW89_H2C_SCANOFLD_BE_OPCH_W0_PORT) | + le32_encode_bits(RTW89_SCAN_OPMODE_INTV, + RTW89_H2C_SCANOFLD_BE_OPCH_W0_POLICY) | + le32_encode_bits(true, + RTW89_H2C_SCANOFLD_BE_OPCH_W0_TXNULL) | + le32_encode_bits(RTW89_OFF_CHAN_TIME / 10, + RTW89_H2C_SCANOFLD_BE_OPCH_W0_POLICY_VAL); + + opch->w1 = le32_encode_bits(RTW89_CHANNEL_TIME, + RTW89_H2C_SCANOFLD_BE_OPCH_W1_DURATION) | + le32_encode_bits(op->band_type, + RTW89_H2C_SCANOFLD_BE_OPCH_W1_CH_BAND) | + le32_encode_bits(op->band_width, + RTW89_H2C_SCANOFLD_BE_OPCH_W1_BW) | + le32_encode_bits(0x3, + RTW89_H2C_SCANOFLD_BE_OPCH_W1_NOTIFY) | + le32_encode_bits(op->primary_channel, + RTW89_H2C_SCANOFLD_BE_OPCH_W1_PRI_CH) | + le32_encode_bits(op->channel, + RTW89_H2C_SCANOFLD_BE_OPCH_W1_CENTRAL_CH); + + opch->w2 = le32_encode_bits(0, + RTW89_H2C_SCANOFLD_BE_OPCH_W2_PKTS_CTRL) | + le32_encode_bits(0, + RTW89_H2C_SCANOFLD_BE_OPCH_W2_SW_DEF) | + le32_encode_bits(2, + RTW89_H2C_SCANOFLD_BE_OPCH_W2_SS); + + opch->w3 = le32_encode_bits(RTW89_SCANOFLD_PKT_NONE, + RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT0) | + le32_encode_bits(RTW89_SCANOFLD_PKT_NONE, + RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT1) | + le32_encode_bits(RTW89_SCANOFLD_PKT_NONE, + RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT2) | + le32_encode_bits(RTW89_SCANOFLD_PKT_NONE, + RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT3); + ptr += sizeof(*opch); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD, + H2C_FUNC_SCANOFLD_BE, 1, 1, + len); + + if (option->enable) + cond = RTW89_SCANOFLD_BE_WAIT_COND_START; + else + cond = RTW89_SCANOFLD_BE_WAIT_COND_STOP; + + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_FW, "failed to scan be ofld\n"); + return ret; + } + + return 0; +} + int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, struct rtw89_fw_h2c_rf_reg_info *info, u16 len, u8 page) @@ -5173,6 +5337,7 @@ static bool rtw89_is_any_vif_connected_or_connecting(struct rtw89_dev *rtwdev) int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, bool enable) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; struct rtw89_scan_option opt = {0}; struct rtw89_vif *rtwvif; bool connected; @@ -5190,7 +5355,18 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, if (ret) goto out; } - ret = rtw89_fw_h2c_scan_offload(rtwdev, &opt, rtwvif); + + if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) { + opt.operation = enable ? RTW89_SCAN_OP_START : RTW89_SCAN_OP_STOP; + opt.scan_mode = RTW89_SCAN_MODE_SA; + opt.band = RTW89_PHY_0; + opt.num_macc_role = 0; + opt.mlo_mode = rtwdev->mlo_dbcc_mode; + opt.num_opch = connected ? 1 : 0; + opt.opch_end = connected ? 0 : RTW89_CHAN_INVALID; + } + + ret = mac->scan_offload(rtwdev, &opt, rtwvif); out: return ret; } diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 132809ea05bf..08b8f6d75065 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -266,6 +266,7 @@ struct rtw89_fw_macid_pause_sleep_grp { #define RTW89_SCANOFLD_MAX_IE_LEN 512 #define RTW89_SCANOFLD_PKT_NONE 0xFF #define RTW89_SCANOFLD_DEBUG_MASK 0x1F +#define RTW89_CHAN_INVALID 0xFF #define RTW89_MAC_CHINFO_SIZE 28 #define RTW89_SCAN_LIST_GUARD 4 #define RTW89_SCAN_LIST_LIMIT \ @@ -325,11 +326,6 @@ struct rtw89_mac_chinfo_be { bool is_psc; }; -struct rtw89_scan_option { - bool enable; - bool target_ch_mode; -}; - struct rtw89_pktofld_info { struct list_head list; u8 id; @@ -2814,6 +2810,79 @@ struct rtw89_h2c_scanofld { #define RTW89_H2C_SCANOFLD_W2_NORM_PD GENMASK(15, 0) #define RTW89_H2C_SCANOFLD_W2_SLOW_PD GENMASK(23, 16) +struct rtw89_h2c_scanofld_be_macc_role { + __le32 w0; +} __packed; + +#define RTW89_H2C_SCANOFLD_BE_MACC_ROLE_W0_BAND GENMASK(1, 0) +#define RTW89_H2C_SCANOFLD_BE_MACC_ROLE_W0_PORT GENMASK(4, 2) +#define RTW89_H2C_SCANOFLD_BE_MACC_ROLE_W0_MACID GENMASK(23, 8) +#define RTW89_H2C_SCANOFLD_BE_MACC_ROLE_W0_OPCH_END GENMASK(31, 24) + +struct rtw89_h2c_scanofld_be_opch { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; +} __packed; + +#define RTW89_H2C_SCANOFLD_BE_OPCH_W0_MACID GENMASK(15, 0) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W0_BAND GENMASK(17, 16) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W0_PORT GENMASK(20, 18) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W0_POLICY GENMASK(22, 21) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W0_TXNULL BIT(23) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W0_POLICY_VAL GENMASK(31, 24) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W1_DURATION GENMASK(7, 0) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W1_CH_BAND GENMASK(9, 8) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W1_BW GENMASK(12, 10) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W1_NOTIFY GENMASK(14, 13) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W1_PRI_CH GENMASK(23, 16) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W1_CENTRAL_CH GENMASK(31, 24) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W2_PKTS_CTRL GENMASK(7, 0) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W2_SW_DEF GENMASK(15, 8) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W2_SS GENMASK(18, 16) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT0 GENMASK(7, 0) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT1 GENMASK(15, 8) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT2 GENMASK(23, 16) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT3 GENMASK(31, 24) + +struct rtw89_h2c_scanofld_be { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; + __le32 w4; + __le32 w5; + __le32 w6; + __le32 w7; + struct rtw89_h2c_scanofld_be_macc_role role[]; +} __packed; + +#define RTW89_H2C_SCANOFLD_BE_W0_OP GENMASK(1, 0) +#define RTW89_H2C_SCANOFLD_BE_W0_SCAN_MODE GENMASK(3, 2) +#define RTW89_H2C_SCANOFLD_BE_W0_REPEAT GENMASK(5, 4) +#define RTW89_H2C_SCANOFLD_BE_W0_NOTIFY_END BIT(6) +#define RTW89_H2C_SCANOFLD_BE_W0_LEARN_CH BIT(7) +#define RTW89_H2C_SCANOFLD_BE_W0_MACID GENMASK(23, 8) +#define RTW89_H2C_SCANOFLD_BE_W0_PORT GENMASK(26, 24) +#define RTW89_H2C_SCANOFLD_BE_W0_BAND GENMASK(28, 27) +#define RTW89_H2C_SCANOFLD_BE_W1_NUM_MACC_ROLE GENMASK(7, 0) +#define RTW89_H2C_SCANOFLD_BE_W1_NUM_OP GENMASK(15, 8) +#define RTW89_H2C_SCANOFLD_BE_W1_NORM_PD GENMASK(31, 16) +#define RTW89_H2C_SCANOFLD_BE_W2_SLOW_PD GENMASK(15, 0) +#define RTW89_H2C_SCANOFLD_BE_W2_NORM_CY GENMASK(23, 16) +#define RTW89_H2C_SCANOFLD_BE_W2_OPCH_END GENMASK(31, 24) +#define RTW89_H2C_SCANOFLD_BE_W3_NUM_SSID GENMASK(7, 0) +#define RTW89_H2C_SCANOFLD_BE_W3_NUM_SHORT_SSID GENMASK(15, 8) +#define RTW89_H2C_SCANOFLD_BE_W3_NUM_BSSID GENMASK(23, 16) +#define RTW89_H2C_SCANOFLD_BE_W3_PROBEID GENMASK(31, 24) +#define RTW89_H2C_SCANOFLD_BE_W4_PROBE_5G GENMASK(7, 0) +#define RTW89_H2C_SCANOFLD_BE_W4_PROBE_6G GENMASK(15, 8) +#define RTW89_H2C_SCANOFLD_BE_W4_DELAY_START GENMASK(31, 16) +#define RTW89_H2C_SCANOFLD_BE_W5_MLO_MODE GENMASK(31, 0) +#define RTW89_H2C_SCANOFLD_BE_W6_CHAN_PROHIB_LOW GENMASK(31, 0) +#define RTW89_H2C_SCANOFLD_BE_W7_CHAN_PROHIB_HIGH GENMASK(31, 0) + static inline void RTW89_SET_FWCMD_P2P_MACID(void *cmd, u32 val) { le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0)); @@ -3773,6 +3842,7 @@ enum rtw89_fw_ofld_h2c_func { H2C_FUNC_OFLD_RSSI = 0x1f, H2C_FUNC_OFLD_TP = 0x20, H2C_FUNC_MAC_MACID_PAUSE_SLEEP = 0x28, + H2C_FUNC_SCANOFLD_BE = 0x2c, NUM_OF_RTW89_FW_OFLD_H2C_FUNC, }; @@ -3788,6 +3858,9 @@ enum rtw89_fw_ofld_h2c_func { #define RTW89_SCANOFLD_WAIT_COND_START RTW89_FW_OFLD_WAIT_COND(0, H2C_FUNC_SCANOFLD) #define RTW89_SCANOFLD_WAIT_COND_STOP RTW89_FW_OFLD_WAIT_COND(1, H2C_FUNC_SCANOFLD) +#define RTW89_SCANOFLD_BE_WAIT_COND_START RTW89_FW_OFLD_WAIT_COND(0, H2C_FUNC_SCANOFLD_BE) +#define RTW89_SCANOFLD_BE_WAIT_COND_STOP RTW89_FW_OFLD_WAIT_COND(1, H2C_FUNC_SCANOFLD_BE) + /* CLASS 10 - Security CAM */ #define H2C_CL_MAC_SEC_CAM 0xa @@ -4009,6 +4082,9 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num, int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev, struct rtw89_scan_option *opt, struct rtw89_vif *vif); +int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, + struct rtw89_scan_option *opt, + struct rtw89_vif *vif); int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, struct rtw89_fw_h2c_rf_reg_info *info, u16 len, u8 page); diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 0bb38f401760..96a98774c322 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4867,6 +4867,9 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le case H2C_FUNC_SCANOFLD: cond = RTW89_SCANOFLD_WAIT_COND_START; break; + case H2C_FUNC_SCANOFLD_BE: + cond = RTW89_SCANOFLD_BE_WAIT_COND_START; + break; } data.err = !!h2c_return; @@ -5116,14 +5119,21 @@ static void rtw89_mac_c2h_scanofld_rsp_atomic(struct rtw89_dev *rtwdev, (const struct rtw89_c2h_scanofld *)skb->data; struct rtw89_wait_info *fw_ofld_wait = &rtwdev->mac.fw_ofld_wait; struct rtw89_completion_data data = {}; + unsigned int cond; u8 status, reason; status = le32_get_bits(c2h->w2, RTW89_C2H_SCANOFLD_W2_STATUS); reason = le32_get_bits(c2h->w2, RTW89_C2H_SCANOFLD_W2_RSN); data.err = status != RTW89_SCAN_STATUS_SUCCESS; - if (reason == RTW89_SCAN_END_SCAN_NOTIFY) - rtw89_complete_cond(fw_ofld_wait, RTW89_SCANOFLD_WAIT_COND_STOP, &data); + if (reason == RTW89_SCAN_END_SCAN_NOTIFY) { + if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) + cond = RTW89_SCANOFLD_BE_WAIT_COND_STOP; + else + cond = RTW89_SCANOFLD_WAIT_COND_STOP; + + rtw89_complete_cond(fw_ofld_wait, cond, &data); + } } bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, struct sk_buff *c2h, @@ -6227,5 +6237,6 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .is_txq_empty = mac_is_txq_empty_ax, .add_chan_list = rtw89_hw_scan_add_chan_list, + .scan_offload = rtw89_fw_h2c_scan_offload, }; EXPORT_SYMBOL(rtw89_mac_gen_ax); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 7d7fbd18491e..8968892175c1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -956,6 +956,9 @@ struct rtw89_mac_gen_def { int (*add_chan_list)(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool connected); + int (*scan_offload)(struct rtw89_dev *rtwdev, + struct rtw89_scan_option *option, + struct rtw89_vif *rtwvif); }; extern const struct rtw89_mac_gen_def rtw89_mac_gen_ax; diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 2213451740e0..7369514189b8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2366,5 +2366,6 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .is_txq_empty = mac_is_txq_empty_be, .add_chan_list = rtw89_hw_scan_add_chan_list_be, + .scan_offload = rtw89_fw_h2c_scan_offload_be, }; EXPORT_SYMBOL(rtw89_mac_gen_be); From e58e3117019cfa732706211c151fd94d8fb08ce3 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Fri, 26 Jan 2024 14:33:54 +0800 Subject: [PATCH 124/378] wifi: rtw89: add new H2C for PS mode in 802.11be chip Because 802.11be chip support MLO mode, driver needs to send new H2C to pass more connected channel information to firmware, to ensure PS mode work fine. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240126063356.17857-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 44 +++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 16 +++++++++ drivers/net/wireless/realtek/rtw89/ps.c | 7 ++-- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 7a0671e4ef2c..2f3f2b503507 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2036,6 +2036,50 @@ int rtw89_fw_h2c_lps_parm(struct rtw89_dev *rtwdev, return ret; } +int rtw89_fw_h2c_lps_ch_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, + rtwvif->sub_entity_idx); + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_lps_ch_info *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen != RTW89_CHIP_BE) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c lps_ch_info\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_lps_ch_info *)skb->data; + + h2c->info[0].central_ch = chan->channel; + h2c->info[0].pri_ch = chan->primary_channel; + h2c->info[0].band = chan->band_type; + h2c->info[0].bw = chan->band_width; + h2c->mlo_dbcc_mode_lps = cpu_to_le32(MLO_2_PLUS_0_1RF); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_DM, + H2C_FUNC_FW_LPS_CH_INFO, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + #define H2C_P2P_ACT_LEN 20 int rtw89_fw_h2c_p2p_act(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_p2p_noa_desc *desc, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 08b8f6d75065..c5c801e9ce3c 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -1974,6 +1974,17 @@ static inline void SET_LPS_PARM_LASTRPWM(void *h2c, u32 val) le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(15, 8)); } +struct rtw89_h2c_lps_ch_info { + struct { + u8 pri_ch; + u8 central_ch; + u8 bw; + u8 band; + } __packed info[2]; + + __le32 mlo_dbcc_mode_lps; +} __packed; + static inline void RTW89_SET_FWCMD_CPU_EXCEPTION_TYPE(void *cmd, u32 val) { le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 0)); @@ -3896,6 +3907,9 @@ enum rtw89_mcc_h2c_func { #define H2C_CL_OUTSRC_RA 0x1 #define H2C_FUNC_OUTSRC_RA_MACIDCFG 0x0 +#define H2C_CL_OUTSRC_DM 0x2 +#define H2C_FUNC_FW_LPS_CH_INFO 0xb + #define H2C_CL_OUTSRC_RF_REG_A 0x8 #define H2C_CL_OUTSRC_RF_REG_B 0x9 #define H2C_CL_OUTSRC_RF_FW_NOTIFY 0xa @@ -4110,6 +4124,8 @@ int rtw89_fw_h2c_init_ba_cam_users(struct rtw89_dev *rtwdev, u8 users, int rtw89_fw_h2c_lps_parm(struct rtw89_dev *rtwdev, struct rtw89_lps_parm *lps_param); +int rtw89_fw_h2c_lps_ch_info(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif); struct sk_buff *rtw89_fw_h2c_alloc_skb_with_hdr(struct rtw89_dev *rtwdev, u32 len); struct sk_buff *rtw89_fw_h2c_alloc_skb_no_hdr(struct rtw89_dev *rtwdev, u32 len); int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index 917c01e5e9ed..be1dcd9eb955 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -83,16 +83,17 @@ void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev) rtw89_ps_power_mode_change(rtwdev, false); } -static void __rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id) +static void __rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { struct rtw89_lps_parm lps_param = { - .macid = mac_id, + .macid = rtwvif->mac_id, .psmode = RTW89_MAC_AX_PS_MODE_LEGACY, .lastrpwm = RTW89_LAST_RPWM_PS, }; rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_FW_CTRL); rtw89_fw_h2c_lps_parm(rtwdev, &lps_param); + rtw89_fw_h2c_lps_ch_info(rtwdev, rtwvif); } static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, u8 mac_id) @@ -123,7 +124,7 @@ void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags)) return; - __rtw89_enter_lps(rtwdev, rtwvif->mac_id); + __rtw89_enter_lps(rtwdev, rtwvif); if (ps_mode) __rtw89_enter_ps_mode(rtwdev, rtwvif); } From f651300cd8849a3703892fe5efd397c7a44a7f60 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Fri, 26 Jan 2024 14:33:55 +0800 Subject: [PATCH 125/378] wifi: rtw89: update ps_state register for chips with different generation The ps_state register is used for driver to check if the WiFi chip leave power save mode successfully. The register is changed for new generation, so update it. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240126063356.17857-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 1 + drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/mac_be.c | 1 + drivers/net/wireless/realtek/rtw89/ps.c | 3 ++- drivers/net/wireless/realtek/rtw89/reg.h | 3 +++ 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 96a98774c322..dbf2d6fe4ea7 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -6184,6 +6184,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .rx_fltr = R_AX_RX_FLTR_OPT, .port_base = &rtw89_port_base_ax, .agg_len_ht = R_AX_AGG_LEN_HT_0, + .ps_status = R_AX_PPWRBIT_SETTING, .muedca_ctrl = { .addr = R_AX_MUEDCA_EN, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 8968892175c1..b3fe4cab6d3a 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -894,6 +894,7 @@ struct rtw89_mac_gen_def { u32 rx_fltr; const struct rtw89_port_reg *port_base; u32 agg_len_ht; + u32 ps_status; struct rtw89_reg_def muedca_ctrl; struct rtw89_reg_def bfee_ctrl; diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 7369514189b8..13e6fa7a3ae8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2313,6 +2313,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .rx_fltr = R_BE_RX_FLTR_OPT, .port_base = &rtw89_port_base_be, .agg_len_ht = R_BE_AGG_LEN_HT_0, + .ps_status = R_BE_WMTX_POWER_BE_BIT_CTL, .muedca_ctrl = { .addr = R_BE_MUEDCA_EN, diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index be1dcd9eb955..31290d8cb7f7 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -14,6 +14,7 @@ static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u32 pwr_en_bit = 0xE; u32 chk_msk = pwr_en_bit << (4 * macid); u32 polling; @@ -21,7 +22,7 @@ static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid) ret = read_poll_timeout_atomic(rtw89_read32_mask, polling, !polling, 1000, 50000, false, rtwdev, - R_AX_PPWRBIT_SETTING, chk_msk); + mac->ps_status, chk_msk); if (ret) { rtw89_info(rtwdev, "rtw89: failed to leave lps state\n"); return -EBUSY; diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index addf11fc76d4..bd7bdd216e5f 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -6794,6 +6794,9 @@ #define B_BE_UPD_HGQMD BIT(1) #define B_BE_UPD_TIMIE BIT(0) +#define R_BE_WMTX_POWER_BE_BIT_CTL 0x10E0C +#define R_BE_WMTX_POWER_BE_BIT_CTL_C1 0x14E0C + #define R_BE_WMTX_TCR_BE_4 0x10E2C #define R_BE_WMTX_TCR_BE_4_C1 0x14E2C #define B_BE_UL_EHT_MUMIMO_LTF_MODE BIT(30) From f1abee76dba829ada6e30ac640443e81bde2f878 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 26 Jan 2024 14:33:56 +0800 Subject: [PATCH 126/378] wifi: rtw89: 8922a: add more fields to beacon H2C command to support multi-links To support multi-links beacon, it needs more fields. But currently we still only support legacy AP mode. Only update struct to fit expected size of firmware. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240126063356.17857-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index c5c801e9ce3c..ae69e455cd64 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -1730,6 +1730,24 @@ struct rtw89_h2c_bcn_upd_be { __le32 w9; __le32 w10; __le32 w11; + __le32 w12; + __le32 w13; + __le32 w14; + __le32 w15; + __le32 w16; + __le32 w17; + __le32 w18; + __le32 w19; + __le32 w20; + __le32 w21; + __le32 w22; + __le32 w23; + __le32 w24; + __le32 w25; + __le32 w26; + __le32 w27; + __le32 w28; + __le32 w29; } __packed; #define RTW89_H2C_BCN_UPD_BE_W0_PORT GENMASK(7, 0) From 17903a283593c1dbf9da041f836004163ca30f7b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 31 Jan 2024 10:10:07 +0300 Subject: [PATCH 127/378] wifi: rtl8xxxu: fix error messages The first parameter of WARN_ONCE() is a condition so this code will end up printing the function name instead of the proper message. Fixes: 3ff7a05996f9 ("wifi: rtl8xxxu: support setting bssid register for multiple interfaces") Signed-off-by: Dan Carpenter Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/7b144531-a8da-4725-8911-9b614a525a35@moroto.mountain --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 3b954c2fe448..bd6fd3120562 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -3593,7 +3593,7 @@ static int rtl8xxxu_set_mac(struct rtl8xxxu_priv *priv, int port_num) reg = REG_MACID1; break; default: - WARN_ONCE("%s: invalid port_num\n", __func__); + WARN_ONCE(1, "%s: invalid port_num\n", __func__); return -EINVAL; } @@ -3618,7 +3618,7 @@ static int rtl8xxxu_set_bssid(struct rtl8xxxu_priv *priv, const u8 *bssid, int p reg = REG_BSSID1; break; default: - WARN_ONCE("%s: invalid port_num\n", __func__); + WARN_ONCE(1, "%s: invalid port_num\n", __func__); return -EINVAL; } From 10159a45666bf127afe8d7c654351d542e7fcb42 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 22:56:32 +0200 Subject: [PATCH 128/378] wifi: iwlwifi: disable eSR when BT is active eSR should be disabled when BT Coex is active and: - LB link is the primary link. - LB link is the secondary link and the predicted BT penalty (the wifi loss rate caused by BT interference) is higher than a given threshold. If one of the conditions above is no longer true then re-enable eSR. In order to implement this, add support for version 5 of BT_PROFILE_NOTIFICATION, in which the bt penalty is provided by FW. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131225342.b922b6485af8.I7d808ce535a7372aca9cb85c045755e6788a4904@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/fw/api/coex.h | 14 +- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 124 ++++++++++++++++++ .../wireless/intel/iwlwifi/mvm/constants.h | 3 + .../wireless/intel/iwlwifi/mvm/mld-mac80211.c | 107 ++++++++++++++- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 21 ++- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 5 +- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 13 +- 7 files changed, 275 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h index 3e81e9369224..bc27e15488f5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* + * Copyright (C) 2023 Intel Corporation * Copyright (C) 2013-2014, 2018-2019 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2017 Intel Deutschland GmbH @@ -170,7 +171,11 @@ enum iwl_bt_ci_compliance { * @bt_activity_grading: the activity of BT &enum iwl_bt_activity_grading * @ttc_status: is TTC enabled - one bit per PHY * @rrc_status: is RRC enabled - one bit per PHY - * @reserved: reserved + * The following fields are only for version 5, and are reserved in version 4: + * @wifi_loss_low_rssi: The predicted lost WiFi rate (% of air time that BT is + * utilizing) when the RSSI is low (<= -65 dBm) + * @wifi_loss_mid_high_rssi: The predicted lost WiFi rate (% of air time that + * BT is utilizing) when the RSSI is mid/high (>= -65 dBm) */ struct iwl_bt_coex_profile_notif { __le32 mbox_msg[4]; @@ -182,7 +187,10 @@ struct iwl_bt_coex_profile_notif { __le32 bt_activity_grading; u8 ttc_status; u8 rrc_status; - __le16 reserved; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ + u8 wifi_loss_low_rssi; + u8 wifi_loss_mid_high_rssi; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 + * BT_COEX_PROFILE_NTFY_API_S_VER_5 + */ #endif /* __iwl_fw_api_coex_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 9fe1761691ec..d26075e3e6ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -252,6 +252,124 @@ static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm, swap(data->primary, data->secondary); } +static void iwl_mvm_bt_coex_enable_esr(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, bool enable) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int link_id; + + lockdep_assert_held(&mvm->mutex); + + if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) + return; + + /* Done already */ + if (mvmvif->bt_coex_esr_disabled == !enable) + return; + + mvmvif->bt_coex_esr_disabled = !enable; + + /* Nothing to do */ + if (mvmvif->esr_active == enable) + return; + + if (enable) { + /* Try to re-enable eSR*/ + iwl_mvm_mld_select_links(mvm, vif, false); + return; + } + + /* + * Find the primary link, as we want to switch to it and drop the + * secondary one. + */ + link_id = iwl_mvm_mld_get_primary_link(mvm, vif, vif->active_links); + WARN_ON(link_id < 0); + + ieee80211_set_active_links_async(vif, + vif->active_links & BIT(link_id)); +} + +/* + * This function receives the LB link id and checks if eSR should be + * enabled or disabled (due to BT coex) + */ +bool +iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + int link_id, int primary_link) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; + bool have_wifi_loss_rate = + iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, + BT_PROFILE_NOTIFICATION, 0) > 4; + s8 link_rssi = 0; + u8 wifi_loss_rate; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->last_bt_notif.wifi_loss_low_rssi == BT_OFF) + return true; + + /* If LB link is the primary one we should always disable eSR */ + if (link_id == primary_link) + return false; + + /* The feature is not supported */ + if (!have_wifi_loss_rate) + return true; + + /* + * We might not have a link_info when checking whether we can + * (re)enable eSR - the LB link might not exist yet + */ + if (link_info) + link_rssi = (s8)link_info->beacon_stats.avg_signal; + + /* + * In case we don't know the RSSI - take the lower wifi loss, + * so we will more likely enter eSR, and if RSSI is low - + * we will get an update on this and exit eSR. + */ + if (!link_rssi) + wifi_loss_rate = mvm->last_bt_notif.wifi_loss_mid_high_rssi; + + else if (!mvmvif->bt_coex_esr_disabled) + /* RSSI needs to get really low to disable eSR... */ + wifi_loss_rate = + link_rssi <= -IWL_MVM_BT_COEX_DISABLE_ESR_THRESH ? + mvm->last_bt_notif.wifi_loss_low_rssi : + mvm->last_bt_notif.wifi_loss_mid_high_rssi; + else + /* ...And really high before we enable it back */ + wifi_loss_rate = + link_rssi <= -IWL_MVM_BT_COEX_ENABLE_ESR_THRESH ? + mvm->last_bt_notif.wifi_loss_low_rssi : + mvm->last_bt_notif.wifi_loss_mid_high_rssi; + + return wifi_loss_rate <= IWL_MVM_BT_COEX_WIFI_LOSS_THRESH; +} + +void iwl_mvm_bt_coex_update_vif_esr(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + int link_id) +{ + unsigned long usable_links = ieee80211_vif_usable_links(vif); + int primary_link = iwl_mvm_mld_get_primary_link(mvm, vif, + usable_links); + bool enable; + + /* Not assoc, not MLD vif or only one usable link */ + if (primary_link < 0) + return; + + enable = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id, + primary_link); + + iwl_mvm_bt_coex_enable_esr(mvm, vif, enable); +} + static void iwl_mvm_bt_notif_per_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_bt_iterator_data *data, @@ -297,6 +415,8 @@ static void iwl_mvm_bt_notif_per_link(struct iwl_mvm *mvm, return; } + iwl_mvm_bt_coex_update_vif_esr(mvm, vif, link_id); + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2)) min_ag_for_static_smps = BT_VERY_HIGH_TRAFFIC; else @@ -432,6 +552,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, return; } + /* When BT is off this will be 0 */ + if (data->notif->wifi_loss_low_rssi == BT_OFF) + iwl_mvm_bt_coex_enable_esr(mvm, vif, true); + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) iwl_mvm_bt_notif_per_link(mvm, vif, data, link_id); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index c832068b5718..f5122c4678a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -11,6 +11,9 @@ #include "fw-api.h" #define IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM 20 +#define IWL_MVM_BT_COEX_DISABLE_ESR_THRESH 69 +#define IWL_MVM_BT_COEX_ENABLE_ESR_THRESH 63 +#define IWL_MVM_BT_COEX_WIFI_LOSS_THRESH 0 #define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC) #define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 449229ced3bb..2b879eeecc8c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -603,6 +603,7 @@ static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw, struct iwl_mvm_link_sel_data { u8 link_id; enum nl80211_band band; + enum nl80211_chan_width width; bool active; }; @@ -655,6 +656,7 @@ void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, data[n_data].link_id = link_id; data[n_data].band = link_conf->chandef.chan->band; + data[n_data].width = link_conf->chandef.width; data[n_data].active = vif->active_links & BIT(link_id); n_data++; } @@ -1215,13 +1217,116 @@ iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw, return ret; } +/* + * This function receives a subset of the usable links bitmap and + * returns the primary link id, and -1 if such link doesn't exist + * (e.g. non-MLO connection) or wasn't found. + */ +int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + unsigned long usable_links) +{ + struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS]; + u8 link_id, n_data = 0; + + if (!ieee80211_vif_is_mld(vif) || !vif->cfg.assoc) + return -1; + + for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + + if (WARN_ON_ONCE(!link_conf)) + continue; + + data[n_data].link_id = link_id; + data[n_data].band = link_conf->chandef.chan->band; + data[n_data].width = link_conf->chandef.width; + data[n_data].active = true; + n_data++; + } + + if (n_data <= 1) + return -1; + + /* The logic should be modified to handle more than 2 links */ + WARN_ON_ONCE(n_data > 2); + + /* Primary link is the link with the wider bandwidth or higher band */ + if (data[0].width > data[1].width) + return data[0].link_id; + if (data[0].width < data[1].width) + return data[1].link_id; + if (data[0].band >= data[1].band) + return data[0].link_id; + + return data[1].link_id; +} + +/* + * This function receives a bitmap of usable links and check if we can enter + * eSR on those links. + */ +static bool iwl_mvm_can_enter_esr(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + unsigned long desired_links) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int primary_link = iwl_mvm_mld_get_primary_link(mvm, vif, + desired_links); + bool ret = true; + int link_id; + + if (primary_link < 0) + return false; + + for_each_set_bit(link_id, &desired_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + + if (WARN_ON_ONCE(!link_conf)) + continue; + + /* BT Coex effects eSR mode only if one of the link is on LB */ + if (link_conf->chandef.chan->band != NL80211_BAND_2GHZ) + continue; + + ret = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id, + primary_link); + // Mark eSR as disabled for the next time + if (!ret) + mvmvif->bt_coex_esr_disabled = true; + break; + } + + return ret; +} + static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 desired_links) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int n_links = hweight16(desired_links); + bool ret = true; - return hweight16(desired_links) <= iwl_mvm_max_active_links(mvm, vif); + if (n_links <= 1) + return true; + + mutex_lock(&mvm->mutex); + + /* Check if HW supports the wanted number of links */ + if (n_links > iwl_mvm_max_active_links(mvm, vif)) { + ret = false; + goto unlock; + } + + /* If it is an eSR device, check that we can enter eSR */ + if (iwl_mvm_is_esr_supported(mvm->fwrt.trans)) + ret = iwl_mvm_can_enter_esr(mvm, vif, desired_links); +unlock: + mutex_unlock(&mvm->mutex); + return ret; } const struct ieee80211_ops iwl_mvm_mld_hw_ops = { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index af5c8b4bb5a6..9a89b91519db 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -359,6 +359,7 @@ struct iwl_mvm_vif_link_info { * @pm_enabled - indicate if MAC power management is allowed * @monitor_active: indicates that monitor context is configured, and that the * interface should get quota etc. + * @bt_coex_esr_disabled: indicates if esr is disabled due to bt coex * @low_latency: bit flags for low latency * see enum &iwl_mvm_low_latency_cause for causes. * @low_latency_actual: boolean, indicates low latency is set, @@ -389,6 +390,7 @@ struct iwl_mvm_vif { bool pm_enabled; bool monitor_active; bool esr_active; + bool bt_coex_esr_disabled; u8 low_latency: 6; u8 low_latency_actual: 1; @@ -1570,13 +1572,17 @@ static inline int iwl_mvm_max_active_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_trans *trans = mvm->fwrt.trans; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + lockdep_assert_held(&mvm->mutex); if (vif->type == NL80211_IFTYPE_AP) return mvm->fw->ucode_capa.num_beacons; - if (iwl_mvm_is_esr_supported(trans) || - (CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM && - CSR_HW_RFID_IS_CDB(trans->hw_rf_id))) + if ((iwl_mvm_is_esr_supported(trans) && + !mvmvif->bt_coex_esr_disabled) || + ((CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM && + CSR_HW_RFID_IS_CDB(trans->hw_rf_id)))) return IWL_MVM_FW_MAX_ACTIVE_LINKS_NUM; return 1; @@ -2119,6 +2125,12 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, u8 iwl_mvm_bt_coex_get_single_ant_msk(struct iwl_mvm *mvm, u8 enabled_ants); u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *info, u8 ac); +bool iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + int link_id, int primary_link); +void iwl_mvm_bt_coex_update_vif_esr(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + int link_id); /* beacon filtering */ #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -2733,4 +2745,7 @@ bool iwl_mvm_enable_fils(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *ctx); void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool valid_links_changed); +int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + unsigned long usable_links); #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 38a84a54ff78..871274eea26f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -320,7 +320,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { struct iwl_tlc_update_notif), RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, - RX_HANDLER_ASYNC_LOCKED, struct iwl_bt_coex_profile_notif), + RX_HANDLER_ASYNC_LOCKED_WIPHY, + struct iwl_bt_coex_profile_notif), RX_HANDLER_NO_SIZE(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, RX_HANDLER_ASYNC_LOCKED), RX_HANDLER_NO_SIZE(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, @@ -328,7 +329,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_GRP(STATISTICS_GROUP, STATISTICS_OPER_NOTIF, iwl_mvm_handle_rx_system_oper_stats, - RX_HANDLER_ASYNC_LOCKED, + RX_HANDLER_ASYNC_LOCKED_WIPHY, struct iwl_system_statistics_notif_oper), RX_HANDLER_GRP(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF, iwl_mvm_handle_rx_system_oper_part1_stats, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 8caa971770c6..72df41996464 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -841,6 +841,7 @@ iwl_mvm_stat_iterator_all_links(struct iwl_mvm *mvm, struct iwl_stats_ntfy_per_link *link_stats; struct ieee80211_bss_conf *bss_conf; struct iwl_mvm_vif *mvmvif; + struct iwl_mvm_vif_link_info *link_info; int link_id; int sig; @@ -857,20 +858,26 @@ iwl_mvm_stat_iterator_all_links(struct iwl_mvm *mvm, continue; mvmvif = iwl_mvm_vif_from_mac80211(bss_conf->vif); - if (!mvmvif || !mvmvif->link[link_id]) + link_info = mvmvif->link[link_id]; + if (!link_info) continue; link_stats = &per_link[fw_link_id]; - mvmvif->link[link_id]->beacon_stats.num_beacons = + link_info->beacon_stats.num_beacons = le32_to_cpu(link_stats->beacon_counter); /* we basically just use the u8 to store 8 bits and then treat * it as a s8 whenever we take it out to a different type. */ - mvmvif->link[link_id]->beacon_stats.avg_signal = + link_info->beacon_stats.avg_signal = -le32_to_cpu(link_stats->beacon_average_energy); + if (link_info->phy_ctxt && + link_info->phy_ctxt->channel->band == NL80211_BAND_2GHZ) + iwl_mvm_bt_coex_update_vif_esr(mvm, bss_conf->vif, + link_id); + /* make sure that beacon statistics don't go backwards with TCM * request to clear statistics */ From 619a900f279800876e425ce4ef41c4e493bdf7d4 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 31 Jan 2024 23:08:16 +0200 Subject: [PATCH 129/378] wifi: iwlwifi: mvm: Add support for removing responder TKs When removing a PASN station, the TK must be removed before the station is removed as otherwise the FW would assert. To handle this, store the key configuration, and use it to remove the key when the station is removed. Signed-off-by: Ilan Peer Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240131230734.3e6364730c04.Ia76dc4a9d399f1f68ac6b157d844b63f74d5159f@changeid Signed-off-by: Johannes Berg --- .../wireless/intel/iwlwifi/mvm/ftm-responder.c | 11 +++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c | 15 +++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 ++++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 15 ++++----------- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 3 ++- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c index 8f10590f9cdd..f72ca38d7c0e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c @@ -12,6 +12,9 @@ struct iwl_mvm_pasn_sta { struct list_head list; struct iwl_mvm_int_sta int_sta; u8 addr[ETH_ALEN]; + + /* must be last as it followed by buffer holding the key */ + struct ieee80211_key_conf keyconf; }; struct iwl_mvm_pasn_hltk_data { @@ -303,6 +306,10 @@ static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm, { list_del(&sta->list); + if (sta->keyconf.keylen) + iwl_mvm_sec_key_del_pasn(mvm, vif, BIT(sta->int_sta.sta_id), + &sta->keyconf); + if (iwl_mvm_has_mld_api(mvm->fw)) iwl_mvm_mld_rm_sta_id(mvm, sta->int_sta.sta_id); else @@ -352,12 +359,12 @@ int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm, } if (tk && tk_len) { - sta = kzalloc(sizeof(*sta), GFP_KERNEL); + sta = kzalloc(sizeof(*sta) + tk_len, GFP_KERNEL); if (!sta) return -ENOBUFS; ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr, - cipher, tk, tk_len); + cipher, tk, tk_len, &sta->keyconf); if (ret) { kfree(sta); return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c index ea3e9e9c6e26..a1ce08a5527c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c @@ -335,6 +335,21 @@ static int _iwl_mvm_sec_key_del(struct iwl_mvm *mvm, return ret; } +int iwl_mvm_sec_key_del_pasn(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 sta_mask, + struct ieee80211_key_conf *keyconf) +{ + u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, NULL, keyconf) | + IWL_SEC_KEY_FLAG_MFP; + + if (WARN_ON(!sta_mask)) + return -EINVAL; + + return __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx, + 0); +} + int iwl_mvm_sec_key_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 9a89b91519db..e148ef02ff73 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2402,6 +2402,10 @@ int iwl_mvm_sec_key_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *keyconf); +int iwl_mvm_sec_key_del_pasn(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 sta_mask, + struct ieee80211_key_conf *keyconf); void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_vif_link_info *link, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 2a3ca9785974..d57fcbe4f8ac 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -4298,12 +4298,12 @@ u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data) int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher, - u8 *key, u32 key_len) + u8 *key, u32 key_len, + struct ieee80211_key_conf *keyconf) { int ret; u16 queue; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_key_conf *keyconf; unsigned int wdg_timeout = iwl_mvm_get_wd_timeout(mvm, vif, false, false); bool mld = iwl_mvm_has_mld_api(mvm->fw); @@ -4328,12 +4328,6 @@ int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (ret) goto out; - keyconf = kzalloc(sizeof(*keyconf) + key_len, GFP_KERNEL); - if (!keyconf) { - ret = -ENOBUFS; - goto out; - } - keyconf->cipher = cipher; memcpy(keyconf->key, key, key_len); keyconf->keylen = key_len; @@ -4354,10 +4348,9 @@ int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 0, NULL, 0, 0, true); } - kfree(keyconf); - return 0; out: - iwl_mvm_dealloc_int_sta(mvm, sta); + if (ret) + iwl_mvm_dealloc_int_sta(mvm, sta); return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index b33a0ce096d4..4668f413abd3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -574,7 +574,8 @@ void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk); int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher, - u8 *key, u32 key_len); + u8 *key, u32 key_len, + struct ieee80211_key_conf *key_conf_out); void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 id); From 91380f768d7f6e3d003755defa792e9a00a1444a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 28 Jan 2024 08:53:48 +0200 Subject: [PATCH 130/378] wifi: iwlwifi: mvm: report beacon protection failures Andrei reports that we just silently drop beacons after we report the key counters, but never report to userspace, so wpa_supplicant cannot send the WNM action frame. Fix that. Fixes: b1fdc2505abc ("iwlwifi: mvm: advertise BIGTK client support if available") Reported-by: Andrei Otcheretianski Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240128084842.7d855442cdce.Iba90b26f893dc8c49bfb8be65373cd0a138af12c@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 886d00098528..451af501c7a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -282,6 +282,7 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta, u32 status, struct ieee80211_rx_status *stats) { + struct wireless_dev *wdev; struct iwl_mvm_sta *mvmsta; struct iwl_mvm_vif *mvmvif; u8 keyid; @@ -303,9 +304,15 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta, if (!ieee80211_is_beacon(hdr->frame_control)) return 0; + if (!sta) + return -1; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + /* key mismatch - will also report !MIC_OK but we shouldn't count it */ if (!(status & IWL_RX_MPDU_STATUS_KEY_VALID)) - return -1; + goto report; /* good cases */ if (likely(status & IWL_RX_MPDU_STATUS_MIC_OK && @@ -314,13 +321,6 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta, return 0; } - if (!sta) - return -1; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - - mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - /* * both keys will have the same cipher and MIC length, use * whichever one is available @@ -329,11 +329,11 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta, if (!key) { key = rcu_dereference(mvmvif->bcn_prot.keys[1]); if (!key) - return -1; + goto report; } if (len < key->icv_len + IEEE80211_GMAC_PN_LEN + 2) - return -1; + goto report; /* get the real key ID */ keyid = frame[len - key->icv_len - IEEE80211_GMAC_PN_LEN - 2]; @@ -347,7 +347,7 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta, return -1; key = rcu_dereference(mvmvif->bcn_prot.keys[keyid - 6]); if (!key) - return -1; + goto report; } /* Report status to mac80211 */ @@ -355,6 +355,10 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta, ieee80211_key_mic_failure(key); else if (status & IWL_RX_MPDU_STATUS_REPLAY_ERROR) ieee80211_key_replay(key); +report: + wdev = ieee80211_vif_to_wdev(mvmsta->vif); + if (wdev->netdev) + cfg80211_rx_unprot_mlme_mgmt(wdev->netdev, (void *)hdr, len); return -1; } From 7255263962ae6a41c73e44fb3520072ef74492a7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 28 Jan 2024 08:53:49 +0200 Subject: [PATCH 131/378] wifi: iwlwifi: mvm: d3: disconnect on GTK rekey failure If there was a rekey failure during D3 when firmware is handling the GTK rekeying, and it decided that we should wake up, then there was an issue in the connection and we don't necessarily have the right keys, so we should disconnect. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240128084842.944af193d479.I5ef9f1f0e048d44d7158615d071b793d69eceb75@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 4582afb149d7..61aeb7f6604b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1462,7 +1462,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, status->pattern_number; if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH | + IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)) wakeup.disconnect = true; if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) From 8a41c017409198dd4de5a4c522cd5ae4810a5911 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 28 Jan 2024 08:53:50 +0200 Subject: [PATCH 132/378] wifi: iwlwifi: fix some kernel-doc issues Add return descriptions, move description contents after (parameter) sections and fix short descriptions. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240128084842.02ac00f67239.I4ad17097badfcbb82ccdb8c126f61a6f3170798e@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 15 +++++++++------ .../net/wireless/intel/iwlwifi/iwl-eeprom-parse.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 2 ++ 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index e27774e7ed74..05e220173fa1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1728,10 +1728,12 @@ iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, /** * mask_apply_and_normalize - applies mask on val and normalize the result * - * The normalization is based on the first set bit in the mask - * * @val: value * @mask: mask to apply and to normalize with + * + * The normalization is based on the first set bit in the mask + * + * Returns: the extracted value */ static u32 mask_apply_and_normalize(u32 val, u32 mask) { @@ -2200,15 +2202,16 @@ struct iwl_dump_ini_mem_ops { }; /** - * iwl_dump_ini_mem - * - * Creates a dump tlv and copy a memory region into it. - * Returns the size of the current dump tlv or 0 if failed + * iwl_dump_ini_mem - dump memory region * * @fwrt: fw runtime struct * @list: list to add the dump tlv to * @reg_data: memory region * @ops: memory dump operations + * + * Creates a dump tlv and copy a memory region into it. + * + * Returns: the size of the current dump tlv or 0 if failed */ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, struct iwl_dump_ini_region_data *reg_data, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c index 5aab64c63a13..2b290fab1ef2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c @@ -270,7 +270,7 @@ enum iwl_eeprom_enhanced_txpwr_flags { }; /** - * struct iwl_eeprom_enhanced_txpwr + * struct iwl_eeprom_enhanced_txpwr - enhanced regulatory TX power limits * @flags: entry flags * @channel: channel number * @chain_a_max: chain a max power in 1/2 dBm diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 871274eea26f..20054a0c7892 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -259,7 +259,7 @@ static void iwl_mvm_rx_thermal_dual_chain_req(struct iwl_mvm *mvm, } /** - * enum iwl_rx_handler_context context for Rx handler + * enum iwl_rx_handler_context: context for Rx handler * @RX_HANDLER_SYNC : this means that it will be called in the Rx path * which can't acquire mvm->mutex. * @RX_HANDLER_ASYNC_LOCKED : If the handler needs to hold mvm->mutex @@ -279,7 +279,7 @@ enum iwl_rx_handler_context { }; /** - * struct iwl_rx_handlers handler for FW notification + * struct iwl_rx_handlers: handler for FW notification * @cmd_id: command id * @min_size: minimum size to expect for the notification * @context: see &iwl_rx_handler_context diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 481d68cbbbd8..a8c4e354e2ce 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -4161,6 +4161,8 @@ static int rs_drv_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, * @mvm: The mvm component * @mvmsta: The station * @enable: Enable Tx protection? + * + * Returns: an error code */ int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool enable) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 91286018a69d..ab56ff87c6f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -249,6 +249,8 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx) * This is the special case in which init is set and we call a callback in * this case to clear the state indicating that station creation is in * progress. + * + * Returns: an error code indicating success or failure */ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq) { From 2f72c759fdd413bbfd8888af8e31312b34d2be33 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 28 Jan 2024 08:53:51 +0200 Subject: [PATCH 133/378] wifi: iwlwifi: dbg-tlv: avoid extra allocation/copy In iwl_dbg_tlv_alloc_trigger() the code makes a copy just to modify it and pass it to another function to make a copy again. Change the API to return the copy so the adjustment can be done without another copy. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240128084842.075c8b9f7030.Id5a61e1a87a9c6932727fb4e2c9b54ed6070362a@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 72075720969c..7b145b417810 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -64,21 +64,22 @@ dbg_ver_table[IWL_DBG_TLV_TYPE_NUM] = { [IWL_DBG_TLV_TYPE_CONF_SET] = {.min_ver = 1, .max_ver = 1,}, }; -static int iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv, - struct list_head *list) +/* add a new TLV node, returning it so it can be modified */ +static struct iwl_ucode_tlv *iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv, + struct list_head *list) { u32 len = le32_to_cpu(tlv->length); struct iwl_dbg_tlv_node *node; node = kzalloc(sizeof(*node) + len, GFP_KERNEL); if (!node) - return -ENOMEM; + return NULL; memcpy(&node->tlv, tlv, sizeof(node->tlv)); memcpy(node->tlv.data, tlv->data, len); list_add_tail(&node->list, list); - return 0; + return &node->tlv; } static bool iwl_dbg_tlv_ver_support(const struct iwl_ucode_tlv *tlv) @@ -106,7 +107,9 @@ static int iwl_dbg_tlv_alloc_debug_info(struct iwl_trans *trans, IWL_DEBUG_FW(trans, "WRT: Loading debug cfg: %s\n", debug_info->debug_cfg_name); - return iwl_dbg_tlv_add(tlv, &trans->dbg.debug_info_tlv_list); + if (!iwl_dbg_tlv_add(tlv, &trans->dbg.debug_info_tlv_list)) + return -ENOMEM; + return 0; } static int iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans *trans, @@ -175,7 +178,9 @@ static int iwl_dbg_tlv_alloc_hcmd(struct iwl_trans *trans, return -EINVAL; } - return iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].hcmd_list); + if (!iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].hcmd_list)) + return -ENOMEM; + return 0; } static int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans, @@ -246,11 +251,9 @@ static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv) { const struct iwl_fw_ini_trigger_tlv *trig = (const void *)tlv->data; - struct iwl_fw_ini_trigger_tlv *dup_trig; u32 tp = le32_to_cpu(trig->time_point); u32 rf = le32_to_cpu(trig->reset_fw); - struct iwl_ucode_tlv *dup = NULL; - int ret; + struct iwl_ucode_tlv *new_tlv; if (le32_to_cpu(tlv->length) < sizeof(*trig)) return -EINVAL; @@ -267,20 +270,18 @@ static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans, "WRT: time point %u for trigger TLV with reset_fw %u\n", tp, rf); trans->dbg.last_tp_resetfw = 0xFF; + + new_tlv = iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].trig_list); + if (!new_tlv) + return -ENOMEM; + if (!le32_to_cpu(trig->occurrences)) { - dup = kmemdup(tlv, sizeof(*tlv) + le32_to_cpu(tlv->length), - GFP_KERNEL); - if (!dup) - return -ENOMEM; - dup_trig = (void *)dup->data; - dup_trig->occurrences = cpu_to_le32(-1); - tlv = dup; + struct iwl_fw_ini_trigger_tlv *new_trig = (void *)new_tlv->data; + + new_trig->occurrences = cpu_to_le32(-1); } - ret = iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].trig_list); - kfree(dup); - - return ret; + return 0; } static int iwl_dbg_tlv_config_set(struct iwl_trans *trans, @@ -304,7 +305,9 @@ static int iwl_dbg_tlv_config_set(struct iwl_trans *trans, return -EINVAL; } - return iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].config_list); + if (!iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].config_list)) + return -ENOMEM; + return 0; } static int (*dbg_tlv_alloc[])(struct iwl_trans *trans, @@ -1148,7 +1151,9 @@ iwl_dbg_tlv_add_active_trigger(struct iwl_fw_runtime *fwrt, if (!match) { IWL_DEBUG_FW(fwrt, "WRT: Enabling trigger (time point %u)\n", le32_to_cpu(trig->time_point)); - return iwl_dbg_tlv_add(trig_tlv, trig_list); + if (!iwl_dbg_tlv_add(trig_tlv, trig_list)) + return -ENOMEM; + return 0; } return iwl_dbg_tlv_override_trig_node(fwrt, trig_tlv, match); From 1722c83f8fbba56c03e0ac597a242c8b0ef4d62c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 28 Jan 2024 08:53:52 +0200 Subject: [PATCH 134/378] wifi: iwlwifi: dbg-tlv: use struct_size() for allocation This is effectively the same since we don't even have a multiplication, but better captures what happens with the length. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240128084842.f301641eb916.I66b7b48a526377d682eecef874373bf3a01076c8@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 7b145b417810..989b100ce6ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -71,7 +71,7 @@ static struct iwl_ucode_tlv *iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv, u32 len = le32_to_cpu(tlv->length); struct iwl_dbg_tlv_node *node; - node = kzalloc(sizeof(*node) + len, GFP_KERNEL); + node = kzalloc(struct_size(node, tlv.data, len), GFP_KERNEL); if (!node) return NULL; From ea1d166fae14e05d49ffb0ea9fcd4658f8d3dcea Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 28 Jan 2024 08:53:53 +0200 Subject: [PATCH 135/378] wifi: iwlwifi: dbg-tlv: ensure NUL termination The iwl_fw_ini_debug_info_tlv is used as a string, so we must ensure the string is terminated correctly before using it. Fixes: a9248de42464 ("iwlwifi: dbg_ini: add TLV allocation new API support") Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240128084842.be15e858ee89.Ibff93429cf999eafc7b26f3eef4c055dc84984a0@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 989b100ce6ab..6cfcf1c14eaf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -104,6 +104,12 @@ static int iwl_dbg_tlv_alloc_debug_info(struct iwl_trans *trans, if (le32_to_cpu(tlv->length) != sizeof(*debug_info)) return -EINVAL; + /* we use this as a string, ensure input was NUL terminated */ + if (strnlen(debug_info->debug_cfg_name, + sizeof(debug_info->debug_cfg_name)) == + sizeof(debug_info->debug_cfg_name)) + return -EINVAL; + IWL_DEBUG_FW(trans, "WRT: Loading debug cfg: %s\n", debug_info->debug_cfg_name); From ec06e9b95944563964170d9e30278361bb26fd3a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 28 Jan 2024 08:53:54 +0200 Subject: [PATCH 136/378] wifi: iwlwifi: fw: dbg: ensure correct config name sizes This hard-codes the size, but it's not obvious why that's correct. Use sizeof() and cross-check the two structs. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240128084842.58fcdea2ace7.I49cb1d7bdbea12085aada0c96ef42fcbcb3d2b38@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 05e220173fa1..b877a2cd4005 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -2430,9 +2430,12 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_debug_info_tlv *debug_info = (void *)node->tlv.data; + BUILD_BUG_ON(sizeof(cfg_name->cfg_name) != + sizeof(debug_info->debug_cfg_name)); + cfg_name->image_type = debug_info->image_type; cfg_name->cfg_name_len = - cpu_to_le32(IWL_FW_INI_MAX_CFG_NAME); + cpu_to_le32(sizeof(cfg_name->cfg_name)); memcpy(cfg_name->cfg_name, debug_info->debug_cfg_name, sizeof(cfg_name->cfg_name)); cfg_name++; From 296f3e926716ded8dc29e349d2b042b362f96515 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 28 Jan 2024 08:53:55 +0200 Subject: [PATCH 137/378] wifi: iwlwifi: acpi: fix WPFC reading The code reading the WPFC table needs to take into account the domain type (first element in the package), shouldn't leak the memory if it fails, and has a bad comment. Fix all these issues. Fixes: c4c954547755 ("wifi: iwlwifi: implement WPFC ACPI table loading") Reported-by: Miri Korenblit Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Gregory Link: https://msgid.link/20240128084842.2afeb476b62d.I200568dc42a277e21c12be99d5aaa39b009d45da@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 10 +++++----- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index b96f30d11644..72a0a9565371 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -1293,7 +1293,6 @@ void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, if (IS_ERR(data)) return; - /* try to read wtas table revision 1 or revision 0*/ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_WPFC_WIFI_DATA_SIZE, &tbl_rev); @@ -1303,13 +1302,14 @@ void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, if (tbl_rev != 0) goto out_free; - BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) != ACPI_WPFC_WIFI_DATA_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) != + ACPI_WPFC_WIFI_DATA_SIZE - 1); for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) { - if (wifi_pkg->package.elements[i].type != ACPI_TYPE_INTEGER) - return; + if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER) + goto out_free; tmp.filter_cfg_chains[i] = - cpu_to_le32(wifi_pkg->package.elements[i].integer.value); + cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value); } IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index e9277f6f3582..39106ccb4b9b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -56,7 +56,7 @@ #define ACPI_EWRD_WIFI_DATA_SIZE_REV2 ((ACPI_SAR_PROFILE_NUM - 1) * \ ACPI_SAR_NUM_CHAINS_REV2 * \ ACPI_SAR_NUM_SUB_BANDS_REV2 + 3) -#define ACPI_WPFC_WIFI_DATA_SIZE 4 /* 4 filter config words */ +#define ACPI_WPFC_WIFI_DATA_SIZE 5 /* domain and 4 filter config words */ /* revision 0 and 1 are identical, except for the semantics in the FW */ #define ACPI_GEO_NUM_BANDS_REV0 2 From e50a88e5cb8792cc416866496288c5f4d1eb4b1f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 28 Jan 2024 08:53:56 +0200 Subject: [PATCH 138/378] wifi: iwlwifi: mvm: disconnect station vifs if recovery failed This will allow to reconnect immediately instead of leaving the connection in a limbo state. Signed-off-by: Emmanuel Grumbach Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240128084842.e90531cd3a36.Iebdc9483983c0d8497f9dcf9d79ec37332a5fdcc@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index b6acf4ade552..175024170357 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1504,6 +1504,13 @@ void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) #endif /* CONFIG_ACPI */ +static void iwl_mvm_disconnect_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_hw_restart_disconnect(vif); +} + void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) { u32 error_log_size = mvm->fw->ucode_capa.error_log_size; @@ -1548,10 +1555,15 @@ void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) /* skb respond is only relevant in ERROR_RECOVERY_UPDATE_DB */ if (flags & ERROR_RECOVERY_UPDATE_DB) { resp = le32_to_cpu(*(__le32 *)host_cmd.resp_pkt->data); - if (resp) + if (resp) { IWL_ERR(mvm, "Failed to send recovery cmd blob was invalid %d\n", resp); + + ieee80211_iterate_interfaces(mvm->hw, 0, + iwl_mvm_disconnect_iterator, + mvm); + } } } From d3b2c6c65bfd3b9616084e91bd0d402964ea7cef Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 28 Jan 2024 08:53:58 +0200 Subject: [PATCH 139/378] wifi: iwlwifi: mvm: initialize rates in FW earlier When connecting to an AP, we currently initialize the rate control only after associating. Since we now use firmware to assign rates to auth/assoc frames rather than using the data in the station and the firmware doesn't know, they're transmitted using low mandatory rates. However, if the AP advertised only higher supported rates we want to use them to be nicer (it still must receive mandatory rates though), so send the information to the firmware earlier to have it know about it and be able to use it. Fixes: 499d02790495 ("wifi: iwlwifi: Use FW rate for non-data frames") Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240128084842.ed7ab1c859c2.I4b4d4fc3905c8d8470fc0fee4648f25c950c9bb7@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 406956574f52..40b8f5a5ccf1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3686,6 +3686,19 @@ iwl_mvm_sta_state_notexist_to_none(struct iwl_mvm *mvm, if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) mvmvif->ap_sta = sta; + /* + * Initialize the rates here already - this really tells + * the firmware only what the supported legacy rates are + * (may be) since it's initialized already from what the + * AP advertised in the beacon/probe response. This will + * allow the firmware to send auth/assoc frames with one + * of the supported rates already, rather than having to + * use a mandatory rate. + * If we're the AP, we'll just assume mandatory rates at + * this point, but we know nothing about the STA anyway. + */ + iwl_mvm_rs_rate_init_all_links(mvm, vif, sta); + return 0; } From ebe8f41319fabee020736220942d79c1644bea85 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Sun, 28 Jan 2024 08:53:59 +0200 Subject: [PATCH 140/378] wifi: iwlwifi: implement GLAI ACPI table loading All the regulatory tables from BIOS are going to be loaded (preferably) from the UEFI instead of the ACPI. There is a security issue with the fact that anyone can add these UEFI variables. The solution for that is to have a lock for all WIFI GUID UEFI variables, and only if the UEFI variables are locked then we can read it. The status of the lock (unlocked, locked, test mode) is indicated in a ACPI table: Guid Lock ACPI Indicator. Load this table so the driver knows whether to read from UEFI or not Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240128084842.53994809fbdd.I1bd10aafc387bc04f375e386861ee2bcb82f0a61@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 35 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 13 ++++++- .../net/wireless/intel/iwlwifi/fw/runtime.h | 4 +++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 ++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 72a0a9565371..dd68d382d7de 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -1318,3 +1318,38 @@ void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, kfree(data); } IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters); + +void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) +{ + union acpi_object *wifi_pkg, *data; + int tbl_rev; + + data = iwl_acpi_get_object(fwrt->dev, ACPI_GLAI_METHOD); + if (IS_ERR(data)) + return; + + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_GLAI_WIFI_DATA_SIZE, + &tbl_rev); + if (IS_ERR(wifi_pkg)) + goto out_free; + + if (tbl_rev != 0) { + IWL_DEBUG_RADIO(fwrt, "Invalid GLAI revision: %d\n", tbl_rev); + goto out_free; + } + + if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER || + wifi_pkg->package.elements[1].integer.value > ACPI_GLAI_MAX_STATUS) + goto out_free; + + fwrt->uefi_tables_lock_status = + wifi_pkg->package.elements[1].integer.value; + + IWL_DEBUG_RADIO(fwrt, + "Loaded UEFI WIFI GUID lock status: %d from ACPI\n", + fwrt->uefi_tables_lock_status); +out_free: + kfree(data); +} +IWL_EXPORT_SYMBOL(iwl_acpi_get_guid_lock_status); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 39106ccb4b9b..7b3c6fca7591 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -25,6 +25,7 @@ #define ACPI_PPAG_METHOD "PPAG" #define ACPI_WTAS_METHOD "WTAS" #define ACPI_WPFC_METHOD "WPFC" +#define ACPI_GLAI_METHOD "GLAI" #define ACPI_WIFI_DOMAIN (0x07) @@ -66,7 +67,12 @@ #define ACPI_WRDD_WIFI_DATA_SIZE 2 #define ACPI_SPLC_WIFI_DATA_SIZE 2 #define ACPI_ECKV_WIFI_DATA_SIZE 2 - +/* + * One element for domain type, + * and one for the status + */ +#define ACPI_GLAI_WIFI_DATA_SIZE 2 +#define ACPI_GLAI_MAX_STATUS 2 /* * TAS size: 1 elelment for type, * 1 element for enabled field, @@ -237,6 +243,8 @@ bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt); void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, struct iwl_phy_specific_cfg *filters); +void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt); + #else /* CONFIG_ACPI */ static inline void *iwl_acpi_get_dsm_object(struct device *dev, int rev, @@ -331,6 +339,9 @@ static inline void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, { } +static inline void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) +{ +} #endif /* CONFIG_ACPI */ #endif /* __iwl_fw_acpi__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 357727774db9..1ec2bcdf92b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -100,6 +100,9 @@ struct iwl_txf_iter_data { * @dump: debug dump data * @uats_enabled: VLP or AFC AP is enabled * @uats_table: AP type table + * @uefi_tables_lock_status: The status of the WIFI GUID UEFI variables lock: + * 0: Unlocked, 1 and 2: Locked. + * Only read the UEFI variables if locked. */ struct iwl_fw_runtime { struct iwl_trans *trans; @@ -175,6 +178,7 @@ struct iwl_fw_runtime { u8 reduced_power_flags; bool uats_enabled; struct iwl_uats_table_cmd uats_table; + u8 uefi_tables_lock_status; #endif }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 175024170357..a6db290923cd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1402,6 +1402,8 @@ void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) { int ret; + iwl_acpi_get_guid_lock_status(&mvm->fwrt); + /* read PPAG table */ ret = iwl_acpi_get_ppag_table(&mvm->fwrt); if (ret < 0) { From a6dfe1e74403082e43b1f5ed49c0044eeb93da6c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Sun, 28 Jan 2024 08:54:00 +0200 Subject: [PATCH 141/378] wifi: iwlwifi: cleanup uefi variables loading Extract the logic that is common to all variables loading to a function. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240128084842.454f32c4bcfe.I4835fe657475ac28ef6aef4d292fac63c6ce9a34@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 128 ++++++++----------- 1 file changed, 51 insertions(+), 77 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 2964c5fb11e9..9c432d7b3674 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -76,6 +76,42 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) return data; } +static +void *iwl_uefi_get_verified_variable(struct iwl_trans *trans, + efi_char16_t *uefi_var_name, + char *var_name, + unsigned int expected_size, + unsigned long *size) +{ + void *var; + unsigned long var_size; + + var = iwl_uefi_get_variable(uefi_var_name, &IWL_EFI_VAR_GUID, + &var_size); + + if (IS_ERR(var)) { + IWL_DEBUG_RADIO(trans, + "%s UEFI variable not found 0x%lx\n", var_name, + PTR_ERR(var)); + return var; + } + + if (var_size < expected_size) { + IWL_DEBUG_RADIO(trans, + "Invalid %s UEFI variable len (%lu)\n", + var_name, var_size); + kfree(var); + return ERR_PTR(-EINVAL); + } + + IWL_DEBUG_RADIO(trans, "%s from UEFI with size %lu\n", var_name, + var_size); + + if (size) + *size = var_size; + return var; +} + int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, u32 tlv_len, struct iwl_pnvm_image *pnvm_data) { @@ -230,26 +266,13 @@ u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) unsigned long package_size; u8 *data; - package = iwl_uefi_get_variable(IWL_UEFI_REDUCED_POWER_NAME, - &IWL_EFI_VAR_GUID, &package_size); - - if (IS_ERR(package)) { - IWL_DEBUG_FW(trans, - "Reduced Power UEFI variable not found 0x%lx (len %lu)\n", - PTR_ERR(package), package_size); + package = iwl_uefi_get_verified_variable(trans, + IWL_UEFI_REDUCED_POWER_NAME, + "Reduced Power", + sizeof(*package), + &package_size); + if (IS_ERR(package)) return ERR_CAST(package); - } - - if (package_size < sizeof(*package)) { - IWL_DEBUG_FW(trans, - "Invalid Reduced Power UEFI variable len (%lu)\n", - package_size); - kfree(package); - return ERR_PTR(-EINVAL); - } - - IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n", - package_size); IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n", package->rev, package->total_size, package->n_skus); @@ -283,32 +306,15 @@ static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_dat void iwl_uefi_get_step_table(struct iwl_trans *trans) { struct uefi_cnv_common_step_data *data; - unsigned long package_size; int ret; if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return; - data = iwl_uefi_get_variable(IWL_UEFI_STEP_NAME, &IWL_EFI_VAR_GUID, - &package_size); - - if (IS_ERR(data)) { - IWL_DEBUG_FW(trans, - "STEP UEFI variable not found 0x%lx\n", - PTR_ERR(data)); + data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_STEP_NAME, + "STEP", sizeof(*data), NULL); + if (IS_ERR(data)) return; - } - - if (package_size < sizeof(*data)) { - IWL_DEBUG_FW(trans, - "Invalid STEP table UEFI variable len (%lu)\n", - package_size); - kfree(data); - return; - } - - IWL_DEBUG_FW(trans, "Read STEP from UEFI with size %lu\n", - package_size); ret = iwl_uefi_step_parse(data, trans); if (ret < 0) @@ -355,31 +361,15 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { struct uefi_cnv_wlan_sgom_data *data; - unsigned long package_size; int ret; if (!fwrt->geo_enabled) return; - data = iwl_uefi_get_variable(IWL_UEFI_SGOM_NAME, &IWL_EFI_VAR_GUID, - &package_size); - if (IS_ERR(data)) { - IWL_DEBUG_FW(trans, - "SGOM UEFI variable not found 0x%lx\n", - PTR_ERR(data)); + data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_SGOM_NAME, + "SGOM", sizeof(*data), NULL); + if (IS_ERR(data)) return; - } - - if (package_size < sizeof(*data)) { - IWL_DEBUG_FW(trans, - "Invalid SGOM table UEFI variable len (%lu)\n", - package_size); - kfree(data); - return; - } - - IWL_DEBUG_FW(trans, "Read SGOM from UEFI with size %lu\n", - package_size); ret = iwl_uefi_sgom_parse(data, fwrt); if (ret < 0) @@ -404,28 +394,12 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { struct uefi_cnv_wlan_uats_data *data; - unsigned long package_size; int ret; - data = iwl_uefi_get_variable(IWL_UEFI_UATS_NAME, &IWL_EFI_VAR_GUID, - &package_size); - if (IS_ERR(data)) { - IWL_DEBUG_FW(trans, - "UATS UEFI variable not found 0x%lx\n", - PTR_ERR(data)); + data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME, + "UATS", sizeof(*data), NULL); + if (IS_ERR(data)) return -EINVAL; - } - - if (package_size < sizeof(*data)) { - IWL_DEBUG_FW(trans, - "Invalid UATS table UEFI variable len (%lu)\n", - package_size); - kfree(data); - return -EINVAL; - } - - IWL_DEBUG_FW(trans, "Read UATS from UEFI with size %lu\n", - package_size); ret = iwl_uefi_uats_parse(data, fwrt); if (ret < 0) { From c8d8f3911135921ace8e939ea0956b55f74bf8a0 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 29 Jan 2024 21:21:49 +0200 Subject: [PATCH 142/378] wifi: iwlwifi: fix EWRD table validity check EWRD ACPI table contains up to 3 additional sar profiles. According to the BIOS spec, the table contains a n_profile variable indicating how many additional profiles exist in the table. Currently we check that n_profiles is not <= 0. But according to the BIOS spec, 0 is a valid value, and it can't be < 0 anyway because we receive that from ACPI as an unsigned integer. Fixes: 39c1a9728f93 ("iwlwifi: refactor the SAR tables from mvm to acpi") Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240129211905.448ea2f40814.Iffd2aadf8e8693e6cb599bee0406a800a0c1e081@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index dd68d382d7de..9be91e6a9882 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -767,7 +767,7 @@ int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) * from index 1, so the maximum value allowed here is * ACPI_SAR_PROFILES_NUM - 1. */ - if (n_profiles <= 0 || n_profiles >= ACPI_SAR_PROFILE_NUM) { + if (n_profiles >= ACPI_SAR_PROFILE_NUM) { ret = -EINVAL; goto out_free; } From 8001849921028775eafb5263feadf49e821e9ecf Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Mon, 29 Jan 2024 21:21:50 +0200 Subject: [PATCH 143/378] wifi: iwlwifi: mvm: add support for TID to link mapping neg request Add support for handling TID to link mapping negotiation request and decide whether to accept it or not. Accept the request if all TIDs are mapped to the same link set, otherwise reject it. Signed-off-by: Ayala Beker Reviewed-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.aab9819c378d.Icf6b79a362763e2e8b85959471f303b586617242@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 5 +++++ .../wireless/intel/iwlwifi/mvm/mld-mac80211.c | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 40b8f5a5ccf1..fa8d14421999 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -263,6 +263,9 @@ static const u8 tm_if_types_ext_capa_sta[] = { __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \ IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \ __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY)) +#define IWL_MVM_MLD_CAPA_OPS FIELD_PREP_CONST( \ + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) static const struct wiphy_iftype_ext_capab add_iftypes_ext_capa[] = { { @@ -272,6 +275,7 @@ static const struct wiphy_iftype_ext_capab add_iftypes_ext_capa[] = { .extended_capabilities_len = sizeof(he_if_types_ext_capa_sta), /* relevant only if EHT is supported */ .eml_capabilities = IWL_MVM_EMLSR_CAPA, + .mld_capa_and_ops = IWL_MVM_MLD_CAPA_OPS, }, { .iftype = NL80211_IFTYPE_STATION, @@ -280,6 +284,7 @@ static const struct wiphy_iftype_ext_capab add_iftypes_ext_capa[] = { .extended_capabilities_len = sizeof(tm_if_types_ext_capa_sta), /* relevant only if EHT is supported */ .eml_capabilities = IWL_MVM_EMLSR_CAPA, + .mld_capa_and_ops = IWL_MVM_MLD_CAPA_OPS, }, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 2b879eeecc8c..5bac39b75e6c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -1329,6 +1329,24 @@ static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw, return ret; } +static enum ieee80211_neg_ttlm_res +iwl_mvm_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_neg_ttlm *neg_ttlm) +{ + u16 map; + u8 i; + + /* Verify all TIDs are mapped to the same links set */ + map = neg_ttlm->downlink[0]; + for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) { + if (neg_ttlm->downlink[i] != neg_ttlm->uplink[i] || + neg_ttlm->uplink[i] != map) + return NEG_TTLM_RES_REJECT; + } + + return NEG_TTLM_RES_ACCEPT; +} + const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .tx = iwl_mvm_mac_tx, .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, @@ -1424,4 +1442,5 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .change_vif_links = iwl_mvm_mld_change_vif_links, .change_sta_links = iwl_mvm_mld_change_sta_links, .can_activate_links = iwl_mvm_mld_can_activate_links, + .can_neg_ttlm = iwl_mvm_mld_can_neg_ttlm, }; From 0c769cb6b9f364423c255f117774c9ecd5bf23ea Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 21:21:51 +0200 Subject: [PATCH 144/378] wifi: iwlwifi: mvm: d3: fix IPN byte order The IPN is reported by the firmware in 6 bytes little endian, but mac80211 expects big endian so it can do memcmp() on it. We used to store this as a u64 which was filled in the right way, but never used. When implementing that it's used, we changed it to just be 6 bytes, but lost the conversion. Add it back. Fixes: 04f78e242fff ("wifi: iwlwifi: mvm: Add support for IGTK in D3 resume flow") Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.138ed8a698e3.I1b66c386e45b5392696424ec636474bff86fd5ef@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 61aeb7f6604b..6ab9def2c670 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2194,7 +2194,10 @@ static void iwl_mvm_convert_gtk_v3(struct iwl_wowlan_status_data *status, static void iwl_mvm_convert_igtk(struct iwl_wowlan_status_data *status, struct iwl_wowlan_igtk_status *data) { + int i; + BUILD_BUG_ON(sizeof(status->igtk.key) < sizeof(data->key)); + BUILD_BUG_ON(sizeof(status->igtk.ipn) != sizeof(data->ipn)); if (!data->key_len) return; @@ -2206,7 +2209,10 @@ static void iwl_mvm_convert_igtk(struct iwl_wowlan_status_data *status, + WOWLAN_IGTK_MIN_INDEX; memcpy(status->igtk.key, data->key, sizeof(data->key)); - memcpy(status->igtk.ipn, data->ipn, sizeof(data->ipn)); + + /* mac80211 expects big endian for memcmp() to work, convert */ + for (i = 0; i < sizeof(data->ipn); i++) + status->igtk.ipn[i] = data->ipn[sizeof(data->ipn) - i - 1]; } static void iwl_mvm_convert_bigtk(struct iwl_wowlan_status_data *status, From 64a06679e680d163d91b4642227244184c29ca02 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 29 Jan 2024 21:21:52 +0200 Subject: [PATCH 145/378] wifi: iwlwifi: Fix spelling mistake "SESION" -> "SESSION" There is a spelling mistake in a WARN message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.ff31e9385d29.I3a224e6a9294fdec431919fb4ec9315801e77454@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index aceab96bcb97..703ccdd4d967 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -929,7 +929,7 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, if (WARN(ver > 2 && mvmvif->time_event_data.link_id >= 0 && mvmvif->time_event_data.link_id != notif_link_id, - "SESION_PROTECTION_NOTIF was received for link %u, while the current time event is on link %u\n", + "SESSION_PROTECTION_NOTIF was received for link %u, while the current time event is on link %u\n", notif_link_id, mvmvif->time_event_data.link_id)) goto out_unlock; From bc197d3c400f48ce7d9402c968ceeb5680e5b639 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 21:21:53 +0200 Subject: [PATCH 146/378] wifi: iwlwifi: mvm: don't set trigger frame padding in AP mode This field is reserved in AP mode, don't set any bits in it. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.f5eeb717212e.I60fa4843a8634922281580b925db2c2699e3a7bc@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index a7152d65eefa..0b2b0c320cb0 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -749,7 +749,6 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { .mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE, .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, .mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_OMI_CONTROL, From f639602a58e7564dd091c7c0793f61042bad9bb6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 21:21:54 +0200 Subject: [PATCH 147/378] wifi: iwlwifi: always have 'uats_enabled' We check this in code that'd be complicated to put under ifdef (CONFIG_ACPI), so just always have 'uats_enabled'. Fixes: 4a9bb5b4d949 ("wifi: iwlwifi: fw: Add support for UATS table in UHB") Signed-off-by: Johannes Berg Reviewed-by: Mukesh Sisodiya Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.bdc5fb20f00a.I902d801d79873c5c9cd51cef8e8226e2acefe88d@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 1ec2bcdf92b8..048830bb09f2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -176,10 +176,10 @@ struct iwl_fw_runtime { struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; u8 reduced_power_flags; - bool uats_enabled; struct iwl_uats_table_cmd uats_table; u8 uefi_tables_lock_status; #endif + bool uats_enabled; }; void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, From 137d33ac476489645e4a67d66d3abf930cecb419 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Mon, 29 Jan 2024 21:21:55 +0200 Subject: [PATCH 148/378] wifi: iwlwifi: mvm: Fix FTM initiator flags When secure LTF is used MFP must also be set. Signed-off-by: Ilan Peer Reviewed-by: Avraham Stern Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.6cad71069e87.I7f9fd5239cfd2244f155f88419980e6e91d00ff2@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index 233ae81884a0..4863a3c74640 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2023 Intel Corporation */ #include #include @@ -821,9 +821,10 @@ iwl_mvm_ftm_put_target_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * If secure LTF is turned off, replace the flag with PMF only */ flags = le32_to_cpu(target->initiator_ap_flags); - if ((flags & IWL_INITIATOR_AP_FLAGS_SECURED) && - !IWL_MVM_FTM_INITIATOR_SECURE_LTF) { - flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED; + if (flags & IWL_INITIATOR_AP_FLAGS_SECURED) { + if (!IWL_MVM_FTM_INITIATOR_SECURE_LTF) + flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED; + flags |= IWL_INITIATOR_AP_FLAGS_PMF; target->initiator_ap_flags = cpu_to_le32(flags); } From 51eb17b8d5597250fea513050c26a53f2ff183b2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 21:21:57 +0200 Subject: [PATCH 149/378] wifi: iwlwifi: remove Gl A-step remnants The IWL_DEVICE_GL_A macro is no longer used, and couldn't be, so remove it. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.8929a06c3a55.I3c21305e4b7fa3aba938bc860269e848fe262e88@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 20799a0fbc07..b0b003a6a46e 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -129,10 +129,6 @@ static const struct iwl_base_params iwl_bz_base_params = { IWL_DEVICE_BZ_COMMON, \ .ht_params = &iwl_22000_ht_params -#define IWL_DEVICE_GL_A \ - IWL_DEVICE_BZ_COMMON, \ - .ht_params = &iwl_gl_a_ht_params - /* * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an * A-MPDU, with additional overhead to account for processing time. From 3d869feacb74309e4f1cb69572df16ec9862012c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 21:21:58 +0200 Subject: [PATCH 150/378] wifi: iwlwifi: mvm: use FW rate for non-data only on new devices With MLO connections we need to let the firmware pick the rate as we don't know the link the frame might be transmitted on (in some cases we do know, but we'd rather always use the FW and find bugs.) We _did_ end up finding bugs and fixing them, but older devices likely won't get fixed as we don't have a need for this there, they cannot support MLO. Thus, go back to picking a rate on the host for the relevant frames on older (pre-Bz) devices. Fixes: 499d02790495 ("wifi: iwlwifi: Use FW rate for non-data frames") Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.e59056d0a8cc.Iccc4c5c1753921d3d85241ede812a150fb05b898@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 33 ++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index db986bfc4dc3..79eb6394e5a7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -520,6 +520,31 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, } } +static bool iwl_mvm_use_host_rate(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, + struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *info) +{ + if (unlikely(!mvmsta)) + return true; + + if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) + return true; + + if (likely(ieee80211_is_data(hdr->frame_control) && + mvmsta->sta_state >= IEEE80211_STA_AUTHORIZED)) + return false; + + /* + * Not a data frame, use host rate if on an old device that + * can't possibly be doing MLO (firmware may be selecting a + * bad rate), if we might be doing MLO we need to let FW pick + * (since we don't necesarily know the link), but FW rate + * selection was fixed. + */ + return mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ; +} + /* * Allocates and sets the Tx cmd the driver data pointers in the skb */ @@ -556,12 +581,12 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, flags |= IWL_TX_FLAGS_ENCRYPT_DIS; /* - * For data and mgmt packets rate info comes from the fw. Only + * For data and mgmt packets rate info comes from the fw (for + * new devices, older FW is somewhat broken for this). Only * set rate/antenna for injected frames with fixed rate, or - * when no sta is given. + * when no sta is given, or with older firmware. */ - if (unlikely(!sta || - info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) { + if (unlikely(iwl_mvm_use_host_rate(mvm, mvmsta, hdr, info))) { flags |= IWL_TX_FLAGS_CMD_RATE; rate_n_flags = iwl_mvm_get_tx_rate_n_flags(mvm, info, sta, From 0fcdf55fced7121c43fa576433986f1c04115b73 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 29 Jan 2024 21:21:59 +0200 Subject: [PATCH 151/378] wifi: iwlwifi: mvm: fix the TLC command after ADD_STA ADD_STA resets the link quality data inside the firmware. This is not supposed to happen and has been fixed for newer devices. For older devices (AX201 and down), this makes us send frames with rates that are not in the TLC table. Fixes: 5a86dcb4a908 ("wifi: iwlwifi: mvm: update station's MFP flag after association") Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.1deca7eaff14.I597abd7aab36fdab4aa8311a48c98a3d5bd433ba@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index fa8d14421999..36a5932d75a6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3802,13 +3802,17 @@ iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm, mvm_sta->authorized = true; - iwl_mvm_rs_rate_init_all_links(mvm, vif, sta); - /* MFP is set by default before the station is authorized. * Clear it here in case it's not used. */ - if (!sta->mfp) - return callbacks->update_sta(mvm, vif, sta); + if (!sta->mfp) { + int ret = callbacks->update_sta(mvm, vif, sta); + + if (ret) + return ret; + } + + iwl_mvm_rs_rate_init_all_links(mvm, vif, sta); return 0; } From 6770eee75148ba10c0c051885379714773e00b48 Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Mon, 29 Jan 2024 21:22:00 +0200 Subject: [PATCH 152/378] wifi: iwlwifi: pcie: Add the PCI device id for new hardware Add the support for a new PCI device id. Signed-off-by: Mukesh Sisodiya Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.fde32107e0a3.I597cff4f340e4bed12b7568a0ad504bd4b2c1cf8@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index c80b02503b41..bbc8dc390bdc 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -502,6 +502,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* Bz devices */ {IWL_PCI_DEVICE(0x2727, PCI_ANY_ID, iwl_bz_trans_cfg)}, + {IWL_PCI_DEVICE(0x272D, PCI_ANY_ID, iwl_bz_trans_cfg)}, {IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_bz_trans_cfg)}, {IWL_PCI_DEVICE(0xA840, PCI_ANY_ID, iwl_bz_trans_cfg)}, {IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_trans_cfg)}, From c289f5cd6978af1bd14c1b7c230aaa011e1b3b5d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 21:22:01 +0200 Subject: [PATCH 153/378] wifi: iwlwifi: mvm: support SPP A-MSDUs If the firmware has the necessary support, enable SPP A-MSDUs. Signed-off-by: Johannes Berg Signed-off-by: Daniel Gabay Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.15e4570e471f.I87cf284d3b19bb9f5558f0f33afaace6d6492acb@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/sta.h | 4 +++- drivers/net/wireless/intel/iwlwifi/fw/file.h | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 5 +++++ drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 3 +++ 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h index d62fed543276..d7f8a276b683 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2021, 2023 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -109,6 +109,7 @@ enum iwl_sta_flags { * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from * station info array (1 - n 1X mode) + * @STA_KEY_FLG_AMSDU_SPP: SPP (signaling and payload protected) A-MSDU * @STA_KEY_FLG_KEYID_MSK: the index of the key * @STA_KEY_FLG_KEYID_POS: key index bit position * @STA_KEY_NOT_VALID: key is invalid @@ -129,6 +130,7 @@ enum iwl_sta_key_flag { STA_KEY_FLG_EN_MSK = (7 << 0), STA_KEY_FLG_WEP_KEY_MAP = BIT(3), + STA_KEY_FLG_AMSDU_SPP = BIT(7), STA_KEY_FLG_KEYID_POS = 8, STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS), STA_KEY_NOT_VALID = BIT(11), diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index bfc39bd5bbc6..b216da7d95fc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -383,6 +383,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * channels even when these are not enabled. * @IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT: Support for indicating dump collection * complete to FW. + * @IWL_UCODE_TLV_CAPA_SPP_AMSDU_SUPPORT: Support SPP (signaling and payload + * protected) A-MSDU. * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -468,6 +470,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_PSC_CHAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)98, IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT = (__force iwl_ucode_tlv_capa_t)100, + IWL_UCODE_TLV_CAPA_SPP_AMSDU_SUPPORT = (__force iwl_ucode_tlv_capa_t)103, IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT = (__force iwl_ucode_tlv_capa_t)104, IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT = (__force iwl_ucode_tlv_capa_t)105, IWL_UCODE_TLV_CAPA_SYNCED_TIME = (__force iwl_ucode_tlv_capa_t)106, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 36a5932d75a6..0cee752584d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -495,6 +495,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_TIME_SYNC_BOTH_FTM_TM)) hw->wiphy->hw_timestamp_max_peers = 1; + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_SPP_AMSDU_SUPPORT)) + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT); + ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); hw->wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c index a1ce08a5527c..bbd37a95d4c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c @@ -104,6 +104,9 @@ u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, (keyconf->keyidx == 4 || keyconf->keyidx == 5))) flags |= IWL_SEC_KEY_FLAG_MFP; + if (keyconf->flags & IEEE80211_KEY_FLAG_SPP_AMSDU) + flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU; + return flags; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index d57fcbe4f8ac..8ffbb8efda73 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -3559,6 +3559,9 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, key_flags = cpu_to_le16(keyidx); key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP); + if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU) + key_flags |= cpu_to_le16(STA_KEY_FLG_AMSDU_SPP); + switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP); From 65d3333e4d4f35853aa2991324206e1cb17b81d7 Mon Sep 17 00:00:00 2001 From: Daniel Gabay Date: Mon, 29 Jan 2024 21:22:02 +0200 Subject: [PATCH 154/378] wifi: iwlwifi: mvm: log dropped packets due to MIC error When we drop frames due to MIC error we want to have something printed in the logger (this won't be printed by default). Signed-off-by: Daniel Gabay Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.41b0abbf1fd2.Ib6ec6a48ec7bebe769d1e1c1df96380a758a0975@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 451af501c7a1..67062fe40152 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -401,8 +401,11 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta, case IWL_RX_MPDU_STATUS_SEC_GCM: BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN); /* alg is CCM: check MIC only */ - if (!(status & IWL_RX_MPDU_STATUS_MIC_OK)) + if (!(status & IWL_RX_MPDU_STATUS_MIC_OK)) { + IWL_DEBUG_DROP(mvm, + "Dropping packet, bad MIC (CCM/GCM)\n"); return -1; + } stats->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED; *crypt_len = IEEE80211_CCMP_HDR_LEN; From ce1fa3adc007ce3fd6beb8f4d733e00daa64c6c0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 21:22:03 +0200 Subject: [PATCH 155/378] wifi: iwlwifi: mvm: refactor duplicate chanctx condition Refactor the check for using a chanctx's def vs. min_def, to have the same in both places and reuse it later. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129211905.6fcde4051adf.I343934874612d21727ed167accaa967958b2c25b@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 12 ++++-------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 13 +++++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 0cee752584d9..4bc25d4a87b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -4808,8 +4808,8 @@ static void iwl_mvm_ftm_responder_chanctx_iter(void *_data, u8 *mac, data->responder = true; } -static bool iwl_mvm_is_ftm_responder_chanctx(struct iwl_mvm *mvm, - struct ieee80211_chanctx_conf *ctx) +bool iwl_mvm_is_ftm_responder_chanctx(struct iwl_mvm *mvm, + struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm_ftm_responder_iter_data data = { .responder = false, @@ -4828,9 +4828,7 @@ static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm, { u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt; - bool use_def = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx) || - iwl_mvm_enable_fils(mvm, ctx); - struct cfg80211_chan_def *def = use_def ? &ctx->def : &ctx->min_def; + struct cfg80211_chan_def *def = iwl_mvm_chanctx_def(mvm, ctx); int ret; lockdep_assert_held(&mvm->mutex); @@ -4896,9 +4894,7 @@ void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; - bool use_def = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx) || - iwl_mvm_enable_fils(mvm, ctx); - struct cfg80211_chan_def *def = use_def ? &ctx->def : &ctx->min_def; + struct cfg80211_chan_def *def = iwl_mvm_chanctx_def(mvm, ctx); if (WARN_ONCE((phy_ctxt->ref > 1) && (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH | diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index e148ef02ff73..d414007c4755 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2752,4 +2752,17 @@ void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, unsigned long usable_links); + +bool iwl_mvm_is_ftm_responder_chanctx(struct iwl_mvm *mvm, + struct ieee80211_chanctx_conf *ctx); + +static inline struct cfg80211_chan_def * +iwl_mvm_chanctx_def(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *ctx) +{ + bool use_def = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx) || + iwl_mvm_enable_fils(mvm, ctx); + + return use_def ? &ctx->def : &ctx->min_def; +} + #endif /* __IWL_MVM_H__ */ From 45d43937a44c806b8649323b8f5d9f42ae838b0e Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 29 Jan 2024 22:09:19 +0100 Subject: [PATCH 156/378] wifi: cfg80211: add a kunit test for 6 GHz colocated AP parsing Test a few things around parsing of 6 GHz colocated APs to e.g. ensure that we are not going to scan for a disabled (affiliated) AP. Signed-off-by: Benjamin Berg Link: https://msgid.link/20240129220918.079dc50ab43b.Ide898d9f1d4c26d7e774d6fd0ec57766967d6572@changeid Signed-off-by: Johannes Berg --- net/wireless/core.h | 44 ++++++++++ net/wireless/scan.c | 49 ++--------- net/wireless/tests/scan.c | 179 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+), 42 deletions(-) diff --git a/net/wireless/core.h b/net/wireless/core.h index 30434551b377..debf63e6c61f 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -550,9 +550,53 @@ int cfg80211_remove_virtual_intf(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void cfg80211_wdev_release_link_bsses(struct wireless_dev *wdev, u16 link_mask); +/** + * struct cfg80211_colocated_ap - colocated AP information + * + * @list: linked list to all colocated APs + * @bssid: BSSID of the reported AP + * @ssid: SSID of the reported AP + * @ssid_len: length of the ssid + * @center_freq: frequency the reported AP is on + * @unsolicited_probe: the reported AP is part of an ESS, where all the APs + * that operate in the same channel as the reported AP and that might be + * detected by a STA receiving this frame, are transmitting unsolicited + * Probe Response frames every 20 TUs + * @oct_recommended: OCT is recommended to exchange MMPDUs with the reported AP + * @same_ssid: the reported AP has the same SSID as the reporting AP + * @multi_bss: the reported AP is part of a multiple BSSID set + * @transmitted_bssid: the reported AP is the transmitting BSSID + * @colocated_ess: all the APs that share the same ESS as the reported AP are + * colocated and can be discovered via legacy bands. + * @short_ssid_valid: short_ssid is valid and can be used + * @short_ssid: the short SSID for this SSID + * @psd_20: The 20MHz PSD EIRP of the primary 20MHz channel for the reported AP + */ +struct cfg80211_colocated_ap { + struct list_head list; + u8 bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + size_t ssid_len; + u32 short_ssid; + u32 center_freq; + u8 unsolicited_probe:1, + oct_recommended:1, + same_ssid:1, + multi_bss:1, + transmitted_bssid:1, + colocated_ess:1, + short_ssid_valid:1; + s8 psd_20; +}; + #if IS_ENABLED(CONFIG_CFG80211_KUNIT_TEST) #define EXPORT_SYMBOL_IF_CFG80211_KUNIT(sym) EXPORT_SYMBOL_IF_KUNIT(sym) #define VISIBLE_IF_CFG80211_KUNIT +void cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list); + +int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies, + struct list_head *list); + size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, const u8 *subie, size_t subie_len, u8 *new_ie, size_t new_ie_len); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 01618c4059f4..c78b10e1a167 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -77,45 +77,6 @@ MODULE_PARM_DESC(bss_entries_limit, #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) -/** - * struct cfg80211_colocated_ap - colocated AP information - * - * @list: linked list to all colocated aPS - * @bssid: BSSID of the reported AP - * @ssid: SSID of the reported AP - * @ssid_len: length of the ssid - * @center_freq: frequency the reported AP is on - * @unsolicited_probe: the reported AP is part of an ESS, where all the APs - * that operate in the same channel as the reported AP and that might be - * detected by a STA receiving this frame, are transmitting unsolicited - * Probe Response frames every 20 TUs - * @oct_recommended: OCT is recommended to exchange MMPDUs with the reported AP - * @same_ssid: the reported AP has the same SSID as the reporting AP - * @multi_bss: the reported AP is part of a multiple BSSID set - * @transmitted_bssid: the reported AP is the transmitting BSSID - * @colocated_ess: all the APs that share the same ESS as the reported AP are - * colocated and can be discovered via legacy bands. - * @short_ssid_valid: short_ssid is valid and can be used - * @short_ssid: the short SSID for this SSID - * @psd_20: The 20MHz PSD EIRP of the primary 20MHz channel for the reported AP - */ -struct cfg80211_colocated_ap { - struct list_head list; - u8 bssid[ETH_ALEN]; - u8 ssid[IEEE80211_MAX_SSID_LEN]; - size_t ssid_len; - u32 short_ssid; - u32 center_freq; - u8 unsolicited_probe:1, - oct_recommended:1, - same_ssid:1, - multi_bss:1, - transmitted_bssid:1, - colocated_ess:1, - short_ssid_valid:1; - s8 psd_20; -}; - static void bss_free(struct cfg80211_internal_bss *bss) { struct cfg80211_bss_ies *ies; @@ -566,7 +527,8 @@ static int cfg80211_calc_short_ssid(const struct cfg80211_bss_ies *ies, return 0; } -static void cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list) +VISIBLE_IF_CFG80211_KUNIT void +cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list) { struct cfg80211_colocated_ap *ap, *tmp_ap; @@ -575,6 +537,7 @@ static void cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list) kfree(ap); } } +EXPORT_SYMBOL_IF_KUNIT(cfg80211_free_coloc_ap_list); static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry, const u8 *pos, u8 length, @@ -648,8 +611,9 @@ static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry, return 0; } -static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies, - struct list_head *list) +VISIBLE_IF_CFG80211_KUNIT int +cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies, + struct list_head *list) { struct ieee80211_neighbor_ap_info *ap_info; const struct element *elem, *ssid_elem; @@ -746,6 +710,7 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies, list_splice_tail(&ap_list, list); return n_coloc; } +EXPORT_SYMBOL_IF_KUNIT(cfg80211_parse_colocated_ap); static void cfg80211_scan_req_add_chan(struct cfg80211_scan_request *request, struct ieee80211_channel *chan, diff --git a/net/wireless/tests/scan.c b/net/wireless/tests/scan.c index f9ea44aee995..b40a0c06a6bb 100644 --- a/net/wireless/tests/scan.c +++ b/net/wireless/tests/scan.c @@ -628,6 +628,172 @@ static void test_inform_bss_ml_sta(struct kunit *test) cfg80211_put_bss(wiphy, link_bss); } +static struct cfg80211_parse_colocated_ap_case { + const char *desc; + u8 op_class; + u8 channel; + struct ieee80211_neighbor_ap_info info; + union { + struct ieee80211_tbtt_info_ge_11 tbtt_long; + struct ieee80211_tbtt_info_7_8_9 tbtt_short; + }; + bool add_junk; + bool same_ssid; + bool valid; +} cfg80211_parse_colocated_ap_cases[] = { + { + .desc = "wrong_band", + .info.op_class = 81, + .info.channel = 11, + .tbtt_long = { + .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, + .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, + }, + .valid = false, + }, + { + .desc = "wrong_type", + /* IEEE80211_AP_INFO_TBTT_HDR_TYPE is in the least significant bits */ + .info.tbtt_info_hdr = IEEE80211_TBTT_INFO_TYPE_MLD, + .tbtt_long = { + .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, + .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, + }, + .valid = false, + }, + { + .desc = "colocated_invalid_len_short", + .info.tbtt_info_len = 6, + .tbtt_short = { + .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, + .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP | + IEEE80211_RNR_TBTT_PARAMS_SAME_SSID, + }, + .valid = false, + }, + { + .desc = "colocated_invalid_len_short_mld", + .info.tbtt_info_len = 10, + .tbtt_long = { + .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, + .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, + }, + .valid = false, + }, + { + .desc = "colocated_non_mld", + .info.tbtt_info_len = sizeof(struct ieee80211_tbtt_info_7_8_9), + .tbtt_short = { + .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, + .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP | + IEEE80211_RNR_TBTT_PARAMS_SAME_SSID, + }, + .same_ssid = true, + .valid = true, + }, + { + .desc = "colocated_non_mld_invalid_bssid", + .info.tbtt_info_len = sizeof(struct ieee80211_tbtt_info_7_8_9), + .tbtt_short = { + .bssid = { 0xff, 0x11, 0x22, 0x33, 0x44, 0x55 }, + .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP | + IEEE80211_RNR_TBTT_PARAMS_SAME_SSID, + }, + .same_ssid = true, + .valid = false, + }, + { + .desc = "colocated_mld", + .tbtt_long = { + .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, + .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, + }, + .valid = true, + }, + { + .desc = "colocated_mld", + .tbtt_long = { + .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, + .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, + }, + .add_junk = true, + .valid = false, + }, + { + .desc = "colocated_disabled_mld", + .tbtt_long = { + .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, + .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, + .mld_params.params = cpu_to_le16(IEEE80211_RNR_MLD_PARAMS_DISABLED_LINK), + }, + .valid = false, + }, +}; +KUNIT_ARRAY_PARAM_DESC(cfg80211_parse_colocated_ap, cfg80211_parse_colocated_ap_cases, desc) + +static void test_cfg80211_parse_colocated_ap(struct kunit *test) +{ + const struct cfg80211_parse_colocated_ap_case *params = test->param_value; + struct sk_buff *input = kunit_zalloc_skb(test, 1024, GFP_KERNEL); + struct cfg80211_bss_ies *ies; + struct ieee80211_neighbor_ap_info info; + LIST_HEAD(coloc_ap_list); + int count; + + KUNIT_ASSERT_NOT_NULL(test, input); + + info = params->info; + + /* Reasonable values for a colocated AP */ + if (!info.tbtt_info_len) + info.tbtt_info_len = sizeof(params->tbtt_long); + if (!info.op_class) + info.op_class = 131; + if (!info.channel) + info.channel = 33; + /* Zero is the correct default for .btt_info_hdr (one entry, TBTT type) */ + + skb_put_u8(input, WLAN_EID_SSID); + skb_put_u8(input, 4); + skb_put_data(input, "TEST", 4); + + skb_put_u8(input, WLAN_EID_REDUCED_NEIGHBOR_REPORT); + skb_put_u8(input, sizeof(info) + info.tbtt_info_len + (params->add_junk ? 3 : 0)); + skb_put_data(input, &info, sizeof(info)); + skb_put_data(input, ¶ms->tbtt_long, info.tbtt_info_len); + + if (params->add_junk) + skb_put_data(input, "123", 3); + + ies = kunit_kzalloc(test, struct_size(ies, data, input->len), GFP_KERNEL); + ies->len = input->len; + memcpy(ies->data, input->data, input->len); + + count = cfg80211_parse_colocated_ap(ies, &coloc_ap_list); + + KUNIT_EXPECT_EQ(test, count, params->valid); + KUNIT_EXPECT_EQ(test, list_count_nodes(&coloc_ap_list), params->valid); + + if (params->valid && !list_empty(&coloc_ap_list)) { + struct cfg80211_colocated_ap *ap; + + ap = list_first_entry(&coloc_ap_list, typeof(*ap), list); + if (info.tbtt_info_len <= sizeof(params->tbtt_short)) + KUNIT_EXPECT_MEMEQ(test, ap->bssid, params->tbtt_short.bssid, ETH_ALEN); + else + KUNIT_EXPECT_MEMEQ(test, ap->bssid, params->tbtt_long.bssid, ETH_ALEN); + + if (params->same_ssid) { + KUNIT_EXPECT_EQ(test, ap->ssid_len, 4); + KUNIT_EXPECT_MEMEQ(test, ap->ssid, "TEST", 4); + } else { + KUNIT_EXPECT_EQ(test, ap->ssid_len, 0); + } + } + + cfg80211_free_coloc_ap_list(&coloc_ap_list); +} + static struct kunit_case gen_new_ie_test_cases[] = { KUNIT_CASE_PARAM(test_gen_new_ie, gen_new_ie_gen_params), KUNIT_CASE(test_gen_new_ie_malformed), @@ -653,3 +819,16 @@ static struct kunit_suite inform_bss = { }; kunit_test_suite(inform_bss); + +static struct kunit_case scan_6ghz_cases[] = { + KUNIT_CASE_PARAM(test_cfg80211_parse_colocated_ap, + cfg80211_parse_colocated_ap_gen_params), + {} +}; + +static struct kunit_suite scan_6ghz = { + .name = "cfg80211-scan-6ghz", + .test_cases = scan_6ghz_cases, +}; + +kunit_test_suite(scan_6ghz); From cfbb2add482ab86a9d183dbb9ac00d18c1389cc7 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 29 Jan 2024 22:09:20 +0100 Subject: [PATCH 157/378] wifi: cfg80211: tests: verify BSS use flags of NSTR links Extend the test to pass an RNR appropriate for an NSTR and verify that use_for as well as cannot_use_reasons are set correctly. Signed-off-by: Benjamin Berg Link: https://msgid.link/20240129220918.b00aee4c4c9f.I942fddf51cabaab761de3865b4e06cce831a46ef@changeid Signed-off-by: Johannes Berg --- net/wireless/tests/scan.c | 68 ++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/net/wireless/tests/scan.c b/net/wireless/tests/scan.c index b40a0c06a6bb..9f458be71659 100644 --- a/net/wireless/tests/scan.c +++ b/net/wireless/tests/scan.c @@ -407,6 +407,7 @@ static struct inform_bss_ml_sta_case { int mld_id; bool sta_prof_vendor_elems; bool include_oper_class; + bool nstr; } inform_bss_ml_sta_cases[] = { { .desc = "zero_mld_id", @@ -426,6 +427,10 @@ static struct inform_bss_ml_sta_case { .mld_id = 1, .sta_prof_vendor_elems = true, .include_oper_class = true, + }, { + .desc = "nstr", + .mld_id = 0, + .nstr = true, }, }; KUNIT_ARRAY_PARAM_DESC(inform_bss_ml_sta, inform_bss_ml_sta_cases, desc) @@ -458,7 +463,7 @@ static void test_inform_bss_ml_sta(struct kunit *test) struct { struct ieee80211_neighbor_ap_info info; struct ieee80211_tbtt_info_ge_11 ap; - } __packed rnr = { + } __packed rnr_normal = { .info = { .tbtt_info_hdr = u8_encode_bits(0, IEEE80211_AP_INFO_TBTT_HDR_COUNT), .tbtt_info_len = sizeof(struct ieee80211_tbtt_info_ge_11), @@ -477,6 +482,28 @@ static void test_inform_bss_ml_sta(struct kunit *test) IEEE80211_RNR_MLD_PARAMS_LINK_ID), } }; + struct { + struct ieee80211_neighbor_ap_info info; + struct ieee80211_rnr_mld_params mld_params; + } __packed rnr_nstr = { + .info = { + .tbtt_info_hdr = + u8_encode_bits(0, IEEE80211_AP_INFO_TBTT_HDR_COUNT) | + u8_encode_bits(IEEE80211_TBTT_INFO_TYPE_MLD, + IEEE80211_AP_INFO_TBTT_HDR_TYPE), + .tbtt_info_len = sizeof(struct ieee80211_rnr_mld_params), + .op_class = 81, + .channel = 11, + }, + .mld_params = { + .mld_id = params->mld_id, + .params = + le16_encode_bits(link_id, + IEEE80211_RNR_MLD_PARAMS_LINK_ID), + } + }; + size_t rnr_len = params->nstr ? sizeof(rnr_nstr) : sizeof(rnr_normal); + void *rnr = params->nstr ? (void *)&rnr_nstr : (void *)&rnr_normal; struct { __le16 control; u8 var_len; @@ -516,7 +543,7 @@ static void test_inform_bss_ml_sta(struct kunit *test) u16_encode_bits(link_id, IEEE80211_MLE_STA_CONTROL_LINK_ID)), .var_len = sizeof(sta_prof) - 2 - 2, - .bssid = { *rnr.ap.bssid }, + .bssid = { *rnr_normal.ap.bssid }, .beacon_int = cpu_to_le16(101), .tsf_offset = cpu_to_le64(-123ll), .capabilities = cpu_to_le16(0xdead), @@ -540,8 +567,8 @@ static void test_inform_bss_ml_sta(struct kunit *test) } skb_put_u8(input, WLAN_EID_REDUCED_NEIGHBOR_REPORT); - skb_put_u8(input, sizeof(rnr)); - skb_put_data(input, &rnr, sizeof(rnr)); + skb_put_u8(input, rnr_len); + skb_put_data(input, rnr, rnr_len); /* build a multi-link element */ skb_put_u8(input, WLAN_EID_EXTENSION); @@ -587,9 +614,10 @@ static void test_inform_bss_ml_sta(struct kunit *test) KUNIT_EXPECT_EQ(test, ctx.inform_bss_count, 2); /* Check link_bss *****************************************************/ - link_bss = cfg80211_get_bss(wiphy, NULL, sta_prof.bssid, NULL, 0, - IEEE80211_BSS_TYPE_ANY, - IEEE80211_PRIVACY_ANY); + link_bss = __cfg80211_get_bss(wiphy, NULL, sta_prof.bssid, NULL, 0, + IEEE80211_BSS_TYPE_ANY, + IEEE80211_PRIVACY_ANY, + 0); KUNIT_ASSERT_NOT_NULL(test, link_bss); KUNIT_EXPECT_EQ(test, link_bss->signal, 0); KUNIT_EXPECT_EQ(test, link_bss->beacon_interval, @@ -600,6 +628,22 @@ static void test_inform_bss_ml_sta(struct kunit *test) KUNIT_EXPECT_PTR_EQ(test, link_bss->channel, ieee80211_get_channel_khz(wiphy, MHZ_TO_KHZ(2462))); + /* Test wiphy does not set WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY */ + if (params->nstr) { + KUNIT_EXPECT_EQ(test, link_bss->use_for, 0); + KUNIT_EXPECT_EQ(test, link_bss->cannot_use_reasons, + NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY); + KUNIT_EXPECT_NULL(test, + cfg80211_get_bss(wiphy, NULL, sta_prof.bssid, + NULL, 0, + IEEE80211_BSS_TYPE_ANY, + IEEE80211_PRIVACY_ANY)); + } else { + KUNIT_EXPECT_EQ(test, link_bss->use_for, + NL80211_BSS_USE_FOR_ALL); + KUNIT_EXPECT_EQ(test, link_bss->cannot_use_reasons, 0); + } + rcu_read_lock(); ies = rcu_dereference(link_bss->ies); KUNIT_EXPECT_NOT_NULL(test, ies); @@ -607,20 +651,20 @@ static void test_inform_bss_ml_sta(struct kunit *test) /* Resulting length should be: * SSID (inherited) + RNR (inherited) + vendor element(s) + * operating class (if requested) + - * generated RNR (if MLD ID == 0) + + * generated RNR (if MLD ID == 0 and not NSTR) + * MLE common info + MLE header and control */ if (params->sta_prof_vendor_elems) KUNIT_EXPECT_EQ(test, ies->len, - 6 + 2 + sizeof(rnr) + 2 + 160 + 2 + 165 + + 6 + 2 + rnr_len + 2 + 160 + 2 + 165 + (params->include_oper_class ? 3 : 0) + - (!params->mld_id ? 22 : 0) + + (!params->mld_id && !params->nstr ? 22 : 0) + mle_basic_common_info.var_len + 5); else KUNIT_EXPECT_EQ(test, ies->len, - 6 + 2 + sizeof(rnr) + 2 + 155 + + 6 + 2 + rnr_len + 2 + 155 + (params->include_oper_class ? 3 : 0) + - (!params->mld_id ? 22 : 0) + + (!params->mld_id && !params->nstr ? 22 : 0) + mle_basic_common_info.var_len + 5); rcu_read_unlock(); From c868a189ecfe8cc0b3173c2eaa7f0b659326c151 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:34 +0200 Subject: [PATCH 158/378] wifi: iwlwifi: read BIOS PNVM only for non-Intel SKU The driver is supposed to read the PNVM from BIOS only for non-Intel SKUs. For Intel SKUs the OEM ID will be 0. Read BIOS PNVM only when a non-Intel SKU is indicated. Fixes: b99e32cbfdf6 ("wifi: iwlwifi: Take loading and setting of pnvm image out of parsing part") Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.3625cf1223d3.Ieffda5f506713b1c979388dd7a0e1c1a0145cfca@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 30 ++++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index d467ec0e3552..053174f00e49 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -268,21 +268,27 @@ static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len) struct pnvm_sku_package *package; u8 *image = NULL; - /* First attempt to get the PNVM from BIOS */ - package = iwl_uefi_get_pnvm(trans_p, len); - if (!IS_ERR_OR_NULL(package)) { - if (*len >= sizeof(*package)) { - /* we need only the data */ - *len -= sizeof(*package); - image = kmemdup(package->data, *len, GFP_KERNEL); + /* Get PNVM from BIOS for non-Intel SKU */ + if (trans_p->sku_id[2]) { + package = iwl_uefi_get_pnvm(trans_p, len); + if (!IS_ERR_OR_NULL(package)) { + if (*len >= sizeof(*package)) { + /* we need only the data */ + *len -= sizeof(*package); + image = kmemdup(package->data, + *len, GFP_KERNEL); + } + /* + * free package regardless of whether kmemdup + * succeeded + */ + kfree(package); + if (image) + return image; } - /* free package regardless of whether kmemdup succeeded */ - kfree(package); - if (image) - return image; } - /* If it's not available, try from the filesystem */ + /* If it's not available, or for Intel SKU, try from the filesystem */ if (iwl_pnvm_get_from_fs(trans_p, &image, len)) return NULL; return image; From 8c9bef26e98b71b18afe8de6212b750c4e9f9ecf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Jan 2024 10:24:35 +0200 Subject: [PATCH 159/378] wifi: iwlwifi: mvm: d3: implement suspend with MLO When using MLO, we need to have only a single link active when entering suspend and of course most of the code also needs to be adjusted to not use deflink, apart from older code that's not used with MLO-capable firmware. Implement that. Note that the link selection currently prefers the "best" link, which might really not be the best for D3, but that can be fixed later once we agree. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240131091413.38f0fd4d2db0.I27c7a1d08aecc5da0af2c351212f22e92ed70219@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 79 ++++++++++++++------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 6ab9def2c670..6396fbde03c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -450,9 +450,9 @@ static void iwl_mvm_wowlan_get_rsc_v5_data(struct ieee80211_hw *hw, } static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + struct iwl_mvm_vif_link_info *mvm_link) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ver = iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_TSC_RSC_PARAM, IWL_FW_CMD_VER_UNKNOWN); int ret; @@ -470,7 +470,7 @@ static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm, for (i = 0; i < ARRAY_SIZE(data.rsc->mcast_key_id_map); i++) data.rsc->mcast_key_id_map[i] = IWL_MCAST_KEY_MAP_INVALID; - data.rsc->sta_id = cpu_to_le32(mvmvif->deflink.ap_sta_id); + data.rsc->sta_id = cpu_to_le32(mvm_link->ap_sta_id); ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_wowlan_get_rsc_v5_data, @@ -494,7 +494,7 @@ static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm, if (ver == 4) { size = sizeof(*data.rsc_tsc); data.rsc_tsc->sta_id = - cpu_to_le32(mvmvif->deflink.ap_sta_id); + cpu_to_le32(mvm_link->ap_sta_id); } else { /* ver == 2 || ver == IWL_FW_CMD_VER_UNKNOWN */ size = sizeof(data.rsc_tsc->params); @@ -668,10 +668,9 @@ static int iwl_mvm_send_patterns_v1(struct iwl_mvm *mvm, } static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, + struct iwl_mvm_vif_link_info *mvm_link, struct cfg80211_wowlan *wowlan) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_wowlan_patterns_cmd *pattern_cmd; struct iwl_host_cmd cmd = { .id = WOWLAN_PATTERNS, @@ -693,7 +692,7 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, pattern_cmd->n_patterns = wowlan->n_patterns; if (ver >= 3) - pattern_cmd->sta_id = mvmvif->deflink.ap_sta_id; + pattern_cmd->sta_id = mvm_link->ap_sta_id; for (i = 0; i < wowlan->n_patterns; i++) { int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); @@ -730,7 +729,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_time_quota_data *quota; u32 status; - if (WARN_ON_ONCE(iwl_mvm_is_cdb_supported(mvm))) + if (WARN_ON_ONCE(iwl_mvm_is_cdb_supported(mvm) || + ieee80211_vif_is_mld(vif))) return -EINVAL; /* add back the PHY */ @@ -987,7 +987,8 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, } static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + struct iwl_mvm_vif_link_info *mvm_link) { bool unified = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); @@ -1016,7 +1017,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, return -EIO; } - ret = iwl_mvm_wowlan_config_rsc_tsc(mvm, vif); + ret = iwl_mvm_wowlan_config_rsc_tsc(mvm, vif, mvm_link); if (ret) return ret; @@ -1030,7 +1031,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, if (ver == 2) { size = sizeof(tkip_data.tkip); tkip_data.tkip.sta_id = - cpu_to_le32(mvmvif->deflink.ap_sta_id); + cpu_to_le32(mvm_link->ap_sta_id); } else if (ver == 1 || ver == IWL_FW_CMD_VER_UNKNOWN) { size = sizeof(struct iwl_wowlan_tkip_params_cmd_ver_1); } else { @@ -1079,7 +1080,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, kek_kck_cmd.kek_len = cpu_to_le16(mvmvif->rekey_data.kek_len); kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr; kek_kck_cmd.akm = cpu_to_le32(mvmvif->rekey_data.akm); - kek_kck_cmd.sta_id = cpu_to_le32(mvmvif->deflink.ap_sta_id); + kek_kck_cmd.sta_id = cpu_to_le32(mvm_link->ap_sta_id); if (cmd_ver == 4) { cmd_size = sizeof(struct iwl_wowlan_kek_kck_material_cmd_v4); @@ -1112,6 +1113,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, struct cfg80211_wowlan *wowlan, struct iwl_wowlan_config_cmd *wowlan_config_cmd, struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, + struct iwl_mvm_vif_link_info *mvm_link, struct ieee80211_sta *ap_sta) { int ret; @@ -1130,7 +1132,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, return ret; } - ret = iwl_mvm_wowlan_config_key_params(mvm, vif); + ret = iwl_mvm_wowlan_config_key_params(mvm, vif, mvm_link); if (ret) return ret; @@ -1142,7 +1144,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WOWLAN_TCP_SYN_WAKE)) - ret = iwl_mvm_send_patterns(mvm, vif, wowlan); + ret = iwl_mvm_send_patterns(mvm, mvm_link, wowlan); else ret = iwl_mvm_send_patterns_v1(mvm, wowlan); if (ret) @@ -1223,6 +1225,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, struct ieee80211_vif *vif = NULL; struct iwl_mvm_vif *mvmvif = NULL; struct ieee80211_sta *ap_sta = NULL; + struct iwl_mvm_vif_link_info *mvm_link; struct iwl_d3_manager_config d3_cfg_cmd_data = { /* * Program the minimum sleep time to 10 seconds, as many @@ -1237,7 +1240,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, .data[0] = &d3_cfg_cmd_data, .len[0] = sizeof(d3_cfg_cmd_data), }; - int ret; + int ret, primary_link; int len __maybe_unused; bool unified_image = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); @@ -1251,21 +1254,44 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, return -EINVAL; } + vif = iwl_mvm_get_bss_vif(mvm); + if (IS_ERR_OR_NULL(vif)) + return 1; + + if (ieee80211_vif_is_mld(vif) && vif->cfg.assoc) { + /* + * Select the 'best' link. May need to revisit, it seems + * better to not optimize for throughput but rather range, + * reliability and power here - and select 2.4 GHz ... + */ + primary_link = + iwl_mvm_mld_get_primary_link(mvm, vif, + vif->active_links); + + if (WARN_ONCE(primary_link < 0, "no primary link in 0x%x\n", + vif->active_links)) + primary_link = __ffs(vif->active_links); + + ret = ieee80211_set_active_links(vif, BIT(primary_link)); + if (ret) + return ret; + } else { + primary_link = 0; + } + mutex_lock(&mvm->mutex); set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); synchronize_net(); - vif = iwl_mvm_get_bss_vif(mvm); - if (IS_ERR_OR_NULL(vif)) { - ret = 1; - goto out_noreset; - } - mvmvif = iwl_mvm_vif_from_mac80211(vif); - if (mvmvif->deflink.ap_sta_id == IWL_MVM_INVALID_STA) { + mvm_link = mvmvif->link[primary_link]; + if (WARN_ON_ONCE(!mvm_link)) + return -EINVAL; + + if (mvm_link->ap_sta_id == IWL_MVM_INVALID_STA) { /* if we're not associated, this must be netdetect */ if (!wowlan->nd_config) { ret = 1; @@ -1281,10 +1307,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, } else { struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; - wowlan_config_cmd.sta_id = mvmvif->deflink.ap_sta_id; + wowlan_config_cmd.sta_id = mvm_link->ap_sta_id; ap_sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvmvif->deflink.ap_sta_id], + mvm->fw_id_to_mac_id[mvm_link->ap_sta_id], lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(ap_sta)) { ret = -EINVAL; @@ -1296,7 +1322,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret) goto out_noreset; ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd, - vif, mvmvif, ap_sta); + vif, mvmvif, mvm_link, ap_sta); if (ret) goto out; @@ -2846,6 +2872,9 @@ iwl_mvm_choose_query_wakeup_reasons(struct iwl_mvm *mvm, u8 sta_id = mvm->net_detect ? IWL_MVM_INVALID_STA : mvmvif->deflink.ap_sta_id; + /* bug - FW with MLO has status notification */ + WARN_ON(ieee80211_vif_is_mld(vif)); + d3_data->status = iwl_mvm_send_wowlan_get_status(mvm, sta_id); } From 760cfa5bbd3bdd5ca2b36ca447d69788651ab17b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Jan 2024 10:24:36 +0200 Subject: [PATCH 160/378] wifi: iwlwifi: mvm: check AP supports EMLSR Before using EMLSR check the AP actually advertises support for it, otherwise reject the link activation. Signed-off-by: Johannes Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240131091413.edaac352488d.Ic3533afc6848591e8977391ae39c144d5e794d26@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 5bac39b75e6c..d3c6eb83cd73 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -1280,6 +1280,9 @@ static bool iwl_mvm_can_enter_esr(struct iwl_mvm *mvm, if (primary_link < 0) return false; + if (!(vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP)) + return false; + for_each_set_bit(link_id, &desired_links, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_bss_conf *link_conf = link_conf_dereference_protected(vif, link_id); From 2594e4d9e1a2d79bf7bb262974abaf5ef153e371 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:37 +0200 Subject: [PATCH 161/378] wifi: iwlwifi: prepare for reading SAR tables from UEFI The driver will support reading BIOS tables from UEFI too. Refactor the SAR tables (WRDS, EWRD, WGDS) flows: 1. Move all the SAR logic/definitions that is common to both UEFI and ACPI to a new file - regulatory.h/c. 2. Rename the relevant functions/definitions so it will be clear which is ACPI specific and which is for both ACPI and UEFI 3. Rename the function that copies the stored tables into the different commands structures, so will be clear what these functions do. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.429a9baff34a.I040460348aa1b43609be3a317b86722d6be71c28@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/Makefile | 1 + drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 165 +++--------------- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 55 +----- .../wireless/intel/iwlwifi/fw/regulatory.c | 132 ++++++++++++++ .../wireless/intel/iwlwifi/fw/regulatory.h | 90 ++++++++++ .../net/wireless/intel/iwlwifi/fw/runtime.h | 7 +- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 32 ++-- 7 files changed, 273 insertions(+), 209 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/regulatory.c create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/regulatory.h diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index 3a2a25333d36..8bb94a4c12cd 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -18,6 +18,7 @@ iwlwifi-objs += queue/tx.o iwlwifi-objs += fw/img.o fw/notif-wait.o fw/rs.o iwlwifi-objs += fw/dbg.o fw/pnvm.o fw/dump.o +iwlwifi-objs += fw/regulatory.o iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o iwlwifi-$(CONFIG_ACPI) += fw/acpi.o iwlwifi-$(CONFIG_EFI) += fw/uefi.o diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 9be91e6a9882..401aca31704c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -501,9 +501,10 @@ int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) } IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv); -static int iwl_sar_set_profile(union acpi_object *table, - struct iwl_sar_profile *profile, - bool enabled, u8 num_chains, u8 num_sub_bands) +static int iwl_acpi_sar_set_profile(union acpi_object *table, + struct iwl_sar_profile *profile, + bool enabled, u8 num_chains, + u8 num_sub_bands) { int i, j, idx = 0; @@ -511,8 +512,8 @@ static int iwl_sar_set_profile(union acpi_object *table, * The table from ACPI is flat, but we store it in a * structured array. */ - for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV2; i++) { - for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS_REV2; j++) { + for (i = 0; i < BIOS_SAR_MAX_CHAINS_PER_PROFILE; i++) { + for (j = 0; j < BIOS_SAR_MAX_SUB_BANDS_NUM; j++) { /* if we don't have the values, use the default */ if (i >= num_chains || j >= num_sub_bands) { profile->chains[i].subbands[j] = 0; @@ -535,73 +536,7 @@ static int iwl_sar_set_profile(union acpi_object *table, return 0; } -static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt, - __le16 *per_chain, u32 n_subbands, - int prof_a, int prof_b) -{ - int profs[ACPI_SAR_NUM_CHAINS_REV0] = { prof_a, prof_b }; - int i, j; - - for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV0; i++) { - struct iwl_sar_profile *prof; - - /* don't allow SAR to be disabled (profile 0 means disable) */ - if (profs[i] == 0) - return -EPERM; - - /* we are off by one, so allow up to ACPI_SAR_PROFILE_NUM */ - if (profs[i] > ACPI_SAR_PROFILE_NUM) - return -EINVAL; - - /* profiles go from 1 to 4, so decrement to access the array */ - prof = &fwrt->sar_profiles[profs[i] - 1]; - - /* if the profile is disabled, do nothing */ - if (!prof->enabled) { - IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n", - profs[i]); - /* - * if one of the profiles is disabled, we - * ignore all of them and return 1 to - * differentiate disabled from other failures. - */ - return 1; - } - - IWL_DEBUG_INFO(fwrt, - "SAR EWRD: chain %d profile index %d\n", - i, profs[i]); - IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i); - for (j = 0; j < n_subbands; j++) { - per_chain[i * n_subbands + j] = - cpu_to_le16(prof->chains[i].subbands[j]); - IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n", - j, prof->chains[i].subbands[j]); - } - } - - return 0; -} - -int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt, - __le16 *per_chain, u32 n_tables, u32 n_subbands, - int prof_a, int prof_b) -{ - int i, ret = 0; - - for (i = 0; i < n_tables; i++) { - ret = iwl_sar_fill_table(fwrt, - &per_chain[i * n_subbands * ACPI_SAR_NUM_CHAINS_REV0], - n_subbands, prof_a, prof_b); - if (ret) - break; - } - - return ret; -} -IWL_EXPORT_SYMBOL(iwl_sar_select_profile); - -int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) +int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt) { union acpi_object *wifi_pkg, *table, *data; int ret, tbl_rev; @@ -680,16 +615,16 @@ int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) /* The profile from WRDS is officially profile 1, but goes * into sar_profiles[0] (because we don't have a profile 0). */ - ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0], - flags & IWL_SAR_ENABLE_MSK, - num_chains, num_sub_bands); + ret = iwl_acpi_sar_set_profile(table, &fwrt->sar_profiles[0], + flags & IWL_SAR_ENABLE_MSK, + num_chains, num_sub_bands); out_free: kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_sar_get_wrds_table); +IWL_EXPORT_SYMBOL(iwl_acpi_get_wrds_table); -int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) +int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) { union acpi_object *wifi_pkg, *data; bool enabled; @@ -767,7 +702,7 @@ int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) * from index 1, so the maximum value allowed here is * ACPI_SAR_PROFILES_NUM - 1. */ - if (n_profiles >= ACPI_SAR_PROFILE_NUM) { + if (n_profiles >= BIOS_SAR_MAX_PROFILE_NUM) { ret = -EINVAL; goto out_free; } @@ -776,13 +711,15 @@ int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) pos = 3; for (i = 0; i < n_profiles; i++) { + union acpi_object *table = &wifi_pkg->package.elements[pos]; /* The EWRD profiles officially go from 2 to 4, but we * save them in sar_profiles[1-3] (because we don't * have profile 0). So in the array we start from 1. */ - ret = iwl_sar_set_profile(&wifi_pkg->package.elements[pos], - &fwrt->sar_profiles[i + 1], enabled, - num_chains, num_sub_bands); + ret = iwl_acpi_sar_set_profile(table, + &fwrt->sar_profiles[i + 1], + enabled, num_chains, + num_sub_bands); if (ret < 0) break; @@ -794,9 +731,9 @@ int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_sar_get_ewrd_table); +IWL_EXPORT_SYMBOL(iwl_acpi_get_ewrd_table); -int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) +int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) { union acpi_object *wifi_pkg, *data; int i, j, k, ret, tbl_rev; @@ -897,7 +834,7 @@ int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) read_table: fwrt->geo_rev = tbl_rev; for (i = 0; i < num_profiles; i++) { - for (j = 0; j < ACPI_GEO_NUM_BANDS_REV2; j++) { + for (j = 0; j < BIOS_GEO_MAX_NUM_BANDS; j++) { union acpi_object *entry; /* @@ -921,7 +858,7 @@ int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) entry->integer.value; } - for (k = 0; k < ACPI_GEO_NUM_CHAINS; k++) { + for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) { /* same here as above */ if (j >= num_bands) { fwrt->geo_profiles[i].bands[j].chains[k] = @@ -949,63 +886,7 @@ int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_sar_get_wgds_table); - -bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) -{ - /* - * The PER_CHAIN_LIMIT_OFFSET_CMD command is not supported on - * earlier firmware versions. Unfortunately, we don't have a - * TLV API flag to rely on, so rely on the major version which - * is in the first byte of ucode_ver. This was implemented - * initially on version 38 and then backported to 17. It was - * also backported to 29, but only for 7265D devices. The - * intention was to have it in 36 as well, but not all 8000 - * family got this feature enabled. The 8000 family is the - * only one using version 36, so skip this version entirely. - */ - return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 || - (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 && - fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) || - (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 && - ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) == - CSR_HW_REV_TYPE_7265D)); -} -IWL_EXPORT_SYMBOL(iwl_sar_geo_support); - -int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt, - struct iwl_per_chain_offset *table, - u32 n_bands, u32 n_profiles) -{ - int i, j; - - if (!fwrt->geo_enabled) - return -ENODATA; - - if (!iwl_sar_geo_support(fwrt)) - return -EOPNOTSUPP; - - for (i = 0; i < n_profiles; i++) { - for (j = 0; j < n_bands; j++) { - struct iwl_per_chain_offset *chain = - &table[i * n_bands + j]; - - chain->max_tx_power = - cpu_to_le16(fwrt->geo_profiles[i].bands[j].max); - chain->chain_a = fwrt->geo_profiles[i].bands[j].chains[0]; - chain->chain_b = fwrt->geo_profiles[i].bands[j].chains[1]; - IWL_DEBUG_RADIO(fwrt, - "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n", - i, j, - fwrt->geo_profiles[i].bands[j].chains[0], - fwrt->geo_profiles[i].bands[j].chains[1], - fwrt->geo_profiles[i].bands[j].max); - } - } - - return 0; -} -IWL_EXPORT_SYMBOL(iwl_sar_geo_init); +IWL_EXPORT_SYMBOL(iwl_acpi_get_wgds_table); __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 7b3c6fca7591..ba7626543b70 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -62,7 +62,6 @@ /* revision 0 and 1 are identical, except for the semantics in the FW */ #define ACPI_GEO_NUM_BANDS_REV0 2 #define ACPI_GEO_NUM_BANDS_REV2 3 -#define ACPI_GEO_NUM_CHAINS 2 #define ACPI_WRDD_WIFI_DATA_SIZE 2 #define ACPI_SPLC_WIFI_DATA_SIZE 2 @@ -111,26 +110,6 @@ * turn a superset of revision 0. So we can store all revisions * inside revision 2, which is what we represent here. */ -struct iwl_sar_profile_chain { - u8 subbands[ACPI_SAR_NUM_SUB_BANDS_REV2]; -}; - -struct iwl_sar_profile { - bool enabled; - struct iwl_sar_profile_chain chains[ACPI_SAR_NUM_CHAINS_REV2]; -}; - -/* Same thing as with SAR, all revisions fit in revision 2 */ -struct iwl_geo_profile_band { - u8 max; - u8 chains[ACPI_GEO_NUM_CHAINS]; -}; - -struct iwl_geo_profile { - struct iwl_geo_profile_band bands[ACPI_GEO_NUM_BANDS_REV2]; -}; - -/* Same thing as with SAR, all revisions fit in revision 2 */ struct iwl_ppag_chain { s8 subbands[ACPI_SAR_NUM_SUB_BANDS_REV2]; }; @@ -212,21 +191,11 @@ u64 iwl_acpi_get_pwr_limit(struct device *dev); */ int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk); -int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt, - __le16 *per_chain, u32 n_tables, u32 n_subbands, - int prof_a, int prof_b); +int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt); -int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt); +int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt); -int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt); - -int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt); - -bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt); - -int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt, - struct iwl_per_chain_offset *table, - u32 n_bands, u32 n_profiles); +int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt); int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, union iwl_tas_config_cmd *cmd, int fw_ver); @@ -280,33 +249,21 @@ static inline int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) return -ENOENT; } -static inline int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt, - __le16 *per_chain, u32 n_tables, u32 n_subbands, - int prof_a, int prof_b) +static inline int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt) { return -ENOENT; } -static inline int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) +static inline int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) { return -ENOENT; } -static inline int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) -{ - return -ENOENT; -} - -static inline int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) +static inline int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) { return 1; } -static inline bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) -{ - return false; -} - static inline int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, union iwl_tas_config_cmd *cmd, int fw_ver) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c new file mode 100644 index 000000000000..58290bf64f42 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2023 Intel Corporation + */ +#include "iwl-drv.h" +#include "iwl-debug.h" +#include "regulatory.h" +#include "fw/runtime.h" + +bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) +{ + /* + * The PER_CHAIN_LIMIT_OFFSET_CMD command is not supported on + * earlier firmware versions. Unfortunately, we don't have a + * TLV API flag to rely on, so rely on the major version which + * is in the first byte of ucode_ver. This was implemented + * initially on version 38 and then backported to 17. It was + * also backported to 29, but only for 7265D devices. The + * intention was to have it in 36 as well, but not all 8000 + * family got this feature enabled. The 8000 family is the + * only one using version 36, so skip this version entirely. + */ + return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 || + (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 && + fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) || + (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 && + ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) == + CSR_HW_REV_TYPE_7265D)); +} +IWL_EXPORT_SYMBOL(iwl_sar_geo_support); + +int iwl_sar_geo_fill_table(struct iwl_fw_runtime *fwrt, + struct iwl_per_chain_offset *table, + u32 n_bands, u32 n_profiles) +{ + int i, j; + + if (!fwrt->geo_enabled) + return -ENODATA; + + if (!iwl_sar_geo_support(fwrt)) + return -EOPNOTSUPP; + + for (i = 0; i < n_profiles; i++) { + for (j = 0; j < n_bands; j++) { + struct iwl_per_chain_offset *chain = + &table[i * n_bands + j]; + + chain->max_tx_power = + cpu_to_le16(fwrt->geo_profiles[i].bands[j].max); + chain->chain_a = + fwrt->geo_profiles[i].bands[j].chains[0]; + chain->chain_b = + fwrt->geo_profiles[i].bands[j].chains[1]; + IWL_DEBUG_RADIO(fwrt, + "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n", + i, j, + fwrt->geo_profiles[i].bands[j].chains[0], + fwrt->geo_profiles[i].bands[j].chains[1], + fwrt->geo_profiles[i].bands[j].max); + } + } + + return 0; +} +IWL_EXPORT_SYMBOL(iwl_sar_geo_fill_table); + +static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt, + __le16 *per_chain, u32 n_subbands, + int prof_a, int prof_b) +{ + int profs[BIOS_SAR_NUM_CHAINS] = { prof_a, prof_b }; + int i, j; + + for (i = 0; i < BIOS_SAR_NUM_CHAINS; i++) { + struct iwl_sar_profile *prof; + + /* don't allow SAR to be disabled (profile 0 means disable) */ + if (profs[i] == 0) + return -EPERM; + + /* we are off by one, so allow up to BIOS_SAR_MAX_PROFILE_NUM */ + if (profs[i] > BIOS_SAR_MAX_PROFILE_NUM) + return -EINVAL; + + /* profiles go from 1 to 4, so decrement to access the array */ + prof = &fwrt->sar_profiles[profs[i] - 1]; + + /* if the profile is disabled, do nothing */ + if (!prof->enabled) { + IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n", + profs[i]); + /* + * if one of the profiles is disabled, we + * ignore all of them and return 1 to + * differentiate disabled from other failures. + */ + return 1; + } + + IWL_DEBUG_INFO(fwrt, + "SAR EWRD: chain %d profile index %d\n", + i, profs[i]); + IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i); + for (j = 0; j < n_subbands; j++) { + per_chain[i * n_subbands + j] = + cpu_to_le16(prof->chains[i].subbands[j]); + IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n", + j, prof->chains[i].subbands[j]); + } + } + + return 0; +} + +int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt, + __le16 *per_chain, u32 n_tables, u32 n_subbands, + int prof_a, int prof_b) +{ + int i, ret = 0; + + for (i = 0; i < n_tables; i++) { + ret = iwl_sar_fill_table(fwrt, + &per_chain[i * n_subbands * BIOS_SAR_NUM_CHAINS], + n_subbands, prof_a, prof_b); + if (ret) + break; + } + + return ret; +} +IWL_EXPORT_SYMBOL(iwl_sar_fill_profile); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h new file mode 100644 index 000000000000..650430f21510 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2023 Intel Corporation + */ + +#ifndef __fw_regulatory_h__ +#define __fw_regulatory_h__ + +#include "fw/img.h" +#include "fw/api/commands.h" +#include "fw/api/power.h" +#include "fw/api/phy.h" +#include "fw/api/nvm-reg.h" +#include "fw/api/config.h" +#include "fw/img.h" +#include "iwl-trans.h" + +#define BIOS_SAR_MAX_PROFILE_NUM 4 +/* + * Each SAR profile has (up to, depends on the table revision) 4 chains: + * chain A, chain B, chain A when in CDB, chain B when in CDB + */ +#define BIOS_SAR_MAX_CHAINS_PER_PROFILE 4 +#define BIOS_SAR_NUM_CHAINS 2 +#define BIOS_SAR_MAX_SUB_BANDS_NUM 11 + +#define BIOS_GEO_NUM_CHAINS 2 +#define BIOS_GEO_MAX_NUM_BANDS 3 +#define BIOS_GEO_MAX_PROFILE_NUM 8 +#define BIOS_GEO_MIN_PROFILE_NUM 3 + +#define IWL_SAR_ENABLE_MSK BIT(0) + +/* + * The profile for revision 2 is a superset of revision 1, which is in + * turn a superset of revision 0. So we can store all revisions + * inside revision 2, which is what we represent here. + */ + +/* + * struct iwl_sar_profile_chain - per-chain values of a SAR profile + * @subbands: the SAR value for each subband + */ +struct iwl_sar_profile_chain { + u8 subbands[BIOS_SAR_MAX_SUB_BANDS_NUM]; +}; + +/* + * struct iwl_sar_profile - SAR profile from SAR tables + * @enabled: whether the profile is enabled or not + * @chains: per-chain SAR values + */ +struct iwl_sar_profile { + bool enabled; + struct iwl_sar_profile_chain chains[BIOS_SAR_MAX_CHAINS_PER_PROFILE]; +}; + +/* Same thing as with SAR, all revisions fit in revision 2 */ + +/* + * struct iwl_geo_profile_band - per-band geo SAR offsets + * @max: the max tx power allowed for the band + * @chains: SAR offsets values for each chain + */ +struct iwl_geo_profile_band { + u8 max; + u8 chains[BIOS_GEO_NUM_CHAINS]; +}; + +/* + * struct iwl_geo_profile - geo profile + * @bands: per-band table of the SAR offsets + */ +struct iwl_geo_profile { + struct iwl_geo_profile_band bands[BIOS_GEO_MAX_NUM_BANDS]; +}; + +struct iwl_fw_runtime; + +bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt); + +int iwl_sar_geo_fill_table(struct iwl_fw_runtime *fwrt, + struct iwl_per_chain_offset *table, + u32 n_bands, u32 n_profiles); + +int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt, + __le16 *per_chain, u32 n_tables, u32 n_subbands, + int prof_a, int prof_b); + +#endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 048830bb09f2..08734f48443e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -14,6 +14,7 @@ #include "fw/api/power.h" #include "iwl-eeprom-parse.h" #include "fw/acpi.h" +#include "fw/regulatory.h" struct iwl_fw_runtime_ops { void (*dump_start)(void *ctx); @@ -103,6 +104,8 @@ struct iwl_txf_iter_data { * @uefi_tables_lock_status: The status of the WIFI GUID UEFI variables lock: * 0: Unlocked, 1 and 2: Locked. * Only read the UEFI variables if locked. + * @sar_profiles: sar profiles as read from WRDS/EWRD BIOS tables + * @geo_profiles: geographic profiles as read from WGDS BIOS table */ struct iwl_fw_runtime { struct iwl_trans *trans; @@ -162,10 +165,10 @@ struct iwl_fw_runtime { bool tpc_enabled; #endif /* CONFIG_IWLWIFI_DEBUGFS */ #ifdef CONFIG_ACPI - struct iwl_sar_profile sar_profiles[ACPI_SAR_PROFILE_NUM]; + struct iwl_sar_profile sar_profiles[BIOS_SAR_MAX_PROFILE_NUM]; u8 sar_chain_a_profile; u8 sar_chain_b_profile; - struct iwl_geo_profile geo_profiles[ACPI_NUM_GEO_PROFILES_REV3]; + struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM]; u32 geo_rev; u32 geo_num_profiles; bool geo_enabled; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index a6db290923cd..f8ab31f9d109 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -936,9 +936,9 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) /* all structs have the same common part, add it */ len += sizeof(cmd.common); - ret = iwl_sar_select_profile(&mvm->fwrt, per_chain, - IWL_NUM_CHAIN_TABLES, - n_subbands, prof_a, prof_b); + ret = iwl_sar_fill_profile(&mvm->fwrt, per_chain, + IWL_NUM_CHAIN_TABLES, + n_subbands, prof_a, prof_b); /* return on error or if the profile is disabled (positive number) */ if (ret) @@ -994,7 +994,7 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) resp = (void *)cmd.resp_pkt->data; ret = le32_to_cpu(resp->profile_idx); - if (WARN_ON(ret > ACPI_NUM_GEO_PROFILES_REV3)) + if (WARN_ON(ret > BIOS_GEO_MAX_PROFILE_NUM)) ret = -EIO; iwl_free_resp(&cmd); @@ -1028,24 +1028,24 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) if (cmd_ver == 5) { len = sizeof(cmd.v5); n_bands = ARRAY_SIZE(cmd.v5.table[0]); - n_profiles = ACPI_NUM_GEO_PROFILES_REV3; + n_profiles = BIOS_GEO_MAX_PROFILE_NUM; } else if (cmd_ver == 4) { len = sizeof(cmd.v4); n_bands = ARRAY_SIZE(cmd.v4.table[0]); - n_profiles = ACPI_NUM_GEO_PROFILES_REV3; + n_profiles = BIOS_GEO_MAX_PROFILE_NUM; } else if (cmd_ver == 3) { len = sizeof(cmd.v3); n_bands = ARRAY_SIZE(cmd.v3.table[0]); - n_profiles = ACPI_NUM_GEO_PROFILES; + n_profiles = BIOS_GEO_MIN_PROFILE_NUM; } else if (fw_has_api(&mvm->fwrt.fw->ucode_capa, IWL_UCODE_TLV_API_SAR_TABLE_VER)) { len = sizeof(cmd.v2); n_bands = ARRAY_SIZE(cmd.v2.table[0]); - n_profiles = ACPI_NUM_GEO_PROFILES; + n_profiles = BIOS_GEO_MIN_PROFILE_NUM; } else { len = sizeof(cmd.v1); n_bands = ARRAY_SIZE(cmd.v1.table[0]); - n_profiles = ACPI_NUM_GEO_PROFILES; + n_profiles = BIOS_GEO_MIN_PROFILE_NUM; } BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v1, table) != @@ -1057,8 +1057,8 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, table) != offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, table)); /* the table is at the same position for all versions, so set use v1 */ - ret = iwl_sar_geo_init(&mvm->fwrt, &cmd.v1.table[0][0], - n_bands, n_profiles); + ret = iwl_sar_geo_fill_table(&mvm->fwrt, &cmd.v1.table[0][0], + n_bands, n_profiles); /* * It is a valid scenario to not support SAR, or miss wgds table, @@ -1076,7 +1076,7 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) * element name is misleading, as it doesn't contain the table * revision number, but whether the South Korea variation * should be used. - * This must be done after calling iwl_sar_geo_init(). + * This must be done after calling iwl_sar_geo_fill_table(). */ if (cmd_ver == 5) cmd.v5.table_revision = cpu_to_le32(sk); @@ -1413,7 +1413,7 @@ void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) } /* read SAR tables */ - ret = iwl_sar_get_wrds_table(&mvm->fwrt); + ret = iwl_acpi_get_wrds_table(&mvm->fwrt); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "WRDS SAR BIOS table invalid or unavailable. (%d)\n", @@ -1422,7 +1422,7 @@ void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) * If not available, don't fail and don't bother with EWRD and * WGDS */ - if (!iwl_sar_get_wgds_table(&mvm->fwrt)) { + if (!iwl_acpi_get_wgds_table(&mvm->fwrt)) { /* * If basic SAR is not available, we check for WGDS, * which should *not* be available either. If it is @@ -1433,7 +1433,7 @@ void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) } } else { - ret = iwl_sar_get_ewrd_table(&mvm->fwrt); + ret = iwl_acpi_get_ewrd_table(&mvm->fwrt); /* if EWRD is not available, we can still use * WRDS, so don't fail */ if (ret < 0) @@ -1443,7 +1443,7 @@ void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) /* read geo SAR table */ if (iwl_sar_geo_support(&mvm->fwrt)) { - ret = iwl_sar_get_wgds_table(&mvm->fwrt); + ret = iwl_acpi_get_wgds_table(&mvm->fwrt); if (ret < 0) IWL_DEBUG_RADIO(mvm, "Geo SAR BIOS table invalid or unavailable. (%d)\n", From c0a3dfc1ce955732ae8cd301a052f2277aa55436 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:38 +0200 Subject: [PATCH 162/378] wifi: iwlwifi: cleanup sending PER_CHAIN_LIMIT_OFFSET_CMD iwl_geo_tx_power_profiles_cmd::table_revision indicates whether to use South Korea scheme or not. We use South Korea scheme if the revision of WGDS table is 1. We used to read the WGDS table from ACPI inside iwl_sar_geo_fill_table(), so we had to set table_revision only after the call to it. This added an extra if...else for each cmd version. But it has been a while since we moved the BIOS tables reading to INIT stage, and iwl_sar_geo_fill_table() is now only copying the previously stored table to the cmd structure. Set the table_revision before the call to iwl_sar_geo_fill_table() and avoid that extra if...else. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.17a2384d4535.I306570874f1da0c6345066ebbf74a04b6c8aeb37@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 31 ++++++--------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index f8ab31f9d109..f964452ba433 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1008,7 +1008,7 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) u16 len; u32 n_bands; u32 n_profiles; - u32 sk = 0; + __le32 sk = cpu_to_le32(0); int ret; u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, IWL_FW_CMD_VER_UNKNOWN); @@ -1025,23 +1025,31 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) /* the ops field is at the same spot for all versions, so set in v1 */ cmd.v1.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES); + /* Only set to South Korea if the table revision is 1 */ + if (mvm->fwrt.geo_rev == 1) + sk = cpu_to_le32(1); + if (cmd_ver == 5) { len = sizeof(cmd.v5); n_bands = ARRAY_SIZE(cmd.v5.table[0]); n_profiles = BIOS_GEO_MAX_PROFILE_NUM; + cmd.v5.table_revision = sk; } else if (cmd_ver == 4) { len = sizeof(cmd.v4); n_bands = ARRAY_SIZE(cmd.v4.table[0]); n_profiles = BIOS_GEO_MAX_PROFILE_NUM; + cmd.v4.table_revision = sk; } else if (cmd_ver == 3) { len = sizeof(cmd.v3); n_bands = ARRAY_SIZE(cmd.v3.table[0]); n_profiles = BIOS_GEO_MIN_PROFILE_NUM; + cmd.v3.table_revision = sk; } else if (fw_has_api(&mvm->fwrt.fw->ucode_capa, IWL_UCODE_TLV_API_SAR_TABLE_VER)) { len = sizeof(cmd.v2); n_bands = ARRAY_SIZE(cmd.v2.table[0]); n_profiles = BIOS_GEO_MIN_PROFILE_NUM; + cmd.v2.table_revision = sk; } else { len = sizeof(cmd.v1); n_bands = ARRAY_SIZE(cmd.v1.table[0]); @@ -1067,27 +1075,6 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) if (ret) return 0; - /* Only set to South Korea if the table revision is 1 */ - if (mvm->fwrt.geo_rev == 1) - sk = 1; - - /* - * Set the table_revision to South Korea (1) or not (0). The - * element name is misleading, as it doesn't contain the table - * revision number, but whether the South Korea variation - * should be used. - * This must be done after calling iwl_sar_geo_fill_table(). - */ - if (cmd_ver == 5) - cmd.v5.table_revision = cpu_to_le32(sk); - else if (cmd_ver == 4) - cmd.v4.table_revision = cpu_to_le32(sk); - else if (cmd_ver == 3) - cmd.v3.table_revision = cpu_to_le32(sk); - else if (fw_has_api(&mvm->fwrt.fw->ucode_capa, - IWL_UCODE_TLV_API_SAR_TABLE_VER)) - cmd.v2.table_revision = cpu_to_le32(sk); - return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); } From 427661e4c48887ea2a226cd972e574ae7686fb95 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:39 +0200 Subject: [PATCH 163/378] wifi: iwlwifi: read SAR tables from UEFI All the regulatory tables will be read from UEFI, and only if it doesn't exist - they will be read from ACPI. Read SAR tables (WRDS, EWRD and WGDS) from UEFI. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.533b687e1efb.Icb316291e593c8d53f41fdea2d083367dc97e3c4@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 5 +- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 3 + .../wireless/intel/iwlwifi/fw/regulatory.c | 17 +++ .../wireless/intel/iwlwifi/fw/regulatory.h | 6 + .../net/wireless/intel/iwlwifi/fw/runtime.h | 4 +- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 108 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 77 +++++++++- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 137 ++++++++---------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 +- 10 files changed, 275 insertions(+), 86 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 401aca31704c..4e638287e9a4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -622,7 +622,6 @@ int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt) kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_wrds_table); int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) { @@ -731,7 +730,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_ewrd_table); int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) { @@ -748,7 +746,7 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) .revisions = BIT(3), .bands = ACPI_GEO_NUM_BANDS_REV2, .profiles = ACPI_NUM_GEO_PROFILES_REV3, - .min_profiles = 3, + .min_profiles = BIOS_GEO_MIN_PROFILE_NUM, }, { .revisions = BIT(2), @@ -886,7 +884,6 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_wgds_table); __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index ba7626543b70..3498deec58a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -105,6 +105,9 @@ #define IWL_SAR_ENABLE_MSK BIT(0) #define IWL_REDUCE_POWER_FLAGS_POS 1 +/* The Inidcator whether UEFI WIFI GUID tables are locked is read from ACPI */ +#define UEFI_WIFI_GUID_UNLOCKED 0 + /* * The profile for revision 2 is a superset of revision 1, which is in * turn a superset of revision 0. So we can store all revisions diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 58290bf64f42..862d8b8b0fc9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -6,6 +6,23 @@ #include "iwl-debug.h" #include "regulatory.h" #include "fw/runtime.h" +#include "fw/uefi.h" + +#define IWL_BIOS_TABLE_LOADER(__name) \ +int iwl_bios_get_ ## __name ## _table(struct iwl_fw_runtime *fwrt) \ +{ \ + int ret = -ENOENT; \ + if (fwrt->uefi_tables_lock_status > UEFI_WIFI_GUID_UNLOCKED) \ + ret = iwl_uefi_get_ ## __name ## _table(fwrt); \ + if (ret < 0) \ + ret = iwl_acpi_get_ ## __name ## _table(fwrt); \ + return ret; \ +} \ +IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name ## _table) + +IWL_BIOS_TABLE_LOADER(wrds); +IWL_BIOS_TABLE_LOADER(ewrd); +IWL_BIOS_TABLE_LOADER(wgds); bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 650430f21510..73c6122e7626 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -87,4 +87,10 @@ int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt, __le16 *per_chain, u32 n_tables, u32 n_subbands, int prof_a, int prof_b); +int iwl_bios_get_wrds_table(struct iwl_fw_runtime *fwrt); + +int iwl_bios_get_ewrd_table(struct iwl_fw_runtime *fwrt); + +int iwl_bios_get_wgds_table(struct iwl_fw_runtime *fwrt); + #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 08734f48443e..e55248b6b4c2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -164,21 +164,21 @@ struct iwl_fw_runtime { #ifdef CONFIG_IWLWIFI_DEBUGFS bool tpc_enabled; #endif /* CONFIG_IWLWIFI_DEBUGFS */ -#ifdef CONFIG_ACPI struct iwl_sar_profile sar_profiles[BIOS_SAR_MAX_PROFILE_NUM]; u8 sar_chain_a_profile; u8 sar_chain_b_profile; + u8 reduced_power_flags; struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM]; u32 geo_rev; u32 geo_num_profiles; bool geo_enabled; +#ifdef CONFIG_ACPI struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; u32 ppag_flags; u32 ppag_ver; bool ppag_table_valid; struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; - u8 reduced_power_flags; struct iwl_uats_table_cmd uats_table; u8 uefi_tables_lock_status; #endif diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 9c432d7b3674..a777cd4c70f7 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -413,3 +413,111 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, } IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table); #endif /* CONFIG_ACPI */ + +static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt, + struct uefi_sar_profile *uefi_sar_prof, + u8 prof_index, bool enabled) +{ + memcpy(&fwrt->sar_profiles[prof_index].chains, uefi_sar_prof, + sizeof(struct uefi_sar_profile)); + + fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK; +} + +int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_var_wrds *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME, + "WRDS", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_WRDS_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDS revision:%d\n", + data->revision); + goto out; + } + + /* The profile from WRDS is officially profile 1, but goes + * into sar_profiles[0] (because we don't have a profile 0). + */ + iwl_uefi_set_sar_profile(fwrt, &data->sar_profile, 0, data->mode); +out: + kfree(data); + return ret; +} + +int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_var_ewrd *data; + int i, ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME, + "EWRD", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_EWRD_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI EWRD revision:%d\n", + data->revision); + goto out; + } + + if (data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) { + ret = -EINVAL; + goto out; + } + + for (i = 0; i < data->num_profiles; i++) + /* The EWRD profiles officially go from 2 to 4, but we + * save them in sar_profiles[1-3] (because we don't + * have profile 0). So in the array we start from 1. + */ + iwl_uefi_set_sar_profile(fwrt, &data->sar_profiles[i], i + 1, + data->mode); + +out: + kfree(data); + return ret; +} + +int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_var_wgds *data; + int i, ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME, + "WGDS", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_WGDS_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n", + data->revision); + goto out; + } + + if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM || + data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Invalid number of profiles in WGDS: %d\n", + data->num_profiles); + goto out; + } + + fwrt->geo_rev = data->revision; + for (i = 0; i < data->num_profiles; i++) + memcpy(&fwrt->geo_profiles[i], &data->geo_profiles[i], + sizeof(struct iwl_geo_profile)); + + fwrt->geo_num_profiles = data->num_profiles; + fwrt->geo_enabled = true; +out: + kfree(data); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index bf61a8df1225..3141fca047c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -5,15 +5,24 @@ #ifndef __iwl_fw_uefi__ #define __iwl_fw_uefi__ +#include "fw/regulatory.h" + #define IWL_UEFI_OEM_PNVM_NAME L"UefiCnvWlanOemSignedPnvm" #define IWL_UEFI_REDUCED_POWER_NAME L"UefiCnvWlanReducedPower" #define IWL_UEFI_SGOM_NAME L"UefiCnvWlanSarGeoOffsetMapping" #define IWL_UEFI_STEP_NAME L"UefiCnvCommonSTEP" #define IWL_UEFI_UATS_NAME L"CnvUefiWlanUATS" +#define IWL_UEFI_WRDS_NAME L"UefiCnvWlanWRDS" +#define IWL_UEFI_EWRD_NAME L"UefiCnvWlanEWRD" +#define IWL_UEFI_WGDS_NAME L"UefiCnvWlanWGDS" #define IWL_SGOM_MAP_SIZE 339 #define IWL_UATS_MAP_SIZE 339 +#define IWL_UEFI_WRDS_REVISION 2 +#define IWL_UEFI_EWRD_REVISION 2 +#define IWL_UEFI_WGDS_REVISION 3 + struct pnvm_sku_package { u8 rev; u32 total_size; @@ -41,6 +50,55 @@ struct uefi_cnv_common_step_data { u8 radio2; } __packed; +/* + * struct uefi_sar_profile - a SAR profile as defined in UEFI + * + * @chains: a per-chain table of SAR values + */ +struct uefi_sar_profile { + struct iwl_sar_profile_chain chains[BIOS_SAR_MAX_CHAINS_PER_PROFILE]; +} __packed; + +/* + * struct uefi_cnv_var_wrds - WRDS table as defined in UEFI + * + * @revision: the revision of the table + * @mode: is WRDS enbaled/disabled + * @sar_profile: sar profile #1 + */ +struct uefi_cnv_var_wrds { + u8 revision; + u32 mode; + struct uefi_sar_profile sar_profile; +} __packed; + +/* + * struct uefi_cnv_var_ewrd - EWRD table as defined in UEFI + * @revision: the revision of the table + * @mode: is WRDS enbaled/disabled + * @num_profiles: how many additional profiles we have in this table (0-3) + * @sar_profiles: the additional SAR profiles (#2-#4) + */ +struct uefi_cnv_var_ewrd { + u8 revision; + u32 mode; + u32 num_profiles; + struct uefi_sar_profile sar_profiles[BIOS_SAR_MAX_PROFILE_NUM - 1]; +} __packed; + +/* + * struct uefi_cnv_var_wgds - WGDS table as defined in UEFI + * @revision: the revision of the table + * @num_profiles: the number of geo profiles we have in the table. + * The first 3 are mandatory, and can have up to 8. + * @geo_profiles: a per-profile table of the offsets to add to SAR values. + */ +struct uefi_cnv_var_wgds { + u8 revision; + u8 num_profiles; + struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM]; +} __packed; + /* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply @@ -55,6 +113,9 @@ int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, void iwl_uefi_get_step_table(struct iwl_trans *trans); int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, u32 tlv_len, struct iwl_pnvm_image *pnvm_data); +int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -85,6 +146,21 @@ iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, { return 0; } + +static inline int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} + +static inline int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} + +static inline int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} #endif /* CONFIG_EFI */ #if defined(CONFIG_EFI) && defined(CONFIG_ACPI) @@ -103,6 +179,5 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, { return 0; } - #endif #endif /* __iwl_fw_uefi__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index f964452ba433..c2267e0bd08e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -16,6 +16,7 @@ #include "fw/acpi.h" #include "fw/pnvm.h" #include "fw/uefi.h" +#include "fw/regulatory.h" #include "mvm.h" #include "fw/dbg.h" @@ -895,7 +896,6 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) sizeof(cmd), &cmd); } -#ifdef CONFIG_ACPI int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) { u32 cmd_id = REDUCE_TX_POWER_CMD; @@ -1078,6 +1078,8 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); } +#ifdef CONFIG_ACPI + int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) { union iwl_ppag_table_cmd cmd; @@ -1385,80 +1387,8 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) mvm->fwrt.uats_enabled = TRUE; } -void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) -{ - int ret; - - iwl_acpi_get_guid_lock_status(&mvm->fwrt); - - /* read PPAG table */ - ret = iwl_acpi_get_ppag_table(&mvm->fwrt); - if (ret < 0) { - IWL_DEBUG_RADIO(mvm, - "PPAG BIOS table invalid or unavailable. (%d)\n", - ret); - } - - /* read SAR tables */ - ret = iwl_acpi_get_wrds_table(&mvm->fwrt); - if (ret < 0) { - IWL_DEBUG_RADIO(mvm, - "WRDS SAR BIOS table invalid or unavailable. (%d)\n", - ret); - /* - * If not available, don't fail and don't bother with EWRD and - * WGDS */ - - if (!iwl_acpi_get_wgds_table(&mvm->fwrt)) { - /* - * If basic SAR is not available, we check for WGDS, - * which should *not* be available either. If it is - * available, issue an error, because we can't use SAR - * Geo without basic SAR. - */ - IWL_ERR(mvm, "BIOS contains WGDS but no WRDS\n"); - } - - } else { - ret = iwl_acpi_get_ewrd_table(&mvm->fwrt); - /* if EWRD is not available, we can still use - * WRDS, so don't fail */ - if (ret < 0) - IWL_DEBUG_RADIO(mvm, - "EWRD SAR BIOS table invalid or unavailable. (%d)\n", - ret); - - /* read geo SAR table */ - if (iwl_sar_geo_support(&mvm->fwrt)) { - ret = iwl_acpi_get_wgds_table(&mvm->fwrt); - if (ret < 0) - IWL_DEBUG_RADIO(mvm, - "Geo SAR BIOS table invalid or unavailable. (%d)\n", - ret); - /* we don't fail if the table is not available */ - } - } - - iwl_acpi_get_phy_filters(&mvm->fwrt, &mvm->phy_filters); -} #else /* CONFIG_ACPI */ -inline int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, - int prof_a, int prof_b) -{ - return 1; -} - -inline int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) -{ - return -ENOENT; -} - -static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) -{ - return 0; -} - int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) { return -ENOENT; @@ -1487,12 +1417,65 @@ static u8 iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) return DSM_VALUE_RFI_DISABLE; } -void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) -{ -} - #endif /* CONFIG_ACPI */ +void iwl_mvm_get_bios_tables(struct iwl_mvm *mvm) +{ + int ret; + + iwl_acpi_get_guid_lock_status(&mvm->fwrt); + + /* read PPAG table */ + ret = iwl_acpi_get_ppag_table(&mvm->fwrt); + if (ret < 0) { + IWL_DEBUG_RADIO(mvm, + "PPAG BIOS table invalid or unavailable. (%d)\n", + ret); + } + + /* read SAR tables */ + ret = iwl_bios_get_wrds_table(&mvm->fwrt); + if (ret < 0) { + IWL_DEBUG_RADIO(mvm, + "WRDS SAR BIOS table invalid or unavailable. (%d)\n", + ret); + /* + * If not available, don't fail and don't bother with EWRD and + * WGDS */ + + if (!iwl_bios_get_wgds_table(&mvm->fwrt)) { + /* + * If basic SAR is not available, we check for WGDS, + * which should *not* be available either. If it is + * available, issue an error, because we can't use SAR + * Geo without basic SAR. + */ + IWL_ERR(mvm, "BIOS contains WGDS but no WRDS\n"); + } + + } else { + ret = iwl_bios_get_ewrd_table(&mvm->fwrt); + /* if EWRD is not available, we can still use + * WRDS, so don't fail */ + if (ret < 0) + IWL_DEBUG_RADIO(mvm, + "EWRD SAR BIOS table invalid or unavailable. (%d)\n", + ret); + + /* read geo SAR table */ + if (iwl_sar_geo_support(&mvm->fwrt)) { + ret = iwl_bios_get_wgds_table(&mvm->fwrt); + if (ret < 0) + IWL_DEBUG_RADIO(mvm, + "Geo SAR BIOS table invalid or unavailable. (%d)\n", + ret); + /* we don't fail if the table is not available */ + } + } + + iwl_acpi_get_phy_filters(&mvm->fwrt, &mvm->phy_filters); +} + static void iwl_mvm_disconnect_iterator(void *data, u8 *mac, struct ieee80211_vif *vif) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index d414007c4755..14f4cf8a67c7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2381,7 +2381,7 @@ u64 iwl_mvm_ptp_get_adj_time(struct iwl_mvm *mvm, u64 base_time); int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b); int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm); int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm); -void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm); +void iwl_mvm_get_bios_tables(struct iwl_mvm *mvm); #ifdef CONFIG_IWLWIFI_DEBUGFS void iwl_mvm_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 20054a0c7892..1b41318e1e55 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1201,7 +1201,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_fw_runtime_init(&mvm->fwrt, trans, fw, &iwl_mvm_fwrt_ops, mvm, &iwl_mvm_sanitize_ops, mvm, dbgfs_dir); - iwl_mvm_get_acpi_tables(mvm); + iwl_mvm_get_bios_tables(mvm); iwl_uefi_get_sgom_table(trans, &mvm->fwrt); iwl_uefi_get_step_table(trans); From be3a8cbb1ca7d6737bfff9ea9ca260958f4bf6f0 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:40 +0200 Subject: [PATCH 164/378] wifi: iwlwifi: small cleanups in PPAG table flows 1. The name of iwl_read_ppag_table is misleading, as this function only fills the command structure from the previously read table. Rename it. 2. Don't initialize fwrt::ppag_flags to 0 as the entire fwrt is zeroed in the INIT stage anyway. 3. Don't filter out the reserved bits from fwrt::ppag_flags when printing it, as it is already done in 'read-from-bios' flow. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.48acf340e817.I810e457b80015c1931d96d3e13c849f0339723c3@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 10 +++++----- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 8 +++++--- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 4e638287e9a4..d2689d93da0c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -956,7 +956,6 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) int idx = 2; u8 cmd_ver; - fwrt->ppag_flags = 0; fwrt->ppag_table_valid = false; data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD); @@ -1057,7 +1056,8 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) } IWL_EXPORT_SYMBOL(iwl_acpi_get_ppag_table); -int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd, +int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, + union iwl_ppag_table_cmd *cmd, int *cmd_size) { u8 cmd_ver; @@ -1117,7 +1117,7 @@ int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *c /* ppag mode */ IWL_DEBUG_RADIO(fwrt, "PPAG MODE bits were read from bios: %d\n", - cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK)); + cmd->v1.flags); if ((cmd_ver == 1 && !fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) || (cmd_ver == 2 && fwrt->ppag_ver == 2)) { @@ -1129,7 +1129,7 @@ int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *c IWL_DEBUG_RADIO(fwrt, "PPAG MODE bits going to be sent: %d\n", - cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK)); + cmd->v1.flags); for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { for (j = 0; j < num_sub_bands; j++) { @@ -1143,7 +1143,7 @@ int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *c return 0; } -IWL_EXPORT_SYMBOL(iwl_read_ppag_table); +IWL_EXPORT_SYMBOL(iwl_fill_ppag_table); bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 3498deec58a5..ef927d74bc7c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -207,7 +207,8 @@ __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt); -int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd, +int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, + union iwl_ppag_table_cmd *cmd, int *cmd_size); bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt); @@ -283,8 +284,9 @@ static inline int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) return -ENOENT; } -static inline int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, - union iwl_ppag_table_cmd *cmd, int *cmd_size) +static inline int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, + union iwl_ppag_table_cmd *cmd, + int *cmd_size) { return -ENOENT; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index c2267e0bd08e..d52fc3119972 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1085,7 +1085,7 @@ int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) union iwl_ppag_table_cmd cmd; int ret, cmd_size; - ret = iwl_read_ppag_table(&mvm->fwrt, &cmd, &cmd_size); + ret = iwl_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size); /* Not supporting PPAG table is a valid scenario */ if (ret < 0) return 0; From 09059c6764a8870ff7515c2d78ecbea7fbcffc23 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:41 +0200 Subject: [PATCH 165/378] wifi: iwlwifi: prepare for reading PPAG table from UEFI As PPAG table is going to be read from UEFI, there are some cleanups required: Move functions/definitions that are common to both UEFI and ACPI to regulatory.h/c. In addition, rename the functions/macros names so it will be clear which one is ACPI specific, and which is common for ACPI and UEFI. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.25623670b422.I8132af7517e4faf0ea8cbeb2efe9651edd319b98@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 172 +----------------- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 35 ---- .../wireless/intel/iwlwifi/fw/regulatory.c | 161 ++++++++++++++++ .../wireless/intel/iwlwifi/fw/regulatory.h | 20 ++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- 5 files changed, 187 insertions(+), 203 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index d2689d93da0c..9b0ecfc087ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -4,7 +4,6 @@ * Copyright (C) 2019-2023 Intel Corporation */ #include -#include #include "iwl-drv.h" #include "iwl-debug.h" #include "acpi.h" @@ -20,63 +19,6 @@ const guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29, 0xDD, 0x26, 0xB5, 0xFD); IWL_EXPORT_SYMBOL(iwl_rfi_guid); -static const struct dmi_system_id dmi_ppag_approved_list[] = { - { .ident = "HP", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HP"), - }, - }, - { .ident = "SAMSUNG", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), - }, - }, - { .ident = "MSFT", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - }, - }, - { .ident = "ASUS", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - }, - }, - { .ident = "GOOGLE-HP", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Google"), - DMI_MATCH(DMI_BOARD_VENDOR, "HP"), - }, - }, - { .ident = "GOOGLE-ASUS", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Google"), - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek COMPUTER INC."), - }, - }, - { .ident = "GOOGLE-SAMSUNG", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Google"), - DMI_MATCH(DMI_BOARD_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), - }, - }, - { .ident = "DELL", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - }, - }, - { .ident = "DELL", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - }, - }, - { .ident = "RAZER", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Razer"), - }, - }, - {} -}; - static int iwl_acpi_get_handle(struct device *dev, acpi_string method, acpi_handle *ret_handle) { @@ -1002,7 +944,7 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) goto out_free; } - fwrt->ppag_flags = flags->integer.value & ACPI_PPAG_MASK; + fwrt->ppag_flags = flags->integer.value & IWL_PPAG_ETSI_CHINA_MASK; cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD), @@ -1036,11 +978,11 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) if (cmd_ver >= 4) continue; if ((j == 0 && - (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB || - fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_LB)) || + (fwrt->ppag_chains[i].subbands[j] > IWL_PPAG_MAX_LB || + fwrt->ppag_chains[i].subbands[j] < IWL_PPAG_MIN_LB)) || (j != 0 && - (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB || - fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) { + (fwrt->ppag_chains[i].subbands[j] > IWL_PPAG_MAX_HB || + fwrt->ppag_chains[i].subbands[j] < IWL_PPAG_MIN_HB))) { ret = -EINVAL; goto out_free; } @@ -1056,110 +998,6 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) } IWL_EXPORT_SYMBOL(iwl_acpi_get_ppag_table); -int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, - union iwl_ppag_table_cmd *cmd, - int *cmd_size) -{ - u8 cmd_ver; - int i, j, num_sub_bands; - s8 *gain; - - /* many firmware images for JF lie about this */ - if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) == - CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF)) - return -EOPNOTSUPP; - - if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) { - IWL_DEBUG_RADIO(fwrt, - "PPAG capability not supported by FW, command not sent.\n"); - return -EINVAL; - } - - cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, - WIDE_ID(PHY_OPS_GROUP, - PER_PLATFORM_ANT_GAIN_CMD), - IWL_FW_CMD_VER_UNKNOWN); - if (!fwrt->ppag_table_valid || (cmd_ver <= 3 && !fwrt->ppag_flags)) { - IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n"); - return -EINVAL; - } - - /* The 'flags' field is the same in v1 and in v2 so we can just - * use v1 to access it. - */ - cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags); - - IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver); - if (cmd_ver == 1) { - num_sub_bands = IWL_NUM_SUB_BANDS_V1; - gain = cmd->v1.gain[0]; - *cmd_size = sizeof(cmd->v1); - if (fwrt->ppag_ver == 1 || fwrt->ppag_ver == 2) { - /* in this case FW supports revision 0 */ - IWL_DEBUG_RADIO(fwrt, - "PPAG table rev is %d, send truncated table\n", - fwrt->ppag_ver); - } - } else if (cmd_ver >= 2 && cmd_ver <= 4) { - num_sub_bands = IWL_NUM_SUB_BANDS_V2; - gain = cmd->v2.gain[0]; - *cmd_size = sizeof(cmd->v2); - if (fwrt->ppag_ver == 0) { - /* in this case FW supports revisions 1 or 2 */ - IWL_DEBUG_RADIO(fwrt, - "PPAG table rev is 0, send padded table\n"); - } - } else { - IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n"); - return -EINVAL; - } - - /* ppag mode */ - IWL_DEBUG_RADIO(fwrt, - "PPAG MODE bits were read from bios: %d\n", - cmd->v1.flags); - if ((cmd_ver == 1 && !fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) || - (cmd_ver == 2 && fwrt->ppag_ver == 2)) { - cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); - IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n"); - } else { - IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n"); - } - - IWL_DEBUG_RADIO(fwrt, - "PPAG MODE bits going to be sent: %d\n", - cmd->v1.flags); - - for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { - for (j = 0; j < num_sub_bands; j++) { - gain[i * num_sub_bands + j] = - fwrt->ppag_chains[i].subbands[j]; - IWL_DEBUG_RADIO(fwrt, - "PPAG table: chain[%d] band[%d]: gain = %d\n", - i, j, gain[i * num_sub_bands + j]); - } - } - - return 0; -} -IWL_EXPORT_SYMBOL(iwl_fill_ppag_table); - -bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt) -{ - - if (!dmi_check_system(dmi_ppag_approved_list)) { - IWL_DEBUG_RADIO(fwrt, - "System vendor '%s' is not in the approved list, disabling PPAG.\n", - dmi_get_system_info(DMI_SYS_VENDOR)); - fwrt->ppag_flags = 0; - return false; - } - - return true; -} -IWL_EXPORT_SYMBOL(iwl_acpi_is_ppag_approved); - void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, struct iwl_phy_specific_cfg *filters) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index ef927d74bc7c..ca3587e9f856 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -94,29 +94,12 @@ #define ACPI_PPAG_WIFI_DATA_SIZE_V2 ((IWL_NUM_CHAIN_LIMITS * \ IWL_NUM_SUB_BANDS_V2) + 2) -/* PPAG gain value bounds in 1/8 dBm */ -#define ACPI_PPAG_MIN_LB -16 -#define ACPI_PPAG_MAX_LB 24 -#define ACPI_PPAG_MIN_HB -16 -#define ACPI_PPAG_MAX_HB 40 -#define ACPI_PPAG_MASK 3 -#define IWL_PPAG_ETSI_MASK BIT(0) - #define IWL_SAR_ENABLE_MSK BIT(0) #define IWL_REDUCE_POWER_FLAGS_POS 1 /* The Inidcator whether UEFI WIFI GUID tables are locked is read from ACPI */ #define UEFI_WIFI_GUID_UNLOCKED 0 -/* - * The profile for revision 2 is a superset of revision 1, which is in - * turn a superset of revision 0. So we can store all revisions - * inside revision 2, which is what we represent here. - */ -struct iwl_ppag_chain { - s8 subbands[ACPI_SAR_NUM_SUB_BANDS_REV2]; -}; - enum iwl_dsm_funcs_rev_0 { DSM_FUNC_QUERY = 0, DSM_FUNC_DISABLE_SRD = 1, @@ -207,12 +190,6 @@ __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt); -int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, - union iwl_ppag_table_cmd *cmd, - int *cmd_size); - -bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt); - void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, struct iwl_phy_specific_cfg *filters); @@ -284,18 +261,6 @@ static inline int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) return -ENOENT; } -static inline int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, - union iwl_ppag_table_cmd *cmd, - int *cmd_size) -{ - return -ENOENT; -} - -static inline bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt) -{ - return false; -} - static inline void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, struct iwl_phy_specific_cfg *filters) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 862d8b8b0fc9..2393c8a8e288 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2023 Intel Corporation */ +#include #include "iwl-drv.h" #include "iwl-debug.h" #include "regulatory.h" @@ -24,6 +25,63 @@ IWL_BIOS_TABLE_LOADER(wrds); IWL_BIOS_TABLE_LOADER(ewrd); IWL_BIOS_TABLE_LOADER(wgds); +static const struct dmi_system_id dmi_ppag_approved_list[] = { + { .ident = "HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + }, + }, + { .ident = "SAMSUNG", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), + }, + }, + { .ident = "MSFT", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + }, + }, + { .ident = "ASUS", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + }, + }, + { .ident = "GOOGLE-HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + }, + }, + { .ident = "GOOGLE-ASUS", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek COMPUTER INC."), + }, + }, + { .ident = "GOOGLE-SAMSUNG", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), + }, + }, + { .ident = "DELL", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + }, + }, + { .ident = "DELL", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + }, + }, + { .ident = "RAZER", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Razer"), + }, + }, + {} +}; + bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) { /* @@ -147,3 +205,106 @@ int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt, return ret; } IWL_EXPORT_SYMBOL(iwl_sar_fill_profile); + +int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, + union iwl_ppag_table_cmd *cmd, int *cmd_size) +{ + u8 cmd_ver; + int i, j, num_sub_bands; + s8 *gain; + + /* many firmware images for JF lie about this */ + if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) == + CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF)) + return -EOPNOTSUPP; + + if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) { + IWL_DEBUG_RADIO(fwrt, + "PPAG capability not supported by FW, command not sent.\n"); + return -EINVAL; + } + + cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, + WIDE_ID(PHY_OPS_GROUP, + PER_PLATFORM_ANT_GAIN_CMD), + IWL_FW_CMD_VER_UNKNOWN); + if (!fwrt->ppag_table_valid || (cmd_ver <= 3 && !fwrt->ppag_flags)) { + IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n"); + return -EINVAL; + } + + /* The 'flags' field is the same in v1 and in v2 so we can just + * use v1 to access it. + */ + cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags); + + IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver); + if (cmd_ver == 1) { + num_sub_bands = IWL_NUM_SUB_BANDS_V1; + gain = cmd->v1.gain[0]; + *cmd_size = sizeof(cmd->v1); + if (fwrt->ppag_ver == 1 || fwrt->ppag_ver == 2) { + /* in this case FW supports revision 0 */ + IWL_DEBUG_RADIO(fwrt, + "PPAG table rev is %d, send truncated table\n", + fwrt->ppag_ver); + } + } else if (cmd_ver >= 2 && cmd_ver <= 4) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = cmd->v2.gain[0]; + *cmd_size = sizeof(cmd->v2); + if (fwrt->ppag_ver == 0) { + /* in this case FW supports revisions 1 or 2 */ + IWL_DEBUG_RADIO(fwrt, + "PPAG table rev is 0, send padded table\n"); + } + } else { + IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n"); + return -EINVAL; + } + + /* ppag mode */ + IWL_DEBUG_RADIO(fwrt, + "PPAG MODE bits were read from bios: %d\n", + cmd->v1.flags); + if ((cmd_ver == 1 && + !fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) || + (cmd_ver == 2 && fwrt->ppag_ver == 2)) { + cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); + IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n"); + } else { + IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n"); + } + + IWL_DEBUG_RADIO(fwrt, + "PPAG MODE bits going to be sent: %d\n", + cmd->v1.flags); + + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + for (j = 0; j < num_sub_bands; j++) { + gain[i * num_sub_bands + j] = + fwrt->ppag_chains[i].subbands[j]; + IWL_DEBUG_RADIO(fwrt, + "PPAG table: chain[%d] band[%d]: gain = %d\n", + i, j, gain[i * num_sub_bands + j]); + } + } + + return 0; +} +IWL_EXPORT_SYMBOL(iwl_fill_ppag_table); + +bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt) +{ + if (!dmi_check_system(dmi_ppag_approved_list)) { + IWL_DEBUG_RADIO(fwrt, + "System vendor '%s' is not in the approved list, disabling PPAG.\n", + dmi_get_system_info(DMI_SYS_VENDOR)); + fwrt->ppag_flags = 0; + return false; + } + + return true; +} +IWL_EXPORT_SYMBOL(iwl_is_ppag_approved); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 73c6122e7626..63f650cb6517 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -31,6 +31,15 @@ #define IWL_SAR_ENABLE_MSK BIT(0) +/* PPAG gain value bounds in 1/8 dBm */ +#define IWL_PPAG_MIN_LB -16 +#define IWL_PPAG_MAX_LB 24 +#define IWL_PPAG_MIN_HB -16 +#define IWL_PPAG_MAX_HB 40 + +#define IWL_PPAG_ETSI_CHINA_MASK 3 +#define IWL_PPAG_ETSI_MASK BIT(0) + /* * The profile for revision 2 is a superset of revision 1, which is in * turn a superset of revision 0. So we can store all revisions @@ -75,6 +84,11 @@ struct iwl_geo_profile { struct iwl_geo_profile_band bands[BIOS_GEO_MAX_NUM_BANDS]; }; +/* Same thing as with SAR, all revisions fit in revision 2 */ +struct iwl_ppag_chain { + s8 subbands[BIOS_SAR_MAX_SUB_BANDS_NUM]; +}; + struct iwl_fw_runtime; bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt); @@ -87,6 +101,12 @@ int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt, __le16 *per_chain, u32 n_tables, u32 n_subbands, int prof_a, int prof_b); +int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, + union iwl_ppag_table_cmd *cmd, + int *cmd_size); + +bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt); + int iwl_bios_get_wrds_table(struct iwl_fw_runtime *fwrt); int iwl_bios_get_ewrd_table(struct iwl_fw_runtime *fwrt); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index d52fc3119972..1d759fe7d12d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1104,7 +1104,7 @@ int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) { /* no need to read the table, done in INIT stage */ - if (!(iwl_acpi_is_ppag_approved(&mvm->fwrt))) + if (!(iwl_is_ppag_approved(&mvm->fwrt))) return 0; return iwl_mvm_ppag_send_cmd(mvm); From 8408e83e16bb4d1d8f0ccf6937cae0357478ec50 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:42 +0200 Subject: [PATCH 166/378] wifi: iwlwifi: validate PPAG table when sent to FW We used to check enablement/validity of the PPAG table while reading it from BIOS. For newer FWs this checks were offloaded, and the driver needs to send the PPAG table anyway. The desicion whether the table needs to be validated before sending it is FW related and shouln't be in 'read-from-bios' flow. Move it to 'send-to-fw' flow instead. This will also help to avoid code duplication of checking validity in both ACPI and UEFI caes. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.7043b4087dda.I5a189f9a349556b84a79597fe1e46ffa93664df9@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 28 ---------------- .../wireless/intel/iwlwifi/fw/regulatory.c | 32 +++++++++++++++++-- .../net/wireless/intel/iwlwifi/fw/runtime.h | 1 - 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 9b0ecfc087ab..b029e88501a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -896,9 +896,6 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) union acpi_object *wifi_pkg, *data, *flags; int i, j, ret, tbl_rev, num_sub_bands = 0; int idx = 2; - u8 cmd_ver; - - fwrt->ppag_table_valid = false; data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD); if (IS_ERR(data)) @@ -945,18 +942,6 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) } fwrt->ppag_flags = flags->integer.value & IWL_PPAG_ETSI_CHINA_MASK; - cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, - WIDE_ID(PHY_OPS_GROUP, - PER_PLATFORM_ANT_GAIN_CMD), - IWL_FW_CMD_VER_UNKNOWN); - if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN) { - ret = -EINVAL; - goto out_free; - } - if (!fwrt->ppag_flags && cmd_ver <= 3) { - ret = 0; - goto out_free; - } /* * read, verify gain values and save them into the PPAG table. @@ -974,22 +959,9 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) } fwrt->ppag_chains[i].subbands[j] = ent->integer.value; - /* from ver 4 the fw deals with out of range values */ - if (cmd_ver >= 4) - continue; - if ((j == 0 && - (fwrt->ppag_chains[i].subbands[j] > IWL_PPAG_MAX_LB || - fwrt->ppag_chains[i].subbands[j] < IWL_PPAG_MIN_LB)) || - (j != 0 && - (fwrt->ppag_chains[i].subbands[j] > IWL_PPAG_MAX_HB || - fwrt->ppag_chains[i].subbands[j] < IWL_PPAG_MIN_HB))) { - ret = -EINVAL; - goto out_free; - } } } - fwrt->ppag_table_valid = true; ret = 0; out_free: diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 2393c8a8e288..3d42ea1ec5fd 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -206,12 +206,28 @@ int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt, } IWL_EXPORT_SYMBOL(iwl_sar_fill_profile); +static bool iwl_ppag_value_valid(struct iwl_fw_runtime *fwrt, int chain, + int subband) +{ + s8 ppag_val = fwrt->ppag_chains[chain].subbands[subband]; + + if ((subband == 0 && + (ppag_val > IWL_PPAG_MAX_LB || ppag_val < IWL_PPAG_MIN_LB)) || + (subband != 0 && + (ppag_val > IWL_PPAG_MAX_HB || ppag_val < IWL_PPAG_MIN_HB))) { + IWL_DEBUG_RADIO(fwrt, "Invalid PPAG value: %d\n", ppag_val); + return false; + } + return true; +} + int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd, int *cmd_size) { u8 cmd_ver; int i, j, num_sub_bands; s8 *gain; + bool send_ppag_always; /* many firmware images for JF lie about this */ if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) == @@ -226,9 +242,15 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, WIDE_ID(PHY_OPS_GROUP, - PER_PLATFORM_ANT_GAIN_CMD), - IWL_FW_CMD_VER_UNKNOWN); - if (!fwrt->ppag_table_valid || (cmd_ver <= 3 && !fwrt->ppag_flags)) { + PER_PLATFORM_ANT_GAIN_CMD), 1); + /* + * Starting from ver 4, driver needs to send the PPAG CMD regradless + * if PPAG is enabled/disabled or valid/invalid. + */ + send_ppag_always = cmd_ver > 3; + + /* Don't send PPAG if it is disabled */ + if (!send_ppag_always && !fwrt->ppag_flags) { IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n"); return -EINVAL; } @@ -283,6 +305,10 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { for (j = 0; j < num_sub_bands; j++) { + if (!send_ppag_always && + !iwl_ppag_value_valid(fwrt, i, j)) + return -EINVAL; + gain[i * num_sub_bands + j] = fwrt->ppag_chains[i].subbands[j]; IWL_DEBUG_RADIO(fwrt, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index e55248b6b4c2..d129782f2be4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -176,7 +176,6 @@ struct iwl_fw_runtime { struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; u32 ppag_flags; u32 ppag_ver; - bool ppag_table_valid; struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; struct iwl_uats_table_cmd uats_table; From bc8d0a4528f167742ecb511ba663795235e9d15c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:43 +0200 Subject: [PATCH 167/378] wifi: iwlwifi: read PPAG table from UEFI Try to read the PPAG table from UEFI first, and if the WIFI UEFI tables are unlocked or the table doesn't exist - try to read it from ACPI Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.6516da09aec1.I0dcaf0b6d8857417ba1318467a28da5d0d7d7f27@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 1 - .../wireless/intel/iwlwifi/fw/regulatory.c | 1 + .../wireless/intel/iwlwifi/fw/regulatory.h | 1 + .../net/wireless/intel/iwlwifi/fw/runtime.h | 2 +- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 29 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 22 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 16 ++-------- 7 files changed, 57 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index b029e88501a1..c150a66eed07 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -968,7 +968,6 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_ppag_table); void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, struct iwl_phy_specific_cfg *filters) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 3d42ea1ec5fd..fb4df1ff061d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -24,6 +24,7 @@ IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name ## _table) IWL_BIOS_TABLE_LOADER(wrds); IWL_BIOS_TABLE_LOADER(ewrd); IWL_BIOS_TABLE_LOADER(wgds); +IWL_BIOS_TABLE_LOADER(ppag); static const struct dmi_system_id dmi_ppag_approved_list[] = { { .ident = "HP", diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 63f650cb6517..954ba83d0277 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -113,4 +113,5 @@ int iwl_bios_get_ewrd_table(struct iwl_fw_runtime *fwrt); int iwl_bios_get_wgds_table(struct iwl_fw_runtime *fwrt); +int iwl_bios_get_ppag_table(struct iwl_fw_runtime *fwrt); #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index d129782f2be4..9bcf04987d8b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -172,10 +172,10 @@ struct iwl_fw_runtime { u32 geo_rev; u32 geo_num_profiles; bool geo_enabled; -#ifdef CONFIG_ACPI struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; u32 ppag_flags; u32 ppag_ver; +#ifdef CONFIG_ACPI struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; struct iwl_uats_table_cmd uats_table; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index a777cd4c70f7..f8092622d988 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -521,3 +521,32 @@ int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) kfree(data); return ret; } + +int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_var_ppag *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME, + "PPAG", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision < IWL_UEFI_MIN_PPAG_REV || + data->revision > IWL_UEFI_MAX_PPAG_REV) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n", + data->revision); + goto out; + } + + fwrt->ppag_ver = data->revision; + fwrt->ppag_flags = data->ppag_modes & IWL_PPAG_ETSI_CHINA_MASK; + + BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains)); + memcpy(&fwrt->ppag_chains, &data->ppag_chains, + sizeof(data->ppag_chains)); +out: + kfree(data); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 3141fca047c6..a2e6eb21de82 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -15,6 +15,7 @@ #define IWL_UEFI_WRDS_NAME L"UefiCnvWlanWRDS" #define IWL_UEFI_EWRD_NAME L"UefiCnvWlanEWRD" #define IWL_UEFI_WGDS_NAME L"UefiCnvWlanWGDS" +#define IWL_UEFI_PPAG_NAME L"UefiCnvWlanPPAG" #define IWL_SGOM_MAP_SIZE 339 #define IWL_UATS_MAP_SIZE 339 @@ -22,6 +23,8 @@ #define IWL_UEFI_WRDS_REVISION 2 #define IWL_UEFI_EWRD_REVISION 2 #define IWL_UEFI_WGDS_REVISION 3 +#define IWL_UEFI_MIN_PPAG_REV 1 +#define IWL_UEFI_MAX_PPAG_REV 3 struct pnvm_sku_package { u8 rev; @@ -99,6 +102,19 @@ struct uefi_cnv_var_wgds { struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM]; } __packed; +/* + * struct uefi_cnv_var_ppag - PPAG table as defined in UEFI + * @revision: the revision of the table + * @ppag_modes: bit 0 - PPAG is enabled/disabled in ETSI, + * bit 1 - PPAG is enabled/disabled in China + * @ppag_chains: the PPAG values per chain and band + */ +struct uefi_cnv_var_ppag { + u8 revision; + u32 ppag_modes; + struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; +} __packed; + /* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply @@ -116,6 +132,7 @@ int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt); int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt); int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -161,6 +178,11 @@ static inline int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) { return -ENOENT; } + +static inline int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} #endif /* CONFIG_EFI */ #if defined(CONFIG_EFI) && defined(CONFIG_ACPI) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 1d759fe7d12d..0a820dbeef23 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1078,8 +1078,6 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); } -#ifdef CONFIG_ACPI - int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) { union iwl_ppag_table_cmd cmd; @@ -1110,6 +1108,8 @@ static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) return iwl_mvm_ppag_send_cmd(mvm); } +#ifdef CONFIG_ACPI + static const struct dmi_system_id dmi_tas_approved_list[] = { { .ident = "HP", .matches = { @@ -1389,16 +1389,6 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) #else /* CONFIG_ACPI */ -int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) -{ - return -ENOENT; -} - -static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) -{ - return 0; -} - static void iwl_mvm_tas_init(struct iwl_mvm *mvm) { } @@ -1426,7 +1416,7 @@ void iwl_mvm_get_bios_tables(struct iwl_mvm *mvm) iwl_acpi_get_guid_lock_status(&mvm->fwrt); /* read PPAG table */ - ret = iwl_acpi_get_ppag_table(&mvm->fwrt); + ret = iwl_bios_get_ppag_table(&mvm->fwrt); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "PPAG BIOS table invalid or unavailable. (%d)\n", From e1c54d6377348fa2f7e216f53426f1b5fb9a59b1 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:44 +0200 Subject: [PATCH 168/378] wifi: iwlwifi: don't check TAS block list size twice Currenltly we check the validity of this variable twice. Remove the second check. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.2234490624c4.I6399b652a3c83afff1b0b5f114604d15892ee01e@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index c150a66eed07..d88a9df20abe 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -317,12 +317,6 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, cmd->v4.block_list_size = cpu_to_le32(block_list_size); IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size); - if (block_list_size > APCI_WTAS_BLACK_LIST_MAX) { - IWL_DEBUG_RADIO(fwrt, "TAS invalid array size value %u\n", - block_list_size); - ret = -EINVAL; - goto out_free; - } for (i = 0; i < block_list_size; i++) { u32 country; From ad5a85d8fdd346ecc34217e3bd713bf0b519912d Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:45 +0200 Subject: [PATCH 169/378] wifi: iwlwifi: prepare for reading TAS table from UEFI We are going to support reading BIOS tables from UEFI too, Refactor the TAS table flow: 1. Rename and move the common code to the regulatory.h/c files. 2. Remove the IWL_TAS_BLOCK_LIST_MAX, as we can use IWL_WTAS_BLACK_LIST_MAX instead. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.0c2197cf1feb.Ib0e83d5bd3f4d5cfa9c3d2925317ba49377d257f@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 19 +--- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 11 +-- .../wireless/intel/iwlwifi/fw/api/nvm-reg.h | 8 +- .../wireless/intel/iwlwifi/fw/regulatory.c | 91 +++++++++++++++++++ .../wireless/intel/iwlwifi/fw/regulatory.h | 15 ++- .../net/wireless/intel/iwlwifi/mvm/debugfs.c | 4 +- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 70 +------------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 - 8 files changed, 118 insertions(+), 101 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index d88a9df20abe..4fd9c6f768e6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -273,22 +273,9 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, ACPI_TYPE_INTEGER) { u32 tas_selection = (u32)wifi_pkg->package.elements[1].integer.value; - u16 override_iec = - (tas_selection & ACPI_WTAS_OVERRIDE_IEC_MSK) >> ACPI_WTAS_OVERRIDE_IEC_POS; - u16 enabled_iec = (tas_selection & ACPI_WTAS_ENABLE_IEC_MSK) >> - ACPI_WTAS_ENABLE_IEC_POS; - u8 usa_tas_uhb = (tas_selection & ACPI_WTAS_USA_UHB_MSK) >> ACPI_WTAS_USA_UHB_POS; - - enabled = tas_selection & ACPI_WTAS_ENABLED_MSK; - if (fw_ver <= 3) { - cmd->v3.override_tas_iec = cpu_to_le16(override_iec); - cmd->v3.enable_tas_iec = cpu_to_le16(enabled_iec); - } else { - cmd->v4.usa_tas_uhb_allowed = usa_tas_uhb; - cmd->v4.override_tas_iec = (u8)override_iec; - cmd->v4.enable_tas_iec = (u8)enabled_iec; - } + enabled = iwl_parse_tas_selection(fwrt, cmd, fw_ver, + tas_selection); } else if (tbl_rev == 0 && wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) { @@ -307,7 +294,7 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev); if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER || wifi_pkg->package.elements[2].integer.value > - APCI_WTAS_BLACK_LIST_MAX) { + IWL_WTAS_BLACK_LIST_MAX) { IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n", wifi_pkg->package.elements[2].integer.value); ret = -EINVAL; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index ca3587e9f856..319158ab36c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -78,16 +78,7 @@ * 1 element for block list size, * 16 elements for block list array */ -#define APCI_WTAS_BLACK_LIST_MAX 16 -#define ACPI_WTAS_WIFI_DATA_SIZE (3 + APCI_WTAS_BLACK_LIST_MAX) -#define ACPI_WTAS_ENABLED_MSK 0x1 -#define ACPI_WTAS_OVERRIDE_IEC_MSK 0x2 -#define ACPI_WTAS_ENABLE_IEC_MSK 0x4 -#define ACPI_WTAS_OVERRIDE_IEC_POS 0x1 -#define ACPI_WTAS_ENABLE_IEC_POS 0x2 -#define ACPI_WTAS_USA_UHB_MSK BIT(16) -#define ACPI_WTAS_USA_UHB_POS 16 - +#define ACPI_WTAS_WIFI_DATA_SIZE (3 + IWL_WTAS_BLACK_LIST_MAX) #define ACPI_PPAG_WIFI_DATA_SIZE_V1 ((IWL_NUM_CHAIN_LIMITS * \ IWL_NUM_SUB_BANDS_V1) + 2) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index 7ec959244ffc..c93a0665b040 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -7,6 +7,7 @@ #ifndef __iwl_fw_api_nvm_reg_h__ #define __iwl_fw_api_nvm_reg_h__ +#include "fw/regulatory.h" /** * enum iwl_regulatory_and_nvm_subcmd_ids - regulatory/NVM commands */ @@ -438,7 +439,6 @@ enum iwl_mcc_source { MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11, }; -#define IWL_TAS_BLOCK_LIST_MAX 16 /** * struct iwl_tas_config_cmd_v2 - configures the TAS * @block_list_size: size of relevant field in block_list_array @@ -446,7 +446,7 @@ enum iwl_mcc_source { */ struct iwl_tas_config_cmd_v2 { __le32 block_list_size; - __le32 block_list_array[IWL_TAS_BLOCK_LIST_MAX]; + __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; } __packed; /* TAS_CONFIG_CMD_API_S_VER_2 */ /** @@ -459,7 +459,7 @@ struct iwl_tas_config_cmd_v2 { */ struct iwl_tas_config_cmd_v3 { __le32 block_list_size; - __le32 block_list_array[IWL_TAS_BLOCK_LIST_MAX]; + __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; __le16 override_tas_iec; __le16 enable_tas_iec; } __packed; /* TAS_CONFIG_CMD_API_S_VER_3 */ @@ -476,7 +476,7 @@ struct iwl_tas_config_cmd_v3 { */ struct iwl_tas_config_cmd_v4 { __le32 block_list_size; - __le32 block_list_array[IWL_TAS_BLOCK_LIST_MAX]; + __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; u8 override_tas_iec; u8 enable_tas_iec; u8 usa_tas_uhb_allowed; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index fb4df1ff061d..570d8e74f839 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -3,6 +3,7 @@ * Copyright (C) 2023 Intel Corporation */ #include +#include "fw/api/nvm-reg.h" #include "iwl-drv.h" #include "iwl-debug.h" #include "regulatory.h" @@ -83,6 +84,62 @@ static const struct dmi_system_id dmi_ppag_approved_list[] = { {} }; +static const struct dmi_system_id dmi_tas_approved_list[] = { + { .ident = "HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + }, + }, + { .ident = "SAMSUNG", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), + }, + }, + { .ident = "LENOVO", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + }, + }, + { .ident = "DELL", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + }, + }, + { .ident = "MSFT", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + }, + }, + { .ident = "Acer", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + }, + }, + { .ident = "ASUS", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + }, + }, + { .ident = "GOOGLE-HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + }, + }, + { .ident = "MSI", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + }, + }, + { .ident = "Honor", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + }, + }, + /* keep last */ + {} +}; + bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) { /* @@ -335,3 +392,37 @@ bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt) return true; } IWL_EXPORT_SYMBOL(iwl_is_ppag_approved); + +bool iwl_is_tas_approved(void) +{ + return dmi_check_system(dmi_tas_approved_list); +} +IWL_EXPORT_SYMBOL(iwl_is_tas_approved); + +int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, + union iwl_tas_config_cmd *cmd, int fw_ver, + const u32 tas_selection) +{ + u8 override_iec = u32_get_bits(tas_selection, + IWL_WTAS_OVERRIDE_IEC_MSK); + u8 enabled_iec = u32_get_bits(tas_selection, IWL_WTAS_ENABLE_IEC_MSK); + u8 usa_tas_uhb = u32_get_bits(tas_selection, IWL_WTAS_USA_UHB_MSK); + int enabled = tas_selection & IWL_WTAS_ENABLED_MSK; + + IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n", + tas_selection); + + if (fw_ver < 3) + return enabled; + + if (fw_ver == 3) { + cmd->v3.override_tas_iec = cpu_to_le16(override_iec); + cmd->v3.enable_tas_iec = cpu_to_le16(enabled_iec); + } else { + cmd->v4.usa_tas_uhb_allowed = usa_tas_uhb; + cmd->v4.override_tas_iec = override_iec; + cmd->v4.enable_tas_iec = enabled_iec; + } + + return enabled; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 954ba83d0277..a2d9d7807833 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -10,7 +10,6 @@ #include "fw/api/commands.h" #include "fw/api/power.h" #include "fw/api/phy.h" -#include "fw/api/nvm-reg.h" #include "fw/api/config.h" #include "fw/img.h" #include "iwl-trans.h" @@ -40,6 +39,12 @@ #define IWL_PPAG_ETSI_CHINA_MASK 3 #define IWL_PPAG_ETSI_MASK BIT(0) +#define IWL_WTAS_BLACK_LIST_MAX 16 +#define IWL_WTAS_ENABLED_MSK 0x1 +#define IWL_WTAS_OVERRIDE_IEC_MSK 0x2 +#define IWL_WTAS_ENABLE_IEC_MSK 0x4 +#define IWL_WTAS_USA_UHB_MSK BIT(16) + /* * The profile for revision 2 is a superset of revision 1, which is in * turn a superset of revision 0. So we can store all revisions @@ -91,6 +96,8 @@ struct iwl_ppag_chain { struct iwl_fw_runtime; +union iwl_tas_config_cmd; + bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt); int iwl_sar_geo_fill_table(struct iwl_fw_runtime *fwrt, @@ -107,6 +114,12 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt); +bool iwl_is_tas_approved(void); + +int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, + union iwl_tas_config_cmd *cmd, int fw_ver, + const u32 tas_selection); + int iwl_bios_get_wrds_table(struct iwl_fw_runtime *fwrt); int iwl_bios_get_ewrd_table(struct iwl_fw_runtime *fwrt); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index edc8204f7c0e..d67986e157a2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -877,14 +877,14 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, le16_to_cpu(rsp->curr_mcc)); pos += scnprintf(pos, endpos - pos, "Block list entries:"); - for (i = 0; i < APCI_WTAS_BLACK_LIST_MAX; i++) + for (i = 0; i < IWL_WTAS_BLACK_LIST_MAX; i++) pos += scnprintf(pos, endpos - pos, " 0x%x", le16_to_cpu(rsp->block_list[i])); pos += scnprintf(pos, endpos - pos, "\nOEM name: %s\n", dmi_get_system_info(DMI_SYS_VENDOR)); pos += scnprintf(pos, endpos - pos, "\tVendor In Approved List: %s\n", - iwl_mvm_is_vendor_in_approved_list() ? "YES" : "NO"); + iwl_is_tas_approved() ? "YES" : "NO"); pos += scnprintf(pos, endpos - pos, "\tDo TAS Support Dual Radio?: %s\n", rsp->in_dual_radio ? "TRUE" : "FALSE"); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 0a820dbeef23..e848b041e995 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1110,66 +1110,7 @@ static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) #ifdef CONFIG_ACPI -static const struct dmi_system_id dmi_tas_approved_list[] = { - { .ident = "HP", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HP"), - }, - }, - { .ident = "SAMSUNG", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), - }, - }, - { .ident = "LENOVO", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - }, - }, - { .ident = "DELL", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - }, - }, - { .ident = "MSFT", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - }, - }, - { .ident = "Acer", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - }, - }, - { .ident = "ASUS", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - }, - }, - { .ident = "GOOGLE-HP", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Google"), - DMI_MATCH(DMI_BOARD_VENDOR, "HP"), - }, - }, - { .ident = "MSI", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), - }, - }, - { .ident = "Honor", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), - }, - }, - /* keep last */ - {} -}; -bool iwl_mvm_is_vendor_in_approved_list(void) -{ - return dmi_check_system(dmi_tas_approved_list); -} static bool iwl_mvm_add_to_tas_block_list(__le32 *list, __le32 *le_size, unsigned int mcc) { @@ -1177,7 +1118,7 @@ static bool iwl_mvm_add_to_tas_block_list(__le32 *list, __le32 *le_size, unsigne u32 size = le32_to_cpu(*le_size); /* Verify that there is room for another country */ - if (size >= IWL_TAS_BLOCK_LIST_MAX) + if (size >= IWL_WTAS_BLACK_LIST_MAX) return false; for (i = 0; i < size; i++) { @@ -1198,7 +1139,7 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) int cmd_size, fw_ver; BUILD_BUG_ON(ARRAY_SIZE(cmd.v3.block_list_array) < - APCI_WTAS_BLACK_LIST_MAX); + IWL_WTAS_BLACK_LIST_MAX); if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) { IWL_DEBUG_RADIO(mvm, "TAS not enabled in FW\n"); @@ -1219,7 +1160,7 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) if (ret == 0) return; - if (!iwl_mvm_is_vendor_in_approved_list()) { + if (!iwl_is_tas_approved()) { IWL_DEBUG_RADIO(mvm, "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", dmi_get_system_info(DMI_SYS_VENDOR)); @@ -1397,11 +1338,6 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) { } -bool iwl_mvm_is_vendor_in_approved_list(void) -{ - return false; -} - static u8 iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) { return DSM_VALUE_RFI_DISABLE; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 14f4cf8a67c7..c76ce6b1fa72 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2620,7 +2620,6 @@ static inline bool iwl_mvm_mei_filter_scan(struct iwl_mvm *mvm, void iwl_mvm_send_roaming_forbidden_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool forbidden); -bool iwl_mvm_is_vendor_in_approved_list(void); /* Callbacks for ieee80211_ops */ void iwl_mvm_mac_tx(struct ieee80211_hw *hw, From 3bc67e7c18cd69e88b801336cfe2a4dc7b4981a4 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:46 +0200 Subject: [PATCH 170/378] wifi: iwlwifi: separate TAS 'read-from-BIOS' and 'send-to-FW' flows Currently the TAS 'read-from-BIOS' flow receives the command struct and the version of it as read from FW TLVs, and fills the command accordingly. This seems wrong, we should have the 'read-from-BIOS' flow (iwl_acpi_get_tas in iwlwifi) reading/parsing/validating the table from BIOS, and the 'send-to-FW' flow (iwl_mvm_tas_init) doing all the FW versioning checks and cmd filling. Move the cmd filling to the 'send-to-fw' flow. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.24df27772a71.I57b702af4feb3f38dc21d52593c25de4b1999e4b@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 8 ++-- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 5 +- .../wireless/intel/iwlwifi/fw/api/nvm-reg.h | 24 ++++------ .../wireless/intel/iwlwifi/fw/regulatory.c | 19 ++------ .../wireless/intel/iwlwifi/fw/regulatory.h | 12 +++-- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 47 ++++++++++++------- 6 files changed, 62 insertions(+), 53 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 4fd9c6f768e6..0abb954f3056 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -251,7 +251,7 @@ iwl_acpi_get_wifi_pkg(struct device *dev, int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - union iwl_tas_config_cmd *cmd, int fw_ver) + struct iwl_tas_data *tas_data) { union acpi_object *wifi_pkg, *data; int ret, tbl_rev, i, block_list_size, enabled; @@ -274,7 +274,7 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, u32 tas_selection = (u32)wifi_pkg->package.elements[1].integer.value; - enabled = iwl_parse_tas_selection(fwrt, cmd, fw_ver, + enabled = iwl_parse_tas_selection(fwrt, tas_data, tas_selection); } else if (tbl_rev == 0 && @@ -301,7 +301,7 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, goto out_free; } block_list_size = wifi_pkg->package.elements[2].integer.value; - cmd->v4.block_list_size = cpu_to_le32(block_list_size); + tas_data->block_list_size = cpu_to_le32(block_list_size); IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size); @@ -317,7 +317,7 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, } country = wifi_pkg->package.elements[3 + i].integer.value; - cmd->v4.block_list_array[i] = cpu_to_le32(country); + tas_data->block_list_array[i] = cpu_to_le32(country); IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country); } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 319158ab36c4..0ce9a33bbb77 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -7,6 +7,7 @@ #define __iwl_fw_acpi__ #include +#include "fw/regulatory.h" #include "fw/api/commands.h" #include "fw/api/power.h" #include "fw/api/phy.h" @@ -175,7 +176,7 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt); int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt); int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - union iwl_tas_config_cmd *cmd, int fw_ver); + struct iwl_tas_data *data); __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); @@ -237,7 +238,7 @@ static inline int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) } static inline int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - union iwl_tas_config_cmd *cmd, int fw_ver) + struct iwl_tas_data *data) { return -ENOENT; } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index c93a0665b040..8c886569f01e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -440,34 +440,29 @@ enum iwl_mcc_source { }; /** - * struct iwl_tas_config_cmd_v2 - configures the TAS + * struct iwl_tas_config_cmd_common - configures the TAS. + * This is also the v2 structure. * @block_list_size: size of relevant field in block_list_array * @block_list_array: list of countries where TAS must be disabled */ -struct iwl_tas_config_cmd_v2 { +struct iwl_tas_config_cmd_common { __le32 block_list_size; __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; } __packed; /* TAS_CONFIG_CMD_API_S_VER_2 */ /** * struct iwl_tas_config_cmd_v3 - configures the TAS - * @block_list_size: size of relevant field in block_list_array - * @block_list_array: list of countries where TAS must be disabled * @override_tas_iec: indicates whether to override default value of IEC regulatory * @enable_tas_iec: in case override_tas_iec is set - * indicates whether IEC regulatory is enabled or disabled */ struct iwl_tas_config_cmd_v3 { - __le32 block_list_size; - __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; __le16 override_tas_iec; __le16 enable_tas_iec; } __packed; /* TAS_CONFIG_CMD_API_S_VER_3 */ /** * struct iwl_tas_config_cmd_v3 - configures the TAS - * @block_list_size: size of relevant field in block_list_array - * @block_list_array: list of countries where TAS must be disabled * @override_tas_iec: indicates whether to override default value of IEC regulatory * @enable_tas_iec: in case override_tas_iec is set - * indicates whether IEC regulatory is enabled or disabled @@ -475,19 +470,20 @@ struct iwl_tas_config_cmd_v3 { * @reserved: reserved */ struct iwl_tas_config_cmd_v4 { - __le32 block_list_size; - __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; u8 override_tas_iec; u8 enable_tas_iec; u8 usa_tas_uhb_allowed; u8 reserved; } __packed; /* TAS_CONFIG_CMD_API_S_VER_4 */ -union iwl_tas_config_cmd { - struct iwl_tas_config_cmd_v2 v2; - struct iwl_tas_config_cmd_v3 v3; - struct iwl_tas_config_cmd_v4 v4; +struct iwl_tas_config_cmd { + struct iwl_tas_config_cmd_common common; + union { + struct iwl_tas_config_cmd_v3 v3; + struct iwl_tas_config_cmd_v4 v4; + }; }; + /** * enum iwl_lari_config_masks - bit masks for the various LARI config operations * @LARI_CONFIG_DISABLE_11AC_UKRAINE_MSK: disable 11ac in ukraine diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 570d8e74f839..20154b0fb7e6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -3,7 +3,6 @@ * Copyright (C) 2023 Intel Corporation */ #include -#include "fw/api/nvm-reg.h" #include "iwl-drv.h" #include "iwl-debug.h" #include "regulatory.h" @@ -400,11 +399,11 @@ bool iwl_is_tas_approved(void) IWL_EXPORT_SYMBOL(iwl_is_tas_approved); int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, - union iwl_tas_config_cmd *cmd, int fw_ver, + struct iwl_tas_data *tas_data, const u32 tas_selection) { u8 override_iec = u32_get_bits(tas_selection, - IWL_WTAS_OVERRIDE_IEC_MSK); + IWL_WTAS_OVERRIDE_IEC_MSK); u8 enabled_iec = u32_get_bits(tas_selection, IWL_WTAS_ENABLE_IEC_MSK); u8 usa_tas_uhb = u32_get_bits(tas_selection, IWL_WTAS_USA_UHB_MSK); int enabled = tas_selection & IWL_WTAS_ENABLED_MSK; @@ -412,17 +411,9 @@ int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n", tas_selection); - if (fw_ver < 3) - return enabled; - - if (fw_ver == 3) { - cmd->v3.override_tas_iec = cpu_to_le16(override_iec); - cmd->v3.enable_tas_iec = cpu_to_le16(enabled_iec); - } else { - cmd->v4.usa_tas_uhb_allowed = usa_tas_uhb; - cmd->v4.override_tas_iec = override_iec; - cmd->v4.enable_tas_iec = enabled_iec; - } + tas_data->usa_tas_uhb_allowed = usa_tas_uhb; + tas_data->override_tas_iec = override_iec; + tas_data->enable_tas_iec = enabled_iec; return enabled; } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index a2d9d7807833..53bd82417cc3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -94,9 +94,15 @@ struct iwl_ppag_chain { s8 subbands[BIOS_SAR_MAX_SUB_BANDS_NUM]; }; -struct iwl_fw_runtime; +struct iwl_tas_data { + __le32 block_list_size; + __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; + u8 override_tas_iec; + u8 enable_tas_iec; + u8 usa_tas_uhb_allowed; +}; -union iwl_tas_config_cmd; +struct iwl_fw_runtime; bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt); @@ -117,7 +123,7 @@ bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt); bool iwl_is_tas_approved(void); int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, - union iwl_tas_config_cmd *cmd, int fw_ver, + struct iwl_tas_data *tas_data, const u32 tas_selection); int iwl_bios_get_wrds_table(struct iwl_fw_runtime *fwrt); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index e848b041e995..0f36eddb3143 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1135,10 +1135,13 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) { u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG); int ret; - union iwl_tas_config_cmd cmd = {}; + struct iwl_tas_data data = {}; + struct iwl_tas_config_cmd cmd = {}; int cmd_size, fw_ver; - BUILD_BUG_ON(ARRAY_SIZE(cmd.v3.block_list_array) < + BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) != + IWL_WTAS_BLACK_LIST_MAX); + BUILD_BUG_ON(ARRAY_SIZE(cmd.common.block_list_array) != IWL_WTAS_BLACK_LIST_MAX); if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) { @@ -1146,10 +1149,7 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) return; } - fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, - IWL_FW_CMD_VER_UNKNOWN); - - ret = iwl_acpi_get_tas(&mvm->fwrt, &cmd, fw_ver); + ret = iwl_acpi_get_tas(&mvm->fwrt, &data); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "TAS table invalid or unavailable. (%d)\n", @@ -1164,12 +1164,12 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", dmi_get_system_info(DMI_SYS_VENDOR)); - if ((!iwl_mvm_add_to_tas_block_list(cmd.v4.block_list_array, - &cmd.v4.block_list_size, - IWL_MCC_US)) || - (!iwl_mvm_add_to_tas_block_list(cmd.v4.block_list_array, - &cmd.v4.block_list_size, - IWL_MCC_CANADA))) { + if ((!iwl_mvm_add_to_tas_block_list(data.block_list_array, + &data.block_list_size, + IWL_MCC_US)) || + (!iwl_mvm_add_to_tas_block_list(data.block_list_array, + &data.block_list_size, + IWL_MCC_CANADA))) { IWL_DEBUG_RADIO(mvm, "Unable to add US/Canada to TAS block list, disabling TAS\n"); return; @@ -1180,10 +1180,25 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) dmi_get_system_info(DMI_SYS_VENDOR)); } - /* v4 is the same size as v3, so no need to differentiate here */ - cmd_size = fw_ver < 3 ? - sizeof(struct iwl_tas_config_cmd_v2) : - sizeof(struct iwl_tas_config_cmd_v3); + fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + + memcpy(&cmd.common, &data, sizeof(struct iwl_tas_config_cmd_common)); + + /* Set v3 or v4 specific parts. will be trunctated for fw_ver < 3 */ + if (fw_ver == 4) { + cmd.v4.override_tas_iec = data.override_tas_iec; + cmd.v4.enable_tas_iec = data.enable_tas_iec; + cmd.v4.usa_tas_uhb_allowed = data.usa_tas_uhb_allowed; + } else { + cmd.v3.override_tas_iec = cpu_to_le16(data.override_tas_iec); + cmd.v3.enable_tas_iec = cpu_to_le16(data.enable_tas_iec); + } + + cmd_size = sizeof(struct iwl_tas_config_cmd_common); + if (fw_ver >= 3) + /* v4 is the same size as v3 */ + cmd_size += sizeof(struct iwl_tas_config_cmd_v3); ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &cmd); if (ret < 0) From 084e0452a42b1d4ccde601cc1873a4ee9d8a4cbb Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 31 Jan 2024 10:24:47 +0200 Subject: [PATCH 171/378] wifi: iwlwifi: read WTAS table from UEFI Try to read the WTAS table from UEFI first, and if the WIFI UEFI tables are unlocked or the table doesn't exist - try to read it from ACPI. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240131091413.45e6ff7b5063.Id3aec70887e14533b10d564f32c0cf5f2a14b792@changeid [move uefi_tables_lock_status outside ifdef to fix build errors] Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 6 +-- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 8 ++-- .../wireless/intel/iwlwifi/fw/regulatory.c | 32 ++++++++----- .../wireless/intel/iwlwifi/fw/regulatory.h | 4 ++ .../net/wireless/intel/iwlwifi/fw/runtime.h | 2 +- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 48 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 23 +++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 12 ++--- 8 files changed, 106 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 0abb954f3056..170c840c321a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -249,9 +249,8 @@ iwl_acpi_get_wifi_pkg(struct device *dev, tbl_rev); } - -int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - struct iwl_tas_data *tas_data) +int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *tas_data) { union acpi_object *wifi_pkg, *data; int ret, tbl_rev, i, block_list_size, enabled; @@ -326,7 +325,6 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_tas); int iwl_acpi_get_mcc(struct device *dev, char *mcc) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 0ce9a33bbb77..61bfdaa467d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -175,8 +175,8 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt); int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt); -int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - struct iwl_tas_data *data); +int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data); __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); @@ -237,8 +237,8 @@ static inline int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) return 1; } -static inline int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - struct iwl_tas_data *data) +static inline int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data) { return -ENOENT; } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 20154b0fb7e6..4cf22e280dfc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -9,22 +9,32 @@ #include "fw/runtime.h" #include "fw/uefi.h" -#define IWL_BIOS_TABLE_LOADER(__name) \ -int iwl_bios_get_ ## __name ## _table(struct iwl_fw_runtime *fwrt) \ -{ \ +#define GET_BIOS_TABLE(__name, ...) \ +do { \ int ret = -ENOENT; \ if (fwrt->uefi_tables_lock_status > UEFI_WIFI_GUID_UNLOCKED) \ - ret = iwl_uefi_get_ ## __name ## _table(fwrt); \ + ret = iwl_uefi_get_ ## __name(__VA_ARGS__); \ if (ret < 0) \ - ret = iwl_acpi_get_ ## __name ## _table(fwrt); \ + ret = iwl_acpi_get_ ## __name(__VA_ARGS__); \ return ret; \ -} \ -IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name ## _table) +} while (0) -IWL_BIOS_TABLE_LOADER(wrds); -IWL_BIOS_TABLE_LOADER(ewrd); -IWL_BIOS_TABLE_LOADER(wgds); -IWL_BIOS_TABLE_LOADER(ppag); +#define IWL_BIOS_TABLE_LOADER(__name) \ +int iwl_bios_get_ ## __name(struct iwl_fw_runtime *fwrt) \ +{GET_BIOS_TABLE(__name, fwrt); } \ +IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name) + +#define IWL_BIOS_TABLE_LOADER_DATA(__name, data_type) \ +int iwl_bios_get_ ## __name(struct iwl_fw_runtime *fwrt, \ + data_type * data) \ +{GET_BIOS_TABLE(__name, fwrt, data); } \ +IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name) + +IWL_BIOS_TABLE_LOADER(wrds_table); +IWL_BIOS_TABLE_LOADER(ewrd_table); +IWL_BIOS_TABLE_LOADER(wgds_table); +IWL_BIOS_TABLE_LOADER(ppag_table); +IWL_BIOS_TABLE_LOADER_DATA(tas_table, struct iwl_tas_data); static const struct dmi_system_id dmi_ppag_approved_list[] = { { .ident = "HP", diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 53bd82417cc3..7719ee764c55 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -133,4 +133,8 @@ int iwl_bios_get_ewrd_table(struct iwl_fw_runtime *fwrt); int iwl_bios_get_wgds_table(struct iwl_fw_runtime *fwrt); int iwl_bios_get_ppag_table(struct iwl_fw_runtime *fwrt); + +int iwl_bios_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data); + #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 9bcf04987d8b..16d9ea6dd386 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -179,8 +179,8 @@ struct iwl_fw_runtime { struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; struct iwl_uats_table_cmd uats_table; - u8 uefi_tables_lock_status; #endif + u8 uefi_tables_lock_status; bool uats_enabled; }; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index f8092622d988..d6cbfe6c5a17 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -550,3 +550,51 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) kfree(data); return ret; } + +int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *tas_data) +{ + struct uefi_cnv_var_wtas *uefi_tas; + int ret = 0, enabled, i; + + uefi_tas = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WTAS_NAME, + "WTAS", sizeof(*uefi_tas), NULL); + if (IS_ERR(uefi_tas)) + return -EINVAL; + + if (uefi_tas->revision != IWL_UEFI_WTAS_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WTAS revision:%d\n", + uefi_tas->revision); + goto out; + } + + enabled = iwl_parse_tas_selection(fwrt, tas_data, + uefi_tas->tas_selection); + if (!enabled) { + IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n"); + ret = 0; + goto out; + } + + IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", + uefi_tas->revision); + if (uefi_tas->black_list_size > IWL_WTAS_BLACK_LIST_MAX) { + IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %d\n", + uefi_tas->black_list_size); + ret = -EINVAL; + goto out; + } + tas_data->block_list_size = cpu_to_le32(uefi_tas->black_list_size); + IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", uefi_tas->black_list_size); + + for (i = 0; i < uefi_tas->black_list_size; i++) { + tas_data->block_list_array[i] = + cpu_to_le32(uefi_tas->black_list[i]); + IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", + uefi_tas->black_list[i]); + } +out: + kfree(uefi_tas); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index a2e6eb21de82..f849a485d0a9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -16,6 +16,7 @@ #define IWL_UEFI_EWRD_NAME L"UefiCnvWlanEWRD" #define IWL_UEFI_WGDS_NAME L"UefiCnvWlanWGDS" #define IWL_UEFI_PPAG_NAME L"UefiCnvWlanPPAG" +#define IWL_UEFI_WTAS_NAME L"UefiCnvWlanWTAS" #define IWL_SGOM_MAP_SIZE 339 #define IWL_UATS_MAP_SIZE 339 @@ -25,6 +26,7 @@ #define IWL_UEFI_WGDS_REVISION 3 #define IWL_UEFI_MIN_PPAG_REV 1 #define IWL_UEFI_MAX_PPAG_REV 3 +#define IWL_UEFI_WTAS_REVISION 1 struct pnvm_sku_package { u8 rev; @@ -115,6 +117,19 @@ struct uefi_cnv_var_ppag { struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; } __packed; +/* struct uefi_cnv_var_wtas - WTAS tabled as defined in UEFI + * @revision: the revision of the table + * @tas_selection: different options of TAS enablement. + * @black_list_size: the number of defined entried in the black list + * @black_list: a list of countries that are not allowed to use the TAS feature + */ +struct uefi_cnv_var_wtas { + u8 revision; + u32 tas_selection; + u8 black_list_size; + u16 black_list[IWL_WTAS_BLACK_LIST_MAX]; +} __packed; + /* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply @@ -133,6 +148,8 @@ int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt); int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt); int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt); int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -183,6 +200,12 @@ static inline int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) { return -ENOENT; } + +static inline int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data) +{ + return -ENOENT; +} #endif /* CONFIG_EFI */ #if defined(CONFIG_EFI) && defined(CONFIG_ACPI) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 0f36eddb3143..72f8a6cf20c7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1108,10 +1108,6 @@ static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) return iwl_mvm_ppag_send_cmd(mvm); } -#ifdef CONFIG_ACPI - - - static bool iwl_mvm_add_to_tas_block_list(__le32 *list, __le32 *le_size, unsigned int mcc) { int i; @@ -1149,7 +1145,7 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) return; } - ret = iwl_acpi_get_tas(&mvm->fwrt, &data); + ret = iwl_bios_get_tas_table(&mvm->fwrt, &data); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "TAS table invalid or unavailable. (%d)\n", @@ -1205,6 +1201,8 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret); } +#ifdef CONFIG_ACPI + static u8 iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) { u8 value; @@ -1345,10 +1343,6 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) #else /* CONFIG_ACPI */ -static void iwl_mvm_tas_init(struct iwl_mvm *mvm) -{ -} - static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) { } From 7d366663b7d84ecdb52ba141c1cafd4f3c73e0ff Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:00:02 +0100 Subject: [PATCH 172/378] wifi: mac80211_hwsim: add control to skip beacons To test certain beacon loss scenarios it can be useful to simply not send a couple of beacons. Add a simple debugfs file (per vif) to skip sending the beacons. They're still fully prepared etc. so their DTIM count etc. will appear as if they were simply corrupt over the air or otherwise not received. Reviewed-by: Jeff Johnson Link: https://msgid.link/20240129200001.a267383709e6.I36f427d17c3478a7df46e205716f5ebc9b35a918@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 403d892c0468..3adc11bcaf12 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -213,6 +213,7 @@ static const struct ieee80211_regdomain *hwsim_world_regdom_custom[] = { struct hwsim_vif_priv { u32 magic; + u32 skip_beacons; u8 bssid[ETH_ALEN]; bool assoc; bool bcn_en; @@ -2128,6 +2129,16 @@ static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw, return 0; } +#ifdef CONFIG_MAC80211_DEBUGFS +static void mac80211_hwsim_vif_add_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + + debugfs_create_u32("skip_beacons", 0600, vif->debugfs_dir, + &vp->skip_beacons); +} +#endif static int mac80211_hwsim_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -2193,12 +2204,19 @@ static void __mac80211_hwsim_beacon_tx(struct ieee80211_bss_conf *link_conf, struct ieee80211_vif *vif, struct sk_buff *skb) { + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct ieee80211_tx_info *info; struct ieee80211_rate *txrate; struct ieee80211_mgmt *mgmt; /* TODO: get MCS */ int bitrate = 100; + if (vp->skip_beacons) { + vp->skip_beacons--; + dev_kfree_skb(skb); + return; + } + info = IEEE80211_SKB_CB(skb); if (ieee80211_hw_check(hw, SUPPORTS_RC_TABLE)) ieee80211_get_tx_rates(vif, NULL, skb, @@ -3857,6 +3875,13 @@ static int hwsim_pmsr_report_nl(struct sk_buff *msg, struct genl_info *info) return err; } +#ifdef CONFIG_MAC80211_DEBUGFS +#define HWSIM_DEBUGFS_OPS \ + .vif_add_debugfs = mac80211_hwsim_vif_add_debugfs, +#else +#define HWSIM_DEBUGFS_OPS +#endif + #define HWSIM_COMMON_OPS \ .tx = mac80211_hwsim_tx, \ .wake_tx_queue = ieee80211_handle_wake_tx_queue, \ @@ -3881,7 +3906,8 @@ static int hwsim_pmsr_report_nl(struct sk_buff *msg, struct genl_info *info) .get_et_stats = mac80211_hwsim_get_et_stats, \ .get_et_strings = mac80211_hwsim_get_et_strings, \ .start_pmsr = mac80211_hwsim_start_pmsr, \ - .abort_pmsr = mac80211_hwsim_abort_pmsr, + .abort_pmsr = mac80211_hwsim_abort_pmsr, \ + HWSIM_DEBUGFS_OPS #define HWSIM_NON_MLO_OPS \ .sta_add = mac80211_hwsim_sta_add, \ From f455f5ad500a993b65ae1c4c59639eff558e1688 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:54:35 +0100 Subject: [PATCH 173/378] wifi: mac80211: trace SMPS requests from driver Even if there are a lot of possible ways drivers might call this, at least knowing when they do and with what settings can be useful. Add tracing for it. Link: https://msgid.link/20240129195435.b20d2ead2013.I8213e65c274451d523a3397519ac578c3ed2df4d@changeid [removed link-id contortions as suggested by Jeff] Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 4 +++- net/mac80211/trace.h | 30 +++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 749f4ecab990..bccc99a00383 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -9,7 +9,7 @@ * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation * Copyright 2017 Intel Deutschland GmbH - * Copyright(c) 2020-2023 Intel Corporation + * Copyright(c) 2020-2024 Intel Corporation */ #include @@ -603,6 +603,8 @@ void ieee80211_request_smps(struct ieee80211_vif *vif, unsigned int link_id, if (WARN_ON(!link)) goto out; + trace_api_request_smps(sdata->local, sdata, link, smps_mode); + if (link->u.mgd.driver_smps_mode == smps_mode) goto out; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 1c0c46b11c6d..e2dde3e77c30 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -2,7 +2,7 @@ /* * Portions of this file * Copyright(c) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2023 Intel Corporation + * Copyright (C) 2018 - 2024 Intel Corporation */ #if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) @@ -3035,6 +3035,34 @@ TRACE_EVENT(api_radar_detected, ) ); +TRACE_EVENT(api_request_smps, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_link_data *link, + enum ieee80211_smps_mode smps_mode), + + TP_ARGS(local, sdata, link, smps_mode), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(int, link_id) + __field(u32, smps_mode) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->link_id = link->link_id, + __entry->smps_mode = smps_mode; + ), + + TP_printk( + LOCAL_PR_FMT " " VIF_PR_FMT " link:%d, smps_mode:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->link_id, __entry->smps_mode + ) +); + /* * Tracing for internal functions * (which may also be called in response to driver calls) From 392d3dfdfd68f4be6964d4fc8f3979a7a859e6f2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:57:39 +0100 Subject: [PATCH 174/378] wifi: mac80211: clean up FILS discovery change flags handling It doesn't make sense to return BSS change flags in an int, as they're a bigger type. For this particular function it still works OK, but clean it up to avoid future errors (or copying this code in a broken way.) Reviewed-by: Jeff Johnson Link: https://msgid.link/20240129195739.e340a7d5e7c6.I1dfcca32d43dce903494a2c474844491682671b4@changeid Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index dd237081dbe3..2830ddac45b2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -953,7 +953,8 @@ ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata, struct cfg80211_fils_discovery *params, struct ieee80211_link_data *link, - struct ieee80211_bss_conf *link_conf) + struct ieee80211_bss_conf *link_conf, + u64 *changed) { struct fils_discovery_data *new, *old = NULL; struct ieee80211_fils_discovery *fd; @@ -980,7 +981,8 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata, RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL); } - return BSS_CHANGED_FILS_DISCOVERY; + *changed |= BSS_CHANGED_FILS_DISCOVERY; + return 0; } static int @@ -1443,10 +1445,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, goto error; err = ieee80211_set_fils_discovery(sdata, ¶ms->fils_discovery, - link, link_conf); + link, link_conf, &changed); if (err < 0) goto error; - changed |= err; err = ieee80211_set_unsol_bcast_probe_resp(sdata, ¶ms->unsol_bcast_probe_resp, @@ -1518,10 +1519,9 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, return err; err = ieee80211_set_fils_discovery(sdata, ¶ms->fils_discovery, - link, link_conf); + link, link_conf, &changed); if (err < 0) return err; - changed |= err; err = ieee80211_set_unsol_bcast_probe_resp(sdata, ¶ms->unsol_bcast_probe_resp, From 57d1b4632e03e5f938972055c45b0f2e475e6929 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:54:21 +0100 Subject: [PATCH 175/378] wifi: nl80211: move WPA version validation to policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For a contiguous mask (starting with bit 0) of allowed values in a bitmap, it's equivalent to check "!(val & ~mask)" and "val ∈ [0, mask]". Use that to move the WPA versions check to the policy, for better error reporting. Reviewed-by: Jeff Johnson Link: https://msgid.link/20240129195421.e8cae9866ccb.I2539b395e3476307d702c6867e51a937e52e57a0@changeid Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e4f41f86e295..68c20409eca6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #include @@ -581,7 +581,11 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, [NL80211_ATTR_STATUS_CODE] = { .type = NLA_U16 }, [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, - [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, + [NL80211_ATTR_WPA_VERSIONS] = + NLA_POLICY_RANGE(NLA_U32, 0, + NL80211_WPA_VERSION_1 | + NL80211_WPA_VERSION_2 | + NL80211_WPA_VERSION_3), [NL80211_ATTR_PID] = { .type = NLA_U32 }, [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, [NL80211_ATTR_PMKID] = NLA_POLICY_EXACT_LEN_WARN(WLAN_PMKID_LEN), @@ -10604,13 +10608,6 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) return res; } -static bool nl80211_valid_wpa_versions(u32 wpa_versions) -{ - return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | - NL80211_WPA_VERSION_2 | - NL80211_WPA_VERSION_3)); -} - static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -10836,12 +10833,9 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, return -EINVAL; } - if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) { + if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) settings->wpa_versions = nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]); - if (!nl80211_valid_wpa_versions(settings->wpa_versions)) - return -EINVAL; - } if (info->attrs[NL80211_ATTR_AKM_SUITES]) { void *data; From 358ddc7bfa980888b69b406a7c4ce0a5b0ac5c30 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 29 Jan 2024 20:00:52 +0100 Subject: [PATCH 176/378] wifi: mac80211_hwsim: enable all links only in MLO The existing code is enabling all usable links when moving to authorized state, but this should happen only for MLO connections. Fix this. Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129200054.f5459f6c29c8.I397814449e17950fcf882ef44a1e790a71aa1dce@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 3adc11bcaf12..a06a462d38f0 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -4,7 +4,7 @@ * Copyright (c) 2008, Jouni Malinen * Copyright (c) 2011, Javier Lopez * Copyright (c) 2016 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2023 Intel Corporation + * Copyright (C) 2018 - 2024 Intel Corporation */ /* @@ -2671,10 +2671,11 @@ static int mac80211_hwsim_sta_state(struct ieee80211_hw *hw, return mac80211_hwsim_sta_add(hw, vif, sta); /* - * when client is authorized (AP station marked as such), - * enable all links + * in an MLO connection, when client is authorized + * (AP station marked as such), enable all links */ - if (vif->type == NL80211_IFTYPE_STATION && + if (ieee80211_vif_is_mld(vif) && + vif->type == NL80211_IFTYPE_STATION && new_state == IEEE80211_STA_AUTHORIZED && !sta->tdls) ieee80211_set_active_links_async(vif, ieee80211_vif_usable_links(vif)); From b341590e77d89d8812051fdd2354ff04e12f211b Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 29 Jan 2024 20:00:53 +0100 Subject: [PATCH 177/378] wifi: mac80211: don't allow deactivation of all links The set_active_links API is intended for link switching, so switching to no links at all is not supported. Add a warning to check that. Signed-off-by: Miri Korenblit Link: https://msgid.link/20240129200054.e3c113f94508.Ia35f927f914bf98dd8f9350dd4f78b1d901b1c1d@changeid Signed-off-by: Johannes Berg --- net/mac80211/link.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/mac80211/link.c b/net/mac80211/link.c index d4f86955afa6..c0d05efcf6f8 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -444,6 +444,9 @@ int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links) lockdep_assert_wiphy(local->hw.wiphy); + if (WARN_ON(!active_links)) + return -EINVAL; + if (!drv_can_activate_links(local, sdata, active_links)) return -EINVAL; @@ -472,6 +475,9 @@ void ieee80211_set_active_links_async(struct ieee80211_vif *vif, { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + if (WARN_ON(!active_links)) + return; + if (!ieee80211_sdata_running(sdata)) return; From d10fb5ecc82211691264156bbf8b8a988d57944e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 31 Jan 2024 21:38:16 +0100 Subject: [PATCH 178/378] iwlwifi: fw: fix more kernel-doc warnings Fix some more kernel-doc warnings in FW API definitions. Signed-off-by: Emmanuel Grumbach Link: https://msgid.link/20240131213817.9f30c6529216.I69e98612c6c81cf1b7bd480d8041b5d3e25610d3@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/d3.h | 2 +- drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h | 2 +- drivers/net/wireless/intel/iwlwifi/fw/api/debug.h | 2 +- drivers/net/wireless/intel/iwlwifi/fw/api/location.h | 1 + drivers/net/wireless/intel/iwlwifi/fw/api/tx.h | 4 ++++ 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index ea99d41040d2..d2a74beed3a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -324,7 +324,7 @@ struct iwl_wowlan_patterns_cmd { u8 n_patterns; /** - * @n_patterns: sta_id + * @sta_id: sta_id */ u8 sta_id; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 751b596ea1a5..0f7903c5a4df 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -101,7 +101,7 @@ enum iwl_data_path_subcmd_ids { RX_NO_DATA_NOTIF = 0xF5, /** - * @THERMAL_DUAL_CHAIN_DISABLE_REQ: firmware request for SMPS mode, + * @THERMAL_DUAL_CHAIN_REQUEST: firmware request for SMPS mode, * &struct iwl_thermal_dual_chain_request */ THERMAL_DUAL_CHAIN_REQUEST = 0xF6, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index 798731ecbefd..a4b54488e0fc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -394,7 +394,7 @@ struct iwl_buf_alloc_cmd { * * @first_word: magic word value * @second_word: magic word value - * @framfrags: DRAM fragmentaion detail + * @dram_frags: DRAM fragmentaion detail */ struct iwl_dram_info { __le32 first_word; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h index b044990c7b87..25530a29317e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h @@ -630,6 +630,7 @@ enum iwl_location_frame_format { * @IWL_LOCATION_BW_20MHZ: 20MHz * @IWL_LOCATION_BW_40MHZ: 40MHz * @IWL_LOCATION_BW_80MHZ: 80MHz + * @IWL_LOCATION_BW_160MHZ: 160MHz */ enum iwl_location_bw { IWL_LOCATION_BW_20MHZ, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index 842360b1e995..d9e4c75403b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -76,6 +76,8 @@ enum iwl_tx_flags { * to a secured STA * @IWL_TX_FLAGS_HIGH_PRI: high priority frame (like EAPOL) - can affect rate * selection, retry limits and BT kill + * @IWL_TX_FLAGS_RTS: firmware used an RTS + * @IWL_TX_FLAGS_CTS: firmware used CTS-to-self */ enum iwl_tx_cmd_flags { IWL_TX_FLAGS_CMD_RATE = BIT(0), @@ -884,6 +886,7 @@ struct iwl_tx_path_flush_cmd { /** * struct iwl_flush_queue_info - virtual flush queue info + * @tid: the tid to flush * @queue_num: virtual queue id * @read_before_flush: read pointer before flush * @read_after_flush: read pointer after flush @@ -897,6 +900,7 @@ struct iwl_flush_queue_info { /** * struct iwl_tx_path_flush_cmd_rsp -- queue/FIFO flush command response + * @sta_id: the station for which the queue was flushed * @num_flushed_queues: number of queues in queues array * @queues: all flushed queues */ From 3ec064e0a2cb667eceea7410d2ce7945a186beb2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Jan 2024 22:00:39 +0100 Subject: [PATCH 179/378] wifi: iwlwifi: remove unused function prototype Saw this while going through the code, this function hasn't existed for a while now; remove it. Link: https://msgid.link/20240131220039.6fdb8cbf4814.I6c46065b836cafd93df676dd88c99a626a25bf46@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/dbg.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index eb38c686b5cb..98d56e778d99 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -306,8 +306,6 @@ static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt, bool sync) _iwl_dbg_tlv_time_point(fwrt, tp_id, NULL, sync); } -void iwl_fw_error_print_fseq_regs(struct iwl_fw_runtime *fwrt); - static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt, struct iwl_lmac_alive *lmac, struct iwl_umac_alive *umac) From f74f397afe2b7a1e2f8a0a3384006e36fb940f87 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Jan 2024 22:02:27 +0100 Subject: [PATCH 180/378] wifi: iwlwifi: api: clean up some kernel-doc/typos Add some kernel-doc for a union, and fix a couple of typos I noticed looking through this. Link: https://msgid.link/20240131220227.7fd507f09bb1.I278edc9a3d5de7fddcd84009a93c494c42686b68@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index f15e6d64c298..362161369884 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -242,9 +242,9 @@ struct iwl_mac_low_latency_cmd { * @esr_transition_timeout: the timeout required by the AP for the * eSR transition. * Available only from version 2 of the command. - * This values comes from the EMLSR transition delay in the EML + * This value comes from the EMLSR transition delay in the EML * Capabilities subfield. - * @medium_sync_delay: the value as it appeasr in P802.11be_D2.2 Figure 9-1002j. + * @medium_sync_delay: the value as it appears in P802.11be_D2.2 Figure 9-1002j. * @assoc_id: unique ID assigned by the AP during association * @reserved1: alignment * @data_policy: see &enum iwl_mac_data_policy @@ -317,7 +317,6 @@ enum iwl_mac_config_filter_flags { * If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0 * len delim to determine if AGG or single. * @client: client mac data - * @go_ibss: mac data for go or ibss * @p2p_dev: mac data for p2p device */ struct iwl_mac_config_cmd { From a51d1cf5ad64a17230cf90e1770d363c5cbc0d5c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:25 +0200 Subject: [PATCH 181/378] wifi: iwlwifi: prepare for reading SPLC from UEFI As the iwl_bios_get_x() functions are now generated using a macro, and this macro requires the all iwl_acpi_get_x() to have the same prototype, change iwl_acpi_get_pwr_limit() to return a int and the actuall power limit will be filled in a pointer function parameter. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.4cce81198afe.Ice8b1b97a68da9ec7b5a4799ddb668642198e1af@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 23 +++++++++----------- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 6 +++-- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 170c840c321a..d6e7de2543b2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -362,31 +362,28 @@ int iwl_acpi_get_mcc(struct device *dev, char *mcc) } IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc); -u64 iwl_acpi_get_pwr_limit(struct device *dev) +int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit) { union acpi_object *data, *wifi_pkg; - u64 dflt_pwr_limit; - int tbl_rev; + int tbl_rev, ret = -EINVAL; - data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD); - if (IS_ERR(data)) { - dflt_pwr_limit = 0; + *dflt_pwr_limit = 0; + data = iwl_acpi_get_object(fwrt->dev, ACPI_SPLC_METHOD); + if (IS_ERR(data)) goto out; - } - wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev); if (IS_ERR(wifi_pkg) || tbl_rev != 0 || - wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) { - dflt_pwr_limit = 0; + wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) goto out_free; - } - dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value; + *dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value; + ret = 0; out_free: kfree(data); out: - return dflt_pwr_limit; + return ret; } IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 61bfdaa467d4..f0ed7174a951 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -156,7 +156,7 @@ int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, */ int iwl_acpi_get_mcc(struct device *dev, char *mcc); -u64 iwl_acpi_get_pwr_limit(struct device *dev); +int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit); /* * iwl_acpi_get_eckv - read external clock validation from ACPI, if available @@ -212,8 +212,10 @@ static inline int iwl_acpi_get_mcc(struct device *dev, char *mcc) return -ENOENT; } -static inline u64 iwl_acpi_get_pwr_limit(struct device *dev) +static inline int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit) { + *dflt_pwr_limit = 0; return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 1b41318e1e55..0e7b66a20b7c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -689,7 +689,7 @@ static u32 iwl_mvm_min_backoff(struct iwl_mvm *mvm) if (!backoff) return 0; - dflt_pwr_limit = iwl_acpi_get_pwr_limit(mvm->dev); + iwl_acpi_get_pwr_limit(&mvm->fwrt, &dflt_pwr_limit); while (backoff->pwr) { if (dflt_pwr_limit >= backoff->pwr) From 18f523654d4943c87da3ec512dad74828be764e4 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:26 +0200 Subject: [PATCH 182/378] wifi: iwlwifi: read SPLC from UEFI Try to read the SPLC table from UEFI first, and if the WIFI UEFI tables are unlocked or the table doesn't exist - try to read it from ACPI Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.3d9d835b6edb.I7ea262df9431ced787b77c87149c6d7bddb7e7d6@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 1 - .../wireless/intel/iwlwifi/fw/regulatory.c | 2 ++ .../wireless/intel/iwlwifi/fw/regulatory.h | 2 ++ drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 23 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 21 +++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 +- 6 files changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index d6e7de2543b2..e74745f939ae 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -385,7 +385,6 @@ int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit) out: return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit); int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 4cf22e280dfc..452c7cc49c27 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -35,6 +35,8 @@ IWL_BIOS_TABLE_LOADER(ewrd_table); IWL_BIOS_TABLE_LOADER(wgds_table); IWL_BIOS_TABLE_LOADER(ppag_table); IWL_BIOS_TABLE_LOADER_DATA(tas_table, struct iwl_tas_data); +IWL_BIOS_TABLE_LOADER_DATA(pwr_limit, u64); + static const struct dmi_system_id dmi_ppag_approved_list[] = { { .ident = "HP", diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 7719ee764c55..b391c6fc3bcc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -137,4 +137,6 @@ int iwl_bios_get_ppag_table(struct iwl_fw_runtime *fwrt); int iwl_bios_get_tas_table(struct iwl_fw_runtime *fwrt, struct iwl_tas_data *data); +int iwl_bios_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit); #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index d6cbfe6c5a17..5ec82205be12 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -598,3 +598,26 @@ int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, kfree(uefi_tas); return ret; } + +int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit) +{ + struct uefi_cnv_var_splc *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_SPLC_NAME, + "SPLC", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_SPLC_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI SPLC revision:%d\n", + data->revision); + goto out; + } + *dflt_pwr_limit = data->default_pwr_limit; +out: + kfree(data); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index f849a485d0a9..4cf3af576920 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -17,6 +17,8 @@ #define IWL_UEFI_WGDS_NAME L"UefiCnvWlanWGDS" #define IWL_UEFI_PPAG_NAME L"UefiCnvWlanPPAG" #define IWL_UEFI_WTAS_NAME L"UefiCnvWlanWTAS" +#define IWL_UEFI_SPLC_NAME L"UefiCnvWlanSPLC" + #define IWL_SGOM_MAP_SIZE 339 #define IWL_UATS_MAP_SIZE 339 @@ -27,6 +29,7 @@ #define IWL_UEFI_MIN_PPAG_REV 1 #define IWL_UEFI_MAX_PPAG_REV 3 #define IWL_UEFI_WTAS_REVISION 1 +#define IWL_UEFI_SPLC_REVISION 0 struct pnvm_sku_package { u8 rev; @@ -130,6 +133,15 @@ struct uefi_cnv_var_wtas { u16 black_list[IWL_WTAS_BLACK_LIST_MAX]; } __packed; +/* struct uefi_cnv_var_splc - SPLC tabled as defined in UEFI + * @revision: the revision of the table + * @default_pwr_limit: The default maximum power per device + */ +struct uefi_cnv_var_splc { + u8 revision; + u32 default_pwr_limit; +} __packed; + /* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply @@ -150,6 +162,8 @@ int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt); int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt); int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, struct iwl_tas_data *data); +int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -206,6 +220,13 @@ static inline int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, { return -ENOENT; } + +static inline int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit) +{ + *dflt_pwr_limit = 0; + return 0; +} #endif /* CONFIG_EFI */ #if defined(CONFIG_EFI) && defined(CONFIG_ACPI) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 0e7b66a20b7c..747fc91ef8d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -689,7 +689,7 @@ static u32 iwl_mvm_min_backoff(struct iwl_mvm *mvm) if (!backoff) return 0; - iwl_acpi_get_pwr_limit(&mvm->fwrt, &dflt_pwr_limit); + iwl_bios_get_pwr_limit(&mvm->fwrt, &dflt_pwr_limit); while (backoff->pwr) { if (dflt_pwr_limit >= backoff->pwr) From 61ff84440c402ad3e0c3989b3bef99f0db5e6766 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 1 Feb 2024 16:17:27 +0200 Subject: [PATCH 183/378] wifi: iwlwifi: mvm: don't send NDPs for new tx devices New tx devices may have issues sending NDPs from the host. Send a CQM event instead. If the AP is really gone, we will get a beacon loss and disconnect. Signed-off-by: Emmanuel Grumbach Reviewed-by: Gregory Greenman Reviewed-by: Berg, Johannes Signed-off-by: Miri Korenblit Link: https://msgid.link/20240201155157.e95d53448e94.I0ec92f1ca56a62cd8c13390b9fe60e9a7e9411c7@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index c4f96125cf33..bcf78ccba8c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1626,10 +1626,14 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, * TODO: the threshold should be adjusted based on latency conditions, * and/or in case of a CS flow on one of the other AP vifs. */ - if (rx_missed_bcon > IWL_MVM_MISSED_BEACONS_THRESHOLD_LONG) + if (rx_missed_bcon > IWL_MVM_MISSED_BEACONS_THRESHOLD_LONG) { iwl_mvm_connection_loss(mvm, vif, "missed beacons"); - else if (rx_missed_bcon_since_rx > IWL_MVM_MISSED_BEACONS_THRESHOLD) - ieee80211_beacon_loss(vif); + } else if (rx_missed_bcon_since_rx > IWL_MVM_MISSED_BEACONS_THRESHOLD) { + if (!iwl_mvm_has_new_tx_api(mvm)) + ieee80211_beacon_loss(vif); + else + ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC); + } iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_MISSED_BEACONS, &tp_data); From dd273e8a22f9302c499ae4248c95a212fccd6811 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Thu, 1 Feb 2024 16:17:28 +0200 Subject: [PATCH 184/378] wifi: iwlwifi: mvm: use fast balance scan in case of an active P2P GO Set fast balance scan in case of active P2P GO, regardless of the BSS DTIM interval. This will increase the chances of scheduler to successfully schedule out-of-channel events. Signed-off-by: Ayala Beker Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://msgid.link/20240201155157.310a00388e11.Ib136140dffa8704e68ff14e8fb69d35b97057171@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 7b6f1cdca067..f3e3986b4c72 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -241,13 +241,11 @@ iwl_mvm_scan_type _iwl_mvm_get_scan_type(struct iwl_mvm *mvm, return IWL_SCAN_TYPE_FRAGMENTED; /* - * in case of DCM with GO where BSS DTIM interval < 220msec - * set all scan requests as fast-balance scan + * in case of DCM with P2P GO set all scan requests as + * fast-balance scan */ if (vif && vif->type == NL80211_IFTYPE_STATION && - data.is_dcm_with_p2p_go && - ((vif->bss_conf.beacon_int * - vif->bss_conf.dtim_period) < 220)) + data.is_dcm_with_p2p_go) return IWL_SCAN_TYPE_FAST_BALANCE; } From 4dde4ff0eadd6cde43aa5f39fbea36355f9f6e44 Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Thu, 1 Feb 2024 16:17:29 +0200 Subject: [PATCH 185/378] wifi: iwlwifi: support link command version 2 In version 2, listen_lmac becomes reserved. Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://msgid.link/20240201155157.df1890aba2fd.Icad9ba10f8bab770adc6a559b2c7bff5cccbffe9@changeid Signed-off-by: Johannes Berg --- .../wireless/intel/iwlwifi/fw/api/mac-cfg.h | 18 ++++++++++++------ drivers/net/wireless/intel/iwlwifi/mvm/link.c | 10 ++++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index 362161369884..200362e5ceca 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -446,6 +446,7 @@ enum iwl_link_ctx_flags { * @listen_lmac: indicates whether the link should be allocated on the Listen * Lmac or on the Main Lmac. Cannot be changed on an active Link. * Relevant only for eSR. + * @reserved1: in version 2, listen_lmac became reserved * @cck_rates: basic rates available for CCK * @ofdm_rates: basic rates available for OFDM * @cck_short_preamble: 1 for enabling short preamble, 0 otherwise @@ -471,10 +472,10 @@ enum iwl_link_ctx_flags { * @bssid_index: index of the associated VAP * @bss_color: 11ax AP ID that is used in the HE SIG-A to mark inter BSS frame * @spec_link_id: link_id as the AP knows it - * @reserved: alignment + * @reserved2: alignment * @ibss_bssid_addr: bssid for ibss * @reserved_for_ibss_bssid_addr: reserved - * @reserved1: reserved for future use + * @reserved3: reserved for future use */ struct iwl_link_config_cmd { __le32 action; @@ -485,7 +486,10 @@ struct iwl_link_config_cmd { __le16 reserved_for_local_link_addr; __le32 modify_mask; __le32 active; - __le32 listen_lmac; + union { + __le32 listen_lmac; + __le32 reserved1; + }; __le32 cck_rates; __le32 ofdm_rates; __le32 cck_short_preamble; @@ -511,11 +515,13 @@ struct iwl_link_config_cmd { u8 bssid_index; u8 bss_color; u8 spec_link_id; - u8 reserved; + u8 reserved2; u8 ibss_bssid_addr[6]; __le16 reserved_for_ibss_bssid_addr; - __le32 reserved1[8]; -} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1 */ + __le32 reserved3[8]; +} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1 and + * LINK_CONTEXT_CONFIG_CMD_API_S_VER_2 + */ /* Currently FW supports link ids in the range 0-3 and can have * at most two active links for each vif. diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/link.c b/drivers/net/wireless/intel/iwlwifi/mvm/link.c index be48b0fc9cb6..f3fcef9034ef 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/link.c @@ -53,6 +53,8 @@ int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, unsigned int link_id = link_conf->link_id; struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; struct iwl_link_config_cmd cmd = {}; + unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD); + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1); if (WARN_ON_ONCE(!link_info)) return -EINVAL; @@ -84,7 +86,8 @@ int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid) memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN); - cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac); + if (cmd_ver < 2) + cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac); return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD); } @@ -100,6 +103,8 @@ int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_link_config_cmd cmd = {}; u32 ht_flag, flags = 0, flags_mask = 0; int ret; + unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD); + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1); if (WARN_ON_ONCE(!link_info || link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID)) @@ -224,7 +229,8 @@ int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cmd.flags = cpu_to_le32(flags); cmd.flags_mask = cpu_to_le32(flags_mask); cmd.spec_link_id = link_conf->link_id; - cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac); + if (cmd_ver < 2) + cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac); ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY); if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE)) From 669761e897a4fc87ae0e4625590f2a396a87a3d1 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:31 +0200 Subject: [PATCH 186/378] wifi: iwlwifi: read WRDD table from UEFI Try to read the WRDD table from UEFI first, and if the WIFI UEFI tables are unlocked or the table doesn't exist - try to read it from ACPI. Change iwl_acpi_get_mcc() to receive fwrt as argument so it will be the same as all iwl_acpi_get_x() functions, so it could be generated by the macro. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.5d52eeb109f7.I4d81700a7ae7fe2dfee14e363de358be59de7823@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 8 ++--- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 6 ++-- .../wireless/intel/iwlwifi/fw/regulatory.c | 1 + .../wireless/intel/iwlwifi/fw/regulatory.h | 2 ++ drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 31 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 19 ++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 2 +- 7 files changed, 61 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index e74745f939ae..ad04d0ebf081 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -326,17 +326,18 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, return ret; } -int iwl_acpi_get_mcc(struct device *dev, char *mcc) +int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) { union acpi_object *wifi_pkg, *data; u32 mcc_val; int ret, tbl_rev; - data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD); + data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDD_METHOD); if (IS_ERR(data)) return PTR_ERR(data); - wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE, + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_WRDD_WIFI_DATA_SIZE, &tbl_rev); if (IS_ERR(wifi_pkg)) { ret = PTR_ERR(wifi_pkg); @@ -360,7 +361,6 @@ int iwl_acpi_get_mcc(struct device *dev, char *mcc) kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc); int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index f0ed7174a951..1cb9271158e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -149,12 +149,12 @@ int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, /** * iwl_acpi_get_mcc - read MCC from ACPI, if available * - * @dev: the struct device + * @fwrt: the fw runtime struct * @mcc: output buffer (3 bytes) that will get the MCC * * This function tries to read the current MCC from ACPI if available. */ -int iwl_acpi_get_mcc(struct device *dev, char *mcc); +int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit); @@ -207,7 +207,7 @@ static inline int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, return -ENOENT; } -static inline int iwl_acpi_get_mcc(struct device *dev, char *mcc) +static inline int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) { return -ENOENT; } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 452c7cc49c27..65022b1c1511 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -36,6 +36,7 @@ IWL_BIOS_TABLE_LOADER(wgds_table); IWL_BIOS_TABLE_LOADER(ppag_table); IWL_BIOS_TABLE_LOADER_DATA(tas_table, struct iwl_tas_data); IWL_BIOS_TABLE_LOADER_DATA(pwr_limit, u64); +IWL_BIOS_TABLE_LOADER_DATA(mcc, char); static const struct dmi_system_id dmi_ppag_approved_list[] = { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index b391c6fc3bcc..f75ca5f7faaf 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -139,4 +139,6 @@ int iwl_bios_get_tas_table(struct iwl_fw_runtime *fwrt, int iwl_bios_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit); + +int iwl_bios_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 5ec82205be12..cd897ad504d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -621,3 +621,34 @@ int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, kfree(data); return ret; } + +int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) +{ + struct uefi_cnv_var_wrdd *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDD_NAME, + "WRDD", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_WRDD_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n", + data->revision); + goto out; + } + + if (data->mcc != UEFI_MCC_CHINA) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "UEFI WRDD is supported only for CN\n"); + goto out; + } + + mcc[0] = (data->mcc >> 8) & 0xff; + mcc[1] = data->mcc & 0xff; + mcc[2] = '\0'; +out: + kfree(data); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 4cf3af576920..62bbd5c992b9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -18,6 +18,7 @@ #define IWL_UEFI_PPAG_NAME L"UefiCnvWlanPPAG" #define IWL_UEFI_WTAS_NAME L"UefiCnvWlanWTAS" #define IWL_UEFI_SPLC_NAME L"UefiCnvWlanSPLC" +#define IWL_UEFI_WRDD_NAME L"UefiCnvWlanWRDD" #define IWL_SGOM_MAP_SIZE 339 @@ -30,6 +31,7 @@ #define IWL_UEFI_MAX_PPAG_REV 3 #define IWL_UEFI_WTAS_REVISION 1 #define IWL_UEFI_SPLC_REVISION 0 +#define IWL_UEFI_WRDD_REVISION 0 struct pnvm_sku_package { u8 rev; @@ -142,6 +144,17 @@ struct uefi_cnv_var_splc { u32 default_pwr_limit; } __packed; +#define UEFI_MCC_CHINA 0x434e + +/* struct uefi_cnv_var_wrdd - WRDD table as defined in UEFI + * @revision: the revision of the table + * @mcc: country identifier as defined in ISO/IEC 3166-1 Alpha 2 code + */ +struct uefi_cnv_var_wrdd { + u8 revision; + u32 mcc; +} __packed; + /* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply @@ -164,6 +177,7 @@ int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, struct iwl_tas_data *data); int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit); +int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -227,6 +241,11 @@ static inline int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, *dflt_pwr_limit = 0; return 0; } + +static inline int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) +{ + return -ENOENT; +} #endif /* CONFIG_EFI */ #if defined(CONFIG_EFI) && defined(CONFIG_ACPI) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index c0dd441e800e..ae8177222881 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -590,7 +590,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm) return -EIO; if (iwl_mvm_is_wifi_mcc_supported(mvm) && - !iwl_acpi_get_mcc(mvm->dev, mcc)) { + !iwl_bios_get_mcc(&mvm->fwrt, mcc)) { kfree(regd); regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, MCC_SOURCE_BIOS, NULL); From 20935f3e646e687f32f044d9d75a4a8637c086db Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:32 +0200 Subject: [PATCH 187/378] wifi: iwlwifi: read ECKV table from UEFI Try to read the ECKV table from UEFI first, and if the WIFI UEFI tables are unlocked or the table doesn't exist - try to read it from ACPI. Change iwl_acpi_get_eckv() to receive fwrt as argument so it will be the same as all iwl_acpi_get_x() functions, so it could be generated by the macro. While at it - move the reading of ECKV to INIT stage. There is no reason to read it each time we load the FW. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.d4937cc00727.I36e5fc7f7850229b9b377c80b5203aa47137c97c@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 8 +++---- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 6 ++--- .../wireless/intel/iwlwifi/fw/regulatory.c | 1 + .../wireless/intel/iwlwifi/fw/regulatory.h | 1 + drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 22 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 17 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 6 ++--- 7 files changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index ad04d0ebf081..7b422ebe2241 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -386,16 +386,17 @@ int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit) return ret; } -int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) +int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) { union acpi_object *wifi_pkg, *data; int ret, tbl_rev; - data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD); + data = iwl_acpi_get_object(fwrt->dev, ACPI_ECKV_METHOD); if (IS_ERR(data)) return PTR_ERR(data); - wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE, + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_ECKV_WIFI_DATA_SIZE, &tbl_rev); if (IS_ERR(wifi_pkg)) { ret = PTR_ERR(wifi_pkg); @@ -416,7 +417,6 @@ int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv); static int iwl_acpi_sar_set_profile(union acpi_object *table, struct iwl_sar_profile *profile, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 1cb9271158e7..ac6655c1f777 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -161,13 +161,13 @@ int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit); /* * iwl_acpi_get_eckv - read external clock validation from ACPI, if available * - * @dev: the struct device + * @fwrt: the fw runtime struct * @extl_clk: output var (2 bytes) that will get the clk indication. * * This function tries to read the external clock indication * from ACPI if available. */ -int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk); +int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk); int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt); @@ -219,7 +219,7 @@ static inline int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, return 0; } -static inline int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) +static inline int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) { return -ENOENT; } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 65022b1c1511..bb07fbfd81eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -37,6 +37,7 @@ IWL_BIOS_TABLE_LOADER(ppag_table); IWL_BIOS_TABLE_LOADER_DATA(tas_table, struct iwl_tas_data); IWL_BIOS_TABLE_LOADER_DATA(pwr_limit, u64); IWL_BIOS_TABLE_LOADER_DATA(mcc, char); +IWL_BIOS_TABLE_LOADER_DATA(eckv, u32); static const struct dmi_system_id dmi_ppag_approved_list[] = { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index f75ca5f7faaf..ec408c06235d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -141,4 +141,5 @@ int iwl_bios_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit); int iwl_bios_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); +int iwl_bios_get_eckv(struct iwl_fw_runtime *fwrt, u32 *ext_clk); #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index cd897ad504d6..4454fae84d1f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -652,3 +652,25 @@ int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) kfree(data); return ret; } + +int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) +{ + struct uefi_cnv_var_eckv *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_ECKV_NAME, + "ECKV", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_ECKV_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n", + data->revision); + goto out; + } + *extl_clk = data->ext_clock_valid; +out: + kfree(data); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 62bbd5c992b9..723933b0b2f1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -19,6 +19,7 @@ #define IWL_UEFI_WTAS_NAME L"UefiCnvWlanWTAS" #define IWL_UEFI_SPLC_NAME L"UefiCnvWlanSPLC" #define IWL_UEFI_WRDD_NAME L"UefiCnvWlanWRDD" +#define IWL_UEFI_ECKV_NAME L"UefiCnvWlanECKV" #define IWL_SGOM_MAP_SIZE 339 @@ -32,6 +33,7 @@ #define IWL_UEFI_WTAS_REVISION 1 #define IWL_UEFI_SPLC_REVISION 0 #define IWL_UEFI_WRDD_REVISION 0 +#define IWL_UEFI_ECKV_REVISION 0 struct pnvm_sku_package { u8 rev; @@ -155,6 +157,15 @@ struct uefi_cnv_var_wrdd { u32 mcc; } __packed; +/* struct uefi_cnv_var_eckv - ECKV table as defined in UEFI + * @revision: the revision of the table + * @ext_clock_valid: indicates if external 32KHz clock is valid + */ +struct uefi_cnv_var_eckv { + u8 revision; + u32 ext_clock_valid; +} __packed; + /* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply @@ -178,6 +189,7 @@ int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit); int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); +int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -246,6 +258,11 @@ static inline int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) { return -ENOENT; } + +static inline int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) +{ + return -ENOENT; +} #endif /* CONFIG_EFI */ #if defined(CONFIG_EFI) && defined(CONFIG_ACPI) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 72f8a6cf20c7..fea2e8a5102d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1409,6 +1409,9 @@ void iwl_mvm_get_bios_tables(struct iwl_mvm *mvm) } iwl_acpi_get_phy_filters(&mvm->fwrt, &mvm->phy_filters); + + if (iwl_bios_get_eckv(&mvm->fwrt, &mvm->ext_clock_valid)) + IWL_DEBUG_RADIO(mvm, "ECKV table doesn't exist in BIOS\n"); } static void iwl_mvm_disconnect_iterator(void *data, u8 *mac, @@ -1705,9 +1708,6 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (!mvm->ptp_data.ptp_clock) iwl_mvm_ptp_init(mvm); - if (iwl_acpi_get_eckv(mvm->dev, &mvm->ext_clock_valid)) - IWL_DEBUG_INFO(mvm, "ECKV table doesn't exist in BIOS\n"); - ret = iwl_mvm_ppag_init(mvm); if (ret) goto error; From dc2b94a111e0fb3779a86dd8d303ad842880f869 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:33 +0200 Subject: [PATCH 188/378] wifi: iwlwifi: rfi: use a single DSM function for all RFI configurations RFI configuration moved from internal guid to the wifi guid, DSM function 11. Update reading RFI configuration from BIOS. Signed-off-by: Anjaneyulu Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.f4e62435310d.I4f9b6860dd8e3c7ae1f816be5ff8b5967eee266f@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 5 --- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 14 +++--- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 45 ++++++++++++-------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 7b422ebe2241..6f9ead79978a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -14,11 +14,6 @@ const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6, 0x8E, 0x28, 0x5A, 0xDE); IWL_EXPORT_SYMBOL(iwl_guid); -const guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29, - 0x81, 0x4F, 0x75, 0xE4, - 0xDD, 0x26, 0xB5, 0xFD); -IWL_EXPORT_SYMBOL(iwl_rfi_guid); - static int iwl_acpi_get_handle(struct device *dev, acpi_string method, acpi_handle *ret_handle) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index ac6655c1f777..e6d68ab83ba9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -103,6 +103,7 @@ enum iwl_dsm_funcs_rev_0 { DSM_FUNC_ACTIVATE_CHANNEL = 8, DSM_FUNC_FORCE_DISABLE_CHANNELS = 9, DSM_FUNC_ENERGY_DETECTION_THRESHOLD = 10, + DSM_FUNC_RFI_CONFIG = 11 }; enum iwl_dsm_values_srd { @@ -119,16 +120,14 @@ enum iwl_dsm_values_indonesia { DSM_VALUE_INDONESIA_MAX }; -/* DSM RFI uses a different GUID, so need separate definitions */ - -#define DSM_RFI_FUNC_ENABLE 3 - enum iwl_dsm_values_rfi { - DSM_VALUE_RFI_ENABLE, - DSM_VALUE_RFI_DISABLE, - DSM_VALUE_RFI_MAX + DSM_VALUE_RFI_DLVR_DISABLE = BIT(0), + DSM_VALUE_RFI_DDR_DISABLE = BIT(1), }; +#define DSM_VALUE_RFI_DISABLE (DSM_VALUE_RFI_DLVR_DISABLE |\ + DSM_VALUE_RFI_DDR_DISABLE) + enum iwl_dsm_masks_reg { DSM_MASK_CHINA_22_REG = BIT(2) }; @@ -138,7 +137,6 @@ enum iwl_dsm_masks_reg { struct iwl_fw_runtime; extern const guid_t iwl_guid; -extern const guid_t iwl_rfi_guid; int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, const guid_t *guid, u8 *value); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index fea2e8a5102d..e9b5dc7ee8c7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1203,28 +1203,37 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) #ifdef CONFIG_ACPI -static u8 iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) +static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) { - u8 value; - int ret = iwl_acpi_get_dsm_u8(mvm->fwrt.dev, 0, DSM_RFI_FUNC_ENABLE, - &iwl_rfi_guid, &value); + u8 value = 0; + /* default behaviour is disabled */ + bool bios_enable_rfi = false; + int ret = iwl_acpi_get_dsm_u8(mvm->fwrt.dev, 0, + DSM_FUNC_RFI_CONFIG, &iwl_guid, + &value); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "Failed to get DSM RFI, ret=%d\n", ret); - - } else if (value >= DSM_VALUE_RFI_MAX) { - IWL_DEBUG_RADIO(mvm, "DSM RFI got invalid value, ret=%d\n", - value); - - } else if (value == DSM_VALUE_RFI_ENABLE) { - IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to enable\n"); - return DSM_VALUE_RFI_ENABLE; + return bios_enable_rfi; } - IWL_DEBUG_RADIO(mvm, "DSM RFI is disabled\n"); + value &= DSM_VALUE_RFI_DISABLE; + /* RFI BIOS CONFIG value can be 0 or 3 only. + * i.e 0 means DDR and DLVR enabled. 3 means DDR and DLVR disabled. + * 1 and 2 are invalid BIOS configurations, So, it's not possible to + * disable ddr/dlvr separately. + */ + if (!value) { + IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to enable\n"); + bios_enable_rfi = true; + } else if (value == DSM_VALUE_RFI_DISABLE) { + IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to disable\n"); + } else { + IWL_DEBUG_RADIO(mvm, + "DSM RFI got invalid value, value=%d\n", value); + } - /* default behaviour is disabled */ - return DSM_VALUE_RFI_DISABLE; + return bios_enable_rfi; } static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) @@ -1347,9 +1356,9 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) { } -static u8 iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) +static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) { - return DSM_VALUE_RFI_DISABLE; + return false; } #endif /* CONFIG_ACPI */ @@ -1727,7 +1736,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) iwl_mvm_uats_init(mvm); if (iwl_rfi_supported(mvm)) { - if (iwl_mvm_eval_dsm_rfi(mvm) == DSM_VALUE_RFI_ENABLE) + if (iwl_mvm_eval_dsm_rfi(mvm)) iwl_rfi_send_config_cmd(mvm, NULL); } From b97ada404c4eecb90c79fd884cdc09022d549d20 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:34 +0200 Subject: [PATCH 189/378] wifi: iwlwifi: take send-DSM-to-FW flows out of ACPI ifdef These functions shouldn't be ACPI_CONFIG dependent, as they don't access the ACPI. The functions that really access ACPI - already handle the case that CONFIG_ACPI is not set. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.1412e6d561f8.I84f67478d01b576457e1bf489fbcb044adfda6fe@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 5 ----- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 15 --------------- 2 files changed, 20 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index e6d68ab83ba9..8b64888052ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -243,11 +243,6 @@ static inline int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, return -ENOENT; } -static inline __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) -{ - return 0; -} - static inline int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) { return -ENOENT; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index e9b5dc7ee8c7..77464620eafc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1201,8 +1201,6 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret); } -#ifdef CONFIG_ACPI - static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) { u8 value = 0; @@ -1350,19 +1348,6 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) mvm->fwrt.uats_enabled = TRUE; } -#else /* CONFIG_ACPI */ - -static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) -{ -} - -static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) -{ - return false; -} - -#endif /* CONFIG_ACPI */ - void iwl_mvm_get_bios_tables(struct iwl_mvm *mvm) { int ret; From 091d89428f18ac8e67b4032dfa305e957040bdd9 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:35 +0200 Subject: [PATCH 190/378] wifi: iwlwifi: simplify getting DSM from ACPI As DSMs are going to be read from UEFI too, we need a unified API to get DSMs for both ACPI and UEFI. The difference in getting DSM in each one of these methods (ACPI, UEFI) is in the GUID, revision (0 for ACPI, 4 for UEFI), and size of the DSM values (8 or 32 for ACPI, 32 for UEFI). Therefore, change the iwl_acpi_get_dsm_x() to iwl_acpi_get_dsm() which determines the GUID, revision (these two are the same for all WiFi DSMs), and size (based on a func-to-size mapping) internally. While at it, fix DSM_FUNC_RFI_CONFIG to expect a 32-bit value (as defined in Intel BIOS spec) and not a 8-bit one. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.1bcd7072a7a5.I344ee0a11abbc27da0c693187d1b8bee653aaeef@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 95 ++++++++++--------- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 33 +++---- .../net/wireless/intel/iwlwifi/mvm/debugfs.c | 4 +- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 32 +++---- 4 files changed, 76 insertions(+), 88 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 6f9ead79978a..22b21bbc294f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -12,7 +12,22 @@ const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6, 0xA5, 0xB3, 0x1F, 0x73, 0x8E, 0x28, 0x5A, 0xDE); -IWL_EXPORT_SYMBOL(iwl_guid); + +static const size_t acpi_dsm_size[DSM_FUNC_NUM_FUNCS] = { + [DSM_FUNC_QUERY] = sizeof(u32), + [DSM_FUNC_DISABLE_SRD] = sizeof(u8), + [DSM_FUNC_ENABLE_INDONESIA_5G2] = sizeof(u8), + [DSM_FUNC_ENABLE_6E] = sizeof(u32), + [DSM_FUNC_REGULATORY_CONFIG] = sizeof(u32), + /* Not supported in driver */ + [5] = (size_t)0, + [DSM_FUNC_11AX_ENABLEMENT] = sizeof(u32), + [DSM_FUNC_ENABLE_UNII4_CHAN] = sizeof(u32), + [DSM_FUNC_ACTIVATE_CHANNEL] = sizeof(u32), + [DSM_FUNC_FORCE_DISABLE_CHANNELS] = sizeof(u32), + [DSM_FUNC_ENERGY_DETECTION_THRESHOLD] = sizeof(u32), + [DSM_FUNC_RFI_CONFIG] = sizeof(u32), +}; static int iwl_acpi_get_handle(struct device *dev, acpi_string method, acpi_handle *ret_handle) @@ -137,46 +152,42 @@ static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, } /* - * Evaluate a DSM with no arguments and a u8 return value, + * This function receives a DSM function number, calculates its expected size + * according to Intel BIOS spec, and fills in the value in a 32-bit field. + * In case the expected size is smaller than 32-bit, padding will be added. */ -int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, - const guid_t *guid, u8 *value) +int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, + enum iwl_dsm_funcs_rev_0 func, u32 *value) { + size_t expected_size; + u64 tmp; int ret; - u64 val; - ret = iwl_acpi_get_dsm_integer(dev, rev, func, - guid, &val, sizeof(u8)); + BUILD_BUG_ON(ARRAY_SIZE(acpi_dsm_size) != DSM_FUNC_NUM_FUNCS); - if (ret < 0) + if (WARN_ON(func >= ARRAY_SIZE(acpi_dsm_size))) + return -EINVAL; + + expected_size = acpi_dsm_size[func]; + + /* Currently all ACPI DSMs are either 8-bit or 32-bit */ + if (expected_size != sizeof(u8) && expected_size != sizeof(u32)) + return -EOPNOTSUPP; + + ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV, func, + &iwl_guid, &tmp, expected_size); + if (ret) return ret; - /* cast val (u64) to be u8 */ - *value = (u8)val; + if ((expected_size == sizeof(u8) && tmp != (u8)tmp) || + (expected_size == sizeof(u32) && tmp != (u32)tmp)) + IWL_DEBUG_RADIO(fwrt, + "DSM value overflows the expected size, truncating\n"); + *value = (u32)tmp; + return 0; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8); - -/* - * Evaluate a DSM with no arguments and a u32 return value, - */ -int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, - const guid_t *guid, u32 *value) -{ - int ret; - u64 val; - - ret = iwl_acpi_get_dsm_integer(dev, rev, func, - guid, &val, sizeof(u32)); - - if (ret < 0) - return ret; - - /* cast val (u64) to be u32 */ - *value = (u32)val; - return 0; -} -IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u32); +IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm); static union acpi_object * iwl_acpi_get_wifi_pkg_range(struct device *dev, @@ -800,7 +811,6 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) { int ret; - u8 value; u32 val; __le32 config_bitmap = 0; @@ -813,11 +823,10 @@ __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) case IWL_CFG_RF_TYPE_HR2: case IWL_CFG_RF_TYPE_JF1: case IWL_CFG_RF_TYPE_JF2: - ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0, - DSM_FUNC_ENABLE_INDONESIA_5G2, - &iwl_guid, &value); + ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_ENABLE_INDONESIA_5G2, + &val); - if (!ret && value == DSM_VALUE_INDONESIA_ENABLE) + if (!ret && val == DSM_VALUE_INDONESIA_ENABLE) config_bitmap |= cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK); break; @@ -828,14 +837,12 @@ __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) /* ** Evaluate func 'DSM_FUNC_DISABLE_SRD' */ - ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0, - DSM_FUNC_DISABLE_SRD, - &iwl_guid, &value); + ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_DISABLE_SRD, &val); if (!ret) { - if (value == DSM_VALUE_SRD_PASSIVE) + if (val == DSM_VALUE_SRD_PASSIVE) config_bitmap |= cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK); - else if (value == DSM_VALUE_SRD_DISABLE) + else if (val == DSM_VALUE_SRD_DISABLE) config_bitmap |= cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK); } @@ -845,9 +852,7 @@ __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) /* ** Evaluate func 'DSM_FUNC_REGULATORY_CONFIG' */ - ret = iwl_acpi_get_dsm_u32(fwrt->dev, 0, - DSM_FUNC_REGULATORY_CONFIG, - &iwl_guid, &val); + ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_REGULATORY_CONFIG, &val); /* * China 2022 enable if the BIOS object does not exist or * if it is enabled in BIOS. diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 8b64888052ad..d84952f90444 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -92,6 +92,8 @@ /* The Inidcator whether UEFI WIFI GUID tables are locked is read from ACPI */ #define UEFI_WIFI_GUID_UNLOCKED 0 +#define ACPI_DSM_REV 0 + enum iwl_dsm_funcs_rev_0 { DSM_FUNC_QUERY = 0, DSM_FUNC_DISABLE_SRD = 1, @@ -103,7 +105,8 @@ enum iwl_dsm_funcs_rev_0 { DSM_FUNC_ACTIVATE_CHANNEL = 8, DSM_FUNC_FORCE_DISABLE_CHANNELS = 9, DSM_FUNC_ENERGY_DETECTION_THRESHOLD = 10, - DSM_FUNC_RFI_CONFIG = 11 + DSM_FUNC_RFI_CONFIG = 11, + DSM_FUNC_NUM_FUNCS = 12, }; enum iwl_dsm_values_srd { @@ -138,12 +141,6 @@ struct iwl_fw_runtime; extern const guid_t iwl_guid; -int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, - const guid_t *guid, u8 *value); - -int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, - const guid_t *guid, u32 *value); - /** * iwl_acpi_get_mcc - read MCC from ACPI, if available * @@ -185,6 +182,9 @@ void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt); +int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, + enum iwl_dsm_funcs_rev_0 func, u32 *value); + #else /* CONFIG_ACPI */ static inline void *iwl_acpi_get_dsm_object(struct device *dev, int rev, @@ -193,18 +193,6 @@ static inline void *iwl_acpi_get_dsm_object(struct device *dev, int rev, return ERR_PTR(-ENOENT); } -static inline int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, - const guid_t *guid, u8 *value) -{ - return -ENOENT; -} - -static inline int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, - const guid_t *guid, u32 *value) -{ - return -ENOENT; -} - static inline int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) { return -ENOENT; @@ -256,6 +244,13 @@ static inline void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, static inline void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) { } + +static inline int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, + enum iwl_dsm_funcs_rev_0 func, + u32 *value) +{ + return -ENOENT; +} #endif /* CONFIG_ACPI */ #endif /* __iwl_fw_acpi__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index d67986e157a2..6f33f791648e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -391,9 +391,7 @@ static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct file *file, char buf[12]; u32 value; - err = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, - DSM_FUNC_ENABLE_6E, - &iwl_guid, &value); + err = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_6E, &value); if (err) return err; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 77464620eafc..f8d7f23741bf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1203,12 +1203,11 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) { - u8 value = 0; + u32 value = 0; /* default behaviour is disabled */ bool bios_enable_rfi = false; - int ret = iwl_acpi_get_dsm_u8(mvm->fwrt.dev, 0, - DSM_FUNC_RFI_CONFIG, &iwl_guid, - &value); + int ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_RFI_CONFIG, &value); + if (ret < 0) { IWL_DEBUG_RADIO(mvm, "Failed to get DSM RFI, ret=%d\n", ret); @@ -1245,41 +1244,32 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) cmd.config_bitmap = iwl_acpi_get_lari_config_bitmap(&mvm->fwrt); - ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, DSM_FUNC_11AX_ENABLEMENT, - &iwl_guid, &value); + ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_11AX_ENABLEMENT, &value); if (!ret) cmd.oem_11ax_allow_bitmap = cpu_to_le32(value); - ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, - DSM_FUNC_ENABLE_UNII4_CHAN, - &iwl_guid, &value); + ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value); if (!ret) cmd.oem_unii4_allow_bitmap = cpu_to_le32(value); - ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, - DSM_FUNC_ACTIVATE_CHANNEL, - &iwl_guid, &value); + ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value); if (!ret) { if (cmd_ver < 8) value &= ~ACTIVATE_5G2_IN_WW_MASK; cmd.chan_state_active_bitmap = cpu_to_le32(value); } - ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, - DSM_FUNC_ENABLE_6E, - &iwl_guid, &value); + ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_6E, &value); if (!ret) cmd.oem_uhb_allow_bitmap = cpu_to_le32(value); - ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, - DSM_FUNC_FORCE_DISABLE_CHANNELS, - &iwl_guid, &value); + ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, + &value); if (!ret) cmd.force_disable_channels_bitmap = cpu_to_le32(value); - ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, - DSM_FUNC_ENERGY_DETECTION_THRESHOLD, - &iwl_guid, &value); + ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD, + &value); if (!ret) cmd.edt_bitmap = cpu_to_le32(value); From dc4fe7500e7a1a1ab56a7708ac9be4c90fd12174 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:36 +0200 Subject: [PATCH 191/378] wifi: iwlwifi: prepare for reading DSM from UEFI Move all the common items (functions, enumerations and mcaros) to regulatory.h/c files, and rename it to a common name. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.eae9bcbc0023.If1175f3143d6369076669ddd5d6ad4df0ee00659@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 60 +------------------ drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 48 +-------------- .../wireless/intel/iwlwifi/fw/regulatory.c | 49 +++++++++++++++ .../wireless/intel/iwlwifi/fw/regulatory.h | 44 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- 5 files changed, 97 insertions(+), 106 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 22b21bbc294f..357047223686 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -157,7 +157,7 @@ static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, * In case the expected size is smaller than 32-bit, padding will be added. */ int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, - enum iwl_dsm_funcs_rev_0 func, u32 *value) + enum iwl_dsm_funcs func, u32 *value) { size_t expected_size; u64 tmp; @@ -808,64 +808,6 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) return ret; } -__le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) -{ - int ret; - u32 val; - __le32 config_bitmap = 0; - - /* - * Evaluate func 'DSM_FUNC_ENABLE_INDONESIA_5G2'. - * Setting config_bitmap Indonesia bit is valid only for HR/JF. - */ - switch (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)) { - case IWL_CFG_RF_TYPE_HR1: - case IWL_CFG_RF_TYPE_HR2: - case IWL_CFG_RF_TYPE_JF1: - case IWL_CFG_RF_TYPE_JF2: - ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_ENABLE_INDONESIA_5G2, - &val); - - if (!ret && val == DSM_VALUE_INDONESIA_ENABLE) - config_bitmap |= - cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK); - break; - default: - break; - } - - /* - ** Evaluate func 'DSM_FUNC_DISABLE_SRD' - */ - ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_DISABLE_SRD, &val); - if (!ret) { - if (val == DSM_VALUE_SRD_PASSIVE) - config_bitmap |= - cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK); - else if (val == DSM_VALUE_SRD_DISABLE) - config_bitmap |= - cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK); - } - - if (fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_CHINA_22_REG_SUPPORT)) { - /* - ** Evaluate func 'DSM_FUNC_REGULATORY_CONFIG' - */ - ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_REGULATORY_CONFIG, &val); - /* - * China 2022 enable if the BIOS object does not exist or - * if it is enabled in BIOS. - */ - if (ret < 0 || val & DSM_MASK_CHINA_22_REG) - config_bitmap |= - cpu_to_le32(LARI_CONFIG_ENABLE_CHINA_22_REG_SUPPORT_MSK); - } - - return config_bitmap; -} -IWL_EXPORT_SYMBOL(iwl_acpi_get_lari_config_bitmap); - int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) { union acpi_object *wifi_pkg, *data, *flags; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index d84952f90444..9cb101776884 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -94,47 +94,6 @@ #define ACPI_DSM_REV 0 -enum iwl_dsm_funcs_rev_0 { - DSM_FUNC_QUERY = 0, - DSM_FUNC_DISABLE_SRD = 1, - DSM_FUNC_ENABLE_INDONESIA_5G2 = 2, - DSM_FUNC_ENABLE_6E = 3, - DSM_FUNC_REGULATORY_CONFIG = 4, - DSM_FUNC_11AX_ENABLEMENT = 6, - DSM_FUNC_ENABLE_UNII4_CHAN = 7, - DSM_FUNC_ACTIVATE_CHANNEL = 8, - DSM_FUNC_FORCE_DISABLE_CHANNELS = 9, - DSM_FUNC_ENERGY_DETECTION_THRESHOLD = 10, - DSM_FUNC_RFI_CONFIG = 11, - DSM_FUNC_NUM_FUNCS = 12, -}; - -enum iwl_dsm_values_srd { - DSM_VALUE_SRD_ACTIVE, - DSM_VALUE_SRD_PASSIVE, - DSM_VALUE_SRD_DISABLE, - DSM_VALUE_SRD_MAX -}; - -enum iwl_dsm_values_indonesia { - DSM_VALUE_INDONESIA_DISABLE, - DSM_VALUE_INDONESIA_ENABLE, - DSM_VALUE_INDONESIA_RESERVED, - DSM_VALUE_INDONESIA_MAX -}; - -enum iwl_dsm_values_rfi { - DSM_VALUE_RFI_DLVR_DISABLE = BIT(0), - DSM_VALUE_RFI_DDR_DISABLE = BIT(1), -}; - -#define DSM_VALUE_RFI_DISABLE (DSM_VALUE_RFI_DLVR_DISABLE |\ - DSM_VALUE_RFI_DDR_DISABLE) - -enum iwl_dsm_masks_reg { - DSM_MASK_CHINA_22_REG = BIT(2) -}; - #ifdef CONFIG_ACPI struct iwl_fw_runtime; @@ -173,8 +132,6 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt); int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, struct iwl_tas_data *data); -__le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); - int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt); void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, @@ -183,7 +140,7 @@ void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt); int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, - enum iwl_dsm_funcs_rev_0 func, u32 *value); + enum iwl_dsm_funcs func, u32 *value); #else /* CONFIG_ACPI */ @@ -246,8 +203,7 @@ static inline void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) } static inline int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, - enum iwl_dsm_funcs_rev_0 func, - u32 *value) + enum iwl_dsm_funcs func, u32 *value) { return -ENOENT; } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index bb07fbfd81eb..3260f21fd2e0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -431,3 +431,52 @@ int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, return enabled; } + +__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) +{ + int ret; + u32 val; + __le32 config_bitmap = 0; + + switch (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)) { + case IWL_CFG_RF_TYPE_HR1: + case IWL_CFG_RF_TYPE_HR2: + case IWL_CFG_RF_TYPE_JF1: + case IWL_CFG_RF_TYPE_JF2: + ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_ENABLE_INDONESIA_5G2, + &val); + + if (!ret && val == DSM_VALUE_INDONESIA_ENABLE) + config_bitmap |= + cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK); + break; + default: + break; + } + + ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_DISABLE_SRD, &val); + if (!ret) { + if (val == DSM_VALUE_SRD_PASSIVE) + config_bitmap |= + cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK); + else if (val == DSM_VALUE_SRD_DISABLE) + config_bitmap |= + cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK); + } + + if (fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CHINA_22_REG_SUPPORT)) { + ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_REGULATORY_CONFIG, + &val); + /* + * China 2022 enable if the BIOS object does not exist or + * if it is enabled in BIOS. + */ + if (ret < 0 || val & DSM_MASK_CHINA_22_REG) + config_bitmap |= + cpu_to_le32(LARI_CONFIG_ENABLE_CHINA_22_REG_SUPPORT_MSK); + } + + return config_bitmap; +} +IWL_EXPORT_SYMBOL(iwl_get_lari_config_bitmap); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index ec408c06235d..da49ed7325d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -102,6 +102,48 @@ struct iwl_tas_data { u8 usa_tas_uhb_allowed; }; +/* For DSM revision 0 and 4 */ +enum iwl_dsm_funcs { + DSM_FUNC_QUERY = 0, + DSM_FUNC_DISABLE_SRD = 1, + DSM_FUNC_ENABLE_INDONESIA_5G2 = 2, + DSM_FUNC_ENABLE_6E = 3, + DSM_FUNC_REGULATORY_CONFIG = 4, + DSM_FUNC_11AX_ENABLEMENT = 6, + DSM_FUNC_ENABLE_UNII4_CHAN = 7, + DSM_FUNC_ACTIVATE_CHANNEL = 8, + DSM_FUNC_FORCE_DISABLE_CHANNELS = 9, + DSM_FUNC_ENERGY_DETECTION_THRESHOLD = 10, + DSM_FUNC_RFI_CONFIG = 11, + DSM_FUNC_NUM_FUNCS = 12, +}; + +enum iwl_dsm_values_srd { + DSM_VALUE_SRD_ACTIVE, + DSM_VALUE_SRD_PASSIVE, + DSM_VALUE_SRD_DISABLE, + DSM_VALUE_SRD_MAX +}; + +enum iwl_dsm_values_indonesia { + DSM_VALUE_INDONESIA_DISABLE, + DSM_VALUE_INDONESIA_ENABLE, + DSM_VALUE_INDONESIA_RESERVED, + DSM_VALUE_INDONESIA_MAX +}; + +enum iwl_dsm_values_rfi { + DSM_VALUE_RFI_DLVR_DISABLE = BIT(0), + DSM_VALUE_RFI_DDR_DISABLE = BIT(1), +}; + +#define DSM_VALUE_RFI_DISABLE (DSM_VALUE_RFI_DLVR_DISABLE |\ + DSM_VALUE_RFI_DDR_DISABLE) + +enum iwl_dsm_masks_reg { + DSM_MASK_CHINA_22_REG = BIT(2) +}; + struct iwl_fw_runtime; bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt); @@ -142,4 +184,6 @@ int iwl_bios_get_pwr_limit(struct iwl_fw_runtime *fwrt, int iwl_bios_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); int iwl_bios_get_eckv(struct iwl_fw_runtime *fwrt, u32 *ext_clk); + +__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index f8d7f23741bf..a05a5f403ae5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1242,7 +1242,7 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) WIDE_ID(REGULATORY_AND_NVM_GROUP, LARI_CONFIG_CHANGE), 1); - cmd.config_bitmap = iwl_acpi_get_lari_config_bitmap(&mvm->fwrt); + cmd.config_bitmap = iwl_get_lari_config_bitmap(&mvm->fwrt); ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_11AX_ENABLEMENT, &value); if (!ret) From fc7214c3c986142758ae9d2cd456c98e48547b5e Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:37 +0200 Subject: [PATCH 192/378] wifi: iwlwifi: read DSM functions from UEFI For each DSM function, try to first read it from the UEFI. If the UEFI WIFI GUID is unclocked, or the DSM function in UEFI is invalid/unavailable - read it from ACPI. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.27dd626ce2bd.Ib90bab74a9d56deb2362edb712294360e4ddae5b@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 1 - .../wireless/intel/iwlwifi/fw/regulatory.c | 13 ++++++-- .../wireless/intel/iwlwifi/fw/regulatory.h | 3 ++ drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 33 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 21 ++++++++++++ .../net/wireless/intel/iwlwifi/mvm/debugfs.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 14 ++++---- 7 files changed, 75 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 357047223686..9afb1b1d6aea 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -187,7 +187,6 @@ int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, return 0; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm); static union acpi_object * iwl_acpi_get_wifi_pkg_range(struct device *dev, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 3260f21fd2e0..a42775141952 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -443,7 +443,7 @@ __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) case IWL_CFG_RF_TYPE_HR2: case IWL_CFG_RF_TYPE_JF1: case IWL_CFG_RF_TYPE_JF2: - ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_ENABLE_INDONESIA_5G2, + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_INDONESIA_5G2, &val); if (!ret && val == DSM_VALUE_INDONESIA_ENABLE) @@ -454,7 +454,7 @@ __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) break; } - ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_DISABLE_SRD, &val); + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_DISABLE_SRD, &val); if (!ret) { if (val == DSM_VALUE_SRD_PASSIVE) config_bitmap |= @@ -466,7 +466,7 @@ __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) if (fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CHINA_22_REG_SUPPORT)) { - ret = iwl_acpi_get_dsm(fwrt, DSM_FUNC_REGULATORY_CONFIG, + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_REGULATORY_CONFIG, &val); /* * China 2022 enable if the BIOS object does not exist or @@ -480,3 +480,10 @@ __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) return config_bitmap; } IWL_EXPORT_SYMBOL(iwl_get_lari_config_bitmap); + +int iwl_bios_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, + u32 *value) +{ + GET_BIOS_TABLE(dsm, fwrt, func, value); +} +IWL_EXPORT_SYMBOL(iwl_bios_get_dsm); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index da49ed7325d6..52389f82cbb9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -186,4 +186,7 @@ int iwl_bios_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); int iwl_bios_get_eckv(struct iwl_fw_runtime *fwrt, u32 *ext_clk); __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); + +int iwl_bios_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, + u32 *value); #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 4454fae84d1f..fe6d0141cd5b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -674,3 +674,36 @@ int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) kfree(data); return ret; } + +int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, + u32 *value) +{ + struct uefi_cnv_var_general_cfg *data; + int ret = EINVAL; + + /* Not supported function index */ + if (func >= DSM_FUNC_NUM_FUNCS || func == 5) + return -EOPNOTSUPP; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_DSM_NAME, + "DSM", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_DSM_REVISION) { + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSM revision:%d\n", + data->revision); + goto out; + } + + if (ARRAY_SIZE(data->functions) != UEFI_MAX_DSM_FUNCS) { + IWL_DEBUG_RADIO(fwrt, "Invalid size of DSM functions array\n"); + goto out; + } + + *value = data->functions[func]; + ret = 0; +out: + kfree(data); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 723933b0b2f1..1f7c3f4c2901 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -20,6 +20,7 @@ #define IWL_UEFI_SPLC_NAME L"UefiCnvWlanSPLC" #define IWL_UEFI_WRDD_NAME L"UefiCnvWlanWRDD" #define IWL_UEFI_ECKV_NAME L"UefiCnvWlanECKV" +#define IWL_UEFI_DSM_NAME L"UefiCnvWlanGeneralCfg" #define IWL_SGOM_MAP_SIZE 339 @@ -34,6 +35,7 @@ #define IWL_UEFI_SPLC_REVISION 0 #define IWL_UEFI_WRDD_REVISION 0 #define IWL_UEFI_ECKV_REVISION 0 +#define IWL_UEFI_DSM_REVISION 4 struct pnvm_sku_package { u8 rev; @@ -166,6 +168,17 @@ struct uefi_cnv_var_eckv { u32 ext_clock_valid; } __packed; +#define UEFI_MAX_DSM_FUNCS 32 + +/* struct uefi_cnv_var_general_cfg - DSM-like table as defined in UEFI + * @revision: the revision of the table + * @functions: payload of the different DSM functions + */ +struct uefi_cnv_var_general_cfg { + u8 revision; + u32 functions[UEFI_MAX_DSM_FUNCS]; +} __packed; + /* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply @@ -190,6 +203,8 @@ int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit); int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk); +int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, + u32 *value); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -263,6 +278,12 @@ static inline int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) { return -ENOENT; } + +static inline int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, + enum iwl_dsm_funcs func, u32 *value) +{ + return -ENOENT; +} #endif /* CONFIG_EFI */ #if defined(CONFIG_EFI) && defined(CONFIG_ACPI) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 6f33f791648e..1ba3a559c1d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -391,7 +391,7 @@ static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct file *file, char buf[12]; u32 value; - err = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_6E, &value); + err = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_6E, &value); if (err) return err; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index a05a5f403ae5..738e90a4fe2f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1206,7 +1206,7 @@ static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) u32 value = 0; /* default behaviour is disabled */ bool bios_enable_rfi = false; - int ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_RFI_CONFIG, &value); + int ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_RFI_CONFIG, &value); if (ret < 0) { @@ -1244,31 +1244,31 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) cmd.config_bitmap = iwl_get_lari_config_bitmap(&mvm->fwrt); - ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_11AX_ENABLEMENT, &value); + ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_11AX_ENABLEMENT, &value); if (!ret) cmd.oem_11ax_allow_bitmap = cpu_to_le32(value); - ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value); + ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value); if (!ret) cmd.oem_unii4_allow_bitmap = cpu_to_le32(value); - ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value); + ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value); if (!ret) { if (cmd_ver < 8) value &= ~ACTIVATE_5G2_IN_WW_MASK; cmd.chan_state_active_bitmap = cpu_to_le32(value); } - ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_6E, &value); + ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_6E, &value); if (!ret) cmd.oem_uhb_allow_bitmap = cpu_to_le32(value); - ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, + ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value); if (!ret) cmd.force_disable_channels_bitmap = cpu_to_le32(value); - ret = iwl_acpi_get_dsm(&mvm->fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD, + ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD, &value); if (!ret) cmd.edt_bitmap = cpu_to_le32(value); From c1b393a7dc237505088129945a85b570af8742da Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 1 Feb 2024 16:17:38 +0200 Subject: [PATCH 193/378] wifi: iwlwifi: mvm: don't send BT_COEX_CI command on new devices AX210 and above have this logic offloaded in the firmware and it just ignores the command coming from the driver. Stop sending it. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://msgid.link/20240201155157.4e3e0b52f98b.I7e9481050921d95c38f5a21ccc47112b3698e859@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index d26075e3e6ad..2c34c55ca5f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -578,6 +578,11 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_bt_notif_iterator, &data); + if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + rcu_read_unlock(); + return; + } + iwl_mvm_bt_coex_tcm_based_ci(mvm, &data); if (data.primary) { From 12e1a6a5b038bcf2c58fd356758710180222e5bc Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 1 Feb 2024 16:17:40 +0200 Subject: [PATCH 194/378] wifi: iwlwifi: bump FW API to 88 for AX/BZ/SC devices Start supporting API version 88 for new devices. Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240201155157.e35556d3f956.I6543857041a33e2b35e67eecf648c9cc6972e60a@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/cfg/ax210.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c index 02b727687fb8..456c7fff60a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_AX210_UCODE_API_MAX 87 +#define IWL_AX210_UCODE_API_MAX 88 /* Lowest firmware API version supported */ #define IWL_AX210_UCODE_API_MIN 59 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index b0b003a6a46e..c858f355701e 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_BZ_UCODE_API_MAX 87 +#define IWL_BZ_UCODE_API_MAX 88 /* Lowest firmware API version supported */ #define IWL_BZ_UCODE_API_MIN 80 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index 51b8f50d8795..e0679093ed8e 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_SC_UCODE_API_MAX 87 +#define IWL_SC_UCODE_API_MAX 88 /* Lowest firmware API version supported */ #define IWL_SC_UCODE_API_MIN 82 From 5932ad87828b267649d750869c89c0f1a3873477 Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Thu, 1 Feb 2024 16:17:41 +0200 Subject: [PATCH 195/378] wifi: iwlwifi: mvm: make functions public In the following patch, iwl_mvm_roc_duration_and_delay and iwl_mvm_roc_add_cmd will be called also from time-event.c. Move then there (where they more belong) and make then public. Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://msgid.link/20240201155157.3edafc4d59aa.Ic68e90758bcad9ae00e0aa602101842dac60e1a1@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 80 ------------------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 8 ++ .../wireless/intel/iwlwifi/mvm/time-event.c | 80 +++++++++++++++++++ 3 files changed, 88 insertions(+), 80 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 4bc25d4a87b6..31cb2e313a05 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -4427,44 +4427,6 @@ static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait, return true; } -#define AUX_ROC_MIN_DURATION MSEC_TO_TU(100) -#define AUX_ROC_MIN_DELAY MSEC_TO_TU(200) -#define AUX_ROC_MAX_DELAY MSEC_TO_TU(600) -#define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20) -#define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10) - -static void iwl_mvm_roc_duration_and_delay(struct ieee80211_vif *vif, - u32 duration_ms, - u32 *duration_tu, - u32 *delay) -{ - u32 dtim_interval = vif->bss_conf.dtim_period * - vif->bss_conf.beacon_int; - - *delay = AUX_ROC_MIN_DELAY; - *duration_tu = MSEC_TO_TU(duration_ms); - - /* - * If we are associated we want the delay time to be at least one - * dtim interval so that the FW can wait until after the DTIM and - * then start the time event, this will potentially allow us to - * remain off-channel for the max duration. - * Since we want to use almost a whole dtim interval we would also - * like the delay to be for 2-3 dtim intervals, in case there are - * other time events with higher priority. - */ - if (vif->cfg.assoc) { - *delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY); - /* We cannot remain off-channel longer than the DTIM interval */ - if (dtim_interval <= *duration_tu) { - *duration_tu = dtim_interval - AUX_ROC_SAFETY_BUFFER; - if (*duration_tu <= AUX_ROC_MIN_DURATION) - *duration_tu = dtim_interval - - AUX_ROC_MIN_SAFETY_BUFFER; - } - } -} - static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, struct ieee80211_channel *channel, struct ieee80211_vif *vif, @@ -4562,48 +4524,6 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, return res; } -static int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm, - struct ieee80211_channel *channel, - struct ieee80211_vif *vif, - int duration, u32 activity) -{ - int res; - u32 duration_tu, delay; - struct iwl_roc_req roc_req = { - .action = cpu_to_le32(FW_CTXT_ACTION_ADD), - .activity = cpu_to_le32(activity), - .sta_id = cpu_to_le32(mvm->aux_sta.sta_id), - }; - - lockdep_assert_held(&mvm->mutex); - - /* Set the channel info data */ - iwl_mvm_set_chan_info(mvm, &roc_req.channel_info, - channel->hw_value, - iwl_mvm_phy_band_from_nl80211(channel->band), - IWL_PHY_CHANNEL_MODE20, 0); - - iwl_mvm_roc_duration_and_delay(vif, duration, &duration_tu, - &delay); - roc_req.duration = cpu_to_le32(duration_tu); - roc_req.max_delay = cpu_to_le32(delay); - - IWL_DEBUG_TE(mvm, - "\t(requested = %ums, max_delay = %ums)\n", - duration, delay); - IWL_DEBUG_TE(mvm, - "Requesting to remain on channel %u for %utu\n", - channel->hw_value, duration_tu); - - /* Set the node address */ - memcpy(roc_req.node_addr, vif->addr, ETH_ALEN); - - res = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), - 0, sizeof(roc_req), &roc_req); - - return res; -} - static int iwl_mvm_add_aux_sta_for_hs20(struct iwl_mvm *mvm, u32 lmac_id) { int ret = 0; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index c76ce6b1fa72..eb30c299a71e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2764,4 +2764,12 @@ iwl_mvm_chanctx_def(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *ctx) return use_def ? &ctx->def : &ctx->min_def; } +void iwl_mvm_roc_duration_and_delay(struct ieee80211_vif *vif, + u32 duration_ms, + u32 *duration_tu, + u32 *delay); +int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm, + struct ieee80211_channel *channel, + struct ieee80211_vif *vif, + int duration, u32 activity); #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 703ccdd4d967..60ec5ca6927c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -986,6 +986,86 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, rcu_read_unlock(); } +#define AUX_ROC_MIN_DURATION MSEC_TO_TU(100) +#define AUX_ROC_MIN_DELAY MSEC_TO_TU(200) +#define AUX_ROC_MAX_DELAY MSEC_TO_TU(600) +#define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20) +#define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10) + +void iwl_mvm_roc_duration_and_delay(struct ieee80211_vif *vif, + u32 duration_ms, + u32 *duration_tu, + u32 *delay) +{ + u32 dtim_interval = vif->bss_conf.dtim_period * + vif->bss_conf.beacon_int; + + *delay = AUX_ROC_MIN_DELAY; + *duration_tu = MSEC_TO_TU(duration_ms); + + /* + * If we are associated we want the delay time to be at least one + * dtim interval so that the FW can wait until after the DTIM and + * then start the time event, this will potentially allow us to + * remain off-channel for the max duration. + * Since we want to use almost a whole dtim interval we would also + * like the delay to be for 2-3 dtim intervals, in case there are + * other time events with higher priority. + */ + if (vif->cfg.assoc) { + *delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY); + /* We cannot remain off-channel longer than the DTIM interval */ + if (dtim_interval <= *duration_tu) { + *duration_tu = dtim_interval - AUX_ROC_SAFETY_BUFFER; + if (*duration_tu <= AUX_ROC_MIN_DURATION) + *duration_tu = dtim_interval - + AUX_ROC_MIN_SAFETY_BUFFER; + } + } +} + +int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm, + struct ieee80211_channel *channel, + struct ieee80211_vif *vif, + int duration, u32 activity) +{ + int res; + u32 duration_tu, delay; + struct iwl_roc_req roc_req = { + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + .activity = cpu_to_le32(activity), + .sta_id = cpu_to_le32(mvm->aux_sta.sta_id), + }; + + lockdep_assert_held(&mvm->mutex); + + /* Set the channel info data */ + iwl_mvm_set_chan_info(mvm, &roc_req.channel_info, + channel->hw_value, + iwl_mvm_phy_band_from_nl80211(channel->band), + IWL_PHY_CHANNEL_MODE20, 0); + + iwl_mvm_roc_duration_and_delay(vif, duration, &duration_tu, + &delay); + roc_req.duration = cpu_to_le32(duration_tu); + roc_req.max_delay = cpu_to_le32(delay); + + IWL_DEBUG_TE(mvm, + "\t(requested = %ums, max_delay = %ums)\n", + duration, delay); + IWL_DEBUG_TE(mvm, + "Requesting to remain on channel %u for %utu\n", + channel->hw_value, duration_tu); + + /* Set the node address */ + memcpy(roc_req.node_addr, vif->addr, ETH_ALEN); + + res = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), + 0, sizeof(roc_req), &roc_req); + + return res; +} + static int iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm, struct ieee80211_vif *vif, From 5f9c1f8f9adaf32e4e39fcf260d4fc20dbfb77c9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Feb 2024 10:48:18 +0100 Subject: [PATCH 196/378] wifi: iwlwifi: fw: fix compile w/o CONFIG_ACPI The user of this function passes a pointer to a value that doesn't exist when compiled w/o CONFIG_ACPI. Since we don't need the value then, make the non-ACPI version a macro to allow it to still build. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202402031454.syX4cSGN-lkp@intel.com/ Fixes: c4c954547755 ("wifi: iwlwifi: implement WPFC ACPI table loading") Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 9cb101776884..1d32b82f73db 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -193,10 +193,8 @@ static inline int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) return -ENOENT; } -static inline void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, - struct iwl_phy_specific_cfg *filters) -{ -} +/* macro since the second argument doesn't always exist */ +#define iwl_acpi_get_phy_filters(fwrt, filters) do { } while (0) static inline void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) { From 4c60c8054dd8a36091aab585569c8d12a0085bd9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Feb 2024 10:53:18 +0100 Subject: [PATCH 197/378] wifi: iwlwifi: fw: fix compiler warning for NULL string print When the system is compiled without CONFIG_DMI, the function here statically returns NULL, leading to a compiler warning. Catch that and print "" in that case. While at it, fix some indentation in the function. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202402030641.zUTuACYV-lkp@intel.com/ Fixes: 09059c6764a8 ("wifi: iwlwifi: prepare for reading PPAG table from UEFI") Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index a42775141952..21b90278d1f2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -397,9 +397,9 @@ bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt) if (!dmi_check_system(dmi_ppag_approved_list)) { IWL_DEBUG_RADIO(fwrt, "System vendor '%s' is not in the approved list, disabling PPAG.\n", - dmi_get_system_info(DMI_SYS_VENDOR)); - fwrt->ppag_flags = 0; - return false; + dmi_get_system_info(DMI_SYS_VENDOR) ?: ""); + fwrt->ppag_flags = 0; + return false; } return true; From 6256760f37baa2e4bf34dcbef69d7450460df9bd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Feb 2024 10:56:27 +0100 Subject: [PATCH 198/378] wifi: iwlwifi: mvm: fix warnings from dmi_get_system_info() dmi_get_system_info() will statically return NULL when the kernel is compiled without CONFIG_DMI, leading to compiler warnings. Fix that by printing "" in that case. Fixes: c3f40c3e0273 ("iwlwifi: mvm: add US/CA to TAS block list if OEM isn't allowed") Fixes: 9457077df49e ("wifi: iwlwifi: mvm: Add debugfs to get TAS status") Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 1ba3a559c1d6..79f4ac8cbc72 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -880,7 +880,7 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, le16_to_cpu(rsp->block_list[i])); pos += scnprintf(pos, endpos - pos, "\nOEM name: %s\n", - dmi_get_system_info(DMI_SYS_VENDOR)); + dmi_get_system_info(DMI_SYS_VENDOR) ?: ""); pos += scnprintf(pos, endpos - pos, "\tVendor In Approved List: %s\n", iwl_is_tas_approved() ? "YES" : "NO"); pos += scnprintf(pos, endpos - pos, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 738e90a4fe2f..b596a1a83750 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1159,7 +1159,7 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) if (!iwl_is_tas_approved()) { IWL_DEBUG_RADIO(mvm, "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", - dmi_get_system_info(DMI_SYS_VENDOR)); + dmi_get_system_info(DMI_SYS_VENDOR) ?: ""); if ((!iwl_mvm_add_to_tas_block_list(data.block_list_array, &data.block_list_size, IWL_MCC_US)) || @@ -1173,7 +1173,7 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) } else { IWL_DEBUG_RADIO(mvm, "System vendor '%s' is in the approved list.\n", - dmi_get_system_info(DMI_SYS_VENDOR)); + dmi_get_system_info(DMI_SYS_VENDOR) ?: ""); } fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, From 679dd27b4ef33d4f596cbf450a3b2742fc54962a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Feb 2024 11:03:39 +0100 Subject: [PATCH 199/378] wifi: cfg80211: fix kunit exports These can only be exported if cfg80211's kunit is enabled, since they're otherwise static. kunit itself can be enabled even if cfg80211's kunit isn't. Fix that by using the right macro. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202402040534.6AEKtZ7Y-lkp@intel.com/ Fixes: 45d43937a44c ("wifi: cfg80211: add a kunit test for 6 GHz colocated AP parsing") Signed-off-by: Johannes Berg --- net/wireless/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index c78b10e1a167..6dd9df347771 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -537,7 +537,7 @@ cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list) kfree(ap); } } -EXPORT_SYMBOL_IF_KUNIT(cfg80211_free_coloc_ap_list); +EXPORT_SYMBOL_IF_CFG80211_KUNIT(cfg80211_free_coloc_ap_list); static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry, const u8 *pos, u8 length, @@ -710,7 +710,7 @@ cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies, list_splice_tail(&ap_list, list); return n_coloc; } -EXPORT_SYMBOL_IF_KUNIT(cfg80211_parse_colocated_ap); +EXPORT_SYMBOL_IF_CFG80211_KUNIT(cfg80211_parse_colocated_ap); static void cfg80211_scan_req_add_chan(struct cfg80211_scan_request *request, struct ieee80211_channel *chan, From 5f0e4aede01cb01fa633171f0533affd25328c3a Mon Sep 17 00:00:00 2001 From: Zhipeng Lu Date: Fri, 26 Jan 2024 15:53:34 +0800 Subject: [PATCH 200/378] wifi: libertas: fix some memleaks in lbs_allocate_cmd_buffer() In the for statement of lbs_allocate_cmd_buffer(), if the allocation of cmdarray[i].cmdbuf fails, both cmdarray and cmdarray[i].cmdbuf needs to be freed. Otherwise, there will be memleaks in lbs_allocate_cmd_buffer(). Fixes: 876c9d3aeb98 ("[PATCH] Marvell Libertas 8388 802.11b/g USB driver") Signed-off-by: Zhipeng Lu Signed-off-by: Kalle Valo Link: https://msgid.link/20240126075336.2825608-1-alexious@zju.edu.cn --- drivers/net/wireless/marvell/libertas/cmd.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/libertas/cmd.c b/drivers/net/wireless/marvell/libertas/cmd.c index 104d2b6dc9af..5a525da434c2 100644 --- a/drivers/net/wireless/marvell/libertas/cmd.c +++ b/drivers/net/wireless/marvell/libertas/cmd.c @@ -1132,7 +1132,7 @@ int lbs_allocate_cmd_buffer(struct lbs_private *priv) if (!cmdarray[i].cmdbuf) { lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); ret = -1; - goto done; + goto free_cmd_array; } } @@ -1140,8 +1140,17 @@ int lbs_allocate_cmd_buffer(struct lbs_private *priv) init_waitqueue_head(&cmdarray[i].cmdwait_q); lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]); } - ret = 0; + return 0; +free_cmd_array: + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + if (cmdarray[i].cmdbuf) { + kfree(cmdarray[i].cmdbuf); + cmdarray[i].cmdbuf = NULL; + } + } + kfree(priv->cmd_array); + priv->cmd_array = NULL; done: return ret; } From 1209f487d452ff7e822dec30661fd6b5163fb8cf Mon Sep 17 00:00:00 2001 From: Chun Qiu Date: Mon, 29 Jan 2024 13:30:30 +0800 Subject: [PATCH 201/378] wifi: rtl8xxxu: Add TP-Link TL-WN823N V2 TP-Link TL-WN823N V2 (2357:0135) is based on rtl8192fu and has been tested to work with the rtl8xxxu driver. Signed-off-by: Chun Qiu Signed-off-by: Kalle Valo Link: https://msgid.link/20240129053030.16369-1-cqca@cock.lu --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index bd6fd3120562..125f03354ceb 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -7734,7 +7734,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, untested = 0; break; case 0x2357: - if (id->idProduct == 0x0109) + if (id->idProduct == 0x0109 || id->idProduct == 0x0135) untested = 0; break; case 0x0b05: @@ -8027,6 +8027,9 @@ static const struct usb_device_id dev_table[] = { .driver_info = (unsigned long)&rtl8192fu_fops}, {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x318b, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8192fu_fops}, +/* TP-Link TL-WN823N V2 */ +{USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0135, 0xff, 0xff, 0xff), + .driver_info = (unsigned long)&rtl8192fu_fops}, #ifdef CONFIG_RTL8XXXU_UNTESTED /* Still supported by rtlwifi */ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8176, 0xff, 0xff, 0xff), From 2b59c9c30b9cbbb9807abcb2eca0a45ce40611df Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 30 Jan 2024 17:15:53 +0200 Subject: [PATCH 202/378] wifi: zd1211rw: remove __nocast from zd_addr_t Sparse warns: drivers/net/wireless/zydas/zd1211rw/zd_usb.c:383:24: warning: implicit cast from nocast type drivers/net/wireless/zydas/zd1211rw/zd_usb.c:419:24: warning: implicit cast from nocast type This is an ancient driver which has not have any meaningfuli changes for a long time and hopefully removed soon. So just remove the __nocast to get rid of the sparse warnings. Compile tested only. Signed-off-by: Kalle Valo Link: https://msgid.link/20240130151556.2315951-2-kvalo@kernel.org --- drivers/net/wireless/zydas/zd1211rw/zd_def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_def.h b/drivers/net/wireless/zydas/zd1211rw/zd_def.h index 8ca2d0aab170..2f55e8deee82 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_def.h +++ b/drivers/net/wireless/zydas/zd1211rw/zd_def.h @@ -12,7 +12,7 @@ #include #include -typedef u16 __nocast zd_addr_t; +typedef u16 zd_addr_t; #define dev_printk_f(level, dev, fmt, args...) \ dev_printk(level, dev, "%s() " fmt, __func__, ##args) From 0583e5acaf43644c4d4f476979c18bf1a034639f Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 30 Jan 2024 17:15:54 +0200 Subject: [PATCH 203/378] wifi: rsi: fix restricted __le32 degrades to integer sparse warnings drivers/net/wireless/rsi/rsi_91x_usb.c:235:27: warning: restricted __le32 degrades to integer drivers/net/wireless/rsi/rsi_91x_usb.c:236:27: warning: restricted __le32 degrades to integer drivers/net/wireless/rsi/rsi_91x_usb.c:237:27: warning: restricted __le32 degrades to integer drivers/net/wireless/rsi/rsi_91x_usb.c:238:27: warning: restricted __le32 degrades to integer drivers/net/wireless/rsi/rsi_91x_usb.c:244:36: warning: restricted __le32 degrades to integer drivers/net/wireless/rsi/rsi_91x_usb.c:245:35: warning: restricted __le32 degrades to integer These cpu_to_le32() are not making sense. With usb_reg_buf we handle the values byte at a time to make sure usb_reg_buf is in little endian so no need to convert anything. And usb_control_msg() expects to have the values in native endian anyway. So just remove these so they are not spamming our logs. Compile tested only. Signed-off-by: Kalle Valo Link: https://msgid.link/20240130151556.2315951-3-kvalo@kernel.org --- drivers/net/wireless/rsi/rsi_91x_usb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 10a465686439..dccc139cabb2 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -232,17 +232,17 @@ static int rsi_usb_reg_write(struct usb_device *usbdev, if (!usb_reg_buf) return status; - usb_reg_buf[0] = (cpu_to_le32(value) & 0x00ff); - usb_reg_buf[1] = (cpu_to_le32(value) & 0xff00) >> 8; - usb_reg_buf[2] = (cpu_to_le32(value) & 0x00ff0000) >> 16; - usb_reg_buf[3] = (cpu_to_le32(value) & 0xff000000) >> 24; + usb_reg_buf[0] = value & 0x00ff; + usb_reg_buf[1] = (value & 0xff00) >> 8; + usb_reg_buf[2] = (value & 0x00ff0000) >> 16; + usb_reg_buf[3] = (value & 0xff000000) >> 24; status = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), USB_VENDOR_REGISTER_WRITE, RSI_USB_REQ_OUT, - ((cpu_to_le32(reg) & 0xffff0000) >> 16), - (cpu_to_le32(reg) & 0xffff), + (reg & 0xffff0000) >> 16, + reg & 0xffff, (void *)usb_reg_buf, len, USB_CTRL_SET_TIMEOUT); From 7ceade653429c1c6af387d4039199eeae3b685c1 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 30 Jan 2024 17:15:55 +0200 Subject: [PATCH 204/378] wifi: cw1200: fix __le16 sparse warnings Sparse warns: drivers/net/wireless/st/cw1200/cw1200_spi.c:83:17: got restricted __le16 [usertype] drivers/net/wireless/st/cw1200/cw1200_spi.c:148:17: warning: incorrect type in assignment (different base types) drivers/net/wireless/st/cw1200/cw1200_spi.c:148:17: expected unsigned short [addressable] [assigned] [usertype] regaddr drivers/net/wireless/st/cw1200/cw1200_spi.c:148:17: got restricted __le16 [usertype] These cpu_to_le16() calls are not really making any sense to me. On a big endian system we first convert regaddr from big to little using cpu_to_le16() but immediately after we convert them back to big endian? So just remove them. Compile tested only. Signed-off-by: Kalle Valo Link: https://msgid.link/20240130151556.2315951-4-kvalo@kernel.org --- drivers/net/wireless/st/cw1200/cw1200_spi.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/st/cw1200/cw1200_spi.c b/drivers/net/wireless/st/cw1200/cw1200_spi.c index c82c0688b549..b27b57fc25bc 100644 --- a/drivers/net/wireless/st/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/st/cw1200/cw1200_spi.c @@ -79,9 +79,6 @@ static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self, pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr); #endif - /* Header is LE16 */ - regaddr = cpu_to_le16(regaddr); - /* We have to byteswap if the SPI bus is limited to 8b operation or we are running on a Big Endian system */ @@ -144,9 +141,6 @@ static int cw1200_spi_memcpy_toio(struct hwbus_priv *self, pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr, regaddr); #endif - /* Header is LE16 */ - regaddr = cpu_to_le16(regaddr); - /* We have to byteswap if the SPI bus is limited to 8b operation or we are running on a Big Endian system */ From 04e9c8af8b2d9b51febb522c1f2dae7521dbc154 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 31 Jan 2024 23:37:21 +0100 Subject: [PATCH 205/378] wifi: ti: wlcore: sdio: Drop unused include The file is including the legacy GPIO header but is not using any symbols from it, drop the header. Signed-off-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://msgid.link/20240131-descriptors-wireless-v1-2-e1c7c5d68746@linaro.org --- drivers/net/wireless/ti/wlcore/sdio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index f0686635db46..45dcc7b400c3 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include From b303de763b638f4a8703651944e7a724739dba74 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 31 Jan 2024 23:37:22 +0100 Subject: [PATCH 206/378] wifi: brcmsmac: Drop legacy header The driver is using all the modern abstractions to obtain and use GPIOs and the legacy header is unused, so drop it. Fixes: a8e59744e16b ("gpiolib: split linux/gpio/driver.h out of linux/gpio.h") Signed-off-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://msgid.link/20240131-descriptors-wireless-v1-3-e1c7c5d68746@linaro.org --- drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c index 89c8829528c2..9540a05247c2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include #include -#include #include #include #include From 163857d995312a7b5fe3039ce0145b84cc7673b1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 31 Jan 2024 23:37:23 +0100 Subject: [PATCH 207/378] wifi: mwifiex: Drop unused headers The mwifiex driver include two legacy GPIO headers but does not use symbols from any of them. The driver does contain some "gpio" handling code, but this is some custom GPIO interface, not the Linux GPIO. Signed-off-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://msgid.link/20240131-descriptors-wireless-v1-4-e1c7c5d68746@linaro.org --- drivers/net/wireless/marvell/mwifiex/main.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 318b42b1896f..175882485a19 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -28,11 +28,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include From d8da5a213812cb0092854f586aa26735b59be85b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 31 Jan 2024 23:37:24 +0100 Subject: [PATCH 208/378] wifi: plfxlc: Drop unused include The driver includes the legacy GPIO header but does not use any symbols from it. Drop the include. Signed-off-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://msgid.link/20240131-descriptors-wireless-v1-5-e1c7c5d68746@linaro.org --- drivers/net/wireless/purelifi/plfxlc/mac.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c index 506d2f31efb5..6f5857d09af0 100644 --- a/drivers/net/wireless/purelifi/plfxlc/mac.c +++ b/drivers/net/wireless/purelifi/plfxlc/mac.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include From 2719a9e7156c4b3983b43db467c1ff96801bda99 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 31 Jan 2024 23:37:25 +0100 Subject: [PATCH 209/378] wifi: cw1200: Convert to GPIO descriptors The CW1200 uses two GPIOs to control the powerup and reset pins, get these from GPIO descriptors instead of being passed as platform data from boardfiles. The RESET line will need to be marked as active low as we will let gpiolib handle the polarity inversion. The SDIO case is a bit special since the "card" need to be powered up before it gets detected on the SDIO bus and properly probed. Fix this by using board-specific GPIOs assigned to device "NULL". There are currently no in-tree users. Signed-off-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://msgid.link/20240131-descriptors-wireless-v1-6-e1c7c5d68746@linaro.org --- drivers/net/wireless/st/cw1200/cw1200_sdio.c | 42 ++++++----- drivers/net/wireless/st/cw1200/cw1200_spi.c | 73 +++++++++++--------- include/linux/platform_data/net-cw1200.h | 4 -- 3 files changed, 66 insertions(+), 53 deletions(-) diff --git a/drivers/net/wireless/st/cw1200/cw1200_sdio.c b/drivers/net/wireless/st/cw1200/cw1200_sdio.c index 4c30b5772ce0..00c4731d8f8e 100644 --- a/drivers/net/wireless/st/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/st/cw1200/cw1200_sdio.c @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include #include @@ -178,12 +178,15 @@ static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self) return ret; } +/* Like the rest of the driver, this only supports one device per system */ +static struct gpio_desc *cw1200_reset; +static struct gpio_desc *cw1200_powerup; + static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata) { - if (pdata->reset) { - gpio_set_value(pdata->reset, 0); + if (cw1200_reset) { + gpiod_set_value(cw1200_reset, 0); msleep(30); /* Min is 2 * CLK32K cycles */ - gpio_free(pdata->reset); } if (pdata->power_ctrl) @@ -196,16 +199,21 @@ static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata) static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata) { - /* Ensure I/Os are pulled low */ - if (pdata->reset) { - gpio_request(pdata->reset, "cw1200_wlan_reset"); - gpio_direction_output(pdata->reset, 0); + /* Ensure I/Os are pulled low (reset is active low) */ + cw1200_reset = devm_gpiod_get_optional(NULL, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(cw1200_reset)) { + pr_err("could not get CW1200 SDIO reset GPIO\n"); + return PTR_ERR(cw1200_reset); } - if (pdata->powerup) { - gpio_request(pdata->powerup, "cw1200_wlan_powerup"); - gpio_direction_output(pdata->powerup, 0); + gpiod_set_consumer_name(cw1200_reset, "cw1200_wlan_reset"); + cw1200_powerup = devm_gpiod_get_optional(NULL, "powerup", GPIOD_OUT_LOW); + if (IS_ERR(cw1200_powerup)) { + pr_err("could not get CW1200 SDIO powerup GPIO\n"); + return PTR_ERR(cw1200_powerup); } - if (pdata->reset || pdata->powerup) + gpiod_set_consumer_name(cw1200_powerup, "cw1200_wlan_powerup"); + + if (cw1200_reset || cw1200_powerup) msleep(10); /* Settle time? */ /* Enable 3v3 and 1v8 to hardware */ @@ -226,13 +234,13 @@ static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata) } /* Enable POWERUP signal */ - if (pdata->powerup) { - gpio_set_value(pdata->powerup, 1); + if (cw1200_powerup) { + gpiod_set_value(cw1200_powerup, 1); msleep(250); /* or more..? */ } - /* Enable RSTn signal */ - if (pdata->reset) { - gpio_set_value(pdata->reset, 1); + /* Deassert RSTn signal, note active low */ + if (cw1200_reset) { + gpiod_set_value(cw1200_reset, 0); msleep(50); /* Or more..? */ } return 0; diff --git a/drivers/net/wireless/st/cw1200/cw1200_spi.c b/drivers/net/wireless/st/cw1200/cw1200_spi.c index b27b57fc25bc..fb3aafcafe18 100644 --- a/drivers/net/wireless/st/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/st/cw1200/cw1200_spi.c @@ -11,7 +11,7 @@ */ #include -#include +#include #include #include #include @@ -38,6 +38,8 @@ struct hwbus_priv { const struct cw1200_platform_data_spi *pdata; spinlock_t lock; /* Serialize all bus operations */ wait_queue_head_t wq; + struct gpio_desc *reset; + struct gpio_desc *powerup; int claimed; }; @@ -269,12 +271,12 @@ static void cw1200_spi_irq_unsubscribe(struct hwbus_priv *self) free_irq(self->func->irq, self); } -static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) +static int cw1200_spi_off(struct hwbus_priv *self, const struct cw1200_platform_data_spi *pdata) { - if (pdata->reset) { - gpio_set_value(pdata->reset, 0); + if (self->reset) { + /* Assert RESET, note active low */ + gpiod_set_value(self->reset, 1); msleep(30); /* Min is 2 * CLK32K cycles */ - gpio_free(pdata->reset); } if (pdata->power_ctrl) @@ -285,18 +287,12 @@ static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) return 0; } -static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) +static int cw1200_spi_on(struct hwbus_priv *self, const struct cw1200_platform_data_spi *pdata) { /* Ensure I/Os are pulled low */ - if (pdata->reset) { - gpio_request(pdata->reset, "cw1200_wlan_reset"); - gpio_direction_output(pdata->reset, 0); - } - if (pdata->powerup) { - gpio_request(pdata->powerup, "cw1200_wlan_powerup"); - gpio_direction_output(pdata->powerup, 0); - } - if (pdata->reset || pdata->powerup) + gpiod_direction_output(self->reset, 1); /* Active low */ + gpiod_direction_output(self->powerup, 0); + if (self->reset || self->powerup) msleep(10); /* Settle time? */ /* Enable 3v3 and 1v8 to hardware */ @@ -317,13 +313,13 @@ static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) } /* Enable POWERUP signal */ - if (pdata->powerup) { - gpio_set_value(pdata->powerup, 1); + if (self->powerup) { + gpiod_set_value(self->powerup, 1); msleep(250); /* or more..? */ } - /* Enable RSTn signal */ - if (pdata->reset) { - gpio_set_value(pdata->reset, 1); + /* Assert RSTn signal, note active low */ + if (self->reset) { + gpiod_set_value(self->reset, 0); msleep(50); /* Or more..? */ } return 0; @@ -375,22 +371,35 @@ static int cw1200_spi_probe(struct spi_device *func) spi_get_chipselect(func, 0), func->mode, func->bits_per_word, func->max_speed_hz); - if (cw1200_spi_on(plat_data)) { - pr_err("spi_on() failed!\n"); - return -1; - } - - if (spi_setup(func)) { - pr_err("spi_setup() failed!\n"); - return -1; - } - self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL); if (!self) { pr_err("Can't allocate SPI hwbus_priv."); return -ENOMEM; } + /* Request reset asserted */ + self->reset = devm_gpiod_get_optional(&func->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(self->reset)) + return dev_err_probe(&func->dev, PTR_ERR(self->reset), + "could not get reset GPIO\n"); + gpiod_set_consumer_name(self->reset, "cw1200_wlan_reset"); + + self->powerup = devm_gpiod_get_optional(&func->dev, "powerup", GPIOD_OUT_LOW); + if (IS_ERR(self->powerup)) + return dev_err_probe(&func->dev, PTR_ERR(self->powerup), + "could not get powerup GPIO\n"); + gpiod_set_consumer_name(self->reset, "cw1200_wlan_powerup"); + + if (cw1200_spi_on(self, plat_data)) { + pr_err("spi_on() failed!\n"); + return -ENODEV; + } + + if (spi_setup(func)) { + pr_err("spi_setup() failed!\n"); + return -ENODEV; + } + self->pdata = plat_data; self->func = func; spin_lock_init(&self->lock); @@ -410,7 +419,7 @@ static int cw1200_spi_probe(struct spi_device *func) if (status) { cw1200_spi_irq_unsubscribe(self); - cw1200_spi_off(plat_data); + cw1200_spi_off(self, plat_data); } return status; @@ -428,7 +437,7 @@ static void cw1200_spi_disconnect(struct spi_device *func) self->core = NULL; } } - cw1200_spi_off(dev_get_platdata(&func->dev)); + cw1200_spi_off(self, dev_get_platdata(&func->dev)); } static int __maybe_unused cw1200_spi_suspend(struct device *dev) diff --git a/include/linux/platform_data/net-cw1200.h b/include/linux/platform_data/net-cw1200.h index c510734405bb..89d0ec6f7d46 100644 --- a/include/linux/platform_data/net-cw1200.h +++ b/include/linux/platform_data/net-cw1200.h @@ -14,8 +14,6 @@ struct cw1200_platform_data_spi { /* All others are optional */ bool have_5ghz; - int reset; /* GPIO to RSTn signal (0 disables) */ - int powerup; /* GPIO to POWERUP signal (0 disables) */ int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata, bool enable); /* Control 3v3 / 1v8 supply */ int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata, @@ -30,8 +28,6 @@ struct cw1200_platform_data_sdio { /* All others are optional */ bool have_5ghz; bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */ - int reset; /* GPIO to RSTn signal (0 disables) */ - int powerup; /* GPIO to POWERUP signal (0 disables) */ int irq; /* IRQ line or 0 to use SDIO IRQ */ int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata, bool enable); /* Control 3v3 / 1v8 supply */ From bed41a344426a407ba5e103b2877a2e560e72229 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 1 Feb 2024 14:12:47 -0600 Subject: [PATCH 210/378] wifi: wilc1000: remove setting msg.spi Calling spi_sync() unconditionally sets the spi field of struct spi_message. Therefore setting msg.spi = spi before calling spi_sync() has no effect and can be removed. (spi_message_add_tail() does not access this field.) Signed-off-by: David Lechner Signed-off-by: Kalle Valo Link: https://msgid.link/20240201201248.2334798-2-dlechner@baylibre.com --- drivers/net/wireless/microchip/wilc1000/spi.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/spi.c b/drivers/net/wireless/microchip/wilc1000/spi.c index 77b4cdff73c3..7eb0f8a421a3 100644 --- a/drivers/net/wireless/microchip/wilc1000/spi.c +++ b/drivers/net/wireless/microchip/wilc1000/spi.c @@ -300,7 +300,6 @@ static int wilc_spi_tx(struct wilc *wilc, u8 *b, u32 len) memset(&msg, 0, sizeof(msg)); spi_message_init(&msg); - msg.spi = spi; spi_message_add_tail(&tr, &msg); ret = spi_sync(spi, &msg); @@ -343,7 +342,6 @@ static int wilc_spi_rx(struct wilc *wilc, u8 *rb, u32 rlen) memset(&msg, 0, sizeof(msg)); spi_message_init(&msg); - msg.spi = spi; spi_message_add_tail(&tr, &msg); ret = spi_sync(spi, &msg); @@ -381,8 +379,6 @@ static int wilc_spi_tx_rx(struct wilc *wilc, u8 *wb, u8 *rb, u32 rlen) memset(&msg, 0, sizeof(msg)); spi_message_init(&msg); - msg.spi = spi; - spi_message_add_tail(&tr, &msg); ret = spi_sync(spi, &msg); if (ret < 0) From ad1c86e92698fa524078abc83a0709ccca06dbcd Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:32 +0800 Subject: [PATCH 211/378] wifi: rtw89: rfk: add a completion to wait RF calibration report from C2H event The RF calibrations should be executed one by one, so add a completion to ensure one is finish before next. The report from C2H event contains state and optional version, and we only check the state for now. We also care about the time a RF calibration takes, so record start time before asking firmware to do calibration and get the delta time when receiving report. Consider SER recovery, we can't receive C2H event, use half of argument 'ms' as fixed delay that is 2 times of measured maximum time of calibrations. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 1 + drivers/net/wireless/realtek/rtw89/core.h | 16 +++++++ drivers/net/wireless/realtek/rtw89/fw.h | 6 +++ drivers/net/wireless/realtek/rtw89/phy.c | 52 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 3 ++ 5 files changed, 78 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 260da86bf04a..c6c3e0c0bf84 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4205,6 +4205,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) INIT_WORK(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work); init_completion(&rtwdev->fw.req.completion); + init_completion(&rtwdev->rfk_wait.completion); schedule_work(&rtwdev->load_firmware_work); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 30cc77ac78c5..d88a6b9bc4e9 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4252,6 +4252,21 @@ struct rtw89_phy_stat { struct rtw89_pkt_stat last_pkt_stat; }; +enum rtw89_rfk_report_state { + RTW89_RFK_STATE_START = 0x0, + RTW89_RFK_STATE_OK = 0x1, + RTW89_RFK_STATE_FAIL = 0x2, + RTW89_RFK_STATE_TIMEOUT = 0x3, + RTW89_RFK_STATE_H2C_CMD_ERR = 0x4, +}; + +struct rtw89_rfk_wait_info { + struct completion completion; + ktime_t start_time; + enum rtw89_rfk_report_state state; + u8 version; +}; + #define RTW89_DACK_PATH_NR 2 #define RTW89_DACK_IDX_NR 2 #define RTW89_DACK_MSBK_NR 16 @@ -4944,6 +4959,7 @@ struct rtw89_dev { DECLARE_BITMAP(pkt_offload, RTW89_MAX_PKT_OFLD_NUM); struct rtw89_phy_stat phystat; + struct rtw89_rfk_wait_info rfk_wait; struct rtw89_dack_info dack; struct rtw89_iqk_info iqk; struct rtw89_dpk_info dpk; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index ae69e455cd64..9a2c50c35f2a 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4013,6 +4013,12 @@ struct rtw89_c2h_rf_txgapk_rpt_log { u8 rsv1; } __packed; +struct rtw89_c2h_rfk_report { + struct rtw89_c2h_hdr hdr; + u8 state; /* enum rtw89_rfk_report_state */ + u8 version; +} __packed; + #define RTW89_FW_RSVD_PLE_SIZE 0x800 #define RTW89_FW_BACKTRACE_INFO_SIZE 8 diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index f661be2f1287..3de61c0d7b03 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2834,9 +2834,61 @@ void (* const rtw89_phy_c2h_rfk_log_handler[])(struct rtw89_dev *rtwdev, [RTW89_PHY_C2H_RFK_LOG_FUNC_TXGAPK] = rtw89_phy_c2h_rfk_log_txgapk, }; +void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev) +{ + struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait; + + wait->state = RTW89_RFK_STATE_START; + wait->start_time = ktime_get(); + reinit_completion(&wait->completion); +} + +int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, + unsigned int ms) +{ + struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait; + unsigned long time_left; + + /* Since we can't receive C2H event during SER, use a fixed delay. */ + if (test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) { + fsleep(1000 * ms / 2); + goto out; + } + + time_left = wait_for_completion_timeout(&wait->completion, + msecs_to_jiffies(ms)); + if (time_left == 0) { + rtw89_warn(rtwdev, "failed to wait RF %s\n", rfk_name); + return -ETIMEDOUT; + } else if (wait->state != RTW89_RFK_STATE_OK) { + rtw89_warn(rtwdev, "failed to do RF %s result from state %d\n", + rfk_name, wait->state); + return -EFAULT; + } + +out: + rtw89_debug(rtwdev, RTW89_DBG_RFK, "RF %s takes %lld ms to complete\n", + rfk_name, ktime_ms_delta(ktime_get(), wait->start_time)); + + return 0; +} + static void rtw89_phy_c2h_rfk_report_state(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { + const struct rtw89_c2h_rfk_report *report = + (const struct rtw89_c2h_rfk_report *)c2h->data; + struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait; + + wait->state = report->state; + wait->version = report->version; + + complete(&wait->completion); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "RFK report state %d with version %d (%*ph)\n", + wait->state, wait->version, + (int)(len - sizeof(report->hdr)), &report->state); } static diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 13903ca1eaa9..df915cb0833f 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -885,6 +885,9 @@ void rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev, bool rtw89_phy_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func); void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func); +void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev); +int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, + unsigned int ms); void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev); void rtw89_phy_cfo_track_work(struct work_struct *work); void rtw89_phy_cfo_parse(struct rtw89_dev *rtwdev, s16 cfo_val, From 80f47f82f3191b5d2530ad510814b4afab121672 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:33 +0800 Subject: [PATCH 212/378] wifi: rtw89: rfk: send channel information to firmware for RF calibrations We are going to do RF calibrations in firmware, so driver needs to provide channel information for calibrations, which can do the same things as they did in driver. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 11 ++-- drivers/net/wireless/realtek/rtw89/fw.c | 73 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 42 +++++++++++++ drivers/net/wireless/realtek/rtw89/reg.h | 2 + 4 files changed, 124 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d88a6b9bc4e9..e049f7b5168b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4282,15 +4282,18 @@ struct rtw89_dack_info { bool msbk_timeout[RTW89_DACK_PATH_NR]; }; -#define RTW89_IQK_CHS_NR 2 -#define RTW89_IQK_PATH_NR 4 +#define RTW89_RFK_CHS_NR 3 struct rtw89_rfk_mcc_info { - u8 ch[RTW89_IQK_CHS_NR]; - u8 band[RTW89_IQK_CHS_NR]; + u8 ch[RTW89_RFK_CHS_NR]; + u8 band[RTW89_RFK_CHS_NR]; + u8 bw[RTW89_RFK_CHS_NR]; u8 table_idx; }; +#define RTW89_IQK_CHS_NR 2 +#define RTW89_IQK_PATH_NR 4 + struct rtw89_lck_info { u8 thermal[RF_PATH_MAX]; }; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 2f3f2b503507..5bd3d08a5d25 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -4520,6 +4520,79 @@ int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev) } EXPORT_SYMBOL(rtw89_fw_h2c_rf_ntfy_mcc); +int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; + struct rtw89_fw_h2c_rfk_pre_info *h2c; + u8 tbl_sel = rfk_mcc->table_idx; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + u8 tbl, path; + u32 val32; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c rfk_pre_ntfy\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_fw_h2c_rfk_pre_info *)skb->data; + + h2c->mlo_mode = cpu_to_le32(rtwdev->mlo_dbcc_mode); + + BUILD_BUG_ON(NUM_OF_RTW89_FW_RFK_TBL > RTW89_RFK_CHS_NR); + + for (tbl = 0; tbl < NUM_OF_RTW89_FW_RFK_TBL; tbl++) { + for (path = 0; path < NUM_OF_RTW89_FW_RFK_PATH; path++) { + h2c->dbcc.ch[path][tbl] = cpu_to_le32(rfk_mcc->ch[tbl]); + h2c->dbcc.band[path][tbl] = cpu_to_le32(rfk_mcc->band[tbl]); + } + } + + for (path = 0; path < NUM_OF_RTW89_FW_RFK_PATH; path++) { + h2c->tbl.cur_ch[path] = cpu_to_le32(rfk_mcc->ch[tbl_sel]); + h2c->tbl.cur_band[path] = cpu_to_le32(rfk_mcc->band[tbl_sel]); + } + + h2c->phy_idx = cpu_to_le32(phy_idx); + h2c->cur_band = cpu_to_le32(rfk_mcc->band[tbl_sel]); + h2c->cur_bw = cpu_to_le32(rfk_mcc->bw[tbl_sel]); + h2c->cur_center_ch = cpu_to_le32(rfk_mcc->ch[tbl_sel]); + + val32 = rtw89_phy_read32_mask(rtwdev, R_COEF_SEL, B_COEF_SEL_IQC_V1); + h2c->ktbl_sel0 = cpu_to_le32(val32); + val32 = rtw89_phy_read32_mask(rtwdev, R_COEF_SEL_C1, B_COEF_SEL_IQC_V1); + h2c->ktbl_sel1 = cpu_to_le32(val32); + val32 = rtw89_read_rf(rtwdev, RF_PATH_A, RR_CFGCH, RFREG_MASK); + h2c->rfmod0 = cpu_to_le32(val32); + val32 = rtw89_read_rf(rtwdev, RF_PATH_B, RR_CFGCH, RFREG_MASK); + h2c->rfmod1 = cpu_to_le32(val32); + + if (rtw89_is_mlo_1_1(rtwdev)) + h2c->mlo_1_1 = cpu_to_le32(1); + + h2c->rfe_type = cpu_to_le32(rtwdev->efuse.rfe_type); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK, + H2C_FUNC_RFK_PRE_NOTIFY, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, u8 *buf, u16 len, bool rack, bool dack) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 9a2c50c35f2a..d9316b66ab76 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3932,6 +3932,11 @@ enum rtw89_mcc_h2c_func { #define H2C_CL_OUTSRC_RF_REG_B 0x9 #define H2C_CL_OUTSRC_RF_FW_NOTIFY 0xa #define H2C_FUNC_OUTSRC_RF_GET_MCCCH 0x2 +#define H2C_CL_OUTSRC_RF_FW_RFK 0xb + +enum rtw89_rfk_offload_h2c_func { + H2C_FUNC_RFK_PRE_NOTIFY = 0x8, +}; struct rtw89_fw_h2c_rf_get_mccch { __le32 ch_0; @@ -3942,6 +3947,41 @@ struct rtw89_fw_h2c_rf_get_mccch { __le32 current_band_type; } __packed; +#define NUM_OF_RTW89_FW_RFK_PATH 2 +#define NUM_OF_RTW89_FW_RFK_TBL 3 + +struct rtw89_fw_h2c_rfk_pre_info { + struct { + __le32 ch[NUM_OF_RTW89_FW_RFK_PATH][NUM_OF_RTW89_FW_RFK_TBL]; + __le32 band[NUM_OF_RTW89_FW_RFK_PATH][NUM_OF_RTW89_FW_RFK_TBL]; + } __packed dbcc; + + __le32 mlo_mode; + struct { + __le32 cur_ch[NUM_OF_RTW89_FW_RFK_PATH]; + __le32 cur_band[NUM_OF_RTW89_FW_RFK_PATH]; + } __packed tbl; + + __le32 phy_idx; + __le32 cur_band; + __le32 cur_bw; + __le32 cur_center_ch; + + __le32 ktbl_sel0; + __le32 ktbl_sel1; + __le32 rfmod0; + __le32 rfmod1; + + __le32 mlo_1_1; + __le32 rfe_type; + __le32 drv_mode; + + struct { + __le32 ch[NUM_OF_RTW89_FW_RFK_PATH]; + __le32 band[NUM_OF_RTW89_FW_RFK_PATH]; + } __packed mlo; +} __packed; + enum rtw89_rf_log_type { RTW89_RF_RUN_LOG = 0, RTW89_RF_RPT_LOG = 1, @@ -4127,6 +4167,8 @@ int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, struct rtw89_fw_h2c_rf_reg_info *info, u16 len, u8 page); int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev); +int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, u8 *buf, u16 len, bool rack, bool dack); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index bd7bdd216e5f..9f209f068679 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8730,7 +8730,9 @@ #define B_PRT_COM_RXBB_V1 GENMASK(4, 0) #define B_PRT_COM_DONE BIT(0) #define R_COEF_SEL 0x8104 +#define R_COEF_SEL_C1 0x8204 #define B_COEF_SEL_IQC BIT(0) +#define B_COEF_SEL_IQC_V1 GENMASK(1, 0) #define B_COEF_SEL_MDPD BIT(8) #define R_CFIR_SYS 0x8120 #define R_IQK_RES 0x8124 From 9c66da3b19b5526b00bdd9ca2ef20560be21291f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:34 +0800 Subject: [PATCH 213/378] wifi: rtw89: rfk: add H2C command to trigger IQK IQ signal calibration is a basic and important calibration to yield good RF performance. Do this calibration on AP channel if we are going to connect. During scanning phase, it transmits with low data rate, so without IQK RF performance is still acceptable. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 35 +++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 7 +++++ 2 files changed, 42 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 5bd3d08a5d25..d2c166ee5c89 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -4593,6 +4593,41 @@ int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, return ret; } +int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + struct rtw89_h2c_rf_iqk *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c RF IQK\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_rf_iqk *)skb->data; + + h2c->phy_idx = cpu_to_le32(phy_idx); + h2c->dbcc = cpu_to_le32(rtwdev->dbcc_en); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK, + H2C_FUNC_RFK_IQK_OFFLOAD, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, u8 *buf, u16 len, bool rack, bool dack) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index d9316b66ab76..e9c7f9532b0b 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3935,6 +3935,7 @@ enum rtw89_mcc_h2c_func { #define H2C_CL_OUTSRC_RF_FW_RFK 0xb enum rtw89_rfk_offload_h2c_func { + H2C_FUNC_RFK_IQK_OFFLOAD = 0x1, H2C_FUNC_RFK_PRE_NOTIFY = 0x8, }; @@ -3982,6 +3983,11 @@ struct rtw89_fw_h2c_rfk_pre_info { } __packed mlo; } __packed; +struct rtw89_h2c_rf_iqk { + __le32 phy_idx; + __le32 dbcc; +} __packed; + enum rtw89_rf_log_type { RTW89_RF_RUN_LOG = 0, RTW89_RF_RPT_LOG = 1, @@ -4169,6 +4175,7 @@ int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, u8 *buf, u16 len, bool rack, bool dack); From 32919a0438946170bd944c557833186514c399f3 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:35 +0800 Subject: [PATCH 214/378] wifi: rtw89: rfk: add H2C command to trigger RX DCK RX DCK is receiver DC calibration. This will calibrate DC offset to reflect correct received signal strength indicator, so mechanisms like CCA can have normalized values. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 43 +++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 13 ++++++++ 2 files changed, 56 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index d2c166ee5c89..f1239b00479d 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -4628,6 +4628,49 @@ int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return ret; } +int rtw89_fw_h2c_rf_rxdck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, + RTW89_SUB_ENTITY_0); + struct rtw89_h2c_rf_rxdck *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c RF RXDCK\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_rf_rxdck *)skb->data; + + h2c->len = len; + h2c->phy = phy_idx; + h2c->is_afe = false; + h2c->kpath = RF_AB; + h2c->cur_band = chan->band_type; + h2c->cur_bw = chan->band_width; + h2c->cur_ch = chan->channel; + h2c->rxdck_dbg_en = rtw89_debug_is_enabled(rtwdev, RTW89_DBG_RFK); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK, + H2C_FUNC_RFK_RXDCK_OFFLOAD, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, u8 *buf, u16 len, bool rack, bool dack) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index e9c7f9532b0b..7e803abe47c6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3936,6 +3936,7 @@ enum rtw89_mcc_h2c_func { enum rtw89_rfk_offload_h2c_func { H2C_FUNC_RFK_IQK_OFFLOAD = 0x1, + H2C_FUNC_RFK_RXDCK_OFFLOAD = 0x6, H2C_FUNC_RFK_PRE_NOTIFY = 0x8, }; @@ -3988,6 +3989,17 @@ struct rtw89_h2c_rf_iqk { __le32 dbcc; } __packed; +struct rtw89_h2c_rf_rxdck { + u8 len; + u8 phy; + u8 is_afe; + u8 kpath; + u8 cur_band; + u8 cur_bw; + u8 cur_ch; + u8 rxdck_dbg_en; +} __packed; + enum rtw89_rf_log_type { RTW89_RF_RUN_LOG = 0, RTW89_RF_RPT_LOG = 1, @@ -4176,6 +4188,7 @@ int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +int rtw89_fw_h2c_rf_rxdck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, u8 *buf, u16 len, bool rack, bool dack); From b835141be5a94c3f170107c1f5446a4a7b204ac6 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:36 +0800 Subject: [PATCH 215/378] wifi: rtw89: rfk: add H2C command to trigger DPK DPK is short for digital pre-distortion calibration. It can adjusts digital waveform according to PA linear characteristics dynamically to enhance TX EVM. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 43 +++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 13 ++++++++ 2 files changed, 56 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index f1239b00479d..1218bab049c3 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -4628,6 +4628,49 @@ int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return ret; } +int rtw89_fw_h2c_rf_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, + RTW89_SUB_ENTITY_0); + struct rtw89_h2c_rf_dpk *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c RF DPK\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_rf_dpk *)skb->data; + + h2c->len = len; + h2c->phy = phy_idx; + h2c->dpk_enable = true; + h2c->kpath = RF_AB; + h2c->cur_band = chan->band_type; + h2c->cur_bw = chan->band_width; + h2c->cur_ch = chan->channel; + h2c->dpk_dbg_en = rtw89_debug_is_enabled(rtwdev, RTW89_DBG_RFK); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK, + H2C_FUNC_RFK_DPK_OFFLOAD, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + int rtw89_fw_h2c_rf_rxdck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 7e803abe47c6..f48d21a772aa 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3936,6 +3936,7 @@ enum rtw89_mcc_h2c_func { enum rtw89_rfk_offload_h2c_func { H2C_FUNC_RFK_IQK_OFFLOAD = 0x1, + H2C_FUNC_RFK_DPK_OFFLOAD = 0x3, H2C_FUNC_RFK_RXDCK_OFFLOAD = 0x6, H2C_FUNC_RFK_PRE_NOTIFY = 0x8, }; @@ -3989,6 +3990,17 @@ struct rtw89_h2c_rf_iqk { __le32 dbcc; } __packed; +struct rtw89_h2c_rf_dpk { + u8 len; + u8 phy; + u8 dpk_enable; + u8 kpath; + u8 cur_band; + u8 cur_bw; + u8 cur_ch; + u8 dpk_dbg_en; +} __packed; + struct rtw89_h2c_rf_rxdck { u8 len; u8 phy; @@ -4188,6 +4200,7 @@ int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +int rtw89_fw_h2c_rf_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_rxdck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, u8 *buf, u16 len, From 1a0cba5dc983048ec0e13615b97c87a8882dff36 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:37 +0800 Subject: [PATCH 216/378] wifi: rtw89: rfk: add H2C command to trigger DACK DACK (digital-to-analog converters calibration) is used to calibrate DAC to output signals with expected quality. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 36 +++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 8 ++++++ 2 files changed, 44 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 1218bab049c3..5b6693ffa290 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -4671,6 +4671,42 @@ int rtw89_fw_h2c_rf_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return ret; } +int rtw89_fw_h2c_rf_dack(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + struct rtw89_h2c_rf_dack *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c RF DACK\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_rf_dack *)skb->data; + + h2c->len = cpu_to_le32(len); + h2c->phy = cpu_to_le32(phy_idx); + h2c->type = cpu_to_le32(0); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK, + H2C_FUNC_RFK_DACK_OFFLOAD, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + int rtw89_fw_h2c_rf_rxdck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index f48d21a772aa..0fdc62fdd25a 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3937,6 +3937,7 @@ enum rtw89_mcc_h2c_func { enum rtw89_rfk_offload_h2c_func { H2C_FUNC_RFK_IQK_OFFLOAD = 0x1, H2C_FUNC_RFK_DPK_OFFLOAD = 0x3, + H2C_FUNC_RFK_DACK_OFFLOAD = 0x5, H2C_FUNC_RFK_RXDCK_OFFLOAD = 0x6, H2C_FUNC_RFK_PRE_NOTIFY = 0x8, }; @@ -4001,6 +4002,12 @@ struct rtw89_h2c_rf_dpk { u8 dpk_dbg_en; } __packed; +struct rtw89_h2c_rf_dack { + __le32 len; + __le32 phy; + __le32 type; +} __packed; + struct rtw89_h2c_rf_rxdck { u8 len; u8 phy; @@ -4201,6 +4208,7 @@ int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +int rtw89_fw_h2c_rf_dack(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_rxdck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, u8 *buf, u16 len, From af41e89ea323ca48d3ddf1a55b0f632d19ae0ae6 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:38 +0800 Subject: [PATCH 217/378] wifi: rtw89: rfk: add H2C command to trigger TXGAPK TXGAPK stands for TX power gap calibration. Use this calibration to compensate TX power gap to output expected power. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 44 +++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 13 ++++++++ 2 files changed, 57 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 5b6693ffa290..3f5e72209001 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -4671,6 +4671,50 @@ int rtw89_fw_h2c_rf_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return ret; } +int rtw89_fw_h2c_rf_txgapk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, + RTW89_SUB_ENTITY_0); + struct rtw89_hal *hal = &rtwdev->hal; + struct rtw89_h2c_rf_txgapk *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c RF TXGAPK\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_rf_txgapk *)skb->data; + + h2c->len = len; + h2c->ktype = 2; + h2c->phy = phy_idx; + h2c->kpath = RF_AB; + h2c->band = chan->band_type; + h2c->bw = chan->band_width; + h2c->ch = chan->channel; + h2c->cv = hal->cv; + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK, + H2C_FUNC_RFK_TXGAPK_OFFLOAD, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + int rtw89_fw_h2c_rf_dack(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { struct rtw89_h2c_rf_dack *h2c; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 0fdc62fdd25a..666c3e7ec0d0 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3937,6 +3937,7 @@ enum rtw89_mcc_h2c_func { enum rtw89_rfk_offload_h2c_func { H2C_FUNC_RFK_IQK_OFFLOAD = 0x1, H2C_FUNC_RFK_DPK_OFFLOAD = 0x3, + H2C_FUNC_RFK_TXGAPK_OFFLOAD = 0x4, H2C_FUNC_RFK_DACK_OFFLOAD = 0x5, H2C_FUNC_RFK_RXDCK_OFFLOAD = 0x6, H2C_FUNC_RFK_PRE_NOTIFY = 0x8, @@ -4002,6 +4003,17 @@ struct rtw89_h2c_rf_dpk { u8 dpk_dbg_en; } __packed; +struct rtw89_h2c_rf_txgapk { + u8 len; + u8 ktype; + u8 phy; + u8 kpath; + u8 band; + u8 bw; + u8 ch; + u8 cv; +} __packed; + struct rtw89_h2c_rf_dack { __le32 len; __le32 phy; @@ -4208,6 +4220,7 @@ int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +int rtw89_fw_h2c_rf_txgapk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_dack(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_rxdck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, From bd6f5f27cb2c2b0635cc9212fb82e6f112e7e681 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:39 +0800 Subject: [PATCH 218/378] wifi: rtw89: rfk: add H2C command to trigger TSSI TSSI is short for transmitter signal strength indication, which is a close-loop hardware circuit to feedback actual transmitting power as a reference to adjust power for next transmission. When connecting and switching bands or channels, do TSSI calibration and reset hardware status to output expected power. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 6 + drivers/net/wireless/realtek/rtw89/fw.c | 48 ++ drivers/net/wireless/realtek/rtw89/fw.h | 32 ++ drivers/net/wireless/realtek/rtw89/phy.c | 607 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 8 + 5 files changed, 701 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index e049f7b5168b..ccc9f96fc18b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -17,6 +17,7 @@ struct rtw89_pci_info; struct rtw89_mac_gen_def; struct rtw89_phy_gen_def; struct rtw89_efuse_block_cfg; +struct rtw89_h2c_rf_tssi; struct rtw89_fw_txpwr_track_cfg; struct rtw89_phy_rfk_log_fmt; @@ -4471,6 +4472,11 @@ struct rtw89_cfo_tracking_info { u8 lock_cnt; }; +enum rtw89_tssi_mode { + RTW89_TSSI_NORMAL = 0, + RTW89_TSSI_SCAN = 1, +}; + enum rtw89_tssi_alimk_band { TSSI_ALIMK_2G = 0, TSSI_ALIMK_5GL, diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 3f5e72209001..c3f0a79f3de4 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -4593,6 +4593,54 @@ int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, return ret; } +int rtw89_fw_h2c_rf_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + enum rtw89_tssi_mode tssi_mode) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, + RTW89_SUB_ENTITY_0); + struct rtw89_hal *hal = &rtwdev->hal; + struct rtw89_h2c_rf_tssi *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c RF TSSI\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_rf_tssi *)skb->data; + + h2c->len = cpu_to_le16(len); + h2c->phy = phy_idx; + h2c->ch = chan->channel; + h2c->bw = chan->band_width; + h2c->band = chan->band_type; + h2c->hwtx_en = true; + h2c->cv = hal->cv; + h2c->tssi_mode = tssi_mode; + + rtw89_phy_rfk_tssi_fill_fwcmd_efuse_to_de(rtwdev, phy_idx, chan, h2c); + rtw89_phy_rfk_tssi_fill_fwcmd_tmeter_tbl(rtwdev, phy_idx, chan, h2c); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK, + H2C_FUNC_RFK_TSSI_OFFLOAD, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { struct rtw89_h2c_rf_iqk *h2c; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 666c3e7ec0d0..02a7c7b2c8be 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3935,6 +3935,7 @@ enum rtw89_mcc_h2c_func { #define H2C_CL_OUTSRC_RF_FW_RFK 0xb enum rtw89_rfk_offload_h2c_func { + H2C_FUNC_RFK_TSSI_OFFLOAD = 0x0, H2C_FUNC_RFK_IQK_OFFLOAD = 0x1, H2C_FUNC_RFK_DPK_OFFLOAD = 0x3, H2C_FUNC_RFK_TXGAPK_OFFLOAD = 0x4, @@ -3987,6 +3988,35 @@ struct rtw89_fw_h2c_rfk_pre_info { } __packed mlo; } __packed; +struct rtw89_h2c_rf_tssi { + __le16 len; + u8 phy; + u8 ch; + u8 bw; + u8 band; + u8 hwtx_en; + u8 cv; + s8 curr_tssi_cck_de[2]; + s8 curr_tssi_cck_de_20m[2]; + s8 curr_tssi_cck_de_40m[2]; + s8 curr_tssi_efuse_cck_de[2]; + s8 curr_tssi_ofdm_de[2]; + s8 curr_tssi_ofdm_de_20m[2]; + s8 curr_tssi_ofdm_de_40m[2]; + s8 curr_tssi_ofdm_de_80m[2]; + s8 curr_tssi_ofdm_de_160m[2]; + s8 curr_tssi_ofdm_de_320m[2]; + s8 curr_tssi_efuse_ofdm_de[2]; + s8 curr_tssi_ofdm_de_diff_20m[2]; + s8 curr_tssi_ofdm_de_diff_80m[2]; + s8 curr_tssi_ofdm_de_diff_160m[2]; + s8 curr_tssi_ofdm_de_diff_320m[2]; + s8 curr_tssi_trim_de[2]; + u8 pg_thermal[2]; + u8 ftable[2][128]; + u8 tssi_mode; +} __packed; + struct rtw89_h2c_rf_iqk { __le32 phy_idx; __le32 dbcc; @@ -4218,6 +4248,8 @@ int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +int rtw89_fw_h2c_rf_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + enum rtw89_tssi_mode tssi_mode); int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); int rtw89_fw_h2c_rf_txgapk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 3de61c0d7b03..77b3b233697b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2959,6 +2959,613 @@ void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, handler(rtwdev, skb, len); } +static u32 phy_tssi_get_cck_group(u8 ch) +{ + switch (ch) { + case 1 ... 2: + return 0; + case 3 ... 5: + return 1; + case 6 ... 8: + return 2; + case 9 ... 11: + return 3; + case 12 ... 13: + return 4; + case 14: + return 5; + } + + return 0; +} + +#define PHY_TSSI_EXTRA_GROUP_BIT BIT(31) +#define PHY_TSSI_EXTRA_GROUP(idx) (PHY_TSSI_EXTRA_GROUP_BIT | (idx)) +#define PHY_IS_TSSI_EXTRA_GROUP(group) ((group) & PHY_TSSI_EXTRA_GROUP_BIT) +#define PHY_TSSI_EXTRA_GET_GROUP_IDX1(group) \ + ((group) & ~PHY_TSSI_EXTRA_GROUP_BIT) +#define PHY_TSSI_EXTRA_GET_GROUP_IDX2(group) \ + (PHY_TSSI_EXTRA_GET_GROUP_IDX1(group) + 1) + +static u32 phy_tssi_get_ofdm_group(u8 ch) +{ + switch (ch) { + case 1 ... 2: + return 0; + case 3 ... 5: + return 1; + case 6 ... 8: + return 2; + case 9 ... 11: + return 3; + case 12 ... 14: + return 4; + case 36 ... 40: + return 5; + case 41 ... 43: + return PHY_TSSI_EXTRA_GROUP(5); + case 44 ... 48: + return 6; + case 49 ... 51: + return PHY_TSSI_EXTRA_GROUP(6); + case 52 ... 56: + return 7; + case 57 ... 59: + return PHY_TSSI_EXTRA_GROUP(7); + case 60 ... 64: + return 8; + case 100 ... 104: + return 9; + case 105 ... 107: + return PHY_TSSI_EXTRA_GROUP(9); + case 108 ... 112: + return 10; + case 113 ... 115: + return PHY_TSSI_EXTRA_GROUP(10); + case 116 ... 120: + return 11; + case 121 ... 123: + return PHY_TSSI_EXTRA_GROUP(11); + case 124 ... 128: + return 12; + case 129 ... 131: + return PHY_TSSI_EXTRA_GROUP(12); + case 132 ... 136: + return 13; + case 137 ... 139: + return PHY_TSSI_EXTRA_GROUP(13); + case 140 ... 144: + return 14; + case 149 ... 153: + return 15; + case 154 ... 156: + return PHY_TSSI_EXTRA_GROUP(15); + case 157 ... 161: + return 16; + case 162 ... 164: + return PHY_TSSI_EXTRA_GROUP(16); + case 165 ... 169: + return 17; + case 170 ... 172: + return PHY_TSSI_EXTRA_GROUP(17); + case 173 ... 177: + return 18; + } + + return 0; +} + +static u32 phy_tssi_get_6g_ofdm_group(u8 ch) +{ + switch (ch) { + case 1 ... 5: + return 0; + case 6 ... 8: + return PHY_TSSI_EXTRA_GROUP(0); + case 9 ... 13: + return 1; + case 14 ... 16: + return PHY_TSSI_EXTRA_GROUP(1); + case 17 ... 21: + return 2; + case 22 ... 24: + return PHY_TSSI_EXTRA_GROUP(2); + case 25 ... 29: + return 3; + case 33 ... 37: + return 4; + case 38 ... 40: + return PHY_TSSI_EXTRA_GROUP(4); + case 41 ... 45: + return 5; + case 46 ... 48: + return PHY_TSSI_EXTRA_GROUP(5); + case 49 ... 53: + return 6; + case 54 ... 56: + return PHY_TSSI_EXTRA_GROUP(6); + case 57 ... 61: + return 7; + case 65 ... 69: + return 8; + case 70 ... 72: + return PHY_TSSI_EXTRA_GROUP(8); + case 73 ... 77: + return 9; + case 78 ... 80: + return PHY_TSSI_EXTRA_GROUP(9); + case 81 ... 85: + return 10; + case 86 ... 88: + return PHY_TSSI_EXTRA_GROUP(10); + case 89 ... 93: + return 11; + case 97 ... 101: + return 12; + case 102 ... 104: + return PHY_TSSI_EXTRA_GROUP(12); + case 105 ... 109: + return 13; + case 110 ... 112: + return PHY_TSSI_EXTRA_GROUP(13); + case 113 ... 117: + return 14; + case 118 ... 120: + return PHY_TSSI_EXTRA_GROUP(14); + case 121 ... 125: + return 15; + case 129 ... 133: + return 16; + case 134 ... 136: + return PHY_TSSI_EXTRA_GROUP(16); + case 137 ... 141: + return 17; + case 142 ... 144: + return PHY_TSSI_EXTRA_GROUP(17); + case 145 ... 149: + return 18; + case 150 ... 152: + return PHY_TSSI_EXTRA_GROUP(18); + case 153 ... 157: + return 19; + case 161 ... 165: + return 20; + case 166 ... 168: + return PHY_TSSI_EXTRA_GROUP(20); + case 169 ... 173: + return 21; + case 174 ... 176: + return PHY_TSSI_EXTRA_GROUP(21); + case 177 ... 181: + return 22; + case 182 ... 184: + return PHY_TSSI_EXTRA_GROUP(22); + case 185 ... 189: + return 23; + case 193 ... 197: + return 24; + case 198 ... 200: + return PHY_TSSI_EXTRA_GROUP(24); + case 201 ... 205: + return 25; + case 206 ... 208: + return PHY_TSSI_EXTRA_GROUP(25); + case 209 ... 213: + return 26; + case 214 ... 216: + return PHY_TSSI_EXTRA_GROUP(26); + case 217 ... 221: + return 27; + case 225 ... 229: + return 28; + case 230 ... 232: + return PHY_TSSI_EXTRA_GROUP(28); + case 233 ... 237: + return 29; + case 238 ... 240: + return PHY_TSSI_EXTRA_GROUP(29); + case 241 ... 245: + return 30; + case 246 ... 248: + return PHY_TSSI_EXTRA_GROUP(30); + case 249 ... 253: + return 31; + } + + return 0; +} + +static u32 phy_tssi_get_trim_group(u8 ch) +{ + switch (ch) { + case 1 ... 8: + return 0; + case 9 ... 14: + return 1; + case 36 ... 48: + return 2; + case 49 ... 51: + return PHY_TSSI_EXTRA_GROUP(2); + case 52 ... 64: + return 3; + case 100 ... 112: + return 4; + case 113 ... 115: + return PHY_TSSI_EXTRA_GROUP(4); + case 116 ... 128: + return 5; + case 132 ... 144: + return 6; + case 149 ... 177: + return 7; + } + + return 0; +} + +static u32 phy_tssi_get_6g_trim_group(u8 ch) +{ + switch (ch) { + case 1 ... 13: + return 0; + case 14 ... 16: + return PHY_TSSI_EXTRA_GROUP(0); + case 17 ... 29: + return 1; + case 33 ... 45: + return 2; + case 46 ... 48: + return PHY_TSSI_EXTRA_GROUP(2); + case 49 ... 61: + return 3; + case 65 ... 77: + return 4; + case 78 ... 80: + return PHY_TSSI_EXTRA_GROUP(4); + case 81 ... 93: + return 5; + case 97 ... 109: + return 6; + case 110 ... 112: + return PHY_TSSI_EXTRA_GROUP(6); + case 113 ... 125: + return 7; + case 129 ... 141: + return 8; + case 142 ... 144: + return PHY_TSSI_EXTRA_GROUP(8); + case 145 ... 157: + return 9; + case 161 ... 173: + return 10; + case 174 ... 176: + return PHY_TSSI_EXTRA_GROUP(10); + case 177 ... 189: + return 11; + case 193 ... 205: + return 12; + case 206 ... 208: + return PHY_TSSI_EXTRA_GROUP(12); + case 209 ... 221: + return 13; + case 225 ... 237: + return 14; + case 238 ... 240: + return PHY_TSSI_EXTRA_GROUP(14); + case 241 ... 253: + return 15; + } + + return 0; +} + +static s8 phy_tssi_get_ofdm_de(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + const struct rtw89_chan *chan, + enum rtw89_rf_path path) +{ + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + enum rtw89_band band = chan->band_type; + u8 ch = chan->channel; + u32 gidx_1st; + u32 gidx_2nd; + s8 de_1st; + s8 de_2nd; + u32 gidx; + s8 val; + + if (band == RTW89_BAND_6G) + goto calc_6g; + + gidx = phy_tssi_get_ofdm_group(ch); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs group_idx=0x%x\n", + path, gidx); + + if (PHY_IS_TSSI_EXTRA_GROUP(gidx)) { + gidx_1st = PHY_TSSI_EXTRA_GET_GROUP_IDX1(gidx); + gidx_2nd = PHY_TSSI_EXTRA_GET_GROUP_IDX2(gidx); + de_1st = tssi_info->tssi_mcs[path][gidx_1st]; + de_2nd = tssi_info->tssi_mcs[path][gidx_2nd]; + val = (de_1st + de_2nd) / 2; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs de=%d 1st=%d 2nd=%d\n", + path, val, de_1st, de_2nd); + } else { + val = tssi_info->tssi_mcs[path][gidx]; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs de=%d\n", path, val); + } + + return val; + +calc_6g: + gidx = phy_tssi_get_6g_ofdm_group(ch); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs group_idx=0x%x\n", + path, gidx); + + if (PHY_IS_TSSI_EXTRA_GROUP(gidx)) { + gidx_1st = PHY_TSSI_EXTRA_GET_GROUP_IDX1(gidx); + gidx_2nd = PHY_TSSI_EXTRA_GET_GROUP_IDX2(gidx); + de_1st = tssi_info->tssi_6g_mcs[path][gidx_1st]; + de_2nd = tssi_info->tssi_6g_mcs[path][gidx_2nd]; + val = (de_1st + de_2nd) / 2; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs de=%d 1st=%d 2nd=%d\n", + path, val, de_1st, de_2nd); + } else { + val = tssi_info->tssi_6g_mcs[path][gidx]; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs de=%d\n", path, val); + } + + return val; +} + +static s8 phy_tssi_get_ofdm_trim_de(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + const struct rtw89_chan *chan, + enum rtw89_rf_path path) +{ + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + enum rtw89_band band = chan->band_type; + u8 ch = chan->channel; + u32 tgidx_1st; + u32 tgidx_2nd; + s8 tde_1st; + s8 tde_2nd; + u32 tgidx; + s8 val; + + if (band == RTW89_BAND_6G) + goto calc_6g; + + tgidx = phy_tssi_get_trim_group(ch); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs trim_group_idx=0x%x\n", + path, tgidx); + + if (PHY_IS_TSSI_EXTRA_GROUP(tgidx)) { + tgidx_1st = PHY_TSSI_EXTRA_GET_GROUP_IDX1(tgidx); + tgidx_2nd = PHY_TSSI_EXTRA_GET_GROUP_IDX2(tgidx); + tde_1st = tssi_info->tssi_trim[path][tgidx_1st]; + tde_2nd = tssi_info->tssi_trim[path][tgidx_2nd]; + val = (tde_1st + tde_2nd) / 2; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs trim_de=%d 1st=%d 2nd=%d\n", + path, val, tde_1st, tde_2nd); + } else { + val = tssi_info->tssi_trim[path][tgidx]; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs trim_de=%d\n", + path, val); + } + + return val; + +calc_6g: + tgidx = phy_tssi_get_6g_trim_group(ch); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs trim_group_idx=0x%x\n", + path, tgidx); + + if (PHY_IS_TSSI_EXTRA_GROUP(tgidx)) { + tgidx_1st = PHY_TSSI_EXTRA_GET_GROUP_IDX1(tgidx); + tgidx_2nd = PHY_TSSI_EXTRA_GET_GROUP_IDX2(tgidx); + tde_1st = tssi_info->tssi_trim_6g[path][tgidx_1st]; + tde_2nd = tssi_info->tssi_trim_6g[path][tgidx_2nd]; + val = (tde_1st + tde_2nd) / 2; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs trim_de=%d 1st=%d 2nd=%d\n", + path, val, tde_1st, tde_2nd); + } else { + val = tssi_info->tssi_trim_6g[path][tgidx]; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs trim_de=%d\n", + path, val); + } + + return val; +} + +void rtw89_phy_rfk_tssi_fill_fwcmd_efuse_to_de(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + const struct rtw89_chan *chan, + struct rtw89_h2c_rf_tssi *h2c) +{ + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + u8 ch = chan->channel; + s8 trim_de; + s8 ofdm_de; + s8 cck_de; + u8 gidx; + s8 val; + int i; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "[TSSI][TRIM]: phy=%d ch=%d\n", + phy, ch); + + for (i = RF_PATH_A; i <= RF_PATH_B; i++) { + trim_de = phy_tssi_get_ofdm_trim_de(rtwdev, phy, chan, i); + h2c->curr_tssi_trim_de[i] = trim_de; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d trim_de=0x%x\n", i, trim_de); + + gidx = phy_tssi_get_cck_group(ch); + cck_de = tssi_info->tssi_cck[i][gidx]; + val = u32_get_bits(cck_de + trim_de, 0xff); + + h2c->curr_tssi_cck_de[i] = 0x0; + h2c->curr_tssi_cck_de_20m[i] = val; + h2c->curr_tssi_cck_de_40m[i] = val; + h2c->curr_tssi_efuse_cck_de[i] = cck_de; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d cck_de=0x%x\n", i, cck_de); + + ofdm_de = phy_tssi_get_ofdm_de(rtwdev, phy, chan, i); + val = u32_get_bits(ofdm_de + trim_de, 0xff); + + h2c->curr_tssi_ofdm_de[i] = 0x0; + h2c->curr_tssi_ofdm_de_20m[i] = val; + h2c->curr_tssi_ofdm_de_40m[i] = val; + h2c->curr_tssi_ofdm_de_80m[i] = val; + h2c->curr_tssi_ofdm_de_160m[i] = val; + h2c->curr_tssi_ofdm_de_320m[i] = val; + h2c->curr_tssi_efuse_ofdm_de[i] = ofdm_de; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d ofdm_de=0x%x\n", i, ofdm_de); + } +} + +void rtw89_phy_rfk_tssi_fill_fwcmd_tmeter_tbl(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + const struct rtw89_chan *chan, + struct rtw89_h2c_rf_tssi *h2c) +{ + struct rtw89_fw_txpwr_track_cfg *trk = rtwdev->fw.elm_info.txpwr_trk; + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + const s8 *thm_up[RF_PATH_B + 1] = {}; + const s8 *thm_down[RF_PATH_B + 1] = {}; + u8 subband = chan->subband_type; + s8 thm_ofst[128] = {0}; + u8 thermal; + u8 path; + u8 i, j; + + switch (subband) { + default: + case RTW89_CH_2G: + thm_up[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P][0]; + thm_down[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N][0]; + thm_up[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_P][0]; + thm_down[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_N][0]; + break; + case RTW89_CH_5G_BAND_1: + thm_up[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][0]; + thm_down[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][0]; + thm_up[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][0]; + thm_down[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][0]; + break; + case RTW89_CH_5G_BAND_3: + thm_up[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][1]; + thm_down[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][1]; + thm_up[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][1]; + thm_down[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][1]; + break; + case RTW89_CH_5G_BAND_4: + thm_up[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][2]; + thm_down[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][2]; + thm_up[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][2]; + thm_down[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][2]; + break; + case RTW89_CH_6G_BAND_IDX0: + case RTW89_CH_6G_BAND_IDX1: + thm_up[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][0]; + thm_down[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][0]; + thm_up[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][0]; + thm_down[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][0]; + break; + case RTW89_CH_6G_BAND_IDX2: + case RTW89_CH_6G_BAND_IDX3: + thm_up[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][1]; + thm_down[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][1]; + thm_up[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][1]; + thm_down[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][1]; + break; + case RTW89_CH_6G_BAND_IDX4: + case RTW89_CH_6G_BAND_IDX5: + thm_up[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][2]; + thm_down[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][2]; + thm_up[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][2]; + thm_down[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][2]; + break; + case RTW89_CH_6G_BAND_IDX6: + case RTW89_CH_6G_BAND_IDX7: + thm_up[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][3]; + thm_down[RF_PATH_A] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][3]; + thm_up[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][3]; + thm_down[RF_PATH_B] = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][3]; + break; + } + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] tmeter tbl on subband: %u\n", subband); + + for (path = RF_PATH_A; path <= RF_PATH_B; path++) { + thermal = tssi_info->thermal[path]; + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "path: %u, pg thermal: 0x%x\n", path, thermal); + + if (thermal == 0xff) { + h2c->pg_thermal[path] = 0x38; + memset(h2c->ftable[path], 0, sizeof(h2c->ftable[path])); + continue; + } + + h2c->pg_thermal[path] = thermal; + + i = 0; + for (j = 0; j < 64; j++) + thm_ofst[j] = i < DELTA_SWINGIDX_SIZE ? + thm_up[path][i++] : + thm_up[path][DELTA_SWINGIDX_SIZE - 1]; + + i = 1; + for (j = 127; j >= 64; j--) + thm_ofst[j] = i < DELTA_SWINGIDX_SIZE ? + -thm_down[path][i++] : + -thm_down[path][DELTA_SWINGIDX_SIZE - 1]; + + for (i = 0; i < 128; i += 4) { + h2c->ftable[path][i + 0] = thm_ofst[i + 3]; + h2c->ftable[path][i + 1] = thm_ofst[i + 2]; + h2c->ftable[path][i + 2] = thm_ofst[i + 1]; + h2c->ftable[path][i + 3] = thm_ofst[i + 0]; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "thm ofst [%x]: %02x %02x %02x %02x\n", + i, thm_ofst[i], thm_ofst[i + 1], + thm_ofst[i + 2], thm_ofst[i + 3]); + } + } +} + static u8 rtw89_phy_cfo_get_xcap_reg(struct rtw89_dev *rtwdev, bool sc_xo) { const struct rtw89_xtal_info *xtal = rtwdev->chip->xtal_info; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index df915cb0833f..459e919ddd24 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -888,6 +888,14 @@ void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev); int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, unsigned int ms); +void rtw89_phy_rfk_tssi_fill_fwcmd_efuse_to_de(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + const struct rtw89_chan *chan, + struct rtw89_h2c_rf_tssi *h2c); +void rtw89_phy_rfk_tssi_fill_fwcmd_tmeter_tbl(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + const struct rtw89_chan *chan, + struct rtw89_h2c_rf_tssi *h2c); void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev); void rtw89_phy_cfo_track_work(struct work_struct *work); void rtw89_phy_cfo_parse(struct rtw89_dev *rtwdev, s16 cfo_val, From ff146ec22d5fe136b71b31703b1bea540ffc4d5f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:40 +0800 Subject: [PATCH 219/378] wifi: rtw89: 8922a: rfk: implement chip_ops to call RF calibrations Calling RF calibrations when interface up, connection, switching bands and going to scan. For 8922AE, RF calibrations are moved to firmware, so use H2C commands to trigger RF calibrations and wait for a C2H event to indicate completion. Then, do next RF calibration. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.h | 1 + drivers/net/wireless/realtek/rtw89/phy.c | 115 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 25 +++- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 69 +++++++++++ 4 files changed, 207 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 46e25c6f88a6..08121fd899e6 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -23,6 +23,7 @@ enum btc_wl_rfk_type { BTC_WRFKT_DACK = 4, BTC_WRFKT_RXDCK = 5, BTC_WRFKT_TSSI = 6, + BTC_WRFKT_CHLK = 7, }; #define NM_EXEC false diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 77b3b233697b..f02b365b0cec 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2834,6 +2834,7 @@ void (* const rtw89_phy_c2h_rfk_log_handler[])(struct rtw89_dev *rtwdev, [RTW89_PHY_C2H_RFK_LOG_FUNC_TXGAPK] = rtw89_phy_c2h_rfk_log_txgapk, }; +static void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev) { struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait; @@ -2843,6 +2844,7 @@ void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev) reinit_completion(&wait->completion); } +static int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, unsigned int ms) { @@ -2959,6 +2961,119 @@ void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, handler(rtwdev, skb, len); } +int rtw89_phy_rfk_pre_ntfy_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms) +{ + int ret; + + rtw89_phy_rfk_report_prep(rtwdev); + + ret = rtw89_fw_h2c_rf_pre_ntfy(rtwdev, phy_idx); + if (ret) + return ret; + + return rtw89_phy_rfk_report_wait(rtwdev, "PRE_NTFY", ms); +} +EXPORT_SYMBOL(rtw89_phy_rfk_pre_ntfy_and_wait); + +int rtw89_phy_rfk_tssi_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + enum rtw89_tssi_mode tssi_mode, + unsigned int ms) +{ + int ret; + + rtw89_phy_rfk_report_prep(rtwdev); + + ret = rtw89_fw_h2c_rf_tssi(rtwdev, phy_idx, tssi_mode); + if (ret) + return ret; + + return rtw89_phy_rfk_report_wait(rtwdev, "TSSI", ms); +} +EXPORT_SYMBOL(rtw89_phy_rfk_tssi_and_wait); + +int rtw89_phy_rfk_iqk_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms) +{ + int ret; + + rtw89_phy_rfk_report_prep(rtwdev); + + ret = rtw89_fw_h2c_rf_iqk(rtwdev, phy_idx); + if (ret) + return ret; + + return rtw89_phy_rfk_report_wait(rtwdev, "IQK", ms); +} +EXPORT_SYMBOL(rtw89_phy_rfk_iqk_and_wait); + +int rtw89_phy_rfk_dpk_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms) +{ + int ret; + + rtw89_phy_rfk_report_prep(rtwdev); + + ret = rtw89_fw_h2c_rf_dpk(rtwdev, phy_idx); + if (ret) + return ret; + + return rtw89_phy_rfk_report_wait(rtwdev, "DPK", ms); +} +EXPORT_SYMBOL(rtw89_phy_rfk_dpk_and_wait); + +int rtw89_phy_rfk_txgapk_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms) +{ + int ret; + + rtw89_phy_rfk_report_prep(rtwdev); + + ret = rtw89_fw_h2c_rf_txgapk(rtwdev, phy_idx); + if (ret) + return ret; + + return rtw89_phy_rfk_report_wait(rtwdev, "TXGAPK", ms); +} +EXPORT_SYMBOL(rtw89_phy_rfk_txgapk_and_wait); + +int rtw89_phy_rfk_dack_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms) +{ + int ret; + + rtw89_phy_rfk_report_prep(rtwdev); + + ret = rtw89_fw_h2c_rf_dack(rtwdev, phy_idx); + if (ret) + return ret; + + return rtw89_phy_rfk_report_wait(rtwdev, "DACK", ms); +} +EXPORT_SYMBOL(rtw89_phy_rfk_dack_and_wait); + +int rtw89_phy_rfk_rxdck_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms) +{ + int ret; + + rtw89_phy_rfk_report_prep(rtwdev); + + ret = rtw89_fw_h2c_rf_rxdck(rtwdev, phy_idx); + if (ret) + return ret; + + return rtw89_phy_rfk_report_wait(rtwdev, "RX_DCK", ms); +} +EXPORT_SYMBOL(rtw89_phy_rfk_rxdck_and_wait); + static u32 phy_tssi_get_cck_group(u8 ch) { switch (ch) { diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 459e919ddd24..d80ddc723e86 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -885,9 +885,28 @@ void rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev, bool rtw89_phy_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func); void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func); -void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev); -int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, - unsigned int ms); +int rtw89_phy_rfk_pre_ntfy_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms); +int rtw89_phy_rfk_tssi_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + enum rtw89_tssi_mode tssi_mode, + unsigned int ms); +int rtw89_phy_rfk_iqk_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms); +int rtw89_phy_rfk_dpk_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms); +int rtw89_phy_rfk_txgapk_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms); +int rtw89_phy_rfk_dack_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms); +int rtw89_phy_rfk_rxdck_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + unsigned int ms); void rtw89_phy_rfk_tssi_fill_fwcmd_efuse_to_de(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, const struct rtw89_chan *chan, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index aefad3f2e612..69ae8f81181e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2,6 +2,7 @@ /* Copyright(c) 2023 Realtek Corporation */ +#include "coex.h" #include "debug.h" #include "efuse.h" #include "fw.h" @@ -1369,6 +1370,69 @@ void rtw8922a_hal_reset(struct rtw89_dev *rtwdev, } } +static void rtw8922a_rfk_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; + + rtwdev->is_tssi_mode[RF_PATH_A] = false; + rtwdev->is_tssi_mode[RF_PATH_B] = false; + memset(rfk_mcc, 0, sizeof(*rfk_mcc)); +} + +static void _wait_rx_mode(struct rtw89_dev *rtwdev, u8 kpath) +{ + u32 rf_mode; + u8 path; + int ret; + + for (path = 0; path < RF_PATH_NUM_8922A; path++) { + if (!(kpath & BIT(path))) + continue; + + ret = read_poll_timeout_atomic(rtw89_read_rf, rf_mode, rf_mode != 2, + 2, 5000, false, rtwdev, path, 0x00, + RR_MOD_MASK); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK] Wait S%d to Rx mode!! (ret = %d)\n", + path, ret); + } +} + +static void rtw8922a_rfk_channel(struct rtw89_dev *rtwdev) +{ + enum rtw89_phy_idx phy_idx = RTW89_PHY_0; + u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, RF_AB); + u32 tx_en; + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_START); + rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); + _wait_rx_mode(rtwdev, RF_AB); + + rtw89_phy_rfk_pre_ntfy_and_wait(rtwdev, phy_idx, 5); + rtw89_phy_rfk_txgapk_and_wait(rtwdev, phy_idx, 54); + rtw89_phy_rfk_iqk_and_wait(rtwdev, phy_idx, 84); + rtw89_phy_rfk_tssi_and_wait(rtwdev, phy_idx, RTW89_TSSI_NORMAL, 6); + rtw89_phy_rfk_dpk_and_wait(rtwdev, phy_idx, 34); + rtw89_phy_rfk_rxdck_and_wait(rtwdev, RTW89_PHY_0, 32); + + rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_STOP); +} + +static void rtw8922a_rfk_band_changed(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + rtw89_phy_rfk_tssi_and_wait(rtwdev, phy_idx, RTW89_TSSI_SCAN, 6); +} + +static void rtw8922a_rfk_scan(struct rtw89_dev *rtwdev, bool start) +{ +} + +static void rtw8922a_rfk_track(struct rtw89_dev *rtwdev) +{ +} + static void rtw8922a_set_txpwr_ref(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { @@ -1622,6 +1686,11 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .read_phycap = rtw8922a_read_phycap, .fem_setup = NULL, .rfe_gpio = NULL, + .rfk_init = rtw8922a_rfk_init, + .rfk_channel = rtw8922a_rfk_channel, + .rfk_band_changed = rtw8922a_rfk_band_changed, + .rfk_scan = rtw8922a_rfk_scan, + .rfk_track = rtw8922a_rfk_track, .power_trim = rtw8922a_power_trim, .set_txpwr = rtw8922a_set_txpwr, .set_txpwr_ctrl = rtw8922a_set_txpwr_ctrl, From 7e2629dc843fb46f0b8b3aba44708b508f6f98cf Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:41 +0800 Subject: [PATCH 220/378] wifi: rtw89: 8922a: add chip_ops::rfk_init_late to do initial RF calibrations later The RF calibrations are moved into firmware, so we trigger calibrations by H2C commands and wait for C2H completion events. However, these events can be received only after HCI (i.e. PCI for now) starts, so we should do initial RF calibrations after that. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 1 + drivers/net/wireless/realtek/rtw89/core.h | 9 +++++++++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 9 +++++++++ 7 files changed, 23 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index c6c3e0c0bf84..61a216464b6d 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4101,6 +4101,7 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) set_bit(RTW89_FLAG_RUNNING, rtwdev->flags); + rtw89_chip_rfk_init_late(rtwdev); rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON); rtw89_fw_h2c_fw_log(rtwdev, rtwdev->fw.log.enable); rtw89_fw_h2c_init_ba_cam(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ccc9f96fc18b..270403f6c3d5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3157,6 +3157,7 @@ struct rtw89_chip_ops { void (*fem_setup)(struct rtw89_dev *rtwdev); void (*rfe_gpio)(struct rtw89_dev *rtwdev); void (*rfk_init)(struct rtw89_dev *rtwdev); + void (*rfk_init_late)(struct rtw89_dev *rtwdev); void (*rfk_channel)(struct rtw89_dev *rtwdev); void (*rfk_band_changed)(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); @@ -5642,6 +5643,14 @@ static inline void rtw89_chip_rfk_init(struct rtw89_dev *rtwdev) chip->ops->rfk_init(rtwdev); } +static inline void rtw89_chip_rfk_init_late(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (chip->ops->rfk_init_late) + chip->ops->rfk_init_late(rtwdev); +} + static inline void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 09b23c56aa8e..09e38713bca7 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2311,6 +2311,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = { .fem_setup = NULL, .rfe_gpio = rtw8851b_rfe_gpio, .rfk_init = rtw8851b_rfk_init, + .rfk_init_late = NULL, .rfk_channel = rtw8851b_rfk_channel, .rfk_band_changed = rtw8851b_rfk_band_changed, .rfk_scan = rtw8851b_rfk_scan, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index c28f05bbdccf..01c249dddfb8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2055,6 +2055,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .fem_setup = rtw8852a_fem_setup, .rfe_gpio = NULL, .rfk_init = rtw8852a_rfk_init, + .rfk_init_late = NULL, .rfk_channel = rtw8852a_rfk_channel, .rfk_band_changed = rtw8852a_rfk_band_changed, .rfk_scan = rtw8852a_rfk_scan, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 18ed372ed5cd..fb6ad335a37a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -2480,6 +2480,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .fem_setup = NULL, .rfe_gpio = NULL, .rfk_init = rtw8852b_rfk_init, + .rfk_init_late = NULL, .rfk_channel = rtw8852b_rfk_channel, .rfk_band_changed = rtw8852b_rfk_band_changed, .rfk_scan = rtw8852b_rfk_scan, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 431acfaba89b..00861c328ca0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2825,6 +2825,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .fem_setup = NULL, .rfe_gpio = NULL, .rfk_init = rtw8852c_rfk_init, + .rfk_init_late = NULL, .rfk_channel = rtw8852c_rfk_channel, .rfk_band_changed = rtw8852c_rfk_band_changed, .rfk_scan = rtw8852c_rfk_scan, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 69ae8f81181e..0cbe4780eb69 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1379,6 +1379,14 @@ static void rtw8922a_rfk_init(struct rtw89_dev *rtwdev) memset(rfk_mcc, 0, sizeof(*rfk_mcc)); } +static void rtw8922a_rfk_init_late(struct rtw89_dev *rtwdev) +{ + rtw89_phy_rfk_pre_ntfy_and_wait(rtwdev, RTW89_PHY_0, 5); + + rtw89_phy_rfk_dack_and_wait(rtwdev, RTW89_PHY_0, 58); + rtw89_phy_rfk_rxdck_and_wait(rtwdev, RTW89_PHY_0, 32); +} + static void _wait_rx_mode(struct rtw89_dev *rtwdev, u8 kpath) { u32 rf_mode; @@ -1687,6 +1695,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .fem_setup = NULL, .rfe_gpio = NULL, .rfk_init = rtw8922a_rfk_init, + .rfk_init_late = rtw8922a_rfk_init_late, .rfk_channel = rtw8922a_rfk_channel, .rfk_band_changed = rtw8922a_rfk_band_changed, .rfk_scan = rtw8922a_rfk_scan, From 4dbd964f33aab6f99891b9610ad4b36cc215be0d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 2 Feb 2024 11:06:42 +0800 Subject: [PATCH 221/378] wifi: rtw89: 8922a: add chip_ops::rfk_hw_init Add a chip_ops for WiFi 7 chips to set additional RF configurations including MLO and PLL settings. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240202030642.108385-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 9 + drivers/net/wireless/realtek/rtw89/mac.h | 2 + drivers/net/wireless/realtek/rtw89/phy.c | 1 + drivers/net/wireless/realtek/rtw89/reg.h | 6 + drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + .../net/wireless/realtek/rtw89/rtw8922a_rfk.c | 202 ++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8922a_rfk.h | 1 + 11 files changed, 226 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 270403f6c3d5..22255e98ab6b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3156,6 +3156,7 @@ struct rtw89_chip_ops { int (*read_phycap)(struct rtw89_dev *rtwdev, u8 *phycap_map); void (*fem_setup)(struct rtw89_dev *rtwdev); void (*rfe_gpio)(struct rtw89_dev *rtwdev); + void (*rfk_hw_init)(struct rtw89_dev *rtwdev); void (*rfk_init)(struct rtw89_dev *rtwdev); void (*rfk_init_late)(struct rtw89_dev *rtwdev); void (*rfk_channel)(struct rtw89_dev *rtwdev); @@ -5604,6 +5605,14 @@ static inline void rtw89_chip_rfe_gpio(struct rtw89_dev *rtwdev) chip->ops->rfe_gpio(rtwdev); } +static inline void rtw89_chip_rfk_hw_init(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (chip->ops->rfk_hw_init) + chip->ops->rfk_hw_init(rtwdev); +} + static inline void rtw89_chip_bb_preinit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index b3fe4cab6d3a..7aea57804e93 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1328,6 +1328,7 @@ enum rtw89_mac_xtal_si_offset { #define XTAL_SI_BIG_PWR_CUT BIT(1) XTAL_SI_XTAL_DRV = 0x15, #define XTAL_SI_DRV_LATCH BIT(4) + XTAL_SI_XTAL_PLL = 0x16, XTAL_SI_XTAL_XMD_2 = 0x24, #define XTAL_SI_LDO_LPS GENMASK(6, 4) XTAL_SI_XTAL_XMD_4 = 0x26, @@ -1361,6 +1362,7 @@ enum rtw89_mac_xtal_si_offset { XTAL_SI_SRAM_CTRL = 0xA1, #define XTAL_SI_SRAM_DIS BIT(1) #define FULL_BIT_MASK GENMASK(7, 0) + XTAL_SI_APBT = 0xD1, XTAL_SI_PLL = 0xE0, XTAL_SI_PLL_1 = 0xE1, }; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index f02b365b0cec..9a8f5b764617 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5874,6 +5874,7 @@ void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) rtw89_chip_rfe_gpio(rtwdev); rtw89_phy_antdiv_set_ant(rtwdev); + rtw89_chip_rfk_hw_init(rtwdev); rtw89_phy_init_rf_nctl(rtwdev); rtw89_chip_rfk_init(rtwdev); rtw89_chip_set_txpwr_ctrl(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 9f209f068679..6368b2b32c0c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -7402,6 +7402,7 @@ #define RR_MOD_M_RXBB GENMASK(9, 5) #define RR_MOD_LO_SEL BIT(1) #define RR_MODOPT 0x01 +#define RR_TXG_SEL GENMASK(19, 17) #define RR_MODOPT_M_TXPWR GENMASK(5, 0) #define RR_WLSEL 0x02 #define RR_WLSEL_AG GENMASK(18, 16) @@ -7594,6 +7595,7 @@ #define RR_MIXER_GN GENMASK(4, 3) #define RR_POW 0xa0 #define RR_POW_SYN GENMASK(3, 2) +#define RR_POW_SYN_V1 GENMASK(3, 0) #define RR_LOGEN 0xa3 #define RR_LOGEN_RPT GENMASK(19, 16) #define RR_SX 0xaf @@ -8734,6 +8736,8 @@ #define B_COEF_SEL_IQC BIT(0) #define B_COEF_SEL_IQC_V1 GENMASK(1, 0) #define B_COEF_SEL_MDPD BIT(8) +#define B_COEF_SEL_MDPD_V1 GENMASK(9, 8) +#define B_COEF_SEL_EN BIT(31) #define R_CFIR_SYS 0x8120 #define R_IQK_RES 0x8124 #define B_IQK_RES_K BIT(28) @@ -8755,8 +8759,10 @@ #define B_RFGAIN_BND GENMASK(4, 0) #define R_CFIR_MAP 0x8150 #define R_CFIR_LUT 0x8154 +#define R_CFIR_LUT_C1 0x8254 #define B_CFIR_LUT_SEL BIT(8) #define B_CFIR_LUT_SET BIT(4) +#define B_CFIR_LUT_G5 BIT(5) #define B_CFIR_LUT_G3 BIT(3) #define B_CFIR_LUT_G2 BIT(2) #define B_CFIR_LUT_GP_V1 GENMASK(2, 0) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 09e38713bca7..83db0a686ee2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2310,6 +2310,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = { .read_phycap = rtw8851b_read_phycap, .fem_setup = NULL, .rfe_gpio = rtw8851b_rfe_gpio, + .rfk_hw_init = NULL, .rfk_init = rtw8851b_rfk_init, .rfk_init_late = NULL, .rfk_channel = rtw8851b_rfk_channel, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 01c249dddfb8..8e808ded5d52 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2054,6 +2054,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .read_phycap = rtw8852a_read_phycap, .fem_setup = rtw8852a_fem_setup, .rfe_gpio = NULL, + .rfk_hw_init = NULL, .rfk_init = rtw8852a_rfk_init, .rfk_init_late = NULL, .rfk_channel = rtw8852a_rfk_channel, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index fb6ad335a37a..19454766f3de 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -2479,6 +2479,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .read_phycap = rtw8852b_read_phycap, .fem_setup = NULL, .rfe_gpio = NULL, + .rfk_hw_init = NULL, .rfk_init = rtw8852b_rfk_init, .rfk_init_late = NULL, .rfk_channel = rtw8852b_rfk_channel, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 00861c328ca0..ca8547fbd70e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2824,6 +2824,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .read_phycap = rtw8852c_read_phycap, .fem_setup = NULL, .rfe_gpio = NULL, + .rfk_hw_init = NULL, .rfk_init = rtw8852c_rfk_init, .rfk_init_late = NULL, .rfk_channel = rtw8852c_rfk_channel, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 0cbe4780eb69..6dc051934b0f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1694,6 +1694,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .read_phycap = rtw8922a_read_phycap, .fem_setup = NULL, .rfe_gpio = NULL, + .rfk_hw_init = rtw8922a_rfk_hw_init, .rfk_init = rtw8922a_rfk_init, .rfk_init_late = rtw8922a_rfk_init_late, .rfk_channel = rtw8922a_rfk_channel, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c index e0e8048db739..d8ef986e7877 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c @@ -2,7 +2,9 @@ /* Copyright(c) 2023 Realtek Corporation */ +#include "chan.h" #include "debug.h" +#include "mac.h" #include "phy.h" #include "reg.h" #include "rtw8922a.h" @@ -31,3 +33,203 @@ void rtw8922a_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx) rtw8922a_tssi_cont_en(rtwdev, en, RF_PATH_B); } } + +enum _rf_syn_pow { + RF_SYN_ON_OFF, + RF_SYN_OFF_ON, + RF_SYN_ALLON, + RF_SYN_ALLOFF, +}; + +static void rtw8922a_set_syn01_cav(struct rtw89_dev *rtwdev, enum _rf_syn_pow syn) +{ + if (syn == RF_SYN_ALLON) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x3); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x2); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x3); + + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN, 0x3); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN, 0x2); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN, 0x3); + } else if (syn == RF_SYN_ON_OFF) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x3); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x2); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x3); + + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN, 0x0); + } else if (syn == RF_SYN_OFF_ON) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x0); + + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN, 0x3); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN, 0x2); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN, 0x3); + } else if (syn == RF_SYN_ALLOFF) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN, 0x0); + } +} + +static void rtw8922a_set_syn01_cbv(struct rtw89_dev *rtwdev, enum _rf_syn_pow syn) +{ + if (syn == RF_SYN_ALLON) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN_V1, 0xf); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN_V1, 0xf); + } else if (syn == RF_SYN_ON_OFF) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN_V1, 0xf); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN_V1, 0x0); + } else if (syn == RF_SYN_OFF_ON) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN_V1, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN_V1, 0xf); + } else if (syn == RF_SYN_ALLOFF) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN_V1, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN_V1, 0x0); + } +} + +static void rtw8922a_set_syn01(struct rtw89_dev *rtwdev, enum _rf_syn_pow syn) +{ + struct rtw89_hal *hal = &rtwdev->hal; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "SYN config=%d\n", syn); + + if (hal->cv == CHIP_CAV) + rtw8922a_set_syn01_cav(rtwdev, syn); + else + rtw8922a_set_syn01_cbv(rtwdev, syn); +} + +static void rtw8922a_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx) +{ + u32 tmp; + + if (idx > 2) { + rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx); + return; + } + + if (kpath & RF_A) { + rtw89_phy_write32_mask(rtwdev, R_COEF_SEL, B_COEF_SEL_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_COEF_SEL, B_COEF_SEL_IQC_V1, idx); + rtw89_phy_write32_mask(rtwdev, R_COEF_SEL, B_COEF_SEL_MDPD_V1, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TXG_SEL, 0x4 | idx); + + tmp = rtw89_phy_read32_mask(rtwdev, R_COEF_SEL, BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G3, tmp); + tmp = rtw89_phy_read32_mask(rtwdev, R_COEF_SEL, BIT(1)); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G5, tmp); + } + + if (kpath & RF_B) { + rtw89_phy_write32_mask(rtwdev, R_COEF_SEL_C1, B_COEF_SEL_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_COEF_SEL_C1, B_COEF_SEL_IQC_V1, idx); + rtw89_phy_write32_mask(rtwdev, R_COEF_SEL_C1, B_COEF_SEL_MDPD_V1, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TXG_SEL, 0x4 | idx); + + tmp = rtw89_phy_read32_mask(rtwdev, R_COEF_SEL_C1, BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT_C1, B_CFIR_LUT_G3, tmp); + tmp = rtw89_phy_read32_mask(rtwdev, R_COEF_SEL_C1, BIT(1)); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT_C1, B_CFIR_LUT_G5, tmp); + } +} + +static void rtw8922a_chlk_reload(struct rtw89_dev *rtwdev) +{ + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; + enum rtw89_sub_entity_idx sub_entity_idx; + const struct rtw89_chan *chan; + enum rtw89_entity_mode mode; + u8 s0_tbl, s1_tbl; + u8 tbl_sel; + + mode = rtw89_get_entity_mode(rtwdev); + switch (mode) { + case RTW89_ENTITY_MODE_MCC_PREPARE: + sub_entity_idx = RTW89_SUB_ENTITY_1; + tbl_sel = 1; + break; + default: + sub_entity_idx = RTW89_SUB_ENTITY_0; + tbl_sel = 0; + break; + } + + chan = rtw89_chan_get(rtwdev, sub_entity_idx); + + rfk_mcc->ch[tbl_sel] = chan->channel; + rfk_mcc->band[tbl_sel] = chan->band_type; + rfk_mcc->bw[tbl_sel] = chan->band_width; + rfk_mcc->table_idx = tbl_sel; + + s0_tbl = tbl_sel; + s1_tbl = tbl_sel; + + rtw8922a_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); + rtw8922a_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); +} + +static void rtw8922a_rfk_mlo_ctrl(struct rtw89_dev *rtwdev) +{ + enum _rf_syn_pow syn_pow; + + if (!rtwdev->dbcc_en) + goto set_rfk_reload; + + switch (rtwdev->mlo_dbcc_mode) { + case MLO_0_PLUS_2_1RF: + syn_pow = RF_SYN_OFF_ON; + break; + case MLO_0_PLUS_2_2RF: + case MLO_1_PLUS_1_2RF: + case MLO_2_PLUS_0_1RF: + case MLO_2_PLUS_0_2RF: + case MLO_2_PLUS_2_2RF: + case MLO_DBCC_NOT_SUPPORT: + default: + syn_pow = RF_SYN_ON_OFF; + break; + case MLO_1_PLUS_1_1RF: + case DBCC_LEGACY: + syn_pow = RF_SYN_ALLON; + break; + } + + rtw8922a_set_syn01(rtwdev, syn_pow); + +set_rfk_reload: + rtw8922a_chlk_reload(rtwdev); +} + +static void rtw8922a_rfk_pll_init(struct rtw89_dev *rtwdev) +{ + int ret; + u8 tmp; + + ret = rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_PLL_1, &tmp); + if (ret) + return; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_PLL_1, tmp | 0xf8, 0xFF); + if (ret) + return; + + ret = rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_APBT, &tmp); + if (ret) + return; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_APBT, tmp & ~0x60, 0xFF); + if (ret) + return; + + ret = rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_XTAL_PLL, &tmp); + if (ret) + return; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_XTAL_PLL, tmp | 0x38, 0xFF); + if (ret) + return; +} + +void rtw8922a_rfk_hw_init(struct rtw89_dev *rtwdev) +{ + if (rtwdev->dbcc_en) + rtw8922a_rfk_mlo_ctrl(rtwdev); + + rtw8922a_rfk_pll_init(rtwdev); +} diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h index fbd22de269e2..de5fa6c74530 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h @@ -8,5 +8,6 @@ #include "core.h" void rtw8922a_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx); +void rtw8922a_rfk_hw_init(struct rtw89_dev *rtwdev); #endif From dedf78efd2885048ca36bc17fbd4e1c0af33f2ad Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 4 Feb 2024 09:26:24 +0800 Subject: [PATCH 222/378] wifi: rtw89: fw: consider checksum length of security data The newer firmware file provides security data with checksum, so we need to consider the length. Otherwise it will fail to validate total firmware length resulting in failed to probe. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240204012627.9647-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 3 +++ drivers/net/wireless/realtek/rtw89/fw.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index c3f0a79f3de4..4a2081131c3e 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -177,6 +177,7 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le u32 i; info->section_num = le32_get_bits(fw_hdr->w6, FW_HDR_V1_W6_SEC_NUM); + info->dsp_checksum = le32_get_bits(fw_hdr->w6, FW_HDR_V1_W6_DSP_CHKSUM); base_hdr_len = struct_size(fw_hdr, sections, info->section_num); info->dynamic_hdr_en = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_DYN_HDR); @@ -205,6 +206,8 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le section_info->mssc = le32_get_bits(section->w2, FWSECTION_HDR_V1_W2_MSSC); mssc_len += section_info->mssc * FWDL_SECURITY_SIGLEN; + if (info->dsp_checksum) + mssc_len += section_info->mssc * FWDL_SECURITY_CHKSUM_LEN; } else { section_info->mssc = 0; } diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 02a7c7b2c8be..b12f93e39d1e 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -237,6 +237,7 @@ struct rtw89_fw_bin_info { u32 hdr_len; bool dynamic_hdr_en; u32 dynamic_hdr_len; + bool dsp_checksum; struct rtw89_fw_hdr_section_info section_info[FWDL_SECTION_MAX_NUM]; }; @@ -466,6 +467,7 @@ static inline void RTW89_SET_EDCA_PARAM(void *cmd, u32 val) #define FWDL_SECURITY_SECTION_TYPE 9 #define FWDL_SECURITY_SIGLEN 512 +#define FWDL_SECURITY_CHKSUM_LEN 8 struct rtw89_fw_dynhdr_sec { __le32 w0; @@ -568,6 +570,7 @@ struct rtw89_fw_hdr_v1 { #define FW_HDR_V1_W5_YEAR GENMASK(15, 0) #define FW_HDR_V1_W5_HDR_SIZE GENMASK(31, 16) #define FW_HDR_V1_W6_SEC_NUM GENMASK(15, 8) +#define FW_HDR_V1_W6_DSP_CHKSUM BIT(24) #define FW_HDR_V1_W7_DYN_HDR BIT(16) static inline void SET_FW_HDR_PART_SIZE(void *fwhdr, u32 val) From 5462b8505f538b00d287a6de9a0fb2be6059bfc4 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 4 Feb 2024 09:26:25 +0800 Subject: [PATCH 223/378] wifi: rtw89: fw: read firmware secure information from efuse To support firmware secure boot, read secure information from efuse to know if current hardware module can support secure boot with certain cryptography method. This information should be prepared before downloading firmware, so read efuse right after power on at probe stage. The secure information includes secure cryptography method and secure key index that are used to choose proper key material when downloading firmware. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240204012627.9647-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 15 ++ drivers/net/wireless/realtek/rtw89/efuse.h | 1 + drivers/net/wireless/realtek/rtw89/efuse_be.c | 142 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/pci.c | 2 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 3 + 5 files changed, 163 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 22255e98ab6b..713383b6d818 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4033,6 +4033,19 @@ struct rtw89_fw_elm_info { struct rtw89_phy_rfk_log_fmt *rfk_log_fmt; }; +enum rtw89_fw_mss_dev_type { + RTW89_FW_MSS_DEV_TYPE_FWSEC_DEF = 0xF, + RTW89_FW_MSS_DEV_TYPE_FWSEC_INV = 0xFF, +}; + +struct rtw89_fw_secure { + bool secure_boot; + u32 sb_sel_mgn; + u8 mss_dev_type; + u8 mss_cust_idx; + u8 mss_key_num; +}; + struct rtw89_fw_info { struct rtw89_fw_req_info req; int fw_format; @@ -4047,6 +4060,7 @@ struct rtw89_fw_info { struct rtw89_fw_log log; u32 feature_map; struct rtw89_fw_elm_info elm_info; + struct rtw89_fw_secure sec; }; #define RTW89_CHK_FW_FEATURE(_feat, _fw) \ @@ -4199,6 +4213,7 @@ enum rtw89_flags { RTW89_FLAG_CMAC1_FUNC, RTW89_FLAG_FW_RDY, RTW89_FLAG_RUNNING, + RTW89_FLAG_PROBE_DONE, RTW89_FLAG_BFEE_MON, RTW89_FLAG_BFEE_EN, RTW89_FLAG_BFEE_TIMER_KEEP, diff --git a/drivers/net/wireless/realtek/rtw89/efuse.h b/drivers/net/wireless/realtek/rtw89/efuse.h index 5c6787179bad..72416f56a071 100644 --- a/drivers/net/wireless/realtek/rtw89/efuse.h +++ b/drivers/net/wireless/realtek/rtw89/efuse.h @@ -23,5 +23,6 @@ int rtw89_parse_efuse_map_be(struct rtw89_dev *rtwdev); int rtw89_parse_phycap_map_be(struct rtw89_dev *rtwdev); int rtw89_cnv_efuse_state_be(struct rtw89_dev *rtwdev, bool idle); int rtw89_read_efuse_ver(struct rtw89_dev *rtwdev, u8 *efv); +int rtw89_efuse_read_fw_secure_be(struct rtw89_dev *rtwdev); #endif diff --git a/drivers/net/wireless/realtek/rtw89/efuse_be.c b/drivers/net/wireless/realtek/rtw89/efuse_be.c index 8e8b7cd315f7..0be26d5fdf7c 100644 --- a/drivers/net/wireless/realtek/rtw89/efuse_be.c +++ b/drivers/net/wireless/realtek/rtw89/efuse_be.c @@ -7,6 +7,31 @@ #include "mac.h" #include "reg.h" +#define EFUSE_EXTERNALPN_ADDR_BE 0x1580 +#define EFUSE_B1_MSSDEVTYPE_MASK GENMASK(3, 0) +#define EFUSE_B1_MSSCUSTIDX0_MASK GENMASK(7, 4) +#define EFUSE_SERIALNUM_ADDR_BE 0x1581 +#define EFUSE_B2_MSSKEYNUM_MASK GENMASK(3, 0) +#define EFUSE_B2_MSSCUSTIDX1_MASK BIT(6) +#define EFUSE_SB_CRYP_SEL_ADDR 0x1582 +#define EFUSE_SB_CRYP_SEL_SIZE 2 +#define EFUSE_SB_CRYP_SEL_DEFAULT 0xFFFF +#define SB_SEL_MGN_MAX_SIZE 2 +#define EFUSE_SEC_BE_START 0x1580 +#define EFUSE_SEC_BE_SIZE 4 + +enum rtw89_efuse_mss_dev_type { + MSS_DEV_TYPE_FWSEC_DEF = 0xF, + MSS_DEV_TYPE_FWSEC_WINLIN_INBOX = 0xC, + MSS_DEV_TYPE_FWSEC_NONLIN_INBOX_NON_COB = 0xA, + MSS_DEV_TYPE_FWSEC_NONLIN_INBOX_COB = 0x9, + MSS_DEV_TYPE_FWSEC_NONWIN_INBOX = 0x6, +}; + +static const u32 sb_sel_mgn[SB_SEL_MGN_MAX_SIZE] = { + 0x8000100, 0xC000180 +}; + static void rtw89_enable_efuse_pwr_cut_ddv_be(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; @@ -418,3 +443,120 @@ int rtw89_parse_phycap_map_be(struct rtw89_dev *rtwdev) return ret; } + +static u16 get_sb_cryp_sel_idx(u16 sb_cryp_sel) +{ + u8 low_bit, high_bit, cnt_zero = 0; + u8 idx, sel_form_v, sel_idx_v; + u16 sb_cryp_sel_v = 0x0; + + sel_form_v = u16_get_bits(sb_cryp_sel, MASKBYTE0); + sel_idx_v = u16_get_bits(sb_cryp_sel, MASKBYTE1); + + for (idx = 0; idx < 4; idx++) { + low_bit = !!(sel_form_v & BIT(idx)); + high_bit = !!(sel_form_v & BIT(7 - idx)); + if (low_bit != high_bit) + return U16_MAX; + if (low_bit) + continue; + + cnt_zero++; + if (cnt_zero == 1) + sb_cryp_sel_v = idx * 16; + else if (cnt_zero > 1) + return U16_MAX; + } + + low_bit = u8_get_bits(sel_idx_v, 0x0F); + high_bit = u8_get_bits(sel_idx_v, 0xF0); + + if ((low_bit ^ high_bit) != 0xF) + return U16_MAX; + + return sb_cryp_sel_v + low_bit; +} + +static u8 get_mss_dev_type_idx(struct rtw89_dev *rtwdev, u8 mss_dev_type) +{ + switch (mss_dev_type) { + case MSS_DEV_TYPE_FWSEC_WINLIN_INBOX: + mss_dev_type = 0x0; + break; + case MSS_DEV_TYPE_FWSEC_NONLIN_INBOX_NON_COB: + mss_dev_type = 0x1; + break; + case MSS_DEV_TYPE_FWSEC_NONLIN_INBOX_COB: + mss_dev_type = 0x2; + break; + case MSS_DEV_TYPE_FWSEC_NONWIN_INBOX: + mss_dev_type = 0x3; + break; + case MSS_DEV_TYPE_FWSEC_DEF: + mss_dev_type = RTW89_FW_MSS_DEV_TYPE_FWSEC_DEF; + break; + default: + rtw89_warn(rtwdev, "unknown mss_dev_type %d", mss_dev_type); + mss_dev_type = RTW89_FW_MSS_DEV_TYPE_FWSEC_INV; + break; + } + + return mss_dev_type; +} + +int rtw89_efuse_read_fw_secure_be(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_secure *sec = &rtwdev->fw.sec; + u32 sec_addr = EFUSE_SEC_BE_START; + u32 sec_size = EFUSE_SEC_BE_SIZE; + u16 sb_cryp_sel, sb_cryp_sel_idx; + u8 sec_map[EFUSE_SEC_BE_SIZE]; + u8 mss_dev_type; + u8 b1, b2; + int ret; + + ret = rtw89_dump_physical_efuse_map_be(rtwdev, sec_map, + sec_addr, sec_size, false); + if (ret) { + rtw89_warn(rtwdev, "failed to dump secsel map\n"); + return ret; + } + + sb_cryp_sel = sec_map[EFUSE_SB_CRYP_SEL_ADDR - sec_addr] | + sec_map[EFUSE_SB_CRYP_SEL_ADDR - sec_addr + 1] << 8; + if (sb_cryp_sel == EFUSE_SB_CRYP_SEL_DEFAULT) + goto out; + + sb_cryp_sel_idx = get_sb_cryp_sel_idx(sb_cryp_sel); + if (sb_cryp_sel_idx >= SB_SEL_MGN_MAX_SIZE) { + rtw89_warn(rtwdev, "invalid SB cryp sel idx %d\n", sb_cryp_sel_idx); + goto out; + } + + sec->sb_sel_mgn = sb_sel_mgn[sb_cryp_sel_idx]; + + b1 = sec_map[EFUSE_EXTERNALPN_ADDR_BE - sec_addr]; + b2 = sec_map[EFUSE_SERIALNUM_ADDR_BE - sec_addr]; + + mss_dev_type = u8_get_bits(b1, EFUSE_B1_MSSDEVTYPE_MASK); + sec->mss_cust_idx = 0x1F - (u8_get_bits(b1, EFUSE_B1_MSSCUSTIDX0_MASK) | + u8_get_bits(b2, EFUSE_B2_MSSCUSTIDX1_MASK) << 4); + sec->mss_key_num = 0xF - u8_get_bits(b2, EFUSE_B2_MSSKEYNUM_MASK); + + sec->mss_dev_type = get_mss_dev_type_idx(rtwdev, mss_dev_type); + if (sec->mss_dev_type == RTW89_FW_MSS_DEV_TYPE_FWSEC_INV) { + rtw89_warn(rtwdev, "invalid mss_dev_type %d\n", mss_dev_type); + goto out; + } + + sec->secure_boot = true; + +out: + rtw89_debug(rtwdev, RTW89_DBG_FW, + "MSS secure_boot=%d dev_type=%d cust_idx=%d key_num=%d\n", + sec->secure_boot, sec->mss_dev_type, sec->mss_cust_idx, + sec->mss_key_num); + + return 0; +} +EXPORT_SYMBOL(rtw89_efuse_read_fw_secure_be); diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index b51ec3cbc715..67d7294e488a 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4180,6 +4180,8 @@ int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_free_irq; } + set_bit(RTW89_FLAG_PROBE_DONE, rtwdev->flags); + return 0; err_free_irq: diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 6dc051934b0f..02ec4d27011f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -377,6 +377,9 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_BE_FEN_RST_ENABLE, B_BE_FEN_BB_IP_RSTN | B_BE_FEN_BBPLAT_RSTB); + if (!test_bit(RTW89_FLAG_PROBE_DONE, rtwdev->flags)) + rtw89_efuse_read_fw_secure_be(rtwdev); + return 0; } From 12ff5e1cca33b32fae0b183203f5b58a610e137e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 4 Feb 2024 09:26:26 +0800 Subject: [PATCH 224/378] wifi: rtw89: fw: parse secure section from firmware file A firmware file can contains more than one section with secure type, so use secure information from efuse to choose the one with matched cryptography method. Then choose key data from MSS poll (multiple security section pool; see below picture) according to key_index from efuse. Note that the size of MSS pool isn't included in section size defined in firmware header, so we also need to parse header of MSS pool to get its size as shift to parse next section. If secure boot isn't supported by current hardware, the first secure section will be adopted, and no need additional process to key data. +---------------------------+ | firmware header | | | | +-----------------------+ | | | section type/size * N-|-|-------+ | | ... | | | | +-----------------------+ | | +---------------------------+ | : : | +---------------------------+ -\ | | secure section type (ID:9)| | | | | | <--+ | | | +---------------------------+ -/ |MSS Pool for above section | | | | | +---------------------------+ Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240204012627.9647-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 202 ++++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/fw.h | 39 +++++ 2 files changed, 227 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 4a2081131c3e..00a6cb7fcd2a 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -13,6 +13,8 @@ #include "reg.h" #include "util.h" +static const u8 mss_signature[] = {0x4D, 0x53, 0x53, 0x4B, 0x50, 0x4F, 0x4F, 0x4C}; + union rtw89_fw_element_arg { size_t offset; enum rtw89_rf_path rf_path; @@ -163,6 +165,161 @@ static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 le return 0; } +static int __get_mssc_key_idx(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mss_pool_hdr *mss_hdr, + u32 rmp_tbl_size, u32 *key_idx) +{ + struct rtw89_fw_secure *sec = &rtwdev->fw.sec; + u32 sel_byte_idx; + u32 mss_sel_idx; + u8 sel_bit_idx; + int i; + + if (sec->mss_dev_type == RTW89_FW_MSS_DEV_TYPE_FWSEC_DEF) { + if (!mss_hdr->defen) + return -ENOENT; + + mss_sel_idx = sec->mss_cust_idx * le16_to_cpu(mss_hdr->msskey_num_max) + + sec->mss_key_num; + } else { + if (mss_hdr->defen) + mss_sel_idx = FWDL_MSS_POOL_DEFKEYSETS_SIZE << 3; + else + mss_sel_idx = 0; + mss_sel_idx += sec->mss_dev_type * le16_to_cpu(mss_hdr->msskey_num_max) * + le16_to_cpu(mss_hdr->msscust_max) + + sec->mss_cust_idx * le16_to_cpu(mss_hdr->msskey_num_max) + + sec->mss_key_num; + } + + sel_byte_idx = mss_sel_idx >> 3; + sel_bit_idx = mss_sel_idx & 0x7; + + if (sel_byte_idx >= rmp_tbl_size) + return -EFAULT; + + if (!(mss_hdr->rmp_tbl[sel_byte_idx] & BIT(sel_bit_idx))) + return -ENOENT; + + *key_idx = hweight8(mss_hdr->rmp_tbl[sel_byte_idx] & (BIT(sel_bit_idx) - 1)); + + for (i = 0; i < sel_byte_idx; i++) + *key_idx += hweight8(mss_hdr->rmp_tbl[i]); + + return 0; +} + +static int __parse_formatted_mssc(struct rtw89_dev *rtwdev, + struct rtw89_fw_bin_info *info, + struct rtw89_fw_hdr_section_info *section_info, + const struct rtw89_fw_hdr_section_v1 *section, + const void *content, + u32 *mssc_len) +{ + const struct rtw89_fw_mss_pool_hdr *mss_hdr = content + section_info->len; + const union rtw89_fw_section_mssc_content *section_content = content; + struct rtw89_fw_secure *sec = &rtwdev->fw.sec; + u32 rmp_tbl_size; + u32 key_sign_len; + u32 real_key_idx; + u32 sb_sel_ver; + int ret; + + if (memcmp(mss_signature, mss_hdr->signature, sizeof(mss_signature)) != 0) { + rtw89_err(rtwdev, "[ERR] wrong MSS signature\n"); + return -ENOENT; + } + + if (mss_hdr->rmpfmt == MSS_POOL_RMP_TBL_BITMASK) { + rmp_tbl_size = (le16_to_cpu(mss_hdr->msskey_num_max) * + le16_to_cpu(mss_hdr->msscust_max) * + mss_hdr->mssdev_max) >> 3; + if (mss_hdr->defen) + rmp_tbl_size += FWDL_MSS_POOL_DEFKEYSETS_SIZE; + } else { + rtw89_err(rtwdev, "[ERR] MSS Key Pool Remap Table Format Unsupport:%X\n", + mss_hdr->rmpfmt); + return -EINVAL; + } + + if (rmp_tbl_size + sizeof(*mss_hdr) != le32_to_cpu(mss_hdr->key_raw_offset)) { + rtw89_err(rtwdev, "[ERR] MSS Key Pool Format Error:0x%X + 0x%X != 0x%X\n", + rmp_tbl_size, (int)sizeof(*mss_hdr), + le32_to_cpu(mss_hdr->key_raw_offset)); + return -EINVAL; + } + + key_sign_len = le16_to_cpu(section_content->key_sign_len.v) >> 2; + if (!key_sign_len) + key_sign_len = 512; + + if (info->dsp_checksum) + key_sign_len += FWDL_SECURITY_CHKSUM_LEN; + + *mssc_len = sizeof(*mss_hdr) + rmp_tbl_size + + le16_to_cpu(mss_hdr->keypair_num) * key_sign_len; + + if (!sec->secure_boot) + goto out; + + sb_sel_ver = le32_to_cpu(section_content->sb_sel_ver.v); + if (sb_sel_ver && sb_sel_ver != sec->sb_sel_mgn) + goto ignore; + + ret = __get_mssc_key_idx(rtwdev, mss_hdr, rmp_tbl_size, &real_key_idx); + if (ret) + goto ignore; + + section_info->key_addr = content + section_info->len + + le32_to_cpu(mss_hdr->key_raw_offset) + + key_sign_len * real_key_idx; + section_info->key_len = key_sign_len; + section_info->key_idx = real_key_idx; + +out: + if (info->secure_section_exist) { + section_info->ignore = true; + return 0; + } + + info->secure_section_exist = true; + + return 0; + +ignore: + section_info->ignore = true; + + return 0; +} + +static int __parse_security_section(struct rtw89_dev *rtwdev, + struct rtw89_fw_bin_info *info, + struct rtw89_fw_hdr_section_info *section_info, + const struct rtw89_fw_hdr_section_v1 *section, + const void *content, + u32 *mssc_len) +{ + int ret; + + section_info->mssc = + le32_get_bits(section->w2, FWSECTION_HDR_V1_W2_MSSC); + + if (section_info->mssc == FORMATTED_MSSC) { + ret = __parse_formatted_mssc(rtwdev, info, section_info, + section, content, mssc_len); + if (ret) + return -EINVAL; + } else { + *mssc_len = section_info->mssc * FWDL_SECURITY_SIGLEN; + if (info->dsp_checksum) + *mssc_len += section_info->mssc * FWDL_SECURITY_CHKSUM_LEN; + + info->secure_section_exist = true; + } + + return 0; +} + static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 len, struct rtw89_fw_bin_info *info) { @@ -173,7 +330,8 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le const u8 *fw_end = fw + len; const u8 *bin; u32 base_hdr_len; - u32 mssc_len = 0; + u32 mssc_len; + int ret; u32 i; info->section_num = le32_get_bits(fw_hdr->w6, FW_HDR_V1_W6_SEC_NUM); @@ -200,18 +358,9 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le section_info = info->section_info; for (i = 0; i < info->section_num; i++) { section = &fw_hdr->sections[i]; + section_info->type = le32_get_bits(section->w1, FWSECTION_HDR_V1_W1_SECTIONTYPE); - if (section_info->type == FWDL_SECURITY_SECTION_TYPE) { - section_info->mssc = - le32_get_bits(section->w2, FWSECTION_HDR_V1_W2_MSSC); - mssc_len += section_info->mssc * FWDL_SECURITY_SIGLEN; - if (info->dsp_checksum) - mssc_len += section_info->mssc * FWDL_SECURITY_CHKSUM_LEN; - } else { - section_info->mssc = 0; - } - section_info->len = le32_get_bits(section->w1, FWSECTION_HDR_V1_W1_SEC_SIZE); if (le32_get_bits(section->w1, FWSECTION_HDR_V1_W1_CHECKSUM)) @@ -220,15 +369,40 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le section_info->dladdr = le32_get_bits(section->w0, FWSECTION_HDR_V1_W0_DL_ADDR); section_info->addr = bin; - bin += section_info->len; + + if (section_info->type == FWDL_SECURITY_SECTION_TYPE) { + ret = __parse_security_section(rtwdev, info, section_info, + section, bin, &mssc_len); + if (ret) + return ret; + } else { + section_info->mssc = 0; + mssc_len = 0; + } + + rtw89_debug(rtwdev, RTW89_DBG_FW, + "section[%d] type=%d len=0x%-6x mssc=%d mssc_len=%d addr=%tx\n", + i, section_info->type, section_info->len, + section_info->mssc, mssc_len, bin - fw); + rtw89_debug(rtwdev, RTW89_DBG_FW, + " ignore=%d key_addr=%p (0x%tx) key_len=%d key_idx=%d\n", + section_info->ignore, section_info->key_addr, + section_info->key_addr ? + section_info->key_addr - section_info->addr : 0, + section_info->key_len, section_info->key_idx); + + bin += section_info->len + mssc_len; section_info++; } - if (fw_end != bin + mssc_len) { + if (fw_end != bin) { rtw89_err(rtwdev, "[ERR]fw bin size\n"); return -EINVAL; } + if (!info->secure_section_exist) + rtw89_warn(rtwdev, "no firmware secure section\n"); + return 0; } @@ -1106,7 +1280,7 @@ static int rtw89_fw_download_suit(struct rtw89_dev *rtwdev, struct rtw89_fw_suit *fw_suit) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; - struct rtw89_fw_bin_info info; + struct rtw89_fw_bin_info info = {}; int ret; ret = rtw89_fw_hdr_parser(rtwdev, fw_suit, &info); diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index b12f93e39d1e..58dbaf7a11e7 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -230,6 +230,10 @@ struct rtw89_fw_hdr_section_info { u32 dladdr; u32 mssc; u8 type; + bool ignore; + const u8 *key_addr; + u32 key_len; + u32 key_idx; }; struct rtw89_fw_bin_info { @@ -238,6 +242,7 @@ struct rtw89_fw_bin_info { bool dynamic_hdr_en; u32 dynamic_hdr_len; bool dsp_checksum; + bool secure_section_exist; struct rtw89_fw_hdr_section_info section_info[FWDL_SECTION_MAX_NUM]; }; @@ -538,6 +543,7 @@ struct rtw89_fw_hdr_section_v1 { #define FWSECTION_HDR_V1_W1_CHECKSUM BIT(28) #define FWSECTION_HDR_V1_W1_REDL BIT(29) #define FWSECTION_HDR_V1_W2_MSSC GENMASK(7, 0) +#define FORMATTED_MSSC 0xFF #define FWSECTION_HDR_V1_W2_BBMCU_IDX GENMASK(27, 24) struct rtw89_fw_hdr_v1 { @@ -578,6 +584,39 @@ static inline void SET_FW_HDR_PART_SIZE(void *fwhdr, u32 val) le32p_replace_bits((__le32 *)fwhdr + 7, val, GENMASK(15, 0)); } +enum rtw89_fw_mss_pool_rmp_tbl_type { + MSS_POOL_RMP_TBL_BITMASK = 0x0, + MSS_POOL_RMP_TBL_RECORD = 0x1, +}; + +#define FWDL_MSS_POOL_DEFKEYSETS_SIZE 8 + +struct rtw89_fw_mss_pool_hdr { + u8 signature[8]; /* equal to mss_signature[] */ + __le32 rmp_tbl_offset; + __le32 key_raw_offset; + u8 defen; + u8 rsvd[3]; + u8 rmpfmt; /* enum rtw89_fw_mss_pool_rmp_tbl_type */ + u8 mssdev_max; + __le16 keypair_num; + __le16 msscust_max; + __le16 msskey_num_max; + __le32 rsvd3; + u8 rmp_tbl[]; +} __packed; + +union rtw89_fw_section_mssc_content { + struct { + u8 pad[58]; + __le32 v; + } __packed sb_sel_ver; + struct { + u8 pad[60]; + __le16 v; + } __packed key_sign_len; +} __packed; + static inline void SET_CTRL_INFO_MACID(void *table, u32 val) { le32p_replace_bits((__le32 *)(table) + 0, val, GENMASK(6, 0)); From 43f8a4dc40a70b3598dd0aae401dddaf63ca0d5b Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 4 Feb 2024 09:26:27 +0800 Subject: [PATCH 225/378] wifi: rtw89: fw: download firmware with key data for secure boot Since firmware header contains multiple secure sections, we need to trim ignored sections, and then download firmware header with single one secure section. For secure boot, when downloading secure section, copy security key data from MSS poll by key_idx read from efuse. If non-secure boot, no need this extra copy. +---------------------------+ -\ | firmware header | | | | | | +-----------------------+ | | only preserve single one secure | | section type/size * N | | | section | | ... | | | | +-----------------------+ | | +---------------------------+ -/ : : +---------------------------+ -\ | secure section type (ID:9)| | | | | +----|-> [ security key data ] | | | +---------------------------+ -/ | |MSS Pool for above section | | | [ security key data 0 ] | +----|- [ security key data 1 ] | by key_idx | [ security key data 2 ] | | ... | +---------------------------+ Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240204012627.9647-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 95 +++++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/fw.h | 7 +- 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 00a6cb7fcd2a..51072a2dcf10 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1098,9 +1098,56 @@ static void rtw89_h2c_pkt_set_hdr_fwdl(struct rtw89_dev *rtwdev, len + H2C_HEADER_LEN)); } -static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, const u8 *fw, u32 len) +static u32 __rtw89_fw_download_tweak_hdr_v0(struct rtw89_dev *rtwdev, + struct rtw89_fw_bin_info *info, + struct rtw89_fw_hdr *fw_hdr) { + le32p_replace_bits(&fw_hdr->w7, FWDL_SECTION_PER_PKT_LEN, + FW_HDR_W7_PART_SIZE); + + return 0; +} + +static u32 __rtw89_fw_download_tweak_hdr_v1(struct rtw89_dev *rtwdev, + struct rtw89_fw_bin_info *info, + struct rtw89_fw_hdr_v1 *fw_hdr) +{ + struct rtw89_fw_hdr_section_info *section_info; + struct rtw89_fw_hdr_section_v1 *section; + u8 dst_sec_idx = 0; + u8 sec_idx; + + le32p_replace_bits(&fw_hdr->w7, FWDL_SECTION_PER_PKT_LEN, + FW_HDR_V1_W7_PART_SIZE); + + for (sec_idx = 0; sec_idx < info->section_num; sec_idx++) { + section_info = &info->section_info[sec_idx]; + section = &fw_hdr->sections[sec_idx]; + + if (section_info->ignore) + continue; + + if (dst_sec_idx != sec_idx) + fw_hdr->sections[dst_sec_idx] = *section; + + dst_sec_idx++; + } + + le32p_replace_bits(&fw_hdr->w6, dst_sec_idx, FW_HDR_V1_W6_SEC_NUM); + + return (info->section_num - dst_sec_idx) * sizeof(*section); +} + +static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, + const struct rtw89_fw_suit *fw_suit, + struct rtw89_fw_bin_info *info) +{ + u32 len = info->hdr_len - info->dynamic_hdr_len; + struct rtw89_fw_hdr_v1 *fw_hdr_v1; + const u8 *fw = fw_suit->data; + struct rtw89_fw_hdr *fw_hdr; struct sk_buff *skb; + u32 truncated; u32 ret = 0; skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); @@ -1110,7 +1157,26 @@ static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, const u8 *fw, u32 l } skb_put_data(skb, fw, len); - SET_FW_HDR_PART_SIZE(skb->data, FWDL_SECTION_PER_PKT_LEN); + + switch (fw_suit->hdr_ver) { + case 0: + fw_hdr = (struct rtw89_fw_hdr *)skb->data; + truncated = __rtw89_fw_download_tweak_hdr_v0(rtwdev, info, fw_hdr); + break; + case 1: + fw_hdr_v1 = (struct rtw89_fw_hdr_v1 *)skb->data; + truncated = __rtw89_fw_download_tweak_hdr_v1(rtwdev, info, fw_hdr_v1); + break; + default: + ret = -EOPNOTSUPP; + goto fail; + } + + if (truncated) { + len -= truncated; + skb_trim(skb, len); + } + rtw89_h2c_pkt_set_hdr_fwdl(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_FWDL, H2C_FUNC_MAC_FWHDR_DL, len); @@ -1129,12 +1195,14 @@ static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, const u8 *fw, u32 l return ret; } -static int rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, const u8 *fw, u32 len) +static int rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, + const struct rtw89_fw_suit *fw_suit, + struct rtw89_fw_bin_info *info) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; int ret; - ret = __rtw89_fw_download_hdr(rtwdev, fw, len); + ret = __rtw89_fw_download_hdr(rtwdev, fw_suit, info); if (ret) { rtw89_err(rtwdev, "[ERR]FW header download\n"); return ret; @@ -1158,9 +1226,21 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev, struct sk_buff *skb; const u8 *section = info->addr; u32 residue_len = info->len; + bool copy_key = false; u32 pkt_len; int ret; + if (info->ignore) + return 0; + + if (info->key_addr && info->key_len) { + if (info->len > FWDL_SECTION_PER_PKT_LEN || info->len < info->key_len) + rtw89_warn(rtwdev, "ignore to copy key data because of len %d, %d, %d\n", + info->len, FWDL_SECTION_PER_PKT_LEN, info->key_len); + else + copy_key = true; + } + while (residue_len) { if (residue_len >= FWDL_SECTION_PER_PKT_LEN) pkt_len = FWDL_SECTION_PER_PKT_LEN; @@ -1174,6 +1254,10 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev, } skb_put_data(skb, section, pkt_len); + if (copy_key) + memcpy(skb->data + pkt_len - info->key_len, + info->key_addr, info->key_len); + ret = rtw89_h2c_tx(rtwdev, skb, true); if (ret) { rtw89_err(rtwdev, "failed to send h2c\n"); @@ -1299,8 +1383,7 @@ static int rtw89_fw_download_suit(struct rtw89_dev *rtwdev, return ret; } - ret = rtw89_fw_download_hdr(rtwdev, fw_suit->data, info.hdr_len - - info.dynamic_hdr_len); + ret = rtw89_fw_download_hdr(rtwdev, fw_suit, &info); if (ret) return ret; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 58dbaf7a11e7..5609e5f7d7eb 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -526,6 +526,7 @@ struct rtw89_fw_hdr { #define FW_HDR_W4_MIN GENMASK(31, 24) #define FW_HDR_W5_YEAR GENMASK(31, 0) #define FW_HDR_W6_SEC_NUM GENMASK(15, 8) +#define FW_HDR_W7_PART_SIZE GENMASK(15, 0) #define FW_HDR_W7_DYN_HDR BIT(16) #define FW_HDR_W7_CMD_VERSERION GENMASK(31, 24) @@ -577,13 +578,9 @@ struct rtw89_fw_hdr_v1 { #define FW_HDR_V1_W5_HDR_SIZE GENMASK(31, 16) #define FW_HDR_V1_W6_SEC_NUM GENMASK(15, 8) #define FW_HDR_V1_W6_DSP_CHKSUM BIT(24) +#define FW_HDR_V1_W7_PART_SIZE GENMASK(15, 0) #define FW_HDR_V1_W7_DYN_HDR BIT(16) -static inline void SET_FW_HDR_PART_SIZE(void *fwhdr, u32 val) -{ - le32p_replace_bits((__le32 *)fwhdr + 7, val, GENMASK(15, 0)); -} - enum rtw89_fw_mss_pool_rmp_tbl_type { MSS_POOL_RMP_TBL_BITMASK = 0x0, MSS_POOL_RMP_TBL_RECORD = 0x1, From b8cfb7c819dd39965136a66fe3a7fde688d976fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Fri, 2 Feb 2024 17:42:13 +0100 Subject: [PATCH 226/378] wifi: wfx: fix memory leak when starting AP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kmemleak reported this error: unreferenced object 0xd73d1180 (size 184): comm "wpa_supplicant", pid 1559, jiffies 13006305 (age 964.245s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 1e 00 01 00 00 00 00 00 ................ backtrace: [<5ca11420>] kmem_cache_alloc+0x20c/0x5ac [<127bdd74>] __alloc_skb+0x144/0x170 [] __netdev_alloc_skb+0x50/0x180 [<0f9fa1d5>] __ieee80211_beacon_get+0x290/0x4d4 [mac80211] [<7accd02d>] ieee80211_beacon_get_tim+0x54/0x18c [mac80211] [<41e25cc3>] wfx_start_ap+0xc8/0x234 [wfx] [<93a70356>] ieee80211_start_ap+0x404/0x6b4 [mac80211] [] nl80211_start_ap+0x76c/0x9e0 [cfg80211] [<47bd8b68>] genl_rcv_msg+0x198/0x378 [<453ef796>] netlink_rcv_skb+0xd0/0x130 [<6b7c977a>] genl_rcv+0x34/0x44 [<66b2d04d>] netlink_unicast+0x1b4/0x258 [] netlink_sendmsg+0x1e8/0x428 [] ____sys_sendmsg+0x1e0/0x274 [] ___sys_sendmsg+0x80/0xb4 [<69954f45>] __sys_sendmsg+0x64/0xa8 unreferenced object 0xce087000 (size 1024): comm "wpa_supplicant", pid 1559, jiffies 13006305 (age 964.246s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 10 00 07 40 00 00 00 00 00 00 00 00 00 00 00 00 ...@............ backtrace: [<9a993714>] __kmalloc_track_caller+0x230/0x600 [] kmalloc_reserve.constprop.0+0x30/0x74 [] __alloc_skb+0xa0/0x170 [] __netdev_alloc_skb+0x50/0x180 [<0f9fa1d5>] __ieee80211_beacon_get+0x290/0x4d4 [mac80211] [<7accd02d>] ieee80211_beacon_get_tim+0x54/0x18c [mac80211] [<41e25cc3>] wfx_start_ap+0xc8/0x234 [wfx] [<93a70356>] ieee80211_start_ap+0x404/0x6b4 [mac80211] [] nl80211_start_ap+0x76c/0x9e0 [cfg80211] [<47bd8b68>] genl_rcv_msg+0x198/0x378 [<453ef796>] netlink_rcv_skb+0xd0/0x130 [<6b7c977a>] genl_rcv+0x34/0x44 [<66b2d04d>] netlink_unicast+0x1b4/0x258 [] netlink_sendmsg+0x1e8/0x428 [] ____sys_sendmsg+0x1e0/0x274 [] ___sys_sendmsg+0x80/0xb4 However, since the kernel is build optimized, it seems the stack is not accurate. It appears the issue is related to wfx_set_mfp_ap(). The issue is obvious in this function: memory allocated by ieee80211_beacon_get() is never released. Fixing this leak makes kmemleak happy. Reported-by: Ulrich Mohr Co-developed-by: Ulrich Mohr Signed-off-by: Ulrich Mohr Fixes: 268bceec1684 ("staging: wfx: fix BA when device is AP and MFP is enabled") Signed-off-by: Jérôme Pouiller Signed-off-by: Kalle Valo Link: https://msgid.link/20240202164213.1606145-1-jerome.pouiller@silabs.com --- drivers/net/wireless/silabs/wfx/sta.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/silabs/wfx/sta.c b/drivers/net/wireless/silabs/wfx/sta.c index 537caf9d914a..bb4446b88c12 100644 --- a/drivers/net/wireless/silabs/wfx/sta.c +++ b/drivers/net/wireless/silabs/wfx/sta.c @@ -344,6 +344,7 @@ static int wfx_set_mfp_ap(struct wfx_vif *wvif) const int pairwise_cipher_suite_count_offset = 8 / sizeof(u16); const int pairwise_cipher_suite_size = 4 / sizeof(u16); const int akm_suite_size = 4 / sizeof(u16); + int ret = -EINVAL; const u16 *ptr; if (unlikely(!skb)) @@ -352,22 +353,26 @@ static int wfx_set_mfp_ap(struct wfx_vif *wvif) ptr = (u16 *)cfg80211_find_ie(WLAN_EID_RSN, skb->data + ieoffset, skb->len - ieoffset); if (unlikely(!ptr)) - return -EINVAL; + goto free_skb; ptr += pairwise_cipher_suite_count_offset; if (WARN_ON(ptr > (u16 *)skb_tail_pointer(skb))) - return -EINVAL; + goto free_skb; ptr += 1 + pairwise_cipher_suite_size * *ptr; if (WARN_ON(ptr > (u16 *)skb_tail_pointer(skb))) - return -EINVAL; + goto free_skb; ptr += 1 + akm_suite_size * *ptr; if (WARN_ON(ptr > (u16 *)skb_tail_pointer(skb))) - return -EINVAL; + goto free_skb; wfx_hif_set_mfp(wvif, *ptr & BIT(7), *ptr & BIT(6)); - return 0; + ret = 0; + +free_skb: + dev_kfree_skb(skb); + return ret; } int wfx_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, From 78092e68557b9d062b8c4058be609254c804f14b Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sun, 4 Feb 2024 17:44:21 -0300 Subject: [PATCH 227/378] ssb: make ssb_bustype const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the driver core can properly handle constant struct bus_type, move the ssb_bustype variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Acked-by: Michael Büsch Reviewed-by: Greg Kroah-Hartman Signed-off-by: Kalle Valo Link: https://msgid.link/20240204-bus_cleanup-ssb-v1-1-511026cd5f3c@marliere.net --- drivers/ssb/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index b9934b9c2d70..9f30e0edadfe 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -384,7 +384,7 @@ static struct attribute *ssb_device_attrs[] = { }; ATTRIBUTE_GROUPS(ssb_device); -static struct bus_type ssb_bustype = { +static const struct bus_type ssb_bustype = { .name = "ssb", .match = ssb_bus_match, .probe = ssb_device_probe, From 06b5b2942cf2b0878bd5bda2d35ffdb997874012 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sun, 4 Feb 2024 17:57:23 -0300 Subject: [PATCH 228/378] bcma: make bcma_bus_type const Now that the driver core can properly handle constant struct bus_type, move the bcma_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Reviewed-by: Greg Kroah-Hartman Signed-off-by: Kalle Valo Link: https://msgid.link/20240204-bus_cleanup-bcma-v1-1-0d881c793190@marliere.net --- drivers/bcma/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 7061d3ee836a..6b5d34919c72 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -68,7 +68,7 @@ static struct attribute *bcma_device_attrs[] = { }; ATTRIBUTE_GROUPS(bcma_device); -static struct bus_type bcma_bus_type = { +static const struct bus_type bcma_bus_type = { .name = "bcma", .match = bcma_bus_match, .probe = bcma_device_probe, From 94dd7ce1885e530a7b10bbe50d5d68ba1bb99e6e Mon Sep 17 00:00:00 2001 From: Martin Kaistra Date: Mon, 5 Feb 2024 10:30:40 +0100 Subject: [PATCH 229/378] wifi: rtl8xxxu: update rate mask per sta Until now, rtl8xxxu_watchdog_callback() only fetches RSSI and updates the rate mask in station mode. This means, in AP mode only the default rate mask is used. In order to have the rate mask reflect the actual connection quality, extend rtl8xxxu_watchdog_callback() to iterate over every sta. Like in the rtw88 driver, add a function to collect all currently present stas and then iterate over a list of copies to ensure no RCU lock problems for register access via USB. Remove the existing RCU lock in rtl8xxxu_refresh_rate_mask(). Since the currently used ieee80211_ave_rssi() is only for 'vif', add driver-level tracking of RSSI per sta. Signed-off-by: Martin Kaistra Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240205093040.1941140-1-martin.kaistra@linutronix.de --- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 8 +- .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 194 ++++++++++++++---- 2 files changed, 161 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 03307da67c2c..fd92d23c43d9 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -6,6 +6,7 @@ */ #include +#include #define RTL8XXXU_DEBUG_REG_WRITE 0x01 #define RTL8XXXU_DEBUG_REG_READ 0x02 @@ -1858,6 +1859,8 @@ struct rtl8xxxu_priv { int next_mbox; int nr_out_eps; + /* Ensure no added or deleted stas while iterating */ + struct mutex sta_mutex; struct mutex h2c_mutex; /* Protect the indirect register accesses of RTL8710BU. */ struct mutex syson_indirect_access_mutex; @@ -1892,7 +1895,6 @@ struct rtl8xxxu_priv { u8 pi_enabled:1; u8 no_pape:1; u8 int_buf[USB_INTR_CONTENT_LENGTH]; - u8 rssi_level; DECLARE_BITMAP(tx_aggr_started, IEEE80211_NUM_TIDS); DECLARE_BITMAP(tid_tx_operational, IEEE80211_NUM_TIDS); @@ -1913,11 +1915,15 @@ struct rtl8xxxu_priv { DECLARE_BITMAP(cam_map, RTL8XXXU_MAX_SEC_CAM_NUM); }; +DECLARE_EWMA(rssi, 10, 16); + struct rtl8xxxu_sta_info { struct ieee80211_sta *sta; struct ieee80211_vif *vif; u8 macid; + struct ewma_rssi avg_rssi; + u8 rssi_level; }; struct rtl8xxxu_vif { diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 125f03354ceb..055f66b623ff 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4991,10 +4991,11 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv; struct rtl8xxxu_priv *priv = hw->priv; struct device *dev = &priv->udev->dev; + struct rtl8xxxu_sta_info *sta_info; struct ieee80211_sta *sta; struct rtl8xxxu_ra_report *rarpt; + u8 val8, macid; u32 val32; - u8 val8; rarpt = &priv->ra_report; @@ -5017,6 +5018,7 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rcu_read_unlock(); goto error; } + macid = rtl8xxxu_get_macid(priv, sta); if (sta->deflink.ht_cap.ht_supported) dev_info(dev, "%s: HT supported\n", __func__); @@ -5037,14 +5039,15 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, bw = RATE_INFO_BW_40; else bw = RATE_INFO_BW_20; + + sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv; + sta_info->rssi_level = RTL8XXXU_RATR_STA_INIT; rcu_read_unlock(); rtl8xxxu_update_ra_report(rarpt, highest_rate, sgi, bw); - priv->rssi_level = RTL8XXXU_RATR_STA_INIT; - priv->fops->update_rate_mask(priv, ramask, 0, sgi, - bw == RATE_INFO_BW_40, 0); + bw == RATE_INFO_BW_40, macid); rtl8xxxu_write8(priv, REG_BCN_MAX_ERR, 0xff); @@ -6317,6 +6320,76 @@ static void rtl8188e_c2hcmd_callback(struct work_struct *work) } } +#define rtl8xxxu_iterate_vifs_atomic(priv, iterator, data) \ + ieee80211_iterate_active_interfaces_atomic((priv)->hw, \ + IEEE80211_IFACE_ITER_NORMAL, iterator, data) + +struct rtl8xxxu_rx_update_rssi_data { + struct rtl8xxxu_priv *priv; + struct ieee80211_hdr *hdr; + struct ieee80211_rx_status *rx_status; + u8 *bssid; +}; + +static void rtl8xxxu_rx_update_rssi_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct rtl8xxxu_rx_update_rssi_data *iter_data = data; + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr = iter_data->hdr; + struct rtl8xxxu_priv *priv = iter_data->priv; + struct rtl8xxxu_sta_info *sta_info; + struct ieee80211_rx_status *rx_status = iter_data->rx_status; + u8 *bssid = iter_data->bssid; + + if (!ether_addr_equal(vif->bss_conf.bssid, bssid)) + return; + + if (!(ether_addr_equal(vif->addr, hdr->addr1) || + ieee80211_is_beacon(hdr->frame_control))) + return; + + sta = ieee80211_find_sta_by_ifaddr(priv->hw, hdr->addr2, + vif->addr); + if (!sta) + return; + + sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv; + ewma_rssi_add(&sta_info->avg_rssi, -rx_status->signal); +} + +static inline u8 *get_hdr_bssid(struct ieee80211_hdr *hdr) +{ + __le16 fc = hdr->frame_control; + u8 *bssid; + + if (ieee80211_has_tods(fc)) + bssid = hdr->addr1; + else if (ieee80211_has_fromds(fc)) + bssid = hdr->addr2; + else + bssid = hdr->addr3; + + return bssid; +} + +static void rtl8xxxu_rx_update_rssi(struct rtl8xxxu_priv *priv, + struct ieee80211_rx_status *rx_status, + struct ieee80211_hdr *hdr) +{ + struct rtl8xxxu_rx_update_rssi_data data = {}; + + if (ieee80211_is_ctl(hdr->frame_control)) + return; + + data.priv = priv; + data.hdr = hdr; + data.rx_status = rx_status; + data.bssid = get_hdr_bssid(hdr); + + rtl8xxxu_iterate_vifs_atomic(priv, rtl8xxxu_rx_update_rssi_iter, &data); +} + int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb) { struct ieee80211_hw *hw = priv->hw; @@ -6376,18 +6449,26 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb) skb_queue_tail(&priv->c2hcmd_queue, skb); schedule_work(&priv->c2hcmd_work); } else { + struct ieee80211_hdr *hdr; + phy_stats = (struct rtl8723au_phy_stats *)skb->data; skb_pull(skb, drvinfo_sz + desc_shift); skb_trim(skb, pkt_len); - if (rx_desc->phy_stats) + hdr = (struct ieee80211_hdr *)skb->data; + if (rx_desc->phy_stats) { priv->fops->parse_phystats( priv, rx_status, phy_stats, rx_desc->rxmcs, - (struct ieee80211_hdr *)skb->data, + hdr, rx_desc->crc32 || rx_desc->icverr); + if (!rx_desc->crc32 && !rx_desc->icverr) + rtl8xxxu_rx_update_rssi(priv, + rx_status, + hdr); + } rx_status->mactime = rx_desc->tsfl; rx_status->flag |= RX_FLAG_MACTIME_START; @@ -6484,10 +6565,15 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb) } else { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - if (rx_desc->phy_stats) + if (rx_desc->phy_stats) { priv->fops->parse_phystats(priv, rx_status, phy_stats, rx_desc->rxmcs, hdr, rx_desc->crc32 || rx_desc->icverr); + if (!rx_desc->crc32 && !rx_desc->icverr) + rtl8xxxu_rx_update_rssi(priv, + rx_status, + hdr); + } rx_status->mactime = rx_desc->tsfl; rx_status->flag |= RX_FLAG_MACTIME_START; @@ -7111,6 +7197,7 @@ static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv, int signal, struct ieee80211_sta *sta, bool force) { + struct rtl8xxxu_sta_info *sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv; struct ieee80211_hw *hw = priv->hw; u16 wireless_mode; u8 rssi_level, ratr_idx; @@ -7119,7 +7206,7 @@ static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv, u8 go_up_gap = 5; u8 macid = rtl8xxxu_get_macid(priv, sta); - rssi_level = priv->rssi_level; + rssi_level = sta_info->rssi_level; snr = rtl8xxxu_signal_to_snr(signal); snr_thresh_high = RTL8XXXU_SNR_THRESH_HIGH; snr_thresh_low = RTL8XXXU_SNR_THRESH_LOW; @@ -7144,18 +7231,16 @@ static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv, else rssi_level = RTL8XXXU_RATR_STA_LOW; - if (rssi_level != priv->rssi_level || force) { + if (rssi_level != sta_info->rssi_level || force) { int sgi = 0; u32 rate_bitmap = 0; - rcu_read_lock(); rate_bitmap = (sta->deflink.supp_rates[0] & 0xfff) | (sta->deflink.ht_cap.mcs.rx_mask[0] << 12) | (sta->deflink.ht_cap.mcs.rx_mask[1] << 20); if (sta->deflink.ht_cap.cap & (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20)) sgi = 1; - rcu_read_unlock(); wireless_mode = rtl8xxxu_wireless_mode(hw, sta); switch (wireless_mode) { @@ -7236,7 +7321,7 @@ static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv, break; } - priv->rssi_level = rssi_level; + sta_info->rssi_level = rssi_level; priv->fops->update_rate_mask(priv, rate_bitmap, ratr_idx, sgi, txbw_40mhz, macid); } } @@ -7329,40 +7414,60 @@ static void rtl8xxxu_track_cfo(struct rtl8xxxu_priv *priv) rtl8xxxu_set_atc_status(priv, abs(cfo_average) >= CFO_TH_ATC); } +static void rtl8xxxu_ra_iter(void *data, struct ieee80211_sta *sta) +{ + struct rtl8xxxu_sta_info *sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv; + struct rtl8xxxu_priv *priv = data; + int signal = -ewma_rssi_read(&sta_info->avg_rssi); + + priv->fops->report_rssi(priv, rtl8xxxu_get_macid(priv, sta), + rtl8xxxu_signal_to_snr(signal)); + rtl8xxxu_refresh_rate_mask(priv, signal, sta, false); +} + +struct rtl8xxxu_stas_entry { + struct list_head list; + struct ieee80211_sta *sta; +}; + +struct rtl8xxxu_iter_stas_data { + struct rtl8xxxu_priv *priv; + struct list_head list; +}; + +static void rtl8xxxu_collect_sta_iter(void *data, struct ieee80211_sta *sta) +{ + struct rtl8xxxu_iter_stas_data *iter_stas = data; + struct rtl8xxxu_stas_entry *stas_entry; + + stas_entry = kmalloc(sizeof(*stas_entry), GFP_ATOMIC); + if (!stas_entry) + return; + + stas_entry->sta = sta; + list_add_tail(&stas_entry->list, &iter_stas->list); +} + static void rtl8xxxu_watchdog_callback(struct work_struct *work) { - struct ieee80211_vif *vif; + + struct rtl8xxxu_iter_stas_data iter_data; + struct rtl8xxxu_stas_entry *sta_entry, *tmp; struct rtl8xxxu_priv *priv; - int i; priv = container_of(work, struct rtl8xxxu_priv, ra_watchdog.work); - for (i = 0; i < ARRAY_SIZE(priv->vifs); i++) { - vif = priv->vifs[i]; + iter_data.priv = priv; + INIT_LIST_HEAD(&iter_data.list); - if (!vif || vif->type != NL80211_IFTYPE_STATION) - continue; - - int signal; - struct ieee80211_sta *sta; - - rcu_read_lock(); - sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); - if (!sta) { - struct device *dev = &priv->udev->dev; - - dev_dbg(dev, "%s: no sta found\n", __func__); - rcu_read_unlock(); - continue; - } - rcu_read_unlock(); - - signal = ieee80211_ave_rssi(vif); - - priv->fops->report_rssi(priv, rtl8xxxu_get_macid(priv, sta), - rtl8xxxu_signal_to_snr(signal)); - - rtl8xxxu_refresh_rate_mask(priv, signal, sta, false); + mutex_lock(&priv->sta_mutex); + ieee80211_iterate_stations_atomic(priv->hw, rtl8xxxu_collect_sta_iter, + &iter_data); + list_for_each_entry_safe(sta_entry, tmp, &iter_data.list, list) { + list_del_init(&sta_entry->list); + rtl8xxxu_ra_iter(priv, sta_entry->sta); + kfree(sta_entry); } + mutex_unlock(&priv->sta_mutex); if (priv->fops->set_crystal_cap) rtl8xxxu_track_cfo(priv); @@ -7504,10 +7609,15 @@ static int rtl8xxxu_sta_add(struct ieee80211_hw *hw, struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv; struct rtl8xxxu_priv *priv = hw->priv; + mutex_lock(&priv->sta_mutex); + ewma_rssi_init(&sta_info->avg_rssi); if (vif->type == NL80211_IFTYPE_AP) { + sta_info->rssi_level = RTL8XXXU_RATR_STA_INIT; sta_info->macid = rtl8xxxu_acquire_macid(priv); - if (sta_info->macid >= RTL8XXXU_MAX_MAC_ID_NUM) + if (sta_info->macid >= RTL8XXXU_MAX_MAC_ID_NUM) { + mutex_unlock(&priv->sta_mutex); return -ENOSPC; + } rtl8xxxu_refresh_rate_mask(priv, 0, sta, true); priv->fops->report_connect(priv, sta_info->macid, H2C_MACID_ROLE_STA, true); @@ -7523,6 +7633,7 @@ static int rtl8xxxu_sta_add(struct ieee80211_hw *hw, break; } } + mutex_unlock(&priv->sta_mutex); return 0; } @@ -7534,8 +7645,10 @@ static int rtl8xxxu_sta_remove(struct ieee80211_hw *hw, struct rtl8xxxu_sta_info *sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv; struct rtl8xxxu_priv *priv = hw->priv; + mutex_lock(&priv->sta_mutex); if (vif->type == NL80211_IFTYPE_AP) rtl8xxxu_release_macid(priv, sta_info->macid); + mutex_unlock(&priv->sta_mutex); return 0; } @@ -7767,6 +7880,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, mutex_init(&priv->usb_buf_mutex); mutex_init(&priv->syson_indirect_access_mutex); mutex_init(&priv->h2c_mutex); + mutex_init(&priv->sta_mutex); INIT_LIST_HEAD(&priv->tx_urb_free_list); spin_lock_init(&priv->tx_urb_lock); INIT_LIST_HEAD(&priv->rx_urb_pending_list); From 2fd53eb04c492eb9a2b06f994b36e5cf34ba7541 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Jan 2024 16:49:07 +0100 Subject: [PATCH 230/378] wifi: mac80211: remove unused MAX_MSG_LEN define This got unused when the tracing was converted to dynamic strings, so the define can be removed. Signed-off-by: Johannes Berg --- net/mac80211/trace_msg.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/mac80211/trace_msg.h b/net/mac80211/trace_msg.h index c9dbe9aab7bd..aea4ce55c5ac 100644 --- a/net/mac80211/trace_msg.h +++ b/net/mac80211/trace_msg.h @@ -16,8 +16,6 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211_msg -#define MAX_MSG_LEN 120 - DECLARE_EVENT_CLASS(mac80211_msg_event, TP_PROTO(struct va_format *vaf), From efa2cce6e272b36c7f9687385ef4fd538cc3bf51 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Jan 2024 16:49:04 +0100 Subject: [PATCH 231/378] wifi: mac80211: remove extra shadowing variable Not sure how this happened or how nothing complained, but this variable already exists in the outer function scope with the same value (and the SKB isn't changed either.) Remove the extra one. Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 615795c4b052..76798e8057f7 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -5214,7 +5214,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, */ if (!status->link_valid && pubsta->mlo) { - struct ieee80211_hdr *hdr = (void *)skb->data; struct link_sta_info *link_sta; link_sta = link_sta_info_get_bss(rx.sdata, From 61f0261131c8dc2beeb6b34781a54788221081e9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:35 +0100 Subject: [PATCH 232/378] wifi: mac80211: clean up band switch in duration Most devices now do duration calculations, so we don't hit this code at all any more. Clearly the approach of warning at compile time here when new bands are added didn't work, the new bands were just added with "TODO". Clean it up, it won't matter for new bands since they'll just not have any need to calculate durations in software. While at it, also clean up and unify the code a bit. Link: https://msgid.link/20240129194108.70a97bd69265.Icdd8b0ac60a382244466510090eb0f5868151f39@changeid Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e448ab338448..7b33942ea51f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -133,6 +133,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, mrate = sband->bitrates[0].bitrate; for (i = 0; i < sband->n_bitrates; i++) { struct ieee80211_rate *r = &sband->bitrates[i]; + u32 flag; if (r->bitrate > txrate->bitrate) break; @@ -145,28 +146,24 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, switch (sband->band) { case NL80211_BAND_2GHZ: - case NL80211_BAND_LC: { - u32 flag; + case NL80211_BAND_LC: if (tx->sdata->deflink.operating_11g_mode) flag = IEEE80211_RATE_MANDATORY_G; else flag = IEEE80211_RATE_MANDATORY_B; - if (r->flags & flag) - mrate = r->bitrate; break; - } case NL80211_BAND_5GHZ: case NL80211_BAND_6GHZ: - if (r->flags & IEEE80211_RATE_MANDATORY_A) - mrate = r->bitrate; + flag = IEEE80211_RATE_MANDATORY_A; break; - case NL80211_BAND_S1GHZ: - case NL80211_BAND_60GHZ: - /* TODO, for now fall through */ - case NUM_NL80211_BANDS: + default: + flag = 0; WARN_ON(1); break; } + + if (r->flags & flag) + mrate = r->bitrate; } if (rate == -1) { /* No matching basic rate found; use highest suitable mandatory From 310c8387c63830bc375827242e0f9fa689f82e21 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:36 +0100 Subject: [PATCH 233/378] wifi: mac80211: clean up connection process Rewrite the station-side connection handling. The connection flags (IEEE80211_DISABLE_*) are rather confusing, and they're not always maintained well. Additionally, for wider-bandwidth OFDMA support we need to know the precise bandwidth of the AP, which is currently somewhat difficult. Rewrite this to have a 'mode' (S1G/legacy/HT/...) and a limit on the bandwidth. This is not entirely clean because some of those modes aren't completely sequenced (as this assumes in some places), e.g. VHT doesn't exist on 2.4 GHz, but HE does. However, it still simplifies things and gives us a good idea what we're operating as, so we can parse elements accordingly etc. This leaves a FIXME for puncturing, this is addressed in a later patch. Reviewed-by: Ilan Peer Reviewed-by: Miriam Rachel Korenblit Link: https://msgid.link/20240129194108.9451722c0110.I3e61f4cfe9da89008e1854160093c76a1e69dc2a@changeid Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 2 +- net/mac80211/debug.h | 18 +- net/mac80211/ibss.c | 15 +- net/mac80211/ieee80211_i.h | 60 +- net/mac80211/mesh.c | 21 +- net/mac80211/mlme.c | 2075 +++++++++++++++++++----------------- net/mac80211/spectmgmt.c | 21 +- net/mac80211/tdls.c | 8 +- net/mac80211/tests/elems.c | 1 + net/mac80211/util.c | 164 ++- 10 files changed, 1328 insertions(+), 1057 deletions(-) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index ef4c2cebc080..70ba5dc4b283 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -382,7 +382,7 @@ _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, /* downgrade chandef up to max_bw */ min_def = ctx->conf.def; while (min_def.width > max_bw) - ieee80211_chandef_downgrade(&min_def); + ieee80211_chandef_downgrade(&min_def, NULL); if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) return 0; diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h index d49894df2351..49da401c5340 100644 --- a/net/mac80211/debug.h +++ b/net/mac80211/debug.h @@ -152,16 +152,17 @@ do { \ else \ _sdata_err((link)->sdata, fmt, ##__VA_ARGS__); \ } while (0) -#define link_dbg(link, fmt, ...) \ +#define _link_id_dbg(print, sdata, link_id, fmt, ...) \ do { \ - if (ieee80211_vif_is_mld(&(link)->sdata->vif)) \ - _sdata_dbg(1, (link)->sdata, "[link %d] " fmt, \ - (link)->link_id, \ - ##__VA_ARGS__); \ + if (ieee80211_vif_is_mld(&(sdata)->vif)) \ + _sdata_dbg(print, sdata, "[link %d] " fmt, \ + link_id, ##__VA_ARGS__); \ else \ - _sdata_dbg(1, (link)->sdata, fmt, \ - ##__VA_ARGS__); \ + _sdata_dbg(1, sdata, fmt, ##__VA_ARGS__); \ } while (0) +#define link_dbg(link, fmt, ...) \ + _link_id_dbg(1, (link)->sdata, (link)->link_id, \ + fmt, ##__VA_ARGS__) #define ht_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_HT_DEBUG, \ @@ -226,6 +227,9 @@ do { \ #define mlme_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_MLME_DEBUG, \ sdata, fmt, ##__VA_ARGS__) +#define mlme_link_id_dbg(sdata, link_id, fmt, ...) \ + _link_id_dbg(MAC80211_MLME_DEBUG, sdata, link_id, \ + fmt, ##__VA_ARGS__) #define mlme_dbg_ratelimited(sdata, fmt, ...) \ _sdata_dbg(MAC80211_MLME_DEBUG && net_ratelimit(), \ diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 8f2b445a5ec3..c23f46218af7 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -757,21 +757,22 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; enum nl80211_channel_type ch_type; int err; - ieee80211_conn_flags_t conn_flags; + struct ieee80211_conn_settings conn = { + .mode = IEEE80211_CONN_MODE_HT, + .bw_limit = IEEE80211_CONN_BW_LIMIT_40, + }; u32 vht_cap_info = 0; lockdep_assert_wiphy(sdata->local->hw.wiphy); - conn_flags = IEEE80211_CONN_DISABLE_VHT; - switch (ifibss->chandef.width) { case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20_NOHT: - conn_flags |= IEEE80211_CONN_DISABLE_HT; + conn.mode = IEEE80211_CONN_MODE_LEGACY; fallthrough; case NL80211_CHAN_WIDTH_20: - conn_flags |= IEEE80211_CONN_DISABLE_40MHZ; + conn.bw_limit = IEEE80211_CONN_BW_LIMIT_20; break; default: break; @@ -783,8 +784,8 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, memset(¶ms, 0, sizeof(params)); err = ieee80211_parse_ch_switch_ie(sdata, elems, ifibss->chandef.chan->band, - vht_cap_info, - conn_flags, ifibss->bssid, &csa_ie); + vht_cap_info, &conn, + ifibss->bssid, &csa_ie); /* can't switch to destination channel, fail */ if (err < 0) goto disconnect; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index eb32174984c3..112029f5a7df 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -370,19 +370,32 @@ enum ieee80211_sta_flags { IEEE80211_STA_ENABLE_RRM = BIT(15), }; -typedef u32 __bitwise ieee80211_conn_flags_t; - -enum ieee80211_conn_flags { - IEEE80211_CONN_DISABLE_HT = (__force ieee80211_conn_flags_t)BIT(0), - IEEE80211_CONN_DISABLE_40MHZ = (__force ieee80211_conn_flags_t)BIT(1), - IEEE80211_CONN_DISABLE_VHT = (__force ieee80211_conn_flags_t)BIT(2), - IEEE80211_CONN_DISABLE_80P80MHZ = (__force ieee80211_conn_flags_t)BIT(3), - IEEE80211_CONN_DISABLE_160MHZ = (__force ieee80211_conn_flags_t)BIT(4), - IEEE80211_CONN_DISABLE_HE = (__force ieee80211_conn_flags_t)BIT(5), - IEEE80211_CONN_DISABLE_EHT = (__force ieee80211_conn_flags_t)BIT(6), - IEEE80211_CONN_DISABLE_320MHZ = (__force ieee80211_conn_flags_t)BIT(7), +enum ieee80211_conn_mode { + IEEE80211_CONN_MODE_S1G, + IEEE80211_CONN_MODE_LEGACY, + IEEE80211_CONN_MODE_HT, + IEEE80211_CONN_MODE_VHT, + IEEE80211_CONN_MODE_HE, + IEEE80211_CONN_MODE_EHT, }; +#define IEEE80211_CONN_MODE_HIGHEST IEEE80211_CONN_MODE_EHT + +enum ieee80211_conn_bw_limit { + IEEE80211_CONN_BW_LIMIT_20, + IEEE80211_CONN_BW_LIMIT_40, + IEEE80211_CONN_BW_LIMIT_80, + IEEE80211_CONN_BW_LIMIT_160, /* also 80+80 */ + IEEE80211_CONN_BW_LIMIT_320, +}; + +struct ieee80211_conn_settings { + enum ieee80211_conn_mode mode; + enum ieee80211_conn_bw_limit bw_limit; +}; + +extern const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited; + struct ieee80211_mgd_auth_data { struct cfg80211_bss *bss; unsigned long timeout; @@ -416,7 +429,7 @@ struct ieee80211_mgd_assoc_data { size_t elems_len; u8 *elems; /* pointing to inside ie[] below */ - ieee80211_conn_flags_t conn_flags; + struct ieee80211_conn_settings conn; u16 status; @@ -943,7 +956,7 @@ struct ieee80211_link_data_managed { enum ieee80211_smps_mode req_smps, /* requested smps mode */ driver_smps_mode; /* smps mode request */ - ieee80211_conn_flags_t conn_flags; + struct ieee80211_conn_settings conn; s16 p2p_noa_index; @@ -2171,9 +2184,8 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, * @elems: parsed 802.11 elements received with the frame * @current_band: indicates the current band * @vht_cap_info: VHT capabilities of the transmitter - * @conn_flags: contains information about own capabilities and restrictions - * to decide which channel switch announcements can be accepted, using - * flags from &enum ieee80211_conn_flags. + * @conn: contains information about own capabilities and restrictions + * to decide which channel switch announcements can be accepted * @bssid: the currently connected bssid (for reporting) * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl. * All of them will be filled with if success only. @@ -2183,7 +2195,8 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems, enum nl80211_band current_band, u32 vht_cap_info, - ieee80211_conn_flags_t conn_flags, u8 *bssid, + struct ieee80211_conn_settings *conn, + u8 *bssid, struct ieee80211_csa_ie *csa_ie); /* Suspend/resume and hw reconfiguration */ @@ -2207,6 +2220,9 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw) /* utility functions/constants */ extern const void *const mac80211_wiphy_privid; /* for wiphy privid */ +const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode); +enum ieee80211_conn_bw_limit +ieee80211_min_bw_limit_from_chandef(struct cfg80211_chan_def *chandef); int ieee80211_frame_duration(enum nl80211_band band, size_t len, int rate, int erp, int short_preamble); void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata, @@ -2248,6 +2264,7 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, /** * struct ieee80211_elems_parse_params - element parsing parameters + * @mode: connection mode for parsing * @start: pointer to the elements * @len: length of the elements * @action: %true if the elements came from an action frame @@ -2265,6 +2282,7 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, * for EHT capabilities parsing) */ struct ieee80211_elems_parse_params { + enum ieee80211_conn_mode mode; const u8 *start; size_t len; bool action; @@ -2284,6 +2302,7 @@ ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, struct cfg80211_bss *bss) { struct ieee80211_elems_parse_params params = { + .mode = IEEE80211_CONN_MODE_HIGHEST, .start = start, .len = len, .action = action, @@ -2459,9 +2478,9 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, const struct cfg80211_chan_def *chandef); u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype); -u8 *ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags, u8 *pos, +u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, const struct ieee80211_sta_he_cap *he_cap, - u8 *end); + u8 *pos, u8 *end); void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps_mode, struct sk_buff *skb); @@ -2501,7 +2520,8 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, struct cfg80211_chan_def *chandef); bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper, struct cfg80211_chan_def *chandef); -ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c); +void ieee80211_chandef_downgrade(struct cfg80211_chan_def *chandef, + struct ieee80211_conn_settings *conn); int __must_check ieee80211_link_use_channel(struct ieee80211_link_data *link, diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index fccbcde3359a..6b48914b39fd 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -586,7 +586,7 @@ int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata, return -ENOMEM; pos = skb_put(skb, ie_len); - ieee80211_ie_build_he_cap(0, pos, he_cap, pos + ie_len); + ieee80211_ie_build_he_cap(NULL, he_cap, pos, pos + ie_len); return 0; } @@ -1292,7 +1292,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_supported_band *sband; int err; - ieee80211_conn_flags_t conn_flags = 0; + struct ieee80211_conn_settings conn = ieee80211_conn_settings_unlimited; u32 vht_cap_info = 0; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -1303,13 +1303,16 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, switch (sdata->vif.bss_conf.chandef.width) { case NL80211_CHAN_WIDTH_20_NOHT: - conn_flags |= IEEE80211_CONN_DISABLE_HT; - fallthrough; + conn.mode = IEEE80211_CONN_MODE_LEGACY; + conn.bw_limit = IEEE80211_CONN_BW_LIMIT_20; + break; case NL80211_CHAN_WIDTH_20: - conn_flags |= IEEE80211_CONN_DISABLE_40MHZ; - fallthrough; + conn.mode = IEEE80211_CONN_MODE_HT; + conn.bw_limit = IEEE80211_CONN_BW_LIMIT_20; + break; case NL80211_CHAN_WIDTH_40: - conn_flags |= IEEE80211_CONN_DISABLE_VHT; + conn.mode = IEEE80211_CONN_MODE_HT; + conn.bw_limit = IEEE80211_CONN_BW_LIMIT_40; break; default: break; @@ -1321,8 +1324,8 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, memset(¶ms, 0, sizeof(params)); err = ieee80211_parse_ch_switch_ie(sdata, elems, sband->band, - vht_cap_info, - conn_flags, sdata->vif.addr, + vht_cap_info, &conn, + sdata->vif.addr, &csa_ie); if (err < 0) return false; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e5d0988513dd..549a06c9a2e5 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -157,8 +157,7 @@ ieee80211_handle_puncturing_bitmap(struct ieee80211_link_data *link, !(bitmap && ieee80211_hw_check(&local->hw, DISALLOW_PUNCTURING))) break; - link->u.mgd.conn_flags |= - ieee80211_chandef_downgrade(chandef); + ieee80211_chandef_downgrade(chandef, &link->u.mgd.conn); *changed |= BSS_CHANGED_BANDWIDTH; } @@ -225,77 +224,84 @@ static int ecw2cw(int ecw) return (1 << ecw) - 1; } -static ieee80211_conn_flags_t -ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, - struct ieee80211_link_data *link, - ieee80211_conn_flags_t conn_flags, - struct ieee80211_supported_band *sband, - struct ieee80211_channel *channel, - u32 vht_cap_info, - const struct ieee80211_ht_operation *ht_oper, - const struct ieee80211_vht_operation *vht_oper, - const struct ieee80211_he_operation *he_oper, - const struct ieee80211_eht_operation *eht_oper, - const struct ieee80211_s1g_oper_ie *s1g_oper, - struct cfg80211_chan_def *chandef, bool tracking) +static enum ieee80211_conn_mode +ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel *channel, + u32 vht_cap_info, + const struct ieee802_11_elems *elems, + bool ignore_ht_channel_mismatch, + const struct ieee80211_conn_settings *conn, + struct cfg80211_chan_def *chandef) { + const struct ieee80211_ht_operation *ht_oper = elems->ht_operation; + const struct ieee80211_vht_operation *vht_oper = elems->vht_operation; + const struct ieee80211_he_operation *he_oper = elems->he_operation; + const struct ieee80211_eht_operation *eht_oper = elems->eht_operation; + struct ieee80211_supported_band *sband = + sdata->local->hw.wiphy->bands[channel->band]; struct cfg80211_chan_def vht_chandef; - struct ieee80211_sta_ht_cap sta_ht_cap; - ieee80211_conn_flags_t ret; + bool no_vht = false; u32 ht_cfreq; - memset(chandef, 0, sizeof(struct cfg80211_chan_def)); - chandef->chan = channel; - chandef->width = NL80211_CHAN_WIDTH_20_NOHT; - chandef->center_freq1 = channel->center_freq; - chandef->freq1_offset = channel->freq_offset; + *chandef = (struct cfg80211_chan_def) { + .chan = channel, + .width = NL80211_CHAN_WIDTH_20_NOHT, + .center_freq1 = channel->center_freq, + .freq1_offset = channel->freq_offset, + }; - if (channel->band == NL80211_BAND_6GHZ) { - if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, eht_oper, - chandef)) { - mlme_dbg(sdata, - "bad 6 GHz operation, disabling HT/VHT/HE/EHT\n"); - ret = IEEE80211_CONN_DISABLE_HT | - IEEE80211_CONN_DISABLE_VHT | - IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT; - } else { - ret = 0; - } - vht_chandef = *chandef; - goto out; - } else if (sband->band == NL80211_BAND_S1GHZ) { - if (!ieee80211_chandef_s1g_oper(s1g_oper, chandef)) { + /* get special S1G case out of the way */ + if (sband->band == NL80211_BAND_S1GHZ) { + if (!ieee80211_chandef_s1g_oper(elems->s1g_oper, chandef)) { sdata_info(sdata, "Missing S1G Operation Element? Trying operating == primary\n"); chandef->width = ieee80211_s1g_channel_width(channel); } - ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_40MHZ | - IEEE80211_CONN_DISABLE_VHT | - IEEE80211_CONN_DISABLE_80P80MHZ | - IEEE80211_CONN_DISABLE_160MHZ; - goto out; + return IEEE80211_CONN_MODE_S1G; } - memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); - ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); + /* get special 6 GHz case out of the way */ + if (sband->band == NL80211_BAND_6GHZ) { + enum ieee80211_conn_mode mode = IEEE80211_CONN_MODE_EHT; - if (!ht_oper || !sta_ht_cap.ht_supported) { - mlme_dbg(sdata, "HT operation missing / HT not supported\n"); - ret = IEEE80211_CONN_DISABLE_HT | - IEEE80211_CONN_DISABLE_VHT | - IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT; - goto out; + /* this is an error */ + if (conn->mode < IEEE80211_CONN_MODE_HE) + return IEEE80211_CONN_MODE_LEGACY; + + if (!elems->he_6ghz_capa || !elems->he_cap) { + sdata_info(sdata, + "HE 6 GHz AP is missing HE/HE 6 GHz band capability\n"); + return IEEE80211_CONN_MODE_LEGACY; + } + + if (!eht_oper || !elems->eht_cap) { + eht_oper = NULL; + mode = IEEE80211_CONN_MODE_HE; + } + + if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, + eht_oper, chandef)) { + sdata_info(sdata, "bad HE/EHT 6 GHz operation\n"); + return IEEE80211_CONN_MODE_LEGACY; + } + + return mode; } + /* now we have the progression HT, VHT, ... */ + if (conn->mode < IEEE80211_CONN_MODE_HT) + return IEEE80211_CONN_MODE_LEGACY; + + if (!ht_oper || !elems->ht_cap_elem) + return IEEE80211_CONN_MODE_LEGACY; + chandef->width = NL80211_CHAN_WIDTH_20; ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, channel->band); /* check that channel matches the right operating channel */ - if (!tracking && channel->center_freq != ht_cfreq) { + if (!ignore_ht_channel_mismatch && channel->center_freq != ht_cfreq) { /* * It's possible that some APs are confused here; * Netgear WNDR3700 sometimes reports 4 higher than @@ -307,36 +313,22 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", channel->center_freq, ht_cfreq, ht_oper->primary_chan, channel->band); - ret = IEEE80211_CONN_DISABLE_HT | - IEEE80211_CONN_DISABLE_VHT | - IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT; - goto out; + return IEEE80211_CONN_MODE_LEGACY; } - /* check 40 MHz support, if we have it */ - if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - ieee80211_chandef_ht_oper(ht_oper, chandef); - } else { - mlme_dbg(sdata, "40 MHz not supported\n"); - /* 40 MHz (and 80 MHz) must be supported for VHT */ - ret = IEEE80211_CONN_DISABLE_VHT; - /* also mark 40 MHz disabled */ - ret |= IEEE80211_CONN_DISABLE_40MHZ; - goto out; - } + ieee80211_chandef_ht_oper(ht_oper, chandef); - if (!vht_oper || !sband->vht_cap.vht_supported) { - mlme_dbg(sdata, "VHT operation missing / VHT not supported\n"); - ret = IEEE80211_CONN_DISABLE_VHT; - goto out; - } + if (conn->mode < IEEE80211_CONN_MODE_VHT) + return IEEE80211_CONN_MODE_HT; vht_chandef = *chandef; - if (!(conn_flags & IEEE80211_CONN_DISABLE_HE) && - he_oper && - (le32_to_cpu(he_oper->he_oper_params) & - IEEE80211_HE_OPERATION_VHT_OPER_INFO)) { + + /* + * having he_cap/he_oper parsed out implies we're at + * least operating as HE STA + */ + if (elems->he_cap && he_oper && + he_oper->he_oper_params & cpu_to_le32(IEEE80211_HE_OPERATION_VHT_OPER_INFO)) { struct ieee80211_vht_operation he_oper_vht_cap; /* @@ -349,52 +341,56 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info, &he_oper_vht_cap, ht_oper, &vht_chandef)) { - if (!(conn_flags & IEEE80211_CONN_DISABLE_HE)) - sdata_info(sdata, - "HE AP VHT information is invalid, disabling HE\n"); - ret = IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT; - goto out; + sdata_info(sdata, + "HE AP VHT information is invalid, disabling HE\n"); + /* this will cause us to re-parse as VHT STA */ + return IEEE80211_CONN_MODE_VHT; } + } else if (!vht_oper || !elems->vht_cap_elem) { + if (sband->band == NL80211_BAND_5GHZ) { + sdata_info(sdata, + "VHT information is missing, disabling VHT\n"); + return IEEE80211_CONN_MODE_HT; + } + no_vht = true; + } else if (sband->band == NL80211_BAND_2GHZ) { + no_vht = true; } else if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info, vht_oper, ht_oper, &vht_chandef)) { - if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT)) - sdata_info(sdata, - "AP VHT information is invalid, disabling VHT\n"); - ret = IEEE80211_CONN_DISABLE_VHT; - goto out; - } - - if (!cfg80211_chandef_valid(&vht_chandef)) { - if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT)) - sdata_info(sdata, - "AP VHT information is invalid, disabling VHT\n"); - ret = IEEE80211_CONN_DISABLE_VHT; - goto out; - } - - if (cfg80211_chandef_identical(chandef, &vht_chandef)) { - ret = 0; - goto out; + sdata_info(sdata, + "AP VHT information is invalid, disabling VHT\n"); + return IEEE80211_CONN_MODE_HT; } if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { - if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT)) - sdata_info(sdata, - "AP VHT information doesn't match HT, disabling VHT\n"); - ret = IEEE80211_CONN_DISABLE_VHT; - goto out; + sdata_info(sdata, + "AP VHT information doesn't match HT, disabling VHT\n"); + return IEEE80211_CONN_MODE_HT; } *chandef = vht_chandef; + /* stick to current max mode if we or the AP don't have HE */ + if (conn->mode < IEEE80211_CONN_MODE_HE || + !elems->he_operation || !elems->he_cap) { + if (no_vht) + return IEEE80211_CONN_MODE_HT; + return IEEE80211_CONN_MODE_VHT; + } + + /* stick to HE if we or the AP don't have EHT */ + if (conn->mode < IEEE80211_CONN_MODE_EHT || + !eht_oper || !elems->eht_cap) + return IEEE80211_CONN_MODE_HE; + /* * handle the case that the EHT operation indicates that it holds EHT * operation information (in case that the channel width differs from * the channel width reported in HT/VHT/HE). */ - if (eht_oper && (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) { + if (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) { struct cfg80211_chan_def eht_chandef = *chandef; ieee80211_chandef_eht_oper((const void *)eht_oper->optional, @@ -403,37 +399,547 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, false, &eht_chandef); if (!cfg80211_chandef_valid(&eht_chandef)) { - if (!(conn_flags & IEEE80211_CONN_DISABLE_EHT)) - sdata_info(sdata, - "AP EHT information is invalid, disabling EHT\n"); - ret = IEEE80211_CONN_DISABLE_EHT; - goto out; + sdata_info(sdata, + "AP EHT information is invalid, disabling EHT\n"); + return IEEE80211_CONN_MODE_HE; } if (!cfg80211_chandef_compatible(chandef, &eht_chandef)) { - if (!(conn_flags & IEEE80211_CONN_DISABLE_EHT)) - sdata_info(sdata, - "AP EHT information is incompatible, disabling EHT\n"); - ret = IEEE80211_CONN_DISABLE_EHT; - goto out; + sdata_info(sdata, + "AP EHT information doesn't match HT/VHT/HE, disabling EHT\n"); + return IEEE80211_CONN_MODE_HE; } *chandef = eht_chandef; } - ret = 0; + return IEEE80211_CONN_MODE_EHT; +} + +static bool +ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_he_cap_elem *he_cap, + const struct ieee80211_he_operation *he_op) +{ + struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp; + u16 mcs_80_map_tx, mcs_80_map_rx; + u16 ap_min_req_set; + int nss; + + if (!he_cap) + return false; + + /* mcs_nss is right after he_cap info */ + he_mcs_nss_supp = (void *)(he_cap + 1); + + mcs_80_map_tx = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80); + mcs_80_map_rx = le16_to_cpu(he_mcs_nss_supp->rx_mcs_80); + + /* P802.11-REVme/D0.3 + * 27.1.1 Introduction to the HE PHY + * ... + * An HE STA shall support the following features: + * ... + * Single spatial stream HE-MCSs 0 to 7 (transmit and receive) in all + * supported channel widths for HE SU PPDUs + */ + if ((mcs_80_map_tx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED || + (mcs_80_map_rx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED) { + sdata_info(sdata, + "Missing mandatory rates for 1 Nss, rx 0x%x, tx 0x%x, disable HE\n", + mcs_80_map_tx, mcs_80_map_rx); + return false; + } + + if (!he_op) + return true; + + ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); -out: /* - * When tracking the current AP, don't do any further checks if the - * new chandef is identical to the one we're currently using for the - * connection. This keeps us from playing ping-pong with regulatory, - * without it the following can happen (for example): + * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all + * zeroes, which is nonsense, and completely inconsistent with itself + * (it doesn't have 8 streams). Accept the settings in this case anyway. + */ + if (!ap_min_req_set) + return true; + + /* make sure the AP is consistent with itself + * + * P802.11-REVme/D0.3 + * 26.17.1 Basic HE BSS operation + * + * A STA that is operating in an HE BSS shall be able to receive and + * transmit at each of the tuple values indicated by the + * Basic HE-MCS And NSS Set field of the HE Operation parameter of the + * MLME-START.request primitive and shall be able to receive at each of + * the tuple values indicated by the Supported HE-MCS and + * NSS Set field in the HE Capabilities parameter of the MLMESTART.request + * primitive + */ + for (nss = 8; nss > 0; nss--) { + u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; + u8 ap_rx_val; + u8 ap_tx_val; + + if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED) + continue; + + ap_rx_val = (mcs_80_map_rx >> (2 * (nss - 1))) & 3; + ap_tx_val = (mcs_80_map_tx >> (2 * (nss - 1))) & 3; + + if (ap_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || + ap_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || + ap_rx_val < ap_op_val || ap_tx_val < ap_op_val) { + sdata_info(sdata, + "Invalid rates for %d Nss, rx %d, tx %d oper %d, disable HE\n", + nss, ap_rx_val, ap_rx_val, ap_op_val); + return false; + } + } + + return true; +} + +static bool +ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + const struct ieee80211_he_operation *he_op) +{ + const struct ieee80211_sta_he_cap *sta_he_cap = + ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); + u16 ap_min_req_set; + int i; + + if (!sta_he_cap || !he_op) + return false; + + ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); + + /* + * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all + * zeroes, which is nonsense, and completely inconsistent with itself + * (it doesn't have 8 streams). Accept the settings in this case anyway. + */ + if (!ap_min_req_set) + return true; + + /* Need to go over for 80MHz, 160MHz and for 80+80 */ + for (i = 0; i < 3; i++) { + const struct ieee80211_he_mcs_nss_supp *sta_mcs_nss_supp = + &sta_he_cap->he_mcs_nss_supp; + u16 sta_mcs_map_rx = + le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i]); + u16 sta_mcs_map_tx = + le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i + 1]); + u8 nss; + bool verified = true; + + /* + * For each band there is a maximum of 8 spatial streams + * possible. Each of the sta_mcs_map_* is a 16-bit struct built + * of 2 bits per NSS (1-8), with the values defined in enum + * ieee80211_he_mcs_support. Need to make sure STA TX and RX + * capabilities aren't less than the AP's minimum requirements + * for this HE BSS per SS. + * It is enough to find one such band that meets the reqs. + */ + for (nss = 8; nss > 0; nss--) { + u8 sta_rx_val = (sta_mcs_map_rx >> (2 * (nss - 1))) & 3; + u8 sta_tx_val = (sta_mcs_map_tx >> (2 * (nss - 1))) & 3; + u8 ap_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; + + if (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED) + continue; + + /* + * Make sure the HE AP doesn't require MCSs that aren't + * supported by the client as required by spec + * + * P802.11-REVme/D0.3 + * 26.17.1 Basic HE BSS operation + * + * An HE STA shall not attempt to join * (MLME-JOIN.request primitive) + * a BSS, unless it supports (i.e., is able to both transmit and + * receive using) all of the tuples in the basic + * HE-MCS and NSS set. + */ + if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || + sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || + (ap_val > sta_rx_val) || (ap_val > sta_tx_val)) { + verified = false; + break; + } + } + + if (verified) + return true; + } + + /* If here, STA doesn't meet AP's HE min requirements */ + return false; +} + +static u8 +ieee80211_get_eht_cap_mcs_nss(const struct ieee80211_sta_he_cap *sta_he_cap, + const struct ieee80211_sta_eht_cap *sta_eht_cap, + unsigned int idx, int bw) +{ + u8 he_phy_cap0 = sta_he_cap->he_cap_elem.phy_cap_info[0]; + u8 eht_phy_cap0 = sta_eht_cap->eht_cap_elem.phy_cap_info[0]; + + /* handle us being a 20 MHz-only EHT STA - with four values + * for MCS 0-7, 8-9, 10-11, 12-13. + */ + if (!(he_phy_cap0 & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) + return sta_eht_cap->eht_mcs_nss_supp.only_20mhz.rx_tx_max_nss[idx]; + + /* the others have MCS 0-9 together, rather than separately from 0-7 */ + if (idx > 0) + idx--; + + switch (bw) { + case 0: + return sta_eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_max_nss[idx]; + case 1: + if (!(he_phy_cap0 & + (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G))) + return 0xff; /* pass check */ + return sta_eht_cap->eht_mcs_nss_supp.bw._160.rx_tx_max_nss[idx]; + case 2: + if (!(eht_phy_cap0 & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)) + return 0xff; /* pass check */ + return sta_eht_cap->eht_mcs_nss_supp.bw._320.rx_tx_max_nss[idx]; + } + + WARN_ON(1); + return 0; +} + +static bool +ieee80211_verify_sta_eht_mcs_support(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + const struct ieee80211_eht_operation *eht_op) +{ + const struct ieee80211_sta_he_cap *sta_he_cap = + ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); + const struct ieee80211_sta_eht_cap *sta_eht_cap = + ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); + const struct ieee80211_eht_mcs_nss_supp_20mhz_only *req; + unsigned int i; + + if (!sta_he_cap || !sta_eht_cap || !eht_op) + return false; + + req = &eht_op->basic_mcs_nss; + + for (i = 0; i < ARRAY_SIZE(req->rx_tx_max_nss); i++) { + u8 req_rx_nss, req_tx_nss; + unsigned int bw; + + req_rx_nss = u8_get_bits(req->rx_tx_max_nss[i], + IEEE80211_EHT_MCS_NSS_RX); + req_tx_nss = u8_get_bits(req->rx_tx_max_nss[i], + IEEE80211_EHT_MCS_NSS_TX); + + for (bw = 0; bw < 3; bw++) { + u8 have, have_rx_nss, have_tx_nss; + + have = ieee80211_get_eht_cap_mcs_nss(sta_he_cap, + sta_eht_cap, + i, bw); + have_rx_nss = u8_get_bits(have, + IEEE80211_EHT_MCS_NSS_RX); + have_tx_nss = u8_get_bits(have, + IEEE80211_EHT_MCS_NSS_TX); + + if (req_rx_nss > have_rx_nss || + req_tx_nss > have_tx_nss) + return false; + } + } + + return true; +} + +static struct ieee802_11_elems * +ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, + struct ieee80211_conn_settings *conn, + struct cfg80211_bss *cbss, int link_id, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_local *local = sdata->local; + const struct cfg80211_bss_ies *ies = rcu_dereference(cbss->ies); + struct ieee80211_bss *bss = (void *)cbss->priv; + struct ieee80211_channel *channel = cbss->channel; + struct ieee80211_elems_parse_params parse_params = { + .link_id = -1, + .from_ap = true, + .start = ies->data, + .len = ies->len, + .mode = conn->mode, + }; + struct ieee802_11_elems *elems; + struct ieee80211_supported_band *sband; + struct cfg80211_chan_def ap_chandef; + enum ieee80211_conn_mode ap_mode; + int ret; + +again: + elems = ieee802_11_parse_elems_full(&parse_params); + if (!elems) + return ERR_PTR(-ENOMEM); + + ap_mode = ieee80211_determine_ap_chan(sdata, channel, bss->vht_cap_info, + elems, false, conn, &ap_chandef); + + mlme_link_id_dbg(sdata, link_id, "determined AP %pM to be %s\n", + cbss->bssid, ieee80211_conn_mode_str(ap_mode)); + + /* this should be impossible since parsing depends on our mode */ + if (WARN_ON(ap_mode > conn->mode)) { + ret = -EINVAL; + goto free; + } + + sband = sdata->local->hw.wiphy->bands[channel->band]; + + switch (channel->band) { + case NL80211_BAND_S1GHZ: + if (WARN_ON(ap_mode != IEEE80211_CONN_MODE_S1G)) { + ret = -EINVAL; + goto free; + } + return elems; + case NL80211_BAND_6GHZ: + if (ap_mode < IEEE80211_CONN_MODE_HE) { + sdata_info(sdata, + "Rejecting non-HE 6/7 GHz connection"); + ret = -EINVAL; + goto free; + } + break; + default: + if (WARN_ON(ap_mode == IEEE80211_CONN_MODE_S1G)) { + ret = -EINVAL; + goto free; + } + } + + switch (ap_mode) { + case IEEE80211_CONN_MODE_S1G: + WARN_ON(1); + ret = -EINVAL; + goto free; + case IEEE80211_CONN_MODE_LEGACY: + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; + break; + case IEEE80211_CONN_MODE_HT: + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_40); + break; + case IEEE80211_CONN_MODE_VHT: + case IEEE80211_CONN_MODE_HE: + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_160); + break; + case IEEE80211_CONN_MODE_EHT: + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_320); + break; + } + + conn->mode = ap_mode; + *chandef = ap_chandef; + + while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, + IEEE80211_CHAN_DISABLED)) { + if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { + ret = -EINVAL; + goto free; + } + + ieee80211_chandef_downgrade(chandef, conn); + } + + if (conn->mode >= IEEE80211_CONN_MODE_HE && + !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, + IEEE80211_CHAN_NO_HE)) { + conn->mode = IEEE80211_CONN_MODE_VHT; + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_160); + } + + if (conn->mode >= IEEE80211_CONN_MODE_EHT && + !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, + IEEE80211_CHAN_NO_EHT)) { + conn->mode = IEEE80211_CONN_MODE_HE; + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_160); + } + + if (chandef->width != ap_chandef.width || ap_mode != conn->mode) + sdata_info(sdata, + "regulatory prevented using AP config, downgraded\n"); + + if (conn->mode >= IEEE80211_CONN_MODE_HE && + (!ieee80211_verify_peer_he_mcs_support(sdata, (void *)elems->he_cap, + elems->he_operation) || + !ieee80211_verify_sta_he_mcs_support(sdata, sband, + elems->he_operation))) { + conn->mode = IEEE80211_CONN_MODE_VHT; + sdata_info(sdata, "required MCSes not supported, disabling HE\n"); + } + + if (conn->mode >= IEEE80211_CONN_MODE_EHT && + !ieee80211_verify_sta_eht_mcs_support(sdata, sband, + elems->eht_operation)) { + conn->mode = IEEE80211_CONN_MODE_HE; + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_160); + sdata_info(sdata, "required MCSes not supported, disabling EHT\n"); + } + + if (conn->mode >= IEEE80211_CONN_MODE_EHT) { + const struct ieee80211_eht_operation *eht_oper; + + eht_oper = elems->eht_operation; + + if (WARN_ON_ONCE(!eht_oper)) { + ret = -EINVAL; + goto free; + } + + if (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT && + eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT) { + const struct ieee80211_eht_operation_info *info = + (void *)eht_oper->optional; + const u8 *disable_subchannel_bitmap = info->optional; + u16 bitmap; + + bitmap = get_unaligned_le16(disable_subchannel_bitmap); + if (!cfg80211_valid_disable_subchannel_bitmap(&bitmap, + &ap_chandef) || + (bitmap && + ieee80211_hw_check(&local->hw, DISALLOW_PUNCTURING))) { + conn->mode = IEEE80211_CONN_MODE_HE; + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_160); + sdata_info(sdata, + "AP has invalid/unsupported puncturing, disabling EHT\n"); + } + /* FIXME: store puncturing bitmap */ + } + } + + /* the mode can only decrease, so this must terminate */ + if (ap_mode != conn->mode) + goto again; + + mlme_link_id_dbg(sdata, link_id, + "connecting with %s mode, max bandwidth %d MHz\n", + ieee80211_conn_mode_str(conn->mode), + 20 * (1 << conn->bw_limit)); + + if (WARN_ON_ONCE(!cfg80211_chandef_valid(chandef))) { + ret = -EINVAL; + goto free; + } + + return elems; +free: + kfree(elems); + return ERR_PTR(ret); +} + +static int ieee80211_config_bw(struct ieee80211_link_data *link, + struct ieee802_11_elems *elems, + u64 *changed) +{ + struct ieee80211_channel *channel = link->conf->chandef.chan; + struct ieee80211_sub_if_data *sdata = link->sdata; + struct cfg80211_chan_def ap_chandef; + enum ieee80211_conn_mode ap_mode; + u32 vht_cap_info = 0; + u16 ht_opmode; + int ret; + + /* don't track any bandwidth changes in legacy/S1G modes */ + if (link->u.mgd.conn.mode == IEEE80211_CONN_MODE_LEGACY || + link->u.mgd.conn.mode == IEEE80211_CONN_MODE_S1G) + return 0; + + if (elems->vht_cap_elem) + vht_cap_info = le32_to_cpu(elems->vht_cap_elem->vht_cap_info); + + ap_mode = ieee80211_determine_ap_chan(sdata, channel, vht_cap_info, + elems, true, &link->u.mgd.conn, + &ap_chandef); + + if (ap_mode != link->u.mgd.conn.mode) { + link_info(link, + "AP appears to change mode (expected %s, found %s), disconnect\n", + ieee80211_conn_mode_str(link->u.mgd.conn.mode), + ieee80211_conn_mode_str(ap_mode)); + return -EINVAL; + } + + /* + * if HT operation mode changed store the new one - + * this may be applicable even if channel is identical + */ + if (elems->ht_operation) { + ht_opmode = le16_to_cpu(elems->ht_operation->operation_mode); + if (link->conf->ht_operation_mode != ht_opmode) { + *changed |= BSS_CHANGED_HT; + link->conf->ht_operation_mode = ht_opmode; + } + } + + /* + * Downgrade the new channel if we associated with restricted + * bandwidth capabilities. For example, if we associated as a + * 20 MHz STA to a 40 MHz AP (due to regulatory, capabilities + * or config reasons) then switching to a 40 MHz channel now + * won't do us any good -- we couldn't use it with the AP. + */ + while (link->u.mgd.conn.bw_limit < + ieee80211_min_bw_limit_from_chandef(&ap_chandef)) + ieee80211_chandef_downgrade(&ap_chandef, NULL); + + if (cfg80211_chandef_identical(&ap_chandef, &link->conf->chandef)) + return 0; + + link_info(link, + "AP %pM changed bandwidth, new config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n", + link->u.mgd.bssid, ap_chandef.chan->center_freq, + ap_chandef.chan->freq_offset, ap_chandef.width, + ap_chandef.center_freq1, ap_chandef.freq1_offset, + ap_chandef.center_freq2); + + if (!cfg80211_chandef_valid(&ap_chandef)) { + sdata_info(sdata, + "AP %pM changed caps/bw in a way we can't support - disconnect\n", + link->u.mgd.bssid); + return -EINVAL; + } + + /* + * We're tracking the current AP here, so don't do any further checks + * here. This keeps us from playing ping-pong with regulatory, without + * it the following can happen (for example): * - connect to an AP with 80 MHz, world regdom allows 80 MHz * - AP advertises regdom US * - CRDA loads regdom US with 80 MHz prohibited (old database) - * - the code below detects an unsupported channel, downgrades, and - * we disconnect from the AP in the caller + * - we detect an unsupported channel and disconnect * - disconnect causes CRDA to reload world regdomain and the game * starts anew. * (see https://bugzilla.kernel.org/show_bug.cgi?id=70881) @@ -442,160 +948,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, * bandwidth changes where a this could happen, but those cases are * less common and wouldn't completely prevent using the AP. */ - if (tracking && - cfg80211_chandef_identical(chandef, &link->conf->chandef)) - return ret; - - /* don't print the message below for VHT mismatch if VHT is disabled */ - if (ret & IEEE80211_CONN_DISABLE_VHT) - vht_chandef = *chandef; - - /* - * Ignore the DISABLED flag when we're already connected and only - * tracking the APs beacon for bandwidth changes - otherwise we - * might get disconnected here if we connect to an AP, update our - * regulatory information based on the AP's country IE and the - * information we have is wrong/outdated and disables the channel - * that we're actually using for the connection to the AP. - */ - while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, - tracking ? 0 : - IEEE80211_CHAN_DISABLED)) { - if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { - ret = IEEE80211_CONN_DISABLE_HT | - IEEE80211_CONN_DISABLE_VHT | - IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT; - break; - } - - ret |= ieee80211_chandef_downgrade(chandef); - } - - if (!he_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, - IEEE80211_CHAN_NO_HE)) - ret |= IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT; - - if (!eht_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, - IEEE80211_CHAN_NO_EHT)) - ret |= IEEE80211_CONN_DISABLE_EHT; - - if (chandef->width != vht_chandef.width && !tracking) - sdata_info(sdata, - "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n"); - - WARN_ON_ONCE(!cfg80211_chandef_valid(chandef)); - return ret; -} - -static int ieee80211_config_bw(struct ieee80211_link_data *link, - struct ieee802_11_elems *elems, - const u8 *bssid, u64 *changed) -{ - const struct ieee80211_vht_cap *vht_cap = elems->vht_cap_elem; - const struct ieee80211_ht_operation *ht_oper = elems->ht_operation; - const struct ieee80211_vht_operation *vht_oper = elems->vht_operation; - const struct ieee80211_he_operation *he_oper = elems->he_operation; - const struct ieee80211_eht_operation *eht_oper = elems->eht_operation; - const struct ieee80211_s1g_oper_ie *s1g_oper = elems->s1g_oper; - struct ieee80211_sub_if_data *sdata = link->sdata; - struct ieee80211_local *local = sdata->local; - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_channel *chan = link->conf->chandef.chan; - struct ieee80211_supported_band *sband = - local->hw.wiphy->bands[chan->band]; - struct cfg80211_chan_def chandef; - u16 ht_opmode; - ieee80211_conn_flags_t flags; - u32 vht_cap_info = 0; - int ret; - - /* if HT was/is disabled, don't track any bandwidth changes */ - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT || !ht_oper) - return 0; - - /* don't check VHT if we associated as non-VHT station */ - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) - vht_oper = NULL; - - /* don't check HE if we associated as non-HE station */ - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE || - !ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif)) { - he_oper = NULL; - eht_oper = NULL; - } - - /* don't check EHT if we associated as non-EHT station */ - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT || - !ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif)) - eht_oper = NULL; - - /* - * if bss configuration changed store the new one - - * this may be applicable even if channel is identical - */ - ht_opmode = le16_to_cpu(ht_oper->operation_mode); - if (link->conf->ht_operation_mode != ht_opmode) { - *changed |= BSS_CHANGED_HT; - link->conf->ht_operation_mode = ht_opmode; - } - - if (vht_cap) - vht_cap_info = le32_to_cpu(vht_cap->vht_cap_info); - - /* calculate new channel (type) based on HT/VHT/HE operation IEs */ - flags = ieee80211_determine_chantype(sdata, link, - link->u.mgd.conn_flags, - sband, chan, vht_cap_info, - ht_oper, vht_oper, - he_oper, eht_oper, - s1g_oper, &chandef, true); - - /* - * Downgrade the new channel if we associated with restricted - * capabilities. For example, if we associated as a 20 MHz STA - * to a 40 MHz AP (due to regulatory, capabilities or config - * reasons) then switching to a 40 MHz channel now won't do us - * any good -- we couldn't use it with the AP. - */ - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ && - chandef.width == NL80211_CHAN_WIDTH_80P80) - flags |= ieee80211_chandef_downgrade(&chandef); - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_160MHZ && - chandef.width == NL80211_CHAN_WIDTH_160) - flags |= ieee80211_chandef_downgrade(&chandef); - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_40MHZ && - chandef.width > NL80211_CHAN_WIDTH_20) - flags |= ieee80211_chandef_downgrade(&chandef); - - if (cfg80211_chandef_identical(&chandef, &link->conf->chandef)) - return 0; - - link_info(link, - "AP %pM changed bandwidth, new config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n", - link->u.mgd.bssid, chandef.chan->center_freq, - chandef.chan->freq_offset, chandef.width, - chandef.center_freq1, chandef.freq1_offset, - chandef.center_freq2); - - if (flags != (link->u.mgd.conn_flags & - (IEEE80211_CONN_DISABLE_HT | - IEEE80211_CONN_DISABLE_VHT | - IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT | - IEEE80211_CONN_DISABLE_40MHZ | - IEEE80211_CONN_DISABLE_80P80MHZ | - IEEE80211_CONN_DISABLE_160MHZ | - IEEE80211_CONN_DISABLE_320MHZ)) || - !cfg80211_chandef_valid(&chandef)) { - sdata_info(sdata, - "AP %pM changed caps/bw in a way we can't support (0x%x/0x%x) - disconnect\n", - link->u.mgd.bssid, flags, ifmgd->flags); - return -EINVAL; - } - - ret = ieee80211_link_change_bandwidth(link, &chandef, changed); + ret = ieee80211_link_change_bandwidth(link, &ap_chandef, changed); if (ret) { sdata_info(sdata, "AP %pM changed bandwidth to incompatible one - disconnect\n", @@ -614,7 +968,7 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_channel *channel, enum ieee80211_smps_mode smps, - ieee80211_conn_flags_t conn_flags) + const struct ieee80211_conn_settings *conn) { u8 *pos; u32 flags = channel->flags; @@ -649,7 +1003,7 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, * capable of 40 MHz -- some broken APs will never fall * back to trying to transmit in 20 MHz. */ - if (conn_flags & IEEE80211_CONN_DISABLE_40MHZ) { + if (conn->bw_limit <= IEEE80211_CONN_BW_LIMIT_20) { cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; cap &= ~IEEE80211_HT_CAP_SGI_40; } @@ -688,7 +1042,7 @@ static bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_supported_band *sband, struct ieee80211_vht_cap *ap_vht_cap, - ieee80211_conn_flags_t conn_flags) + const struct ieee80211_conn_settings *conn) { struct ieee80211_local *local = sdata->local; u8 *pos; @@ -705,16 +1059,7 @@ static bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, /* determine capability flags */ cap = vht_cap.cap; - if (conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ) { - u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; - - cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; - if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || - bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) - cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; - } - - if (conn_flags & IEEE80211_CONN_DISABLE_160MHZ) { + if (conn->bw_limit <= IEEE80211_CONN_BW_LIMIT_80) { cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; } @@ -778,7 +1123,7 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_supported_band *sband, enum ieee80211_smps_mode smps_mode, - ieee80211_conn_flags_t conn_flags) + const struct ieee80211_conn_settings *conn) { u8 *pos, *pre_he_pos; const struct ieee80211_sta_he_cap *he_cap; @@ -796,8 +1141,7 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, he_cap->he_cap_elem.phy_cap_info); pos = skb_put(skb, he_cap_size); pre_he_pos = pos; - pos = ieee80211_ie_build_he_cap(conn_flags, - pos, he_cap, pos + he_cap_size); + pos = ieee80211_ie_build_he_cap(conn, he_cap, pos, pos + he_cap_size); /* trim excess if any */ skb_trim(skb, skb->len - (pre_he_pos + he_cap_size - pos)); @@ -1135,11 +1479,11 @@ static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, offset); if (sband->band != NL80211_BAND_6GHZ && - !(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HT)) { + assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_HT) { ieee80211_add_ht_ie(sdata, skb, assoc_data->link[link_id].ap_ht_param, sband, chan, smps_mode, - assoc_data->link[link_id].conn_flags); + &assoc_data->link[link_id].conn); ADD_PRESENT_ELEM(WLAN_EID_HT_CAPABILITY); } @@ -1149,36 +1493,25 @@ static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, offset); if (sband->band != NL80211_BAND_6GHZ && - !(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_VHT)) { + assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_VHT) { bool mu_mimo_owner = ieee80211_add_vht_ie(sdata, skb, sband, &assoc_data->link[link_id].ap_vht_cap, - assoc_data->link[link_id].conn_flags); + &assoc_data->link[link_id].conn); if (link) link->conf->mu_mimo_owner = mu_mimo_owner; ADD_PRESENT_ELEM(WLAN_EID_VHT_CAPABILITY); } - /* - * If AP doesn't support HT, mark HE and EHT as disabled. - * If on the 5GHz band, make sure it supports VHT. - */ - if (assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HT || - (sband->band == NL80211_BAND_5GHZ && - assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_VHT)) - assoc_data->link[link_id].conn_flags |= - IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT; - /* if present, add any custom IEs that go before HE */ offset = ieee80211_add_before_he_elems(skb, extra_elems, extra_elems_len, offset); - if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HE)) { + if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_HE) { ieee80211_add_he_ie(sdata, skb, sband, smps_mode, - assoc_data->link[link_id].conn_flags); + &assoc_data->link[link_id].conn); ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_HE_CAPABILITY); } @@ -1187,7 +1520,7 @@ static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, * calling ieee80211_assoc_add_ml_elem(), so add this one if * we're going to put it after the ML element */ - if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_EHT)) + if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_EHT) ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_EHT_CAPABILITY); if (link_id == assoc_data->assoc_link_id) @@ -1197,7 +1530,7 @@ static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, /* crash if somebody gets it wrong */ present_elems = NULL; - if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_EHT)) + if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_EHT) ieee80211_add_eht_ie(sdata, skb, sband); if (sband->band == NL80211_BAND_S1GHZ) { @@ -1208,9 +1541,6 @@ static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, if (iftd && iftd->vendor_elems.data && iftd->vendor_elems.len) skb_put_data(skb, iftd->vendor_elems.data, iftd->vendor_elems.len); - if (link) - link->u.mgd.conn_flags = assoc_data->link[link_id].conn_flags; - return offset; } @@ -1499,7 +1829,7 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) /* Set MBSSID support for HE AP if needed */ if (ieee80211_hw_check(&local->hw, SUPPORTS_ONLY_HE_MULTI_BSSID) && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HE && ext_capa && ext_capa->datalen >= 3) ext_capa->data[2] |= WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT; @@ -1544,7 +1874,7 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) * for some reason check it and want it to be set, set the bit for all * pre-EHT connections as we used to do. */ - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT) + if (link->u.mgd.conn.mode < IEEE80211_CONN_MODE_EHT) capab |= WLAN_CAPABILITY_ESS; /* add the elements for the assoc (main) link */ @@ -1876,7 +2206,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, bss = (void *)cbss->priv; res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band, bss->vht_cap_info, - link->u.mgd.conn_flags, + &link->u.mgd.conn, link->u.mgd.bssid, &csa_ie); if (!res) { @@ -3057,8 +3387,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sdata->deflink.u.mgd.tracking_signal_avg = false; sdata->deflink.u.mgd.disable_wmm_tracking = false; + sdata->vif.bss_conf.eht_puncturing = 0; + ifmgd->flags = 0; - sdata->deflink.u.mgd.conn_flags = 0; for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { struct ieee80211_link_data *link; @@ -3526,7 +3857,6 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, sta_info_destroy_addr(sdata, auth_data->ap_addr); /* other links are destroyed */ - sdata->deflink.u.mgd.conn_flags = 0; eth_zero_addr(sdata->deflink.u.mgd.bssid); ieee80211_link_info_change_notify(sdata, &sdata->deflink, BSS_CHANGED_BSSID); @@ -3564,7 +3894,6 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, del_timer_sync(&sdata->u.mgd.timer); sta_info_destroy_addr(sdata, assoc_data->ap_addr); - sdata->deflink.u.mgd.conn_flags = 0; eth_zero_addr(sdata->deflink.u.mgd.bssid); ieee80211_link_info_change_notify(sdata, &sdata->deflink, BSS_CHANGED_BSSID); @@ -4014,11 +4343,13 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, struct ieee80211_local *local = sdata->local; unsigned int link_id = link->link_id; struct ieee80211_elems_parse_params parse_params = { + .mode = link->u.mgd.conn.mode, .start = elem_start, .len = elem_len, .link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id, .from_ap = true, }; + bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ; bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ; const struct cfg80211_bss_ies *bss_ies = NULL; @@ -4094,9 +4425,9 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, */ if (!is_6ghz && ((assoc_data->wmm && !elems->wmm_param) || - (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) && + (link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT && (!elems->ht_cap_elem || !elems->ht_operation)) || - (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) && + (link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT && (!elems->vht_cap_elem || !elems->vht_operation)))) { const struct cfg80211_bss_ies *ies; struct ieee802_11_elems *bss_elems; @@ -4133,25 +4464,25 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, * have to include the IEs in the (re)association response. */ if (!elems->ht_cap_elem && bss_elems->ht_cap_elem && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) { + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT) { elems->ht_cap_elem = bss_elems->ht_cap_elem; sdata_info(sdata, "AP bug: HT capability missing from AssocResp\n"); } if (!elems->ht_operation && bss_elems->ht_operation && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) { + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT) { elems->ht_operation = bss_elems->ht_operation; sdata_info(sdata, "AP bug: HT operation missing from AssocResp\n"); } if (!elems->vht_cap_elem && bss_elems->vht_cap_elem && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT) { elems->vht_cap_elem = bss_elems->vht_cap_elem; sdata_info(sdata, "AP bug: VHT capa missing from AssocResp\n"); } if (!elems->vht_operation && bss_elems->vht_operation && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT) { elems->vht_operation = bss_elems->vht_operation; sdata_info(sdata, "AP bug: VHT operation missing from AssocResp\n"); @@ -4164,7 +4495,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, * We previously checked these in the beacon/probe response, so * they should be present here. This is just a safety net. */ - if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) && + if (!is_6ghz && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT && (!elems->wmm_param || !elems->ht_cap_elem || !elems->ht_operation)) { sdata_info(sdata, "HT AP is missing WMM params or HT capability/operation\n"); @@ -4172,7 +4503,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, goto out; } - if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) && + if (is_5ghz && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT && (!elems->vht_cap_elem || !elems->vht_operation)) { sdata_info(sdata, "VHT AP is missing VHT capability/operation\n"); @@ -4180,36 +4511,20 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, goto out; } - if (is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && - !elems->he_6ghz_capa) { - sdata_info(sdata, - "HE 6 GHz AP is missing HE 6 GHz band capability\n"); - ret = false; - goto out; - } - if (WARN_ON(!link->conf->chandef.chan)) { ret = false; goto out; } sband = local->hw.wiphy->bands[link->conf->chandef.chan->band]; - if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && - (!elems->he_cap || !elems->he_operation)) { - sdata_info(sdata, - "HE AP is missing HE capability/operation\n"); - ret = false; - goto out; - } - /* Set up internal HT/VHT capabilities */ - if (elems->ht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) + if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems->ht_cap_elem, link_sta); if (elems->vht_cap_elem && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT) { const struct ieee80211_vht_cap *bss_vht_cap = NULL; const struct cfg80211_bss_ies *ies; @@ -4236,7 +4551,8 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, rcu_read_unlock(); } - if (elems->he_operation && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && + if (elems->he_operation && + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HE && elems->he_cap) { const struct ieee80211_he_6ghz_oper *he_6ghz_oper; @@ -4283,7 +4599,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, link_sta, elems); if (elems->eht_operation && elems->eht_cap && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) { + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_EHT) { ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband, elems->he_cap, elems->he_cap_len, @@ -4490,7 +4806,7 @@ static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link, bool support_160; u8 chains = 1; - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) + if (link->u.mgd.conn.mode < IEEE80211_CONN_MODE_HT) return chains; ht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_HT_CAPABILITY); @@ -4503,7 +4819,7 @@ static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link, */ } - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) + if (link->u.mgd.conn.mode < IEEE80211_CONN_MODE_VHT) return chains; vht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_VHT_CAPABILITY); @@ -4522,7 +4838,7 @@ static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link, chains = max(chains, nss); } - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) + if (link->u.mgd.conn.mode < IEEE80211_CONN_MODE_HE) return chains; ies = rcu_dereference(cbss->ies); @@ -4573,465 +4889,311 @@ static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link, return chains; } -static bool -ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata, - const struct ieee80211_he_cap_elem *he_cap, - const struct ieee80211_he_operation *he_op) +static void +ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + struct cfg80211_assoc_request *req, + bool wmm_used, int link_id, + struct ieee80211_conn_settings *conn) { - struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp; - u16 mcs_80_map_tx, mcs_80_map_rx; - u16 ap_min_req_set; - int nss; + struct ieee80211_sta_ht_cap sta_ht_cap = sband->ht_cap; + bool is_5ghz = sband->band == NL80211_BAND_5GHZ; + bool is_6ghz = sband->band == NL80211_BAND_6GHZ; + const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; + struct ieee80211_sta_vht_cap vht_cap; - if (!he_cap) - return false; - - /* mcs_nss is right after he_cap info */ - he_mcs_nss_supp = (void *)(he_cap + 1); - - mcs_80_map_tx = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80); - mcs_80_map_rx = le16_to_cpu(he_mcs_nss_supp->rx_mcs_80); - - /* P802.11-REVme/D0.3 - * 27.1.1 Introduction to the HE PHY - * ... - * An HE STA shall support the following features: - * ... - * Single spatial stream HE-MCSs 0 to 7 (transmit and receive) in all - * supported channel widths for HE SU PPDUs - */ - if ((mcs_80_map_tx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED || - (mcs_80_map_rx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED) { - sdata_info(sdata, - "Missing mandatory rates for 1 Nss, rx 0x%x, tx 0x%x, disable HE\n", - mcs_80_map_tx, mcs_80_map_rx); - return false; + if (sband->band == NL80211_BAND_S1GHZ) { + conn->mode = IEEE80211_CONN_MODE_S1G; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; + mlme_dbg(sdata, "operating as S1G STA\n"); + return; } - if (!he_op) - return true; + conn->mode = IEEE80211_CONN_MODE_LEGACY; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; - ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); + ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); - /* - * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all - * zeroes, which is nonsense, and completely inconsistent with itself - * (it doesn't have 8 streams). Accept the settings in this case anyway. - */ - if (!ap_min_req_set) - return true; - - /* make sure the AP is consistent with itself - * - * P802.11-REVme/D0.3 - * 26.17.1 Basic HE BSS operation - * - * A STA that is operating in an HE BSS shall be able to receive and - * transmit at each of the tuple values indicated by the - * Basic HE-MCS And NSS Set field of the HE Operation parameter of the - * MLME-START.request primitive and shall be able to receive at each of - * the tuple values indicated by the Supported HE-MCS and - * NSS Set field in the HE Capabilities parameter of the MLMESTART.request - * primitive - */ - for (nss = 8; nss > 0; nss--) { - u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; - u8 ap_rx_val; - u8 ap_tx_val; - - if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED) - continue; - - ap_rx_val = (mcs_80_map_rx >> (2 * (nss - 1))) & 3; - ap_tx_val = (mcs_80_map_tx >> (2 * (nss - 1))) & 3; - - if (ap_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || - ap_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || - ap_rx_val < ap_op_val || ap_tx_val < ap_op_val) { - sdata_info(sdata, - "Invalid rates for %d Nss, rx %d, tx %d oper %d, disable HE\n", - nss, ap_rx_val, ap_rx_val, ap_op_val); - return false; - } + if (req && req->flags & ASSOC_REQ_DISABLE_HT) { + mlme_link_id_dbg(sdata, link_id, + "HT disabled by flag, limiting to legacy\n"); + goto out; } - return true; -} + if (!wmm_used) { + mlme_link_id_dbg(sdata, link_id, + "WMM/QoS not supported, limiting to legacy\n"); + goto out; + } -static bool -ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata, - struct ieee80211_supported_band *sband, - const struct ieee80211_he_operation *he_op) -{ - const struct ieee80211_sta_he_cap *sta_he_cap = - ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); - u16 ap_min_req_set; - int i; + if (req) { + unsigned int i; - if (!sta_he_cap || !he_op) - return false; - - ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); - - /* - * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all - * zeroes, which is nonsense, and completely inconsistent with itself - * (it doesn't have 8 streams). Accept the settings in this case anyway. - */ - if (!ap_min_req_set) - return true; - - /* Need to go over for 80MHz, 160MHz and for 80+80 */ - for (i = 0; i < 3; i++) { - const struct ieee80211_he_mcs_nss_supp *sta_mcs_nss_supp = - &sta_he_cap->he_mcs_nss_supp; - u16 sta_mcs_map_rx = - le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i]); - u16 sta_mcs_map_tx = - le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i + 1]); - u8 nss; - bool verified = true; - - /* - * For each band there is a maximum of 8 spatial streams - * possible. Each of the sta_mcs_map_* is a 16-bit struct built - * of 2 bits per NSS (1-8), with the values defined in enum - * ieee80211_he_mcs_support. Need to make sure STA TX and RX - * capabilities aren't less than the AP's minimum requirements - * for this HE BSS per SS. - * It is enough to find one such band that meets the reqs. - */ - for (nss = 8; nss > 0; nss--) { - u8 sta_rx_val = (sta_mcs_map_rx >> (2 * (nss - 1))) & 3; - u8 sta_tx_val = (sta_mcs_map_tx >> (2 * (nss - 1))) & 3; - u8 ap_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; - - if (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED) - continue; - - /* - * Make sure the HE AP doesn't require MCSs that aren't - * supported by the client as required by spec - * - * P802.11-REVme/D0.3 - * 26.17.1 Basic HE BSS operation - * - * An HE STA shall not attempt to join * (MLME-JOIN.request primitive) - * a BSS, unless it supports (i.e., is able to both transmit and - * receive using) all of the tuples in the basic - * HE-MCS and NSS set. - */ - if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || - sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || - (ap_val > sta_rx_val) || (ap_val > sta_tx_val)) { - verified = false; - break; + for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) { + if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || + req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || + req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { + netdev_info(sdata->dev, + "WEP/TKIP use, limiting to legacy\n"); + goto out; } } - - if (verified) - return true; } - /* If here, STA doesn't meet AP's HE min requirements */ - return false; -} - -static u8 -ieee80211_get_eht_cap_mcs_nss(const struct ieee80211_sta_he_cap *sta_he_cap, - const struct ieee80211_sta_eht_cap *sta_eht_cap, - unsigned int idx, int bw) -{ - u8 he_phy_cap0 = sta_he_cap->he_cap_elem.phy_cap_info[0]; - u8 eht_phy_cap0 = sta_eht_cap->eht_cap_elem.phy_cap_info[0]; - - /* handle us being a 20 MHz-only EHT STA - with four values - * for MCS 0-7, 8-9, 10-11, 12-13. - */ - if (!(he_phy_cap0 & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) - return sta_eht_cap->eht_mcs_nss_supp.only_20mhz.rx_tx_max_nss[idx]; - - /* the others have MCS 0-9 together, rather than separately from 0-7 */ - if (idx > 0) - idx--; - - switch (bw) { - case 0: - return sta_eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_max_nss[idx]; - case 1: - if (!(he_phy_cap0 & - (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G))) - return 0xff; /* pass check */ - return sta_eht_cap->eht_mcs_nss_supp.bw._160.rx_tx_max_nss[idx]; - case 2: - if (!(eht_phy_cap0 & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)) - return 0xff; /* pass check */ - return sta_eht_cap->eht_mcs_nss_supp.bw._320.rx_tx_max_nss[idx]; + if (!sta_ht_cap.ht_supported && !is_6ghz) { + mlme_link_id_dbg(sdata, link_id, + "HT not supported (and not on 6 GHz), limiting to legacy\n"); + goto out; } - WARN_ON(1); - return 0; -} + /* HT is fine */ + conn->mode = IEEE80211_CONN_MODE_HT; + conn->bw_limit = sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? + IEEE80211_CONN_BW_LIMIT_40 : + IEEE80211_CONN_BW_LIMIT_20; -static bool -ieee80211_verify_sta_eht_mcs_support(struct ieee80211_sub_if_data *sdata, - struct ieee80211_supported_band *sband, - const struct ieee80211_eht_operation *eht_op) -{ - const struct ieee80211_sta_he_cap *sta_he_cap = - ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); - const struct ieee80211_sta_eht_cap *sta_eht_cap = - ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); - const struct ieee80211_eht_mcs_nss_supp_20mhz_only *req; - unsigned int i; + memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); + ieee80211_apply_vhtcap_overrides(sdata, &vht_cap); - if (!sta_he_cap || !sta_eht_cap || !eht_op) - return false; + if (req && req->flags & ASSOC_REQ_DISABLE_VHT) { + mlme_link_id_dbg(sdata, link_id, + "VHT disabled by flag, limiting to HT\n"); + goto out; + } - req = &eht_op->basic_mcs_nss; + if (vht_cap.vht_supported && is_5ghz) { + bool have_80mhz = false; + unsigned int i; - for (i = 0; i < ARRAY_SIZE(req->rx_tx_max_nss); i++) { - u8 req_rx_nss, req_tx_nss; - unsigned int bw; - - req_rx_nss = u8_get_bits(req->rx_tx_max_nss[i], - IEEE80211_EHT_MCS_NSS_RX); - req_tx_nss = u8_get_bits(req->rx_tx_max_nss[i], - IEEE80211_EHT_MCS_NSS_TX); - - for (bw = 0; bw < 3; bw++) { - u8 have, have_rx_nss, have_tx_nss; - - have = ieee80211_get_eht_cap_mcs_nss(sta_he_cap, - sta_eht_cap, - i, bw); - have_rx_nss = u8_get_bits(have, - IEEE80211_EHT_MCS_NSS_RX); - have_tx_nss = u8_get_bits(have, - IEEE80211_EHT_MCS_NSS_TX); - - if (req_rx_nss > have_rx_nss || - req_tx_nss > have_tx_nss) - return false; + if (conn->bw_limit == IEEE80211_CONN_BW_LIMIT_20) { + mlme_link_id_dbg(sdata, link_id, + "no 40 MHz support on 5 GHz, limiting to HT\n"); + goto out; } + + /* Allow VHT if at least one channel on the sband supports 80 MHz */ + for (i = 0; i < sband->n_channels; i++) { + if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_NO_80MHZ)) + continue; + + have_80mhz = true; + break; + } + + if (!have_80mhz) { + mlme_link_id_dbg(sdata, link_id, + "no 80 MHz channel support on 5 GHz, limiting to HT\n"); + goto out; + } + } else if (is_5ghz) { /* !vht_supported but on 5 GHz */ + mlme_link_id_dbg(sdata, link_id, + "no VHT support on 5 GHz, limiting to HT\n"); + goto out; } - return true; + /* VHT - if we have - is fine, including 80 MHz, check 160 below again */ + if (sband->band != NL80211_BAND_2GHZ) { + conn->mode = IEEE80211_CONN_MODE_VHT; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_160; + } + + if (is_5ghz && + !(vht_cap.cap & (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80; + mlme_link_id_dbg(sdata, link_id, + "no VHT 160 MHz capability on 5 GHz, limiting to 80 MHz"); + } + + if (req && req->flags & ASSOC_REQ_DISABLE_HE) { + mlme_link_id_dbg(sdata, link_id, + "HE disabled by flag, limiting to HT/VHT\n"); + goto out; + } + + he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); + if (!he_cap) { + WARN_ON(is_6ghz); + mlme_link_id_dbg(sdata, link_id, + "no HE support, limiting to HT/VHT\n"); + goto out; + } + + /* so we have HE */ + conn->mode = IEEE80211_CONN_MODE_HE; + + /* check bandwidth */ + switch (sband->band) { + default: + case NL80211_BAND_2GHZ: + if (he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) + break; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; + mlme_link_id_dbg(sdata, link_id, + "no 40 MHz HE cap in 2.4 GHz, limiting to 20 MHz\n"); + break; + case NL80211_BAND_5GHZ: + if (!(he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) { + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; + mlme_link_id_dbg(sdata, link_id, + "no 40/80 MHz HE cap in 5 GHz, limiting to 20 MHz\n"); + break; + } + if (!(he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G)) { + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_80); + mlme_link_id_dbg(sdata, link_id, + "no 160 MHz HE cap in 5 GHz, limiting to 80 MHz\n"); + } + break; + case NL80211_BAND_6GHZ: + if (he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) + break; + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_80); + mlme_link_id_dbg(sdata, link_id, + "no 160 MHz HE cap in 6 GHz, limiting to 80 MHz\n"); + break; + } + + if (req && req->flags & ASSOC_REQ_DISABLE_EHT) { + mlme_link_id_dbg(sdata, link_id, + "EHT disabled by flag, limiting to HE\n"); + goto out; + } + + eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); + if (!eht_cap) { + mlme_link_id_dbg(sdata, link_id, + "no EHT support, limiting to HE\n"); + goto out; + } + + /* we have EHT */ + + conn->mode = IEEE80211_CONN_MODE_EHT; + + /* check bandwidth */ + if (is_6ghz && + eht_cap->eht_cap_elem.phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_320; + else if (is_6ghz) + mlme_link_id_dbg(sdata, link_id, + "no EHT 320 MHz cap in 6 GHz, limiting to 160 MHz\n"); + +out: + mlme_link_id_dbg(sdata, link_id, + "determined local STA to be %s, BW limited to %d MHz\n", + ieee80211_conn_mode_str(conn->mode), + 20 * (1 << conn->bw_limit)); +} + +static void +ieee80211_determine_our_sta_mode_auth(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + struct cfg80211_auth_request *req, + bool wmm_used, + struct ieee80211_conn_settings *conn) +{ + ieee80211_determine_our_sta_mode(sdata, sband, NULL, wmm_used, + req->link_id > 0 ? req->link_id : 0, + conn); +} + +static void +ieee80211_determine_our_sta_mode_assoc(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + struct cfg80211_assoc_request *req, + bool wmm_used, int link_id, + struct ieee80211_conn_settings *conn) +{ + struct ieee80211_conn_settings tmp; + + WARN_ON(!req); + + ieee80211_determine_our_sta_mode(sdata, sband, req, wmm_used, link_id, + &tmp); + + conn->mode = min_t(enum ieee80211_conn_mode, + conn->mode, tmp.mode); + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, tmp.bw_limit); } static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link, - struct cfg80211_bss *cbss, - bool mlo, - ieee80211_conn_flags_t *conn_flags) + int link_id, + struct cfg80211_bss *cbss, bool mlo, + struct ieee80211_conn_settings *conn) { struct ieee80211_local *local = sdata->local; - const struct ieee80211_ht_cap *ht_cap = NULL; - const struct ieee80211_ht_operation *ht_oper = NULL; - const struct ieee80211_vht_operation *vht_oper = NULL; - const struct ieee80211_he_operation *he_oper = NULL; - const struct ieee80211_eht_operation *eht_oper = NULL; - const struct ieee80211_s1g_oper_ie *s1g_oper = NULL; - struct ieee80211_supported_band *sband; struct cfg80211_chan_def chandef; bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; - bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ; - bool supports_mlo = false; - struct ieee80211_bss *bss = (void *)cbss->priv; - struct ieee80211_elems_parse_params parse_params = { - .link_id = -1, - .from_ap = true, - }; struct ieee802_11_elems *elems; - const struct cfg80211_bss_ies *ies; int ret; u32 i; - bool have_80mhz; lockdep_assert_wiphy(local->hw.wiphy); rcu_read_lock(); + elems = ieee80211_determine_chan_mode(sdata, conn, cbss, link_id, + &chandef); - ies = rcu_dereference(cbss->ies); - parse_params.start = ies->data; - parse_params.len = ies->len; - elems = ieee802_11_parse_elems_full(&parse_params); - if (!elems) { + if (IS_ERR(elems)) { rcu_read_unlock(); - return -ENOMEM; + return PTR_ERR(elems); } - sband = local->hw.wiphy->bands[cbss->channel->band]; - - *conn_flags &= ~(IEEE80211_CONN_DISABLE_40MHZ | - IEEE80211_CONN_DISABLE_80P80MHZ | - IEEE80211_CONN_DISABLE_160MHZ); - - /* disable HT/VHT/HE if we don't support them */ - if (!sband->ht_cap.ht_supported && !is_6ghz) { - mlme_dbg(sdata, "HT not supported, disabling HT/VHT/HE/EHT\n"); - *conn_flags |= IEEE80211_CONN_DISABLE_HT; - *conn_flags |= IEEE80211_CONN_DISABLE_VHT; - *conn_flags |= IEEE80211_CONN_DISABLE_HE; - *conn_flags |= IEEE80211_CONN_DISABLE_EHT; + if (mlo && !elems->ml_basic) { + sdata_info(sdata, "Rejecting MLO as it is not supported by AP\n"); + rcu_read_unlock(); + kfree(elems); + return -EINVAL; } - if (!sband->vht_cap.vht_supported && is_5ghz) { - mlme_dbg(sdata, "VHT not supported, disabling VHT/HE/EHT\n"); - *conn_flags |= IEEE80211_CONN_DISABLE_VHT; - *conn_flags |= IEEE80211_CONN_DISABLE_HE; - *conn_flags |= IEEE80211_CONN_DISABLE_EHT; - } + if (link && is_6ghz && conn->mode >= IEEE80211_CONN_MODE_HE) { + struct ieee80211_bss_conf *bss_conf; + u8 j = 0; - if (!ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif)) { - mlme_dbg(sdata, "HE not supported, disabling HE and EHT\n"); - *conn_flags |= IEEE80211_CONN_DISABLE_HE; - *conn_flags |= IEEE80211_CONN_DISABLE_EHT; - } + bss_conf = link->conf; - if (!ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif)) { - mlme_dbg(sdata, "EHT not supported, disabling EHT\n"); - *conn_flags |= IEEE80211_CONN_DISABLE_EHT; - } + if (elems->pwr_constr_elem) + bss_conf->pwr_reduction = *elems->pwr_constr_elem; - if (!(*conn_flags & IEEE80211_CONN_DISABLE_HT) && !is_6ghz) { - ht_oper = elems->ht_operation; - ht_cap = elems->ht_cap_elem; + BUILD_BUG_ON(ARRAY_SIZE(bss_conf->tx_pwr_env) != + ARRAY_SIZE(elems->tx_pwr_env)); - if (!ht_cap) { - *conn_flags |= IEEE80211_CONN_DISABLE_HT; - ht_oper = NULL; + for (i = 0; i < elems->tx_pwr_env_num; i++) { + if (elems->tx_pwr_env_len[i] > sizeof(bss_conf->tx_pwr_env[j])) + continue; + + bss_conf->tx_pwr_env_num++; + memcpy(&bss_conf->tx_pwr_env[j], elems->tx_pwr_env[i], + elems->tx_pwr_env_len[i]); + j++; } } - - if (!(*conn_flags & IEEE80211_CONN_DISABLE_VHT) && !is_6ghz) { - vht_oper = elems->vht_operation; - if (vht_oper && !ht_oper) { - vht_oper = NULL; - sdata_info(sdata, - "AP advertised VHT without HT, disabling HT/VHT/HE\n"); - *conn_flags |= IEEE80211_CONN_DISABLE_HT; - *conn_flags |= IEEE80211_CONN_DISABLE_VHT; - *conn_flags |= IEEE80211_CONN_DISABLE_HE; - *conn_flags |= IEEE80211_CONN_DISABLE_EHT; - } - - if (!elems->vht_cap_elem) { - *conn_flags |= IEEE80211_CONN_DISABLE_VHT; - vht_oper = NULL; - } - } - - if (!(*conn_flags & IEEE80211_CONN_DISABLE_HE)) { - he_oper = elems->he_operation; - - if (link && is_6ghz) { - struct ieee80211_bss_conf *bss_conf; - u8 j = 0; - - bss_conf = link->conf; - - if (elems->pwr_constr_elem) - bss_conf->pwr_reduction = *elems->pwr_constr_elem; - - BUILD_BUG_ON(ARRAY_SIZE(bss_conf->tx_pwr_env) != - ARRAY_SIZE(elems->tx_pwr_env)); - - for (i = 0; i < elems->tx_pwr_env_num; i++) { - if (elems->tx_pwr_env_len[i] > - sizeof(bss_conf->tx_pwr_env[j])) - continue; - - bss_conf->tx_pwr_env_num++; - memcpy(&bss_conf->tx_pwr_env[j], elems->tx_pwr_env[i], - elems->tx_pwr_env_len[i]); - j++; - } - } - - if (!ieee80211_verify_peer_he_mcs_support(sdata, - (void *)elems->he_cap, - he_oper) || - !ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper)) - *conn_flags |= IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT; - } - - /* - * EHT requires HE to be supported as well. Specifically for 6 GHz - * channels, the operation channel information can only be deduced from - * both the 6 GHz operation information (from the HE operation IE) and - * EHT operation. - */ - if (!(*conn_flags & - (IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT)) && - he_oper) { - eht_oper = elems->eht_operation; - - if (!ieee80211_verify_sta_eht_mcs_support(sdata, sband, eht_oper)) - *conn_flags |= IEEE80211_CONN_DISABLE_EHT; - - supports_mlo = elems->ml_basic; - } - - /* Allow VHT if at least one channel on the sband supports 80 MHz */ - have_80mhz = false; - for (i = 0; i < sband->n_channels; i++) { - if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | - IEEE80211_CHAN_NO_80MHZ)) - continue; - - have_80mhz = true; - break; - } - - if (!have_80mhz) { - sdata_info(sdata, "80 MHz not supported, disabling VHT\n"); - *conn_flags |= IEEE80211_CONN_DISABLE_VHT; - } - - if (sband->band == NL80211_BAND_S1GHZ) { - s1g_oper = elems->s1g_oper; - if (!s1g_oper) - sdata_info(sdata, - "AP missing S1G operation element?\n"); - } - - *conn_flags |= - ieee80211_determine_chantype(sdata, link, *conn_flags, - sband, - cbss->channel, - bss->vht_cap_info, - ht_oper, vht_oper, - he_oper, eht_oper, - s1g_oper, - &chandef, false); - - if (link) - link->needed_rx_chains = - min(ieee80211_max_rx_chains(link, cbss), - local->rx_chains); - rcu_read_unlock(); /* the element data was RCU protected so no longer valid anyway */ kfree(elems); elems = NULL; - if (*conn_flags & IEEE80211_CONN_DISABLE_HE && is_6ghz) { - sdata_info(sdata, "Rejecting non-HE 6/7 GHz connection"); - return -EINVAL; - } - - if (mlo && !supports_mlo) { - sdata_info(sdata, "Rejecting MLO as it is not supported by AP\n"); - return -EINVAL; - } - if (!link) return 0; + rcu_read_lock(); + link->needed_rx_chains = min(ieee80211_max_rx_chains(link, cbss), + local->rx_chains); + rcu_read_unlock(); + /* * If this fails (possibly due to channel context sharing * on incompatible channels, e.g. 80+80 and 160 sharing the @@ -5043,15 +5205,14 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, /* don't downgrade for 5 and 10 MHz channels, though. */ if (chandef.width == NL80211_CHAN_WIDTH_5 || chandef.width == NL80211_CHAN_WIDTH_10) - goto out; + return ret; while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { - *conn_flags |= - ieee80211_chandef_downgrade(&chandef); + ieee80211_chandef_downgrade(&chandef, conn); ret = ieee80211_link_use_channel(link, &chandef, IEEE80211_CHANCTX_SHARED); } - out: + return ret; } @@ -5177,8 +5338,10 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, link->conf->dtim_period = link->u.mgd.dtim_period ?: 1; if (link_id != assoc_data->assoc_link_id) { - err = ieee80211_prep_channel(sdata, link, cbss, true, - &link->u.mgd.conn_flags); + link->u.mgd.conn = assoc_data->link[link_id].conn; + + err = ieee80211_prep_channel(sdata, link, link_id, cbss, + true, &link->u.mgd.conn); if (err) { link_info(link, "prep_channel failed\n"); goto out_err; @@ -5296,6 +5459,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (!assoc_data) return; + parse_params.mode = + assoc_data->link[assoc_data->assoc_link_id].conn.mode; + if (!ether_addr_equal(assoc_data->ap_addr, mgmt->bssid) || !ether_addr_equal(assoc_data->ap_addr, mgmt->sa)) return; @@ -6162,6 +6328,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, u8 *bssid, *variable = mgmt->u.beacon.variable; u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; struct ieee80211_elems_parse_params parse_params = { + .mode = link->u.mgd.conn.mode, .link_id = -1, .from_ap = true, }; @@ -6416,10 +6583,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, changed |= ieee80211_recalc_twt_req(sdata, sband, link, link_sta, elems); - if (ieee80211_config_bw(link, elems, bssid, &changed)) { - sdata_info(sdata, - "failed to follow AP %pM bandwidth change, disconnect\n", - bssid); + if (ieee80211_config_bw(link, elems, &changed)) { ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DEAUTH_LEAVING, true, deauth_buf); @@ -6442,7 +6606,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, elems->cisco_dtpc_elem); if (elems->eht_operation && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) { + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_EHT) { if (!ieee80211_config_puncturing(link, elems->eht_operation, &changed)) { ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, @@ -7494,7 +7658,6 @@ void ieee80211_mgd_setup_link(struct ieee80211_link_data *link) unsigned int link_id = link->link_id; link->u.mgd.p2p_noa_index = -1; - link->u.mgd.conn_flags = 0; link->conf->bssid = link->u.mgd.bssid; link->smps_mode = IEEE80211_SMPS_OFF; @@ -7534,6 +7697,7 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss, s8 link_id, const u8 *ap_mld_addr, bool assoc, + struct ieee80211_conn_settings *conn, bool override) { struct ieee80211_local *local = sdata->local; @@ -7665,13 +7829,22 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, } if (new_sta || override) { - err = ieee80211_prep_channel(sdata, link, cbss, mlo, - &link->u.mgd.conn_flags); + /* + * Only set this if we're also going to calculate the AP + * settings etc., otherwise this was set before in a + * previous call. Note override is set to %true in assoc + * if the settings were changed. + */ + link->u.mgd.conn = *conn; + err = ieee80211_prep_channel(sdata, link, link->link_id, cbss, + mlo, &link->u.mgd.conn); if (err) { if (new_sta) sta_info_free(local, new_sta); goto out_err; } + /* pass out for use in assoc */ + *conn = link->u.mgd.conn; } if (new_sta) { @@ -7786,10 +7959,13 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_auth_data *auth_data; + struct ieee80211_conn_settings conn; struct ieee80211_link_data *link; + struct ieee80211_supported_band *sband; + struct ieee80211_bss *bss; u16 auth_alg; int err; - bool cont_auth; + bool cont_auth, wmm_used; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -7920,8 +8096,17 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, /* needed for transmitting the auth frame(s) properly */ memcpy(sdata->vif.cfg.ap_addr, auth_data->ap_addr, ETH_ALEN); + bss = (void *)req->bss->priv; + wmm_used = bss->wmm_used && (local->hw.queues >= IEEE80211_NUM_ACS); + + sband = local->hw.wiphy->bands[req->bss->channel->band]; + + ieee80211_determine_our_sta_mode_auth(sdata, sband, req, wmm_used, + &conn); + err = ieee80211_prep_connection(sdata, req->bss, req->link_id, - req->ap_mld_addr, cont_auth, false); + req->ap_mld_addr, cont_auth, + &conn, false); if (err) goto err_clear; @@ -7960,38 +8145,33 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, return err; } -static ieee80211_conn_flags_t +static void ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgd_assoc_data *assoc_data, struct cfg80211_assoc_request *req, - ieee80211_conn_flags_t conn_flags, + struct ieee80211_conn_settings *conn, unsigned int link_id) { struct ieee80211_local *local = sdata->local; const struct cfg80211_bss_ies *bss_ies; struct ieee80211_supported_band *sband; - const struct element *ht_elem, *vht_elem; struct ieee80211_link_data *link; struct cfg80211_bss *cbss; struct ieee80211_bss *bss; - bool is_5ghz, is_6ghz; cbss = assoc_data->link[link_id].bss; if (WARN_ON(!cbss)) - return 0; + return; bss = (void *)cbss->priv; sband = local->hw.wiphy->bands[cbss->channel->band]; if (WARN_ON(!sband)) - return 0; + return; link = sdata_dereference(sdata->link[link_id], sdata); if (WARN_ON(!link)) - return 0; - - is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ; - is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; + return; /* for MLO connections assume advertising all rates is OK */ if (!req->ap_mld_addr) { @@ -8008,40 +8188,18 @@ ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, assoc_data->ie_pos += req->links[link_id].elems_len; } - rcu_read_lock(); - ht_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_HT_OPERATION); - if (ht_elem && ht_elem->datalen >= sizeof(struct ieee80211_ht_operation)) - assoc_data->link[link_id].ap_ht_param = - ((struct ieee80211_ht_operation *)(ht_elem->data))->ht_param; - else if (!is_6ghz) - conn_flags |= IEEE80211_CONN_DISABLE_HT; - vht_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_VHT_CAPABILITY); - if (vht_elem && vht_elem->datalen >= sizeof(struct ieee80211_vht_cap)) { - memcpy(&assoc_data->link[link_id].ap_vht_cap, vht_elem->data, - sizeof(struct ieee80211_vht_cap)); - } else if (is_5ghz) { - link_info(link, - "VHT capa missing/short, disabling VHT/HE/EHT\n"); - conn_flags |= IEEE80211_CONN_DISABLE_VHT | - IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT; - } - rcu_read_unlock(); - link->u.mgd.beacon_crc_valid = false; link->u.mgd.dtim_period = 0; link->u.mgd.have_beacon = false; - /* override HT/VHT configuration only if the AP and we support it */ - if (!(conn_flags & IEEE80211_CONN_DISABLE_HT)) { + /* override HT configuration only if the AP and we support it */ + if (conn->mode >= IEEE80211_CONN_MODE_HT) { struct ieee80211_sta_ht_cap sta_ht_cap; memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); } - link->conf->eht_puncturing = 0; - rcu_read_lock(); bss_ies = rcu_dereference(cbss->beacon_ies); if (bss_ies) { @@ -8062,7 +8220,6 @@ ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, } if (bss_ies) { - const struct ieee80211_eht_operation *eht_oper; const struct element *elem; elem = cfg80211_find_ext_elem(WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION, @@ -8079,32 +8236,6 @@ ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, link->conf->ema_ap = true; else link->conf->ema_ap = false; - - elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, - bss_ies->data, bss_ies->len); - eht_oper = (const void *)(elem->data + 1); - - if (elem && - ieee80211_eht_oper_size_ok((const void *)(elem->data + 1), - elem->datalen - 1) && - (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) && - (eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) { - const struct ieee80211_eht_operation_info *info = - (void *)eht_oper->optional; - const u8 *disable_subchannel_bitmap = info->optional; - u16 bitmap; - - bitmap = get_unaligned_le16(disable_subchannel_bitmap); - if (cfg80211_valid_disable_subchannel_bitmap(&bitmap, - &link->conf->chandef) && - !(bitmap && ieee80211_hw_check(&local->hw, DISALLOW_PUNCTURING))) - ieee80211_handle_puncturing_bitmap(link, - eht_oper, - bitmap, - NULL); - else - conn_flags |= IEEE80211_CONN_DISABLE_EHT; - } } rcu_read_unlock(); @@ -8131,8 +8262,6 @@ ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, } else { link->smps_mode = link->u.mgd.req_smps; } - - return conn_flags; } int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, @@ -8144,11 +8273,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgd_assoc_data *assoc_data; const struct element *ssid_elem; struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; - ieee80211_conn_flags_t conn_flags = 0; struct ieee80211_link_data *link; struct cfg80211_bss *cbss; - struct ieee80211_bss *bss; - bool override; + bool override, uapsd_supported; + bool match_auth; int i, err; size_t size = sizeof(*assoc_data) + req->ie_len; @@ -8167,44 +8295,26 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, if (ieee80211_mgd_csa_in_process(sdata, cbss)) { sdata_info(sdata, "AP is in CSA process, reject assoc\n"); - kfree(assoc_data); - return -EINVAL; + err = -EINVAL; + goto err_free; } rcu_read_lock(); ssid_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_SSID); if (!ssid_elem || ssid_elem->datalen > sizeof(assoc_data->ssid)) { rcu_read_unlock(); - kfree(assoc_data); - return -EINVAL; + err = -EINVAL; + goto err_free; } memcpy(assoc_data->ssid, ssid_elem->data, ssid_elem->datalen); assoc_data->ssid_len = ssid_elem->datalen; - memcpy(vif_cfg->ssid, assoc_data->ssid, assoc_data->ssid_len); - vif_cfg->ssid_len = assoc_data->ssid_len; rcu_read_unlock(); - if (req->ap_mld_addr) { - for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - if (!req->links[i].bss) - continue; - link = sdata_dereference(sdata->link[i], sdata); - if (link) - ether_addr_copy(assoc_data->link[i].addr, - link->conf->addr); - else - eth_random_addr(assoc_data->link[i].addr); - } - } else { - memcpy(assoc_data->link[0].addr, sdata->vif.addr, ETH_ALEN); - } - - assoc_data->s1g = cbss->channel->band == NL80211_BAND_S1GHZ; - - memcpy(assoc_data->ap_addr, - req->ap_mld_addr ?: req->bss->bssid, - ETH_ALEN); + if (req->ap_mld_addr) + memcpy(assoc_data->ap_addr, req->ap_mld_addr, ETH_ALEN); + else + memcpy(assoc_data->ap_addr, cbss->bssid, ETH_ALEN); if (ifmgd->associated) { u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -8222,89 +8332,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, false); } - if (ifmgd->auth_data && !ifmgd->auth_data->done) { - err = -EBUSY; - goto err_free; - } - - if (ifmgd->assoc_data) { - err = -EBUSY; - goto err_free; - } - - if (ifmgd->auth_data) { - bool match; - - /* keep sta info, bssid if matching */ - match = ether_addr_equal(ifmgd->auth_data->ap_addr, - assoc_data->ap_addr) && - ifmgd->auth_data->link_id == req->link_id; - - /* Cleanup is delayed if auth_data matches */ - if (!match) - ieee80211_destroy_auth_data(sdata, false); - } - - /* prepare assoc data */ - - bss = (void *)cbss->priv; - assoc_data->wmm = bss->wmm_used && - (local->hw.queues >= IEEE80211_NUM_ACS); - - assoc_data->spp_amsdu = req->flags & ASSOC_REQ_SPP_AMSDU; - - /* - * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. - * We still associate in non-HT mode (11a/b/g) if any one of these - * ciphers is configured as pairwise. - * We can set this to true for non-11n hardware, that'll be checked - * separately along with the peer capabilities. - */ - for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) { - if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || - req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || - req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { - conn_flags |= IEEE80211_CONN_DISABLE_HT; - conn_flags |= IEEE80211_CONN_DISABLE_VHT; - conn_flags |= IEEE80211_CONN_DISABLE_HE; - conn_flags |= IEEE80211_CONN_DISABLE_EHT; - netdev_info(sdata->dev, - "disabling HT/VHT/HE due to WEP/TKIP use\n"); - } - } - - /* also disable HT/VHT/HE/EHT if the AP doesn't use WMM */ - if (!bss->wmm_used) { - conn_flags |= IEEE80211_CONN_DISABLE_HT; - conn_flags |= IEEE80211_CONN_DISABLE_VHT; - conn_flags |= IEEE80211_CONN_DISABLE_HE; - conn_flags |= IEEE80211_CONN_DISABLE_EHT; - netdev_info(sdata->dev, - "disabling HT/VHT/HE as WMM/QoS is not supported by the AP\n"); - } - - if (req->flags & ASSOC_REQ_DISABLE_HT) { - mlme_dbg(sdata, "HT disabled by flag, disabling HT/VHT/HE\n"); - conn_flags |= IEEE80211_CONN_DISABLE_HT; - conn_flags |= IEEE80211_CONN_DISABLE_VHT; - conn_flags |= IEEE80211_CONN_DISABLE_HE; - conn_flags |= IEEE80211_CONN_DISABLE_EHT; - } - - if (req->flags & ASSOC_REQ_DISABLE_VHT) { - mlme_dbg(sdata, "VHT disabled by flag, disabling VHT\n"); - conn_flags |= IEEE80211_CONN_DISABLE_VHT; - } - - if (req->flags & ASSOC_REQ_DISABLE_HE) { - mlme_dbg(sdata, "HE disabled by flag, disabling HE/EHT\n"); - conn_flags |= IEEE80211_CONN_DISABLE_HE; - conn_flags |= IEEE80211_CONN_DISABLE_EHT; - } - - if (req->flags & ASSOC_REQ_DISABLE_EHT) - conn_flags |= IEEE80211_CONN_DISABLE_EHT; - memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa)); memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask, sizeof(ifmgd->ht_capa_mask)); @@ -8317,6 +8344,123 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, memcpy(&ifmgd->s1g_capa_mask, &req->s1g_capa_mask, sizeof(ifmgd->s1g_capa_mask)); + /* keep some setup (AP STA, channel, ...) if matching */ + if (ifmgd->auth_data) + match_auth = ether_addr_equal(ifmgd->auth_data->ap_addr, + assoc_data->ap_addr) && + ifmgd->auth_data->link_id == req->link_id; + + if (req->ap_mld_addr) { + uapsd_supported = true; + + for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + struct ieee80211_supported_band *sband; + struct cfg80211_bss *link_cbss = req->links[i].bss; + struct ieee80211_bss *bss; + + if (!link_cbss) + continue; + + bss = (void *)link_cbss->priv; + + if (!bss->wmm_used) { + err = -EINVAL; + goto err_free; + } + + if (req->flags & (ASSOC_REQ_DISABLE_HT | + ASSOC_REQ_DISABLE_VHT | + ASSOC_REQ_DISABLE_HE | + ASSOC_REQ_DISABLE_EHT)) { + err = -EINVAL; + goto err_free; + } + + if (link_cbss->channel->band == NL80211_BAND_S1GHZ) { + err = -EINVAL; + goto err_free; + } + + link = sdata_dereference(sdata->link[i], sdata); + if (link) + ether_addr_copy(assoc_data->link[i].addr, + link->conf->addr); + else + eth_random_addr(assoc_data->link[i].addr); + sband = local->hw.wiphy->bands[link_cbss->channel->band]; + + if (match_auth && i == assoc_link_id) + assoc_data->link[i].conn = link->u.mgd.conn; + else + assoc_data->link[i].conn = + ieee80211_conn_settings_unlimited; + ieee80211_determine_our_sta_mode_assoc(sdata, sband, + req, true, i, + &assoc_data->link[i].conn); + assoc_data->link[i].bss = link_cbss; + assoc_data->link[i].disabled = req->links[i].disabled; + + if (!bss->uapsd_supported) + uapsd_supported = false; + + if (assoc_data->link[i].conn.mode < IEEE80211_CONN_MODE_EHT) { + err = -EINVAL; + req->links[i].error = err; + goto err_free; + } + } + + assoc_data->wmm = true; + } else { + struct ieee80211_supported_band *sband; + struct ieee80211_bss *bss = (void *)cbss->priv; + + memcpy(assoc_data->link[0].addr, sdata->vif.addr, ETH_ALEN); + assoc_data->s1g = cbss->channel->band == NL80211_BAND_S1GHZ; + + assoc_data->wmm = bss->wmm_used && + (local->hw.queues >= IEEE80211_NUM_ACS); + + if (cbss->channel->band == NL80211_BAND_6GHZ && + req->flags & (ASSOC_REQ_DISABLE_HT | + ASSOC_REQ_DISABLE_VHT | + ASSOC_REQ_DISABLE_HE)) { + err = -EINVAL; + goto err_free; + } + + sband = local->hw.wiphy->bands[cbss->channel->band]; + + assoc_data->link[0].bss = cbss; + + if (match_auth) + assoc_data->link[0].conn = sdata->deflink.u.mgd.conn; + else + assoc_data->link[0].conn = + ieee80211_conn_settings_unlimited; + ieee80211_determine_our_sta_mode_assoc(sdata, sband, req, + assoc_data->wmm, 0, + &assoc_data->link[0].conn); + + uapsd_supported = bss->uapsd_supported; + } + + assoc_data->spp_amsdu = req->flags & ASSOC_REQ_SPP_AMSDU; + + if (ifmgd->auth_data && !ifmgd->auth_data->done) { + err = -EBUSY; + goto err_free; + } + + if (ifmgd->assoc_data) { + err = -EBUSY; + goto err_free; + } + + /* Cleanup is delayed if auth_data matches */ + if (ifmgd->auth_data && !match_auth) + ieee80211_destroy_auth_data(sdata, false); + if (req->ie && req->ie_len) { memcpy(assoc_data->ie, req->ie, req->ie_len); assoc_data->ie_len = req->ie_len; @@ -8347,19 +8491,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, assoc_data->assoc_link_id = assoc_link_id; if (req->ap_mld_addr) { - for (i = 0; i < ARRAY_SIZE(assoc_data->link); i++) { - assoc_data->link[i].conn_flags = conn_flags; - assoc_data->link[i].bss = req->links[i].bss; - assoc_data->link[i].disabled = req->links[i].disabled; - } - /* if there was no authentication, set up the link */ err = ieee80211_vif_set_links(sdata, BIT(assoc_link_id), 0); if (err) goto err_clear; - } else { - assoc_data->link[0].conn_flags = conn_flags; - assoc_data->link[0].bss = cbss; } link = sdata_dereference(sdata->link[assoc_link_id], sdata); @@ -8368,19 +8503,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, goto err_clear; } - /* keep old conn_flags from ieee80211_prep_channel() from auth */ - conn_flags |= link->u.mgd.conn_flags; - conn_flags |= ieee80211_setup_assoc_link(sdata, assoc_data, req, - conn_flags, assoc_link_id); - override = link->u.mgd.conn_flags != conn_flags; - link->u.mgd.conn_flags |= conn_flags; + override = link->u.mgd.conn.mode != + assoc_data->link[assoc_link_id].conn.mode || + link->u.mgd.conn.bw_limit != + assoc_data->link[assoc_link_id].conn.bw_limit; + link->u.mgd.conn = assoc_data->link[assoc_link_id].conn; + + ieee80211_setup_assoc_link(sdata, assoc_data, req, &link->u.mgd.conn, + assoc_link_id); if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) && ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK), "U-APSD not supported with HW_PS_NULLFUNC_STACK\n")) sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; - if (bss->wmm_used && bss->uapsd_supported && + if (assoc_data->wmm && uapsd_supported && (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) { assoc_data->uapsd = true; ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED; @@ -8424,27 +8561,29 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, continue; if (i == assoc_data->assoc_link_id) continue; - /* only calculate the flags, hence link == NULL */ - err = ieee80211_prep_channel(sdata, NULL, + /* only calculate the mode, hence link == NULL */ + err = ieee80211_prep_channel(sdata, NULL, i, assoc_data->link[i].bss, true, - &assoc_data->link[i].conn_flags); + &assoc_data->link[i].conn); if (err) { req->links[i].error = err; goto err_clear; } } + memcpy(vif_cfg->ssid, assoc_data->ssid, assoc_data->ssid_len); + vif_cfg->ssid_len = assoc_data->ssid_len; + /* needed for transmitting the assoc frames properly */ memcpy(sdata->vif.cfg.ap_addr, assoc_data->ap_addr, ETH_ALEN); err = ieee80211_prep_connection(sdata, cbss, req->link_id, - req->ap_mld_addr, true, override); + req->ap_mld_addr, true, + &assoc_data->link[assoc_link_id].conn, + override); if (err) goto err_clear; - assoc_data->link[assoc_data->assoc_link_id].conn_flags = - link->u.mgd.conn_flags; - if (ieee80211_hw_check(&sdata->local->hw, NEED_DTIM_BEFORE_ASSOC)) { const struct cfg80211_bss_ies *beacon_ies; diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 55959b0b24c5..d8c7b3e16eb7 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -23,7 +23,8 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems, enum nl80211_band current_band, u32 vht_cap_info, - ieee80211_conn_flags_t conn_flags, u8 *bssid, + struct ieee80211_conn_settings *conn, + u8 *bssid, struct ieee80211_csa_ie *csa_ie) { enum nl80211_band new_band = current_band; @@ -42,13 +43,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, wide_bw_chansw_ie = elems->wide_bw_chansw_ie; bwi = elems->bandwidth_indication; - if (conn_flags & (IEEE80211_CONN_DISABLE_HT | - IEEE80211_CONN_DISABLE_40MHZ)) { + if (conn->mode < IEEE80211_CONN_MODE_HT || + conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) { sec_chan_offs = NULL; wide_bw_chansw_ie = NULL; } - if (conn_flags & IEEE80211_CONN_DISABLE_VHT) + if (conn->mode < IEEE80211_CONN_MODE_VHT) wide_bw_chansw_ie = NULL; if (elems->ext_chansw_ie) { @@ -95,7 +96,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, if (sec_chan_offs) { secondary_channel_offset = sec_chan_offs->sec_chan_offs; - } else if (!(conn_flags & IEEE80211_CONN_DISABLE_HT)) { + } else if (conn->mode >= IEEE80211_CONN_MODE_HT) { /* If the secondary channel offset IE is not present, * we can't know what's the post-CSA offset, so the * best we can do is use 20MHz. @@ -169,12 +170,10 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, &new_vht_chandef)) new_vht_chandef.chan = NULL; - if (conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ && - new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) - ieee80211_chandef_downgrade(&new_vht_chandef); - if (conn_flags & IEEE80211_CONN_DISABLE_160MHZ && - new_vht_chandef.width == NL80211_CHAN_WIDTH_160) - ieee80211_chandef_downgrade(&new_vht_chandef); + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160 && + (new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80 || + new_vht_chandef.width == NL80211_CHAN_WIDTH_160)) + ieee80211_chandef_downgrade(&new_vht_chandef, NULL); } /* if VHT data is there validate & use it */ diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 49730b424141..396fd54d8bf7 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -347,7 +347,7 @@ ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata, (uc.width > sta->tdls_chandef.width && !cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &uc, sdata->wdev.iftype))) - ieee80211_chandef_downgrade(&uc); + ieee80211_chandef_downgrade(&uc, NULL); if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) { tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n", @@ -561,7 +561,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, ieee80211_he_ppe_size(he_cap->ppe_thres[0], he_cap->he_cap_elem.phy_cap_info); pos = skb_put(skb, cap_size); - pos = ieee80211_ie_build_he_cap(0, pos, he_cap, pos + cap_size); + pos = ieee80211_ie_build_he_cap(NULL, he_cap, pos, pos + cap_size); /* Build HE 6Ghz capa IE from sband */ if (sband->band == NL80211_BAND_6GHZ) { @@ -1413,8 +1413,8 @@ iee80211_tdls_recalc_ht_protection(struct ieee80211_sub_if_data *sdata, IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; u16 opmode; - /* Nothing to do if the BSS connection uses HT */ - if (!(sdata->deflink.u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) + /* Nothing to do if the BSS connection uses (at least) HT */ + if (sdata->deflink.u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT) return; tdls_ht = (sta && sta->sta.deflink.ht_cap.ht_supported) || diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c index 997d0cd27b2d..30fc0acb7ac2 100644 --- a/net/mac80211/tests/elems.c +++ b/net/mac80211/tests/elems.c @@ -14,6 +14,7 @@ static void mle_defrag(struct kunit *test) struct ieee80211_elems_parse_params parse_params = { .link_id = 12, .from_ap = true, + .mode = IEEE80211_CONN_MODE_EHT, }; struct ieee802_11_elems *parsed; struct sk_buff *skb; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 685b55a053f3..1d504c8e6bfc 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -46,6 +46,11 @@ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy) } EXPORT_SYMBOL(wiphy_to_ieee80211_hw); +const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited = { + .mode = IEEE80211_CONN_MODE_EHT, + .bw_limit = IEEE80211_CONN_BW_LIMIT_320, +}; + u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, enum nl80211_iftype type) { @@ -929,23 +934,31 @@ ieee80211_parse_extension_element(u32 *crc, switch (elem->data[0]) { case WLAN_EID_EXT_HE_MU_EDCA: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; calc_crc = true; if (len >= sizeof(*elems->mu_edca_param_set)) elems->mu_edca_param_set = data; break; case WLAN_EID_EXT_HE_CAPABILITY: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; if (ieee80211_he_capa_size_ok(data, len)) { elems->he_cap = data; elems->he_cap_len = len; } break; case WLAN_EID_EXT_HE_OPERATION: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; calc_crc = true; if (len >= sizeof(*elems->he_operation) && len >= ieee80211_he_oper_size(data) - 1) elems->he_operation = data; break; case WLAN_EID_EXT_UORA: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; if (len >= 1) elems->uora_element = data; break; @@ -958,15 +971,21 @@ ieee80211_parse_extension_element(u32 *crc, elems->mbssid_config_ie = data; break; case WLAN_EID_EXT_HE_SPR: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; if (len >= sizeof(*elems->he_spr) && len >= ieee80211_he_spr_size(data)) elems->he_spr = data; break; case WLAN_EID_EXT_HE_6GHZ_CAPA: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; if (len >= sizeof(*elems->he_6ghz_capa)) elems->he_6ghz_capa = data; break; case WLAN_EID_EXT_EHT_CAPABILITY: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; if (ieee80211_eht_capa_size_ok(elems->he_cap, data, len, params->from_ap)) { @@ -975,11 +994,15 @@ ieee80211_parse_extension_element(u32 *crc, } break; case WLAN_EID_EXT_EHT_OPERATION: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; if (ieee80211_eht_oper_size_ok(data, len)) elems->eht_operation = data; calc_crc = true; break; case WLAN_EID_EXT_EHT_MULTI_LINK: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; calc_crc = true; if (ieee80211_mle_size_ok(data, len)) { @@ -1004,11 +1027,15 @@ ieee80211_parse_extension_element(u32 *crc, } break; case WLAN_EID_EXT_BANDWIDTH_INDICATION: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; if (ieee80211_bandwidth_indication_size_ok(data, len)) elems->bandwidth_indication = data; calc_crc = true; break; case WLAN_EID_EXT_TID_TO_LINK_MAPPING: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; calc_crc = true; if (ieee80211_tid_to_link_map_size_ok(data, len) && elems->ttlm_num < ARRAY_SIZE(elems->ttlm)) { @@ -1178,24 +1205,32 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elems->ext_supp_rates_len = elen; break; case WLAN_EID_HT_CAPABILITY: + if (params->mode < IEEE80211_CONN_MODE_HT) + break; if (elen >= sizeof(struct ieee80211_ht_cap)) elems->ht_cap_elem = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_HT_OPERATION: + if (params->mode < IEEE80211_CONN_MODE_HT) + break; if (elen >= sizeof(struct ieee80211_ht_operation)) elems->ht_operation = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_VHT_CAPABILITY: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; if (elen >= sizeof(struct ieee80211_vht_cap)) elems->vht_cap_elem = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_VHT_OPERATION: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; if (elen >= sizeof(struct ieee80211_vht_operation)) { elems->vht_operation = (void *)pos; if (calc_crc) @@ -1205,6 +1240,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elem_parse_failed = true; break; case WLAN_EID_OPMODE_NOTIF: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; if (elen > 0) { elems->opmode_notif = pos; if (calc_crc) @@ -1264,6 +1301,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elems->ext_chansw_ie = (void *)pos; break; case WLAN_EID_SECONDARY_CHANNEL_OFFSET: + if (params->mode < IEEE80211_CONN_MODE_HT) + break; if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { elem_parse_failed = true; break; @@ -1279,6 +1318,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elems->mesh_chansw_params_ie = (void *)pos; break; case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; if (!params->action || elen < sizeof(*elems->wide_bw_chansw_ie)) { elem_parse_failed = true; @@ -1287,6 +1328,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elems->wide_bw_chansw_ie = (void *)pos; break; case WLAN_EID_CHANNEL_SWITCH_WRAPPER: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; if (params->action) { elem_parse_failed = true; break; @@ -1305,6 +1348,9 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elem_parse_failed = true; } + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; + subelem = cfg80211_find_ext_elem(WLAN_EID_EXT_BANDWIDTH_INDICATION, pos, elen); if (subelem) { @@ -1393,24 +1439,32 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elem, elems, params); break; case WLAN_EID_S1G_CAPABILITIES: + if (params->mode != IEEE80211_CONN_MODE_S1G) + break; if (elen >= sizeof(*elems->s1g_capab)) elems->s1g_capab = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_S1G_OPERATION: + if (params->mode != IEEE80211_CONN_MODE_S1G) + break; if (elen == sizeof(*elems->s1g_oper)) elems->s1g_oper = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_S1G_BCN_COMPAT: + if (params->mode != IEEE80211_CONN_MODE_S1G) + break; if (elen == sizeof(*elems->s1g_bcn_compat)) elems->s1g_bcn_compat = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_AID_RESPONSE: + if (params->mode != IEEE80211_CONN_MODE_S1G) + break; if (elen == sizeof(struct ieee80211_aid_response_ie)) elems->aid_resp = (void *)pos; else @@ -1562,6 +1616,7 @@ static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems, { struct ieee80211_mle_per_sta_profile *prof; struct ieee80211_elems_parse_params sub = { + .mode = params->mode, .action = params->action, .from_ap = params->from_ap, .link_id = -1, @@ -1649,6 +1704,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) /* Override with nontransmitted profile, if found */ if (nontransmitted_profile_len) { struct ieee80211_elems_parse_params sub = { + .mode = params->mode, .start = nontransmitted_profile, .len = nontransmitted_profile_len, .action = params->action, @@ -2142,7 +2198,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, if (he_cap && cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), IEEE80211_CHAN_NO_HE)) { - pos = ieee80211_ie_build_he_cap(0, pos, he_cap, end); + pos = ieee80211_ie_build_he_cap(NULL, he_cap, pos, end); if (!pos) goto out_err; } @@ -3201,15 +3257,18 @@ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) he_cap->he_cap_elem.phy_cap_info); } -u8 *ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags, u8 *pos, +u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, const struct ieee80211_sta_he_cap *he_cap, - u8 *end) + u8 *pos, u8 *end) { struct ieee80211_he_cap_elem elem; u8 n; u8 ie_len; u8 *orig_pos = pos; + if (!conn) + conn = &ieee80211_conn_settings_unlimited; + /* Make sure we have place for the IE */ /* * TODO: the 1 added is because this temporarily is under the EXTENSION @@ -3221,18 +3280,15 @@ u8 *ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags, u8 *pos, /* modify on stack first to calculate 'n' and 'ie_len' correctly */ elem = he_cap->he_cap_elem; - if (disable_flags & IEEE80211_CONN_DISABLE_40MHZ) + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) elem.phy_cap_info[0] &= ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G); - if (disable_flags & IEEE80211_CONN_DISABLE_160MHZ) + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160) elem.phy_cap_info[0] &= - ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; - - if (disable_flags & IEEE80211_CONN_DISABLE_80P80MHZ) - elem.phy_cap_info[0] &= - ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; + ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G); n = ieee80211_he_mcs_nss_size(&elem); ie_len = 2 + 1 + @@ -4367,21 +4423,32 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_radar_detected); -ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) +void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, + struct ieee80211_conn_settings *conn) { - ieee80211_conn_flags_t ret; + struct ieee80211_conn_settings _ignored = {}; int tmp; + /* allow passing NULL if caller doesn't care */ + if (!conn) + conn = &_ignored; + switch (c->width) { + default: + case NL80211_CHAN_WIDTH_20_NOHT: + WARN_ON_ONCE(1); + fallthrough; case NL80211_CHAN_WIDTH_20: c->width = NL80211_CHAN_WIDTH_20_NOHT; - ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT; + conn->mode = IEEE80211_CONN_MODE_LEGACY; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; break; case NL80211_CHAN_WIDTH_40: c->width = NL80211_CHAN_WIDTH_20; c->center_freq1 = c->chan->center_freq; - ret = IEEE80211_CONN_DISABLE_40MHZ | - IEEE80211_CONN_DISABLE_VHT; + if (conn->mode == IEEE80211_CONN_MODE_VHT) + conn->mode = IEEE80211_CONN_MODE_HT; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; break; case NL80211_CHAN_WIDTH_80: tmp = (30 + c->chan->center_freq - c->center_freq1)/20; @@ -4390,13 +4457,14 @@ ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) /* freq_P40 */ c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; c->width = NL80211_CHAN_WIDTH_40; - ret = IEEE80211_CONN_DISABLE_VHT; + if (conn->mode == IEEE80211_CONN_MODE_VHT) + conn->mode = IEEE80211_CONN_MODE_HT; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_40; break; case NL80211_CHAN_WIDTH_80P80: c->center_freq2 = 0; c->width = NL80211_CHAN_WIDTH_80; - ret = IEEE80211_CONN_DISABLE_80P80MHZ | - IEEE80211_CONN_DISABLE_160MHZ; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80; break; case NL80211_CHAN_WIDTH_160: /* n_P20 */ @@ -4405,8 +4473,7 @@ ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) tmp /= 4; c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; c->width = NL80211_CHAN_WIDTH_80; - ret = IEEE80211_CONN_DISABLE_80P80MHZ | - IEEE80211_CONN_DISABLE_160MHZ; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80; break; case NL80211_CHAN_WIDTH_320: /* n_P20 */ @@ -4415,30 +4482,28 @@ ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) tmp /= 8; c->center_freq1 = c->center_freq1 - 80 + 160 * tmp; c->width = NL80211_CHAN_WIDTH_160; - ret = IEEE80211_CONN_DISABLE_320MHZ; - break; - default: - case NL80211_CHAN_WIDTH_20_NOHT: - WARN_ON_ONCE(1); - c->width = NL80211_CHAN_WIDTH_20_NOHT; - ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_160; break; case NL80211_CHAN_WIDTH_1: case NL80211_CHAN_WIDTH_2: case NL80211_CHAN_WIDTH_4: case NL80211_CHAN_WIDTH_8: case NL80211_CHAN_WIDTH_16: + WARN_ON_ONCE(1); + /* keep c->width */ + conn->mode = IEEE80211_CONN_MODE_S1G; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; + break; case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_10: WARN_ON_ONCE(1); /* keep c->width */ - ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT; + conn->mode = IEEE80211_CONN_MODE_LEGACY; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; break; } WARN_ON_ONCE(!cfg80211_chandef_valid(c)); - - return ret; } /* @@ -5090,3 +5155,42 @@ u8 *ieee80211_ie_build_eht_cap(u8 *pos, return pos; } + +const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode) +{ + static const char * const modes[] = { + [IEEE80211_CONN_MODE_S1G] = "S1G", + [IEEE80211_CONN_MODE_LEGACY] = "legacy", + [IEEE80211_CONN_MODE_HT] = "HT", + [IEEE80211_CONN_MODE_VHT] = "VHT", + [IEEE80211_CONN_MODE_HE] = "HE", + [IEEE80211_CONN_MODE_EHT] = "EHT", + }; + + if (WARN_ON(mode >= ARRAY_SIZE(modes))) + return ""; + + return modes[mode] ?: ""; +} + +enum ieee80211_conn_bw_limit +ieee80211_min_bw_limit_from_chandef(struct cfg80211_chan_def *chandef) +{ + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return IEEE80211_CONN_BW_LIMIT_20; + case NL80211_CHAN_WIDTH_40: + return IEEE80211_CONN_BW_LIMIT_40; + case NL80211_CHAN_WIDTH_80: + return IEEE80211_CONN_BW_LIMIT_80; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + return IEEE80211_CONN_BW_LIMIT_160; + case NL80211_CHAN_WIDTH_320: + return IEEE80211_CONN_BW_LIMIT_320; + default: + WARN(1, "unhandled chandef width %d\n", chandef->width); + return IEEE80211_CONN_BW_LIMIT_20; + } +} From 2d9698dd32d086e47b8bff3df4322cc017c17b55 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:37 +0100 Subject: [PATCH 234/378] wifi: mac80211: clean up HE 6 GHz and EHT chandef parsing In the code we currently check for support 80+80, 160 and 320 channel widths, but really the way this should be (and is otherwise) handled is that we compute the highest channel bandwidth given there, and then cut it down to what we support. This is also needed for wider bandwidth OFDMA support. Change the code to remove this limitation and always parse the highest possible channel width. Reviewed-by: Miriam Rachel Korenblit Link: https://msgid.link/20240129194108.d06f85082e29.I47e68ed3d97b0a2f4ee61e5d8abfcefc8a5b9c08@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 3 +- net/mac80211/mesh.c | 3 +- net/mac80211/mlme.c | 8 ++- net/mac80211/spectmgmt.c | 4 +- net/mac80211/util.c | 104 ++++++++----------------------------- 5 files changed, 29 insertions(+), 93 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 112029f5a7df..29294cf88d39 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2512,9 +2512,8 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, const struct ieee80211_ht_operation *htop, struct cfg80211_chan_def *chandef); void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation_info *info, - bool support_160, bool support_320, struct cfg80211_chan_def *chandef); -bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, +bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_local *local, const struct ieee80211_he_operation *he_oper, const struct ieee80211_eht_operation *eht_oper, struct cfg80211_chan_def *chandef); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6b48914b39fd..60dc453acb5a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -107,7 +107,8 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info, ie->vht_operation, ie->ht_operation, &sta_chan_def); - ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, ie->eht_operation, + ieee80211_chandef_he_6ghz_oper(sdata->local, ie->he_operation, + ie->eht_operation, &sta_chan_def); if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 549a06c9a2e5..7c0339d4f059 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -116,7 +116,7 @@ ieee80211_extract_dis_subch_bmap(const struct ieee80211_eht_operation *eht_oper, /* set 160/320 supported to get the full AP definition */ ieee80211_chandef_eht_oper((const void *)eht_oper->optional, - true, true, &ap_chandef); + &ap_chandef); ap_center_freq = ap_chandef.center_freq1; ap_bw = 20 * BIT(u8_get_bits(info->control, IEEE80211_EHT_OPER_CHAN_WIDTH)); @@ -280,7 +280,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, mode = IEEE80211_CONN_MODE_HE; } - if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, + if (!ieee80211_chandef_he_6ghz_oper(sdata->local, he_oper, eht_oper, chandef)) { sdata_info(sdata, "bad HE/EHT 6 GHz operation\n"); return IEEE80211_CONN_MODE_LEGACY; @@ -394,9 +394,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, struct cfg80211_chan_def eht_chandef = *chandef; ieee80211_chandef_eht_oper((const void *)eht_oper->optional, - eht_chandef.width == - NL80211_CHAN_WIDTH_160, - false, &eht_chandef); + &eht_chandef); if (!cfg80211_chandef_valid(&eht_chandef)) { sdata_info(sdata, diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index d8c7b3e16eb7..51efc9bc8168 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -139,9 +139,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, /* start with the CSA one */ new_vht_chandef = csa_ie->chandef; /* and update the width accordingly */ - /* FIXME: support 160/320 */ - ieee80211_chandef_eht_oper(&bwi->info, true, true, - &new_vht_chandef); + ieee80211_chandef_eht_oper(&bwi->info, &new_vht_chandef); } else if (wide_bw_chansw_ie) { u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1; struct ieee80211_vht_operation vht_oper = { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 1d504c8e6bfc..1a2522e699d4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3841,7 +3841,6 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, } void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation_info *info, - bool support_160, bool support_320, struct cfg80211_chan_def *chandef) { chandef->center_freq1 = @@ -3860,80 +3859,34 @@ void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation_info *info, chandef->width = NL80211_CHAN_WIDTH_80; break; case IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ: - if (support_160) { - chandef->width = NL80211_CHAN_WIDTH_160; - chandef->center_freq1 = - ieee80211_channel_to_frequency(info->ccfs1, - chandef->chan->band); - } else { - chandef->width = NL80211_CHAN_WIDTH_80; - } + chandef->width = NL80211_CHAN_WIDTH_160; + chandef->center_freq1 = + ieee80211_channel_to_frequency(info->ccfs1, + chandef->chan->band); break; case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ: - if (support_320) { - chandef->width = NL80211_CHAN_WIDTH_320; - chandef->center_freq1 = - ieee80211_channel_to_frequency(info->ccfs1, - chandef->chan->band); - } else if (support_160) { - chandef->width = NL80211_CHAN_WIDTH_160; - } else { - chandef->width = NL80211_CHAN_WIDTH_80; - - if (chandef->center_freq1 > chandef->chan->center_freq) - chandef->center_freq1 -= 40; - else - chandef->center_freq1 += 40; - } + chandef->width = NL80211_CHAN_WIDTH_320; + chandef->center_freq1 = + ieee80211_channel_to_frequency(info->ccfs1, + chandef->chan->band); break; } } -bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, +bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_local *local, const struct ieee80211_he_operation *he_oper, const struct ieee80211_eht_operation *eht_oper, struct cfg80211_chan_def *chandef) { - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; - enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); - const struct ieee80211_sta_he_cap *he_cap; - const struct ieee80211_sta_eht_cap *eht_cap; struct cfg80211_chan_def he_chandef = *chandef; const struct ieee80211_he_6ghz_oper *he_6ghz_oper; - bool support_80_80, support_160, support_320; - u8 he_phy_cap, eht_phy_cap; u32 freq; if (chandef->chan->band != NL80211_BAND_6GHZ) return true; - sband = local->hw.wiphy->bands[NL80211_BAND_6GHZ]; - - he_cap = ieee80211_get_he_iftype_cap(sband, iftype); - if (!he_cap) { - sdata_info(sdata, "Missing iftype sband data/HE cap"); + if (!he_oper) return false; - } - - he_phy_cap = he_cap->he_cap_elem.phy_cap_info[0]; - support_160 = - he_phy_cap & - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; - support_80_80 = - he_phy_cap & - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; - - if (!he_oper) { - sdata_info(sdata, - "HE is not advertised on (on %d MHz), expect issues\n", - chandef->chan->center_freq); - return false; - } - - eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype); - if (!eht_cap) - eht_oper = NULL; he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper); if (!he_6ghz_oper) @@ -3946,7 +3899,10 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, */ freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary, NL80211_BAND_6GHZ); - he_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq); + he_chandef.chan = ieee80211_get_channel(local->hw.wiphy, freq); + + if (!he_chandef.chan) + return false; if (!eht_oper || !(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) { @@ -3965,13 +3921,10 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, he_chandef.width = NL80211_CHAN_WIDTH_80; if (!he_6ghz_oper->ccfs1) break; - if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) { - if (support_160) - he_chandef.width = NL80211_CHAN_WIDTH_160; - } else { - if (support_80_80) - he_chandef.width = NL80211_CHAN_WIDTH_80P80; - } + if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) + he_chandef.width = NL80211_CHAN_WIDTH_160; + else + he_chandef.width = NL80211_CHAN_WIDTH_80P80; break; } @@ -3983,30 +3936,17 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, he_chandef.center_freq1 = ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0, NL80211_BAND_6GHZ); - if (support_80_80 || support_160) - he_chandef.center_freq2 = - ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, - NL80211_BAND_6GHZ); + he_chandef.center_freq2 = + ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, + NL80211_BAND_6GHZ); } } else { - eht_phy_cap = eht_cap->eht_cap_elem.phy_cap_info[0]; - support_320 = - eht_phy_cap & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; - ieee80211_chandef_eht_oper((const void *)eht_oper->optional, - support_160, support_320, &he_chandef); } - if (!cfg80211_chandef_valid(&he_chandef)) { - sdata_info(sdata, - "HE 6GHz operation resulted in invalid chandef: %d MHz/%d/%d MHz/%d MHz\n", - he_chandef.chan ? he_chandef.chan->center_freq : 0, - he_chandef.width, - he_chandef.center_freq1, - he_chandef.center_freq2); + if (!cfg80211_chandef_valid(&he_chandef)) return false; - } *chandef = he_chandef; From 0a44dfc070749514b804ccac0b1fd38718f7daa1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:38 +0100 Subject: [PATCH 235/378] wifi: mac80211: simplify non-chanctx drivers There are still surprisingly many non-chanctx drivers, but in mac80211 that code is a bit awkward. Simplify this by having those drivers assign 'emulated' ops, so that the mac80211 code can be more unified between non-chanctx/chanctx drivers. This cuts the number of places caring about it by about 15, which are scattered across - now they're fewer and no longer in the channel context handling. Link: https://msgid.link/20240129194108.6d0ead50f5cf.I60d093b2fc81ca1853925a4d0ac3a2337d5baa5b@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/admtek/adm8211.c | 4 + drivers/net/wireless/ath/ar5523/ar5523.c | 4 + drivers/net/wireless/ath/ath5k/mac80211-ops.c | 4 + drivers/net/wireless/ath/ath9k/htc_drv_main.c | 4 + drivers/net/wireless/ath/ath9k/main.c | 4 + drivers/net/wireless/ath/carl9170/main.c | 4 + drivers/net/wireless/ath/wcn36xx/main.c | 4 + drivers/net/wireless/atmel/at76c50x-usb.c | 4 + drivers/net/wireless/broadcom/b43/main.c | 4 + .../net/wireless/broadcom/b43legacy/main.c | 4 + .../broadcom/brcm80211/brcmsmac/mac80211_if.c | 4 + .../net/wireless/intel/iwlegacy/3945-mac.c | 4 + .../net/wireless/intel/iwlegacy/4965-mac.c | 4 + .../net/wireless/intel/iwlwifi/dvm/mac80211.c | 4 + drivers/net/wireless/intersil/p54/main.c | 4 + .../net/wireless/marvell/libertas_tf/main.c | 4 + drivers/net/wireless/marvell/mwl8k.c | 4 + .../net/wireless/mediatek/mt76/mt7603/main.c | 4 + .../net/wireless/mediatek/mt76/mt76x0/pci.c | 4 + .../net/wireless/mediatek/mt76/mt76x0/usb.c | 4 + .../wireless/mediatek/mt76/mt76x2/pci_main.c | 4 + .../wireless/mediatek/mt76/mt76x2/usb_main.c | 4 + .../net/wireless/mediatek/mt76/mt792x_core.c | 7 +- .../net/wireless/mediatek/mt76/mt7996/main.c | 4 + drivers/net/wireless/mediatek/mt7601u/main.c | 4 + drivers/net/wireless/purelifi/plfxlc/mac.c | 4 + .../net/wireless/ralink/rt2x00/rt2400pci.c | 4 + .../net/wireless/ralink/rt2x00/rt2500pci.c | 4 + .../net/wireless/ralink/rt2x00/rt2500usb.c | 4 + .../net/wireless/ralink/rt2x00/rt2800pci.c | 4 + .../net/wireless/ralink/rt2x00/rt2800soc.c | 4 + .../net/wireless/ralink/rt2x00/rt2800usb.c | 4 + drivers/net/wireless/ralink/rt2x00/rt61pci.c | 4 + drivers/net/wireless/ralink/rt2x00/rt73usb.c | 4 + .../wireless/realtek/rtl818x/rtl8180/dev.c | 4 + .../wireless/realtek/rtl818x/rtl8187/dev.c | 4 + .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 4 + drivers/net/wireless/realtek/rtlwifi/core.c | 4 + drivers/net/wireless/realtek/rtw88/mac80211.c | 4 + drivers/net/wireless/realtek/rtw89/core.c | 7 +- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 4 + drivers/net/wireless/st/cw1200/main.c | 4 + drivers/net/wireless/ti/wl1251/main.c | 4 + drivers/net/wireless/virtual/mac80211_hwsim.c | 4 + drivers/net/wireless/zydas/zd1211rw/zd_mac.c | 4 + drivers/staging/vt6655/device_main.c | 4 + drivers/staging/vt6656/main_usb.c | 4 + include/net/mac80211.h | 13 ++ net/mac80211/cfg.c | 42 ++-- net/mac80211/chan.c | 111 ++-------- net/mac80211/ieee80211_i.h | 9 +- net/mac80211/iface.c | 6 +- net/mac80211/main.c | 207 +++++++++++++++--- net/mac80211/mlme.c | 3 +- net/mac80211/offchannel.c | 21 +- net/mac80211/scan.c | 18 +- net/mac80211/tx.c | 2 - net/mac80211/util.c | 25 +-- 58 files changed, 444 insertions(+), 207 deletions(-) diff --git a/drivers/net/wireless/admtek/adm8211.c b/drivers/net/wireless/admtek/adm8211.c index 2fceea9f6550..e3fd48dd3909 100644 --- a/drivers/net/wireless/admtek/adm8211.c +++ b/drivers/net/wireless/admtek/adm8211.c @@ -1759,6 +1759,10 @@ static int adm8211_alloc_rings(struct ieee80211_hw *dev) } static const struct ieee80211_ops adm8211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = adm8211_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = adm8211_start, diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index a742cec44e3d..815f8f599f5d 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1358,6 +1358,10 @@ static void ar5523_configure_filter(struct ieee80211_hw *hw, } static const struct ieee80211_ops ar5523_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .start = ar5523_start, .stop = ar5523_stop, .tx = ar5523_tx, diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index c630343ca4f9..eea4bda77608 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -779,6 +779,10 @@ static int ath5k_set_ringparam(struct ieee80211_hw *hw, u32 tx, u32 rx) const struct ieee80211_ops ath5k_hw_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = ath5k_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = ath5k_start, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 9a9b5212051a..b389e19381c4 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1868,6 +1868,10 @@ static void ath9k_htc_channel_switch_beacon(struct ieee80211_hw *hw, } struct ieee80211_ops ath9k_htc_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = ath9k_htc_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = ath9k_htc_start, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c48ff0ffbfef..a2943aaecb20 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2786,6 +2786,10 @@ static int ath9k_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } struct ieee80211_ops ath9k_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = ath9k_tx, .start = ath9k_start, .stop = ath9k_stop, diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 524327d24964..7e7797bf44b7 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1712,6 +1712,10 @@ static bool carl9170_tx_frames_pending(struct ieee80211_hw *hw) } static const struct ieee80211_ops carl9170_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .start = carl9170_op_start, .stop = carl9170_op_stop, .tx = carl9170_op_tx, diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 4e6b4df8562f..bfbd3c7a70b3 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1347,6 +1347,10 @@ static void wcn36xx_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif } static const struct ieee80211_ops wcn36xx_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .start = wcn36xx_start, .stop = wcn36xx_stop, .add_interface = wcn36xx_add_interface, diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 447b51cff8f9..0b55a272bfd6 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -2178,6 +2178,10 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } static const struct ieee80211_ops at76_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = at76_mac80211_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .add_interface = at76_add_interface, diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index effb6c23f825..badb2f494035 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -5172,6 +5172,10 @@ static int b43_op_get_survey(struct ieee80211_hw *hw, int idx, } static const struct ieee80211_ops b43_hw_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = b43_op_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .conf_tx = b43_op_conf_tx, diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c index 760136638a95..18eb610f600a 100644 --- a/drivers/net/wireless/broadcom/b43legacy/main.c +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -3531,6 +3531,10 @@ static int b43legacy_op_get_survey(struct ieee80211_hw *hw, int idx, } static const struct ieee80211_ops b43legacy_hw_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = b43legacy_op_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .conf_tx = b43legacy_op_conf_tx, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c index 543e93ec49d2..92860dc0a92e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -959,6 +959,10 @@ static int brcms_ops_beacon_set_tim(struct ieee80211_hw *hw, } static const struct ieee80211_ops brcms_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = brcms_ops_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = brcms_ops_start, diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index 9eaf5ec133f9..075b705a8d7b 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -3432,6 +3432,10 @@ static const struct attribute_group il3945_attribute_group = { }; static struct ieee80211_ops il3945_mac_ops __ro_after_init = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = il3945_mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = il3945_mac_start, diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 70e420df1643..4beb7be6d51d 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -6301,6 +6301,10 @@ il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq, } static const struct ieee80211_ops il4965_mac_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = il4965_mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = il4965_mac_start, diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 5f3d5b15f727..52b008ce53bd 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -1570,6 +1570,10 @@ static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, } const struct ieee80211_ops iwlagn_hw_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = iwlagn_mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = iwlagn_mac_start, diff --git a/drivers/net/wireless/intersil/p54/main.c b/drivers/net/wireless/intersil/p54/main.c index c6084683aedd..687841b2fa2a 100644 --- a/drivers/net/wireless/intersil/p54/main.c +++ b/drivers/net/wireless/intersil/p54/main.c @@ -704,6 +704,10 @@ static void p54_set_coverage_class(struct ieee80211_hw *dev, } static const struct ieee80211_ops p54_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = p54_tx_80211, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = p54_start, diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c index 199d33ed3bb9..9cca69fe04d7 100644 --- a/drivers/net/wireless/marvell/libertas_tf/main.c +++ b/drivers/net/wireless/marvell/libertas_tf/main.c @@ -473,6 +473,10 @@ static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx, } static const struct ieee80211_ops lbtf_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = lbtf_op_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = lbtf_op_start, diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c index 13bcb123d122..ce8fea76dbb2 100644 --- a/drivers/net/wireless/marvell/mwl8k.c +++ b/drivers/net/wireless/marvell/mwl8k.c @@ -5610,6 +5610,10 @@ static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw, } static const struct ieee80211_ops mwl8k_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mwl8k_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = mwl8k_start, diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c index e2146d30e553..9b49267b1eab 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c @@ -701,6 +701,10 @@ static void mt7603_tx(struct ieee80211_hw *hw, } const struct ieee80211_ops mt7603_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mt7603_tx, .start = mt7603_start, .stop = mt7603_stop, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c index 293e66fa83d5..79b7996ad1a8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c @@ -59,6 +59,10 @@ mt76x0e_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } static const struct ieee80211_ops mt76x0e_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mt76x02_tx, .start = mt76x0e_start, .stop = mt76x0e_stop, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index dd042949cf82..bba44f289b4e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -118,6 +118,10 @@ static int mt76x0u_start(struct ieee80211_hw *hw) } static const struct ieee80211_ops mt76x0u_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mt76x02_tx, .start = mt76x0u_start, .stop = mt76x0u_stop, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c index b38bb7a2362b..bfc8c69f43fa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c @@ -132,6 +132,10 @@ static int mt76x2_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, } const struct ieee80211_ops mt76x2_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mt76x02_tx, .start = mt76x2_start, .stop = mt76x2_stop, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c index ac07ed1f63a3..9fe390fdd730 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c @@ -103,6 +103,10 @@ mt76x2u_config(struct ieee80211_hw *hw, u32 changed) } const struct ieee80211_ops mt76x2u_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mt76x02_tx, .start = mt76x2u_start, .stop = mt76x2u_stop, diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index c42101aa9e45..7872fbae7252 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -684,9 +684,10 @@ mt792x_get_mac80211_ops(struct device *dev, if (!(*fw_features & MT792x_FW_CAP_CNM)) { ops->remain_on_channel = NULL; ops->cancel_remain_on_channel = NULL; - ops->add_chanctx = NULL; - ops->remove_chanctx = NULL; - ops->change_chanctx = NULL; + ops->add_chanctx = ieee80211_emulate_add_chanctx; + ops->remove_chanctx = ieee80211_emulate_remove_chanctx; + ops->change_chanctx = ieee80211_emulate_change_chanctx; + ops->switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx; ops->assign_vif_chanctx = NULL; ops->unassign_vif_chanctx = NULL; ops->mgd_prepare_tx = NULL; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 51deea84b642..234e6495871b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1450,6 +1450,10 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, #endif const struct ieee80211_ops mt7996_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mt7996_tx, .start = mt7996_start, .stop = mt7996_stop, diff --git a/drivers/net/wireless/mediatek/mt7601u/main.c b/drivers/net/wireless/mediatek/mt7601u/main.c index c8d332456a6b..a7330576486b 100644 --- a/drivers/net/wireless/mediatek/mt7601u/main.c +++ b/drivers/net/wireless/mediatek/mt7601u/main.c @@ -405,6 +405,10 @@ mt76_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } const struct ieee80211_ops mt7601u_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mt7601u_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = mt7601u_start, diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c index 6f5857d09af0..641f847d47ab 100644 --- a/drivers/net/wireless/purelifi/plfxlc/mac.c +++ b/drivers/net/wireless/purelifi/plfxlc/mac.c @@ -684,6 +684,10 @@ static int plfxlc_set_rts_threshold(struct ieee80211_hw *hw, u32 value) } static const struct ieee80211_ops plfxlc_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = plfxlc_op_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = plfxlc_op_start, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c index 13dd672b825e..42e21e9f303b 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c @@ -1705,6 +1705,10 @@ static int rt2400pci_tx_last_beacon(struct ieee80211_hw *hw) } static const struct ieee80211_ops rt2400pci_mac80211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rt2x00mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c index ecddda4c471e..36ddc5a69fa4 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c @@ -2003,6 +2003,10 @@ static int rt2500pci_tx_last_beacon(struct ieee80211_hw *hw) } static const struct ieee80211_ops rt2500pci_mac80211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rt2x00mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c index 13fdcff0ad66..09923765e2db 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c @@ -1794,6 +1794,10 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) } static const struct ieee80211_ops rt2500usb_mac80211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rt2x00mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c index dcb56f708a5f..14c45aba836f 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c @@ -287,6 +287,10 @@ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) } static const struct ieee80211_ops rt2800pci_mac80211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rt2x00mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c index 7118d4f9038d..701ba54bf3e5 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c @@ -132,6 +132,10 @@ static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev, } static const struct ieee80211_ops rt2800soc_mac80211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rt2x00mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c index b2a8e75a901b..160bef79acdb 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -629,6 +629,10 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) } static const struct ieee80211_ops rt2800usb_mac80211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rt2x00mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c index 483723bf514b..d1cd5694e3c7 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c @@ -2872,6 +2872,10 @@ static u64 rt61pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) } static const struct ieee80211_ops rt61pci_mac80211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rt2x00mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, diff --git a/drivers/net/wireless/ralink/rt2x00/rt73usb.c b/drivers/net/wireless/ralink/rt2x00/rt73usb.c index dfa9d5213898..b79dda952a33 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c @@ -2291,6 +2291,10 @@ static u64 rt73usb_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) } static const struct ieee80211_ops rt73usb_mac80211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rt2x00mac_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c index f6c25a52b69a..77b6cb7e1f6b 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c @@ -1607,6 +1607,10 @@ static void rtl8180_configure_filter(struct ieee80211_hw *dev, } static const struct ieee80211_ops rtl8180_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rtl8180_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rtl8180_start, diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 04945f905d6d..78d99afa373d 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -1377,6 +1377,10 @@ static int rtl8187_conf_tx(struct ieee80211_hw *dev, static const struct ieee80211_ops rtl8187_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rtl8187_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rtl8187_start, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 055f66b623ff..b0c1db726d7a 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -7654,6 +7654,10 @@ static int rtl8xxxu_sta_remove(struct ieee80211_hw *hw, } static const struct ieee80211_ops rtl8xxxu_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rtl8xxxu_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .add_interface = rtl8xxxu_add_interface, diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index 69e97647e3d6..2e60a6991ca1 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -1903,6 +1903,10 @@ void rtl_init_sw_leds(struct ieee80211_hw *hw) EXPORT_SYMBOL(rtl_init_sw_leds); const struct ieee80211_ops rtl_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .start = rtl_op_start, .stop = rtl_op_stop, .tx = rtl_op_tx, diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index d8d68f16014e..7af5bf7fe5b6 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -927,6 +927,10 @@ static void rtw_ops_sta_rc_update(struct ieee80211_hw *hw, } const struct ieee80211_ops rtw_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rtw_ops_tx, .wake_tx_queue = rtw_ops_wake_tx_queue, .start = rtw_ops_start, diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 61a216464b6d..650c507c8ed3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4579,9 +4579,10 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, !RTW89_CHK_FW_FEATURE(BEACON_FILTER, &early_fw); if (no_chanctx) { - ops->add_chanctx = NULL; - ops->remove_chanctx = NULL; - ops->change_chanctx = NULL; + ops->add_chanctx = ieee80211_emulate_add_chanctx; + ops->remove_chanctx = ieee80211_emulate_remove_chanctx; + ops->change_chanctx = ieee80211_emulate_change_chanctx; + ops->switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx; ops->assign_vif_chanctx = NULL; ops->unassign_vif_chanctx = NULL; ops->remain_on_channel = NULL; diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 05890536e353..e8aeb4d76c13 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -1957,6 +1957,10 @@ static int rsi_mac80211_resume(struct ieee80211_hw *hw) #endif static const struct ieee80211_ops mac80211_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = rsi_mac80211_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rsi_mac80211_start, diff --git a/drivers/net/wireless/st/cw1200/main.c b/drivers/net/wireless/st/cw1200/main.c index 381013e0db63..a54a7b86864f 100644 --- a/drivers/net/wireless/st/cw1200/main.c +++ b/drivers/net/wireless/st/cw1200/main.c @@ -203,6 +203,10 @@ static const unsigned long cw1200_ttl[] = { }; static const struct ieee80211_ops cw1200_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .start = cw1200_start, .stop = cw1200_stop, .add_interface = cw1200_add_interface, diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index cd9a41f59f32..0da2d29dd7bd 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -1351,6 +1351,10 @@ static struct ieee80211_supported_band wl1251_band_2ghz = { }; static const struct ieee80211_ops wl1251_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .start = wl1251_op_start, .stop = wl1251_op_stop, .add_interface = wl1251_op_add_interface, diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index a06a462d38f0..907c0842fee7 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -3922,6 +3922,10 @@ static const struct ieee80211_ops mac80211_hwsim_ops = { HWSIM_NON_MLO_OPS .sw_scan_start = mac80211_hwsim_sw_scan, .sw_scan_complete = mac80211_hwsim_sw_scan_complete, + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, }; #define HWSIM_CHANCTX_OPS \ diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c index 5d534e15a844..900c063bd724 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c @@ -1343,6 +1343,10 @@ static u64 zd_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) } static const struct ieee80211_ops zd_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = zd_op_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = zd_op_start, diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index b0b262de6480..e23a5da2b67e 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -1684,6 +1684,10 @@ static void vnt_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) } static const struct ieee80211_ops vnt_mac_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = vnt_tx_80211, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = vnt_start, diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 2abae90f3f52..6c70493d1b01 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -956,6 +956,10 @@ static void vnt_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) } static const struct ieee80211_ops vnt_mac_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = vnt_tx_80211, .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = vnt_start, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8d6ae22c09bf..62c4b4d10bb4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -7532,4 +7532,17 @@ int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links); void ieee80211_set_active_links_async(struct ieee80211_vif *vif, u16 active_links); +/* for older drivers - let's not document these ... */ +int ieee80211_emulate_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx); +void ieee80211_emulate_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx); +void ieee80211_emulate_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed); +int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode); + #endif /* MAC80211_H */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 6e2acad7fd71..d3bf029709d5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -886,33 +886,30 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata; - int ret = 0; + int ret; lockdep_assert_wiphy(local->hw.wiphy); if (cfg80211_chandef_identical(&local->monitor_chandef, chandef)) return 0; - if (local->use_chanctx) { - sdata = wiphy_dereference(local->hw.wiphy, - local->monitor_sdata); - if (sdata) { - ieee80211_link_release_channel(&sdata->deflink); - ret = ieee80211_link_use_channel(&sdata->deflink, - chandef, - IEEE80211_CHANCTX_EXCLUSIVE); - } - } else { - if (local->open_count == local->monitors) { - local->_oper_chandef = *chandef; - ieee80211_hw_config(local, 0); - } - } + sdata = wiphy_dereference(local->hw.wiphy, + local->monitor_sdata); + if (!sdata) + goto done; - if (ret == 0) - local->monitor_chandef = *chandef; + if (cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef, chandef)) + return 0; - return ret; + ieee80211_link_release_channel(&sdata->deflink); + ret = ieee80211_link_use_channel(&sdata->deflink, + chandef, + IEEE80211_CHANCTX_EXCLUSIVE); + if (ret) + return ret; +done: + local->monitor_chandef = *chandef; + return 0; } static int @@ -3086,7 +3083,7 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, if (local->ops->get_txpower) return drv_get_txpower(local, sdata, dbm); - if (!local->use_chanctx) + if (local->emulate_chanctx) *dbm = local->hw.conf.power_level; else *dbm = sdata->vif.bss_conf.txpower; @@ -4214,10 +4211,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy, } else if (local->open_count > 0 && local->open_count == local->monitors && sdata->vif.type == NL80211_IFTYPE_MONITOR) { - if (local->use_chanctx) - *chandef = local->monitor_chandef; - else - *chandef = local->_oper_chandef; + *chandef = local->monitor_chandef; ret = 0; } out: diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 70ba5dc4b283..cf6297ffaef3 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -519,11 +519,6 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, drv_change_chanctx(local, ctx, changed); - if (!local->use_chanctx) { - local->_oper_chandef = *chandef; - ieee80211_hw_config(local, 0); - } - /* check is BW wider */ ieee80211_chan_bw_change(local, old_ctx, false); } @@ -674,23 +669,15 @@ static int ieee80211_add_chanctx(struct ieee80211_local *local, ieee80211_add_wbrf(local, &ctx->conf.def); - if (!local->use_chanctx) - local->hw.conf.radar_enabled = ctx->conf.radar_enabled; - /* turn idle off *before* setting channel -- some drivers need that */ changed = ieee80211_idle_off(local); if (changed) ieee80211_hw_config(local, changed); - if (!local->use_chanctx) { - local->_oper_chandef = ctx->conf.def; - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - } else { - err = drv_add_chanctx(local, ctx); - if (err) { - ieee80211_recalc_idle(local); - return err; - } + err = drv_add_chanctx(local, ctx); + if (err) { + ieee80211_recalc_idle(local); + return err; } return 0; @@ -725,32 +712,7 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local, { lockdep_assert_wiphy(local->hw.wiphy); - if (!local->use_chanctx) { - struct cfg80211_chan_def *chandef = &local->_oper_chandef; - /* S1G doesn't have 20MHz, so get the correct width for the - * current channel. - */ - if (chandef->chan->band == NL80211_BAND_S1GHZ) - chandef->width = - ieee80211_s1g_channel_width(chandef->chan); - else - chandef->width = NL80211_CHAN_WIDTH_20_NOHT; - chandef->center_freq1 = chandef->chan->center_freq; - chandef->freq1_offset = chandef->chan->freq_offset; - chandef->center_freq2 = 0; - - /* NOTE: Disabling radar is only valid here for - * single channel context. To be sure, check it ... - */ - WARN_ON(local->hw.conf.radar_enabled && - !list_empty(&local->chanctx_list)); - - local->hw.conf.radar_enabled = false; - - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - } else { - drv_remove_chanctx(local, ctx); - } + drv_remove_chanctx(local, ctx); ieee80211_recalc_idle(local); @@ -849,11 +811,6 @@ static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, chanctx->conf.radar_enabled = radar_enabled; - if (!local->use_chanctx) { - local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - } - drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); } @@ -995,16 +952,6 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, rcu_read_unlock(); - if (!local->use_chanctx) { - if (rx_chains_static > 1) - local->smps_mode = IEEE80211_SMPS_OFF; - else if (rx_chains_dynamic > 1) - local->smps_mode = IEEE80211_SMPS_DYNAMIC; - else - local->smps_mode = IEEE80211_SMPS_STATIC; - ieee80211_hw_config(local, 0); - } - if (rx_chains_static == chanctx->conf.rx_chains_static && rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) return; @@ -1114,7 +1061,7 @@ int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link, lockdep_assert_wiphy(local->hw.wiphy); curr_ctx = ieee80211_link_get_chanctx(link); - if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) + if (curr_ctx && !local->ops->switch_vif_chanctx) return -EOPNOTSUPP; new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); @@ -1412,24 +1359,6 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link) return true; } -static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, - struct ieee80211_chanctx *new_ctx) -{ - const struct cfg80211_chan_def *chandef; - - lockdep_assert_wiphy(local->hw.wiphy); - - chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); - if (WARN_ON(!chandef)) - return -EINVAL; - - local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; - local->_oper_chandef = *chandef; - ieee80211_hw_config(local, 0); - - return 0; -} - static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, int n_vifs) { @@ -1518,7 +1447,6 @@ static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) { struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; - struct ieee80211_chanctx *new_ctx = NULL; int err, n_assigned, n_reserved, n_ready; int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; @@ -1551,9 +1479,6 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) goto err; } - if (!local->use_chanctx) - new_ctx = ctx; - n_ctx++; n_assigned = 0; @@ -1607,9 +1532,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) if (WARN_ON(n_ctx == 0) || WARN_ON(n_vifs_switch == 0 && n_vifs_assign == 0 && - n_vifs_ctxless == 0) || - WARN_ON(n_ctx > 1 && !local->use_chanctx) || - WARN_ON(!new_ctx && !local->use_chanctx)) { + n_vifs_ctxless == 0)) { err = -EINVAL; goto err; } @@ -1619,20 +1542,14 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) * reservations and driver capabilities. */ - if (local->use_chanctx) { - if (n_vifs_switch > 0) { - err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); - if (err) - goto err; - } + if (n_vifs_switch > 0) { + err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); + if (err) + goto err; + } - if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { - err = ieee80211_chsw_switch_ctxs(local); - if (err) - goto err; - } - } else { - err = ieee80211_chsw_switch_hwconf(local, new_ctx); + if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { + err = ieee80211_chsw_switch_ctxs(local); if (err) goto err; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 29294cf88d39..cb4684a9451e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1348,7 +1348,8 @@ struct ieee80211_local { bool wiphy_ciphers_allocated; - bool use_chanctx; + struct cfg80211_chan_def dflt_chandef; + bool emulate_chanctx; /* protects the aggregated multicast list and filter calls */ spinlock_t filter_lock; @@ -1474,8 +1475,6 @@ struct ieee80211_local { enum mac80211_scan_state next_scan_state; struct wiphy_delayed_work scan_work; struct ieee80211_sub_if_data __rcu *scan_sdata; - /* For backward compatibility only -- do not use */ - struct cfg80211_chan_def _oper_chandef; /* Temporary remain-on-channel for off-channel operations */ struct ieee80211_channel *tmp_channel; @@ -1549,8 +1548,6 @@ struct ieee80211_local { int user_power_level; /* in dBm, for all interfaces */ - enum ieee80211_smps_mode smps_mode; - struct work_struct restart_work; #ifdef CONFIG_MAC80211_DEBUGFS @@ -1819,6 +1816,8 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, unsigned int mpdu_len, unsigned int mpdu_offset); int ieee80211_hw_config(struct ieee80211_local *local, u32 changed); +int ieee80211_hw_conf_chan(struct ieee80211_local *local); +void ieee80211_hw_conf_init(struct ieee80211_local *local); void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx); void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, u64 changed); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 1a6e9dd39a37..d81162bf7d48 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1288,8 +1288,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) res = drv_start(local); if (res) goto err_del_bss; - /* we're brought up, everything changes */ - hw_reconf_flags = ~0; ieee80211_led_radio(local, true); ieee80211_mod_tpt_led_trig(local, IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); @@ -1436,7 +1434,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) if (coming_up) local->open_count++; - if (hw_reconf_flags) + if (local->open_count == 1) + ieee80211_hw_conf_init(local); + else if (hw_reconf_flags) ieee80211_hw_config(local, hw_reconf_flags); ieee80211_recalc_ps(local); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index e05bcc35bc1e..ce0cba8d7afc 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -93,16 +93,32 @@ static void ieee80211_reconfig_filter(struct wiphy *wiphy, ieee80211_configure_filter(local); } -static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) +static u32 ieee80211_calc_hw_conf_chan(struct ieee80211_local *local, + struct ieee80211_chanctx_conf *ctx) { struct ieee80211_sub_if_data *sdata; struct cfg80211_chan_def chandef = {}; + struct cfg80211_chan_def *oper = NULL; + enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_STATIC; u32 changed = 0; int power; u32 offchannel_flag; + if (!local->emulate_chanctx) + return 0; + offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; + if (ctx && !WARN_ON(!ctx->def.chan)) { + oper = &ctx->def; + if (ctx->rx_chains_static > 1) + smps_mode = IEEE80211_SMPS_OFF; + else if (ctx->rx_chains_dynamic > 1) + smps_mode = IEEE80211_SMPS_DYNAMIC; + else + smps_mode = IEEE80211_SMPS_STATIC; + } + if (local->scan_chandef.chan) { chandef = local->scan_chandef; } else if (local->tmp_channel) { @@ -110,25 +126,30 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) chandef.width = NL80211_CHAN_WIDTH_20_NOHT; chandef.center_freq1 = chandef.chan->center_freq; chandef.freq1_offset = chandef.chan->freq_offset; - } else - chandef = local->_oper_chandef; + } else if (oper) { + chandef = *oper; + } else { + chandef = local->dflt_chandef; + } - WARN(!cfg80211_chandef_valid(&chandef), - "control:%d.%03d MHz width:%d center: %d.%03d/%d MHz", - chandef.chan->center_freq, chandef.chan->freq_offset, - chandef.width, chandef.center_freq1, chandef.freq1_offset, - chandef.center_freq2); + if (WARN(!cfg80211_chandef_valid(&chandef), + "control:%d.%03d MHz width:%d center: %d.%03d/%d MHz", + chandef.chan ? chandef.chan->center_freq : -1, + chandef.chan ? chandef.chan->freq_offset : 0, + chandef.width, chandef.center_freq1, chandef.freq1_offset, + chandef.center_freq2)) + return 0; - if (!cfg80211_chandef_identical(&chandef, &local->_oper_chandef)) + if (!oper || !cfg80211_chandef_identical(&chandef, oper)) local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; else local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL; offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; - if (offchannel_flag || - !cfg80211_chandef_identical(&local->hw.conf.chandef, - &local->_oper_chandef)) { + /* force it also for scanning, since drivers might config differently */ + if (offchannel_flag || local->scanning || + !cfg80211_chandef_identical(&local->hw.conf.chandef, &chandef)) { local->hw.conf.chandef = chandef; changed |= IEEE80211_CONF_CHANGE_CHANNEL; } @@ -140,8 +161,8 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) * that otherwise STATIC is used. */ local->hw.conf.smps_mode = IEEE80211_SMPS_STATIC; - } else if (local->hw.conf.smps_mode != local->smps_mode) { - local->hw.conf.smps_mode = local->smps_mode; + } else if (local->hw.conf.smps_mode != smps_mode) { + local->hw.conf.smps_mode = smps_mode; changed |= IEEE80211_CONF_CHANGE_SMPS; } @@ -173,12 +194,9 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) might_sleep(); - if (!local->use_chanctx) - changed |= ieee80211_hw_conf_chan(local); - else - changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL | - IEEE80211_CONF_CHANGE_POWER | - IEEE80211_CONF_CHANGE_SMPS); + WARN_ON(changed & (IEEE80211_CONF_CHANGE_CHANNEL | + IEEE80211_CONF_CHANGE_POWER | + IEEE80211_CONF_CHANGE_SMPS)); if (changed && local->open_count) { ret = drv_config(local, changed); @@ -202,6 +220,107 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) return ret; } +/* for scanning, offchannel and chanctx emulation only */ +static int _ieee80211_hw_conf_chan(struct ieee80211_local *local, + struct ieee80211_chanctx_conf *ctx) +{ + u32 changed; + + if (!local->open_count) + return 0; + + changed = ieee80211_calc_hw_conf_chan(local, ctx); + if (!changed) + return 0; + + return drv_config(local, changed); +} + +int ieee80211_hw_conf_chan(struct ieee80211_local *local) +{ + struct ieee80211_chanctx *ctx; + + ctx = list_first_entry_or_null(&local->chanctx_list, + struct ieee80211_chanctx, + list); + + return _ieee80211_hw_conf_chan(local, ctx ? &ctx->conf : NULL); +} + +void ieee80211_hw_conf_init(struct ieee80211_local *local) +{ + u32 changed = ~(IEEE80211_CONF_CHANGE_CHANNEL | + IEEE80211_CONF_CHANGE_POWER | + IEEE80211_CONF_CHANGE_SMPS); + + if (WARN_ON(!local->open_count)) + return; + + if (local->emulate_chanctx) { + struct ieee80211_chanctx *ctx; + + ctx = list_first_entry_or_null(&local->chanctx_list, + struct ieee80211_chanctx, + list); + + changed |= ieee80211_calc_hw_conf_chan(local, + ctx ? &ctx->conf : NULL); + } + + WARN_ON(drv_config(local, changed)); +} + +int ieee80211_emulate_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct ieee80211_local *local = hw_to_local(hw); + + local->hw.conf.radar_enabled = ctx->radar_enabled; + + return _ieee80211_hw_conf_chan(local, ctx); +} +EXPORT_SYMBOL(ieee80211_emulate_add_chanctx); + +void ieee80211_emulate_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct ieee80211_local *local = hw_to_local(hw); + + local->hw.conf.radar_enabled = false; + + _ieee80211_hw_conf_chan(local, NULL); +} +EXPORT_SYMBOL(ieee80211_emulate_remove_chanctx); + +void ieee80211_emulate_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + struct ieee80211_local *local = hw_to_local(hw); + + local->hw.conf.radar_enabled = ctx->radar_enabled; + + _ieee80211_hw_conf_chan(local, ctx); +} +EXPORT_SYMBOL(ieee80211_emulate_change_chanctx); + +int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + struct ieee80211_local *local = hw_to_local(hw); + + if (n_vifs <= 0) + return -EINVAL; + + local->hw.conf.radar_enabled = vifs[0].new_ctx->radar_enabled; + _ieee80211_hw_conf_chan(local, vifs[0].new_ctx); + + return 0; +} +EXPORT_SYMBOL(ieee80211_emulate_switch_vif_chanctx); + #define BSS_CHANGED_VIF_CFG_FLAGS (BSS_CHANGED_ASSOC |\ BSS_CHANGED_IDLE |\ BSS_CHANGED_PS |\ @@ -645,7 +764,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, struct ieee80211_local *local; int priv_size, i; struct wiphy *wiphy; - bool use_chanctx; + bool emulate_chanctx; if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config || !ops->add_interface || !ops->remove_interface || @@ -660,12 +779,26 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, return NULL; /* check all or no channel context operations exist */ - i = !!ops->add_chanctx + !!ops->remove_chanctx + - !!ops->change_chanctx + !!ops->assign_vif_chanctx + - !!ops->unassign_vif_chanctx; - if (WARN_ON(i != 0 && i != 5)) - return NULL; - use_chanctx = i == 5; + if (ops->add_chanctx == ieee80211_emulate_add_chanctx && + ops->remove_chanctx == ieee80211_emulate_remove_chanctx && + ops->change_chanctx == ieee80211_emulate_change_chanctx) { + if (WARN_ON(ops->assign_vif_chanctx || + ops->unassign_vif_chanctx)) + return NULL; + emulate_chanctx = true; + } else { + if (WARN_ON(ops->add_chanctx == ieee80211_emulate_add_chanctx || + ops->remove_chanctx == ieee80211_emulate_remove_chanctx || + ops->change_chanctx == ieee80211_emulate_change_chanctx)) + return NULL; + if (WARN_ON(!ops->add_chanctx || + !ops->remove_chanctx || + !ops->change_chanctx || + !ops->assign_vif_chanctx || + !ops->unassign_vif_chanctx)) + return NULL; + emulate_chanctx = false; + } /* Ensure 32-byte alignment of our private data and hw private data. * We use the wiphy priv data for both our ieee80211_local and for @@ -699,7 +832,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, WIPHY_FLAG_REPORTS_OBSS | WIPHY_FLAG_OFFCHAN_TX; - if (!use_chanctx || ops->remain_on_channel) + if (emulate_chanctx || ops->remain_on_channel) wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | @@ -756,7 +889,10 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); local->ops = ops; - local->use_chanctx = use_chanctx; + local->emulate_chanctx = emulate_chanctx; + + if (emulate_chanctx) + ieee80211_hw_set(&local->hw, CHANCTX_STA_CSA); /* * We need a bit of data queued to build aggregates properly, so @@ -833,7 +969,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, ieee80211_dfs_radar_detected_work); wiphy_work_init(&local->reconfig_filter, ieee80211_reconfig_filter); - local->smps_mode = IEEE80211_SMPS_OFF; wiphy_work_init(&local->dynamic_ps_enable_work, ieee80211_dynamic_ps_enable_work); @@ -984,7 +1119,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) * as much, e.g. monitoring beacons would be hard if we * might not even know which link is active at which time. */ - if (WARN_ON(!local->use_chanctx)) + if (WARN_ON(local->emulate_chanctx)) return -EINVAL; if (WARN_ON(!local->ops->link_info_changed)) @@ -1028,7 +1163,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) return -EINVAL; #endif - if (!local->use_chanctx) { + if (local->emulate_chanctx) { for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { const struct ieee80211_iface_combination *comb; @@ -1094,11 +1229,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) &sband->channels[i], NL80211_CHAN_NO_HT); /* init channel we're on */ - if (!local->use_chanctx && !local->_oper_chandef.chan) { - local->hw.conf.chandef = dflt_chandef; - local->_oper_chandef = dflt_chandef; - } local->monitor_chandef = dflt_chandef; + if (local->emulate_chanctx) { + local->dflt_chandef = dflt_chandef; + local->hw.conf.chandef = dflt_chandef; + } } channels += sband->n_channels; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7c0339d4f059..9968bc0ddf6e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2287,8 +2287,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, chanctx = container_of(conf, struct ieee80211_chanctx, conf); - if (local->use_chanctx && - !ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA)) { + if (!ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA)) { sdata_info(sdata, "driver doesn't support chan-switch with channel contexts\n"); goto drop_connection; diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 6c4080202573..221695d841fd 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -86,7 +86,7 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) lockdep_assert_wiphy(local->hw.wiphy); - if (WARN_ON(local->use_chanctx)) + if (WARN_ON(!local->emulate_chanctx)) return; /* @@ -136,7 +136,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local) lockdep_assert_wiphy(local->hw.wiphy); - if (WARN_ON(local->use_chanctx)) + if (WARN_ON(!local->emulate_chanctx)) return; list_for_each_entry(sdata, &local->interfaces, list) { @@ -351,10 +351,13 @@ static void _ieee80211_start_next_roc(struct ieee80211_local *local) * 20 MHz channel width) don't stop all the operations but still * treat it as though the ROC operation started properly, so * other ROC operations won't interfere with this one. + * + * Note: scan can't run, tmp_channel is what we use, so this + * must be the currently active channel. */ - roc->on_channel = roc->chan == local->_oper_chandef.chan && - local->_oper_chandef.width != NL80211_CHAN_WIDTH_5 && - local->_oper_chandef.width != NL80211_CHAN_WIDTH_10; + roc->on_channel = roc->chan == local->hw.conf.chandef.chan && + local->hw.conf.chandef.width != NL80211_CHAN_WIDTH_5 && + local->hw.conf.chandef.width != NL80211_CHAN_WIDTH_10; /* start this ROC */ ieee80211_recalc_idle(local); @@ -363,7 +366,7 @@ static void _ieee80211_start_next_roc(struct ieee80211_local *local) ieee80211_offchannel_stop_vifs(local); local->tmp_channel = roc->chan; - ieee80211_hw_config(local, 0); + ieee80211_hw_conf_chan(local); } wiphy_delayed_work_queue(local->hw.wiphy, &local->roc_work, @@ -426,7 +429,7 @@ static void __ieee80211_roc_work(struct ieee80211_local *local) return; if (!roc->started) { - WARN_ON(local->use_chanctx); + WARN_ON(!local->emulate_chanctx); _ieee80211_start_next_roc(local); } else { on_channel = roc->on_channel; @@ -439,7 +442,7 @@ static void __ieee80211_roc_work(struct ieee80211_local *local) ieee80211_flush_queues(local, NULL, false); local->tmp_channel = NULL; - ieee80211_hw_config(local, 0); + ieee80211_hw_conf_chan(local); ieee80211_offchannel_return(local); } @@ -539,7 +542,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, /* this may work, but is untested */ return -EOPNOTSUPP; - if (local->use_chanctx && !local->ops->remain_on_channel) + if (!local->emulate_chanctx && !local->ops->remain_on_channel) return -EOPNOTSUPP; roc = kzalloc(sizeof(*roc), GFP_KERNEL); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 81644d15fe2f..6dedbbba153a 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -476,7 +476,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) } /* Set power back to normal operating levels. */ - ieee80211_hw_config(local, 0); + ieee80211_hw_conf_chan(local); if (!hw_scan && was_scanning) { ieee80211_configure_filter(local); @@ -523,7 +523,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { /* Software scan is not supported in multi-channel cases */ - if (local->use_chanctx) + if (!local->emulate_chanctx) return -EOPNOTSUPP; /* @@ -553,7 +553,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local, ieee80211_configure_filter(local); /* We need to set power level at maximum rate for scanning. */ - ieee80211_hw_config(local, 0); + ieee80211_hw_conf_chan(local); wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0); @@ -790,7 +790,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (hw_scan) { __set_bit(SCAN_HW_SCANNING, &local->scanning); } else if ((req->n_channels == 1) && - (req->channels[0] == local->_oper_chandef.chan)) { + (req->channels[0] == local->hw.conf.chandef.chan)) { /* * If we are scanning only on the operating channel * then we do not need to stop normal activities @@ -808,7 +808,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, ieee80211_configure_filter(local); /* accept probe-responses */ /* We need to ensure power level is at max for scanning. */ - ieee80211_hw_config(local, 0); + ieee80211_hw_conf_chan(local); if ((req->channels[0]->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) || @@ -973,13 +973,13 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, /* If scanning on oper channel, use whatever channel-type * is currently in use. */ - if (chan == local->_oper_chandef.chan) - local->scan_chandef = local->_oper_chandef; + if (chan == local->hw.conf.chandef.chan) + local->scan_chandef = local->hw.conf.chandef; else local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT; set_channel: - if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) + if (ieee80211_hw_conf_chan(local)) skip = 1; /* advance state machine to next channel/band */ @@ -1023,7 +1023,7 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local, { /* switch back to the operating channel */ local->scan_chandef.chan = NULL; - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + ieee80211_hw_conf_chan(local); /* disable PS */ ieee80211_offchannel_return(local); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7b33942ea51f..f57f7963ca37 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2390,8 +2390,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, if (chanctx_conf) chandef = &chanctx_conf->def; - else if (!local->use_chanctx) - chandef = &local->_oper_chandef; else goto fail_rcu; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 1a2522e699d4..51c1a99f57b8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2472,9 +2472,6 @@ static void ieee80211_assign_chanctx(struct ieee80211_local *local, lockdep_assert_wiphy(local->hw.wiphy); - if (!local->use_chanctx) - return; - conf = rcu_dereference_protected(link->conf->chanctx_conf, lockdep_is_held(&local->hw.wiphy->mtx)); if (conf) { @@ -2704,20 +2701,20 @@ int ieee80211_reconfig(struct ieee80211_local *local) } /* add channel contexts */ - if (local->use_chanctx) { - list_for_each_entry(ctx, &local->chanctx_list, list) - if (ctx->replace_state != - IEEE80211_CHANCTX_REPLACES_OTHER) - WARN_ON(drv_add_chanctx(local, ctx)); + list_for_each_entry(ctx, &local->chanctx_list, list) + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + WARN_ON(drv_add_chanctx(local, ctx)); - sdata = wiphy_dereference(local->hw.wiphy, - local->monitor_sdata); - if (sdata && ieee80211_sdata_running(sdata)) - ieee80211_assign_chanctx(local, sdata, &sdata->deflink); - } + sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata); + if (sdata && ieee80211_sdata_running(sdata)) + ieee80211_assign_chanctx(local, sdata, &sdata->deflink); /* reconfigure hardware */ - ieee80211_hw_config(local, ~0); + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_LISTEN_INTERVAL | + IEEE80211_CONF_CHANGE_MONITOR | + IEEE80211_CONF_CHANGE_PS | + IEEE80211_CONF_CHANGE_RETRY_LIMITS | + IEEE80211_CONF_CHANGE_IDLE); ieee80211_configure_filter(local); From 9bf7079bc2271321fac467cae981c44e495b76b9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:39 +0100 Subject: [PATCH 236/378] wifi: mac80211: chan: chandef is non-NULL for reserved The last caller of this with a NULL argument was related to the non-chanctx code, so we can now remove this odd logic. Reviewed-by: Miriam Rachel Korenblit Link: https://msgid.link/20240129194108.bad8ec1e76c8.I12287452f42c54baf75821e75491cf6d021af20a@changeid Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index cf6297ffaef3..6b82c79cf7a6 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -90,11 +90,11 @@ ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, lockdep_assert_wiphy(local->hw.wiphy); + if (WARN_ON(!compat)) + return NULL; + list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) { - if (!compat) - compat = &link->reserved_chandef; - compat = cfg80211_chandef_compatible(&link->reserved_chandef, compat); if (!compat) From 6092077ad09ce880c61735c314060f0bd79ae4aa Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:40 +0100 Subject: [PATCH 237/378] wifi: mac80211: introduce 'channel request' For channel contexts, mac80211 currently uses the cfg80211 chandef struct (control channel, center freq(s), width) to define towards drivers and internally how these behave. In fact, there are _two_ such structs used, where the min_def can reduce bandwidth according to the stations connected. Unfortunately, with EHT this is longer be sufficient, at least not for all hardware. EHT requires that non-AP STAs that are connected to an AP with a lower bandwidth than it (the AP) advertises (e.g. 160 MHz STA connected to 320 MHz AP) still be able to receive downlink OFDMA and respond to trigger frames for uplink OFDMA that specify the position and bandwidth for the non-AP STA relative to the channel the AP is using. Therefore, they need to be aware of this, and at least for some hardware (e.g. Intel) this awareness is in the hardware. As a result, use of the "same" channel may need to be split over two channel contexts where they differ by the AP being used. As a first step, introduce a concept of a channel request ('chanreq') for each interface, to control the context it requests. This step does nothing but reorganise the code, so that later the AP's chandef can be added to the request in order to handle the EHT case described above. Link: https://msgid.link/20240129194108.2e88e48bd2e9.I4256183debe975c5ed71621611206fdbb69ba330@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 8 +- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 12 +- .../wireless/intel/iwlwifi/mvm/mld-mac80211.c | 10 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 8 +- .../net/wireless/intel/iwlwifi/mvm/rs-fw.c | 6 +- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 4 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 4 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 4 +- drivers/net/wireless/realtek/rtw89/mac.c | 4 +- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 4 +- drivers/net/wireless/silabs/wfx/sta.c | 4 +- drivers/net/wireless/ti/wlcore/main.c | 6 +- drivers/staging/vt6655/device_main.c | 2 +- drivers/staging/vt6656/main_usb.c | 2 +- include/net/mac80211.h | 12 +- net/mac80211/agg-tx.c | 2 +- net/mac80211/cfg.c | 66 ++--- net/mac80211/chan.c | 233 ++++++++++-------- net/mac80211/ht.c | 2 +- net/mac80211/ibss.c | 36 +-- net/mac80211/ieee80211_i.h | 21 +- net/mac80211/iface.c | 6 +- net/mac80211/link.c | 3 +- net/mac80211/main.c | 2 +- net/mac80211/mesh.c | 81 +++--- net/mac80211/mesh_plink.c | 4 +- net/mac80211/mlme.c | 87 ++++--- net/mac80211/ocb.c | 3 +- net/mac80211/rate.c | 12 +- net/mac80211/spectmgmt.c | 22 +- net/mac80211/tdls.c | 8 +- net/mac80211/trace.h | 6 +- net/mac80211/util.c | 18 +- net/mac80211/vht.c | 6 +- 34 files changed, 379 insertions(+), 329 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index bcf78ccba8c1..61269c7b1934 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -455,7 +455,7 @@ void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, break; case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: /* Protect when channel wider than 20MHz */ - if (link_conf->chandef.width > NL80211_CHAN_WIDTH_20) + if (link_conf->chanreq.oper.width > NL80211_CHAN_WIDTH_20) *protection_flags |= cpu_to_le32(ht_flag); break; default: @@ -494,7 +494,7 @@ void iwl_mvm_set_fw_qos_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (link_conf->qos) *qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); - if (link_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT) + if (link_conf->chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) *qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); } @@ -910,8 +910,8 @@ u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm, link_conf = rcu_dereference(vif->link_conf[link_id]); if (link_conf) { basic = link_conf->basic_rates; - if (link_conf->chandef.chan) - band = link_conf->chandef.chan->band; + if (link_conf->chanreq.oper.chan) + band = link_conf->chanreq.oper.chan->band; } rcu_read_unlock(); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 93baec9bb3fc..65293b45a98f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1641,7 +1641,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, if (vif->type == NL80211_IFTYPE_MONITOR) { mvm->monitor_on = true; mvm->monitor_p80 = - iwl_mvm_chandef_get_primary_80(&vif->bss_conf.chandef); + iwl_mvm_chandef_get_primary_80(&vif->bss_conf.chanreq.oper); } if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) @@ -3421,16 +3421,16 @@ iwl_mvm_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw, .tolerated = true, }; - if (WARN_ON_ONCE(!link_conf->chandef.chan || + if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan || !mvmvif->link[link_id])) return; - if (!(link_conf->chandef.chan->flags & IEEE80211_CHAN_RADAR)) { + if (!(link_conf->chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR)) { mvmvif->link[link_id]->he_ru_2mhz_block = false; return; } - cfg80211_bss_iter(hw->wiphy, &link_conf->chandef, + cfg80211_bss_iter(hw->wiphy, &link_conf->chanreq.oper, iwl_mvm_check_he_obss_narrow_bw_ru_iter, &iter_data); @@ -3490,10 +3490,10 @@ static void iwl_mvm_mei_host_associated(struct iwl_mvm *mvm, return; /* FIXME: MEI needs to be updated for MLO */ - if (!vif->bss_conf.chandef.chan) + if (!vif->bss_conf.chanreq.oper.chan) return; - conn_info.channel = vif->bss_conf.chandef.chan->hw_value; + conn_info.channel = vif->bss_conf.chanreq.oper.chan->hw_value; switch (mvm_sta->pairwise_cipher) { case WLAN_CIPHER_SUITE_TKIP: diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index ff7d9a7d607e..b633a2a09c32 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -656,8 +656,8 @@ void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, continue; data[n_data].link_id = link_id; - data[n_data].band = link_conf->chandef.chan->band; - data[n_data].width = link_conf->chandef.width; + data[n_data].band = link_conf->chanreq.oper.chan->band; + data[n_data].width = link_conf->chanreq.oper.width; data[n_data].active = vif->active_links & BIT(link_id); n_data++; } @@ -1241,8 +1241,8 @@ int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm, continue; data[n_data].link_id = link_id; - data[n_data].band = link_conf->chandef.chan->band; - data[n_data].width = link_conf->chandef.width; + data[n_data].band = link_conf->chanreq.oper.chan->band; + data[n_data].width = link_conf->chanreq.oper.width; data[n_data].active = true; n_data++; } @@ -1292,7 +1292,7 @@ static bool iwl_mvm_can_enter_esr(struct iwl_mvm *mvm, continue; /* BT Coex effects eSR mode only if one of the link is on LB */ - if (link_conf->chandef.chan->band != NL80211_BAND_2GHZ) + if (link_conf->chanreq.oper.chan->band != NL80211_BAND_2GHZ) continue; ret = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 747fc91ef8d0..ad4146d3345a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -161,9 +161,9 @@ static void iwl_mvm_rx_monitor_notif(struct iwl_mvm *mvm, if (!vif || vif->type != NL80211_IFTYPE_STATION) return; - if (!vif->bss_conf.chandef.chan || - vif->bss_conf.chandef.chan->band != NL80211_BAND_2GHZ || - vif->bss_conf.chandef.width < NL80211_CHAN_WIDTH_40) + if (!vif->bss_conf.chanreq.oper.chan || + vif->bss_conf.chanreq.oper.chan->band != NL80211_BAND_2GHZ || + vif->bss_conf.chanreq.oper.width < NL80211_CHAN_WIDTH_40) return; if (!vif->cfg.assoc) @@ -219,7 +219,7 @@ void iwl_mvm_update_link_smps(struct ieee80211_vif *vif, return; if (mvm->fw_static_smps_request && - link_conf->chandef.width == NL80211_CHAN_WIDTH_160 && + link_conf->chanreq.oper.width == NL80211_CHAN_WIDTH_160 && link_conf->he_support) mode = IEEE80211_SMPS_STATIC; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index 6cba8a353b53..71d92635d6d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -525,10 +525,10 @@ u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta, const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; const struct ieee80211_sta_eht_cap *eht_cap = &link_sta->eht_cap; - if (WARN_ON_ONCE(!link_conf->chandef.chan)) + if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan)) return IEEE80211_MAX_MPDU_LEN_VHT_3895; - if (link_conf->chandef.chan->band == NL80211_BAND_6GHZ) { + if (link_conf->chanreq.oper.chan->band == NL80211_BAND_6GHZ) { switch (le16_get_bits(link_sta->he_6ghz_capa.capa, IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) { case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: @@ -538,7 +538,7 @@ u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta, default: return IEEE80211_MAX_MPDU_LEN_VHT_3895; } - } else if (link_conf->chandef.chan->band == NL80211_BAND_2GHZ && + } else if (link_conf->chanreq.oper.chan->band == NL80211_BAND_2GHZ && eht_cap->has_eht) { switch (u8_get_bits(eht_cap->eht_cap_elem.mac_cap_info[0], IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK)) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 8ffbb8efda73..7e9f3a670212 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -71,7 +71,7 @@ u32 iwl_mvm_get_sta_ampdu_dens(struct ieee80211_link_sta *link_sta, mpdu_dens = link_sta->ht_cap.ampdu_density; } - if (link_conf->chandef.chan->band == NL80211_BAND_6GHZ) { + if (link_conf->chanreq.oper.chan->band == NL80211_BAND_6GHZ) { /* overwrite HT values on 6 GHz */ mpdu_dens = le16_get_bits(link_sta->he_6ghz_capa.capa, IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); @@ -208,7 +208,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } if (sta->deflink.ht_cap.ht_supported || - mvm_sta->vif->bss_conf.chandef.chan->band == NL80211_BAND_6GHZ) + mvm_sta->vif->bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ) add_sta_cmd.station_flags_msk |= cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK | STA_FLG_AGG_MPDU_DENS_MSK); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 79eb6394e5a7..58d1f283d628 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -894,10 +894,10 @@ unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm, if (WARN_ON(!link_conf)) band = NL80211_BAND_2GHZ; else - band = link_conf->chandef.chan->band; + band = link_conf->chanreq.oper.chan->band; rcu_read_unlock(); } else { - band = mvmsta->vif->bss_conf.chandef.chan->band; + band = mvmsta->vif->bss_conf.chanreq.oper.chan->band; } lmac = iwl_mvm_get_lmac_id(mvm, band); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index c67c4f6ca2aa..df1ad6d4e12d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -463,10 +463,10 @@ static bool mt7915_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw, .tolerated = true, }; - if (!(vif->bss_conf.chandef.chan->flags & IEEE80211_CHAN_RADAR)) + if (!(vif->bss_conf.chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR)) return false; - cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chandef, + cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chanreq.oper, mt7915_check_he_obss_narrow_bw_ru_iter, &iter_data); diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index dbf2d6fe4ea7..ef4c492003d8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4615,10 +4615,10 @@ void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, if (!vif->bss_conf.he_support || vif->type != NL80211_IFTYPE_STATION) return; - if (!(vif->bss_conf.chandef.chan->flags & IEEE80211_CHAN_RADAR)) + if (!(vif->bss_conf.chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR)) return; - cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chandef, + cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chanreq.oper, rtw89_mac_check_he_obss_narrow_bw_ru_iter, &tolerated); diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index e8aeb4d76c13..211fa25b9a78 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -740,7 +740,7 @@ u16 rsi_get_connected_channel(struct ieee80211_vif *vif) return 0; bss = &vif->bss_conf; - channel = bss->chandef.chan; + channel = bss->chanreq.oper.chan; if (!channel) return 0; @@ -759,7 +759,7 @@ static void rsi_switch_channel(struct rsi_hw *adapter, if (!vif) return; - channel = vif->bss_conf.chandef.chan; + channel = vif->bss_conf.chanreq.oper.chan; if (!channel) return; diff --git a/drivers/net/wireless/silabs/wfx/sta.c b/drivers/net/wireless/silabs/wfx/sta.c index bb4446b88c12..a904602f02ce 100644 --- a/drivers/net/wireless/silabs/wfx/sta.c +++ b/drivers/net/wireless/silabs/wfx/sta.c @@ -144,13 +144,13 @@ static int wfx_get_ps_timeout(struct wfx_vif *wvif, bool *enable_ps) struct wfx_vif *wvif_ch0 = wdev_to_wvif(wvif->wdev, 0); struct ieee80211_vif *vif_ch0 = wvif_to_vif(wvif_ch0); - chan0 = vif_ch0->bss_conf.chandef.chan; + chan0 = vif_ch0->bss_conf.chanreq.oper.chan; } if (wdev_to_wvif(wvif->wdev, 1)) { struct wfx_vif *wvif_ch1 = wdev_to_wvif(wvif->wdev, 1); struct ieee80211_vif *vif_ch1 = wvif_to_vif(wvif_ch1); - chan1 = vif_ch1->bss_conf.chandef.chan; + chan1 = vif_ch1->bss_conf.chanreq.oper.chan; } if (chan0 && chan1 && vif->type != NL80211_IFTYPE_AP) { if (chan0->hw_value == chan1->hw_value) { diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 5736acb4d206..ef12169f8044 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2910,7 +2910,7 @@ static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif, int ret; wlvif->aid = vif->cfg.aid; - wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chandef); + wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chanreq.oper); wlvif->beacon_int = bss_conf->beacon_int; wlvif->wmm_enabled = bss_conf->qos; @@ -4242,7 +4242,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, /* Handle HT information change */ if ((changed & BSS_CHANGED_HT) && - (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) { + (bss_conf->chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT)) { ret = wl1271_acx_set_ht_information(wl, wlvif, bss_conf->ht_operation_mode); if (ret < 0) { @@ -4515,7 +4515,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, /* Handle new association with HT. Do this after join. */ if (sta_exists) { bool enabled = - bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT; + bss_conf->chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT; ret = wlcore_hw_set_peer_cap(wl, &sta_ht_cap, diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index e23a5da2b67e..283804b49e91 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -1515,7 +1515,7 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_TXPOWER) RFbSetPower(priv, priv->wCurrentRate, - conf->chandef.chan->hw_value); + conf->chanreq.oper.chan->hw_value); if (changed & BSS_CHANGED_BEACON_ENABLED) { dev_dbg(&priv->pcid->dev, diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 6c70493d1b01..7bbed462f062 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -794,7 +794,7 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw, vnt_set_bss_mode(priv); if (changed & (BSS_CHANGED_TXPOWER | BSS_CHANGED_BANDWIDTH)) - vnt_rf_setpower(priv, conf->chandef.chan); + vnt_rf_setpower(priv, conf->chanreq.oper.chan); if (changed & BSS_CHANGED_BEACON_ENABLED) { dev_dbg(&priv->usb->dev, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 62c4b4d10bb4..dd8a66e9afd9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -223,6 +223,14 @@ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_MIN_WIDTH = BIT(4), }; +/** + * struct ieee80211_chan_req - A channel "request" + * @oper: channel definition to use for operation + */ +struct ieee80211_chan_req { + struct cfg80211_chan_def oper; +}; + /** * struct ieee80211_chanctx_conf - channel context that vifs may be tuned to * @@ -583,7 +591,7 @@ struct ieee80211_fils_discovery { * @mcast_rate: per-band multicast rate index + 1 (0: disabled) * @bssid: The BSSID for this BSS * @enable_beacon: whether beaconing should be enabled or not - * @chandef: Channel definition for this BSS -- the hardware might be + * @chanreq: Channel request for this BSS -- the hardware might be * configured a higher bandwidth than this BSS uses, for example. * @mu_group: VHT MU-MIMO group membership data * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation. @@ -716,7 +724,7 @@ struct ieee80211_bss_conf { u32 cqm_rssi_hyst; s32 cqm_rssi_low; s32 cqm_rssi_high; - struct cfg80211_chan_def chandef; + struct ieee80211_chan_req chanreq; struct ieee80211_mu_group_data mu_group; bool qos; bool hidden_ssid; diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index b8a278355e18..21d55dc539f6 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -616,7 +616,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, return -EINVAL; if (!pubsta->deflink.ht_cap.ht_supported && - sta->sdata->vif.bss_conf.chandef.chan->band != NL80211_BAND_6GHZ) + sta->sdata->vif.bss_conf.chanreq.oper.chan->band != NL80211_BAND_6GHZ) return -EINVAL; if (WARN_ON_ONCE(!local->ops->ampdu_action)) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d3bf029709d5..5aa02b0872d9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -886,11 +886,13 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata; + struct ieee80211_chan_req chanreq = { .oper = *chandef }; int ret; lockdep_assert_wiphy(local->hw.wiphy); - if (cfg80211_chandef_identical(&local->monitor_chandef, chandef)) + if (cfg80211_chandef_identical(&local->monitor_chanreq.oper, + &chanreq.oper)) return 0; sdata = wiphy_dereference(local->hw.wiphy, @@ -898,17 +900,17 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, if (!sdata) goto done; - if (cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef, chandef)) + if (cfg80211_chandef_identical(&sdata->vif.bss_conf.chanreq.oper, + &chanreq.oper)) return 0; ieee80211_link_release_channel(&sdata->deflink); - ret = ieee80211_link_use_channel(&sdata->deflink, - chandef, + ret = ieee80211_link_use_channel(&sdata->deflink, &chanreq, IEEE80211_CHANCTX_EXCLUSIVE); if (ret) return ret; done: - local->monitor_chandef = *chandef; + local->monitor_chanreq = chanreq; return 0; } @@ -1257,6 +1259,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id = params->beacon.link_id; struct ieee80211_link_data *link; struct ieee80211_bss_conf *link_conf; + struct ieee80211_chan_req chanreq = { .oper = params->chandef }; lockdep_assert_wiphy(local->hw.wiphy); @@ -1369,7 +1372,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, return err; } - err = ieee80211_link_use_channel(link, ¶ms->chandef, + err = ieee80211_link_use_channel(link, &chanreq, IEEE80211_CHANCTX_SHARED); if (!err) ieee80211_link_copy_chanctx_to_vlans(link, false); @@ -1626,7 +1629,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, BSS_CHANGED_BEACON_ENABLED); if (sdata->wdev.cac_started) { - chandef = link_conf->chandef; + chandef = link_conf->chanreq.oper; wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work); cfg80211_cac_event(sdata->dev, &chandef, NL80211_RADAR_CAC_ABORTED, @@ -1826,7 +1829,7 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, if (params->supported_rates && params->supported_rates_len) { - ieee80211_parse_bitrates(link->conf->chandef.width, + ieee80211_parse_bitrates(link->conf->chanreq.oper.width, sband, params->supported_rates, params->supported_rates_len, &link_sta->pub->supp_rates[sband->band]); @@ -2602,6 +2605,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, const struct mesh_setup *setup) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_chan_req chanreq = { .oper = setup->chandef }; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; int err; @@ -2618,7 +2622,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; sdata->deflink.needed_rx_chains = sdata->local->rx_chains; - err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef, + err = ieee80211_link_use_channel(&sdata->deflink, &chanreq, IEEE80211_CHANCTX_SHARED); if (err) return err; @@ -2661,7 +2665,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy, return -EINVAL; if (params->basic_rates) { - if (!ieee80211_parse_bitrates(link->conf->chandef.width, + if (!ieee80211_parse_bitrates(link->conf->chanreq.oper.width, wiphy->bands[sband->band], params->basic_rates, params->basic_rates_len, @@ -3176,7 +3180,7 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, * the new value until we associate. */ if (!sdata->u.mgd.associated || - link->conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) + link->conf->chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; ap = sdata->vif.cfg.ap_addr; @@ -3331,9 +3335,11 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, * so at a basic rate so that all clients can receive it. */ if (rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) && - sdata->vif.bss_conf.chandef.chan) { + sdata->vif.bss_conf.chanreq.oper.chan) { u32 basic_rates = sdata->vif.bss_conf.basic_rates; - enum nl80211_band band = sdata->vif.bss_conf.chandef.chan->band; + enum nl80211_band band; + + band = sdata->vif.bss_conf.chanreq.oper.chan->band; if (!(mask->control[band].legacy & basic_rates)) return -EINVAL; @@ -3385,6 +3391,7 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy, u32 cac_time_ms) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_chan_req chanreq = { .oper = *chandef }; struct ieee80211_local *local = sdata->local; int err; @@ -3399,7 +3406,7 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy, sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; sdata->deflink.needed_rx_chains = local->rx_chains; - err = ieee80211_link_use_channel(&sdata->deflink, chandef, + err = ieee80211_link_use_channel(&sdata->deflink, &chanreq, IEEE80211_CHANCTX_SHARED); if (err) goto out_unlock; @@ -3651,8 +3658,8 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) return ieee80211_link_use_reserved_context(&sdata->deflink); } - if (!cfg80211_chandef_identical(&link_data->conf->chandef, - &link_data->csa_chandef)) + if (!cfg80211_chandef_identical(&link_data->conf->chanreq.oper, + &link_data->csa_chanreq.oper)) return -EINVAL; sdata->vif.bss_conf.csa_active = false; @@ -3679,7 +3686,7 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) if (err) return err; - cfg80211_ch_switch_notify(sdata->dev, &link_data->csa_chandef, + cfg80211_ch_switch_notify(sdata->dev, &link_data->csa_chanreq.oper, link_data->link_id, link_data->conf->eht_puncturing); @@ -3814,7 +3821,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; /* changes into another band are not supported */ - if (sdata->vif.bss_conf.chandef.chan->band != + if (sdata->vif.bss_conf.chanreq.oper.chan->band != params->chandef.chan->band) return -EINVAL; @@ -3862,6 +3869,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_chan_req chanreq = { .oper = params->chandef }; struct ieee80211_local *local = sdata->local; struct ieee80211_channel_switch ch_switch; struct ieee80211_chanctx_conf *conf; @@ -3877,8 +3885,8 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (sdata->wdev.cac_started) return -EBUSY; - if (cfg80211_chandef_identical(¶ms->chandef, - &sdata->vif.bss_conf.chandef)) + if (cfg80211_chandef_identical(&chanreq.oper, + &sdata->vif.bss_conf.chanreq.oper)) return -EINVAL; /* don't allow another channel switch if one is already active. */ @@ -3903,14 +3911,14 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, ch_switch.timestamp = 0; ch_switch.device_timestamp = 0; ch_switch.block_tx = params->block_tx; - ch_switch.chandef = params->chandef; + ch_switch.chandef = chanreq.oper; ch_switch.count = params->count; err = drv_pre_channel_switch(sdata, &ch_switch); if (err) goto out; - err = ieee80211_link_reserve_chanctx(&sdata->deflink, ¶ms->chandef, + err = ieee80211_link_reserve_chanctx(&sdata->deflink, &chanreq, chanctx->mode, params->radar_required); if (err) @@ -3936,7 +3944,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (params->punct_bitmap && !sdata->vif.bss_conf.eht_support) goto out; - sdata->deflink.csa_chandef = params->chandef; + sdata->deflink.csa_chanreq = chanreq; sdata->deflink.csa_block_tx = params->block_tx; sdata->vif.bss_conf.csa_active = true; sdata->vif.bss_conf.csa_punct_bitmap = params->punct_bitmap; @@ -3946,14 +3954,15 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, IEEE80211_QUEUE_STOP_REASON_CSA); cfg80211_ch_switch_started_notify(sdata->dev, - &sdata->deflink.csa_chandef, 0, + &sdata->deflink.csa_chanreq.oper, 0, params->count, params->block_tx, sdata->vif.bss_conf.csa_punct_bitmap); if (changed) { ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed); - drv_channel_switch_beacon(sdata, ¶ms->chandef); + drv_channel_switch_beacon(sdata, + &sdata->deflink.csa_chanreq.oper); } else { /* if the beacon didn't change, we can finalize immediately */ ieee80211_csa_finalize(&sdata->deflink); @@ -4206,12 +4215,12 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy, chanctx_conf = rcu_dereference(link->conf->chanctx_conf); if (chanctx_conf) { - *chandef = link->conf->chandef; + *chandef = link->conf->chanreq.oper; ret = 0; } else if (local->open_count > 0 && local->open_count == local->monitors && sdata->vif.type == NL80211_IFTYPE_MONITOR) { - *chandef = local->monitor_chandef; + *chandef = local->monitor_chanreq.oper; ret = 0; } out: @@ -4259,12 +4268,13 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_link_data *link; + struct ieee80211_chan_req chanreq = { .oper = *chandef }; int ret; u64 changed = 0; link = sdata_dereference(sdata->link[link_id], sdata); - ret = ieee80211_link_change_bandwidth(link, chandef, &changed); + ret = ieee80211_link_change_chanreq(link, &chanreq, &changed); if (ret == 0) ieee80211_link_info_change_notify(sdata, link, changed); diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 6b82c79cf7a6..f1cef332e4db 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -81,87 +81,97 @@ ieee80211_link_get_chanctx(struct ieee80211_link_data *link) return container_of(conf, struct ieee80211_chanctx, conf); } -static const struct cfg80211_chan_def * -ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, +static const struct ieee80211_chan_req * +ieee80211_chanreq_compatible(const struct ieee80211_chan_req *a, + const struct ieee80211_chan_req *b) +{ + const struct cfg80211_chan_def *compat; + + compat = cfg80211_chandef_compatible(&a->oper, &b->oper); + + if (compat == &a->oper) + return a; + + if (compat == &b->oper) + return b; + + WARN_ON(compat); + return NULL; +} + +static const struct ieee80211_chan_req * +ieee80211_chanctx_compatible(struct ieee80211_chanctx *ctx, + const struct ieee80211_chan_req *req, + struct ieee80211_chan_req *tmp) +{ + *tmp = (struct ieee80211_chan_req){ + .oper = ctx->conf.def, + }; + + return ieee80211_chanreq_compatible(tmp, req); +} + +static const struct ieee80211_chan_req * +ieee80211_chanctx_reserved_chanreq(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, - const struct cfg80211_chan_def *compat) + const struct ieee80211_chan_req *req) { struct ieee80211_link_data *link; lockdep_assert_wiphy(local->hw.wiphy); - if (WARN_ON(!compat)) + if (WARN_ON(!req)) return NULL; - list_for_each_entry(link, &ctx->reserved_links, - reserved_chanctx_list) { - compat = cfg80211_chandef_compatible(&link->reserved_chandef, - compat); - if (!compat) + list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) { + req = ieee80211_chanreq_compatible(&link->reserved, req); + if (!req) break; } - return compat; + return req; } -static const struct cfg80211_chan_def * +static const struct ieee80211_chan_req * ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, - const struct cfg80211_chan_def *compat) + const struct ieee80211_chan_req *compat) { struct ieee80211_link_data *link; + const struct ieee80211_chan_req *comp_def = compat; lockdep_assert_wiphy(local->hw.wiphy); - list_for_each_entry(link, &ctx->assigned_links, - assigned_chanctx_list) { + list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) { struct ieee80211_bss_conf *link_conf = link->conf; if (link->reserved_chanctx) continue; - if (!compat) - compat = &link_conf->chandef; - - compat = cfg80211_chandef_compatible( - &link_conf->chandef, compat); - if (!compat) + comp_def = ieee80211_chanreq_compatible(&link_conf->chanreq, + comp_def); + if (!comp_def) break; } - return compat; -} - -static const struct cfg80211_chan_def * -ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx, - const struct cfg80211_chan_def *compat) -{ - lockdep_assert_wiphy(local->hw.wiphy); - - compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); - if (!compat) - return NULL; - - compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); - if (!compat) - return NULL; - - return compat; + return comp_def; } static bool -ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx, - const struct cfg80211_chan_def *def) +ieee80211_chanctx_can_reserve(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + const struct ieee80211_chan_req *req) { lockdep_assert_wiphy(local->hw.wiphy); - if (ieee80211_chanctx_combined_chandef(local, ctx, def)) - return true; + if (!ieee80211_chanctx_reserved_chanreq(local, ctx, req)) + return false; + + if (!ieee80211_chanctx_non_reserved_chandef(local, ctx, req)) + return false; if (!list_empty(&ctx->reserved_links) && - ieee80211_chanctx_reserved_chandef(local, ctx, def)) + ieee80211_chanctx_reserved_chanreq(local, ctx, req)) return true; return false; @@ -169,7 +179,7 @@ ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, static struct ieee80211_chanctx * ieee80211_find_reservation_chanctx(struct ieee80211_local *local, - const struct cfg80211_chan_def *chandef, + const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode) { struct ieee80211_chanctx *ctx; @@ -186,8 +196,7 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local, if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) continue; - if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, - chandef)) + if (!ieee80211_chanctx_can_reserve(local, ctx, chanreq)) continue; return ctx; @@ -290,7 +299,7 @@ ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, * point, so take the width from the chandef, but * account also for TDLS peers */ - width = max(link->conf->chandef.width, + width = max(link->conf->chanreq.oper.width, ieee80211_get_max_required_bw(sdata, link_id)); break; case NL80211_IFTYPE_P2P_DEVICE: @@ -299,7 +308,7 @@ ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB: - width = link->conf->chandef.width; + width = link->conf->chanreq.oper.width; break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_UNSPECIFIED: @@ -395,7 +404,7 @@ _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, } /* calling this function is assuming that station vif is updated to - * lates changes by calling ieee80211_link_update_chandef + * lates changes by calling ieee80211_link_update_chanreq */ static void ieee80211_chan_bw_change(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, @@ -475,9 +484,10 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, static void _ieee80211_change_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, struct ieee80211_chanctx *old_ctx, - const struct cfg80211_chan_def *chandef, + const struct ieee80211_chan_req *chanreq, struct ieee80211_link_data *rsvd_for) { + const struct cfg80211_chan_def *chandef = &chanreq->oper; u32 changed; /* expected to handle only 20/40/80/160/320 channel widths */ @@ -526,16 +536,17 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, static void ieee80211_change_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, struct ieee80211_chanctx *old_ctx, - const struct cfg80211_chan_def *chandef) + const struct ieee80211_chan_req *chanreq) { - _ieee80211_change_chanctx(local, ctx, old_ctx, chandef, NULL); + _ieee80211_change_chanctx(local, ctx, old_ctx, chanreq, NULL); } static struct ieee80211_chanctx * ieee80211_find_chanctx(struct ieee80211_local *local, - const struct cfg80211_chan_def *chandef, + const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode) { + struct ieee80211_chan_req tmp; struct ieee80211_chanctx *ctx; lockdep_assert_wiphy(local->hw.wiphy); @@ -544,7 +555,7 @@ ieee80211_find_chanctx(struct ieee80211_local *local, return NULL; list_for_each_entry(ctx, &local->chanctx_list, list) { - const struct cfg80211_chan_def *compat; + const struct ieee80211_chan_req *compat; if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) continue; @@ -552,11 +563,11 @@ ieee80211_find_chanctx(struct ieee80211_local *local, if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) continue; - compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); + compat = ieee80211_chanctx_compatible(ctx, chanreq, &tmp); if (!compat) continue; - compat = ieee80211_chanctx_reserved_chandef(local, ctx, + compat = ieee80211_chanctx_reserved_chanreq(local, ctx, compat); if (!compat) continue; @@ -636,7 +647,7 @@ ieee80211_chanctx_radar_required(struct ieee80211_local *local, static struct ieee80211_chanctx * ieee80211_alloc_chanctx(struct ieee80211_local *local, - const struct cfg80211_chan_def *chandef, + const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode) { struct ieee80211_chanctx *ctx; @@ -649,7 +660,7 @@ ieee80211_alloc_chanctx(struct ieee80211_local *local, INIT_LIST_HEAD(&ctx->assigned_links); INIT_LIST_HEAD(&ctx->reserved_links); - ctx->conf.def = *chandef; + ctx->conf.def = chanreq->oper; ctx->conf.rx_chains_static = 1; ctx->conf.rx_chains_dynamic = 1; ctx->mode = mode; @@ -685,7 +696,7 @@ static int ieee80211_add_chanctx(struct ieee80211_local *local, static struct ieee80211_chanctx * ieee80211_new_chanctx(struct ieee80211_local *local, - const struct cfg80211_chan_def *chandef, + const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode) { struct ieee80211_chanctx *ctx; @@ -693,7 +704,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local, lockdep_assert_wiphy(local->hw.wiphy); - ctx = ieee80211_alloc_chanctx(local, chandef, mode); + ctx = ieee80211_alloc_chanctx(local, chanreq, mode); if (!ctx) return ERR_PTR(-ENOMEM); @@ -737,6 +748,7 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, struct ieee80211_chanctx_conf *conf = &ctx->conf; struct ieee80211_sub_if_data *sdata; const struct cfg80211_chan_def *compat = NULL; + struct ieee80211_chan_req chanreq = {}; struct sta_info *sta; lockdep_assert_wiphy(local->hw.wiphy); @@ -762,9 +774,9 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, continue; if (!compat) - compat = &link_conf->chandef; + compat = &link_conf->chanreq.oper; - compat = cfg80211_chandef_compatible(&link_conf->chandef, + compat = cfg80211_chandef_compatible(&link_conf->chanreq.oper, compat); if (WARN_ON_ONCE(!compat)) break; @@ -794,7 +806,9 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, if (!compat) return; - ieee80211_change_chanctx(local, ctx, ctx, compat); + chanreq.oper = *compat; + + ieee80211_change_chanctx(local, ctx, ctx, &chanreq); } static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, @@ -1050,7 +1064,7 @@ int ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link) } int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link, - const struct cfg80211_chan_def *chandef, + const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode, bool radar_required) { @@ -1064,10 +1078,10 @@ int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link, if (curr_ctx && !local->ops->switch_vif_chanctx) return -EOPNOTSUPP; - new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); + new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode); if (!new_ctx) { if (ieee80211_can_create_new_chanctx(local)) { - new_ctx = ieee80211_new_chanctx(local, chandef, mode); + new_ctx = ieee80211_new_chanctx(local, chanreq, mode); if (IS_ERR(new_ctx)) return PTR_ERR(new_ctx); } else { @@ -1121,7 +1135,7 @@ int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link, !list_empty(&curr_ctx->reserved_links)) return -EBUSY; - new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); + new_ctx = ieee80211_alloc_chanctx(local, chanreq, mode); if (!new_ctx) return -ENOMEM; @@ -1139,7 +1153,7 @@ int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link, list_add(&link->reserved_chanctx_list, &new_ctx->reserved_links); link->reserved_chanctx = new_ctx; - link->reserved_chandef = *chandef; + link->reserved = *chanreq; link->reserved_radar_required = radar_required; link->reserved_ready = false; @@ -1178,14 +1192,14 @@ ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link) } static void -ieee80211_link_update_chandef(struct ieee80211_link_data *link, - const struct cfg80211_chan_def *chandef) +ieee80211_link_update_chanreq(struct ieee80211_link_data *link, + const struct ieee80211_chan_req *chanreq) { struct ieee80211_sub_if_data *sdata = link->sdata; unsigned int link_id = link->link_id; struct ieee80211_sub_if_data *vlan; - link->conf->chandef = *chandef; + link->conf->chanreq = *chanreq; if (sdata->vif.type != NL80211_IFTYPE_AP) return; @@ -1198,7 +1212,7 @@ ieee80211_link_update_chandef(struct ieee80211_link_data *link, if (WARN_ON(!vlan_conf)) continue; - vlan_conf->chandef = *chandef; + vlan_conf->chanreq = *chanreq; } rcu_read_unlock(); } @@ -1211,7 +1225,7 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) struct ieee80211_local *local = sdata->local; struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; struct ieee80211_chanctx *old_ctx, *new_ctx; - const struct cfg80211_chan_def *chandef; + const struct ieee80211_chan_req *chanreq; u64 changed = 0; int err; @@ -1233,17 +1247,17 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) IEEE80211_CHANCTX_REPLACES_OTHER)) return -EINVAL; - chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, - &link->reserved_chandef); - if (WARN_ON(!chandef)) + chanreq = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, + &link->reserved); + if (WARN_ON(!chanreq)) return -EINVAL; - if (link_conf->chandef.width != link->reserved_chandef.width) + if (link_conf->chanreq.oper.width != link->reserved.oper.width) changed = BSS_CHANGED_BANDWIDTH; - ieee80211_link_update_chandef(link, &link->reserved_chandef); + ieee80211_link_update_chanreq(link, &link->reserved); - _ieee80211_change_chanctx(local, new_ctx, old_ctx, chandef, link); + _ieee80211_change_chanctx(local, new_ctx, old_ctx, chanreq, link); vif_chsw[0].vif = &sdata->vif; vif_chsw[0].old_ctx = &old_ctx->conf; @@ -1291,7 +1305,7 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link) struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx *old_ctx, *new_ctx; - const struct cfg80211_chan_def *chandef; + const struct ieee80211_chan_req *chanreq; int err; old_ctx = ieee80211_link_get_chanctx(link); @@ -1310,12 +1324,12 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link) IEEE80211_CHANCTX_REPLACES_OTHER)) return -EINVAL; - chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, - &link->reserved_chandef); - if (WARN_ON(!chandef)) + chanreq = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, + &link->reserved); + if (WARN_ON(!chanreq)) return -EINVAL; - ieee80211_change_chanctx(local, new_ctx, new_ctx, chandef); + ieee80211_change_chanctx(local, new_ctx, new_ctx, chanreq); list_del(&link->reserved_chanctx_list); link->reserved_chanctx = NULL; @@ -1589,10 +1603,10 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) link->radar_required = link->reserved_radar_required; - if (link_conf->chandef.width != link->reserved_chandef.width) + if (link_conf->chanreq.oper.width != link->reserved.oper.width) changed = BSS_CHANGED_BANDWIDTH; - ieee80211_link_update_chandef(link, &link->reserved_chandef); + ieee80211_link_update_chanreq(link, &link->reserved); if (changed) ieee80211_link_info_change_notify(sdata, link, @@ -1727,7 +1741,7 @@ static void __ieee80211_link_release_channel(struct ieee80211_link_data *link) } int ieee80211_link_use_channel(struct ieee80211_link_data *link, - const struct cfg80211_chan_def *chandef, + const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode) { struct ieee80211_sub_if_data *sdata = link->sdata; @@ -1740,36 +1754,36 @@ int ieee80211_link_use_channel(struct ieee80211_link_data *link, if (sdata->vif.active_links && !(sdata->vif.active_links & BIT(link->link_id))) { - ieee80211_link_update_chandef(link, chandef); + ieee80211_link_update_chanreq(link, chanreq); return 0; } ret = cfg80211_chandef_dfs_required(local->hw.wiphy, - chandef, + &chanreq->oper, sdata->wdev.iftype); if (ret < 0) goto out; if (ret > 0) - radar_detect_width = BIT(chandef->width); + radar_detect_width = BIT(chanreq->oper.width); link->radar_required = ret; - ret = ieee80211_check_combinations(sdata, chandef, mode, + ret = ieee80211_check_combinations(sdata, &chanreq->oper, mode, radar_detect_width); if (ret < 0) goto out; __ieee80211_link_release_channel(link); - ctx = ieee80211_find_chanctx(local, chandef, mode); + ctx = ieee80211_find_chanctx(local, chanreq, mode); if (!ctx) - ctx = ieee80211_new_chanctx(local, chandef, mode); + ctx = ieee80211_new_chanctx(local, chanreq, mode); if (IS_ERR(ctx)) { ret = PTR_ERR(ctx); goto out; } - ieee80211_link_update_chandef(link, chandef); + ieee80211_link_update_chanreq(link, chanreq); ret = ieee80211_assign_link_chanctx(link, ctx); if (ret) { @@ -1849,28 +1863,33 @@ int ieee80211_link_use_reserved_context(struct ieee80211_link_data *link) return 0; } -int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link, - const struct cfg80211_chan_def *chandef, - u64 *changed) +int ieee80211_link_change_chanreq(struct ieee80211_link_data *link, + const struct ieee80211_chan_req *chanreq, + u64 *changed) { struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_bss_conf *link_conf = link->conf; struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *ctx; - const struct cfg80211_chan_def *compat; + const struct ieee80211_chan_req *compat; + struct ieee80211_chan_req tmp; lockdep_assert_wiphy(local->hw.wiphy); - if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, + if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, + &chanreq->oper, IEEE80211_CHAN_DISABLED)) return -EINVAL; - if (cfg80211_chandef_identical(chandef, &link_conf->chandef)) + /* for non-HT 20 MHz the rest doesn't matter */ + if (chanreq->oper.width == NL80211_CHAN_WIDTH_20_NOHT && + cfg80211_chandef_identical(&chanreq->oper, &link_conf->chanreq.oper)) return 0; - if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || - link_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) + /* but you cannot switch to/from it */ + if (chanreq->oper.width == NL80211_CHAN_WIDTH_20_NOHT || + link_conf->chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return -EINVAL; conf = rcu_dereference_protected(link_conf->chanctx_conf, @@ -1880,13 +1899,13 @@ int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link, ctx = container_of(conf, struct ieee80211_chanctx, conf); - compat = cfg80211_chandef_compatible(&conf->def, chandef); + compat = ieee80211_chanctx_compatible(ctx, chanreq, &tmp); if (!compat) return -EINVAL; switch (ctx->replace_state) { case IEEE80211_CHANCTX_REPLACE_NONE: - if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) + if (!ieee80211_chanctx_reserved_chanreq(local, ctx, compat)) return -EBUSY; break; case IEEE80211_CHANCTX_WILL_BE_REPLACED: @@ -1901,7 +1920,7 @@ int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link, break; } - ieee80211_link_update_chandef(link, chandef); + ieee80211_link_update_chanreq(link, chanreq); ieee80211_recalc_chanctx_chantype(local, ctx); diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index bccc99a00383..c3330aea4da3 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -257,7 +257,7 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, if (WARN_ON(!link_conf)) width = NL80211_CHAN_WIDTH_20_NOHT; else - width = link_conf->chandef.width; + width = link_conf->chanreq.oper.width; switch (width) { default: diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index c23f46218af7..27cc9ddd0432 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -223,7 +223,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; struct cfg80211_bss *bss; u64 bss_change; - struct cfg80211_chan_def chandef; + struct ieee80211_chan_req chanreq = {}; struct ieee80211_channel *chan; struct beacon_data *presp; struct cfg80211_inform_bss bss_meta = {}; @@ -257,22 +257,22 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, kfree_rcu(presp, rcu_head); /* make a copy of the chandef, it could be modified below. */ - chandef = *req_chandef; - chan = chandef.chan; - if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef, + chanreq.oper = *req_chandef; + chan = chanreq.oper.chan; + if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chanreq.oper, NL80211_IFTYPE_ADHOC)) { - if (chandef.width == NL80211_CHAN_WIDTH_5 || - chandef.width == NL80211_CHAN_WIDTH_10 || - chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - chandef.width == NL80211_CHAN_WIDTH_20) { + if (chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + chanreq.oper.width == NL80211_CHAN_WIDTH_10 || + chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + chanreq.oper.width == NL80211_CHAN_WIDTH_20) { sdata_info(sdata, "Failed to join IBSS, beacons forbidden\n"); return; } - chandef.width = NL80211_CHAN_WIDTH_20; - chandef.center_freq1 = chan->center_freq; + chanreq.oper.width = NL80211_CHAN_WIDTH_20; + chanreq.oper.center_freq1 = chan->center_freq; /* check again for downgraded chandef */ - if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef, + if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chanreq.oper, NL80211_IFTYPE_ADHOC)) { sdata_info(sdata, "Failed to join IBSS, beacons forbidden\n"); @@ -281,7 +281,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, } err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, - &chandef, NL80211_IFTYPE_ADHOC); + &chanreq.oper, NL80211_IFTYPE_ADHOC); if (err < 0) { sdata_info(sdata, "Failed to join IBSS, invalid chandef\n"); @@ -295,7 +295,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, radar_required = err; - if (ieee80211_link_use_channel(&sdata->deflink, &chandef, + if (ieee80211_link_use_channel(&sdata->deflink, &chanreq, ifibss->fixed_channel ? IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE)) { @@ -307,7 +307,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memcpy(ifibss->bssid, bssid, ETH_ALEN); presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates, - capability, tsf, &chandef, + capability, tsf, &chanreq.oper, &have_higher_than_11mbit, NULL); if (!presp) return; @@ -533,12 +533,12 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata, u64 *changed) IEEE80211_PRIVACY(ifibss->privacy)); /* XXX: should not really modify cfg80211 data */ if (cbss) { - cbss->channel = sdata->deflink.csa_chandef.chan; + cbss->channel = sdata->deflink.csa_chanreq.oper.chan; cfg80211_put_bss(sdata->local->hw.wiphy, cbss); } } - ifibss->chandef = sdata->deflink.csa_chandef; + ifibss->chandef = sdata->deflink.csa_chanreq.oper; /* generate the beacon */ return ieee80211_ibss_csa_beacon(sdata, NULL, changed); @@ -799,7 +799,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, goto disconnect; params.count = csa_ie.count; - params.chandef = csa_ie.chandef; + params.chandef = csa_ie.chanreq.oper; switch (ifibss->chandef.width) { case NL80211_CHAN_WIDTH_20_NOHT: @@ -858,7 +858,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, params.radar_required = err; if (cfg80211_chandef_identical(¶ms.chandef, - &sdata->vif.bss_conf.chandef)) { + &sdata->vif.bss_conf.chanreq.oper)) { ibss_dbg(sdata, "received csa with an identical chandef, ignoring\n"); return true; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index cb4684a9451e..70c48cad180a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -884,6 +884,9 @@ struct ieee80211_chanctx { enum ieee80211_chanctx_mode mode; bool driver_present; + /* temporary data for search algorithm etc. */ + struct ieee80211_chan_req req; + struct ieee80211_chanctx_conf conf; }; @@ -1035,7 +1038,7 @@ struct ieee80211_link_data { bool operating_11g_mode; - struct cfg80211_chan_def csa_chandef; + struct ieee80211_chan_req csa_chanreq; struct wiphy_work color_change_finalize_work; struct delayed_work color_collision_detect_work; @@ -1043,7 +1046,7 @@ struct ieee80211_link_data { /* context reservation -- protected with wiphy mutex */ struct ieee80211_chanctx *reserved_chanctx; - struct cfg80211_chan_def reserved_chandef; + struct ieee80211_chan_req reserved; bool reserved_radar_required; bool reserved_ready; @@ -1574,7 +1577,7 @@ struct ieee80211_local { /* virtual monitor interface */ struct ieee80211_sub_if_data __rcu *monitor_sdata; - struct cfg80211_chan_def monitor_chandef; + struct ieee80211_chan_req monitor_chanreq; /* extended capabilities provided by mac80211 */ u8 ext_capa[8]; @@ -1639,7 +1642,7 @@ ieee80211_get_link_sband(struct ieee80211_link_data *link) /* this struct holds the value parsing from channel switch IE */ struct ieee80211_csa_ie { - struct cfg80211_chan_def chandef; + struct ieee80211_chan_req chanreq; u8 mode; u8 count; u8 ttl; @@ -2523,11 +2526,11 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *chandef, int __must_check ieee80211_link_use_channel(struct ieee80211_link_data *link, - const struct cfg80211_chan_def *chandef, + const struct ieee80211_chan_req *req, enum ieee80211_chanctx_mode mode); int __must_check ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link, - const struct cfg80211_chan_def *chandef, + const struct ieee80211_chan_req *req, enum ieee80211_chanctx_mode mode, bool radar_required); int __must_check @@ -2535,9 +2538,9 @@ ieee80211_link_use_reserved_context(struct ieee80211_link_data *link); int ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link); int __must_check -ieee80211_link_change_bandwidth(struct ieee80211_link_data *link, - const struct cfg80211_chan_def *chandef, - u64 *changed); +ieee80211_link_change_chanreq(struct ieee80211_link_data *link, + const struct ieee80211_chan_req *req, + u64 *changed); void ieee80211_link_release_channel(struct ieee80211_link_data *link); void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link); void ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d81162bf7d48..227c8dc3fbe5 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -557,7 +557,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do &sdata->deflink.dfs_cac_timer_work); if (sdata->wdev.cac_started) { - chandef = sdata->vif.bss_conf.chandef; + chandef = sdata->vif.bss_conf.chanreq.oper; WARN_ON(local->suspended); ieee80211_link_release_channel(&sdata->deflink); cfg80211_cac_event(sdata->dev, &chandef, @@ -1164,7 +1164,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local) rcu_assign_pointer(local->monitor_sdata, sdata); mutex_unlock(&local->iflist_mtx); - ret = ieee80211_link_use_channel(&sdata->deflink, &local->monitor_chandef, + ret = ieee80211_link_use_channel(&sdata->deflink, &local->monitor_chanreq, IEEE80211_CHANCTX_EXCLUSIVE); if (ret) { mutex_lock(&local->iflist_mtx); @@ -1252,7 +1252,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) sdata->vif.cab_queue = master->vif.cab_queue; memcpy(sdata->vif.hw_queue, master->vif.hw_queue, sizeof(sdata->vif.hw_queue)); - sdata->vif.bss_conf.chandef = master->vif.bss_conf.chandef; + sdata->vif.bss_conf.chanreq = master->vif.bss_conf.chanreq; sdata->crypto_tx_tailroom_needed_cnt += master->crypto_tx_tailroom_needed_cnt; diff --git a/net/mac80211/link.c b/net/mac80211/link.c index c0d05efcf6f8..2231eb38a211 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -402,7 +402,8 @@ static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata, link = sdata_dereference(sdata->link[link_id], sdata); - ret = ieee80211_link_use_channel(link, &link->conf->chandef, + ret = ieee80211_link_use_channel(link, + &link->conf->chanreq, IEEE80211_CHANCTX_SHARED); WARN_ON_ONCE(ret); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index ce0cba8d7afc..879abe216a3e 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1229,7 +1229,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) &sband->channels[i], NL80211_CHAN_NO_HT); /* init channel we're on */ - local->monitor_chandef = dflt_chandef; + local->monitor_chanreq.oper = dflt_chandef; if (local->emulate_chanctx) { local->dflt_chandef = dflt_chandef; local->hw.conf.chandef = dflt_chandef; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 60dc453acb5a..e06d9ed2d31b 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -97,7 +97,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, if (sdata->vif.bss_conf.basic_rates != basic_rates) return false; - cfg80211_chandef_create(&sta_chan_def, sdata->vif.bss_conf.chandef.chan, + cfg80211_chandef_create(&sta_chan_def, sdata->vif.bss_conf.chanreq.oper.chan, NL80211_CHAN_NO_HT); ieee80211_chandef_ht_oper(ie->ht_operation, &sta_chan_def); @@ -111,7 +111,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, ie->eht_operation, &sta_chan_def); - if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, + if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chanreq.oper, &sta_chan_def)) return false; @@ -436,9 +436,9 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!sband->ht_cap.ht_supported || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) @@ -477,16 +477,16 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!ht_cap->ht_supported || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation)) return -ENOMEM; pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); - ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef, + ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chanreq.oper, sdata->vif.bss_conf.ht_operation_mode, false); @@ -508,9 +508,9 @@ int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!sband->vht_cap.vht_supported || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_cap)) @@ -549,9 +549,9 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!vht_cap->vht_supported || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_operation)) @@ -559,7 +559,7 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation)); ieee80211_ie_build_vht_oper(pos, vht_cap, - &sdata->vif.bss_conf.chandef); + &sdata->vif.bss_conf.chanreq.oper); return 0; } @@ -578,9 +578,9 @@ int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata, he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); if (!he_cap || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; if (skb_tailroom(skb) < ie_len) @@ -606,20 +606,20 @@ int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata, he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); if (!he_cap || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; len = 2 + 1 + sizeof(struct ieee80211_he_operation); - if (sdata->vif.bss_conf.chandef.chan->band == NL80211_BAND_6GHZ) + if (sdata->vif.bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ) len += sizeof(struct ieee80211_he_6ghz_oper); if (skb_tailroom(skb) < len) return -ENOMEM; pos = skb_put(skb, len); - ieee80211_ie_build_he_oper(pos, &sdata->vif.bss_conf.chandef); + ieee80211_ie_build_he_oper(pos, &sdata->vif.bss_conf.chanreq.oper); return 0; } @@ -659,9 +659,9 @@ int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata, he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); if (!he_cap || !eht_cap || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; if (skb_tailroom(skb) < ie_len) @@ -686,9 +686,9 @@ int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *sk eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); if (!eht_cap || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; len = 2 + 1 + offsetof(struct ieee80211_eht_operation, optional) + @@ -698,7 +698,7 @@ int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *sk return -ENOMEM; pos = skb_put(skb, len); - ieee80211_ie_build_eht_oper(pos, &sdata->vif.bss_conf.chandef, eht_cap); + ieee80211_ie_build_eht_oper(pos, &sdata->vif.bss_conf.chanreq.oper, eht_cap); return 0; } @@ -746,9 +746,9 @@ ieee80211_mesh_update_bss_params(struct ieee80211_sub_if_data *sdata, return; if (!ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT) || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return; sdata->vif.bss_conf.he_support = true; @@ -1277,11 +1277,12 @@ static void ieee80211_mesh_csa_mark_radar(struct ieee80211_sub_if_data *sdata) * unavailable. */ err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, - &sdata->vif.bss_conf.chandef, + &sdata->vif.bss_conf.chanreq.oper, NL80211_IFTYPE_MESH_POINT); if (err > 0) cfg80211_radar_event(sdata->local->hw.wiphy, - &sdata->vif.bss_conf.chandef, GFP_ATOMIC); + &sdata->vif.bss_conf.chanreq.oper, + GFP_ATOMIC); } static bool @@ -1302,7 +1303,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, if (!sband) return false; - switch (sdata->vif.bss_conf.chandef.width) { + switch (sdata->vif.bss_conf.chanreq.oper.width) { case NL80211_CHAN_WIDTH_20_NOHT: conn.mode = IEEE80211_CONN_MODE_LEGACY; conn.bw_limit = IEEE80211_CONN_BW_LIMIT_20; @@ -1339,7 +1340,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, if (csa_ie.reason_code == WLAN_REASON_MESH_CHAN_REGULATORY) ieee80211_mesh_csa_mark_radar(sdata); - params.chandef = csa_ie.chandef; + params.chandef = csa_ie.chanreq.oper; params.count = csa_ie.count; if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef, @@ -1375,7 +1376,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, params.radar_required = err; if (cfg80211_chandef_identical(¶ms.chandef, - &sdata->vif.bss_conf.chandef)) { + &sdata->vif.bss_conf.chanreq.oper)) { mcsa_dbg(sdata, "received csa with an identical chandef, ignoring\n"); return true; @@ -1555,7 +1556,7 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata, u64 *changed) *changed |= BSS_CHANGED_BEACON; mcsa_dbg(sdata, "complete switching to center freq %d MHz", - sdata->vif.bss_conf.chandef.chan->center_freq); + sdata->vif.bss_conf.chanreq.oper.chan->center_freq); return 0; } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 28bf794f67f8..e3aad60f68ab 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -163,7 +163,7 @@ static u64 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) u16 ht_opmode; bool non_ht_sta = false, ht20_sta = false; - switch (sdata->vif.bss_conf.chandef.width) { + switch (sdata->vif.bss_conf.chanreq.oper.width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_10: @@ -196,7 +196,7 @@ static u64 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) if (non_ht_sta) ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; else if (ht20_sta && - sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) + sdata->vif.bss_conf.chanreq.oper.width > NL80211_CHAN_WIDTH_20) ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; else ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9968bc0ddf6e..e9d720f25ddf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -139,7 +139,7 @@ ieee80211_handle_puncturing_bitmap(struct ieee80211_link_data *link, const struct ieee80211_eht_operation *eht_oper, u16 bitmap, u64 *changed) { - struct cfg80211_chan_def *chandef = &link->conf->chandef; + struct cfg80211_chan_def *chandef = &link->conf->chanreq.oper; struct ieee80211_local *local = link->sdata->local; u16 extracted; u64 _changed = 0; @@ -862,8 +862,9 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, struct ieee802_11_elems *elems, u64 *changed) { - struct ieee80211_channel *channel = link->conf->chandef.chan; + struct ieee80211_channel *channel = link->conf->chanreq.oper.chan; struct ieee80211_sub_if_data *sdata = link->sdata; + struct ieee80211_chan_req chanreq = {}; struct cfg80211_chan_def ap_chandef; enum ieee80211_conn_mode ap_mode; u32 vht_cap_info = 0; @@ -913,7 +914,7 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, ieee80211_min_bw_limit_from_chandef(&ap_chandef)) ieee80211_chandef_downgrade(&ap_chandef, NULL); - if (cfg80211_chandef_identical(&ap_chandef, &link->conf->chandef)) + if (cfg80211_chandef_identical(&ap_chandef, &link->conf->chanreq.oper)) return 0; link_info(link, @@ -946,8 +947,9 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, * bandwidth changes where a this could happen, but those cases are * less common and wouldn't completely prevent using the AP. */ + chanreq.oper = ap_chandef; - ret = ieee80211_link_change_bandwidth(link, &ap_chandef, changed); + ret = ieee80211_link_change_chanreq(link, &chanreq, changed); if (ret) { sdata_info(sdata, "AP %pM changed bandwidth to incompatible one - disconnect\n", @@ -2069,8 +2071,8 @@ static void ieee80211_chswitch_work(struct wiphy *wiphy, return; } - if (!cfg80211_chandef_identical(&link->conf->chandef, - &link->csa_chandef)) { + if (!cfg80211_chandef_identical(&link->conf->chanreq.oper, + &link->csa_chanreq.oper)) { sdata_info(sdata, "failed to finalize channel switch, disconnecting\n"); wiphy_work_queue(sdata->local->hw.wiphy, @@ -2118,7 +2120,7 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) return; } - cfg80211_ch_switch_notify(sdata->dev, &link->reserved_chandef, + cfg80211_ch_switch_notify(sdata->dev, &link->reserved.oper, link->link_id, 0); } @@ -2211,7 +2213,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, ch_switch.timestamp = timestamp; ch_switch.device_timestamp = device_timestamp; ch_switch.block_tx = csa_ie.mode; - ch_switch.chandef = csa_ie.chandef; + ch_switch.chandef = csa_ie.chanreq.oper; ch_switch.count = csa_ie.count; ch_switch.delay = csa_ie.max_switch_time; } @@ -2231,34 +2233,36 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, return; } - if (link->conf->chandef.chan->band != - csa_ie.chandef.chan->band) { + if (link->conf->chanreq.oper.chan->band != + csa_ie.chanreq.oper.chan->band) { sdata_info(sdata, "AP %pM switches to different band (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", link->u.mgd.bssid, - csa_ie.chandef.chan->center_freq, - csa_ie.chandef.width, csa_ie.chandef.center_freq1, - csa_ie.chandef.center_freq2); + csa_ie.chanreq.oper.chan->center_freq, + csa_ie.chanreq.oper.width, + csa_ie.chanreq.oper.center_freq1, + csa_ie.chanreq.oper.center_freq2); goto drop_connection; } - if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef, + if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chanreq.oper, IEEE80211_CHAN_DISABLED)) { sdata_info(sdata, "AP %pM switches to unsupported channel " "(%d.%03d MHz, width:%d, CF1/2: %d.%03d/%d MHz), " "disconnecting\n", link->u.mgd.bssid, - csa_ie.chandef.chan->center_freq, - csa_ie.chandef.chan->freq_offset, - csa_ie.chandef.width, csa_ie.chandef.center_freq1, - csa_ie.chandef.freq1_offset, - csa_ie.chandef.center_freq2); + csa_ie.chanreq.oper.chan->center_freq, + csa_ie.chanreq.oper.chan->freq_offset, + csa_ie.chanreq.oper.width, + csa_ie.chanreq.oper.center_freq1, + csa_ie.chanreq.oper.freq1_offset, + csa_ie.chanreq.oper.center_freq2); goto drop_connection; } - if (cfg80211_chandef_identical(&csa_ie.chandef, - &link->conf->chandef) && + if (cfg80211_chandef_identical(&csa_ie.chanreq.oper, + &link->conf->chanreq.oper) && (!csa_ie.mode || !beacon)) { if (link->u.mgd.csa_ignored_same_chan) return; @@ -2299,7 +2303,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, goto drop_connection; } - res = ieee80211_link_reserve_chanctx(link, &csa_ie.chandef, + res = ieee80211_link_reserve_chanctx(link, &csa_ie.chanreq, chanctx->mode, false); if (res) { sdata_info(sdata, @@ -2309,7 +2313,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, } link->conf->csa_active = true; - link->csa_chandef = csa_ie.chandef; + link->csa_chanreq = csa_ie.chanreq; link->csa_block_tx = csa_ie.mode; link->u.mgd.csa_ignored_same_chan = false; link->u.mgd.beacon_crc_valid = false; @@ -2318,7 +2322,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, ieee80211_stop_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); - cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, + cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chanreq.oper, link->link_id, csa_ie.count, csa_ie.mode, 0); @@ -2741,7 +2745,7 @@ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work) struct ieee80211_link_data *link = container_of(work, struct ieee80211_link_data, dfs_cac_timer_work.work); - struct cfg80211_chan_def chandef = link->conf->chandef; + struct cfg80211_chan_def chandef = link->conf->chanreq.oper; struct ieee80211_sub_if_data *sdata = link->sdata; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -4508,11 +4512,11 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, goto out; } - if (WARN_ON(!link->conf->chandef.chan)) { + if (WARN_ON(!link->conf->chanreq.oper.chan)) { ret = false; goto out; } - sband = local->hw.wiphy->bands[link->conf->chandef.chan->band]; + sband = local->hw.wiphy->bands[link->conf->chanreq.oper.chan->band]; /* Set up internal HT/VHT capabilities */ if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT) @@ -4580,7 +4584,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, } else if (is_6ghz) { link_info(link, "HE 6 GHz operation missing (on %d MHz), expect issues\n", - bss_conf->chandef.chan->center_freq); + bss_conf->chanreq.oper.chan->center_freq); } bss_conf->he_support = link_sta->pub->he_cap.has_he; @@ -5132,8 +5136,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, struct ieee80211_conn_settings *conn) { struct ieee80211_local *local = sdata->local; - struct cfg80211_chan_def chandef; bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; + struct ieee80211_chan_req chanreq = {}; struct ieee802_11_elems *elems; int ret; u32 i; @@ -5142,7 +5146,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); elems = ieee80211_determine_chan_mode(sdata, conn, cbss, link_id, - &chandef); + &chanreq.oper); if (IS_ERR(elems)) { rcu_read_unlock(); @@ -5196,17 +5200,18 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, * on incompatible channels, e.g. 80+80 and 160 sharing the * same control channel) try to use a smaller bandwidth. */ - ret = ieee80211_link_use_channel(link, &chandef, + ret = ieee80211_link_use_channel(link, &chanreq, IEEE80211_CHANCTX_SHARED); /* don't downgrade for 5 and 10 MHz channels, though. */ - if (chandef.width == NL80211_CHAN_WIDTH_5 || - chandef.width == NL80211_CHAN_WIDTH_10) + if (chanreq.oper.width == NL80211_CHAN_WIDTH_5 || + chanreq.oper.width == NL80211_CHAN_WIDTH_10) return ret; - while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { - ieee80211_chandef_downgrade(&chandef, conn); - ret = ieee80211_link_use_channel(link, &chandef, + while (ret && chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) { + ieee80211_chandef_downgrade(&chanreq.oper, conn); + + ret = ieee80211_link_use_channel(link, &chanreq, IEEE80211_CHANCTX_SHARED); } @@ -5862,7 +5867,7 @@ static bool ieee80211_config_puncturing(struct ieee80211_link_data *link, } extracted = ieee80211_extract_dis_subch_bmap(eht_oper, - &link->conf->chandef, + &link->conf->chanreq.oper, bitmap); /* accept if there are no changes */ @@ -5871,12 +5876,12 @@ static bool ieee80211_config_puncturing(struct ieee80211_link_data *link, return true; if (!cfg80211_valid_disable_subchannel_bitmap(&bitmap, - &link->conf->chandef)) { + &link->conf->chanreq.oper)) { link_info(link, "Got an invalid disable subchannel bitmap from AP %pM: bitmap = 0x%x, bw = 0x%x. disconnect\n", link->u.mgd.bssid, bitmap, - link->conf->chandef.width); + link->conf->chanreq.oper.width); return false; } @@ -6573,10 +6578,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, goto free; } - if (WARN_ON(!link->conf->chandef.chan)) + if (WARN_ON(!link->conf->chanreq.oper.chan)) goto free; - sband = local->hw.wiphy->bands[link->conf->chandef.chan->band]; + sband = local->hw.wiphy->bands[link->conf->chanreq.oper.chan->band]; changed |= ieee80211_recalc_twt_req(sdata, sband, link, link_sta, elems); diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c index 449af4e1cca4..2dd4a2196af4 100644 --- a/net/mac80211/ocb.c +++ b/net/mac80211/ocb.c @@ -168,6 +168,7 @@ void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata) int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, struct ocb_setup *setup) { + struct ieee80211_chan_req chanreq = { .oper = setup->chandef }; struct ieee80211_local *local = sdata->local; struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; u64 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID; @@ -182,7 +183,7 @@ int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; sdata->deflink.needed_rx_chains = sdata->local->rx_chains; - err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef, + err = ieee80211_link_use_channel(&sdata->deflink, &chanreq, IEEE80211_CHANCTX_SHARED); if (err) return err; diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index d5ea5f5bcf3a..34e03b9522c8 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -4,7 +4,7 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2022 Intel Corporation + * Copyright (C) 2019, 2022-2024 Intel Corporation */ #include @@ -278,10 +278,10 @@ void ieee80211_check_rate_mask(struct ieee80211_link_data *link) u32 user_mask, basic_rates = link->conf->basic_rates; enum nl80211_band band; - if (WARN_ON(!link->conf->chandef.chan)) + if (WARN_ON(!link->conf->chanreq.oper.chan)) return; - band = link->conf->chandef.chan->band; + band = link->conf->chanreq.oper.chan->band; if (band == NL80211_BAND_S1GHZ) { /* TODO */ return; @@ -761,7 +761,7 @@ static bool rate_control_cap_mask(struct ieee80211_sub_if_data *sdata, u32 i, flags; *mask = sdata->rc_rateidx_mask[sband->band]; - flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); + flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chanreq.oper); for (i = 0; i < sband->n_bitrates; i++) { if ((flags & sband->bitrates[i].flags) != flags) *mask &= ~BIT(i); @@ -817,7 +817,7 @@ rate_control_apply_mask_ratetbl(struct sta_info *sta, mcs_mask, vht_mask)) return; - chan_width = sta->sdata->vif.bss_conf.chandef.width; + chan_width = sta->sdata->vif.bss_conf.chanreq.oper.width; for (i = 0; i < IEEE80211_TX_RATE_TABLE_SIZE; i++) { if (rates->rate[i].idx < 0) break; @@ -854,7 +854,7 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata, * included in the configured mask and change the rate indexes * if needed. */ - chan_width = sdata->vif.bss_conf.chandef.width; + chan_width = sdata->vif.bss_conf.chanreq.oper.width; for (i = 0; i < max_rates; i++) { /* Skip invalid rates */ if (rates[i].idx < 0) diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 51efc9bc8168..2b0bf2a1a877 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -108,26 +108,26 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, default: /* secondary_channel_offset was present but is invalid */ case IEEE80211_HT_PARAM_CHA_SEC_NONE: - cfg80211_chandef_create(&csa_ie->chandef, new_chan, + cfg80211_chandef_create(&csa_ie->chanreq.oper, new_chan, NL80211_CHAN_HT20); break; case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - cfg80211_chandef_create(&csa_ie->chandef, new_chan, + cfg80211_chandef_create(&csa_ie->chanreq.oper, new_chan, NL80211_CHAN_HT40PLUS); break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - cfg80211_chandef_create(&csa_ie->chandef, new_chan, + cfg80211_chandef_create(&csa_ie->chanreq.oper, new_chan, NL80211_CHAN_HT40MINUS); break; case -1: - cfg80211_chandef_create(&csa_ie->chandef, new_chan, + cfg80211_chandef_create(&csa_ie->chanreq.oper, new_chan, NL80211_CHAN_NO_HT); /* keep width for 5/10 MHz channels */ - switch (sdata->vif.bss_conf.chandef.width) { + switch (sdata->vif.bss_conf.chanreq.oper.width) { case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_10: - csa_ie->chandef.width = - sdata->vif.bss_conf.chandef.width; + csa_ie->chanreq.oper.width = + sdata->vif.bss_conf.chanreq.oper.width; break; default: break; @@ -137,7 +137,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, if (bwi) { /* start with the CSA one */ - new_vht_chandef = csa_ie->chandef; + new_vht_chandef = csa_ie->chanreq.oper; /* and update the width accordingly */ ieee80211_chandef_eht_oper(&bwi->info, &new_vht_chandef); } else if (wide_bw_chansw_ie) { @@ -159,7 +159,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT, * to the previously parsed chandef */ - new_vht_chandef = csa_ie->chandef; + new_vht_chandef = csa_ie->chanreq.oper; /* ignore if parsing fails */ if (!ieee80211_chandef_vht_oper(&sdata->local->hw, @@ -177,13 +177,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, /* if VHT data is there validate & use it */ if (new_vht_chandef.chan) { if (!cfg80211_chandef_compatible(&new_vht_chandef, - &csa_ie->chandef)) { + &csa_ie->chanreq.oper)) { sdata_info(sdata, "BSS %pM: CSA has inconsistent channel data, disconnecting\n", bssid); return -EINVAL; } - csa_ie->chandef = new_vht_chandef; + csa_ie->chanreq.oper = new_vht_chandef; } if (elems->max_channel_switch_time) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 396fd54d8bf7..0f4aa42e070f 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -159,7 +159,7 @@ static void ieee80211_tdls_add_oper_classes(struct ieee80211_link_data *link, u8 *pos; u8 op_class; - if (!ieee80211_chandef_to_operating_class(&link->conf->chandef, + if (!ieee80211_chandef_to_operating_class(&link->conf->chanreq.oper, &op_class)) return; @@ -438,7 +438,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, if (WARN_ON_ONCE(!sta)) return; - sta->tdls_chandef = link->conf->chandef; + sta->tdls_chandef = link->conf->chanreq.oper; } ieee80211_tdls_add_oper_classes(link, skb); @@ -638,7 +638,7 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_link_data *link, if (WARN_ON_ONCE(!sta || !ap_sta)) return; - sta->tdls_chandef = link->conf->chandef; + sta->tdls_chandef = link->conf->chanreq.oper; /* add any custom IEs that go before the QoS IE */ if (extra_ies_len) { @@ -684,7 +684,7 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_link_data *link, pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); ieee80211_ie_build_ht_oper(pos, &sta->sta.deflink.ht_cap, - &link->conf->chandef, prot, + &link->conf->chanreq.oper, prot, true); } diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e2dde3e77c30..efff20d7fe0e 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -503,9 +503,9 @@ TRACE_EVENT(drv_link_info_changed, __entry->ht_operation_mode = link_conf->ht_operation_mode; __entry->cqm_rssi_thold = link_conf->cqm_rssi_thold; __entry->cqm_rssi_hyst = link_conf->cqm_rssi_hyst; - __entry->channel_width = link_conf->chandef.width; - __entry->channel_cfreq1 = link_conf->chandef.center_freq1; - __entry->channel_cfreq1_offset = link_conf->chandef.freq1_offset; + __entry->channel_width = link_conf->chanreq.oper.width; + __entry->channel_cfreq1 = link_conf->chanreq.oper.center_freq1; + __entry->channel_cfreq1_offset = link_conf->chanreq.oper.freq1_offset; __entry->qos = link_conf->qos; __entry->hidden_ssid = link_conf->hidden_ssid; __entry->txpower = link_conf->txpower; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 51c1a99f57b8..49eef33b5e70 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2309,7 +2309,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, * in order to maximize the chance that we get a response. Some * badly-behaved APs don't respond when this parameter is included. */ - chandef.width = sdata->vif.bss_conf.chandef.width; + chandef.width = sdata->vif.bss_conf.chanreq.oper.width; if (flags & IEEE80211_PROBE_FLAG_DIRECTED) chandef.chan = NULL; else @@ -2351,7 +2351,8 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, if (WARN_ON(!sband)) return 1; - rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); + rate_flags = + ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chanreq.oper); num_rates = sband->n_bitrates; supp_rates = 0; @@ -4026,7 +4027,8 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, u32 basic_rates = sdata->vif.bss_conf.basic_rates; u32 rate_flags; - rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); + rate_flags = + ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chanreq.oper); sband = local->hw.wiphy->bands[band]; rates = 0; for (i = 0; i < sband->n_bitrates; i++) { @@ -4068,8 +4070,8 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, u32 basic_rates = sdata->vif.bss_conf.basic_rates; u32 rate_flags; - rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); - + rate_flags = + ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chanreq.oper); sband = local->hw.wiphy->bands[band]; exrates = 0; for (i = 0; i < sband->n_bitrates; i++) { @@ -4312,7 +4314,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) &sdata->deflink.dfs_cac_timer_work); if (sdata->wdev.cac_started) { - chandef = sdata->vif.bss_conf.chandef; + chandef = sdata->vif.bss_conf.chanreq.oper; ieee80211_link_release_channel(&sdata->deflink); cfg80211_cac_event(sdata->dev, &chandef, @@ -4756,7 +4758,7 @@ static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local, list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) if (link->reserved_radar_required) - radar_detect |= BIT(link->reserved_chandef.width); + radar_detect |= BIT(link->reserved.oper.width); /* * An in-place reservation context should not have any assigned vifs @@ -4770,7 +4772,7 @@ static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local, continue; radar_detect |= - BIT(link->conf->chandef.width); + BIT(link->conf->chanreq.oper.width); } return radar_detect; diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index b3a5c3e96a72..2c475c439ba9 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -369,7 +369,7 @@ ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta) link_conf = rcu_dereference(sdata->vif.link_conf[link_id]); if (eht_cap->has_eht && - link_conf->chandef.chan->band == NL80211_BAND_6GHZ) { + link_conf->chanreq.oper.chan->band == NL80211_BAND_6GHZ) { info = eht_cap->eht_cap_elem.phy_cap_info[0]; if (info & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) { @@ -380,7 +380,7 @@ ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta) info = he_cap->he_cap_elem.phy_cap_info[0]; - if (link_conf->chandef.chan->band == NL80211_BAND_2GHZ) { + if (link_conf->chanreq.oper.chan->band == NL80211_BAND_2GHZ) { if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) ret = IEEE80211_STA_RX_BW_40; else @@ -515,7 +515,7 @@ ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta) if (WARN_ON(!link_conf)) bss_width = NL80211_CHAN_WIDTH_20_NOHT; else - bss_width = link_conf->chandef.width; + bss_width = link_conf->chanreq.oper.width; rcu_read_unlock(); bw = ieee80211_sta_cap_rx_bw(link_sta); From d1256c1546a0e03f103e2f0381103dc747ea7f81 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:41 +0100 Subject: [PATCH 238/378] wifi: mac80211: add and use a link iteration macro In the channel context code we have quite a few instances of nested loops iterating the interfaces and then links. Add a new for_each_sdata_link() macro and use it. Also, since it's easier, convert all the loops and a few other places away from RCU as we now hold the wiphy mutex everywhere anyway. This does cause a little bit more work (such as checking interface types for each link of an interface rather than not iterating links in some cases), but that's not a huge issue and seems like an acceptable trade-off, readability is important too. Link: https://msgid.link/20240129194108.7240829bd96d.I5ccbb8dd019cbcb5326c85d76121359225d6541a@changeid Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 246 ++++++++++++------------------------- net/mac80211/ieee80211_i.h | 13 ++ 2 files changed, 89 insertions(+), 170 deletions(-) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index f1cef332e4db..c84449bdc928 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -211,7 +211,7 @@ static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta, enum ieee80211_sta_rx_bandwidth width; struct link_sta_info *link_sta; - link_sta = rcu_dereference(sta->link[link_id]); + link_sta = wiphy_dereference(sta->local->hw.wiphy, sta->link[link_id]); /* no effect if this STA has no presence on this link */ if (!link_sta) @@ -249,9 +249,10 @@ static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta, } static enum nl80211_chan_width -ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata, - unsigned int link_id) +ieee80211_get_max_required_bw(struct ieee80211_link_data *link) { + struct ieee80211_sub_if_data *sdata = link->sdata; + unsigned int link_id = link->link_id; enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; struct sta_info *sta; @@ -267,31 +268,25 @@ ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata, } static enum nl80211_chan_width -ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, - struct ieee80211_chanctx *ctx, - struct ieee80211_link_data *rsvd_for) +ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + struct ieee80211_link_data *rsvd_for) { + struct ieee80211_sub_if_data *sdata; + struct ieee80211_link_data *link; enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; - struct ieee80211_vif *vif = &sdata->vif; - int link_id; - rcu_read_lock(); - for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { + for_each_sdata_link(local, link) { enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; - struct ieee80211_link_data *link = - rcu_dereference(sdata->link[link_id]); - - if (!link) - continue; if (link != rsvd_for && rcu_access_pointer(link->conf->chanctx_conf) != &ctx->conf) continue; - switch (vif->type) { + switch (link->sdata->vif.type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: - width = ieee80211_get_max_required_bw(sdata, link_id); + width = ieee80211_get_max_required_bw(link); break; case NL80211_IFTYPE_STATION: /* @@ -300,7 +295,7 @@ ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, * account also for TDLS peers */ width = max(link->conf->chanreq.oper.width, - ieee80211_get_max_required_bw(sdata, link_id)); + ieee80211_get_max_required_bw(link)); break; case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: @@ -321,40 +316,13 @@ ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, max_bw = max(max_bw, width); } - rcu_read_unlock(); - - return max_bw; -} - -static enum nl80211_chan_width -ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx, - struct ieee80211_link_data *rsvd_for) -{ - struct ieee80211_sub_if_data *sdata; - enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; - - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - enum nl80211_chan_width width; - - if (!ieee80211_sdata_running(sdata)) - continue; - - width = ieee80211_get_chanctx_vif_max_required_bw(sdata, ctx, - rsvd_for); - - max_bw = max(max_bw, width); - } /* use the configured bandwidth in case of monitor interface */ - sdata = rcu_dereference(local->monitor_sdata); + sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata); if (sdata && rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &ctx->conf) max_bw = max(max_bw, ctx->conf.def.width); - rcu_read_unlock(); - return max_bw; } @@ -582,26 +550,14 @@ ieee80211_find_chanctx(struct ieee80211_local *local, bool ieee80211_is_radar_required(struct ieee80211_local *local) { - struct ieee80211_sub_if_data *sdata; + struct ieee80211_link_data *link; lockdep_assert_wiphy(local->hw.wiphy); - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - unsigned int link_id; - - for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { - struct ieee80211_link_data *link; - - link = rcu_dereference(sdata->link[link_id]); - - if (link && link->radar_required) { - rcu_read_unlock(); - return true; - } - } + for_each_sdata_link(local, link) { + if (link->radar_required) + return true; } - rcu_read_unlock(); return false; } @@ -611,38 +567,19 @@ ieee80211_chanctx_radar_required(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { struct ieee80211_chanctx_conf *conf = &ctx->conf; - struct ieee80211_sub_if_data *sdata; - bool required = false; + struct ieee80211_link_data *link; lockdep_assert_wiphy(local->hw.wiphy); - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - unsigned int link_id; - - if (!ieee80211_sdata_running(sdata)) + for_each_sdata_link(local, link) { + if (rcu_access_pointer(link->conf->chanctx_conf) != conf) continue; - for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { - struct ieee80211_link_data *link; - - link = rcu_dereference(sdata->link[link_id]); - if (!link) - continue; - - if (rcu_access_pointer(link->conf->chanctx_conf) != conf) - continue; - if (!link->radar_required) - continue; - required = true; - break; - } - - if (required) - break; + if (!link->radar_required) + continue; + return true; } - rcu_read_unlock(); - return required; + return false; } static struct ieee80211_chanctx * @@ -746,50 +683,38 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { struct ieee80211_chanctx_conf *conf = &ctx->conf; - struct ieee80211_sub_if_data *sdata; const struct cfg80211_chan_def *compat = NULL; + struct ieee80211_link_data *link; struct ieee80211_chan_req chanreq = {}; struct sta_info *sta; lockdep_assert_wiphy(local->hw.wiphy); - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - int link_id; + for_each_sdata_link(local, link) { + struct ieee80211_bss_conf *link_conf; - if (!ieee80211_sdata_running(sdata)) + if (link->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) continue; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + link_conf = link->conf; + + if (rcu_access_pointer(link_conf->chanctx_conf) != conf) continue; - for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { - struct ieee80211_bss_conf *link_conf = - rcu_dereference(sdata->vif.link_conf[link_id]); + if (!compat) + compat = &link_conf->chanreq.oper; - if (!link_conf) - continue; - - if (rcu_access_pointer(link_conf->chanctx_conf) != conf) - continue; - - if (!compat) - compat = &link_conf->chanreq.oper; - - compat = cfg80211_chandef_compatible(&link_conf->chanreq.oper, - compat); - if (WARN_ON_ONCE(!compat)) - break; - } + compat = cfg80211_chandef_compatible(&link_conf->chanreq.oper, + compat); + if (WARN_ON_ONCE(!compat)) + return; } - if (WARN_ON_ONCE(!compat)) { - rcu_read_unlock(); + if (WARN_ON_ONCE(!compat)) return; - } /* TDLS peers can sometimes affect the chandef width */ - list_for_each_entry_rcu(sta, &local->sta_list, list) { + list_for_each_entry(sta, &local->sta_list, list) { if (!sta->uploaded || !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) || !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || @@ -799,9 +724,8 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, compat = cfg80211_chandef_compatible(&sta->tdls_chandef, compat); if (WARN_ON_ONCE(!compat)) - break; + return; } - rcu_read_unlock(); if (!compat) return; @@ -895,23 +819,19 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, { struct ieee80211_sub_if_data *sdata; u8 rx_chains_static, rx_chains_dynamic; + struct ieee80211_link_data *link; lockdep_assert_wiphy(local->hw.wiphy); rx_chains_static = 1; rx_chains_dynamic = 1; - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { + for_each_sdata_link(local, link) { u8 needed_static, needed_dynamic; - unsigned int link_id; - if (!ieee80211_sdata_running(sdata)) - continue; - - switch (sdata->vif.type) { + switch (link->sdata->vif.type) { case NL80211_IFTYPE_STATION: - if (!sdata->u.mgd.associated) + if (!link->sdata->u.mgd.associated) continue; break; case NL80211_IFTYPE_AP: @@ -923,49 +843,38 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, continue; } - for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { - struct ieee80211_link_data *link; + if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf) + continue; - link = rcu_dereference(sdata->link[link_id]); - - if (!link) - continue; - - if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf) - continue; - - switch (link->smps_mode) { - default: - WARN_ONCE(1, "Invalid SMPS mode %d\n", - link->smps_mode); - fallthrough; - case IEEE80211_SMPS_OFF: - needed_static = link->needed_rx_chains; - needed_dynamic = link->needed_rx_chains; - break; - case IEEE80211_SMPS_DYNAMIC: - needed_static = 1; - needed_dynamic = link->needed_rx_chains; - break; - case IEEE80211_SMPS_STATIC: - needed_static = 1; - needed_dynamic = 1; - break; - } - - rx_chains_static = max(rx_chains_static, needed_static); - rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); + switch (link->smps_mode) { + default: + WARN_ONCE(1, "Invalid SMPS mode %d\n", + link->smps_mode); + fallthrough; + case IEEE80211_SMPS_OFF: + needed_static = link->needed_rx_chains; + needed_dynamic = link->needed_rx_chains; + break; + case IEEE80211_SMPS_DYNAMIC: + needed_static = 1; + needed_dynamic = link->needed_rx_chains; + break; + case IEEE80211_SMPS_STATIC: + needed_static = 1; + needed_dynamic = 1; + break; } + + rx_chains_static = max(rx_chains_static, needed_static); + rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); } /* Disable SMPS for the monitor interface */ - sdata = rcu_dereference(local->monitor_sdata); + sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata); if (sdata && rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &chanctx->conf) rx_chains_dynamic = rx_chains_static = local->rx_chains; - rcu_read_unlock(); - if (rx_chains_static == chanctx->conf.rx_chains_static && rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) return; @@ -1004,17 +913,16 @@ __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, if (clear) conf = NULL; - rcu_read_lock(); list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { struct ieee80211_bss_conf *vlan_conf; - vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]); + vlan_conf = wiphy_dereference(local->hw.wiphy, + vlan->vif.link_conf[link_id]); if (WARN_ON(!vlan_conf)) continue; rcu_assign_pointer(vlan_conf->chanctx_conf, conf); } - rcu_read_unlock(); } void ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, @@ -1204,17 +1112,16 @@ ieee80211_link_update_chanreq(struct ieee80211_link_data *link, if (sdata->vif.type != NL80211_IFTYPE_AP) return; - rcu_read_lock(); list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { struct ieee80211_bss_conf *vlan_conf; - vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]); + vlan_conf = wiphy_dereference(sdata->local->hw.wiphy, + vlan->vif.link_conf[link_id]); if (WARN_ON(!vlan_conf)) continue; vlan_conf->chanreq = *chanreq; } - rcu_read_unlock(); } static int @@ -1955,12 +1862,11 @@ void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link) ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); - rcu_read_lock(); - ap_conf = rcu_dereference(ap->vif.link_conf[link_id]); - conf = rcu_dereference_protected(ap_conf->chanctx_conf, - lockdep_is_held(&local->hw.wiphy->mtx)); + ap_conf = wiphy_dereference(local->hw.wiphy, + ap->vif.link_conf[link_id]); + conf = wiphy_dereference(local->hw.wiphy, + ap_conf->chanctx_conf); rcu_assign_pointer(link_conf->chanctx_conf, conf); - rcu_read_unlock(); } void ieee80211_iter_chan_contexts_atomic( diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 70c48cad180a..601b889b6237 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1181,6 +1181,19 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p) #define sdata_dereference(p, sdata) \ wiphy_dereference(sdata->local->hw.wiphy, p) +#define for_each_sdata_link(_local, _link) \ + /* outer loop just to define the variables ... */ \ + for (struct ieee80211_sub_if_data *___sdata = NULL; \ + !___sdata; \ + ___sdata = (void *)~0 /* always stop */) \ + list_for_each_entry(___sdata, &(_local)->interfaces, list) \ + if (ieee80211_sdata_running(___sdata)) \ + for (int ___link_id = 0; \ + ___link_id < ARRAY_SIZE(___sdata->link); \ + ___link_id++) \ + if ((_link = wiphy_dereference((local)->hw.wiphy, \ + ___sdata->link[___link_id]))) + static inline int ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems, struct cfg80211_rnr_elems *rnr_elems, From 761748f001800d925c2ee8b04407e7aee12c3ffb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:42 +0100 Subject: [PATCH 239/378] wifi: mac80211: support wider bandwidth OFDMA config EHT requires that stations are able to participate in wider bandwidth OFDMA, i.e. parse downlink OFDMA and uplink OFDMA triggers when they're not capable of (or not connected at) the (wider) bandwidth that the AP is using. This requires hardware configuration, since the entity responsible for parsing (possibly hardware) needs to know the AP bandwidth. To support this, change the channel request to have the AP's bandwidth for clients, and track that in the channel context in mac80211. This means that the same chandef might need to be split up into two different contexts, if the APs are different. Interfaces other than client are not participating in OFDMA the same way, so they don't request any AP setting. Note that this doesn't introduce any API to split a channel context, so that there are cases where this might lead to a disconnect, e.g. if there are two client interfaces using the same channel context, e.g. both 160 MHz connected to different 320 MHz APs, and one of the APs switches to 160 MHz. Note also there are possible cases where this can be optimised, e.g. when using the upper or lower 160 Mhz, but I haven't been able to really fully understand the spec and/or hardware limitations. If, for some reason, there are no hardware limits on this because the OFDMA (downlink/trigger) parsing is done in firmware and can take the transmitter into account, then drivers can set the new flag IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW on interfaces to not have them request any AP bandwidth in the channel context and ignore this issue entirely. The bss_conf still contains the AP configuration (if any, i.e. EHT) in the chanreq. Link: https://msgid.link/20240129194108.d3d5b35dd783.I939d04674f4ff06f39934b1591c8d36a30ce74c2@changeid Signed-off-by: Johannes Berg --- include/net/mac80211.h | 14 ++++ net/mac80211/chan.c | 167 ++++++++++++++++++++++++++++--------- net/mac80211/ieee80211_i.h | 13 +++ net/mac80211/mlme.c | 60 +++++++------ net/mac80211/trace.h | 31 ++++++- 5 files changed, 215 insertions(+), 70 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dd8a66e9afd9..ab6bc89d3394 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -214,6 +214,8 @@ struct ieee80211_low_level_stats { * @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel, * this is used only with channel switching with CSA * @IEEE80211_CHANCTX_CHANGE_MIN_WIDTH: The min required channel width changed + * @IEEE80211_CHANCTX_CHANGE_AP: The AP channel definition changed, so (wider + * bandwidth) OFDMA settings need to be changed */ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0), @@ -221,14 +223,18 @@ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2), IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(3), IEEE80211_CHANCTX_CHANGE_MIN_WIDTH = BIT(4), + IEEE80211_CHANCTX_CHANGE_AP = BIT(5), }; /** * struct ieee80211_chan_req - A channel "request" * @oper: channel definition to use for operation + * @ap: the channel definition of the AP, if any + * (otherwise the chan member is %NULL) */ struct ieee80211_chan_req { struct cfg80211_chan_def oper; + struct cfg80211_chan_def ap; }; /** @@ -239,6 +245,8 @@ struct ieee80211_chan_req { * * @def: the channel definition * @min_def: the minimum channel definition currently required. + * @ap: the channel definition the AP actually is operating as, + * for use with (wider bandwidth) OFDMA * @rx_chains_static: The number of RX chains that must always be * active on the channel to receive MIMO transmissions * @rx_chains_dynamic: The number of RX chains that must be enabled @@ -251,6 +259,7 @@ struct ieee80211_chan_req { struct ieee80211_chanctx_conf { struct cfg80211_chan_def def; struct cfg80211_chan_def min_def; + struct cfg80211_chan_def ap; u8 rx_chains_static, rx_chains_dynamic; @@ -1782,6 +1791,10 @@ struct ieee80211_channel_switch { * this is not pure P2P vif. * @IEEE80211_VIF_EML_ACTIVE: The driver indicates that EML operation is * enabled for the interface. + * @IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW: Ignore wider bandwidth OFDMA + * operation on this interface and request a channel context without + * the AP definition. Use this e.g. because the device is able to + * handle OFDMA (downlink and trigger for uplink) on a per-AP basis. */ enum ieee80211_vif_flags { IEEE80211_VIF_BEACON_FILTER = BIT(0), @@ -1789,6 +1802,7 @@ enum ieee80211_vif_flags { IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2), IEEE80211_VIF_GET_NOA_UPDATE = BIT(3), IEEE80211_VIF_EML_ACTIVE = BIT(4), + IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW = BIT(5), }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index c84449bdc928..fe1489ba58c6 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -81,22 +81,35 @@ ieee80211_link_get_chanctx(struct ieee80211_link_data *link) return container_of(conf, struct ieee80211_chanctx, conf); } +bool ieee80211_chanreq_identical(const struct ieee80211_chan_req *a, + const struct ieee80211_chan_req *b) +{ + if (!cfg80211_chandef_identical(&a->oper, &b->oper)) + return false; + if (!a->ap.chan && !b->ap.chan) + return true; + return cfg80211_chandef_identical(&a->ap, &b->ap); +} + static const struct ieee80211_chan_req * ieee80211_chanreq_compatible(const struct ieee80211_chan_req *a, - const struct ieee80211_chan_req *b) + const struct ieee80211_chan_req *b, + struct ieee80211_chan_req *tmp) { const struct cfg80211_chan_def *compat; + if (a->ap.chan && b->ap.chan && + !cfg80211_chandef_identical(&a->ap, &b->ap)) + return NULL; + compat = cfg80211_chandef_compatible(&a->oper, &b->oper); + if (!compat) + return NULL; - if (compat == &a->oper) - return a; - - if (compat == &b->oper) - return b; - - WARN_ON(compat); - return NULL; + /* Note: later code assumes this always fills & returns tmp if compat */ + tmp->oper = *compat; + tmp->ap = a->ap.chan ? a->ap : b->ap; + return tmp; } static const struct ieee80211_chan_req * @@ -104,17 +117,26 @@ ieee80211_chanctx_compatible(struct ieee80211_chanctx *ctx, const struct ieee80211_chan_req *req, struct ieee80211_chan_req *tmp) { + const struct ieee80211_chan_req *ret; + struct ieee80211_chan_req tmp2; + *tmp = (struct ieee80211_chan_req){ .oper = ctx->conf.def, + .ap = ctx->conf.ap, }; - return ieee80211_chanreq_compatible(tmp, req); + ret = ieee80211_chanreq_compatible(tmp, req, &tmp2); + if (!ret) + return NULL; + *tmp = *ret; + return tmp; } static const struct ieee80211_chan_req * ieee80211_chanctx_reserved_chanreq(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, - const struct ieee80211_chan_req *req) + const struct ieee80211_chan_req *req, + struct ieee80211_chan_req *tmp) { struct ieee80211_link_data *link; @@ -124,7 +146,7 @@ ieee80211_chanctx_reserved_chanreq(struct ieee80211_local *local, return NULL; list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) { - req = ieee80211_chanreq_compatible(&link->reserved, req); + req = ieee80211_chanreq_compatible(&link->reserved, req, tmp); if (!req) break; } @@ -135,7 +157,8 @@ ieee80211_chanctx_reserved_chanreq(struct ieee80211_local *local, static const struct ieee80211_chan_req * ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, - const struct ieee80211_chan_req *compat) + const struct ieee80211_chan_req *compat, + struct ieee80211_chan_req *tmp) { struct ieee80211_link_data *link; const struct ieee80211_chan_req *comp_def = compat; @@ -149,7 +172,7 @@ ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, continue; comp_def = ieee80211_chanreq_compatible(&link_conf->chanreq, - comp_def); + comp_def, tmp); if (!comp_def) break; } @@ -162,16 +185,18 @@ ieee80211_chanctx_can_reserve(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, const struct ieee80211_chan_req *req) { + struct ieee80211_chan_req tmp; + lockdep_assert_wiphy(local->hw.wiphy); - if (!ieee80211_chanctx_reserved_chanreq(local, ctx, req)) + if (!ieee80211_chanctx_reserved_chanreq(local, ctx, req, &tmp)) return false; - if (!ieee80211_chanctx_non_reserved_chandef(local, ctx, req)) + if (!ieee80211_chanctx_non_reserved_chandef(local, ctx, req, &tmp)) return false; if (!list_empty(&ctx->reserved_links) && - ieee80211_chanctx_reserved_chanreq(local, ctx, req)) + ieee80211_chanctx_reserved_chanreq(local, ctx, req, &tmp)) return true; return false; @@ -456,7 +481,11 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, struct ieee80211_link_data *rsvd_for) { const struct cfg80211_chan_def *chandef = &chanreq->oper; - u32 changed; + struct ieee80211_chan_req ctx_req = { + .oper = ctx->conf.def, + .ap = ctx->conf.ap, + }; + u32 changed = 0; /* expected to handle only 20/40/80/160/320 channel widths */ switch (chandef->width) { @@ -478,26 +507,31 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, */ ieee80211_chan_bw_change(local, old_ctx, true); - if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) { + if (ieee80211_chanreq_identical(&ctx_req, chanreq)) { ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for); return; } - WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); + WARN_ON(ieee80211_chanctx_refcount(local, ctx) > 1 && + !cfg80211_chandef_compatible(&ctx->conf.def, &chanreq->oper)); ieee80211_remove_wbrf(local, &ctx->conf.def); + if (!cfg80211_chandef_identical(&ctx->conf.def, &chanreq->oper)) + changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; + if (!cfg80211_chandef_identical(&ctx->conf.ap, &chanreq->ap)) + changed |= IEEE80211_CHANCTX_CHANGE_AP; ctx->conf.def = *chandef; + ctx->conf.ap = chanreq->ap; /* check if min chanctx also changed */ - changed = IEEE80211_CHANCTX_CHANGE_WIDTH | - _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for); + changed |= _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for); ieee80211_add_wbrf(local, &ctx->conf.def); drv_change_chanctx(local, ctx, changed); - /* check is BW wider */ + /* check if BW is wider */ ieee80211_chan_bw_change(local, old_ctx, false); } @@ -536,7 +570,7 @@ ieee80211_find_chanctx(struct ieee80211_local *local, continue; compat = ieee80211_chanctx_reserved_chanreq(local, ctx, - compat); + compat, &tmp); if (!compat) continue; @@ -598,6 +632,7 @@ ieee80211_alloc_chanctx(struct ieee80211_local *local, INIT_LIST_HEAD(&ctx->assigned_links); INIT_LIST_HEAD(&ctx->reserved_links); ctx->conf.def = chanreq->oper; + ctx->conf.ap = chanreq->ap; ctx->conf.rx_chains_static = 1; ctx->conf.rx_chains_dynamic = 1; ctx->mode = mode; @@ -683,9 +718,9 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { struct ieee80211_chanctx_conf *conf = &ctx->conf; - const struct cfg80211_chan_def *compat = NULL; + const struct ieee80211_chan_req *compat = NULL; struct ieee80211_link_data *link; - struct ieee80211_chan_req chanreq = {}; + struct ieee80211_chan_req tmp; struct sta_info *sta; lockdep_assert_wiphy(local->hw.wiphy); @@ -702,10 +737,10 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, continue; if (!compat) - compat = &link_conf->chanreq.oper; + compat = &link_conf->chanreq; - compat = cfg80211_chandef_compatible(&link_conf->chanreq.oper, - compat); + compat = ieee80211_chanreq_compatible(&link_conf->chanreq, + compat, &tmp); if (WARN_ON_ONCE(!compat)) return; } @@ -715,24 +750,23 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, /* TDLS peers can sometimes affect the chandef width */ list_for_each_entry(sta, &local->sta_list, list) { + struct ieee80211_chan_req tdls_chanreq = {}; if (!sta->uploaded || !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) || !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || !sta->tdls_chandef.chan) continue; - compat = cfg80211_chandef_compatible(&sta->tdls_chandef, - compat); + tdls_chanreq.oper = sta->tdls_chandef; + + /* note this always fills and returns &tmp if compat */ + compat = ieee80211_chanreq_compatible(&tdls_chanreq, + compat, &tmp); if (WARN_ON_ONCE(!compat)) return; } - if (!compat) - return; - - chanreq.oper = *compat; - - ieee80211_change_chanctx(local, ctx, ctx, &chanreq); + ieee80211_change_chanctx(local, ctx, ctx, compat); } static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, @@ -1133,6 +1167,7 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; struct ieee80211_chanctx *old_ctx, *new_ctx; const struct ieee80211_chan_req *chanreq; + struct ieee80211_chan_req tmp; u64 changed = 0; int err; @@ -1155,7 +1190,8 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) return -EINVAL; chanreq = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, - &link->reserved); + &link->reserved, + &tmp); if (WARN_ON(!chanreq)) return -EINVAL; @@ -1213,6 +1249,7 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link) struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx *old_ctx, *new_ctx; const struct ieee80211_chan_req *chanreq; + struct ieee80211_chan_req tmp; int err; old_ctx = ieee80211_link_get_chanctx(link); @@ -1232,7 +1269,8 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link) return -EINVAL; chanreq = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, - &link->reserved); + &link->reserved, + &tmp); if (WARN_ON(!chanreq)) return -EINVAL; @@ -1770,6 +1808,52 @@ int ieee80211_link_use_reserved_context(struct ieee80211_link_data *link) return 0; } +/* + * This is similar to ieee80211_chanctx_compatible(), but rechecks + * against all the links actually using it (except the one that's + * passed, since that one is changing). + * This is done in order to allow changes to the AP's bandwidth for + * wider bandwidth OFDMA purposes, which wouldn't be treated as + * compatible by ieee80211_chanctx_recheck() but is OK if the link + * requesting the update is the only one using it. + */ +static const struct ieee80211_chan_req * +ieee80211_chanctx_recheck(struct ieee80211_local *local, + struct ieee80211_link_data *skip_link, + struct ieee80211_chanctx *ctx, + const struct ieee80211_chan_req *req, + struct ieee80211_chan_req *tmp) +{ + const struct ieee80211_chan_req *ret = req; + struct ieee80211_link_data *link; + + lockdep_assert_wiphy(local->hw.wiphy); + + for_each_sdata_link(local, link) { + if (link == skip_link) + continue; + + if (rcu_access_pointer(link->conf->chanctx_conf) == &ctx->conf) { + ret = ieee80211_chanreq_compatible(ret, + &link->conf->chanreq, + tmp); + if (!ret) + return NULL; + } + + if (link->reserved_chanctx == ctx) { + ret = ieee80211_chanreq_compatible(ret, + &link->reserved, + tmp); + if (!ret) + return NULL; + } + } + + *tmp = *ret; + return tmp; +} + int ieee80211_link_change_chanreq(struct ieee80211_link_data *link, const struct ieee80211_chan_req *chanreq, u64 *changed) @@ -1806,13 +1890,14 @@ int ieee80211_link_change_chanreq(struct ieee80211_link_data *link, ctx = container_of(conf, struct ieee80211_chanctx, conf); - compat = ieee80211_chanctx_compatible(ctx, chanreq, &tmp); + compat = ieee80211_chanctx_recheck(local, link, ctx, chanreq, &tmp); if (!compat) return -EINVAL; switch (ctx->replace_state) { case IEEE80211_CHANCTX_REPLACE_NONE: - if (!ieee80211_chanctx_reserved_chanreq(local, ctx, compat)) + if (!ieee80211_chanctx_reserved_chanreq(local, ctx, compat, + &tmp)) return -EBUSY; break; case IEEE80211_CHANCTX_WILL_BE_REPLACED: diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 601b889b6237..534cac3fc8df 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2536,6 +2536,19 @@ bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper, struct cfg80211_chan_def *chandef); void ieee80211_chandef_downgrade(struct cfg80211_chan_def *chandef, struct ieee80211_conn_settings *conn); +static inline void +ieee80211_chanreq_downgrade(struct ieee80211_chan_req *chanreq, + struct ieee80211_conn_settings *conn) +{ + ieee80211_chandef_downgrade(&chanreq->oper, conn); + if (WARN_ON(!conn)) + return; + if (conn->mode < IEEE80211_CONN_MODE_EHT) + chanreq->ap.chan = NULL; +} + +bool ieee80211_chanreq_identical(const struct ieee80211_chan_req *a, + const struct ieee80211_chan_req *b); int __must_check ieee80211_link_use_channel(struct ieee80211_link_data *link, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e9d720f25ddf..31d6a5603582 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -665,7 +665,7 @@ static struct ieee802_11_elems * ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, struct ieee80211_conn_settings *conn, struct cfg80211_bss *cbss, int link_id, - struct cfg80211_chan_def *chandef) + struct ieee80211_chan_req *chanreq) { struct ieee80211_local *local = sdata->local; const struct cfg80211_bss_ies *ies = rcu_dereference(cbss->ies); @@ -752,20 +752,27 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, } conn->mode = ap_mode; - *chandef = ap_chandef; + chanreq->oper = ap_chandef; - while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, + /* wider-bandwidth OFDMA is only done in EHT */ + if (conn->mode >= IEEE80211_CONN_MODE_EHT && + !(sdata->vif.driver_flags & IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW)) + chanreq->ap = ap_chandef; + else + chanreq->ap.chan = NULL; + + while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &chanreq->oper, IEEE80211_CHAN_DISABLED)) { - if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { + if (WARN_ON(chanreq->oper.width == NL80211_CHAN_WIDTH_20_NOHT)) { ret = -EINVAL; goto free; } - ieee80211_chandef_downgrade(chandef, conn); + ieee80211_chanreq_downgrade(chanreq, conn); } if (conn->mode >= IEEE80211_CONN_MODE_HE && - !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, + !cfg80211_chandef_usable(sdata->wdev.wiphy, &chanreq->oper, IEEE80211_CHAN_NO_HE)) { conn->mode = IEEE80211_CONN_MODE_VHT; conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, @@ -774,7 +781,7 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, } if (conn->mode >= IEEE80211_CONN_MODE_EHT && - !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, + !cfg80211_chandef_usable(sdata->wdev.wiphy, &chanreq->oper, IEEE80211_CHAN_NO_EHT)) { conn->mode = IEEE80211_CONN_MODE_HE; conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, @@ -782,7 +789,7 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, IEEE80211_CONN_BW_LIMIT_160); } - if (chandef->width != ap_chandef.width || ap_mode != conn->mode) + if (chanreq->oper.width != ap_chandef.width || ap_mode != conn->mode) sdata_info(sdata, "regulatory prevented using AP config, downgraded\n"); @@ -847,7 +854,7 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, ieee80211_conn_mode_str(conn->mode), 20 * (1 << conn->bw_limit)); - if (WARN_ON_ONCE(!cfg80211_chandef_valid(chandef))) { + if (WARN_ON_ONCE(!cfg80211_chandef_valid(&chanreq->oper))) { ret = -EINVAL; goto free; } @@ -865,7 +872,6 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, struct ieee80211_channel *channel = link->conf->chanreq.oper.chan; struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_chan_req chanreq = {}; - struct cfg80211_chan_def ap_chandef; enum ieee80211_conn_mode ap_mode; u32 vht_cap_info = 0; u16 ht_opmode; @@ -881,7 +887,7 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, ap_mode = ieee80211_determine_ap_chan(sdata, channel, vht_cap_info, elems, true, &link->u.mgd.conn, - &ap_chandef); + &chanreq.ap); if (ap_mode != link->u.mgd.conn.mode) { link_info(link, @@ -891,6 +897,11 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, return -EINVAL; } + chanreq.oper = chanreq.ap; + if (link->u.mgd.conn.mode < IEEE80211_CONN_MODE_EHT || + sdata->vif.driver_flags & IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW) + chanreq.ap.chan = NULL; + /* * if HT operation mode changed store the new one - * this may be applicable even if channel is identical @@ -911,20 +922,20 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, * won't do us any good -- we couldn't use it with the AP. */ while (link->u.mgd.conn.bw_limit < - ieee80211_min_bw_limit_from_chandef(&ap_chandef)) - ieee80211_chandef_downgrade(&ap_chandef, NULL); + ieee80211_min_bw_limit_from_chandef(&chanreq.oper)) + ieee80211_chandef_downgrade(&chanreq.oper, NULL); - if (cfg80211_chandef_identical(&ap_chandef, &link->conf->chanreq.oper)) + if (ieee80211_chanreq_identical(&chanreq, &link->conf->chanreq)) return 0; link_info(link, - "AP %pM changed bandwidth, new config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n", - link->u.mgd.bssid, ap_chandef.chan->center_freq, - ap_chandef.chan->freq_offset, ap_chandef.width, - ap_chandef.center_freq1, ap_chandef.freq1_offset, - ap_chandef.center_freq2); + "AP %pM changed bandwidth, new used config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n", + link->u.mgd.bssid, chanreq.oper.chan->center_freq, + chanreq.oper.chan->freq_offset, chanreq.oper.width, + chanreq.oper.center_freq1, chanreq.oper.freq1_offset, + chanreq.oper.center_freq2); - if (!cfg80211_chandef_valid(&ap_chandef)) { + if (!cfg80211_chandef_valid(&chanreq.oper)) { sdata_info(sdata, "AP %pM changed caps/bw in a way we can't support - disconnect\n", link->u.mgd.bssid); @@ -947,7 +958,6 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, * bandwidth changes where a this could happen, but those cases are * less common and wouldn't completely prevent using the AP. */ - chanreq.oper = ap_chandef; ret = ieee80211_link_change_chanreq(link, &chanreq, changed); if (ret) { @@ -2071,8 +2081,8 @@ static void ieee80211_chswitch_work(struct wiphy *wiphy, return; } - if (!cfg80211_chandef_identical(&link->conf->chanreq.oper, - &link->csa_chanreq.oper)) { + if (!ieee80211_chanreq_identical(&link->conf->chanreq, + &link->csa_chanreq)) { sdata_info(sdata, "failed to finalize channel switch, disconnecting\n"); wiphy_work_queue(sdata->local->hw.wiphy, @@ -5146,7 +5156,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); elems = ieee80211_determine_chan_mode(sdata, conn, cbss, link_id, - &chanreq.oper); + &chanreq); if (IS_ERR(elems)) { rcu_read_unlock(); @@ -5209,7 +5219,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, return ret; while (ret && chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) { - ieee80211_chandef_downgrade(&chanreq.oper, conn); + ieee80211_chanreq_downgrade(&chanreq, conn); ret = ieee80211_link_use_channel(link, &chanreq, IEEE80211_CHANCTX_SHARED); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index efff20d7fe0e..478b32d2520a 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -50,7 +50,7 @@ __entry->center_freq1 = (c) ? (c)->center_freq1 : 0; \ __entry->freq1_offset = (c) ? (c)->freq1_offset : 0; \ __entry->center_freq2 = (c) ? (c)->center_freq2 : 0; -#define CHANDEF_PR_FMT " control:%d.%03d MHz width:%d center: %d.%03d/%d MHz" +#define CHANDEF_PR_FMT " chandef(%d.%03d MHz,width:%d,center: %d.%03d/%d MHz)" #define CHANDEF_PR_ARG __entry->control_freq, __entry->freq_offset, __entry->chan_width, \ __entry->center_freq1, __entry->freq1_offset, __entry->center_freq2 @@ -69,22 +69,45 @@ __entry->min_center_freq1 = (c)->center_freq1; \ __entry->min_freq1_offset = (c)->freq1_offset; \ __entry->min_center_freq2 = (c)->center_freq2; -#define MIN_CHANDEF_PR_FMT " min_control:%d.%03d MHz min_width:%d min_center: %d.%03d/%d MHz" +#define MIN_CHANDEF_PR_FMT " mindef(%d.%03d MHz,width:%d,center: %d.%03d/%d MHz)" #define MIN_CHANDEF_PR_ARG __entry->min_control_freq, __entry->min_freq_offset, \ __entry->min_chan_width, \ __entry->min_center_freq1, __entry->min_freq1_offset, \ __entry->min_center_freq2 +#define AP_CHANDEF_ENTRY \ + __field(u32, ap_control_freq) \ + __field(u32, ap_freq_offset) \ + __field(u32, ap_chan_width) \ + __field(u32, ap_center_freq1) \ + __field(u32, ap_freq1_offset) \ + __field(u32, ap_center_freq2) + +#define AP_CHANDEF_ASSIGN(c) \ + __entry->ap_control_freq = (c)->chan ? (c)->chan->center_freq : 0;\ + __entry->ap_freq_offset = (c)->chan ? (c)->chan->freq_offset : 0;\ + __entry->ap_chan_width = (c)->chan ? (c)->width : 0; \ + __entry->ap_center_freq1 = (c)->chan ? (c)->center_freq1 : 0; \ + __entry->ap_freq1_offset = (c)->chan ? (c)->freq1_offset : 0; \ + __entry->ap_center_freq2 = (c)->chan ? (c)->center_freq2 : 0; +#define AP_CHANDEF_PR_FMT " ap(%d.%03d MHz,width:%d,center: %d.%03d/%d MHz)" +#define AP_CHANDEF_PR_ARG __entry->ap_control_freq, __entry->ap_freq_offset, \ + __entry->ap_chan_width, \ + __entry->ap_center_freq1, __entry->ap_freq1_offset, \ + __entry->ap_center_freq2 + #define CHANCTX_ENTRY CHANDEF_ENTRY \ MIN_CHANDEF_ENTRY \ + AP_CHANDEF_ENTRY \ __field(u8, rx_chains_static) \ __field(u8, rx_chains_dynamic) #define CHANCTX_ASSIGN CHANDEF_ASSIGN(&ctx->conf.def) \ MIN_CHANDEF_ASSIGN(&ctx->conf.min_def) \ + AP_CHANDEF_ASSIGN(&ctx->conf.ap) \ __entry->rx_chains_static = ctx->conf.rx_chains_static; \ __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic -#define CHANCTX_PR_FMT CHANDEF_PR_FMT MIN_CHANDEF_PR_FMT " chains:%d/%d" -#define CHANCTX_PR_ARG CHANDEF_PR_ARG, MIN_CHANDEF_PR_ARG, \ +#define CHANCTX_PR_FMT CHANDEF_PR_FMT MIN_CHANDEF_PR_FMT AP_CHANDEF_PR_FMT " chains:%d/%d" +#define CHANCTX_PR_ARG CHANDEF_PR_ARG, MIN_CHANDEF_PR_ARG, AP_CHANDEF_PR_ARG, \ __entry->rx_chains_static, __entry->rx_chains_dynamic #define KEY_ENTRY __field(u32, cipher) \ From 6bc574a7cd27ba0c8d77425055a8fd672ec928f2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:43 +0100 Subject: [PATCH 240/378] wifi: mac80211: validate assoc response channel config Due to the earlier restructuring we now mostly ignore the channel configuration in the association response, apart from the HT/VHT checks we had. Don't do that, but parse it and update, also dropping the association if the AP changed its mode in the response. Link: https://msgid.link/20240129194108.b3efa5eae60c.I1b70c9fd56781b22cdfdca55d34d69f7d0733e31@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 31d6a5603582..da202103faf0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -867,7 +867,7 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, static int ieee80211_config_bw(struct ieee80211_link_data *link, struct ieee802_11_elems *elems, - u64 *changed) + bool update, u64 *changed) { struct ieee80211_channel *channel = link->conf->chanreq.oper.chan; struct ieee80211_sub_if_data *sdata = link->sdata; @@ -942,6 +942,11 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, return -EINVAL; } + if (!update) { + link->conf->chanreq = chanreq; + return 0; + } + /* * We're tracking the current AP here, so don't do any further checks * here. This keeps us from playing ping-pong with regulatory, without @@ -4505,6 +4510,8 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, /* * We previously checked these in the beacon/probe response, so * they should be present here. This is just a safety net. + * Note that the ieee80211_config_bw() below would also check + * for this (and more), but this has better error reporting. */ if (!is_6ghz && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT && (!elems->wmm_param || !elems->ht_cap_elem || !elems->ht_operation)) { @@ -4522,6 +4529,14 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, goto out; } + /* check/update if AP changed anything in assoc response vs. scan */ + if (ieee80211_config_bw(link, elems, + link_id == assoc_data->assoc_link_id, + changed)) { + ret = false; + goto out; + } + if (WARN_ON(!link->conf->chanreq.oper.chan)) { ret = false; goto out; @@ -6595,7 +6610,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, changed |= ieee80211_recalc_twt_req(sdata, sband, link, link_sta, elems); - if (ieee80211_config_bw(link, elems, &changed)) { + if (ieee80211_config_bw(link, elems, true, &changed)) { ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DEAUTH_LEAVING, true, deauth_buf); From 719036ae06d4bfdb65139e3947a8404dec298bc5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:44 +0100 Subject: [PATCH 241/378] wifi: cfg80211: move puncturing validation code Upcoming patches will move the puncturing bitmap into the chandef, so chandef validation will need to check for correct puncturing. Purely move the code first so later changes are easier to review. Link: https://msgid.link/20240129194108.1ca184427c76.I077deb8d52c4648eac145b63f88b6c5a3b920ddc@changeid Signed-off-by: Johannes Berg --- net/wireless/chan.c | 138 ++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/net/wireless/chan.c b/net/wireless/chan.c index ceb9174c5c3d..71f1bd456d88 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -56,6 +56,75 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, } EXPORT_SYMBOL(cfg80211_chandef_create); +struct cfg80211_per_bw_puncturing_values { + u8 len; + const u16 *valid_values; +}; + +static const u16 puncturing_values_80mhz[] = { + 0x8, 0x4, 0x2, 0x1 +}; + +static const u16 puncturing_values_160mhz[] = { + 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1, 0xc0, 0x30, 0xc, 0x3 +}; + +static const u16 puncturing_values_320mhz[] = { + 0xc000, 0x3000, 0xc00, 0x300, 0xc0, 0x30, 0xc, 0x3, 0xf000, 0xf00, + 0xf0, 0xf, 0xfc00, 0xf300, 0xf0c0, 0xf030, 0xf00c, 0xf003, 0xc00f, + 0x300f, 0xc0f, 0x30f, 0xcf, 0x3f +}; + +#define CFG80211_PER_BW_VALID_PUNCTURING_VALUES(_bw) \ + { \ + .len = ARRAY_SIZE(puncturing_values_ ## _bw ## mhz), \ + .valid_values = puncturing_values_ ## _bw ## mhz \ + } + +static const struct cfg80211_per_bw_puncturing_values per_bw_puncturing[] = { + CFG80211_PER_BW_VALID_PUNCTURING_VALUES(80), + CFG80211_PER_BW_VALID_PUNCTURING_VALUES(160), + CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320) +}; + +bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap, + const struct cfg80211_chan_def *chandef) +{ + u32 idx, i, start_freq; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_80: + idx = 0; + start_freq = chandef->center_freq1 - 40; + break; + case NL80211_CHAN_WIDTH_160: + idx = 1; + start_freq = chandef->center_freq1 - 80; + break; + case NL80211_CHAN_WIDTH_320: + idx = 2; + start_freq = chandef->center_freq1 - 160; + break; + default: + *bitmap = 0; + break; + } + + if (!*bitmap) + return true; + + /* check if primary channel is punctured */ + if (*bitmap & (u16)BIT((chandef->chan->center_freq - start_freq) / 20)) + return false; + + for (i = 0; i < per_bw_puncturing[idx].len; i++) + if (per_bw_puncturing[idx].valid_values[i] == *bitmap) + return true; + + return false; +} +EXPORT_SYMBOL(cfg80211_valid_disable_subchannel_bitmap); + static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef) { int max_contiguous = 0; @@ -1532,72 +1601,3 @@ struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev, } } EXPORT_SYMBOL(wdev_chandef); - -struct cfg80211_per_bw_puncturing_values { - u8 len; - const u16 *valid_values; -}; - -static const u16 puncturing_values_80mhz[] = { - 0x8, 0x4, 0x2, 0x1 -}; - -static const u16 puncturing_values_160mhz[] = { - 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1, 0xc0, 0x30, 0xc, 0x3 -}; - -static const u16 puncturing_values_320mhz[] = { - 0xc000, 0x3000, 0xc00, 0x300, 0xc0, 0x30, 0xc, 0x3, 0xf000, 0xf00, - 0xf0, 0xf, 0xfc00, 0xf300, 0xf0c0, 0xf030, 0xf00c, 0xf003, 0xc00f, - 0x300f, 0xc0f, 0x30f, 0xcf, 0x3f -}; - -#define CFG80211_PER_BW_VALID_PUNCTURING_VALUES(_bw) \ - { \ - .len = ARRAY_SIZE(puncturing_values_ ## _bw ## mhz), \ - .valid_values = puncturing_values_ ## _bw ## mhz \ - } - -static const struct cfg80211_per_bw_puncturing_values per_bw_puncturing[] = { - CFG80211_PER_BW_VALID_PUNCTURING_VALUES(80), - CFG80211_PER_BW_VALID_PUNCTURING_VALUES(160), - CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320) -}; - -bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap, - const struct cfg80211_chan_def *chandef) -{ - u32 idx, i, start_freq; - - switch (chandef->width) { - case NL80211_CHAN_WIDTH_80: - idx = 0; - start_freq = chandef->center_freq1 - 40; - break; - case NL80211_CHAN_WIDTH_160: - idx = 1; - start_freq = chandef->center_freq1 - 80; - break; - case NL80211_CHAN_WIDTH_320: - idx = 2; - start_freq = chandef->center_freq1 - 160; - break; - default: - *bitmap = 0; - break; - } - - if (!*bitmap) - return true; - - /* check if primary channel is punctured */ - if (*bitmap & (u16)BIT((chandef->chan->center_freq - start_freq) / 20)) - return false; - - for (i = 0; i < per_bw_puncturing[idx].len; i++) - if (per_bw_puncturing[idx].valid_values[i] == *bitmap) - return true; - - return false; -} -EXPORT_SYMBOL(cfg80211_valid_disable_subchannel_bitmap); From c478db84c8544156b80c5e5d3a8c7840d557707a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:45 +0100 Subject: [PATCH 242/378] wifi: mac80211: refactor puncturing bitmap extraction Add a new inline helper function to ieee80211.h to extract the disabled subchannels bitmap from an EHT operation element, and use that in mac80211 where we do that. Link: https://msgid.link/20240129194108.d9f50dcec8d0.I8b08cbc2490a734fafcce0fa0fc328211ba6f10b@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 16 +++++++++++++ net/mac80211/mlme.c | 50 +++++++++++++-------------------------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index a70388ae3a7b..d9d2c1253157 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -3189,6 +3189,22 @@ ieee80211_eht_oper_size_ok(const u8 *data, u8 len) return len >= needed; } +/* must validate ieee80211_eht_oper_size_ok() first */ +static inline u16 +ieee80211_eht_oper_dis_subchan_bitmap(const struct ieee80211_eht_operation *eht_oper) +{ + const struct ieee80211_eht_operation_info *info = + (const void *)eht_oper->optional; + + if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) + return 0; + + if (!(eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) + return 0; + + return get_unaligned_le16(info->optional); +} + #define IEEE80211_BW_IND_DIS_SUBCH_PRESENT BIT(1) struct ieee80211_bandwidth_indication { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index da202103faf0..74a15f18e7ee 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -813,36 +813,27 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, } if (conn->mode >= IEEE80211_CONN_MODE_EHT) { - const struct ieee80211_eht_operation *eht_oper; + u16 bitmap; - eht_oper = elems->eht_operation; - - if (WARN_ON_ONCE(!eht_oper)) { + if (WARN_ON_ONCE(!elems->eht_operation)) { ret = -EINVAL; goto free; } - if (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT && - eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT) { - const struct ieee80211_eht_operation_info *info = - (void *)eht_oper->optional; - const u8 *disable_subchannel_bitmap = info->optional; - u16 bitmap; + bitmap = ieee80211_eht_oper_dis_subchan_bitmap(elems->eht_operation); - bitmap = get_unaligned_le16(disable_subchannel_bitmap); - if (!cfg80211_valid_disable_subchannel_bitmap(&bitmap, - &ap_chandef) || - (bitmap && - ieee80211_hw_check(&local->hw, DISALLOW_PUNCTURING))) { - conn->mode = IEEE80211_CONN_MODE_HE; - conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, - conn->bw_limit, - IEEE80211_CONN_BW_LIMIT_160); - sdata_info(sdata, - "AP has invalid/unsupported puncturing, disabling EHT\n"); - } - /* FIXME: store puncturing bitmap */ + if (!cfg80211_valid_disable_subchannel_bitmap(&bitmap, + &ap_chandef) || + (bitmap && + ieee80211_hw_check(&local->hw, DISALLOW_PUNCTURING))) { + conn->mode = IEEE80211_CONN_MODE_HE; + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_160); + sdata_info(sdata, + "AP has invalid/unsupported puncturing, disabling EHT\n"); } + /* FIXME: store puncturing bitmap */ } /* the mode can only decrease, so this must terminate */ @@ -5879,18 +5870,9 @@ static bool ieee80211_config_puncturing(struct ieee80211_link_data *link, u64 *changed) { struct ieee80211_local *local = link->sdata->local; - u16 bitmap = 0, extracted; - - if ((eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) && - (eht_oper->params & - IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) { - const struct ieee80211_eht_operation_info *info = - (void *)eht_oper->optional; - const u8 *disable_subchannel_bitmap = info->optional; - - bitmap = get_unaligned_le16(disable_subchannel_bitmap); - } + u16 bitmap, extracted; + bitmap = ieee80211_eht_oper_dis_subchan_bitmap(eht_oper); extracted = ieee80211_extract_dis_subch_bmap(eht_oper, &link->conf->chanreq.oper, bitmap); From b9d908dc3a294d25c7d6c2f54ca3987cbd98f040 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:46 +0100 Subject: [PATCH 243/378] wifi: wireless: declare different S1G chandefs incompatible It doesn't look like we can get into this code, but make it more robust and declare two S1G chandefs to be incompatible unless they're identical. Link: https://msgid.link/20240129194108.b28fb0644a8c.I9297ada5cf1baf00dbbdf8fcffd1806883489fc9@changeid Signed-off-by: Johannes Berg --- net/wireless/chan.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 71f1bd456d88..159b8aac451e 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -465,13 +465,18 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, return NULL; /* - * can't be compatible if one of them is 5 or 10 MHz, + * can't be compatible if one of them is 5/10 MHz or S1G * but they don't have the same width. */ - if (c1->width == NL80211_CHAN_WIDTH_5 || - c1->width == NL80211_CHAN_WIDTH_10 || - c2->width == NL80211_CHAN_WIDTH_5 || - c2->width == NL80211_CHAN_WIDTH_10) +#define NARROW_OR_S1G(width) ((width) == NL80211_CHAN_WIDTH_5 || \ + (width) == NL80211_CHAN_WIDTH_10 || \ + (width) == NL80211_CHAN_WIDTH_1 || \ + (width) == NL80211_CHAN_WIDTH_2 || \ + (width) == NL80211_CHAN_WIDTH_4 || \ + (width) == NL80211_CHAN_WIDTH_8 || \ + (width) == NL80211_CHAN_WIDTH_16) + + if (NARROW_OR_S1G(c1->width) || NARROW_OR_S1G(c2->width)) return NULL; if (c1->width == NL80211_CHAN_WIDTH_20_NOHT || From 8f251a0a1566e3e1da0f1d9322c8ffae808a7509 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:47 +0100 Subject: [PATCH 244/378] wifi: cfg80211: simplify cfg80211_chandef_compatible() Simplify cfg80211_chandef_compatible() a bit by switching c1 and c2 around so that c1 is always the narrower one (once they're not identical or narrow/S1G). Then we can just check the various primary channels and exit with the wider one (c2), or NULL. Also refactor the primary 40/80/160 function to not have all the calculations hard-coded, and use a wrapper around it to check primary 40/80/160 compatibility. While at it, add some kunit tests for this functionality. Also expose the new cfg80211_chandef_primary_freq() to drivers, mac80211 will use it. Link: https://msgid.link/20240129194108.be3e6eccaba3.I8399c2ff1435d7378e5837794cb5aa6dd2ee1416@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 11 +++ net/wireless/chan.c | 190 +++++++++++++++++++----------------- net/wireless/tests/Makefile | 2 +- net/wireless/tests/chan.c | 182 ++++++++++++++++++++++++++++++++++ 4 files changed, 294 insertions(+), 91 deletions(-) create mode 100644 net/wireless/tests/chan.c diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5b42bfc1b660..fc2ad80118e8 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1047,6 +1047,17 @@ unsigned int cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef); +/** + * cfg80211_chandef_primary_freq - calculate primary 40/80/160 MHz freq + * @chandef: chandef to calculate for + * @primary_chan_width: primary channel width to calculate center for + * + * Returns: the primary 40/80/160 MHz channel center frequency, or -1 + * for errors + */ +int cfg80211_chandef_primary_freq(const struct cfg80211_chan_def *chandef, + enum nl80211_chan_width primary_chan_width); + /** * nl80211_send_chandef - sends the channel definition. * @msg: the msg to send channel definition diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 159b8aac451e..bfa2ea935fc2 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -390,68 +390,60 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) } EXPORT_SYMBOL(cfg80211_chandef_valid); -static void chandef_primary_freqs(const struct cfg80211_chan_def *c, - u32 *pri40, u32 *pri80, u32 *pri160) +int cfg80211_chandef_primary_freq(const struct cfg80211_chan_def *c, + enum nl80211_chan_width primary_chan_width) { - int tmp; + int pri_width = nl80211_chan_width_to_mhz(primary_chan_width); + int width = cfg80211_chandef_get_width(c); + u32 control = c->chan->center_freq; + u32 center = c->center_freq1; - switch (c->width) { - case NL80211_CHAN_WIDTH_40: - *pri40 = c->center_freq1; - *pri80 = 0; - *pri160 = 0; - break; - case NL80211_CHAN_WIDTH_80: - case NL80211_CHAN_WIDTH_80P80: - *pri160 = 0; - *pri80 = c->center_freq1; - /* n_P20 */ - tmp = (30 + c->chan->center_freq - c->center_freq1)/20; - /* n_P40 */ - tmp /= 2; - /* freq_P40 */ - *pri40 = c->center_freq1 - 20 + 40 * tmp; - break; - case NL80211_CHAN_WIDTH_160: - *pri160 = c->center_freq1; - /* n_P20 */ - tmp = (70 + c->chan->center_freq - c->center_freq1)/20; - /* n_P40 */ - tmp /= 2; - /* freq_P40 */ - *pri40 = c->center_freq1 - 60 + 40 * tmp; - /* n_P80 */ - tmp /= 2; - *pri80 = c->center_freq1 - 40 + 80 * tmp; - break; - case NL80211_CHAN_WIDTH_320: - /* n_P20 */ - tmp = (150 + c->chan->center_freq - c->center_freq1) / 20; - /* n_P40 */ - tmp /= 2; - /* freq_P40 */ - *pri40 = c->center_freq1 - 140 + 40 * tmp; - /* n_P80 */ - tmp /= 2; - *pri80 = c->center_freq1 - 120 + 80 * tmp; - /* n_P160 */ - tmp /= 2; - *pri160 = c->center_freq1 - 80 + 160 * tmp; - break; - default: - WARN_ON_ONCE(1); + if (WARN_ON_ONCE(pri_width < 0 || width < 0)) + return -1; + + /* not intended to be called this way, can't determine */ + if (WARN_ON_ONCE(pri_width > width)) + return -1; + + while (width > pri_width) { + if (control > center) + center += width / 4; + else + center -= width / 4; + width /= 2; } + + return center; +} +EXPORT_SYMBOL(cfg80211_chandef_primary_freq); + +static const struct cfg80211_chan_def * +check_chandef_primary_compat(const struct cfg80211_chan_def *c1, + const struct cfg80211_chan_def *c2, + enum nl80211_chan_width primary_chan_width) +{ + /* check primary is compatible -> error if not */ + if (cfg80211_chandef_primary_freq(c1, primary_chan_width) != + cfg80211_chandef_primary_freq(c2, primary_chan_width)) + return ERR_PTR(-EINVAL); + + /* assumes c1 is smaller width, if that was just checked -> done */ + if (c1->width == primary_chan_width) + return c2; + + /* otherwise continue checking the next width */ + return NULL; } -const struct cfg80211_chan_def * -cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, - const struct cfg80211_chan_def *c2) +static const struct cfg80211_chan_def * +_cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, + const struct cfg80211_chan_def *c2) { - u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80, c1_pri160, c2_pri160; + const struct cfg80211_chan_def *ret; /* If they are identical, return */ if (cfg80211_chandef_identical(c1, c2)) - return c1; + return c2; /* otherwise, must have same control channel */ if (c1->chan != c2->chan) @@ -479,44 +471,62 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, if (NARROW_OR_S1G(c1->width) || NARROW_OR_S1G(c2->width)) return NULL; - if (c1->width == NL80211_CHAN_WIDTH_20_NOHT || - c1->width == NL80211_CHAN_WIDTH_20) - return c2; - - if (c2->width == NL80211_CHAN_WIDTH_20_NOHT || - c2->width == NL80211_CHAN_WIDTH_20) - return c1; - - chandef_primary_freqs(c1, &c1_pri40, &c1_pri80, &c1_pri160); - chandef_primary_freqs(c2, &c2_pri40, &c2_pri80, &c2_pri160); - - if (c1_pri40 != c2_pri40) - return NULL; - - if (c1->width == NL80211_CHAN_WIDTH_40) - return c2; - - if (c2->width == NL80211_CHAN_WIDTH_40) - return c1; - - if (c1_pri80 != c2_pri80) - return NULL; - - if (c1->width == NL80211_CHAN_WIDTH_80 && - c2->width > NL80211_CHAN_WIDTH_80) - return c2; - - if (c2->width == NL80211_CHAN_WIDTH_80 && - c1->width > NL80211_CHAN_WIDTH_80) - return c1; - - WARN_ON(!c1_pri160 && !c2_pri160); - if (c1_pri160 && c2_pri160 && c1_pri160 != c2_pri160) - return NULL; - + /* + * Make sure that c1 is always the narrower one, so that later + * we either return NULL or c2 and don't have to check both + * directions. + */ if (c1->width > c2->width) - return c1; - return c2; + swap(c1, c2); + + /* + * No further checks needed if the "narrower" one is only 20 MHz. + * Here "narrower" includes being a 20 MHz non-HT channel vs. a + * 20 MHz HT (or later) one. + */ + if (c1->width <= NL80211_CHAN_WIDTH_20) + return c2; + + ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_40); + if (ret) + return ret; + + ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_80); + if (ret) + return ret; + + /* + * If c1 is 80+80, then c2 is 160 or higher, but that cannot + * match. If c2 was also 80+80 it was already either accepted + * or rejected above (identical or not, respectively.) + */ + if (c1->width == NL80211_CHAN_WIDTH_80P80) + return NULL; + + ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_160); + if (ret) + return ret; + + /* + * Getting here would mean they're both wider than 160, have the + * same primary 160, but are not identical - this cannot happen + * since they must be 320 (no wider chandefs exist, at least yet.) + */ + WARN_ON_ONCE(1); + + return NULL; +} + +const struct cfg80211_chan_def * +cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, + const struct cfg80211_chan_def *c2) +{ + const struct cfg80211_chan_def *ret; + + ret = _cfg80211_chandef_compatible(c1, c2); + if (IS_ERR(ret)) + return NULL; + return ret; } EXPORT_SYMBOL(cfg80211_chandef_compatible); diff --git a/net/wireless/tests/Makefile b/net/wireless/tests/Makefile index 1f6622fcb758..c364e63b508e 100644 --- a/net/wireless/tests/Makefile +++ b/net/wireless/tests/Makefile @@ -1,3 +1,3 @@ -cfg80211-tests-y += module.o fragmentation.o scan.o util.o +cfg80211-tests-y += module.o fragmentation.o scan.o util.o chan.o obj-$(CONFIG_CFG80211_KUNIT_TEST) += cfg80211-tests.o diff --git a/net/wireless/tests/chan.c b/net/wireless/tests/chan.c new file mode 100644 index 000000000000..b9e7a27e43cb --- /dev/null +++ b/net/wireless/tests/chan.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2023 Intel Corporation + */ +#include +#include + +MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); + +static struct ieee80211_channel chan_6ghz_1 = { + .band = NL80211_BAND_6GHZ, + .center_freq = 5955, +}; + +static struct ieee80211_channel chan_6ghz_5 = { + .band = NL80211_BAND_6GHZ, + .center_freq = 5975, +}; + +static struct ieee80211_channel chan_6ghz_105 = { + .band = NL80211_BAND_6GHZ, + .center_freq = 6475, +}; + +static const struct chandef_compat_case { + const char *desc; + /* leave c1 empty for tests for identical */ + struct cfg80211_chan_def c1, c2; + /* we test both ways around, so c2 should always be the compat one */ + bool compat; +} chandef_compat_cases[] = { + { + .desc = "identical non-HT", + .c2 = { + .width = NL80211_CHAN_WIDTH_20_NOHT, + .chan = &chan_6ghz_1, + .center_freq1 = 5955, + }, + .compat = true, + }, + { + .desc = "identical 20 MHz", + .c2 = { + .width = NL80211_CHAN_WIDTH_20, + .chan = &chan_6ghz_1, + .center_freq1 = 5955, + }, + .compat = true, + }, + { + .desc = "identical 40 MHz", + .c2 = { + .width = NL80211_CHAN_WIDTH_40, + .chan = &chan_6ghz_1, + .center_freq1 = 5955 + 10, + }, + .compat = true, + }, + { + .desc = "identical 80 MHz", + .c2 = { + .width = NL80211_CHAN_WIDTH_80, + .chan = &chan_6ghz_1, + .center_freq1 = 5955 + 10 + 20, + }, + .compat = true, + }, + { + .desc = "identical 160 MHz", + .c2 = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_1, + .center_freq1 = 5955 + 10 + 20 + 40, + }, + .compat = true, + }, + { + .desc = "identical 320 MHz", + .c2 = { + .width = NL80211_CHAN_WIDTH_320, + .chan = &chan_6ghz_1, + .center_freq1 = 5955 + 10 + 20 + 40 + 80, + }, + .compat = true, + }, + { + .desc = "20 MHz in 320 MHz\n", + .c1 = { + .width = NL80211_CHAN_WIDTH_20, + .chan = &chan_6ghz_1, + .center_freq1 = 5955, + }, + .c2 = { + .width = NL80211_CHAN_WIDTH_320, + .chan = &chan_6ghz_1, + .center_freq1 = 5955 + 10 + 20 + 40 + 80, + }, + .compat = true, + }, + { + .desc = "different 20 MHz", + .c1 = { + .width = NL80211_CHAN_WIDTH_20, + .chan = &chan_6ghz_1, + .center_freq1 = 5955, + }, + .c2 = { + .width = NL80211_CHAN_WIDTH_20, + .chan = &chan_6ghz_5, + .center_freq1 = 5975, + }, + }, + { + .desc = "different primary 160 MHz", + .c1 = { + .width = NL80211_CHAN_WIDTH_320, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 150, + }, + .c2 = { + .width = NL80211_CHAN_WIDTH_320, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 - 10, + }, + }, + { + /* similar to previous test but one has lower BW */ + .desc = "matching primary 160 MHz", + .c1 = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 70, + }, + .c2 = { + .width = NL80211_CHAN_WIDTH_320, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 - 10, + }, + .compat = true, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(chandef_compat, chandef_compat_cases, desc) + +static void test_chandef_compat(struct kunit *test) +{ + const struct chandef_compat_case *params = test->param_value; + const struct cfg80211_chan_def *ret, *expect; + struct cfg80211_chan_def c1 = params->c1; + + /* tests with identical ones */ + if (!params->c1.chan) + c1 = params->c2; + + KUNIT_EXPECT_EQ(test, cfg80211_chandef_valid(&c1), true); + KUNIT_EXPECT_EQ(test, cfg80211_chandef_valid(¶ms->c2), true); + + expect = params->compat ? ¶ms->c2 : NULL; + + ret = cfg80211_chandef_compatible(&c1, ¶ms->c2); + KUNIT_EXPECT_PTR_EQ(test, ret, expect); + + if (!params->c1.chan) + expect = &c1; + + ret = cfg80211_chandef_compatible(¶ms->c2, &c1); + KUNIT_EXPECT_PTR_EQ(test, ret, expect); +} + +static struct kunit_case chandef_compat_test_cases[] = { + KUNIT_CASE_PARAM(test_chandef_compat, chandef_compat_gen_params), + {} +}; + +static struct kunit_suite chandef_compat = { + .name = "cfg80211-chandef-compat", + .test_cases = chandef_compat_test_cases, +}; + +kunit_test_suite(chandef_compat); From 8616f27b3fb0d47be053d7bebc9539171a514917 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:48 +0100 Subject: [PATCH 245/378] wifi: mac80211: use cfg80211_chandef_primary_freq() Instead of calculating the new primary 40/80/160 MHz center frequency here, use the new helper function from cfg80211. Link: https://msgid.link/20240129194108.eb59d6433d18.I74b745f0d1a32e779fb25d50c56407be7c35b840@changeid Signed-off-by: Johannes Berg --- net/mac80211/util.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 49eef33b5e70..63a88169d53d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -4365,8 +4365,9 @@ EXPORT_SYMBOL(ieee80211_radar_detected); void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, struct ieee80211_conn_settings *conn) { + /* no-HT indicates nothing to do */ + enum nl80211_chan_width new_primary_width = NL80211_CHAN_WIDTH_20_NOHT; struct ieee80211_conn_settings _ignored = {}; - int tmp; /* allow passing NULL if caller doesn't care */ if (!conn) @@ -4390,12 +4391,7 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; break; case NL80211_CHAN_WIDTH_80: - tmp = (30 + c->chan->center_freq - c->center_freq1)/20; - /* n_P40 */ - tmp /= 2; - /* freq_P40 */ - c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; - c->width = NL80211_CHAN_WIDTH_40; + new_primary_width = NL80211_CHAN_WIDTH_40; if (conn->mode == IEEE80211_CONN_MODE_VHT) conn->mode = IEEE80211_CONN_MODE_HT; conn->bw_limit = IEEE80211_CONN_BW_LIMIT_40; @@ -4406,21 +4402,11 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80; break; case NL80211_CHAN_WIDTH_160: - /* n_P20 */ - tmp = (70 + c->chan->center_freq - c->center_freq1)/20; - /* n_P80 */ - tmp /= 4; - c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; - c->width = NL80211_CHAN_WIDTH_80; + new_primary_width = NL80211_CHAN_WIDTH_80; conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80; break; case NL80211_CHAN_WIDTH_320: - /* n_P20 */ - tmp = (150 + c->chan->center_freq - c->center_freq1) / 20; - /* n_P160 */ - tmp /= 8; - c->center_freq1 = c->center_freq1 - 80 + 160 * tmp; - c->width = NL80211_CHAN_WIDTH_160; + new_primary_width = NL80211_CHAN_WIDTH_160; conn->bw_limit = IEEE80211_CONN_BW_LIMIT_160; break; case NL80211_CHAN_WIDTH_1: @@ -4442,6 +4428,12 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, break; } + if (new_primary_width != NL80211_CHAN_WIDTH_20_NOHT) { + c->center_freq1 = + cfg80211_chandef_primary_freq(c, new_primary_width); + c->width = new_primary_width; + } + WARN_ON_ONCE(!cfg80211_chandef_valid(c)); } From b82730bf57b54803ab94abbfd8c4422a7081886d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 19:34:49 +0100 Subject: [PATCH 246/378] wifi: cfg80211/mac80211: move puncturing into chandef Aloka originally suggested that puncturing should be part of the chandef, so that it's treated correctly. At the time, I disagreed and it ended up not part of the chandef, but I've now realized that this was wrong. Even for clients, the RX, and perhaps more importantly, CCA configuration needs to take puncturing into account. Move puncturing into the chandef, and adjust all the code accordingly. Also add a few tests for puncturing in chandef compatibility checking. Link: https://lore.kernel.org/linux-wireless/20220214223051.3610-1-quic_alokad@quicinc.com/ Suggested-by: Aloka Dixit Link: https://msgid.link/20240129194108.307183a5d2e5.I4d7fe2f126b2366c1312010e2900dfb2abffa0f6@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath12k/mac.c | 10 +- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/link.c | 16 +- .../wireless/intel/iwlwifi/mvm/mld-mac80211.c | 6 +- drivers/net/wireless/marvell/mwifiex/11h.c | 2 +- .../net/wireless/quantenna/qtnfmac/event.c | 2 +- drivers/net/wireless/realtek/rtw89/fw.c | 5 +- include/net/cfg80211.h | 44 ++--- include/net/mac80211.h | 11 +- net/mac80211/cfg.c | 21 +- net/mac80211/chan.c | 10 +- net/mac80211/link.c | 2 +- net/mac80211/mlme.c | 180 +++--------------- net/mac80211/util.c | 26 ++- net/wireless/chan.c | 62 +++--- net/wireless/nl80211.c | 67 +++---- net/wireless/tests/chan.c | 48 ++++- net/wireless/trace.h | 38 ++-- 18 files changed, 227 insertions(+), 325 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index a27480a69b27..18fb42c045cc 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -2784,9 +2784,6 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, } ath12k_mac_fils_discovery(arvif, info); - - if (changed & BSS_CHANGED_EHT_PUNCTURING) - arvif->punct_bitmap = info->eht_puncturing; } static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, @@ -6373,6 +6370,8 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, if (WARN_ON(!arvif->is_started)) continue; + arvif->punct_bitmap = vifs[i].new_ctx->def.punctured; + /* Firmware expect vdev_restart only if vdev is up. * If vdev is down then it expect vdev_stop->vdev_start. */ @@ -6473,7 +6472,8 @@ static void ath12k_mac_op_change_chanctx(struct ieee80211_hw *hw, goto unlock; if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH || - changed & IEEE80211_CHANCTX_CHANGE_RADAR) + changed & IEEE80211_CHANCTX_CHANGE_RADAR || + changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING) ath12k_mac_update_active_vif_chan(ar, ctx); /* TODO: Recalc radar detection */ @@ -6536,7 +6536,7 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, "mac chanctx assign ptr %pK vdev_id %i\n", ctx, arvif->vdev_id); - arvif->punct_bitmap = link_conf->eht_puncturing; + arvif->punct_bitmap = ctx->def.punctured; /* for some targets bss peer must be created before vdev_start */ if (ab->hw_params->vdev_start_delay && diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index e37db4af33de..61b2e3f15f0e 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1119,7 +1119,7 @@ void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT); wiphy_lock(vif->ar->wiphy); - cfg80211_ch_switch_notify(vif->ndev, &chandef, 0, 0); + cfg80211_ch_switch_notify(vif->ndev, &chandef, 0); wiphy_unlock(vif->ar->wiphy); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/link.c b/drivers/net/wireless/intel/iwlwifi/mvm/link.c index f3fcef9034ef..129eefcc45d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/link.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022 - 2023 Intel Corporation + * Copyright (C) 2022 - 2024 Intel Corporation */ #include "mvm.h" #include "time-event.h" @@ -195,12 +195,20 @@ int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, } if (changes & LINK_CONTEXT_MODIFY_EHT_PARAMS) { + struct ieee80211_chanctx_conf *ctx; + struct cfg80211_chan_def *def = NULL; + + rcu_read_lock(); + ctx = rcu_dereference(link_conf->chanctx_conf); + if (ctx) + def = iwl_mvm_chanctx_def(mvm, ctx); + if (iwlwifi_mod_params.disable_11be || - !link_conf->eht_support) + !link_conf->eht_support || !def) changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS; else - cmd.puncture_mask = - cpu_to_le16(link_conf->eht_puncturing); + cmd.puncture_mask = cpu_to_le16(def->punctured); + rcu_read_unlock(); } cmd.bss_color = link_conf->he_bss_color.color; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index b633a2a09c32..7d89f50fbb10 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022-2023 Intel Corporation + * Copyright (C) 2022-2024 Intel Corporation */ #include "mvm.h" @@ -752,8 +752,8 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; } - /* Update EHT Puncturing info */ - if (changes & BSS_CHANGED_EHT_PUNCTURING && vif->cfg.assoc) + /* if associated, maybe puncturing changed - we'll check later */ + if (vif->cfg.assoc) link_changes |= LINK_CONTEXT_MODIFY_EHT_PARAMS; if (link_changes) { diff --git a/drivers/net/wireless/marvell/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c index da211372a481..b90f922f1cdc 100644 --- a/drivers/net/wireless/marvell/mwifiex/11h.c +++ b/drivers/net/wireless/marvell/mwifiex/11h.c @@ -288,6 +288,6 @@ void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) mwifiex_dbg(priv->adapter, MSG, "indicating channel switch completion to kernel\n"); wiphy_lock(priv->wdev.wiphy); - cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef, 0, 0); + cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef, 0); wiphy_unlock(priv->wdev.wiphy); } diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c index 3b283e93a13e..76b07db284f8 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/event.c +++ b/drivers/net/wireless/quantenna/qtnfmac/event.c @@ -478,7 +478,7 @@ qtnf_event_handle_freq_change(struct qtnf_wmac *mac, continue; wiphy_lock(priv_to_wiphy(vif->mac)); - cfg80211_ch_switch_notify(vif->netdev, &chandef, 0, 0); + cfg80211_ch_switch_notify(vif->netdev, &chandef, 0); wiphy_unlock(priv_to_wiphy(vif->mac)); } diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 51072a2dcf10..540ea16f048e 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2804,8 +2804,11 @@ int rtw89_fw_h2c_assoc_cmac_tbl_g7(struct rtw89_dev *rtwdev, } if (vif->bss_conf.eht_support) { - h2c->w4 |= le32_encode_bits(~vif->bss_conf.eht_puncturing, + u16 punct = vif->bss_conf.chanreq.oper.punctured; + + h2c->w4 |= le32_encode_bits(~punct, CCTLINFO_G7_W4_ACT_SUBCH_CBW); + rcu_read_unlock(); h2c->m4 |= cpu_to_le32(CCTLINFO_G7_W4_ACT_SUBCH_CBW); } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fc2ad80118e8..cb5e34d640cd 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -7,7 +7,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021, 2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #include @@ -808,6 +808,9 @@ struct key_params { * chan will define the primary channel and all other * parameters are ignored. * @freq1_offset: offset from @center_freq1, in KHz + * @punctured: mask of the punctured 20 MHz subchannels, with + * bits turned on being disabled (punctured); numbered + * from lower to higher frequency (like in the spec) */ struct cfg80211_chan_def { struct ieee80211_channel *chan; @@ -816,6 +819,7 @@ struct cfg80211_chan_def { u32 center_freq2; struct ieee80211_edmg edmg; u16 freq1_offset; + u16 punctured; }; /* @@ -956,7 +960,8 @@ cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1, chandef1->width == chandef2->width && chandef1->center_freq1 == chandef2->center_freq1 && chandef1->freq1_offset == chandef2->freq1_offset && - chandef1->center_freq2 == chandef2->center_freq2); + chandef1->center_freq2 == chandef2->center_freq2 && + chandef1->punctured == chandef2->punctured); } /** @@ -1051,12 +1056,15 @@ cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, * cfg80211_chandef_primary_freq - calculate primary 40/80/160 MHz freq * @chandef: chandef to calculate for * @primary_chan_width: primary channel width to calculate center for + * @punctured: punctured sub-channel bitmap, will be recalculated + * according to the new bandwidth, can be %NULL * * Returns: the primary 40/80/160 MHz channel center frequency, or -1 - * for errors + * for errors, updating the punctured bitmap */ -int cfg80211_chandef_primary_freq(const struct cfg80211_chan_def *chandef, - enum nl80211_chan_width primary_chan_width); +int cfg80211_chandef_primary(const struct cfg80211_chan_def *chandef, + enum nl80211_chan_width primary_chan_width, + u16 *punctured); /** * nl80211_send_chandef - sends the channel definition. @@ -1468,9 +1476,6 @@ struct cfg80211_unsol_bcast_probe_resp { * @fils_discovery: FILS discovery transmission parameters * @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters * @mbssid_config: AP settings for multiple bssid - * @punct_bitmap: Preamble puncturing bitmap. Each bit represents - * a 20 MHz channel, lowest bit corresponding to the lowest channel. - * Bit set to 1 indicates that the channel is punctured. */ struct cfg80211_ap_settings { struct cfg80211_chan_def chandef; @@ -1505,7 +1510,6 @@ struct cfg80211_ap_settings { struct cfg80211_fils_discovery fils_discovery; struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp; struct cfg80211_mbssid_config mbssid_config; - u16 punct_bitmap; }; @@ -1539,9 +1543,6 @@ struct cfg80211_ap_update { * @radar_required: whether radar detection is required on the new channel * @block_tx: whether transmissions should be blocked while changing * @count: number of beacons until switch - * @punct_bitmap: Preamble puncturing bitmap. Each bit represents - * a 20 MHz channel, lowest bit corresponding to the lowest channel. - * Bit set to 1 indicates that the channel is punctured. */ struct cfg80211_csa_settings { struct cfg80211_chan_def chandef; @@ -1554,7 +1555,6 @@ struct cfg80211_csa_settings { bool radar_required; bool block_tx; u8 count; - u16 punct_bitmap; }; /** @@ -8738,14 +8738,13 @@ bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy, * @dev: the device which switched channels * @chandef: the new channel definition * @link_id: the link ID for MLO, must be 0 for non-MLO - * @punct_bitmap: the new puncturing bitmap * * Caller must hold wiphy mutex, therefore must only be called from sleepable * driver context! */ void cfg80211_ch_switch_notify(struct net_device *dev, struct cfg80211_chan_def *chandef, - unsigned int link_id, u16 punct_bitmap); + unsigned int link_id); /* * cfg80211_ch_switch_started_notify - notify channel switch start @@ -8754,7 +8753,6 @@ void cfg80211_ch_switch_notify(struct net_device *dev, * @link_id: the link ID for MLO, must be 0 for non-MLO * @count: the number of TBTTs until the channel switch happens * @quiet: whether or not immediate quiet was requested by the AP - * @punct_bitmap: the future puncturing bitmap * * Inform the userspace about the channel switch that has just * started, so that it can take appropriate actions (eg. starting @@ -8763,7 +8761,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, void cfg80211_ch_switch_started_notify(struct net_device *dev, struct cfg80211_chan_def *chandef, unsigned int link_id, u8 count, - bool quiet, u16 punct_bitmap); + bool quiet); /** * ieee80211_operating_class_to_band - convert operating class to band @@ -9381,18 +9379,6 @@ static inline int cfg80211_color_change_notify(struct net_device *dev) 0, 0); } -/** - * cfg80211_valid_disable_subchannel_bitmap - validate puncturing bitmap - * @bitmap: bitmap to be validated - * @chandef: channel definition - * - * Validate the puncturing bitmap. - * - * Return: %true if the bitmap is valid. %false otherwise. - */ -bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap, - const struct cfg80211_chan_def *chandef); - /** * cfg80211_links_removed - Notify about removed STA MLD setup links. * @dev: network device. diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ab6bc89d3394..54aa4a06c878 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -7,7 +7,7 @@ * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2023 Intel Corporation + * Copyright (C) 2018 - 2024 Intel Corporation */ #ifndef MAC80211_H @@ -216,6 +216,8 @@ struct ieee80211_low_level_stats { * @IEEE80211_CHANCTX_CHANGE_MIN_WIDTH: The min required channel width changed * @IEEE80211_CHANCTX_CHANGE_AP: The AP channel definition changed, so (wider * bandwidth) OFDMA settings need to be changed + * @IEEE80211_CHANCTX_CHANGE_PUNCTURING: The punctured channel(s) bitmap + * was changed. */ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0), @@ -224,6 +226,7 @@ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(3), IEEE80211_CHANCTX_CHANGE_MIN_WIDTH = BIT(4), IEEE80211_CHANCTX_CHANGE_AP = BIT(5), + IEEE80211_CHANCTX_CHANGE_PUNCTURING = BIT(6), }; /** @@ -357,7 +360,6 @@ struct ieee80211_vif_chanctx_switch { * @BSS_CHANGED_FILS_DISCOVERY: FILS discovery status changed. * @BSS_CHANGED_UNSOL_BCAST_PROBE_RESP: Unsolicited broadcast probe response * status changed. - * @BSS_CHANGED_EHT_PUNCTURING: The channel puncturing bitmap changed. * @BSS_CHANGED_MLD_VALID_LINKS: MLD valid links status changed. * @BSS_CHANGED_MLD_TTLM: TID to link mapping was changed */ @@ -394,7 +396,6 @@ enum ieee80211_bss_change { BSS_CHANGED_HE_BSS_COLOR = 1<<29, BSS_CHANGED_FILS_DISCOVERY = 1<<30, BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = 1<<31, - BSS_CHANGED_EHT_PUNCTURING = BIT_ULL(32), BSS_CHANGED_MLD_VALID_LINKS = BIT_ULL(33), BSS_CHANGED_MLD_TTLM = BIT_ULL(34), @@ -661,9 +662,7 @@ struct ieee80211_fils_discovery { * @tx_pwr_env_num: number of @tx_pwr_env. * @pwr_reduction: power constraint of BSS. * @eht_support: does this BSS support EHT - * @eht_puncturing: bitmap to indicate which channels are punctured in this BSS * @csa_active: marks whether a channel switch is going on. - * @csa_punct_bitmap: new puncturing bitmap for channel switch * @mu_mimo_owner: indicates interface owns MU-MIMO capability * @chanctx_conf: The channel context this interface is assigned to, or %NULL * when it is not assigned. This pointer is RCU-protected due to the TX @@ -766,10 +765,8 @@ struct ieee80211_bss_conf { u8 tx_pwr_env_num; u8 pwr_reduction; bool eht_support; - u16 eht_puncturing; bool csa_active; - u16 csa_punct_bitmap; bool mu_mimo_owner; struct ieee80211_chanctx_conf __rcu *chanctx_conf; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5aa02b0872d9..2ddaf0037952 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1343,8 +1343,6 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, return -EOPNOTSUPP; link_conf->eht_support = true; - link_conf->eht_puncturing = params->punct_bitmap; - changed |= BSS_CHANGED_EHT_PUNCTURING; link_conf->eht_su_beamformer = params->eht_cap->fixed.phy_cap_info[0] & @@ -3668,12 +3666,6 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) if (err) return err; - if (sdata->vif.bss_conf.eht_puncturing != sdata->vif.bss_conf.csa_punct_bitmap) { - sdata->vif.bss_conf.eht_puncturing = - sdata->vif.bss_conf.csa_punct_bitmap; - changed |= BSS_CHANGED_EHT_PUNCTURING; - } - ieee80211_link_info_change_notify(sdata, link_data, changed); if (link_data->csa_block_tx) { @@ -3687,8 +3679,7 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) return err; cfg80211_ch_switch_notify(sdata->dev, &link_data->csa_chanreq.oper, - link_data->link_id, - link_data->conf->eht_puncturing); + link_data->link_id); return 0; } @@ -3889,6 +3880,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, &sdata->vif.bss_conf.chanreq.oper)) return -EINVAL; + if (chanreq.oper.punctured && !sdata->vif.bss_conf.eht_support) + return -EINVAL; + /* don't allow another channel switch if one is already active. */ if (sdata->vif.bss_conf.csa_active) return -EBUSY; @@ -3941,13 +3935,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, goto out; } - if (params->punct_bitmap && !sdata->vif.bss_conf.eht_support) - goto out; - sdata->deflink.csa_chanreq = chanreq; sdata->deflink.csa_block_tx = params->block_tx; sdata->vif.bss_conf.csa_active = true; - sdata->vif.bss_conf.csa_punct_bitmap = params->punct_bitmap; if (sdata->deflink.csa_block_tx) ieee80211_stop_vif_queues(local, sdata, @@ -3955,8 +3945,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, cfg80211_ch_switch_started_notify(sdata->dev, &sdata->deflink.csa_chanreq.oper, 0, - params->count, params->block_tx, - sdata->vif.bss_conf.csa_punct_bitmap); + params->count, params->block_tx); if (changed) { ieee80211_link_info_change_notify(sdata, &sdata->deflink, diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index fe1489ba58c6..38acdc458c7c 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * mac80211 - channel management - * Copyright 2020 - 2022 Intel Corporation + * Copyright 2020 - 2024 Intel Corporation */ #include @@ -517,8 +517,12 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, ieee80211_remove_wbrf(local, &ctx->conf.def); - if (!cfg80211_chandef_identical(&ctx->conf.def, &chanreq->oper)) - changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; + if (!cfg80211_chandef_identical(&ctx->conf.def, &chanreq->oper)) { + if (ctx->conf.def.width != chanreq->oper.width) + changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; + if (ctx->conf.def.punctured != chanreq->oper.punctured) + changed |= IEEE80211_CHANCTX_CHANGE_PUNCTURING; + } if (!cfg80211_chandef_identical(&ctx->conf.ap, &chanreq->ap)) changed |= IEEE80211_CHANCTX_CHANGE_AP; ctx->conf.def = *chandef; diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 2231eb38a211..4f19d6479bef 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -2,7 +2,7 @@ /* * MLO link handling * - * Copyright (C) 2022-2023 Intel Corporation + * Copyright (C) 2022-2024 Intel Corporation */ #include #include diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 74a15f18e7ee..4eaf5c10efdb 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -93,83 +93,6 @@ MODULE_PARM_DESC(probe_wait_ms, */ #define IEEE80211_SIGNAL_AVE_MIN_COUNT 4 -/* - * Extract from the given disabled subchannel bitmap (raw format - * from the EHT Operation Element) the bits for the subchannel - * we're using right now. - */ -static u16 -ieee80211_extract_dis_subch_bmap(const struct ieee80211_eht_operation *eht_oper, - struct cfg80211_chan_def *chandef, u16 bitmap) -{ - struct ieee80211_eht_operation_info *info = (void *)eht_oper->optional; - struct cfg80211_chan_def ap_chandef = *chandef; - u32 ap_center_freq, local_center_freq; - u32 ap_bw, local_bw; - int ap_start_freq, local_start_freq; - u16 shift, mask; - - if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) || - !(eht_oper->params & - IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) - return 0; - - /* set 160/320 supported to get the full AP definition */ - ieee80211_chandef_eht_oper((const void *)eht_oper->optional, - &ap_chandef); - ap_center_freq = ap_chandef.center_freq1; - ap_bw = 20 * BIT(u8_get_bits(info->control, - IEEE80211_EHT_OPER_CHAN_WIDTH)); - ap_start_freq = ap_center_freq - ap_bw / 2; - local_center_freq = chandef->center_freq1; - local_bw = 20 * BIT(ieee80211_chan_width_to_rx_bw(chandef->width)); - local_start_freq = local_center_freq - local_bw / 2; - shift = (local_start_freq - ap_start_freq) / 20; - mask = BIT(local_bw / 20) - 1; - - return (bitmap >> shift) & mask; -} - -/* - * Handle the puncturing bitmap, possibly downgrading bandwidth to get a - * valid bitmap. - */ -static void -ieee80211_handle_puncturing_bitmap(struct ieee80211_link_data *link, - const struct ieee80211_eht_operation *eht_oper, - u16 bitmap, u64 *changed) -{ - struct cfg80211_chan_def *chandef = &link->conf->chanreq.oper; - struct ieee80211_local *local = link->sdata->local; - u16 extracted; - u64 _changed = 0; - - if (!changed) - changed = &_changed; - - while (chandef->width > NL80211_CHAN_WIDTH_40) { - extracted = - ieee80211_extract_dis_subch_bmap(eht_oper, chandef, - bitmap); - - if (cfg80211_valid_disable_subchannel_bitmap(&bitmap, - chandef) && - !(bitmap && ieee80211_hw_check(&local->hw, - DISALLOW_PUNCTURING))) - break; - ieee80211_chandef_downgrade(chandef, &link->u.mgd.conn); - *changed |= BSS_CHANGED_BANDWIDTH; - } - - if (chandef->width <= NL80211_CHAN_WIDTH_40) - extracted = 0; - - if (link->conf->eht_puncturing != extracted) { - link->conf->eht_puncturing = extracted; - *changed |= BSS_CHANGED_EHT_PUNCTURING; - } -} - /* * We can have multiple work items (and connection probing) * scheduling this timer, but we need to take care to only @@ -396,6 +319,9 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, ieee80211_chandef_eht_oper((const void *)eht_oper->optional, &eht_chandef); + eht_chandef.punctured = + ieee80211_eht_oper_dis_subchan_bitmap(eht_oper); + if (!cfg80211_chandef_valid(&eht_chandef)) { sdata_info(sdata, "AP EHT information is invalid, disabling EHT\n"); @@ -661,13 +587,27 @@ ieee80211_verify_sta_eht_mcs_support(struct ieee80211_sub_if_data *sdata, return true; } +static bool ieee80211_chandef_usable(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + u32 prohibited_flags) +{ + if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, + chandef, prohibited_flags)) + return false; + + if (chandef->punctured && + ieee80211_hw_check(&sdata->local->hw, DISALLOW_PUNCTURING)) + return false; + + return true; +} + static struct ieee802_11_elems * ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, struct ieee80211_conn_settings *conn, struct cfg80211_bss *cbss, int link_id, struct ieee80211_chan_req *chanreq) { - struct ieee80211_local *local = sdata->local; const struct cfg80211_bss_ies *ies = rcu_dereference(cbss->ies); struct ieee80211_bss *bss = (void *)cbss->priv; struct ieee80211_channel *channel = cbss->channel; @@ -761,8 +701,8 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, else chanreq->ap.chan = NULL; - while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &chanreq->oper, - IEEE80211_CHAN_DISABLED)) { + while (!ieee80211_chandef_usable(sdata, &chanreq->oper, + IEEE80211_CHAN_DISABLED)) { if (WARN_ON(chanreq->oper.width == NL80211_CHAN_WIDTH_20_NOHT)) { ret = -EINVAL; goto free; @@ -812,30 +752,6 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, sdata_info(sdata, "required MCSes not supported, disabling EHT\n"); } - if (conn->mode >= IEEE80211_CONN_MODE_EHT) { - u16 bitmap; - - if (WARN_ON_ONCE(!elems->eht_operation)) { - ret = -EINVAL; - goto free; - } - - bitmap = ieee80211_eht_oper_dis_subchan_bitmap(elems->eht_operation); - - if (!cfg80211_valid_disable_subchannel_bitmap(&bitmap, - &ap_chandef) || - (bitmap && - ieee80211_hw_check(&local->hw, DISALLOW_PUNCTURING))) { - conn->mode = IEEE80211_CONN_MODE_HE; - conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, - conn->bw_limit, - IEEE80211_CONN_BW_LIMIT_160); - sdata_info(sdata, - "AP has invalid/unsupported puncturing, disabling EHT\n"); - } - /* FIXME: store puncturing bitmap */ - } - /* the mode can only decrease, so this must terminate */ if (ap_mode != conn->mode) goto again; @@ -2127,7 +2043,7 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) } cfg80211_ch_switch_notify(sdata->dev, &link->reserved.oper, - link->link_id, 0); + link->link_id); } void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success, @@ -2330,7 +2246,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chanreq.oper, link->link_id, csa_ie.count, - csa_ie.mode, 0); + csa_ie.mode); if (local->ops->channel_switch) { /* use driver's channel switch callback */ @@ -3394,8 +3310,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sdata->deflink.u.mgd.tracking_signal_avg = false; sdata->deflink.u.mgd.disable_wmm_tracking = false; - sdata->vif.bss_conf.eht_puncturing = 0; - ifmgd->flags = 0; for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { @@ -4625,7 +4539,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, link_sta); bss_conf->eht_support = link_sta->pub->eht_cap.has_eht; - *changed |= BSS_CHANGED_EHT_PUNCTURING; } else { bss_conf->eht_support = false; } @@ -5865,40 +5778,6 @@ static bool ieee80211_rx_our_beacon(const u8 *tx_bssid, return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid); } -static bool ieee80211_config_puncturing(struct ieee80211_link_data *link, - const struct ieee80211_eht_operation *eht_oper, - u64 *changed) -{ - struct ieee80211_local *local = link->sdata->local; - u16 bitmap, extracted; - - bitmap = ieee80211_eht_oper_dis_subchan_bitmap(eht_oper); - extracted = ieee80211_extract_dis_subch_bmap(eht_oper, - &link->conf->chanreq.oper, - bitmap); - - /* accept if there are no changes */ - if (!(*changed & BSS_CHANGED_BANDWIDTH) && - extracted == link->conf->eht_puncturing) - return true; - - if (!cfg80211_valid_disable_subchannel_bitmap(&bitmap, - &link->conf->chanreq.oper)) { - link_info(link, - "Got an invalid disable subchannel bitmap from AP %pM: bitmap = 0x%x, bw = 0x%x. disconnect\n", - link->u.mgd.bssid, - bitmap, - link->conf->chanreq.oper.width); - return false; - } - - if (bitmap && ieee80211_hw_check(&local->hw, DISALLOW_PUNCTURING)) - return false; - - ieee80211_handle_puncturing_bitmap(link, eht_oper, bitmap, changed); - return true; -} - static void ieee80211_ml_reconf_work(struct wiphy *wiphy, struct wiphy_work *work) { @@ -6614,21 +6493,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, elems->pwr_constr_elem, elems->cisco_dtpc_elem); - if (elems->eht_operation && - link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_EHT) { - if (!ieee80211_config_puncturing(link, elems->eht_operation, - &changed)) { - ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, - WLAN_REASON_DEAUTH_LEAVING, - true, deauth_buf); - ieee80211_report_disconnect(sdata, deauth_buf, - sizeof(deauth_buf), true, - WLAN_REASON_DEAUTH_LEAVING, - false); - goto free; - } - } - ieee80211_ml_reconfiguration(sdata, elems); ieee80211_process_adv_ttlm(sdata, elems, le64_to_cpu(mgmt->u.beacon.timestamp)); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 63a88169d53d..5108dbaa9360 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -6,7 +6,7 @@ * Copyright 2007 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation * * utilities for mac80211 */ @@ -2810,9 +2810,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) sdata->vif.bss_conf.protected_keep_alive) changed |= BSS_CHANGED_KEEP_ALIVE; - if (sdata->vif.bss_conf.eht_puncturing) - changed |= BSS_CHANGED_EHT_PUNCTURING; - ieee80211_bss_info_change_notify(sdata, changed); } else if (!WARN_ON(!link)) { @@ -4365,14 +4362,17 @@ EXPORT_SYMBOL(ieee80211_radar_detected); void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, struct ieee80211_conn_settings *conn) { - /* no-HT indicates nothing to do */ - enum nl80211_chan_width new_primary_width = NL80211_CHAN_WIDTH_20_NOHT; + enum nl80211_chan_width new_primary_width; struct ieee80211_conn_settings _ignored = {}; /* allow passing NULL if caller doesn't care */ if (!conn) conn = &_ignored; +again: + /* no-HT indicates nothing to do */ + new_primary_width = NL80211_CHAN_WIDTH_20_NOHT; + switch (c->width) { default: case NL80211_CHAN_WIDTH_20_NOHT: @@ -4382,6 +4382,7 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, c->width = NL80211_CHAN_WIDTH_20_NOHT; conn->mode = IEEE80211_CONN_MODE_LEGACY; conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; + c->punctured = 0; break; case NL80211_CHAN_WIDTH_40: c->width = NL80211_CHAN_WIDTH_20; @@ -4389,6 +4390,7 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, if (conn->mode == IEEE80211_CONN_MODE_VHT) conn->mode = IEEE80211_CONN_MODE_HT; conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; + c->punctured = 0; break; case NL80211_CHAN_WIDTH_80: new_primary_width = NL80211_CHAN_WIDTH_40; @@ -4429,11 +4431,19 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, } if (new_primary_width != NL80211_CHAN_WIDTH_20_NOHT) { - c->center_freq1 = - cfg80211_chandef_primary_freq(c, new_primary_width); + c->center_freq1 = cfg80211_chandef_primary(c, new_primary_width, + &c->punctured); c->width = new_primary_width; } + /* + * With an 80 MHz channel, we might have the puncturing in the primary + * 40 Mhz channel, but that's not valid when downgraded to 40 MHz width. + * In that case, downgrade again. + */ + if (!cfg80211_chandef_valid(c) && c->punctured) + goto again; + WARN_ON_ONCE(!cfg80211_chandef_valid(c)); } diff --git a/net/wireless/chan.c b/net/wireless/chan.c index bfa2ea935fc2..e2ce89afa9ff 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -6,7 +6,7 @@ * * Copyright 2009 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright 2018-2023 Intel Corporation + * Copyright 2018-2024 Intel Corporation */ #include @@ -27,11 +27,10 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, if (WARN_ON(!chan)) return; - chandef->chan = chan; - chandef->freq1_offset = chan->freq_offset; - chandef->center_freq2 = 0; - chandef->edmg.bw_config = 0; - chandef->edmg.channels = 0; + *chandef = (struct cfg80211_chan_def) { + .chan = chan, + .freq1_offset = chan->freq_offset, + }; switch (chan_type) { case NL80211_CHAN_NO_HT: @@ -87,10 +86,9 @@ static const struct cfg80211_per_bw_puncturing_values per_bw_puncturing[] = { CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320) }; -bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap, - const struct cfg80211_chan_def *chandef) +static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef) { - u32 idx, i, start_freq; + u32 idx, i, start_freq, primary_center = chandef->chan->center_freq; switch (chandef->width) { case NL80211_CHAN_WIDTH_80: @@ -106,24 +104,23 @@ bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap, start_freq = chandef->center_freq1 - 160; break; default: - *bitmap = 0; - break; + return chandef->punctured == 0; } - if (!*bitmap) + if (!chandef->punctured) return true; /* check if primary channel is punctured */ - if (*bitmap & (u16)BIT((chandef->chan->center_freq - start_freq) / 20)) + if (chandef->punctured & (u16)BIT((primary_center - start_freq) / 20)) return false; - for (i = 0; i < per_bw_puncturing[idx].len; i++) - if (per_bw_puncturing[idx].valid_values[i] == *bitmap) + for (i = 0; i < per_bw_puncturing[idx].len; i++) { + if (per_bw_puncturing[idx].valid_values[i] == chandef->punctured) return true; + } return false; } -EXPORT_SYMBOL(cfg80211_valid_disable_subchannel_bitmap); static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef) { @@ -386,17 +383,19 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) !cfg80211_edmg_chandef_valid(chandef)) return false; - return true; + return valid_puncturing_bitmap(chandef); } EXPORT_SYMBOL(cfg80211_chandef_valid); -int cfg80211_chandef_primary_freq(const struct cfg80211_chan_def *c, - enum nl80211_chan_width primary_chan_width) +int cfg80211_chandef_primary(const struct cfg80211_chan_def *c, + enum nl80211_chan_width primary_chan_width, + u16 *punctured) { int pri_width = nl80211_chan_width_to_mhz(primary_chan_width); int width = cfg80211_chandef_get_width(c); u32 control = c->chan->center_freq; u32 center = c->center_freq1; + u16 _punct = 0; if (WARN_ON_ONCE(pri_width < 0 || width < 0)) return -1; @@ -405,26 +404,41 @@ int cfg80211_chandef_primary_freq(const struct cfg80211_chan_def *c, if (WARN_ON_ONCE(pri_width > width)) return -1; + if (!punctured) + punctured = &_punct; + + *punctured = c->punctured; + while (width > pri_width) { - if (control > center) + unsigned int bits_to_drop = width / 20 / 2; + + if (control > center) { center += width / 4; - else + *punctured >>= bits_to_drop; + } else { center -= width / 4; + *punctured &= (1 << bits_to_drop) - 1; + } width /= 2; } return center; } -EXPORT_SYMBOL(cfg80211_chandef_primary_freq); +EXPORT_SYMBOL(cfg80211_chandef_primary); static const struct cfg80211_chan_def * check_chandef_primary_compat(const struct cfg80211_chan_def *c1, const struct cfg80211_chan_def *c2, enum nl80211_chan_width primary_chan_width) { + u16 punct_c1 = 0, punct_c2 = 0; + /* check primary is compatible -> error if not */ - if (cfg80211_chandef_primary_freq(c1, primary_chan_width) != - cfg80211_chandef_primary_freq(c2, primary_chan_width)) + if (cfg80211_chandef_primary(c1, primary_chan_width, &punct_c1) != + cfg80211_chandef_primary(c2, primary_chan_width, &punct_c2)) + return ERR_PTR(-EINVAL); + + if (punct_c1 != punct_c2) return ERR_PTR(-EINVAL); /* assumes c1 is smaller width, if that was just checked -> done */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 68c20409eca6..b533412ad1e0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3218,21 +3218,6 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) wdev->iftype == NL80211_IFTYPE_P2P_GO; } -static int nl80211_parse_punct_bitmap(struct cfg80211_registered_device *rdev, - struct genl_info *info, - const struct cfg80211_chan_def *chandef, - u16 *punct_bitmap) -{ - if (!wiphy_ext_feature_isset(&rdev->wiphy, NL80211_EXT_FEATURE_PUNCT)) - return -EINVAL; - - *punct_bitmap = nla_get_u32(info->attrs[NL80211_ATTR_PUNCT_BITMAP]); - if (!cfg80211_valid_disable_subchannel_bitmap(punct_bitmap, chandef)) - return -EINVAL; - - return 0; -} - int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, struct genl_info *info, struct cfg80211_chan_def *chandef) @@ -3340,6 +3325,19 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, chandef->edmg.channels = 0; } + if (info->attrs[NL80211_ATTR_PUNCT_BITMAP]) { + chandef->punctured = + nla_get_u32(info->attrs[NL80211_ATTR_PUNCT_BITMAP]); + + if (chandef->punctured && + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_PUNCT)) { + NL_SET_ERR_MSG(extack, + "driver doesn't support puncturing"); + return -EINVAL; + } + } + if (!cfg80211_chandef_valid(chandef)) { NL_SET_ERR_MSG(extack, "invalid channel definition"); return -EINVAL; @@ -3816,6 +3814,10 @@ int nl80211_send_chandef(struct sk_buff *msg, const struct cfg80211_chan_def *ch if (chandef->center_freq2 && nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2)) return -ENOBUFS; + if (chandef->punctured && + nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP, chandef->punctured)) + return -ENOBUFS; + return 0; } EXPORT_SYMBOL(nl80211_send_chandef); @@ -6061,14 +6063,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) goto out; } - if (info->attrs[NL80211_ATTR_PUNCT_BITMAP]) { - err = nl80211_parse_punct_bitmap(rdev, info, - ¶ms->chandef, - ¶ms->punct_bitmap); - if (err) - goto out; - } - if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms->chandef, wdev->iftype)) { err = -EINVAL; @@ -10228,14 +10222,6 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) params.block_tx = true; - if (info->attrs[NL80211_ATTR_PUNCT_BITMAP]) { - err = nl80211_parse_punct_bitmap(rdev, info, - ¶ms.chandef, - ¶ms.punct_bitmap); - if (err) - goto free; - } - err = rdev_channel_switch(rdev, dev, ¶ms); free: @@ -19356,7 +19342,7 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, struct cfg80211_chan_def *chandef, gfp_t gfp, enum nl80211_commands notif, - u8 count, bool quiet, u16 punct_bitmap) + u8 count, bool quiet) { struct wireless_dev *wdev = netdev->ieee80211_ptr; struct sk_buff *msg; @@ -19390,9 +19376,6 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, goto nla_put_failure; } - if (nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP, punct_bitmap)) - goto nla_put_failure; - genlmsg_end(msg, hdr); genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, @@ -19405,7 +19388,7 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, void cfg80211_ch_switch_notify(struct net_device *dev, struct cfg80211_chan_def *chandef, - unsigned int link_id, u16 punct_bitmap) + unsigned int link_id) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -19414,7 +19397,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, lockdep_assert_wiphy(wdev->wiphy); WARN_INVALID_LINK_ID(wdev, link_id); - trace_cfg80211_ch_switch_notify(dev, chandef, link_id, punct_bitmap); + trace_cfg80211_ch_switch_notify(dev, chandef, link_id); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: @@ -19443,15 +19426,14 @@ void cfg80211_ch_switch_notify(struct net_device *dev, cfg80211_sched_dfs_chan_update(rdev); nl80211_ch_switch_notify(rdev, dev, link_id, chandef, GFP_KERNEL, - NL80211_CMD_CH_SWITCH_NOTIFY, 0, false, - punct_bitmap); + NL80211_CMD_CH_SWITCH_NOTIFY, 0, false); } EXPORT_SYMBOL(cfg80211_ch_switch_notify); void cfg80211_ch_switch_started_notify(struct net_device *dev, struct cfg80211_chan_def *chandef, unsigned int link_id, u8 count, - bool quiet, u16 punct_bitmap) + bool quiet) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -19460,13 +19442,12 @@ void cfg80211_ch_switch_started_notify(struct net_device *dev, lockdep_assert_wiphy(wdev->wiphy); WARN_INVALID_LINK_ID(wdev, link_id); - trace_cfg80211_ch_switch_started_notify(dev, chandef, link_id, - punct_bitmap); + trace_cfg80211_ch_switch_started_notify(dev, chandef, link_id); nl80211_ch_switch_notify(rdev, dev, link_id, chandef, GFP_KERNEL, NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, - count, quiet, punct_bitmap); + count, quiet); } EXPORT_SYMBOL(cfg80211_ch_switch_started_notify); diff --git a/net/wireless/tests/chan.c b/net/wireless/tests/chan.c index b9e7a27e43cb..d02258ac2dab 100644 --- a/net/wireless/tests/chan.c +++ b/net/wireless/tests/chan.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2023 Intel Corporation + * Copyright (C) 2023-2024 Intel Corporation */ #include #include @@ -140,6 +140,52 @@ static const struct chandef_compat_case { }, .compat = true, }, + { + .desc = "matching primary 160 MHz & punctured secondary 160 Mhz", + .c1 = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 70, + }, + .c2 = { + .width = NL80211_CHAN_WIDTH_320, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 - 10, + .punctured = 0xf, + }, + .compat = true, + }, + { + .desc = "matching primary 160 MHz & punctured matching", + .c1 = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 70, + .punctured = 0xc0, + }, + .c2 = { + .width = NL80211_CHAN_WIDTH_320, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 - 10, + .punctured = 0xc000, + }, + .compat = true, + }, + { + .desc = "matching primary 160 MHz & punctured not matching", + .c1 = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 70, + .punctured = 0x80, + }, + .c2 = { + .width = NL80211_CHAN_WIDTH_320, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 - 10, + .punctured = 0xc000, + }, + }, }; KUNIT_ARRAY_PARAM_DESC(chandef_compat, chandef_compat_cases, desc) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 1f374c8a17a5..ae5e585b6863 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1,4 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Portions of this file + * Copyright(c) 2016-2017 Intel Deutschland GmbH + * Copyright (C) 2018, 2020-2024 Intel Corporation + */ #undef TRACE_SYSTEM #define TRACE_SYSTEM cfg80211 @@ -135,7 +140,8 @@ __field(u32, width) \ __field(u32, center_freq1) \ __field(u32, freq1_offset) \ - __field(u32, center_freq2) + __field(u32, center_freq2) \ + __field(u16, punctured) #define CHAN_DEF_ASSIGN(chandef) \ do { \ if ((chandef) && (chandef)->chan) { \ @@ -148,6 +154,7 @@ __entry->center_freq1 = (chandef)->center_freq1;\ __entry->freq1_offset = (chandef)->freq1_offset;\ __entry->center_freq2 = (chandef)->center_freq2;\ + __entry->punctured = (chandef)->punctured; \ } else { \ __entry->band = 0; \ __entry->control_freq = 0; \ @@ -156,14 +163,15 @@ __entry->center_freq1 = 0; \ __entry->freq1_offset = 0; \ __entry->center_freq2 = 0; \ + __entry->punctured = 0; \ } \ } while (0) #define CHAN_DEF_PR_FMT \ - "band: %d, control freq: %u.%03u, width: %d, cf1: %u.%03u, cf2: %u" + "band: %d, control freq: %u.%03u, width: %d, cf1: %u.%03u, cf2: %u, punct: 0x%x" #define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \ __entry->freq_offset, __entry->width, \ __entry->center_freq1, __entry->freq1_offset, \ - __entry->center_freq2 + __entry->center_freq2, __entry->punctured #define FILS_AAD_ASSIGN(fa) \ do { \ @@ -3267,47 +3275,39 @@ TRACE_EVENT(cfg80211_chandef_dfs_required, TRACE_EVENT(cfg80211_ch_switch_notify, TP_PROTO(struct net_device *netdev, struct cfg80211_chan_def *chandef, - unsigned int link_id, - u16 punct_bitmap), - TP_ARGS(netdev, chandef, link_id, punct_bitmap), + unsigned int link_id), + TP_ARGS(netdev, chandef, link_id), TP_STRUCT__entry( NETDEV_ENTRY CHAN_DEF_ENTRY __field(unsigned int, link_id) - __field(u16, punct_bitmap) ), TP_fast_assign( NETDEV_ASSIGN; CHAN_DEF_ASSIGN(chandef); __entry->link_id = link_id; - __entry->punct_bitmap = punct_bitmap; ), - TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT ", link:%d, punct_bitmap:%u", - NETDEV_PR_ARG, CHAN_DEF_PR_ARG, __entry->link_id, - __entry->punct_bitmap) + TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT ", link:%d", + NETDEV_PR_ARG, CHAN_DEF_PR_ARG, __entry->link_id) ); TRACE_EVENT(cfg80211_ch_switch_started_notify, TP_PROTO(struct net_device *netdev, struct cfg80211_chan_def *chandef, - unsigned int link_id, - u16 punct_bitmap), - TP_ARGS(netdev, chandef, link_id, punct_bitmap), + unsigned int link_id), + TP_ARGS(netdev, chandef, link_id), TP_STRUCT__entry( NETDEV_ENTRY CHAN_DEF_ENTRY __field(unsigned int, link_id) - __field(u16, punct_bitmap) ), TP_fast_assign( NETDEV_ASSIGN; CHAN_DEF_ASSIGN(chandef); __entry->link_id = link_id; - __entry->punct_bitmap = punct_bitmap; ), - TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT ", link:%d, punct_bitmap:%u", - NETDEV_PR_ARG, CHAN_DEF_PR_ARG, __entry->link_id, - __entry->punct_bitmap) + TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT ", link:%d", + NETDEV_PR_ARG, CHAN_DEF_PR_ARG, __entry->link_id) ); TRACE_EVENT(cfg80211_radar_event, From b1344b1399daec9aca62bd0b2ea94874f5b8e126 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:04:56 +0100 Subject: [PATCH 247/378] wifi: mac80211: add/use ieee80211_get_sn() This will also be useful for MLO duplicate multicast detection, but add it already here and use it in one place that trivially converts. Link: https://msgid.link/20240129200456.f0ff49c80006.I850d2785ab1640e56e262d3ad7343b87f6962552@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 7 ++++++- net/mac80211/rx.c | 5 ++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index d9d2c1253157..b9367d5f04c4 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -9,7 +9,7 @@ * Copyright (c) 2006, Michael Wu * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright (c) 2016 - 2017 Intel Deutschland GmbH - * Copyright (c) 2018 - 2023 Intel Corporation + * Copyright (c) 2018 - 2024 Intel Corporation */ #ifndef LINUX_IEEE80211_H @@ -808,6 +808,11 @@ static inline bool ieee80211_is_frag(struct ieee80211_hdr *hdr) hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG); } +static inline u16 ieee80211_get_sn(struct ieee80211_hdr *hdr) +{ + return le16_get_bits(hdr->seq_ctrl, IEEE80211_SCTL_SEQ); +} + struct ieee80211s_hdr { u8 flags; u8 ttl; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 76798e8057f7..53c4764dc1ed 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -6,7 +6,7 @@ * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #include @@ -1251,8 +1251,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - u16 sc = le16_to_cpu(hdr->seq_ctrl); - u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; + u16 mpdu_seq_num = ieee80211_get_sn(hdr); u16 head_seq_num, buf_size; int index; bool ret = true; From 676259100cf3a81dd2d47918b36edb237986b9df Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:04:57 +0100 Subject: [PATCH 248/378] wifi: mac80211: implement MLO multicast deduplication If the vif is an MLD then it may receive multicast from different links, and should drop those frames according to the SN. Implement that. Link: https://msgid.link/20240129200456.693b77d14b44.I491846f2bea0058c14eab6422962c10bfae9b675@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 5 +++++ net/mac80211/ieee80211_i.h | 4 +++- net/mac80211/mlme.c | 3 +++ net/mac80211/rx.c | 22 ++++++++++++++++++++-- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b9367d5f04c4..e9078143b822 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -191,6 +191,11 @@ static inline bool ieee80211_sn_less(u16 sn1, u16 sn2) return ((sn1 - sn2) & IEEE80211_SN_MASK) > (IEEE80211_SN_MODULO >> 1); } +static inline bool ieee80211_sn_less_eq(u16 sn1, u16 sn2) +{ + return ((sn2 - sn1) & IEEE80211_SN_MASK) <= (IEEE80211_SN_MODULO >> 1); +} + static inline u16 ieee80211_sn_add(u16 sn1, u16 sn2) { return (sn1 + sn2) & IEEE80211_SN_MASK; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 534cac3fc8df..46b517cf47ea 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -5,7 +5,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007-2010 Johannes Berg * Copyright 2013-2015 Intel Mobile Communications GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #ifndef IEEE80211_I_H @@ -523,6 +523,8 @@ struct ieee80211_if_managed { unsigned int flags; + u16 mcast_seq_last; + bool status_acked; bool status_received; __le16 status_fc; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4eaf5c10efdb..35dda5982854 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3351,6 +3351,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->neg_ttlm_timeout_work); ieee80211_vif_set_links(sdata, 0, 0); + + ifmgd->mcast_seq_last = IEEE80211_SN_MODULO; } static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) @@ -7512,6 +7514,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) spin_lock_init(&ifmgd->teardown_lock); ifmgd->teardown_skb = NULL; ifmgd->orig_teardown_skb = NULL; + ifmgd->mcast_seq_last = IEEE80211_SN_MODULO; } static void ieee80211_recalc_smps_work(struct wiphy *wiphy, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 53c4764dc1ed..9902ea69af0a 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1434,13 +1434,31 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (ieee80211_is_ctl(hdr->frame_control) || - ieee80211_is_any_nullfunc(hdr->frame_control) || - is_multicast_ether_addr(hdr->addr1)) + ieee80211_is_any_nullfunc(hdr->frame_control)) return RX_CONTINUE; if (!rx->sta) return RX_CONTINUE; + if (unlikely(is_multicast_ether_addr(hdr->addr1))) { + struct ieee80211_sub_if_data *sdata = rx->sdata; + u16 sn = ieee80211_get_sn(hdr); + + if (!ieee80211_is_data_present(hdr->frame_control)) + return RX_CONTINUE; + + if (!ieee80211_vif_is_mld(&sdata->vif) || + sdata->vif.type != NL80211_IFTYPE_STATION) + return RX_CONTINUE; + + if (sdata->u.mgd.mcast_seq_last != IEEE80211_SN_MODULO && + ieee80211_sn_less_eq(sn, sdata->u.mgd.mcast_seq_last)) + return RX_DROP_U_DUP; + + sdata->u.mgd.mcast_seq_last = sn; + return RX_CONTINUE; + } + if (unlikely(ieee80211_has_retry(hdr->frame_control) && rx->sta->last_seq_ctrl[rx->seqno_idx] == hdr->seq_ctrl)) { I802_DEBUG_INC(rx->local->dot11FrameDuplicateCount); From 3552a22880eed3e834a33d02aec6800974bb42b4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:06:53 +0100 Subject: [PATCH 249/378] wifi: mac80211: disambiguate element parsing errors Let the element parsing function return what kind of error was encountered, as a bitmap, even if nothing currently checks for which specific error it was, we'll use it later. Link: https://msgid.link/20240129200652.1a69f2a31ec7.I55b86561d64e7ef1504c73f6f2813c33030c8136@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 11 +++- net/mac80211/util.c | 108 ++++++++++++++++++++++++------------- 2 files changed, 81 insertions(+), 38 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 46b517cf47ea..f5fe659a1efd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1666,6 +1666,13 @@ struct ieee80211_csa_ie { u32 max_switch_time; }; +enum ieee80211_elems_parse_error { + IEEE80211_PARSE_ERR_INVALID_END = BIT(0), + IEEE80211_PARSE_ERR_DUP_ELEM = BIT(1), + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE = BIT(2), + IEEE80211_PARSE_ERR_UNEXPECTED_ELEM = BIT(3), +}; + /* Parsed Information Elements */ struct ieee802_11_elems { const u8 *ie_start; @@ -1776,8 +1783,8 @@ struct ieee802_11_elems { struct ieee80211_mle_per_sta_profile *prof; size_t sta_prof_len; - /* whether a parse error occurred while retrieving these elements */ - bool parse_error; + /* whether/which parse error occurred while retrieving these elements */ + u8 parse_error; /* * scratch buffer that can be used for various element parsing related diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 5108dbaa9360..c1fa762f0cba 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1063,7 +1063,7 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, for_each_element(elem, params->start, params->len) { const struct element *subelem; - bool elem_parse_failed; + u8 elem_parse_failed; u8 id = elem->id; u8 elen = elem->datalen; const u8 *pos = elem->data; @@ -1119,7 +1119,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, * that if the content gets bigger it might be needed more than once */ if (test_bit(id, seen_elems)) { - elems->parse_error = true; + elems->parse_error |= + IEEE80211_PARSE_ERR_DUP_ELEM; continue; } break; @@ -1128,19 +1129,21 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (calc_crc && id < 64 && (params->filter & (1ULL << id))) crc = crc32_be(crc, pos - 2, elen + 2); - elem_parse_failed = false; + elem_parse_failed = 0; switch (id) { case WLAN_EID_LINK_ID: if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } elems->lnk_id = (void *)(pos - 2); break; case WLAN_EID_CHAN_SWITCH_TIMING: if (elen < sizeof(struct ieee80211_ch_switch_timing)) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } elems->ch_sw_timing = (void *)pos; @@ -1161,14 +1164,16 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen >= 1) elems->ds_params = pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_TIM: if (elen >= sizeof(struct ieee80211_tim_ie)) { elems->tim = (void *)pos; elems->tim_len = elen; } else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_VENDOR_SPECIFIC: if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && @@ -1198,7 +1203,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen >= 1) elems->erp_info = pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_EXT_SUPP_RATES: elems->ext_supp_rates = pos; @@ -1210,7 +1216,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen >= sizeof(struct ieee80211_ht_cap)) elems->ht_cap_elem = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_HT_OPERATION: if (params->mode < IEEE80211_CONN_MODE_HT) @@ -1218,7 +1225,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen >= sizeof(struct ieee80211_ht_operation)) elems->ht_operation = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_VHT_CAPABILITY: if (params->mode < IEEE80211_CONN_MODE_VHT) @@ -1226,7 +1234,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen >= sizeof(struct ieee80211_vht_cap)) elems->vht_cap_elem = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_VHT_OPERATION: if (params->mode < IEEE80211_CONN_MODE_VHT) @@ -1237,7 +1246,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, crc = crc32_be(crc, pos - 2, elen + 2); break; } - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_OPMODE_NOTIF: if (params->mode < IEEE80211_CONN_MODE_VHT) @@ -1248,7 +1258,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, crc = crc32_be(crc, pos - 2, elen + 2); break; } - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_MESH_ID: elems->mesh_id = pos; @@ -1258,7 +1269,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen >= sizeof(struct ieee80211_meshconf_ie)) elems->mesh_config = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_PEER_MGMT: elems->peering = pos; @@ -1284,18 +1296,21 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen >= sizeof(struct ieee80211_rann_ie)) elems->rann = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_CHANNEL_SWITCH: if (elen != sizeof(struct ieee80211_channel_sw_ie)) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } elems->ch_switch_ie = (void *)pos; break; case WLAN_EID_EXT_CHANSWITCH_ANN: if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } elems->ext_chansw_ie = (void *)pos; @@ -1304,7 +1319,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (params->mode < IEEE80211_CONN_MODE_HT) break; if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } elems->sec_chan_offs = (void *)pos; @@ -1312,7 +1328,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, case WLAN_EID_CHAN_SWITCH_PARAM: if (elen < sizeof(*elems->mesh_chansw_params_ie)) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } elems->mesh_chansw_params_ie = (void *)pos; @@ -1320,9 +1337,16 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: if (params->mode < IEEE80211_CONN_MODE_VHT) break; - if (!params->action || - elen < sizeof(*elems->wide_bw_chansw_ie)) { - elem_parse_failed = true; + + if (!params->action) { + elem_parse_failed = + IEEE80211_PARSE_ERR_UNEXPECTED_ELEM; + break; + } + + if (elen < sizeof(*elems->wide_bw_chansw_ie)) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } elems->wide_bw_chansw_ie = (void *)pos; @@ -1331,7 +1355,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (params->mode < IEEE80211_CONN_MODE_VHT) break; if (params->action) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_UNEXPECTED_ELEM; break; } /* @@ -1345,7 +1370,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elems->wide_bw_chansw_ie = (void *)subelem->data; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; } if (params->mode < IEEE80211_CONN_MODE_EHT) @@ -1361,7 +1387,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, edatalen)) elems->bandwidth_indication = edata; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; } break; case WLAN_EID_COUNTRY: @@ -1370,7 +1397,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, break; case WLAN_EID_PWR_CONSTRAINT: if (elen != 1) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } elems->pwr_constr_elem = pos; @@ -1382,7 +1410,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, * tag (0x00). */ if (elen < 4) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } @@ -1391,7 +1420,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, break; if (elen != 6) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } @@ -1402,7 +1432,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, break; case WLAN_EID_ADDBA_EXT: if (elen < sizeof(struct ieee80211_addba_ext_ie)) { - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; } elems->addba_ext_ie = (void *)pos; @@ -1411,7 +1442,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) elems->timeout_int = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_BSS_MAX_IDLE_PERIOD: if (elen >= sizeof(*elems->max_idle_period_ie)) @@ -1444,7 +1476,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen >= sizeof(*elems->s1g_capab)) elems->s1g_capab = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_S1G_OPERATION: if (params->mode != IEEE80211_CONN_MODE_S1G) @@ -1452,7 +1485,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen == sizeof(*elems->s1g_oper)) elems->s1g_oper = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_S1G_BCN_COMPAT: if (params->mode != IEEE80211_CONN_MODE_S1G) @@ -1460,7 +1494,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen == sizeof(*elems->s1g_bcn_compat)) elems->s1g_bcn_compat = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; case WLAN_EID_AID_RESPONSE: if (params->mode != IEEE80211_CONN_MODE_S1G) @@ -1468,20 +1503,21 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, if (elen == sizeof(struct ieee80211_aid_response_ie)) elems->aid_resp = (void *)pos; else - elem_parse_failed = true; + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; break; default: break; } if (elem_parse_failed) - elems->parse_error = true; + elems->parse_error |= elem_parse_failed; else __set_bit(id, seen_elems); } if (!for_each_element_completed(elem, params->start, params->len)) - elems->parse_error = true; + elems->parse_error |= IEEE80211_PARSE_ERR_INVALID_END; return crc; } From a57944d1ee8bf29cbdf7d6b8e9579d88bf912c3e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:06:54 +0100 Subject: [PATCH 250/378] wifi: mac80211: disallow basic multi-link element in per-STA profile There really shouldn't be a basic multi-link element in any per-STA profile in an association response, it's not clear what that would really mean. Refuse connecting in this case since the AP isn't following the spec. Link: https://msgid.link/20240129200652.23f1e3b337f1.Idd2e43cdbfe3ba15b3e9b8aeb54c8115587177a0@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 3 ++- net/mac80211/util.c | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f5fe659a1efd..e11297b4dc63 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1671,6 +1671,7 @@ enum ieee80211_elems_parse_error { IEEE80211_PARSE_ERR_DUP_ELEM = BIT(1), IEEE80211_PARSE_ERR_BAD_ELEM_SIZE = BIT(2), IEEE80211_PARSE_ERR_UNEXPECTED_ELEM = BIT(3), + IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC = BIT(4), }; /* Parsed Information Elements */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 35dda5982854..9a0331d914d3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4304,7 +4304,8 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, link->u.mgd.bss_param_ch_cnt = ieee80211_mle_get_bss_param_ch_cnt(elems->ml_basic); } - } else if (!elems->prof || + } else if (elems->parse_error & IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC || + !elems->prof || !(elems->prof->control & prof_bss_param_ch_present)) { ret = false; goto out; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index c1fa762f0cba..d85a9c5cde26 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1012,6 +1012,11 @@ ieee80211_parse_extension_element(u32 *crc, switch (le16_get_bits(mle->control, IEEE80211_ML_CONTROL_TYPE)) { case IEEE80211_ML_CONTROL_TYPE_BASIC: + if (elems->ml_basic) { + elems->parse_error |= + IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC; + break; + } elems->ml_basic_elem = (void *)elem; elems->ml_basic = data; elems->ml_basic_len = len; From 90233160d761e4ea56c7ac30e36fdaec73b3bdc4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:27 +0100 Subject: [PATCH 251/378] wifi: mac80211: simplify HE/EHT element length functions We don't need to pass the iftype there, we already have it in the sdata. Simplify this code. Link: https://msgid.link/20240129202041.5890eb1d4184.Ibce7e5abcc7887630da03ac2263d8004ec541418@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 4 ++-- net/mac80211/mesh.c | 8 +++----- net/mac80211/mesh_plink.c | 8 +++----- net/mac80211/util.c | 15 ++++++++------- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e11297b4dc63..44400ce9a0b1 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2502,7 +2502,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u32 cap); u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, const struct cfg80211_chan_def *chandef); -u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype); +u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata); u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, const struct ieee80211_sta_he_cap *he_cap, u8 *pos, u8 *end); @@ -2651,7 +2651,7 @@ u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache); void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache); -u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype); +u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata); u8 *ieee80211_ie_build_eht_cap(u8 *pos, const struct ieee80211_sta_he_cap *he_cap, const struct ieee80211_sta_eht_cap *eht_cap, diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index e06d9ed2d31b..9fd209e4ca19 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2008, 2009 open80211s Ltd. - * Copyright (C) 2018 - 2023 Intel Corporation + * Copyright (C) 2018 - 2024 Intel Corporation * Authors: Luis Carlos Cobo * Javier Cardona */ @@ -981,10 +981,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) band = chanctx_conf->def.chan->band; rcu_read_unlock(); - ie_len_he_cap = ieee80211_ie_len_he_cap(sdata, - NL80211_IFTYPE_MESH_POINT); - ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata, - NL80211_IFTYPE_MESH_POINT); + ie_len_he_cap = ieee80211_ie_len_he_cap(sdata); + ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata); head_len = hdr_len + 2 + /* NULL SSID */ /* Channel Switch Announcement */ diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index e3aad60f68ab..7ba0f01805a4 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2008, 2009 open80211s Ltd. - * Copyright (C) 2019, 2021-2023 Intel Corporation + * Copyright (C) 2019, 2021-2024 Intel Corporation * Author: Luis Carlos Cobo */ #include @@ -226,10 +226,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.self_prot); int err = -ENOMEM; - ie_len_he_cap = ieee80211_ie_len_he_cap(sdata, - NL80211_IFTYPE_MESH_POINT); - ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata, - NL80211_IFTYPE_MESH_POINT); + ie_len_he_cap = ieee80211_ie_len_he_cap(sdata); + ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata); skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + /* capability info */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d85a9c5cde26..e9a7978d47d4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3272,7 +3272,8 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, return pos; } -u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) +/* this may return more than ieee80211_ie_build_he_cap() will need */ +u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata) { const struct ieee80211_sta_he_cap *he_cap; struct ieee80211_supported_band *sband; @@ -3282,7 +3283,7 @@ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) if (!sband) return 0; - he_cap = ieee80211_get_he_iftype_cap(sband, iftype); + he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); if (!he_cap) return 0; @@ -5065,7 +5066,8 @@ u16 ieee80211_encode_usf(int listen_interval) return (u16) listen_interval; } -u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) +/* this may return more than ieee80211_ie_build_eht_cap() will need */ +u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata) { const struct ieee80211_sta_he_cap *he_cap; const struct ieee80211_sta_eht_cap *eht_cap; @@ -5077,13 +5079,12 @@ u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) if (!sband) return 0; - he_cap = ieee80211_get_he_iftype_cap(sband, iftype); - eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype); + he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); + eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); if (!he_cap || !eht_cap) return 0; - is_ap = iftype == NL80211_IFTYPE_AP || - iftype == NL80211_IFTYPE_P2P_GO; + is_ap = sdata->vif.type == NL80211_IFTYPE_AP; n = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, &eht_cap->eht_cap_elem, From 6239da18d2f947523a80fb1f85f8d8a13d1726c1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:28 +0100 Subject: [PATCH 252/378] wifi: mac80211: adjust EHT capa when lowering bandwidth If intending to associate with a lower bandwidth, remove capabilities related to 320 MHz from the EHT capabilities element. Also change the EHT MCS-NSS set accordingly: if just reducing 320->160 or similar the format doesn't change, just cut off the last bytes. If changing from higher bandwidth to 20 MHz only EHT STA, adjust the format. Note that this also requires adjusting the caller in mlme.c since the data written can now be shorter than it determined. We need to clean all that up. Since the other callers pass NULL for the conn limit, we don't need to change things there. Link: https://msgid.link/20240129202041.b5f6df108c77.I0d8ea04079c61cb3744cc88625eeaf0d4776dc2b@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 3 + net/mac80211/ieee80211_i.h | 3 +- net/mac80211/mesh.c | 3 +- net/mac80211/mlme.c | 15 +++-- net/mac80211/tdls.c | 5 +- net/mac80211/util.c | 114 ++++++++++++++++++++++++++++--------- 6 files changed, 106 insertions(+), 37 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index e9078143b822..e4322238f273 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -3060,6 +3060,9 @@ ieee80211_he_spr_size(const u8 *he_spr_ie) #define IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF 0x40 #define IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK 0x07 +#define IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_80MHZ 0x08 +#define IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_160MHZ 0x30 +#define IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_320MHZ 0x40 #define IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK 0x78 #define IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP 0x80 diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 44400ce9a0b1..43c55ea6349c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2652,7 +2652,8 @@ void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache); void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache); u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata); -u8 *ieee80211_ie_build_eht_cap(u8 *pos, +u8 *ieee80211_ie_build_eht_cap(const struct ieee80211_conn_settings *conn, + u8 *pos, const struct ieee80211_sta_he_cap *he_cap, const struct ieee80211_sta_eht_cap *eht_cap, u8 *end, diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 9fd209e4ca19..000fa9484b4e 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -668,7 +668,8 @@ int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata, return -ENOMEM; pos = skb_put(skb, ie_len); - ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + ie_len, false); + ieee80211_ie_build_eht_cap(NULL, pos, he_cap, eht_cap, pos + ie_len, + false); return 0; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9a0331d914d3..bbc7894ccad0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1072,9 +1072,10 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_supported_band *sband) + struct ieee80211_supported_band *sband, + const struct ieee80211_conn_settings *conn) { - u8 *pos; + u8 *pos, *pre_eht_pos; const struct ieee80211_sta_he_cap *he_cap; const struct ieee80211_sta_eht_cap *eht_cap; u8 eht_cap_size; @@ -1097,8 +1098,11 @@ static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata, ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], eht_cap->eht_cap_elem.phy_cap_info); pos = skb_put(skb, eht_cap_size); - ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + eht_cap_size, - false); + pre_eht_pos = pos; + pos = ieee80211_ie_build_eht_cap(conn, pos, he_cap, eht_cap, + pos + eht_cap_size, false); + /* trim excess if any */ + skb_trim(skb, skb->len - (pre_eht_pos + eht_cap_size - pos)); } static void ieee80211_assoc_add_rates(struct sk_buff *skb, @@ -1453,7 +1457,8 @@ static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, present_elems = NULL; if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_EHT) - ieee80211_add_eht_ie(sdata, skb, sband); + ieee80211_add_eht_ie(sdata, skb, sband, + &assoc_data->link[link_id].conn); if (sband->band == NL80211_BAND_S1GHZ) { ieee80211_add_aid_request_ie(sdata, skb); diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 0f4aa42e070f..57673f27daf4 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -6,7 +6,7 @@ * Copyright 2014, Intel Corporation * Copyright 2014 Intel Mobile Communications GmbH * Copyright 2015 - 2016 Intel Deutschland GmbH - * Copyright (C) 2019, 2021-2023 Intel Corporation + * Copyright (C) 2019, 2021-2024 Intel Corporation */ #include @@ -604,7 +604,8 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], eht_cap->eht_cap_elem.phy_cap_info); pos = skb_put(skb, cap_size); - ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + cap_size, false); + ieee80211_ie_build_eht_cap(NULL, pos, he_cap, eht_cap, + pos + cap_size, false); } /* add any remaining IEs */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e9a7978d47d4..5224c22b1afc 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2250,7 +2250,8 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), IEEE80211_CHAN_NO_HE | IEEE80211_CHAN_NO_EHT)) { - pos = ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, end, + pos = ieee80211_ie_build_eht_cap(NULL, pos, he_cap, eht_cap, + end, sdata->vif.type == NL80211_IFTYPE_AP); if (!pos) goto out_err; @@ -3294,6 +3295,24 @@ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata) he_cap->he_cap_elem.phy_cap_info); } +static void +ieee80211_get_adjusted_he_cap(const struct ieee80211_conn_settings *conn, + const struct ieee80211_sta_he_cap *he_cap, + struct ieee80211_he_cap_elem *elem) +{ + *elem = he_cap->he_cap_elem; + + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) + elem->phy_cap_info[0] &= + ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G); + + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160) + elem->phy_cap_info[0] &= + ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G); +} + u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, const struct ieee80211_sta_he_cap *he_cap, u8 *pos, u8 *end) @@ -3307,25 +3326,11 @@ u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, conn = &ieee80211_conn_settings_unlimited; /* Make sure we have place for the IE */ - /* - * TODO: the 1 added is because this temporarily is under the EXTENSION - * IE. Get rid of it when it moves. - */ if (!he_cap) return orig_pos; /* modify on stack first to calculate 'n' and 'ie_len' correctly */ - elem = he_cap->he_cap_elem; - - if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) - elem.phy_cap_info[0] &= - ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G); - - if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160) - elem.phy_cap_info[0] &= - ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G); + ieee80211_get_adjusted_he_cap(conn, he_cap, &elem); n = ieee80211_he_mcs_nss_size(&elem); ie_len = 2 + 1 + @@ -5096,25 +5101,65 @@ u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata) return 0; } -u8 *ieee80211_ie_build_eht_cap(u8 *pos, +u8 *ieee80211_ie_build_eht_cap(const struct ieee80211_conn_settings *conn, + u8 *pos, const struct ieee80211_sta_he_cap *he_cap, const struct ieee80211_sta_eht_cap *eht_cap, - u8 *end, - bool for_ap) + u8 *end, bool for_ap) { + struct ieee80211_eht_cap_elem_fixed fixed, *out; + struct ieee80211_he_cap_elem he; u8 mcs_nss_len, ppet_len; + u8 orig_mcs_nss_len; u8 ie_len; u8 *orig_pos = pos; + if (!conn) + conn = &ieee80211_conn_settings_unlimited; + /* Make sure we have place for the IE */ if (!he_cap || !eht_cap) return orig_pos; - mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, - &eht_cap->eht_cap_elem, - for_ap); + orig_mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, + &eht_cap->eht_cap_elem, + for_ap); + + ieee80211_get_adjusted_he_cap(conn, he_cap, &he); + + fixed = eht_cap->eht_cap_elem; + + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_80) + fixed.phy_cap_info[6] &= + ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_80MHZ; + + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160) { + fixed.phy_cap_info[1] &= + ~IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK; + fixed.phy_cap_info[2] &= + ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK; + fixed.phy_cap_info[6] &= + ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_160MHZ; + } + + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_320) { + fixed.phy_cap_info[0] &= + ~IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + fixed.phy_cap_info[1] &= + ~IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK; + fixed.phy_cap_info[2] &= + ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK; + fixed.phy_cap_info[6] &= + ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_320MHZ; + } + + if (conn->bw_limit == IEEE80211_CONN_BW_LIMIT_20) + fixed.phy_cap_info[0] &= + ~IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ; + + mcs_nss_len = ieee80211_eht_mcs_nss_size(&he, &fixed, for_ap); ppet_len = ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], - eht_cap->eht_cap_elem.phy_cap_info); + fixed.phy_cap_info); ie_len = 2 + 1 + sizeof(eht_cap->eht_cap_elem) + mcs_nss_len + ppet_len; if ((end - pos) < ie_len) @@ -5124,12 +5169,25 @@ u8 *ieee80211_ie_build_eht_cap(u8 *pos, *pos++ = ie_len - 2; *pos++ = WLAN_EID_EXT_EHT_CAPABILITY; - /* Fixed data */ - memcpy(pos, &eht_cap->eht_cap_elem, sizeof(eht_cap->eht_cap_elem)); - pos += sizeof(eht_cap->eht_cap_elem); + out = (void *)pos; + *out = fixed; + pos += sizeof(*out); - memcpy(pos, &eht_cap->eht_mcs_nss_supp, mcs_nss_len); - pos += mcs_nss_len; + if (mcs_nss_len == 4 && orig_mcs_nss_len != 4) { + /* + * If the (non-AP) STA became 20 MHz only, then convert from + * <=80 to 20-MHz-only format, where MCSes are indicated in + * the groups 0-7, 8-9, 10-11, 12-13 rather than just 0-9, + * 10-11, 12-13. Thus, use 0-9 for 0-7 and 8-9. + */ + *pos++ = eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss; + *pos++ = eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss; + *pos++ = eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs11_max_nss; + *pos++ = eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss; + } else { + memcpy(pos, &eht_cap->eht_mcs_nss_supp, mcs_nss_len); + pos += mcs_nss_len; + } if (ppet_len) { memcpy(pos, &eht_cap->eht_ppe_thres, ppet_len); From 06b4c8665dcff32f50153fd4cddc4b05f31569a7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:29 +0100 Subject: [PATCH 253/378] wifi: mac80211: limit HE RU capabilities when limiting bandwidth When limiting a station's supported bandwidth while connecting, also limit various other HE capabilities according to the bandwidth needed for them. Link: https://msgid.link/20240129202041.34be99efca25.I02a695961bc6aadd37768b17c50fcdec4427d460@changeid Signed-off-by: Johannes Berg --- net/mac80211/util.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 5224c22b1afc..c2fe9aece00d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3300,17 +3300,48 @@ ieee80211_get_adjusted_he_cap(const struct ieee80211_conn_settings *conn, const struct ieee80211_sta_he_cap *he_cap, struct ieee80211_he_cap_elem *elem) { + u8 ru_limit, max_ru; + *elem = he_cap->he_cap_elem; - if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) + switch (conn->bw_limit) { + case IEEE80211_CONN_BW_LIMIT_20: + ru_limit = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242; + break; + case IEEE80211_CONN_BW_LIMIT_40: + ru_limit = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484; + break; + case IEEE80211_CONN_BW_LIMIT_80: + ru_limit = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996; + break; + default: + ru_limit = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996; + break; + } + + max_ru = elem->phy_cap_info[8] & IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK; + max_ru = min(max_ru, ru_limit); + elem->phy_cap_info[8] &= ~IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK; + elem->phy_cap_info[8] |= max_ru; + + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) { elem->phy_cap_info[0] &= ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G); + elem->phy_cap_info[9] &= + ~IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM; + } - if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160) + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160) { elem->phy_cap_info[0] &= ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G); + elem->phy_cap_info[5] &= + ~IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; + elem->phy_cap_info[7] &= + ~(IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ | + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ); + } } u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, From 552a26b3854e12d73ab0527ee0d46454a2e650fb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:30 +0100 Subject: [PATCH 254/378] wifi: mac80211: rename ieee80211_ie_build_he_6ghz_cap() The term 'IE' isn't really in use in the spec, and I want to rework all of this to use SKBs as the primary method for building elements. Rename this one already. Link: https://msgid.link/20240129202041.b8064a4e73b5.I8d2f4526562029107c6414c6cda378b300b1b0b0@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 8 +++++--- net/mac80211/mesh.c | 2 +- net/mac80211/mlme.c | 2 +- net/mac80211/util.c | 6 +++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 43c55ea6349c..2b5d49399500 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2506,9 +2506,6 @@ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata); u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, const struct ieee80211_sta_he_cap *he_cap, u8 *pos, u8 *end); -void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata, - enum ieee80211_smps_mode smps_mode, - struct sk_buff *skb); u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef); u8 *ieee80211_ie_build_eht_oper(u8 *pos, struct cfg80211_chan_def *chandef, const struct ieee80211_sta_eht_cap *eht_cap); @@ -2529,6 +2526,11 @@ void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); u8 *ieee80211_ie_build_s1g_cap(u8 *pos, struct ieee80211_sta_s1g_cap *s1g_cap); +/* element building in SKBs */ +void ieee80211_put_he_6ghz_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + enum ieee80211_smps_mode smps_mode); + /* channel management */ bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, struct cfg80211_chan_def *chandef); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 000fa9484b4e..10c7d7714ffe 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -640,7 +640,7 @@ int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata, if (!iftd) return 0; - ieee80211_ie_build_he_6ghz_cap(sdata, sdata->deflink.smps_mode, skb); + ieee80211_put_he_6ghz_cap(skb, sdata, sdata->deflink.smps_mode); return 0; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bbc7894ccad0..74a6c67a94da 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1067,7 +1067,7 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, /* trim excess if any */ skb_trim(skb, skb->len - (pre_he_pos + he_cap_size - pos)); - ieee80211_ie_build_he_6ghz_cap(sdata, smps_mode, skb); + ieee80211_put_he_6ghz_cap(skb, sdata, smps_mode); } static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index c2fe9aece00d..e8de82bafeef 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3413,9 +3413,9 @@ u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, return pos; } -void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata, - enum ieee80211_smps_mode smps_mode, - struct sk_buff *skb) +void ieee80211_put_he_6ghz_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + enum ieee80211_smps_mode smps_mode) { struct ieee80211_supported_band *sband; const struct ieee80211_sband_iftype_data *iftd; From e0b5ee918723dcf47d3773bc95cbcb428436f817 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:31 +0100 Subject: [PATCH 255/378] wifi: mac80211: tdls: use ieee80211_put_he_6ghz_cap() We don't need to use the write function here since we already have an SKB, so use ieee80211_put_he_6ghz_cap() with the SMPS mode taken from the link we're using. Link: https://msgid.link/20240129202041.6454ac78ff8c.I7152e3c27645105478c68d40ca493feb27cac6bf@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 1 - net/mac80211/tdls.c | 11 ++--------- net/mac80211/util.c | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2b5d49399500..f7b2381878a9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2457,7 +2457,6 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, const u8 *da, const u8 *bssid, u16 stype, u16 reason, bool send_frame, u8 *frame_buf); -u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end); enum { IEEE80211_PROBE_FLAG_DIRECTED = BIT(0), diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 57673f27daf4..e6808b7660ff 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -552,7 +552,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, (action_code == WLAN_TDLS_SETUP_REQUEST || action_code == WLAN_TDLS_SETUP_RESPONSE || action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) { - __le16 he_6ghz_capa; u8 cap_size; cap_size = @@ -564,14 +563,8 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, pos = ieee80211_ie_build_he_cap(NULL, he_cap, pos, pos + cap_size); /* Build HE 6Ghz capa IE from sband */ - if (sband->band == NL80211_BAND_6GHZ) { - cap_size = 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa); - pos = skb_put(skb, cap_size); - he_6ghz_capa = - ieee80211_get_he_6ghz_capa_vif(sband, &sdata->vif); - pos = ieee80211_write_he_6ghz_cap(pos, he_6ghz_capa, - pos + cap_size); - } + if (sband->band == NL80211_BAND_6GHZ) + ieee80211_put_he_6ghz_cap(skb, sdata, link->smps_mode); } /* add any custom IEs that go before EHT capabilities */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e8de82bafeef..c90f338b229c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2035,7 +2035,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, } } -u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end) +static u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end) { if ((end - pos) < 5) return pos; From 147ceae2053402cbbe543944abf3a8494ef76895 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:32 +0100 Subject: [PATCH 256/378] wifi: mac80211: simplify adding supported rates Make this a new-style "put" function, and change the parameters to pass more information directly, this makes it usable also for the MLME code. Link: https://msgid.link/20240129202041.f604a03bd728.I8c798ea45b8479ac9982e77d0378af11a09ccdaf@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 10 ++-- net/mac80211/mesh.c | 20 ++++--- net/mac80211/mesh_plink.c | 16 ++++-- net/mac80211/mlme.c | 53 ++++-------------- net/mac80211/tdls.c | 4 +- net/mac80211/util.c | 109 +++++++++++++------------------------ 6 files changed, 79 insertions(+), 133 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f7b2381878a9..b69f081e1c1f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2511,12 +2511,6 @@ u8 *ieee80211_ie_build_eht_oper(u8 *pos, struct cfg80211_chan_def *chandef, int ieee80211_parse_bitrates(enum nl80211_chan_width width, const struct ieee80211_supported_band *sband, const u8 *srates, int srates_len, u32 *rates); -int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool need_basic, - enum nl80211_band band); -int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool need_basic, - enum nl80211_band band); u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo); void ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_s1g_cap *caps, @@ -2526,6 +2520,10 @@ void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata, u8 *ieee80211_ie_build_s1g_cap(u8 *pos, struct ieee80211_sta_s1g_cap *s1g_cap); /* element building in SKBs */ +int ieee80211_put_srates_elem(struct sk_buff *skb, + const struct ieee80211_supported_band *sband, + u32 basic_rates, u32 rate_flags, u32 masked_rates, + u8 element_id); void ieee80211_put_he_6ghz_cap(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps_mode); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 10c7d7714ffe..7e860486c6bc 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -968,19 +968,19 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) int head_len, tail_len; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - struct ieee80211_chanctx_conf *chanctx_conf; struct mesh_csa_settings *csa; - enum nl80211_band band; + const struct ieee80211_supported_band *sband; u8 ie_len_he_cap, ie_len_eht_cap; u8 *pos; struct ieee80211_sub_if_data *sdata; int hdr_len = offsetofend(struct ieee80211_mgmt, u.beacon); + u32 rate_flags; sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh); - rcu_read_lock(); - chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf); - band = chanctx_conf->def.chan->band; - rcu_read_unlock(); + + sband = ieee80211_get_sband(sdata); + rate_flags = + ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chanreq.oper); ie_len_he_cap = ieee80211_ie_len_he_cap(sdata); ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata); @@ -1107,7 +1107,9 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) } rcu_read_unlock(); - if (ieee80211_add_srates_ie(sdata, skb, true, band) || + if (ieee80211_put_srates_elem(skb, sband, + sdata->vif.bss_conf.basic_rates, + rate_flags, 0, WLAN_EID_SUPP_RATES) || mesh_add_ds_params_ie(sdata, skb)) goto out_free; @@ -1118,7 +1120,9 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) skb_trim(skb, 0); bcn->tail = bcn->head + bcn->head_len; - if (ieee80211_add_ext_srates_ie(sdata, skb, true, band) || + if (ieee80211_put_srates_elem(skb, sband, + sdata->vif.bss_conf.basic_rates, + rate_flags, 0, WLAN_EID_EXT_SUPP_RATES) || mesh_add_rsn_ie(sdata, skb) || mesh_add_ht_cap_ie(sdata, skb) || mesh_add_ht_oper_ie(sdata, skb) || diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 7ba0f01805a4..8f2b492a9fe9 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -264,14 +264,13 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, if (action != WLAN_SP_MESH_PEERING_CLOSE) { struct ieee80211_supported_band *sband; - enum nl80211_band band; + u32 rate_flags, basic_rates; sband = ieee80211_get_sband(sdata); if (!sband) { err = -EINVAL; goto free; } - band = sband->band; /* capability info */ pos = skb_put_zero(skb, 2); @@ -280,8 +279,17 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, 2); put_unaligned_le16(sta->sta.aid, pos); } - if (ieee80211_add_srates_ie(sdata, skb, true, band) || - ieee80211_add_ext_srates_ie(sdata, skb, true, band) || + + rate_flags = + ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chanreq.oper); + basic_rates = sdata->vif.bss_conf.basic_rates; + + if (ieee80211_put_srates_elem(skb, sband, basic_rates, + rate_flags, 0, + WLAN_EID_SUPP_RATES) || + ieee80211_put_srates_elem(skb, sband, basic_rates, + rate_flags, 0, + WLAN_EID_EXT_SUPP_RATES) || mesh_add_rsn_ie(sdata, skb) || mesh_add_meshid_ie(sdata, skb) || mesh_add_meshconf_ie(sdata, skb)) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 74a6c67a94da..d807a904419c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1110,10 +1110,7 @@ static void ieee80211_assoc_add_rates(struct sk_buff *skb, struct ieee80211_supported_band *sband, struct ieee80211_mgd_assoc_data *assoc_data) { - unsigned int rates_len, supp_rates_len; - u32 rates = 0; - int i, count; - u8 *pos; + u32 rates; if (assoc_data->supp_rates_len) { /* @@ -1122,53 +1119,23 @@ static void ieee80211_assoc_add_rates(struct sk_buff *skb, * in the association request (e.g. D-Link DAP 1353 in * b-only mode)... */ - rates_len = ieee80211_parse_bitrates(width, sband, - assoc_data->supp_rates, - assoc_data->supp_rates_len, - &rates); + ieee80211_parse_bitrates(width, sband, + assoc_data->supp_rates, + assoc_data->supp_rates_len, + &rates); } else { /* * In case AP not provide any supported rates information * before association, we send information element(s) with * all rates that we support. */ - rates_len = sband->n_bitrates; - for (i = 0; i < sband->n_bitrates; i++) - rates |= BIT(i); + rates = ~0; } - supp_rates_len = rates_len; - if (supp_rates_len > 8) - supp_rates_len = 8; - - pos = skb_put(skb, supp_rates_len + 2); - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = supp_rates_len; - - count = 0; - for (i = 0; i < sband->n_bitrates; i++) { - if (BIT(i) & rates) { - int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); - *pos++ = (u8)rate; - if (++count == 8) - break; - } - } - - if (rates_len > count) { - pos = skb_put(skb, rates_len - count + 2); - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = rates_len - count; - - for (i++; i < sband->n_bitrates; i++) { - if (BIT(i) & rates) { - int rate; - - rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); - *pos++ = (u8)rate; - } - } - } + ieee80211_put_srates_elem(skb, sband, 0, 0, ~rates, + WLAN_EID_SUPP_RATES); + ieee80211_put_srates_elem(skb, sband, 0, 0, ~rates, + WLAN_EID_EXT_SUPP_RATES); } static size_t ieee80211_add_before_ht_elems(struct sk_buff *skb, diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index e6808b7660ff..edbd3fd8a737 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -382,8 +382,8 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, if (WARN_ON_ONCE(!sband)) return; - ieee80211_add_srates_ie(sdata, skb, false, sband->band); - ieee80211_add_ext_srates_ie(sdata, skb, false, sband->band); + ieee80211_put_srates_elem(skb, sband, 0, 0, 0, WLAN_EID_SUPP_RATES); + ieee80211_put_srates_elem(skb, sband, 0, 0, 0, WLAN_EID_EXT_SUPP_RATES); ieee80211_tdls_add_supp_channels(sdata, skb); /* add any custom IEs that go before Extended Capabilities */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index c90f338b229c..3888ad3b052f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -4091,93 +4091,62 @@ int ieee80211_parse_bitrates(enum nl80211_chan_width width, return count; } -int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool need_basic, - enum nl80211_band band) +int ieee80211_put_srates_elem(struct sk_buff *skb, + const struct ieee80211_supported_band *sband, + u32 basic_rates, u32 rate_flags, u32 masked_rates, + u8 element_id) { - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; - int rate; - u8 i, rates, *pos; - u32 basic_rates = sdata->vif.bss_conf.basic_rates; - u32 rate_flags; + u8 i, rates, skip; - rate_flags = - ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chanreq.oper); - sband = local->hw.wiphy->bands[band]; rates = 0; for (i = 0; i < sband->n_bitrates; i++) { if ((rate_flags & sband->bitrates[i].flags) != rate_flags) continue; + if (masked_rates & BIT(i)) + continue; rates++; } - if (rates > 8) - rates = 8; + + if (element_id == WLAN_EID_SUPP_RATES) { + rates = min_t(u8, rates, 8); + skip = 0; + } else { + skip = 8; + if (rates <= skip) + return 0; + rates -= skip; + } if (skb_tailroom(skb) < rates + 2) - return -ENOMEM; + return -ENOBUFS; + + skb_put_u8(skb, element_id); + skb_put_u8(skb, rates); + + for (i = 0; i < sband->n_bitrates && rates; i++) { + int rate; + u8 basic; - pos = skb_put(skb, rates + 2); - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = rates; - for (i = 0; i < rates; i++) { - u8 basic = 0; if ((rate_flags & sband->bitrates[i].flags) != rate_flags) continue; - - if (need_basic && basic_rates & BIT(i)) - basic = 0x80; - rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); - *pos++ = basic | (u8) rate; - } - - return 0; -} - -int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool need_basic, - enum nl80211_band band) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; - int rate; - u8 i, exrates, *pos; - u32 basic_rates = sdata->vif.bss_conf.basic_rates; - u32 rate_flags; - - rate_flags = - ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chanreq.oper); - sband = local->hw.wiphy->bands[band]; - exrates = 0; - for (i = 0; i < sband->n_bitrates; i++) { - if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + if (masked_rates & BIT(i)) continue; - exrates++; - } - if (exrates > 8) - exrates -= 8; - else - exrates = 0; - - if (skb_tailroom(skb) < exrates + 2) - return -ENOMEM; - - if (exrates) { - pos = skb_put(skb, exrates + 2); - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = exrates; - for (i = 8; i < sband->n_bitrates; i++) { - u8 basic = 0; - if ((rate_flags & sband->bitrates[i].flags) - != rate_flags) - continue; - if (need_basic && basic_rates & BIT(i)) - basic = 0x80; - rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); - *pos++ = basic | (u8) rate; + if (skip > 0) { + skip--; + continue; } + + basic = basic_rates & BIT(i) ? 0x80 : 0; + + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); + skb_put_u8(skb, basic | (u8)rate); + rates--; } + + WARN(rates > 0, "rates confused: rates:%d, element:%d\n", + rates, element_id); + return 0; } From 07095d167749d49de876613b3567a246d86135a1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:33 +0100 Subject: [PATCH 257/378] wifi: mac80211: start building elements in SKBs The building of elements is really mess, and really the only reason we're not doing it in SKBs in the first place is that the scan code in ieee80211_build_preq_ies() doesn't. Convert ieee80211_build_preq_ies() to use an SKB internally so that we can gradually convert other things to ..._put_*() style interfaces. Link: https://msgid.link/20240129202041.c3a8e3c2cc99.I9d9920858c30ae5154719783933de0d7bc2a2cb9@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 7 +- net/mac80211/scan.c | 14 +- net/mac80211/util.c | 357 +++++++++++++++++++------------------ 3 files changed, 191 insertions(+), 187 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b69f081e1c1f..fde8c0b67125 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2517,16 +2517,15 @@ void ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); -u8 *ieee80211_ie_build_s1g_cap(u8 *pos, struct ieee80211_sta_s1g_cap *s1g_cap); /* element building in SKBs */ int ieee80211_put_srates_elem(struct sk_buff *skb, const struct ieee80211_supported_band *sband, u32 basic_rates, u32 rate_flags, u32 masked_rates, u8 element_id); -void ieee80211_put_he_6ghz_cap(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata, - enum ieee80211_smps_mode smps_mode); +int ieee80211_put_he_6ghz_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + enum ieee80211_smps_mode smps_mode); /* channel management */ bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 6dedbbba153a..bca2a259fda6 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -400,6 +400,8 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_sub_if_data *sdata) req->ie, req->ie_len, bands_used, req->rates, &chandef, flags); + if (ielen < 0) + return false; local->hw_scan_req->req.ie_len = ielen; local->hw_scan_req->req.no_cck = req->no_cck; ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr); @@ -1322,10 +1324,12 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, ieee80211_prepare_scan_chandef(&chandef); - ieee80211_build_preq_ies(sdata, ie, num_bands * iebufsz, - &sched_scan_ies, req->ie, - req->ie_len, bands_used, rate_masks, &chandef, - flags); + ret = ieee80211_build_preq_ies(sdata, ie, num_bands * iebufsz, + &sched_scan_ies, req->ie, + req->ie_len, bands_used, rate_masks, + &chandef, flags); + if (ret < 0) + goto error; ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); if (ret == 0) { @@ -1333,8 +1337,8 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, rcu_assign_pointer(local->sched_scan_req, req); } +error: kfree(ie); - out: if (ret) { /* Clean in case of failure after HW restart or upon resume. */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 3888ad3b052f..ea863d78061e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2035,37 +2035,36 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, } } -static u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end) +static int ieee80211_put_s1g_cap(struct sk_buff *skb, + struct ieee80211_sta_s1g_cap *s1g_cap) { - if ((end - pos) < 5) - return pos; + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_s1g_cap)) + return -ENOBUFS; - *pos++ = WLAN_EID_EXTENSION; - *pos++ = 1 + sizeof(cap); - *pos++ = WLAN_EID_EXT_HE_6GHZ_CAPA; - memcpy(pos, &cap, sizeof(cap)); + skb_put_u8(skb, WLAN_EID_S1G_CAPABILITIES); + skb_put_u8(skb, sizeof(struct ieee80211_s1g_cap)); - return pos + 2; + skb_put_data(skb, &s1g_cap->cap, sizeof(s1g_cap->cap)); + skb_put_data(skb, &s1g_cap->nss_mcs, sizeof(s1g_cap->nss_mcs)); + + return 0; } -static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, - u8 *buffer, size_t buffer_len, - const u8 *ie, size_t ie_len, - enum nl80211_band band, - u32 rate_mask, - struct cfg80211_chan_def *chandef, - size_t *offset, u32 flags) +static int ieee80211_put_preq_ies_band(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + const u8 *ie, size_t ie_len, + size_t *offset, + enum nl80211_band band, + u32 rate_mask, + struct cfg80211_chan_def *chandef, + u32 flags) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; const struct ieee80211_sta_he_cap *he_cap; const struct ieee80211_sta_eht_cap *eht_cap; - u8 *pos = buffer, *end = buffer + buffer_len; + int i, err; size_t noffset; - int supp_rates_len, i; - u8 rates[32]; - int num_rates; - int ext_rates_len; u32 rate_flags; bool have_80mhz = false; @@ -2078,32 +2077,13 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, rate_flags = ieee80211_chandef_rate_flags(chandef); /* For direct scan add S1G IE and consider its override bits */ - if (band == NL80211_BAND_S1GHZ) { - if (end - pos < 2 + sizeof(struct ieee80211_s1g_cap)) - goto out_err; - pos = ieee80211_ie_build_s1g_cap(pos, &sband->s1g_cap); - goto done; - } + if (band == NL80211_BAND_S1GHZ) + return ieee80211_put_s1g_cap(skb, &sband->s1g_cap); - num_rates = 0; - for (i = 0; i < sband->n_bitrates; i++) { - if ((BIT(i) & rate_mask) == 0) - continue; /* skip rate */ - if ((rate_flags & sband->bitrates[i].flags) != rate_flags) - continue; - - rates[num_rates++] = - (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); - } - - supp_rates_len = min_t(int, num_rates, 8); - - if (end - pos < 2 + supp_rates_len) - goto out_err; - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = supp_rates_len; - memcpy(pos, rates, supp_rates_len); - pos += supp_rates_len; + err = ieee80211_put_srates_elem(skb, sband, 0, rate_flags, 0, + WLAN_EID_SUPP_RATES); + if (err) + return err; /* insert "request information" if in custom IEs */ if (ie && ie_len) { @@ -2116,34 +2096,28 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, before_extrates, ARRAY_SIZE(before_extrates), *offset); - if (end - pos < noffset - *offset) - goto out_err; - memcpy(pos, ie + *offset, noffset - *offset); - pos += noffset - *offset; + if (skb_tailroom(skb) < noffset - *offset) + return -ENOBUFS; + skb_put_data(skb, ie + *offset, noffset - *offset); *offset = noffset; } - ext_rates_len = num_rates - supp_rates_len; - if (ext_rates_len > 0) { - if (end - pos < 2 + ext_rates_len) - goto out_err; - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = ext_rates_len; - memcpy(pos, rates + supp_rates_len, ext_rates_len); - pos += ext_rates_len; - } + err = ieee80211_put_srates_elem(skb, sband, 0, rate_flags, 0, + WLAN_EID_EXT_SUPP_RATES); + if (err) + return err; if (chandef->chan && sband->band == NL80211_BAND_2GHZ) { - if (end - pos < 3) - goto out_err; - *pos++ = WLAN_EID_DS_PARAMS; - *pos++ = 1; - *pos++ = ieee80211_frequency_to_channel( - chandef->chan->center_freq); + if (skb_tailroom(skb) < 3) + return -ENOBUFS; + skb_put_u8(skb, WLAN_EID_DS_PARAMS); + skb_put_u8(skb, 1); + skb_put_u8(skb, + ieee80211_frequency_to_channel(chandef->chan->center_freq)); } if (flags & IEEE80211_PROBE_FLAG_MIN_CONTENT) - goto done; + return 0; /* insert custom IEs that go before HT */ if (ie && ie_len) { @@ -2158,18 +2132,21 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, noffset = ieee80211_ie_split(ie, ie_len, before_ht, ARRAY_SIZE(before_ht), *offset); - if (end - pos < noffset - *offset) - goto out_err; - memcpy(pos, ie + *offset, noffset - *offset); - pos += noffset - *offset; + if (skb_tailroom(skb) < noffset - *offset) + return -ENOBUFS; + skb_put_data(skb, ie + *offset, noffset - *offset); *offset = noffset; } if (sband->ht_cap.ht_supported) { - if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) - goto out_err; - pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, - sband->ht_cap.cap); + u8 *pos; + + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) + return -ENOBUFS; + + pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap)); + ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, + sband->ht_cap.cap); } /* insert custom IEs that go before VHT */ @@ -2190,10 +2167,9 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, noffset = ieee80211_ie_split(ie, ie_len, before_vht, ARRAY_SIZE(before_vht), *offset); - if (end - pos < noffset - *offset) - goto out_err; - memcpy(pos, ie + *offset, noffset - *offset); - pos += noffset - *offset; + if (skb_tailroom(skb) < noffset - *offset) + return -ENOBUFS; + skb_put_data(skb, ie + *offset, noffset - *offset); *offset = noffset; } @@ -2208,10 +2184,14 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, } if (sband->vht_cap.vht_supported && have_80mhz) { - if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) - goto out_err; - pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, - sband->vht_cap.cap); + u8 *pos; + + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_cap)) + return -ENOBUFS; + + pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_cap)); + ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, + sband->vht_cap.cap); } /* insert custom IEs that go before HE */ @@ -2228,10 +2208,9 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, noffset = ieee80211_ie_split(ie, ie_len, before_he, ARRAY_SIZE(before_he), *offset); - if (end - pos < noffset - *offset) - goto out_err; - memcpy(pos, ie + *offset, noffset - *offset); - pos += noffset - *offset; + if (skb_tailroom(skb) < noffset - *offset) + return -ENOBUFS; + skb_put_data(skb, ie + *offset, noffset - *offset); *offset = noffset; } @@ -2239,9 +2218,13 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, if (he_cap && cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), IEEE80211_CHAN_NO_HE)) { + u8 *pos = skb_tail_pointer(skb); + u8 *end = pos + skb_tailroom(skb); + pos = ieee80211_ie_build_he_cap(NULL, he_cap, pos, end); if (!pos) - goto out_err; + return -ENOBUFS; + skb_put(skb, pos - skb_tail_pointer(skb)); } eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); @@ -2250,42 +2233,72 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), IEEE80211_CHAN_NO_HE | IEEE80211_CHAN_NO_EHT)) { + u8 *pos = skb_tail_pointer(skb); + u8 *end = pos + skb_tailroom(skb); + pos = ieee80211_ie_build_eht_cap(NULL, pos, he_cap, eht_cap, end, sdata->vif.type == NL80211_IFTYPE_AP); if (!pos) - goto out_err; + return -ENOBUFS; + skb_put(skb, pos - skb_tail_pointer(skb)); } - if (cfg80211_any_usable_channels(local->hw.wiphy, - BIT(NL80211_BAND_6GHZ), - IEEE80211_CHAN_NO_HE)) { - struct ieee80211_supported_band *sband6; - - sband6 = local->hw.wiphy->bands[NL80211_BAND_6GHZ]; - he_cap = ieee80211_get_he_iftype_cap_vif(sband6, &sdata->vif); - - if (he_cap) { - enum nl80211_iftype iftype = - ieee80211_vif_type_p2p(&sdata->vif); - __le16 cap = ieee80211_get_he_6ghz_capa(sband6, iftype); - - pos = ieee80211_write_he_6ghz_cap(pos, cap, end); - } - } + err = ieee80211_put_he_6ghz_cap(skb, sdata, IEEE80211_SMPS_OFF); + if (err) + return err; /* * If adding more here, adjust code in main.c * that calculates local->scan_ies_len. */ - return pos - buffer; - out_err: - WARN_ONCE(1, "not enough space for preq IEs\n"); - done: - return pos - buffer; + return 0; } +static int ieee80211_put_preq_ies(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_scan_ies *ie_desc, + const u8 *ie, size_t ie_len, + u8 bands_used, u32 *rate_masks, + struct cfg80211_chan_def *chandef, + u32 flags) +{ + size_t custom_ie_offset = 0; + int i, err; + + memset(ie_desc, 0, sizeof(*ie_desc)); + + for (i = 0; i < NUM_NL80211_BANDS; i++) { + if (bands_used & BIT(i)) { + ie_desc->ies[i] = skb_tail_pointer(skb); + err = ieee80211_put_preq_ies_band(skb, sdata, + ie, ie_len, + &custom_ie_offset, + i, rate_masks[i], + chandef, flags); + if (err) + return err; + ie_desc->len[i] = skb_tail_pointer(skb) - + ie_desc->ies[i]; + } + } + + /* add any remaining custom IEs */ + if (ie && ie_len) { + if (WARN_ONCE(skb_tailroom(skb) < ie_len - custom_ie_offset, + "not enough space for preq custom IEs\n")) + return -ENOBUFS; + ie_desc->common_ies = skb_tail_pointer(skb); + skb_put_data(skb, ie + custom_ie_offset, + ie_len - custom_ie_offset); + ie_desc->common_ie_len = skb_tail_pointer(skb) - + ie_desc->common_ies; + } + + return 0; +}; + int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer, size_t buffer_len, struct ieee80211_scan_ies *ie_desc, @@ -2294,41 +2307,43 @@ int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer, struct cfg80211_chan_def *chandef, u32 flags) { - size_t pos = 0, old_pos = 0, custom_ie_offset = 0; - int i; + struct sk_buff *skb = alloc_skb(buffer_len, GFP_KERNEL); + uintptr_t offs; + int ret, i; + u8 *start; - memset(ie_desc, 0, sizeof(*ie_desc)); + if (!skb) + return -ENOMEM; + start = skb_tail_pointer(skb); + memset(start, 0, skb_tailroom(skb)); + ret = ieee80211_put_preq_ies(skb, sdata, ie_desc, ie, ie_len, + bands_used, rate_masks, chandef, + flags); + if (ret < 0) { + goto out; + } + + if (skb->len > buffer_len) { + ret = -ENOBUFS; + goto out; + } + + memcpy(buffer, start, skb->len); + + /* adjust ie_desc for copy */ for (i = 0; i < NUM_NL80211_BANDS; i++) { - if (bands_used & BIT(i)) { - pos += ieee80211_build_preq_ies_band(sdata, - buffer + pos, - buffer_len - pos, - ie, ie_len, i, - rate_masks[i], - chandef, - &custom_ie_offset, - flags); - ie_desc->ies[i] = buffer + old_pos; - ie_desc->len[i] = pos - old_pos; - old_pos = pos; - } + offs = ie_desc->ies[i] - start; + ie_desc->ies[i] = buffer + offs; } + offs = ie_desc->common_ies - start; + ie_desc->common_ies = buffer + offs; - /* add any remaining custom IEs */ - if (ie && ie_len) { - if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset, - "not enough space for preq custom IEs\n")) - return pos; - memcpy(buffer + pos, ie + custom_ie_offset, - ie_len - custom_ie_offset); - ie_desc->common_ies = buffer + pos; - ie_desc->common_ie_len = ie_len - custom_ie_offset; - pos += ie_len - custom_ie_offset; - } - - return pos; -}; + ret = skb->len; +out: + consume_skb(skb); + return ret; +} struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, const u8 *src, const u8 *dst, @@ -2342,7 +2357,6 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, struct cfg80211_chan_def chandef; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - int ies_len; u32 rate_masks[NUM_NL80211_BANDS] = {}; struct ieee80211_scan_ies dummy_ie_desc; @@ -2363,11 +2377,9 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, return NULL; rate_masks[chan->band] = ratemask; - ies_len = ieee80211_build_preq_ies(sdata, skb_tail_pointer(skb), - skb_tailroom(skb), &dummy_ie_desc, - ie, ie_len, BIT(chan->band), - rate_masks, &chandef, flags); - skb_put(skb, ies_len); + ieee80211_put_preq_ies(skb, sdata, &dummy_ie_desc, + ie, ie_len, BIT(chan->band), + rate_masks, &chandef, flags); if (dst) { mgmt = (struct ieee80211_mgmt *) skb->data; @@ -3202,21 +3214,6 @@ size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) return pos; } -u8 *ieee80211_ie_build_s1g_cap(u8 *pos, struct ieee80211_sta_s1g_cap *s1g_cap) -{ - *pos++ = WLAN_EID_S1G_CAPABILITIES; - *pos++ = sizeof(struct ieee80211_s1g_cap); - memset(pos, 0, sizeof(struct ieee80211_s1g_cap)); - - memcpy(pos, &s1g_cap->cap, sizeof(s1g_cap->cap)); - pos += sizeof(s1g_cap->cap); - - memcpy(pos, &s1g_cap->nss_mcs, sizeof(s1g_cap->nss_mcs)); - pos += sizeof(s1g_cap->nss_mcs); - - return pos; -} - u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap) { @@ -3413,33 +3410,32 @@ u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, return pos; } -void ieee80211_put_he_6ghz_cap(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata, - enum ieee80211_smps_mode smps_mode) +int ieee80211_put_he_6ghz_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + enum ieee80211_smps_mode smps_mode) { struct ieee80211_supported_band *sband; const struct ieee80211_sband_iftype_data *iftd; enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); - u8 *pos; - u16 cap; + __le16 cap; if (!cfg80211_any_usable_channels(sdata->local->hw.wiphy, BIT(NL80211_BAND_6GHZ), IEEE80211_CHAN_NO_HE)) - return; + return 0; sband = sdata->local->hw.wiphy->bands[NL80211_BAND_6GHZ]; iftd = ieee80211_get_sband_iftype_data(sband, iftype); if (!iftd) - return; + return 0; /* Check for device HE 6 GHz capability before adding element */ if (!iftd->he_6ghz_capa.capa) - return; + return 0; - cap = le16_to_cpu(iftd->he_6ghz_capa.capa); - cap &= ~IEEE80211_HE_6GHZ_CAP_SM_PS; + cap = iftd->he_6ghz_capa.capa; + cap &= cpu_to_le16(~IEEE80211_HE_6GHZ_CAP_SM_PS); switch (smps_mode) { case IEEE80211_SMPS_AUTOMATIC: @@ -3447,22 +3443,27 @@ void ieee80211_put_he_6ghz_cap(struct sk_buff *skb, WARN_ON(1); fallthrough; case IEEE80211_SMPS_OFF: - cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DISABLED, - IEEE80211_HE_6GHZ_CAP_SM_PS); + cap |= le16_encode_bits(WLAN_HT_CAP_SM_PS_DISABLED, + IEEE80211_HE_6GHZ_CAP_SM_PS); break; case IEEE80211_SMPS_STATIC: - cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_STATIC, - IEEE80211_HE_6GHZ_CAP_SM_PS); + cap |= le16_encode_bits(WLAN_HT_CAP_SM_PS_STATIC, + IEEE80211_HE_6GHZ_CAP_SM_PS); break; case IEEE80211_SMPS_DYNAMIC: - cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DYNAMIC, - IEEE80211_HE_6GHZ_CAP_SM_PS); + cap |= le16_encode_bits(WLAN_HT_CAP_SM_PS_DYNAMIC, + IEEE80211_HE_6GHZ_CAP_SM_PS); break; } - pos = skb_put(skb, 2 + 1 + sizeof(cap)); - ieee80211_write_he_6ghz_cap(pos, cpu_to_le16(cap), - pos + 2 + 1 + sizeof(cap)); + if (skb_tailroom(skb) < 2 + 1 + sizeof(cap)) + return -ENOBUFS; + + skb_put_u8(skb, WLAN_EID_EXTENSION); + skb_put_u8(skb, 1 + sizeof(cap)); + skb_put_u8(skb, WLAN_EID_EXT_HE_6GHZ_CAPA); + skb_put_data(skb, &cap, sizeof(cap)); + return 0; } u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, From 9d0480a7c05b6482195acafbc5f4f3b4a1cc2b8b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:34 +0100 Subject: [PATCH 258/378] wifi: mac80211: move element parsing to a new file This code got really big, move it to a new file. Pure code move. Link: https://msgid.link/20240129202041.7f27f7c895e4.I0adfc28bd656a4d44c2bf47966277eecf56cbaa0@changeid Signed-off-by: Johannes Berg --- net/mac80211/Makefile | 2 +- net/mac80211/parse.c | 926 ++++++++++++++++++++++++++++++++++++++++++ net/mac80211/util.c | 891 ---------------------------------------- 3 files changed, 927 insertions(+), 892 deletions(-) create mode 100644 net/mac80211/parse.c diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 4406b4f8f3b9..a33884967f21 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -29,7 +29,7 @@ mac80211-y := \ spectmgmt.o \ tx.o \ key.o \ - util.o \ + util.o parse.o \ wme.o \ chan.o \ trace.o mlme.o \ diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c new file mode 100644 index 000000000000..196a882e4c19 --- /dev/null +++ b/net/mac80211/parse.c @@ -0,0 +1,926 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright 2006-2007 Jiri Benc + * Copyright 2007 Johannes Berg + * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright (C) 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2024 Intel Corporation + * + * element parsing for mac80211 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211_i.h" +#include "driver-ops.h" +#include "rate.h" +#include "mesh.h" +#include "wme.h" +#include "led.h" +#include "wep.h" + +static void +ieee80211_parse_extension_element(u32 *crc, + const struct element *elem, + struct ieee802_11_elems *elems, + struct ieee80211_elems_parse_params *params) +{ + const void *data = elem->data + 1; + bool calc_crc = false; + u8 len; + + if (!elem->datalen) + return; + + len = elem->datalen - 1; + + switch (elem->data[0]) { + case WLAN_EID_EXT_HE_MU_EDCA: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; + calc_crc = true; + if (len >= sizeof(*elems->mu_edca_param_set)) + elems->mu_edca_param_set = data; + break; + case WLAN_EID_EXT_HE_CAPABILITY: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; + if (ieee80211_he_capa_size_ok(data, len)) { + elems->he_cap = data; + elems->he_cap_len = len; + } + break; + case WLAN_EID_EXT_HE_OPERATION: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; + calc_crc = true; + if (len >= sizeof(*elems->he_operation) && + len >= ieee80211_he_oper_size(data) - 1) + elems->he_operation = data; + break; + case WLAN_EID_EXT_UORA: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; + if (len >= 1) + elems->uora_element = data; + break; + case WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME: + if (len == 3) + elems->max_channel_switch_time = data; + break; + case WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION: + if (len >= sizeof(*elems->mbssid_config_ie)) + elems->mbssid_config_ie = data; + break; + case WLAN_EID_EXT_HE_SPR: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; + if (len >= sizeof(*elems->he_spr) && + len >= ieee80211_he_spr_size(data)) + elems->he_spr = data; + break; + case WLAN_EID_EXT_HE_6GHZ_CAPA: + if (params->mode < IEEE80211_CONN_MODE_HE) + break; + if (len >= sizeof(*elems->he_6ghz_capa)) + elems->he_6ghz_capa = data; + break; + case WLAN_EID_EXT_EHT_CAPABILITY: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; + if (ieee80211_eht_capa_size_ok(elems->he_cap, + data, len, + params->from_ap)) { + elems->eht_cap = data; + elems->eht_cap_len = len; + } + break; + case WLAN_EID_EXT_EHT_OPERATION: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; + if (ieee80211_eht_oper_size_ok(data, len)) + elems->eht_operation = data; + calc_crc = true; + break; + case WLAN_EID_EXT_EHT_MULTI_LINK: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; + calc_crc = true; + + if (ieee80211_mle_size_ok(data, len)) { + const struct ieee80211_multi_link_elem *mle = + (void *)data; + + switch (le16_get_bits(mle->control, + IEEE80211_ML_CONTROL_TYPE)) { + case IEEE80211_ML_CONTROL_TYPE_BASIC: + if (elems->ml_basic) { + elems->parse_error |= + IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC; + break; + } + elems->ml_basic_elem = (void *)elem; + elems->ml_basic = data; + elems->ml_basic_len = len; + break; + case IEEE80211_ML_CONTROL_TYPE_RECONF: + elems->ml_reconf_elem = (void *)elem; + elems->ml_reconf = data; + elems->ml_reconf_len = len; + break; + default: + break; + } + } + break; + case WLAN_EID_EXT_BANDWIDTH_INDICATION: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; + if (ieee80211_bandwidth_indication_size_ok(data, len)) + elems->bandwidth_indication = data; + calc_crc = true; + break; + case WLAN_EID_EXT_TID_TO_LINK_MAPPING: + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; + calc_crc = true; + if (ieee80211_tid_to_link_map_size_ok(data, len) && + elems->ttlm_num < ARRAY_SIZE(elems->ttlm)) { + elems->ttlm[elems->ttlm_num] = (void *)data; + elems->ttlm_num++; + } + break; + } + + if (crc && calc_crc) + *crc = crc32_be(*crc, (void *)elem, elem->datalen + 2); +} + +static u32 +_ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, + struct ieee802_11_elems *elems, + const struct element *check_inherit) +{ + const struct element *elem; + bool calc_crc = params->filter != 0; + DECLARE_BITMAP(seen_elems, 256); + u32 crc = params->crc; + + bitmap_zero(seen_elems, 256); + + for_each_element(elem, params->start, params->len) { + const struct element *subelem; + u8 elem_parse_failed; + u8 id = elem->id; + u8 elen = elem->datalen; + const u8 *pos = elem->data; + + if (check_inherit && + !cfg80211_is_element_inherited(elem, + check_inherit)) + continue; + + switch (id) { + case WLAN_EID_SSID: + case WLAN_EID_SUPP_RATES: + case WLAN_EID_FH_PARAMS: + case WLAN_EID_DS_PARAMS: + case WLAN_EID_CF_PARAMS: + case WLAN_EID_TIM: + case WLAN_EID_IBSS_PARAMS: + case WLAN_EID_CHALLENGE: + case WLAN_EID_RSN: + case WLAN_EID_ERP_INFO: + case WLAN_EID_EXT_SUPP_RATES: + case WLAN_EID_HT_CAPABILITY: + case WLAN_EID_HT_OPERATION: + case WLAN_EID_VHT_CAPABILITY: + case WLAN_EID_VHT_OPERATION: + case WLAN_EID_MESH_ID: + case WLAN_EID_MESH_CONFIG: + case WLAN_EID_PEER_MGMT: + case WLAN_EID_PREQ: + case WLAN_EID_PREP: + case WLAN_EID_PERR: + case WLAN_EID_RANN: + case WLAN_EID_CHANNEL_SWITCH: + case WLAN_EID_EXT_CHANSWITCH_ANN: + case WLAN_EID_COUNTRY: + case WLAN_EID_PWR_CONSTRAINT: + case WLAN_EID_TIMEOUT_INTERVAL: + case WLAN_EID_SECONDARY_CHANNEL_OFFSET: + case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: + case WLAN_EID_CHAN_SWITCH_PARAM: + case WLAN_EID_EXT_CAPABILITY: + case WLAN_EID_CHAN_SWITCH_TIMING: + case WLAN_EID_LINK_ID: + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + case WLAN_EID_RSNX: + case WLAN_EID_S1G_BCN_COMPAT: + case WLAN_EID_S1G_CAPABILITIES: + case WLAN_EID_S1G_OPERATION: + case WLAN_EID_AID_RESPONSE: + case WLAN_EID_S1G_SHORT_BCN_INTERVAL: + /* + * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible + * that if the content gets bigger it might be needed more than once + */ + if (test_bit(id, seen_elems)) { + elems->parse_error |= + IEEE80211_PARSE_ERR_DUP_ELEM; + continue; + } + break; + } + + if (calc_crc && id < 64 && (params->filter & (1ULL << id))) + crc = crc32_be(crc, pos - 2, elen + 2); + + elem_parse_failed = 0; + + switch (id) { + case WLAN_EID_LINK_ID: + if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + elems->lnk_id = (void *)(pos - 2); + break; + case WLAN_EID_CHAN_SWITCH_TIMING: + if (elen < sizeof(struct ieee80211_ch_switch_timing)) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + elems->ch_sw_timing = (void *)pos; + break; + case WLAN_EID_EXT_CAPABILITY: + elems->ext_capab = pos; + elems->ext_capab_len = elen; + break; + case WLAN_EID_SSID: + elems->ssid = pos; + elems->ssid_len = elen; + break; + case WLAN_EID_SUPP_RATES: + elems->supp_rates = pos; + elems->supp_rates_len = elen; + break; + case WLAN_EID_DS_PARAMS: + if (elen >= 1) + elems->ds_params = pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_TIM: + if (elen >= sizeof(struct ieee80211_tim_ie)) { + elems->tim = (void *)pos; + elems->tim_len = elen; + } else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_VENDOR_SPECIFIC: + if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && + pos[2] == 0xf2) { + /* Microsoft OUI (00:50:F2) */ + + if (calc_crc) + crc = crc32_be(crc, pos - 2, elen + 2); + + if (elen >= 5 && pos[3] == 2) { + /* OUI Type 2 - WMM IE */ + if (pos[4] == 0) { + elems->wmm_info = pos; + elems->wmm_info_len = elen; + } else if (pos[4] == 1) { + elems->wmm_param = pos; + elems->wmm_param_len = elen; + } + } + } + break; + case WLAN_EID_RSN: + elems->rsn = pos; + elems->rsn_len = elen; + break; + case WLAN_EID_ERP_INFO: + if (elen >= 1) + elems->erp_info = pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_EXT_SUPP_RATES: + elems->ext_supp_rates = pos; + elems->ext_supp_rates_len = elen; + break; + case WLAN_EID_HT_CAPABILITY: + if (params->mode < IEEE80211_CONN_MODE_HT) + break; + if (elen >= sizeof(struct ieee80211_ht_cap)) + elems->ht_cap_elem = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_HT_OPERATION: + if (params->mode < IEEE80211_CONN_MODE_HT) + break; + if (elen >= sizeof(struct ieee80211_ht_operation)) + elems->ht_operation = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_VHT_CAPABILITY: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; + if (elen >= sizeof(struct ieee80211_vht_cap)) + elems->vht_cap_elem = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_VHT_OPERATION: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; + if (elen >= sizeof(struct ieee80211_vht_operation)) { + elems->vht_operation = (void *)pos; + if (calc_crc) + crc = crc32_be(crc, pos - 2, elen + 2); + break; + } + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_OPMODE_NOTIF: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; + if (elen > 0) { + elems->opmode_notif = pos; + if (calc_crc) + crc = crc32_be(crc, pos - 2, elen + 2); + break; + } + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_MESH_ID: + elems->mesh_id = pos; + elems->mesh_id_len = elen; + break; + case WLAN_EID_MESH_CONFIG: + if (elen >= sizeof(struct ieee80211_meshconf_ie)) + elems->mesh_config = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_PEER_MGMT: + elems->peering = pos; + elems->peering_len = elen; + break; + case WLAN_EID_MESH_AWAKE_WINDOW: + if (elen >= 2) + elems->awake_window = (void *)pos; + break; + case WLAN_EID_PREQ: + elems->preq = pos; + elems->preq_len = elen; + break; + case WLAN_EID_PREP: + elems->prep = pos; + elems->prep_len = elen; + break; + case WLAN_EID_PERR: + elems->perr = pos; + elems->perr_len = elen; + break; + case WLAN_EID_RANN: + if (elen >= sizeof(struct ieee80211_rann_ie)) + elems->rann = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_CHANNEL_SWITCH: + if (elen != sizeof(struct ieee80211_channel_sw_ie)) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + elems->ch_switch_ie = (void *)pos; + break; + case WLAN_EID_EXT_CHANSWITCH_ANN: + if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + elems->ext_chansw_ie = (void *)pos; + break; + case WLAN_EID_SECONDARY_CHANNEL_OFFSET: + if (params->mode < IEEE80211_CONN_MODE_HT) + break; + if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + elems->sec_chan_offs = (void *)pos; + break; + case WLAN_EID_CHAN_SWITCH_PARAM: + if (elen < + sizeof(*elems->mesh_chansw_params_ie)) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + elems->mesh_chansw_params_ie = (void *)pos; + break; + case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; + + if (!params->action) { + elem_parse_failed = + IEEE80211_PARSE_ERR_UNEXPECTED_ELEM; + break; + } + + if (elen < sizeof(*elems->wide_bw_chansw_ie)) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + elems->wide_bw_chansw_ie = (void *)pos; + break; + case WLAN_EID_CHANNEL_SWITCH_WRAPPER: + if (params->mode < IEEE80211_CONN_MODE_VHT) + break; + if (params->action) { + elem_parse_failed = + IEEE80211_PARSE_ERR_UNEXPECTED_ELEM; + break; + } + /* + * This is a bit tricky, but as we only care about + * a few elements, parse them out manually. + */ + subelem = cfg80211_find_elem(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, + pos, elen); + if (subelem) { + if (subelem->datalen >= sizeof(*elems->wide_bw_chansw_ie)) + elems->wide_bw_chansw_ie = + (void *)subelem->data; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + } + + if (params->mode < IEEE80211_CONN_MODE_EHT) + break; + + subelem = cfg80211_find_ext_elem(WLAN_EID_EXT_BANDWIDTH_INDICATION, + pos, elen); + if (subelem) { + const void *edata = subelem->data + 1; + u8 edatalen = subelem->datalen - 1; + + if (ieee80211_bandwidth_indication_size_ok(edata, + edatalen)) + elems->bandwidth_indication = edata; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + } + break; + case WLAN_EID_COUNTRY: + elems->country_elem = pos; + elems->country_elem_len = elen; + break; + case WLAN_EID_PWR_CONSTRAINT: + if (elen != 1) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + elems->pwr_constr_elem = pos; + break; + case WLAN_EID_CISCO_VENDOR_SPECIFIC: + /* Lots of different options exist, but we only care + * about the Dynamic Transmit Power Control element. + * First check for the Cisco OUI, then for the DTPC + * tag (0x00). + */ + if (elen < 4) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + + if (pos[0] != 0x00 || pos[1] != 0x40 || + pos[2] != 0x96 || pos[3] != 0x00) + break; + + if (elen != 6) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + + if (calc_crc) + crc = crc32_be(crc, pos - 2, elen + 2); + + elems->cisco_dtpc_elem = pos; + break; + case WLAN_EID_ADDBA_EXT: + if (elen < sizeof(struct ieee80211_addba_ext_ie)) { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + } + elems->addba_ext_ie = (void *)pos; + break; + case WLAN_EID_TIMEOUT_INTERVAL: + if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) + elems->timeout_int = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + if (elen >= sizeof(*elems->max_idle_period_ie)) + elems->max_idle_period_ie = (void *)pos; + break; + case WLAN_EID_RSNX: + elems->rsnx = pos; + elems->rsnx_len = elen; + break; + case WLAN_EID_TX_POWER_ENVELOPE: + if (elen < 1 || + elen > sizeof(struct ieee80211_tx_pwr_env)) + break; + + if (elems->tx_pwr_env_num >= ARRAY_SIZE(elems->tx_pwr_env)) + break; + + elems->tx_pwr_env[elems->tx_pwr_env_num] = (void *)pos; + elems->tx_pwr_env_len[elems->tx_pwr_env_num] = elen; + elems->tx_pwr_env_num++; + break; + case WLAN_EID_EXTENSION: + ieee80211_parse_extension_element(calc_crc ? + &crc : NULL, + elem, elems, params); + break; + case WLAN_EID_S1G_CAPABILITIES: + if (params->mode != IEEE80211_CONN_MODE_S1G) + break; + if (elen >= sizeof(*elems->s1g_capab)) + elems->s1g_capab = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_S1G_OPERATION: + if (params->mode != IEEE80211_CONN_MODE_S1G) + break; + if (elen == sizeof(*elems->s1g_oper)) + elems->s1g_oper = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_S1G_BCN_COMPAT: + if (params->mode != IEEE80211_CONN_MODE_S1G) + break; + if (elen == sizeof(*elems->s1g_bcn_compat)) + elems->s1g_bcn_compat = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + case WLAN_EID_AID_RESPONSE: + if (params->mode != IEEE80211_CONN_MODE_S1G) + break; + if (elen == sizeof(struct ieee80211_aid_response_ie)) + elems->aid_resp = (void *)pos; + else + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + break; + default: + break; + } + + if (elem_parse_failed) + elems->parse_error |= elem_parse_failed; + else + __set_bit(id, seen_elems); + } + + if (!for_each_element_completed(elem, params->start, params->len)) + elems->parse_error |= IEEE80211_PARSE_ERR_INVALID_END; + + return crc; +} + +static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, + struct ieee802_11_elems *elems, + struct cfg80211_bss *bss, + u8 *nontransmitted_profile) +{ + const struct element *elem, *sub; + size_t profile_len = 0; + bool found = false; + + if (!bss || !bss->transmitted_bss) + return profile_len; + + for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) { + if (elem->datalen < 2) + continue; + if (elem->data[0] < 1 || elem->data[0] > 8) + continue; + + for_each_element(sub, elem->data + 1, elem->datalen - 1) { + u8 new_bssid[ETH_ALEN]; + const u8 *index; + + if (sub->id != 0 || sub->datalen < 4) { + /* not a valid BSS profile */ + continue; + } + + if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP || + sub->data[1] != 2) { + /* The first element of the + * Nontransmitted BSSID Profile is not + * the Nontransmitted BSSID Capability + * element. + */ + continue; + } + + memset(nontransmitted_profile, 0, len); + profile_len = cfg80211_merge_profile(start, len, + elem, + sub, + nontransmitted_profile, + len); + + /* found a Nontransmitted BSSID Profile */ + index = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, + nontransmitted_profile, + profile_len); + if (!index || index[1] < 1 || index[2] == 0) { + /* Invalid MBSSID Index element */ + continue; + } + + cfg80211_gen_new_bssid(bss->transmitted_bss->bssid, + elem->data[0], + index[2], + new_bssid); + if (ether_addr_equal(new_bssid, bss->bssid)) { + found = true; + elems->bssid_index_len = index[1]; + elems->bssid_index = (void *)&index[2]; + break; + } + } + } + + return found ? profile_len : 0; +} + +static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems, + u8 link_id) +{ + const struct ieee80211_multi_link_elem *ml = elems->ml_basic; + ssize_t ml_len = elems->ml_basic_len; + const struct element *sub; + + if (!ml || !ml_len) + return; + + if (le16_get_bits(ml->control, IEEE80211_ML_CONTROL_TYPE) != + IEEE80211_ML_CONTROL_TYPE_BASIC) + return; + + for_each_mle_subelement(sub, (u8 *)ml, ml_len) { + struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data; + ssize_t sta_prof_len; + u16 control; + + if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE) + continue; + + if (!ieee80211_mle_basic_sta_prof_size_ok(sub->data, + sub->datalen)) + return; + + control = le16_to_cpu(prof->control); + + if (link_id != u16_get_bits(control, + IEEE80211_MLE_STA_CONTROL_LINK_ID)) + continue; + + if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE)) + return; + + /* the sub element can be fragmented */ + sta_prof_len = + cfg80211_defragment_element(sub, + (u8 *)ml, ml_len, + elems->scratch_pos, + elems->scratch + + elems->scratch_len - + elems->scratch_pos, + IEEE80211_MLE_SUBELEM_FRAGMENT); + + if (sta_prof_len < 0) + return; + + elems->prof = (void *)elems->scratch_pos; + elems->sta_prof_len = sta_prof_len; + elems->scratch_pos += sta_prof_len; + + return; + } +} + +static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems, + struct ieee80211_elems_parse_params *params) +{ + struct ieee80211_mle_per_sta_profile *prof; + struct ieee80211_elems_parse_params sub = { + .mode = params->mode, + .action = params->action, + .from_ap = params->from_ap, + .link_id = -1, + }; + ssize_t ml_len = elems->ml_basic_len; + const struct element *non_inherit = NULL; + const u8 *end; + + if (params->link_id == -1) + return; + + ml_len = cfg80211_defragment_element(elems->ml_basic_elem, + elems->ie_start, + elems->total_len, + elems->scratch_pos, + elems->scratch + + elems->scratch_len - + elems->scratch_pos, + WLAN_EID_FRAGMENT); + + if (ml_len < 0) + return; + + elems->ml_basic = (const void *)elems->scratch_pos; + elems->ml_basic_len = ml_len; + + ieee80211_mle_get_sta_prof(elems, params->link_id); + prof = elems->prof; + + if (!prof) + return; + + /* check if we have the 4 bytes for the fixed part in assoc response */ + if (elems->sta_prof_len < sizeof(*prof) + prof->sta_info_len - 1 + 4) { + elems->prof = NULL; + elems->sta_prof_len = 0; + return; + } + + /* + * Skip the capability information and the status code that are expected + * as part of the station profile in association response frames. Note + * the -1 is because the 'sta_info_len' is accounted to as part of the + * per-STA profile, but not part of the 'u8 variable[]' portion. + */ + sub.start = prof->variable + prof->sta_info_len - 1 + 4; + end = (const u8 *)prof + elems->sta_prof_len; + sub.len = end - sub.start; + + non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, + sub.start, sub.len); + _ieee802_11_parse_elems_full(&sub, elems, non_inherit); +} + +struct ieee802_11_elems * +ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) +{ + struct ieee802_11_elems *elems; + const struct element *non_inherit = NULL; + u8 *nontransmitted_profile; + int nontransmitted_profile_len = 0; + size_t scratch_len = 3 * params->len; + + elems = kzalloc(struct_size(elems, scratch, scratch_len), GFP_ATOMIC); + if (!elems) + return NULL; + elems->ie_start = params->start; + elems->total_len = params->len; + elems->scratch_len = scratch_len; + elems->scratch_pos = elems->scratch; + + nontransmitted_profile = elems->scratch_pos; + nontransmitted_profile_len = + ieee802_11_find_bssid_profile(params->start, params->len, + elems, params->bss, + nontransmitted_profile); + elems->scratch_pos += nontransmitted_profile_len; + elems->scratch_len -= nontransmitted_profile_len; + non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, + nontransmitted_profile, + nontransmitted_profile_len); + + elems->crc = _ieee802_11_parse_elems_full(params, elems, non_inherit); + + /* Override with nontransmitted profile, if found */ + if (nontransmitted_profile_len) { + struct ieee80211_elems_parse_params sub = { + .mode = params->mode, + .start = nontransmitted_profile, + .len = nontransmitted_profile_len, + .action = params->action, + .link_id = params->link_id, + }; + + _ieee802_11_parse_elems_full(&sub, elems, NULL); + } + + ieee80211_mle_parse_link(elems, params); + + if (elems->tim && !elems->parse_error) { + const struct ieee80211_tim_ie *tim_ie = elems->tim; + + elems->dtim_period = tim_ie->dtim_period; + elems->dtim_count = tim_ie->dtim_count; + } + + /* Override DTIM period and count if needed */ + if (elems->bssid_index && + elems->bssid_index_len >= + offsetofend(struct ieee80211_bssid_index, dtim_period)) + elems->dtim_period = elems->bssid_index->dtim_period; + + if (elems->bssid_index && + elems->bssid_index_len >= + offsetofend(struct ieee80211_bssid_index, dtim_count)) + elems->dtim_count = elems->bssid_index->dtim_count; + + return elems; +} +EXPORT_SYMBOL_IF_KUNIT(ieee802_11_parse_elems_full); + +int ieee80211_parse_bitrates(enum nl80211_chan_width width, + const struct ieee80211_supported_band *sband, + const u8 *srates, int srates_len, u32 *rates) +{ + u32 rate_flags = ieee80211_chanwidth_rate_flags(width); + struct ieee80211_rate *br; + int brate, rate, i, j, count = 0; + + *rates = 0; + + for (i = 0; i < srates_len; i++) { + rate = srates[i] & 0x7f; + + for (j = 0; j < sband->n_bitrates; j++) { + br = &sband->bitrates[j]; + if ((rate_flags & br->flags) != rate_flags) + continue; + + brate = DIV_ROUND_UP(br->bitrate, 5); + if (brate == rate) { + *rates |= BIT(j); + count++; + break; + } + } + } + return count; +} diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ea863d78061e..c675ac59d2bb 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -917,868 +917,6 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_queue_delayed_work); -static void -ieee80211_parse_extension_element(u32 *crc, - const struct element *elem, - struct ieee802_11_elems *elems, - struct ieee80211_elems_parse_params *params) -{ - const void *data = elem->data + 1; - bool calc_crc = false; - u8 len; - - if (!elem->datalen) - return; - - len = elem->datalen - 1; - - switch (elem->data[0]) { - case WLAN_EID_EXT_HE_MU_EDCA: - if (params->mode < IEEE80211_CONN_MODE_HE) - break; - calc_crc = true; - if (len >= sizeof(*elems->mu_edca_param_set)) - elems->mu_edca_param_set = data; - break; - case WLAN_EID_EXT_HE_CAPABILITY: - if (params->mode < IEEE80211_CONN_MODE_HE) - break; - if (ieee80211_he_capa_size_ok(data, len)) { - elems->he_cap = data; - elems->he_cap_len = len; - } - break; - case WLAN_EID_EXT_HE_OPERATION: - if (params->mode < IEEE80211_CONN_MODE_HE) - break; - calc_crc = true; - if (len >= sizeof(*elems->he_operation) && - len >= ieee80211_he_oper_size(data) - 1) - elems->he_operation = data; - break; - case WLAN_EID_EXT_UORA: - if (params->mode < IEEE80211_CONN_MODE_HE) - break; - if (len >= 1) - elems->uora_element = data; - break; - case WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME: - if (len == 3) - elems->max_channel_switch_time = data; - break; - case WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION: - if (len >= sizeof(*elems->mbssid_config_ie)) - elems->mbssid_config_ie = data; - break; - case WLAN_EID_EXT_HE_SPR: - if (params->mode < IEEE80211_CONN_MODE_HE) - break; - if (len >= sizeof(*elems->he_spr) && - len >= ieee80211_he_spr_size(data)) - elems->he_spr = data; - break; - case WLAN_EID_EXT_HE_6GHZ_CAPA: - if (params->mode < IEEE80211_CONN_MODE_HE) - break; - if (len >= sizeof(*elems->he_6ghz_capa)) - elems->he_6ghz_capa = data; - break; - case WLAN_EID_EXT_EHT_CAPABILITY: - if (params->mode < IEEE80211_CONN_MODE_EHT) - break; - if (ieee80211_eht_capa_size_ok(elems->he_cap, - data, len, - params->from_ap)) { - elems->eht_cap = data; - elems->eht_cap_len = len; - } - break; - case WLAN_EID_EXT_EHT_OPERATION: - if (params->mode < IEEE80211_CONN_MODE_EHT) - break; - if (ieee80211_eht_oper_size_ok(data, len)) - elems->eht_operation = data; - calc_crc = true; - break; - case WLAN_EID_EXT_EHT_MULTI_LINK: - if (params->mode < IEEE80211_CONN_MODE_EHT) - break; - calc_crc = true; - - if (ieee80211_mle_size_ok(data, len)) { - const struct ieee80211_multi_link_elem *mle = - (void *)data; - - switch (le16_get_bits(mle->control, - IEEE80211_ML_CONTROL_TYPE)) { - case IEEE80211_ML_CONTROL_TYPE_BASIC: - if (elems->ml_basic) { - elems->parse_error |= - IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC; - break; - } - elems->ml_basic_elem = (void *)elem; - elems->ml_basic = data; - elems->ml_basic_len = len; - break; - case IEEE80211_ML_CONTROL_TYPE_RECONF: - elems->ml_reconf_elem = (void *)elem; - elems->ml_reconf = data; - elems->ml_reconf_len = len; - break; - default: - break; - } - } - break; - case WLAN_EID_EXT_BANDWIDTH_INDICATION: - if (params->mode < IEEE80211_CONN_MODE_EHT) - break; - if (ieee80211_bandwidth_indication_size_ok(data, len)) - elems->bandwidth_indication = data; - calc_crc = true; - break; - case WLAN_EID_EXT_TID_TO_LINK_MAPPING: - if (params->mode < IEEE80211_CONN_MODE_EHT) - break; - calc_crc = true; - if (ieee80211_tid_to_link_map_size_ok(data, len) && - elems->ttlm_num < ARRAY_SIZE(elems->ttlm)) { - elems->ttlm[elems->ttlm_num] = (void *)data; - elems->ttlm_num++; - } - break; - } - - if (crc && calc_crc) - *crc = crc32_be(*crc, (void *)elem, elem->datalen + 2); -} - -static u32 -_ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, - struct ieee802_11_elems *elems, - const struct element *check_inherit) -{ - const struct element *elem; - bool calc_crc = params->filter != 0; - DECLARE_BITMAP(seen_elems, 256); - u32 crc = params->crc; - - bitmap_zero(seen_elems, 256); - - for_each_element(elem, params->start, params->len) { - const struct element *subelem; - u8 elem_parse_failed; - u8 id = elem->id; - u8 elen = elem->datalen; - const u8 *pos = elem->data; - - if (check_inherit && - !cfg80211_is_element_inherited(elem, - check_inherit)) - continue; - - switch (id) { - case WLAN_EID_SSID: - case WLAN_EID_SUPP_RATES: - case WLAN_EID_FH_PARAMS: - case WLAN_EID_DS_PARAMS: - case WLAN_EID_CF_PARAMS: - case WLAN_EID_TIM: - case WLAN_EID_IBSS_PARAMS: - case WLAN_EID_CHALLENGE: - case WLAN_EID_RSN: - case WLAN_EID_ERP_INFO: - case WLAN_EID_EXT_SUPP_RATES: - case WLAN_EID_HT_CAPABILITY: - case WLAN_EID_HT_OPERATION: - case WLAN_EID_VHT_CAPABILITY: - case WLAN_EID_VHT_OPERATION: - case WLAN_EID_MESH_ID: - case WLAN_EID_MESH_CONFIG: - case WLAN_EID_PEER_MGMT: - case WLAN_EID_PREQ: - case WLAN_EID_PREP: - case WLAN_EID_PERR: - case WLAN_EID_RANN: - case WLAN_EID_CHANNEL_SWITCH: - case WLAN_EID_EXT_CHANSWITCH_ANN: - case WLAN_EID_COUNTRY: - case WLAN_EID_PWR_CONSTRAINT: - case WLAN_EID_TIMEOUT_INTERVAL: - case WLAN_EID_SECONDARY_CHANNEL_OFFSET: - case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: - case WLAN_EID_CHAN_SWITCH_PARAM: - case WLAN_EID_EXT_CAPABILITY: - case WLAN_EID_CHAN_SWITCH_TIMING: - case WLAN_EID_LINK_ID: - case WLAN_EID_BSS_MAX_IDLE_PERIOD: - case WLAN_EID_RSNX: - case WLAN_EID_S1G_BCN_COMPAT: - case WLAN_EID_S1G_CAPABILITIES: - case WLAN_EID_S1G_OPERATION: - case WLAN_EID_AID_RESPONSE: - case WLAN_EID_S1G_SHORT_BCN_INTERVAL: - /* - * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible - * that if the content gets bigger it might be needed more than once - */ - if (test_bit(id, seen_elems)) { - elems->parse_error |= - IEEE80211_PARSE_ERR_DUP_ELEM; - continue; - } - break; - } - - if (calc_crc && id < 64 && (params->filter & (1ULL << id))) - crc = crc32_be(crc, pos - 2, elen + 2); - - elem_parse_failed = 0; - - switch (id) { - case WLAN_EID_LINK_ID: - if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - elems->lnk_id = (void *)(pos - 2); - break; - case WLAN_EID_CHAN_SWITCH_TIMING: - if (elen < sizeof(struct ieee80211_ch_switch_timing)) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - elems->ch_sw_timing = (void *)pos; - break; - case WLAN_EID_EXT_CAPABILITY: - elems->ext_capab = pos; - elems->ext_capab_len = elen; - break; - case WLAN_EID_SSID: - elems->ssid = pos; - elems->ssid_len = elen; - break; - case WLAN_EID_SUPP_RATES: - elems->supp_rates = pos; - elems->supp_rates_len = elen; - break; - case WLAN_EID_DS_PARAMS: - if (elen >= 1) - elems->ds_params = pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_TIM: - if (elen >= sizeof(struct ieee80211_tim_ie)) { - elems->tim = (void *)pos; - elems->tim_len = elen; - } else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_VENDOR_SPECIFIC: - if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && - pos[2] == 0xf2) { - /* Microsoft OUI (00:50:F2) */ - - if (calc_crc) - crc = crc32_be(crc, pos - 2, elen + 2); - - if (elen >= 5 && pos[3] == 2) { - /* OUI Type 2 - WMM IE */ - if (pos[4] == 0) { - elems->wmm_info = pos; - elems->wmm_info_len = elen; - } else if (pos[4] == 1) { - elems->wmm_param = pos; - elems->wmm_param_len = elen; - } - } - } - break; - case WLAN_EID_RSN: - elems->rsn = pos; - elems->rsn_len = elen; - break; - case WLAN_EID_ERP_INFO: - if (elen >= 1) - elems->erp_info = pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_EXT_SUPP_RATES: - elems->ext_supp_rates = pos; - elems->ext_supp_rates_len = elen; - break; - case WLAN_EID_HT_CAPABILITY: - if (params->mode < IEEE80211_CONN_MODE_HT) - break; - if (elen >= sizeof(struct ieee80211_ht_cap)) - elems->ht_cap_elem = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_HT_OPERATION: - if (params->mode < IEEE80211_CONN_MODE_HT) - break; - if (elen >= sizeof(struct ieee80211_ht_operation)) - elems->ht_operation = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_VHT_CAPABILITY: - if (params->mode < IEEE80211_CONN_MODE_VHT) - break; - if (elen >= sizeof(struct ieee80211_vht_cap)) - elems->vht_cap_elem = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_VHT_OPERATION: - if (params->mode < IEEE80211_CONN_MODE_VHT) - break; - if (elen >= sizeof(struct ieee80211_vht_operation)) { - elems->vht_operation = (void *)pos; - if (calc_crc) - crc = crc32_be(crc, pos - 2, elen + 2); - break; - } - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_OPMODE_NOTIF: - if (params->mode < IEEE80211_CONN_MODE_VHT) - break; - if (elen > 0) { - elems->opmode_notif = pos; - if (calc_crc) - crc = crc32_be(crc, pos - 2, elen + 2); - break; - } - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_MESH_ID: - elems->mesh_id = pos; - elems->mesh_id_len = elen; - break; - case WLAN_EID_MESH_CONFIG: - if (elen >= sizeof(struct ieee80211_meshconf_ie)) - elems->mesh_config = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_PEER_MGMT: - elems->peering = pos; - elems->peering_len = elen; - break; - case WLAN_EID_MESH_AWAKE_WINDOW: - if (elen >= 2) - elems->awake_window = (void *)pos; - break; - case WLAN_EID_PREQ: - elems->preq = pos; - elems->preq_len = elen; - break; - case WLAN_EID_PREP: - elems->prep = pos; - elems->prep_len = elen; - break; - case WLAN_EID_PERR: - elems->perr = pos; - elems->perr_len = elen; - break; - case WLAN_EID_RANN: - if (elen >= sizeof(struct ieee80211_rann_ie)) - elems->rann = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_CHANNEL_SWITCH: - if (elen != sizeof(struct ieee80211_channel_sw_ie)) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - elems->ch_switch_ie = (void *)pos; - break; - case WLAN_EID_EXT_CHANSWITCH_ANN: - if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - elems->ext_chansw_ie = (void *)pos; - break; - case WLAN_EID_SECONDARY_CHANNEL_OFFSET: - if (params->mode < IEEE80211_CONN_MODE_HT) - break; - if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - elems->sec_chan_offs = (void *)pos; - break; - case WLAN_EID_CHAN_SWITCH_PARAM: - if (elen < - sizeof(*elems->mesh_chansw_params_ie)) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - elems->mesh_chansw_params_ie = (void *)pos; - break; - case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: - if (params->mode < IEEE80211_CONN_MODE_VHT) - break; - - if (!params->action) { - elem_parse_failed = - IEEE80211_PARSE_ERR_UNEXPECTED_ELEM; - break; - } - - if (elen < sizeof(*elems->wide_bw_chansw_ie)) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - elems->wide_bw_chansw_ie = (void *)pos; - break; - case WLAN_EID_CHANNEL_SWITCH_WRAPPER: - if (params->mode < IEEE80211_CONN_MODE_VHT) - break; - if (params->action) { - elem_parse_failed = - IEEE80211_PARSE_ERR_UNEXPECTED_ELEM; - break; - } - /* - * This is a bit tricky, but as we only care about - * a few elements, parse them out manually. - */ - subelem = cfg80211_find_elem(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, - pos, elen); - if (subelem) { - if (subelem->datalen >= sizeof(*elems->wide_bw_chansw_ie)) - elems->wide_bw_chansw_ie = - (void *)subelem->data; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - } - - if (params->mode < IEEE80211_CONN_MODE_EHT) - break; - - subelem = cfg80211_find_ext_elem(WLAN_EID_EXT_BANDWIDTH_INDICATION, - pos, elen); - if (subelem) { - const void *edata = subelem->data + 1; - u8 edatalen = subelem->datalen - 1; - - if (ieee80211_bandwidth_indication_size_ok(edata, - edatalen)) - elems->bandwidth_indication = edata; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - } - break; - case WLAN_EID_COUNTRY: - elems->country_elem = pos; - elems->country_elem_len = elen; - break; - case WLAN_EID_PWR_CONSTRAINT: - if (elen != 1) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - elems->pwr_constr_elem = pos; - break; - case WLAN_EID_CISCO_VENDOR_SPECIFIC: - /* Lots of different options exist, but we only care - * about the Dynamic Transmit Power Control element. - * First check for the Cisco OUI, then for the DTPC - * tag (0x00). - */ - if (elen < 4) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - - if (pos[0] != 0x00 || pos[1] != 0x40 || - pos[2] != 0x96 || pos[3] != 0x00) - break; - - if (elen != 6) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - - if (calc_crc) - crc = crc32_be(crc, pos - 2, elen + 2); - - elems->cisco_dtpc_elem = pos; - break; - case WLAN_EID_ADDBA_EXT: - if (elen < sizeof(struct ieee80211_addba_ext_ie)) { - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - } - elems->addba_ext_ie = (void *)pos; - break; - case WLAN_EID_TIMEOUT_INTERVAL: - if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) - elems->timeout_int = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_BSS_MAX_IDLE_PERIOD: - if (elen >= sizeof(*elems->max_idle_period_ie)) - elems->max_idle_period_ie = (void *)pos; - break; - case WLAN_EID_RSNX: - elems->rsnx = pos; - elems->rsnx_len = elen; - break; - case WLAN_EID_TX_POWER_ENVELOPE: - if (elen < 1 || - elen > sizeof(struct ieee80211_tx_pwr_env)) - break; - - if (elems->tx_pwr_env_num >= ARRAY_SIZE(elems->tx_pwr_env)) - break; - - elems->tx_pwr_env[elems->tx_pwr_env_num] = (void *)pos; - elems->tx_pwr_env_len[elems->tx_pwr_env_num] = elen; - elems->tx_pwr_env_num++; - break; - case WLAN_EID_EXTENSION: - ieee80211_parse_extension_element(calc_crc ? - &crc : NULL, - elem, elems, params); - break; - case WLAN_EID_S1G_CAPABILITIES: - if (params->mode != IEEE80211_CONN_MODE_S1G) - break; - if (elen >= sizeof(*elems->s1g_capab)) - elems->s1g_capab = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_S1G_OPERATION: - if (params->mode != IEEE80211_CONN_MODE_S1G) - break; - if (elen == sizeof(*elems->s1g_oper)) - elems->s1g_oper = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_S1G_BCN_COMPAT: - if (params->mode != IEEE80211_CONN_MODE_S1G) - break; - if (elen == sizeof(*elems->s1g_bcn_compat)) - elems->s1g_bcn_compat = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - case WLAN_EID_AID_RESPONSE: - if (params->mode != IEEE80211_CONN_MODE_S1G) - break; - if (elen == sizeof(struct ieee80211_aid_response_ie)) - elems->aid_resp = (void *)pos; - else - elem_parse_failed = - IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; - break; - default: - break; - } - - if (elem_parse_failed) - elems->parse_error |= elem_parse_failed; - else - __set_bit(id, seen_elems); - } - - if (!for_each_element_completed(elem, params->start, params->len)) - elems->parse_error |= IEEE80211_PARSE_ERR_INVALID_END; - - return crc; -} - -static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, - struct ieee802_11_elems *elems, - struct cfg80211_bss *bss, - u8 *nontransmitted_profile) -{ - const struct element *elem, *sub; - size_t profile_len = 0; - bool found = false; - - if (!bss || !bss->transmitted_bss) - return profile_len; - - for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) { - if (elem->datalen < 2) - continue; - if (elem->data[0] < 1 || elem->data[0] > 8) - continue; - - for_each_element(sub, elem->data + 1, elem->datalen - 1) { - u8 new_bssid[ETH_ALEN]; - const u8 *index; - - if (sub->id != 0 || sub->datalen < 4) { - /* not a valid BSS profile */ - continue; - } - - if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP || - sub->data[1] != 2) { - /* The first element of the - * Nontransmitted BSSID Profile is not - * the Nontransmitted BSSID Capability - * element. - */ - continue; - } - - memset(nontransmitted_profile, 0, len); - profile_len = cfg80211_merge_profile(start, len, - elem, - sub, - nontransmitted_profile, - len); - - /* found a Nontransmitted BSSID Profile */ - index = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, - nontransmitted_profile, - profile_len); - if (!index || index[1] < 1 || index[2] == 0) { - /* Invalid MBSSID Index element */ - continue; - } - - cfg80211_gen_new_bssid(bss->transmitted_bss->bssid, - elem->data[0], - index[2], - new_bssid); - if (ether_addr_equal(new_bssid, bss->bssid)) { - found = true; - elems->bssid_index_len = index[1]; - elems->bssid_index = (void *)&index[2]; - break; - } - } - } - - return found ? profile_len : 0; -} - -static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems, - u8 link_id) -{ - const struct ieee80211_multi_link_elem *ml = elems->ml_basic; - ssize_t ml_len = elems->ml_basic_len; - const struct element *sub; - - if (!ml || !ml_len) - return; - - if (le16_get_bits(ml->control, IEEE80211_ML_CONTROL_TYPE) != - IEEE80211_ML_CONTROL_TYPE_BASIC) - return; - - for_each_mle_subelement(sub, (u8 *)ml, ml_len) { - struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data; - ssize_t sta_prof_len; - u16 control; - - if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE) - continue; - - if (!ieee80211_mle_basic_sta_prof_size_ok(sub->data, - sub->datalen)) - return; - - control = le16_to_cpu(prof->control); - - if (link_id != u16_get_bits(control, - IEEE80211_MLE_STA_CONTROL_LINK_ID)) - continue; - - if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE)) - return; - - /* the sub element can be fragmented */ - sta_prof_len = - cfg80211_defragment_element(sub, - (u8 *)ml, ml_len, - elems->scratch_pos, - elems->scratch + - elems->scratch_len - - elems->scratch_pos, - IEEE80211_MLE_SUBELEM_FRAGMENT); - - if (sta_prof_len < 0) - return; - - elems->prof = (void *)elems->scratch_pos; - elems->sta_prof_len = sta_prof_len; - elems->scratch_pos += sta_prof_len; - - return; - } -} - -static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems, - struct ieee80211_elems_parse_params *params) -{ - struct ieee80211_mle_per_sta_profile *prof; - struct ieee80211_elems_parse_params sub = { - .mode = params->mode, - .action = params->action, - .from_ap = params->from_ap, - .link_id = -1, - }; - ssize_t ml_len = elems->ml_basic_len; - const struct element *non_inherit = NULL; - const u8 *end; - - if (params->link_id == -1) - return; - - ml_len = cfg80211_defragment_element(elems->ml_basic_elem, - elems->ie_start, - elems->total_len, - elems->scratch_pos, - elems->scratch + - elems->scratch_len - - elems->scratch_pos, - WLAN_EID_FRAGMENT); - - if (ml_len < 0) - return; - - elems->ml_basic = (const void *)elems->scratch_pos; - elems->ml_basic_len = ml_len; - - ieee80211_mle_get_sta_prof(elems, params->link_id); - prof = elems->prof; - - if (!prof) - return; - - /* check if we have the 4 bytes for the fixed part in assoc response */ - if (elems->sta_prof_len < sizeof(*prof) + prof->sta_info_len - 1 + 4) { - elems->prof = NULL; - elems->sta_prof_len = 0; - return; - } - - /* - * Skip the capability information and the status code that are expected - * as part of the station profile in association response frames. Note - * the -1 is because the 'sta_info_len' is accounted to as part of the - * per-STA profile, but not part of the 'u8 variable[]' portion. - */ - sub.start = prof->variable + prof->sta_info_len - 1 + 4; - end = (const u8 *)prof + elems->sta_prof_len; - sub.len = end - sub.start; - - non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, - sub.start, sub.len); - _ieee802_11_parse_elems_full(&sub, elems, non_inherit); -} - -struct ieee802_11_elems * -ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) -{ - struct ieee802_11_elems *elems; - const struct element *non_inherit = NULL; - u8 *nontransmitted_profile; - int nontransmitted_profile_len = 0; - size_t scratch_len = 3 * params->len; - - elems = kzalloc(struct_size(elems, scratch, scratch_len), GFP_ATOMIC); - if (!elems) - return NULL; - elems->ie_start = params->start; - elems->total_len = params->len; - elems->scratch_len = scratch_len; - elems->scratch_pos = elems->scratch; - - nontransmitted_profile = elems->scratch_pos; - nontransmitted_profile_len = - ieee802_11_find_bssid_profile(params->start, params->len, - elems, params->bss, - nontransmitted_profile); - elems->scratch_pos += nontransmitted_profile_len; - elems->scratch_len -= nontransmitted_profile_len; - non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, - nontransmitted_profile, - nontransmitted_profile_len); - - elems->crc = _ieee802_11_parse_elems_full(params, elems, non_inherit); - - /* Override with nontransmitted profile, if found */ - if (nontransmitted_profile_len) { - struct ieee80211_elems_parse_params sub = { - .mode = params->mode, - .start = nontransmitted_profile, - .len = nontransmitted_profile_len, - .action = params->action, - .link_id = params->link_id, - }; - - _ieee802_11_parse_elems_full(&sub, elems, NULL); - } - - ieee80211_mle_parse_link(elems, params); - - if (elems->tim && !elems->parse_error) { - const struct ieee80211_tim_ie *tim_ie = elems->tim; - - elems->dtim_period = tim_ie->dtim_period; - elems->dtim_count = tim_ie->dtim_count; - } - - /* Override DTIM period and count if needed */ - if (elems->bssid_index && - elems->bssid_index_len >= - offsetofend(struct ieee80211_bssid_index, dtim_period)) - elems->dtim_period = elems->bssid_index->dtim_period; - - if (elems->bssid_index && - elems->bssid_index_len >= - offsetofend(struct ieee80211_bssid_index, dtim_count)) - elems->dtim_count = elems->bssid_index->dtim_count; - - return elems; -} -EXPORT_SYMBOL_IF_KUNIT(ieee802_11_parse_elems_full); - void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_queue_params *qparam, int ac) @@ -4063,35 +3201,6 @@ bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper, return true; } -int ieee80211_parse_bitrates(enum nl80211_chan_width width, - const struct ieee80211_supported_band *sband, - const u8 *srates, int srates_len, u32 *rates) -{ - u32 rate_flags = ieee80211_chanwidth_rate_flags(width); - struct ieee80211_rate *br; - int brate, rate, i, j, count = 0; - - *rates = 0; - - for (i = 0; i < srates_len; i++) { - rate = srates[i] & 0x7f; - - for (j = 0; j < sband->n_bitrates; j++) { - br = &sband->bitrates[j]; - if ((rate_flags & br->flags) != rate_flags) - continue; - - brate = DIV_ROUND_UP(br->bitrate, 5); - if (brate == rate) { - *rates |= BIT(j); - count++; - break; - } - } - } - return count; -} - int ieee80211_put_srates_elem(struct sk_buff *skb, const struct ieee80211_supported_band *sband, u32 basic_rates, u32 rate_flags, u32 masked_rates, From 28aa895bb0b324ed4a0cb441e5fbb882befb5e1c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:35 +0100 Subject: [PATCH 259/378] wifi: mac80211: convert ieee80211_ie_build_he_cap() to SKB use Convert ieee80211_ie_build_he_cap() to the SKB-put function style, renaming it to ieee80211_put_he_cap(). Link: https://msgid.link/20240129202041.e6ef888980d9.Ied9e014314b5d27611e693e3d4cb63bdc8d7de17@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 7 ++--- net/mac80211/mesh.c | 15 ++--------- net/mac80211/mlme.c | 37 +++----------------------- net/mac80211/tdls.c | 17 +++--------- net/mac80211/util.c | 54 +++++++++++++++++--------------------- 5 files changed, 37 insertions(+), 93 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fde8c0b67125..de0fdbd366c5 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2502,9 +2502,6 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, const struct cfg80211_chan_def *chandef); u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata); -u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, - const struct ieee80211_sta_he_cap *he_cap, - u8 *pos, u8 *end); u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef); u8 *ieee80211_ie_build_eht_oper(u8 *pos, struct cfg80211_chan_def *chandef, const struct ieee80211_sta_eht_cap *eht_cap); @@ -2523,6 +2520,10 @@ int ieee80211_put_srates_elem(struct sk_buff *skb, const struct ieee80211_supported_band *sband, u32 basic_rates, u32 rate_flags, u32 masked_rates, u8 element_id); +int ieee80211_put_he_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + const struct ieee80211_supported_band *sband, + const struct ieee80211_conn_settings *conn); int ieee80211_put_he_6ghz_cap(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps_mode); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 7e860486c6bc..cb217657c42e 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -567,29 +567,18 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata, int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u8 ie_len) { - const struct ieee80211_sta_he_cap *he_cap; struct ieee80211_supported_band *sband; - u8 *pos; sband = ieee80211_get_sband(sdata); if (!sband) return -EINVAL; - he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); - - if (!he_cap || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; - if (skb_tailroom(skb) < ie_len) - return -ENOMEM; - - pos = skb_put(skb, ie_len); - ieee80211_ie_build_he_cap(NULL, he_cap, pos, pos + ie_len); - - return 0; + return ieee80211_put_he_cap(skb, sdata, sband, NULL); } int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d807a904419c..de3d0e08ca2a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1038,38 +1038,6 @@ static bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, return mu_mimo_owner; } -/* This function determines HE capability flags for the association - * and builds the IE. - */ -static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct ieee80211_supported_band *sband, - enum ieee80211_smps_mode smps_mode, - const struct ieee80211_conn_settings *conn) -{ - u8 *pos, *pre_he_pos; - const struct ieee80211_sta_he_cap *he_cap; - u8 he_cap_size; - - he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); - if (WARN_ON(!he_cap)) - return; - - /* get a max size estimate */ - he_cap_size = - 2 + 1 + sizeof(he_cap->he_cap_elem) + - ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) + - ieee80211_he_ppe_size(he_cap->ppe_thres[0], - he_cap->he_cap_elem.phy_cap_info); - pos = skb_put(skb, he_cap_size); - pre_he_pos = pos; - pos = ieee80211_ie_build_he_cap(conn, he_cap, pos, pos + he_cap_size); - /* trim excess if any */ - skb_trim(skb, skb->len - (pre_he_pos + he_cap_size - pos)); - - ieee80211_put_he_6ghz_cap(skb, sdata, smps_mode); -} - static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_supported_band *sband, @@ -1403,9 +1371,10 @@ static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, offset); if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_HE) { - ieee80211_add_he_ie(sdata, skb, sband, smps_mode, - &assoc_data->link[link_id].conn); + ieee80211_put_he_cap(skb, sdata, sband, + &assoc_data->link[link_id].conn); ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_HE_CAPABILITY); + ieee80211_put_he_6ghz_cap(skb, sdata, smps_mode); } /* diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index edbd3fd8a737..3f9c2b2771c6 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -548,19 +548,10 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, } /* build the HE-cap from sband */ - if (he_cap && - (action_code == WLAN_TDLS_SETUP_REQUEST || - action_code == WLAN_TDLS_SETUP_RESPONSE || - action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) { - u8 cap_size; - - cap_size = - 2 + 1 + sizeof(he_cap->he_cap_elem) + - ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) + - ieee80211_he_ppe_size(he_cap->ppe_thres[0], - he_cap->he_cap_elem.phy_cap_info); - pos = skb_put(skb, cap_size); - pos = ieee80211_ie_build_he_cap(NULL, he_cap, pos, pos + cap_size); + if (action_code == WLAN_TDLS_SETUP_REQUEST || + action_code == WLAN_TDLS_SETUP_RESPONSE || + action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { + ieee80211_put_he_cap(skb, sdata, sband, NULL); /* Build HE 6Ghz capa IE from sband */ if (sband->band == NL80211_BAND_6GHZ) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index c675ac59d2bb..4dcb62e9d4c6 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1352,19 +1352,14 @@ static int ieee80211_put_preq_ies_band(struct sk_buff *skb, *offset = noffset; } - he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); - if (he_cap && - cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), + if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), IEEE80211_CHAN_NO_HE)) { - u8 *pos = skb_tail_pointer(skb); - u8 *end = pos + skb_tailroom(skb); - - pos = ieee80211_ie_build_he_cap(NULL, he_cap, pos, end); - if (!pos) - return -ENOBUFS; - skb_put(skb, pos - skb_tail_pointer(skb)); + err = ieee80211_put_he_cap(skb, sdata, sband, NULL); + if (err) + return err; } + he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); if (eht_cap && @@ -2408,7 +2403,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, return pos; } -/* this may return more than ieee80211_ie_build_he_cap() will need */ +/* this may return more than ieee80211_put_he_6ghz_cap() will need */ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata) { const struct ieee80211_sta_he_cap *he_cap; @@ -2479,21 +2474,23 @@ ieee80211_get_adjusted_he_cap(const struct ieee80211_conn_settings *conn, } } -u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, - const struct ieee80211_sta_he_cap *he_cap, - u8 *pos, u8 *end) +int ieee80211_put_he_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + const struct ieee80211_supported_band *sband, + const struct ieee80211_conn_settings *conn) { + const struct ieee80211_sta_he_cap *he_cap; struct ieee80211_he_cap_elem elem; + u8 *len; u8 n; u8 ie_len; - u8 *orig_pos = pos; if (!conn) conn = &ieee80211_conn_settings_unlimited; - /* Make sure we have place for the IE */ + he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); if (!he_cap) - return orig_pos; + return 0; /* modify on stack first to calculate 'n' and 'ie_len' correctly */ ieee80211_get_adjusted_he_cap(conn, he_cap, &elem); @@ -2504,19 +2501,17 @@ u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, ieee80211_he_ppe_size(he_cap->ppe_thres[0], he_cap->he_cap_elem.phy_cap_info); - if ((end - pos) < ie_len) - return orig_pos; + if (skb_tailroom(skb) < ie_len) + return -ENOBUFS; - *pos++ = WLAN_EID_EXTENSION; - pos++; /* We'll set the size later below */ - *pos++ = WLAN_EID_EXT_HE_CAPABILITY; + skb_put_u8(skb, WLAN_EID_EXTENSION); + len = skb_put(skb, 1); /* We'll set the size later below */ + skb_put_u8(skb, WLAN_EID_EXT_HE_CAPABILITY); /* Fixed data */ - memcpy(pos, &elem, sizeof(elem)); - pos += sizeof(elem); + skb_put_data(skb, &elem, sizeof(elem)); - memcpy(pos, &he_cap->he_mcs_nss_supp, n); - pos += n; + skb_put_data(skb, &he_cap->he_mcs_nss_supp, n); /* Check if PPE Threshold should be present */ if ((he_cap->he_cap_elem.phy_cap_info[6] & @@ -2540,12 +2535,11 @@ u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn, n = DIV_ROUND_UP(n, 8); /* Copy PPE Thresholds */ - memcpy(pos, &he_cap->ppe_thres, n); - pos += n; + skb_put_data(skb, &he_cap->ppe_thres, n); end: - orig_pos[1] = (pos - orig_pos) - 2; - return pos; + *len = skb_tail_pointer(skb) - len - 1; + return 0; } int ieee80211_put_he_6ghz_cap(struct sk_buff *skb, From ea8af8be4232a3f476d8659319cab794be44e73b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:19:36 +0100 Subject: [PATCH 260/378] wifi: mac80211: convert ieee80211_ie_build_eht_cap() to SKB use Convert ieee80211_ie_build_eht_cap() to the SKB-put function style, renaming it to ieee80211_put_eht_cap(). Link: https://msgid.link/20240129202041.ece9769e3c94.Ibd17bea6311f0c7ba56f6c1803fa3208abaaebb9@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 10 ++--- net/mac80211/mesh.c | 17 +-------- net/mac80211/mlme.c | 39 +------------------- net/mac80211/tdls.c | 20 ++-------- net/mac80211/util.c | 75 +++++++++++++++----------------------- 5 files changed, 42 insertions(+), 119 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index de0fdbd366c5..f3edb1a148a7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2527,6 +2527,10 @@ int ieee80211_put_he_cap(struct sk_buff *skb, int ieee80211_put_he_6ghz_cap(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps_mode); +int ieee80211_put_eht_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + const struct ieee80211_supported_band *sband, + const struct ieee80211_conn_settings *conn); /* channel management */ bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, @@ -2651,12 +2655,6 @@ void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache); void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache); u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata); -u8 *ieee80211_ie_build_eht_cap(const struct ieee80211_conn_settings *conn, - u8 *pos, - const struct ieee80211_sta_he_cap *he_cap, - const struct ieee80211_sta_eht_cap *eht_cap, - u8 *end, - bool for_ap); void ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index cb217657c42e..49f79512c144 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -636,31 +636,18 @@ int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata, int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u8 ie_len) { - const struct ieee80211_sta_he_cap *he_cap; - const struct ieee80211_sta_eht_cap *eht_cap; struct ieee80211_supported_band *sband; - u8 *pos; sband = ieee80211_get_sband(sdata); if (!sband) return -EINVAL; - he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); - eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); - if (!he_cap || !eht_cap || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) return 0; - if (skb_tailroom(skb) < ie_len) - return -ENOMEM; - - pos = skb_put(skb, ie_len); - ieee80211_ie_build_eht_cap(NULL, pos, he_cap, eht_cap, pos + ie_len, - false); - - return 0; + return ieee80211_put_eht_cap(skb, sdata, sband, NULL); } int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index de3d0e08ca2a..dd97f402a624 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1038,41 +1038,6 @@ static bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, return mu_mimo_owner; } -static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct ieee80211_supported_band *sband, - const struct ieee80211_conn_settings *conn) -{ - u8 *pos, *pre_eht_pos; - const struct ieee80211_sta_he_cap *he_cap; - const struct ieee80211_sta_eht_cap *eht_cap; - u8 eht_cap_size; - - he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); - eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); - - /* - * EHT capabilities element is only added if the HE capabilities element - * was added so assume that 'he_cap' is valid and don't check it. - */ - if (WARN_ON(!he_cap || !eht_cap)) - return; - - eht_cap_size = - 2 + 1 + sizeof(eht_cap->eht_cap_elem) + - ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, - &eht_cap->eht_cap_elem, - false) + - ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], - eht_cap->eht_cap_elem.phy_cap_info); - pos = skb_put(skb, eht_cap_size); - pre_eht_pos = pos; - pos = ieee80211_ie_build_eht_cap(conn, pos, he_cap, eht_cap, - pos + eht_cap_size, false); - /* trim excess if any */ - skb_trim(skb, skb->len - (pre_eht_pos + eht_cap_size - pos)); -} - static void ieee80211_assoc_add_rates(struct sk_buff *skb, enum nl80211_chan_width width, struct ieee80211_supported_band *sband, @@ -1393,8 +1358,8 @@ static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, present_elems = NULL; if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_EHT) - ieee80211_add_eht_ie(sdata, skb, sband, - &assoc_data->link[link_id].conn); + ieee80211_put_eht_cap(skb, sdata, sband, + &assoc_data->link[link_id].conn); if (sband->band == NL80211_BAND_S1GHZ) { ieee80211_add_aid_request_ie(sdata, skb); diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 3f9c2b2771c6..42d9c06cbb84 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -575,22 +575,10 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, } /* build the EHT-cap from sband */ - if (he_cap && eht_cap && - (action_code == WLAN_TDLS_SETUP_REQUEST || - action_code == WLAN_TDLS_SETUP_RESPONSE || - action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) { - u8 cap_size; - - cap_size = - 2 + 1 + sizeof(eht_cap->eht_cap_elem) + - ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, - &eht_cap->eht_cap_elem, false) + - ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], - eht_cap->eht_cap_elem.phy_cap_info); - pos = skb_put(skb, cap_size); - ieee80211_ie_build_eht_cap(NULL, pos, he_cap, eht_cap, - pos + cap_size, false); - } + if (action_code == WLAN_TDLS_SETUP_REQUEST || + action_code == WLAN_TDLS_SETUP_RESPONSE || + action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) + ieee80211_put_eht_cap(skb, sdata, sband, NULL); /* add any remaining IEs */ if (extra_ies_len) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 4dcb62e9d4c6..627bd5a8bda5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1199,8 +1199,6 @@ static int ieee80211_put_preq_ies_band(struct sk_buff *skb, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; - const struct ieee80211_sta_he_cap *he_cap; - const struct ieee80211_sta_eht_cap *eht_cap; int i, err; size_t noffset; u32 rate_flags; @@ -1359,22 +1357,12 @@ static int ieee80211_put_preq_ies_band(struct sk_buff *skb, return err; } - he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); - eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); - - if (eht_cap && - cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), + if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), IEEE80211_CHAN_NO_HE | IEEE80211_CHAN_NO_EHT)) { - u8 *pos = skb_tail_pointer(skb); - u8 *end = pos + skb_tailroom(skb); - - pos = ieee80211_ie_build_eht_cap(NULL, pos, he_cap, eht_cap, - end, - sdata->vif.type == NL80211_IFTYPE_AP); - if (!pos) - return -ENOBUFS; - skb_put(skb, pos - skb_tail_pointer(skb)); + err = ieee80211_put_eht_cap(skb, sdata, sband, NULL); + if (err) + return err; } err = ieee80211_put_he_6ghz_cap(skb, sdata, IEEE80211_SMPS_OFF); @@ -4175,7 +4163,7 @@ u16 ieee80211_encode_usf(int listen_interval) return (u16) listen_interval; } -/* this may return more than ieee80211_ie_build_eht_cap() will need */ +/* this may return more than ieee80211_put_eht_cap() will need */ u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata) { const struct ieee80211_sta_he_cap *he_cap; @@ -4205,25 +4193,28 @@ u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata) return 0; } -u8 *ieee80211_ie_build_eht_cap(const struct ieee80211_conn_settings *conn, - u8 *pos, - const struct ieee80211_sta_he_cap *he_cap, - const struct ieee80211_sta_eht_cap *eht_cap, - u8 *end, bool for_ap) +int ieee80211_put_eht_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + const struct ieee80211_supported_band *sband, + const struct ieee80211_conn_settings *conn) { - struct ieee80211_eht_cap_elem_fixed fixed, *out; + const struct ieee80211_sta_he_cap *he_cap = + ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); + const struct ieee80211_sta_eht_cap *eht_cap = + ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); + bool for_ap = sdata->vif.type == NL80211_IFTYPE_AP; + struct ieee80211_eht_cap_elem_fixed fixed; struct ieee80211_he_cap_elem he; u8 mcs_nss_len, ppet_len; u8 orig_mcs_nss_len; u8 ie_len; - u8 *orig_pos = pos; if (!conn) conn = &ieee80211_conn_settings_unlimited; /* Make sure we have place for the IE */ if (!he_cap || !eht_cap) - return orig_pos; + return 0; orig_mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, &eht_cap->eht_cap_elem, @@ -4266,16 +4257,13 @@ u8 *ieee80211_ie_build_eht_cap(const struct ieee80211_conn_settings *conn, fixed.phy_cap_info); ie_len = 2 + 1 + sizeof(eht_cap->eht_cap_elem) + mcs_nss_len + ppet_len; - if ((end - pos) < ie_len) - return orig_pos; + if (skb_tailroom(skb) < ie_len) + return -ENOBUFS; - *pos++ = WLAN_EID_EXTENSION; - *pos++ = ie_len - 2; - *pos++ = WLAN_EID_EXT_EHT_CAPABILITY; - - out = (void *)pos; - *out = fixed; - pos += sizeof(*out); + skb_put_u8(skb, WLAN_EID_EXTENSION); + skb_put_u8(skb, ie_len - 2); + skb_put_u8(skb, WLAN_EID_EXT_EHT_CAPABILITY); + skb_put_data(skb, &fixed, sizeof(fixed)); if (mcs_nss_len == 4 && orig_mcs_nss_len != 4) { /* @@ -4284,21 +4272,18 @@ u8 *ieee80211_ie_build_eht_cap(const struct ieee80211_conn_settings *conn, * the groups 0-7, 8-9, 10-11, 12-13 rather than just 0-9, * 10-11, 12-13. Thus, use 0-9 for 0-7 and 8-9. */ - *pos++ = eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss; - *pos++ = eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss; - *pos++ = eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs11_max_nss; - *pos++ = eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss; + skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss); + skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss); + skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs11_max_nss); + skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss); } else { - memcpy(pos, &eht_cap->eht_mcs_nss_supp, mcs_nss_len); - pos += mcs_nss_len; + skb_put_data(skb, &eht_cap->eht_mcs_nss_supp, mcs_nss_len); } - if (ppet_len) { - memcpy(pos, &eht_cap->eht_ppe_thres, ppet_len); - pos += ppet_len; - } + if (ppet_len) + skb_put_data(skb, &eht_cap->eht_ppe_thres, ppet_len); - return pos; + return 0; } const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode) From 55167a3eed53ce8b40d5bf2a3e0be4e15d2eba57 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Jan 2024 20:35:45 +0100 Subject: [PATCH 261/378] wifi: mac80211: allow CSA to same channel This could be used e.g. for temporarily sending quiet (mode=1 in CSA/ECSA), or updating bandwidth. This is also useful for testing, since it's something that an AP may do and the client needs to be prepared. Simply allow it. Link: https://msgid.link/20240129203544.ef7258d5790d.Idafe22e41621757458d4960659b9621853f7104d@changeid Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 2ddaf0037952..17b445491881 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3876,10 +3876,6 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (sdata->wdev.cac_started) return -EBUSY; - if (cfg80211_chandef_identical(&chanreq.oper, - &sdata->vif.bss_conf.chanreq.oper)) - return -EINVAL; - if (chanreq.oper.punctured && !sdata->vif.bss_conf.eht_support) return -EINVAL; From 91cdcbbcde1092da5225cfb05c88961658a34353 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 31 Jan 2024 16:49:11 +0100 Subject: [PATCH 262/378] wifi: mac80211: clarify vif handling in TX dequeue The vif pointer at least looks like it can actually be NULL in some cases such as the monitor-mode vif, causing static checkers to complain with the immediate derefence. In these cases the sta pointer will also be NULL, but clarify it in the code anyway. Link: https://msgid.link/20240131164910.60066625a239.Idfb6a5a9876f9f631eae760055e1c4018259a971@changeid Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f57f7963ca37..098b32947c2b 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -3953,7 +3953,8 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, ieee80211_free_txskb(&local->hw, skb); goto begin; } else { - vif = NULL; + info->control.vif = NULL; + return skb; } break; case NL80211_IFTYPE_AP_VLAN: From 03145a1d5d38eb1fe79d3bc01d37bf4f28076796 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 13:45:55 +0100 Subject: [PATCH 263/378] wifi: mac80211: add missing kernel-doc for fast_tx_check This was added earlier, add kernel-doc for it. Link: https://msgid.link/20240206134555.6354b0ac8610.Ib90d3651834c556b73697388f59bd396a1f6f9b0@changeid Signed-off-by: Johannes Berg --- net/mac80211/mesh.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index ad8469293d71..d913ce7ba72e 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2008, 2009 open80211s Ltd. - * Copyright (C) 2023 Intel Corporation + * Copyright (C) 2023-2024 Intel Corporation * Authors: Luis Carlos Cobo * Javier Cardona */ @@ -94,6 +94,7 @@ enum mesh_deferred_task_flags { * @is_root: the destination station of this path is a root node * @is_gate: the destination station of this path is a mesh gate * @path_change_count: the number of path changes to destination + * @fast_tx_check: timestamp of last fast-xmit enable attempt * * * The dst address is unique in the mesh path table. Since the mesh_path is From 84d3776ef71df5ef3631021536d6b04e2383ebc8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 13:45:56 +0100 Subject: [PATCH 264/378] wifi: mac80211_hwsim: add missing kernel-doc Some kernel-doc is missing here, add it. Link: https://msgid.link/20240206134555.eb95c1dfc1f0.Ibaf8b3249d9de59358bf6503fe4a186d9ac6544d@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.h b/drivers/net/wireless/virtual/mac80211_hwsim.h index 4676cdaf4cfd..21b1afd83dc1 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim.h @@ -3,7 +3,7 @@ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 * Copyright (c) 2008, Jouni Malinen * Copyright (c) 2011, Javier Lopez - * Copyright (C) 2020, 2022-2023 Intel Corporation + * Copyright (C) 2020, 2022-2024 Intel Corporation */ #ifndef __MAC80211_HWSIM_H @@ -84,6 +84,8 @@ enum hwsim_tx_control_flags { * @HWSIM_CMD_START_PMSR: request to start peer measurement with the * %HWSIM_ATTR_PMSR_REQUEST. Result will be sent back asynchronously * with %HWSIM_CMD_REPORT_PMSR. + * @HWSIM_CMD_ABORT_PMSR: Abort previously started peer measurement. + * @HWSIM_CMD_REPORT_PMSR: Report peer measurement data. * @__HWSIM_CMD_MAX: enum limit */ enum hwsim_commands { @@ -298,6 +300,7 @@ enum hwsim_vqs { * Information about a receiving or transmitting bitrate * that can be mapped to struct rate_info * + * @__HWSIM_RATE_INFO_ATTR_INVALID: reserved, netlink attribute 0 is invalid * @HWSIM_RATE_INFO_ATTR_FLAGS: bitflag of flags from &enum rate_info_flags * @HWSIM_RATE_INFO_ATTR_MCS: mcs index if struct describes an HT/VHT/HE rate * @HWSIM_RATE_INFO_ATTR_LEGACY: bitrate in 100kbit/s for 802.11abg From 37c37096ad80bfa38ab427f33f58124e043fa2fe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 7 Feb 2024 11:59:20 +0100 Subject: [PATCH 265/378] wifi: mac80211: don't use sband->band early Some drivers may (notably mt76 does) not set up sband->band before registering, and cfg80211 will fill it in later. But since the HT/HE capability check mac80211 required it to be initialized already, otherwise failing things. This really isn't necessary though since the code is iterating the list of bands, and has the 'band' variable available. Fix it to not require the sband->band to be initialized already. Fixes: f04d2c247e04 ("wifi: mac80211: disallow drivers with HT wider than HE") Reported-by: Bert Karwatzki Debugged-by: Bert Karwatzki Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218466 Link: https://msgid.link/20240207115920.43cbedffb5c3.I4968e12275a3f95926e3f3ccae81e50f23fe4d4d@changeid Signed-off-by: Johannes Berg --- net/mac80211/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 879abe216a3e..4eaea0a9975b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1259,7 +1259,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) supp_he = supp_he || iftd->he_cap.has_he; supp_eht = supp_eht || iftd->eht_cap.has_eht; - if (sband->band == NL80211_BAND_2GHZ) + if (band == NL80211_BAND_2GHZ) he_40_mhz_cap = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; else From f29a8be886f5dd12b97624a87d6c46e2cf8d1821 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 6 Feb 2024 16:08:52 +0300 Subject: [PATCH 266/378] wifi: iwlwifi: return negative -EINVAL instead of positive EINVAL The '-' character is missing in -EINVAL. Fixes: fc7214c3c986 ("wifi: iwlwifi: read DSM functions from UEFI") Signed-off-by: Dan Carpenter Link: https://msgid.link/f0391316-ab30-4664-96ac-03445ab2aeba@moroto.mountain Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index fe6d0141cd5b..3c4c99eed110 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -679,7 +679,7 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, u32 *value) { struct uefi_cnv_var_general_cfg *data; - int ret = EINVAL; + int ret = -EINVAL; /* Not supported function index */ if (func >= DSM_FUNC_NUM_FUNCS || func == 5) From 68de13028b94572fc570b7eb1e0e2de1d751fe7e Mon Sep 17 00:00:00 2001 From: Michael-CY Lee Date: Fri, 22 Dec 2023 09:09:13 +0800 Subject: [PATCH 267/378] wifi: cfg80211: Add utility for converting op_class into chandef This utility is used in STA CSA handling. The op_class in the ECSA Element can be converted into chandef. Co-developed-by: Money Wang Signed-off-by: Michael-CY Lee Link: https://msgid.link/20231222010914.6521-2-michael-cy.lee@mediatek.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 13 ++++++++ net/wireless/util.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index cb5e34d640cd..e27ed2307cdb 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -8774,6 +8774,19 @@ void cfg80211_ch_switch_started_notify(struct net_device *dev, bool ieee80211_operating_class_to_band(u8 operating_class, enum nl80211_band *band); +/** + * ieee80211_operating_class_to_chandef - convert operating class to chandef + * + * @operating_class: the operating class to convert + * @chan: the ieee80211_channel to convert + * @chandef: a pointer to the resulting chandef + * + * Returns %true if the conversion was successful, %false otherwise. + */ +bool ieee80211_operating_class_to_chandef(u8 operating_class, + struct ieee80211_channel *chan, + struct cfg80211_chan_def *chandef); + /** * ieee80211_chandef_to_operating_class - convert chandef to operation class * diff --git a/net/wireless/util.c b/net/wireless/util.c index d1ce3bee2797..379f742fd741 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -2073,6 +2073,82 @@ bool ieee80211_operating_class_to_band(u8 operating_class, } EXPORT_SYMBOL(ieee80211_operating_class_to_band); +bool ieee80211_operating_class_to_chandef(u8 operating_class, + struct ieee80211_channel *chan, + struct cfg80211_chan_def *chandef) +{ + u32 control_freq, offset = 0; + enum nl80211_band band; + + if (!ieee80211_operating_class_to_band(operating_class, &band) || + !chan || band != chan->band) + return false; + + control_freq = chan->center_freq; + chandef->chan = chan; + + if (control_freq >= 5955) + offset = control_freq - 5955; + else if (control_freq >= 5745) + offset = control_freq - 5745; + else if (control_freq >= 5180) + offset = control_freq - 5180; + offset /= 20; + + switch (operating_class) { + case 81: /* 2 GHz band; 20 MHz; channels 1..13 */ + case 82: /* 2 GHz band; 20 MHz; channel 14 */ + case 115: /* 5 GHz band; 20 MHz; channels 36,40,44,48 */ + case 118: /* 5 GHz band; 20 MHz; channels 52,56,60,64 */ + case 121: /* 5 GHz band; 20 MHz; channels 100..144 */ + case 124: /* 5 GHz band; 20 MHz; channels 149,153,157,161 */ + case 125: /* 5 GHz band; 20 MHz; channels 149..177 */ + case 131: /* 6 GHz band; 20 MHz; channels 1..233*/ + case 136: /* 6 GHz band; 20 MHz; channel 2 */ + chandef->center_freq1 = control_freq; + chandef->width = NL80211_CHAN_WIDTH_20; + return true; + case 83: /* 2 GHz band; 40 MHz; channels 1..9 */ + case 116: /* 5 GHz band; 40 MHz; channels 36,44 */ + case 119: /* 5 GHz band; 40 MHz; channels 52,60 */ + case 122: /* 5 GHz band; 40 MHz; channels 100,108,116,124,132,140 */ + case 126: /* 5 GHz band; 40 MHz; channels 149,157,165,173 */ + chandef->center_freq1 = control_freq + 10; + chandef->width = NL80211_CHAN_WIDTH_40; + return true; + case 84: /* 2 GHz band; 40 MHz; channels 5..13 */ + case 117: /* 5 GHz band; 40 MHz; channels 40,48 */ + case 120: /* 5 GHz band; 40 MHz; channels 56,64 */ + case 123: /* 5 GHz band; 40 MHz; channels 104,112,120,128,136,144 */ + case 127: /* 5 GHz band; 40 MHz; channels 153,161,169,177 */ + chandef->center_freq1 = control_freq - 10; + chandef->width = NL80211_CHAN_WIDTH_40; + return true; + case 132: /* 6 GHz band; 40 MHz; channels 1,5,..,229*/ + chandef->center_freq1 = control_freq + 10 - (offset & 1) * 20; + chandef->width = NL80211_CHAN_WIDTH_40; + return true; + case 128: /* 5 GHz band; 80 MHz; channels 36..64,100..144,149..177 */ + case 133: /* 6 GHz band; 80 MHz; channels 1,5,..,229 */ + chandef->center_freq1 = control_freq + 30 - (offset & 3) * 20; + chandef->width = NL80211_CHAN_WIDTH_80; + return true; + case 129: /* 5 GHz band; 160 MHz; channels 36..64,100..144,149..177 */ + case 134: /* 6 GHz band; 160 MHz; channels 1,5,..,229 */ + chandef->center_freq1 = control_freq + 70 - (offset & 7) * 20; + chandef->width = NL80211_CHAN_WIDTH_160; + return true; + case 130: /* 5 GHz band; 80+80 MHz; channels 36..64,100..144,149..177 */ + case 135: /* 6 GHz band; 80+80 MHz; channels 1,5,..,229 */ + /* The center_freq2 of 80+80 MHz is unknown */ + case 137: /* 6 GHz band; 320 MHz; channels 1,5,..,229 */ + /* 320-1 or 320-2 channelization is unknown */ + default: + return false; + } +} +EXPORT_SYMBOL(ieee80211_operating_class_to_chandef); + bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef, u8 *op_class) { From 21c3f8f95554feff9bed15703e89adbe582e0383 Mon Sep 17 00:00:00 2001 From: Michael-CY Lee Date: Fri, 22 Dec 2023 09:09:14 +0800 Subject: [PATCH 268/378] wifi: mac80211: refactor STA CSA parsing flows The new Wi-Fi Standard (IEEE Std 802.11-2020 9.4.2.160) specifies that the Wide Bandwidth Channel Switch (WBCS) Element subfields have the same definitions as VHT operation information if the operating band is not S1G. The problem comes when the BSS is in 6 GHz band, the STA parses the WBCS Element by ieee80211_chandef_vht_oper(), which checks the capabilities for HT/VHT mode, not HE/EHT mode. This patch refactors STA CSA parsing flow so that the corresponding capabilities can be checked. Also, it adds the way to use op_class in ECSA Element to build a new chandef. In summary, the new steps for STA to handle CSA event are: 1. build the new chandef from one of the CSA-related (Sub)Elements in following order, - Bandwidth Indication (Sub)Element - Wide Bandwidth Channel Switch (Sub)Element - Operating class in Extended Channel Switch Announcement Element - Channel Switch Announcement Element 2. convert the new chandef into operation information according to the operating band in order to check if the new chandef fits STA's capabilities. 3. downgrade the bandwidth until current bandwidth is not disabled. Co-developed-by: Money Wang Signed-off-by: Michael-CY Lee Link: https://msgid.link/20231222010914.6521-3-michael-cy.lee@mediatek.com [rebase on top of the changes with struct ieee80211_conn_settings, prefer leXY_encode_bits()] Signed-off-by: Johannes Berg --- net/mac80211/spectmgmt.c | 306 ++++++++++++++++++++++++++++++++------- 1 file changed, 252 insertions(+), 54 deletions(-) diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 2b0bf2a1a877..327c74e296e2 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -19,6 +19,205 @@ #include "sta_info.h" #include "wme.h" +static bool +wbcs_elem_to_chandef(const struct ieee80211_wide_bw_chansw_ie *wbcs_elem, + struct cfg80211_chan_def *chandef) +{ + u8 ccfs0 = wbcs_elem->new_center_freq_seg0; + u8 ccfs1 = wbcs_elem->new_center_freq_seg1; + u32 cf0 = ieee80211_channel_to_frequency(ccfs0, chandef->chan->band); + u32 cf1 = ieee80211_channel_to_frequency(ccfs1, chandef->chan->band); + + switch (wbcs_elem->new_channel_width) { + case IEEE80211_VHT_CHANWIDTH_160MHZ: + /* deprecated encoding */ + chandef->width = NL80211_CHAN_WIDTH_160; + chandef->center_freq1 = cf0; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + /* deprecated encoding */ + chandef->width = NL80211_CHAN_WIDTH_80P80; + chandef->center_freq1 = cf0; + chandef->center_freq2 = cf1; + break; + case IEEE80211_VHT_CHANWIDTH_80MHZ: + chandef->width = NL80211_CHAN_WIDTH_80; + chandef->center_freq1 = cf0; + + if (ccfs1) { + u8 diff = abs(ccfs0 - ccfs1); + + if (diff == 8) { + chandef->width = NL80211_CHAN_WIDTH_160; + chandef->center_freq1 = cf1; + } else if (diff > 8) { + chandef->width = NL80211_CHAN_WIDTH_80P80; + chandef->center_freq2 = cf1; + } + } + break; + case IEEE80211_VHT_CHANWIDTH_USE_HT: + default: + /* If the WBCS Element is present, new channel bandwidth is + * at least 40 MHz. + */ + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 = cf0; + break; + } + + return cfg80211_chandef_valid(chandef); +} + +static void +validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data *sdata, + struct ieee80211_conn_settings *conn, + u32 vht_cap_info, + struct cfg80211_chan_def *chandef) +{ + u32 control_freq, center_freq1, center_freq2; + enum nl80211_chan_width chan_width; + struct ieee80211_ht_operation ht_oper; + struct ieee80211_vht_operation vht_oper; + + if (conn->mode < IEEE80211_CONN_MODE_HT || + conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) { + chandef->chan = NULL; + return; + } + + control_freq = chandef->chan->center_freq; + center_freq1 = chandef->center_freq1; + center_freq2 = chandef->center_freq2; + chan_width = chandef->width; + + ht_oper.primary_chan = ieee80211_frequency_to_channel(control_freq); + if (control_freq != center_freq1) + ht_oper.ht_param = control_freq > center_freq1 ? + IEEE80211_HT_PARAM_CHA_SEC_BELOW : + IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + else + ht_oper.ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; + + ieee80211_chandef_ht_oper(&ht_oper, chandef); + + if (conn->mode < IEEE80211_CONN_MODE_VHT) + return; + + vht_oper.center_freq_seg0_idx = + ieee80211_frequency_to_channel(center_freq1); + vht_oper.center_freq_seg1_idx = center_freq2 ? + ieee80211_frequency_to_channel(center_freq2) : 0; + + switch (chan_width) { + case NL80211_CHAN_WIDTH_320: + WARN_ON(1); + break; + case NL80211_CHAN_WIDTH_160: + vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; + vht_oper.center_freq_seg1_idx = vht_oper.center_freq_seg0_idx; + vht_oper.center_freq_seg0_idx += + control_freq < center_freq1 ? -8 : 8; + break; + case NL80211_CHAN_WIDTH_80P80: + vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + case NL80211_CHAN_WIDTH_80: + vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + default: + vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + ht_oper.operation_mode = + le16_encode_bits(vht_oper.center_freq_seg1_idx, + IEEE80211_HT_OP_MODE_CCFS2_MASK); + + if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info, + &vht_oper, &ht_oper, chandef)) + chandef->chan = NULL; +} + +static void +validate_chandef_by_6ghz_he_eht_oper(struct ieee80211_sub_if_data *sdata, + struct ieee80211_conn_settings *conn, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_local *local = sdata->local; + u32 control_freq, center_freq1, center_freq2; + enum nl80211_chan_width chan_width; + struct { + struct ieee80211_he_operation _oper; + struct ieee80211_he_6ghz_oper _6ghz_oper; + } __packed he; + struct { + struct ieee80211_eht_operation _oper; + struct ieee80211_eht_operation_info _oper_info; + } __packed eht; + + if (conn->mode < IEEE80211_CONN_MODE_HE) { + chandef->chan = NULL; + return; + } + + control_freq = chandef->chan->center_freq; + center_freq1 = chandef->center_freq1; + center_freq2 = chandef->center_freq2; + chan_width = chandef->width; + + he._oper.he_oper_params = + le32_encode_bits(1, IEEE80211_HE_OPERATION_6GHZ_OP_INFO); + he._6ghz_oper.primary = + ieee80211_frequency_to_channel(control_freq); + he._6ghz_oper.ccfs0 = ieee80211_frequency_to_channel(center_freq1); + he._6ghz_oper.ccfs1 = center_freq2 ? + ieee80211_frequency_to_channel(center_freq2) : 0; + + switch (chan_width) { + case NL80211_CHAN_WIDTH_320: + he._6ghz_oper.ccfs1 = he._6ghz_oper.ccfs0; + he._6ghz_oper.ccfs0 += control_freq < center_freq1 ? -16 : 16; + he._6ghz_oper.control = IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ; + break; + case NL80211_CHAN_WIDTH_160: + he._6ghz_oper.ccfs1 = he._6ghz_oper.ccfs0; + he._6ghz_oper.ccfs0 += control_freq < center_freq1 ? -8 : 8; + fallthrough; + case NL80211_CHAN_WIDTH_80P80: + he._6ghz_oper.control = + IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ; + break; + case NL80211_CHAN_WIDTH_80: + he._6ghz_oper.control = + IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ; + break; + case NL80211_CHAN_WIDTH_40: + he._6ghz_oper.control = + IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ; + break; + default: + he._6ghz_oper.control = + IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ; + break; + } + + if (conn->mode < IEEE80211_CONN_MODE_EHT) { + if (!ieee80211_chandef_he_6ghz_oper(local, &he._oper, + NULL, chandef)) + chandef->chan = NULL; + } else { + eht._oper.params = IEEE80211_EHT_OPER_INFO_PRESENT; + eht._oper_info.control = he._6ghz_oper.control; + eht._oper_info.ccfs0 = he._6ghz_oper.ccfs0; + eht._oper_info.ccfs1 = he._6ghz_oper.ccfs1; + + if (!ieee80211_chandef_he_6ghz_oper(local, &he._oper, + &eht._oper, chandef)) + chandef->chan = NULL; + } +} + int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems, enum nl80211_band current_band, @@ -29,12 +228,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, { enum nl80211_band new_band = current_band; int new_freq; - u8 new_chan_no; + u8 new_chan_no = 0, new_op_class = 0; struct ieee80211_channel *new_chan; - struct cfg80211_chan_def new_vht_chandef = {}; + struct cfg80211_chan_def new_chandef = {}; const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; const struct ieee80211_bandwidth_indication *bwi; + const struct ieee80211_ext_chansw_ie *ext_chansw_elem; int secondary_channel_offset = -1; memset(csa_ie, 0, sizeof(*csa_ie)); @@ -42,6 +242,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, sec_chan_offs = elems->sec_chan_offs; wide_bw_chansw_ie = elems->wide_bw_chansw_ie; bwi = elems->bandwidth_indication; + ext_chansw_elem = elems->ext_chansw_ie; if (conn->mode < IEEE80211_CONN_MODE_HT || conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) { @@ -52,26 +253,30 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, if (conn->mode < IEEE80211_CONN_MODE_VHT) wide_bw_chansw_ie = NULL; - if (elems->ext_chansw_ie) { - if (!ieee80211_operating_class_to_band( - elems->ext_chansw_ie->new_operating_class, - &new_band)) { - sdata_info(sdata, - "cannot understand ECSA IE operating class, %d, ignoring\n", - elems->ext_chansw_ie->new_operating_class); + if (ext_chansw_elem) { + new_op_class = ext_chansw_elem->new_operating_class; + + if (!ieee80211_operating_class_to_band(new_op_class, &new_band)) { + new_op_class = 0; + sdata_info(sdata, "cannot understand ECSA IE operating class, %d, ignoring\n", + ext_chansw_elem->new_operating_class); + } else { + new_chan_no = ext_chansw_elem->new_ch_num; + csa_ie->count = ext_chansw_elem->count; + csa_ie->mode = ext_chansw_elem->mode; } - new_chan_no = elems->ext_chansw_ie->new_ch_num; - csa_ie->count = elems->ext_chansw_ie->count; - csa_ie->mode = elems->ext_chansw_ie->mode; - } else if (elems->ch_switch_ie) { + } + + if (!new_op_class && elems->ch_switch_ie) { new_chan_no = elems->ch_switch_ie->new_ch_num; csa_ie->count = elems->ch_switch_ie->count; csa_ie->mode = elems->ch_switch_ie->mode; - } else { - /* nothing here we understand */ - return 1; } + /* nothing here we understand */ + if (!new_chan_no) + return 1; + /* Mesh Channel Switch Parameters Element */ if (elems->mesh_chansw_params_ie) { csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl; @@ -135,55 +340,48 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, break; } + /* parse one of the Elements to build a new chandef */ + memset(&new_chandef, 0, sizeof(new_chandef)); + new_chandef.chan = new_chan; if (bwi) { /* start with the CSA one */ - new_vht_chandef = csa_ie->chanreq.oper; + new_chandef = csa_ie->chanreq.oper; /* and update the width accordingly */ - ieee80211_chandef_eht_oper(&bwi->info, &new_vht_chandef); - } else if (wide_bw_chansw_ie) { - u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1; - struct ieee80211_vht_operation vht_oper = { - .chan_width = - wide_bw_chansw_ie->new_channel_width, - .center_freq_seg0_idx = - wide_bw_chansw_ie->new_center_freq_seg0, - .center_freq_seg1_idx = new_seg1, - /* .basic_mcs_set doesn't matter */ - }; - struct ieee80211_ht_operation ht_oper = { - .operation_mode = - cpu_to_le16(new_seg1 << - IEEE80211_HT_OP_MODE_CCFS2_SHIFT), - }; - - /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT, - * to the previously parsed chandef - */ - new_vht_chandef = csa_ie->chanreq.oper; - - /* ignore if parsing fails */ - if (!ieee80211_chandef_vht_oper(&sdata->local->hw, - vht_cap_info, - &vht_oper, &ht_oper, - &new_vht_chandef)) - new_vht_chandef.chan = NULL; - - if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160 && - (new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80 || - new_vht_chandef.width == NL80211_CHAN_WIDTH_160)) - ieee80211_chandef_downgrade(&new_vht_chandef, NULL); + ieee80211_chandef_eht_oper(&bwi->info, &new_chandef); + } else if (!wide_bw_chansw_ie || !wbcs_elem_to_chandef(wide_bw_chansw_ie, + &new_chandef)) { + if (!ieee80211_operating_class_to_chandef(new_op_class, new_chan, + &new_chandef)) + new_chandef = csa_ie->chanreq.oper; } - /* if VHT data is there validate & use it */ - if (new_vht_chandef.chan) { - if (!cfg80211_chandef_compatible(&new_vht_chandef, + /* check if the new chandef fits the capabilities */ + if (new_band == NL80211_BAND_6GHZ) + validate_chandef_by_6ghz_he_eht_oper(sdata, conn, &new_chandef); + else + validate_chandef_by_ht_vht_oper(sdata, conn, vht_cap_info, + &new_chandef); + + /* if data is there validate the bandwidth & use it */ + if (new_chandef.chan) { + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_320 && + new_chandef.width == NL80211_CHAN_WIDTH_320) + ieee80211_chandef_downgrade(&new_chandef, NULL); + + if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160 && + (new_chandef.width == NL80211_CHAN_WIDTH_80P80 || + new_chandef.width == NL80211_CHAN_WIDTH_160)) + ieee80211_chandef_downgrade(&new_chandef, NULL); + + if (!cfg80211_chandef_compatible(&new_chandef, &csa_ie->chanreq.oper)) { sdata_info(sdata, "BSS %pM: CSA has inconsistent channel data, disconnecting\n", bssid); return -EINVAL; } - csa_ie->chanreq.oper = new_vht_chandef; + + csa_ie->chanreq.oper = new_chandef; } if (elems->max_channel_switch_time) From 4ace04c0bdbde3b028ec0a5a3be2471cdb1efb67 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 30 Jan 2024 19:39:14 +0530 Subject: [PATCH 269/378] wifi: cfg80211: send link id in channel_switch ops Currently, during channel switch, no link id information is passed down. In order to support channel switch during Multi Link Operation, it is required to pass link id as well. Add changes to pass link id in the channel_switch cfg80211_ops. Signed-off-by: Aditya Kumar Singh Link: https://msgid.link/20240130140918.1172387-2-quic_adisi@quicinc.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 +++ net/wireless/nl80211.c | 1 + net/wireless/trace.h | 7 +++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e27ed2307cdb..d4c83ea3213d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1543,6 +1543,8 @@ struct cfg80211_ap_update { * @radar_required: whether radar detection is required on the new channel * @block_tx: whether transmissions should be blocked while changing * @count: number of beacons until switch + * @link_id: defines the link on which channel switch is expected during + * MLO. 0 in case of non-MLO. */ struct cfg80211_csa_settings { struct cfg80211_chan_def chandef; @@ -1555,6 +1557,7 @@ struct cfg80211_csa_settings { bool radar_required; bool block_tx; u8 count; + u8 link_id; }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b533412ad1e0..e1106ae35e21 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10222,6 +10222,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) params.block_tx = true; + params.link_id = link_id; err = rdev_channel_switch(rdev, dev, ¶ms); free: diff --git a/net/wireless/trace.h b/net/wireless/trace.h index ae5e585b6863..194ea2471717 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2332,6 +2332,7 @@ TRACE_EVENT(rdev_channel_switch, __field(u8, count) __dynamic_array(u16, bcn_ofs, params->n_counter_offsets_beacon) __dynamic_array(u16, pres_ofs, params->n_counter_offsets_presp) + __field(u8, link_id) ), TP_fast_assign( WIPHY_ASSIGN; @@ -2349,11 +2350,13 @@ TRACE_EVENT(rdev_channel_switch, memcpy(__get_dynamic_array(pres_ofs), params->counter_offsets_presp, params->n_counter_offsets_presp * sizeof(u16)); + __entry->link_id = params->link_id; ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT - ", block_tx: %d, count: %u, radar_required: %d", + ", block_tx: %d, count: %u, radar_required: %d, link_id: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG, - __entry->block_tx, __entry->count, __entry->radar_required) + __entry->block_tx, __entry->count, __entry->radar_required, + __entry->link_id) ); TRACE_EVENT(rdev_set_qos_map, From 480e7048aa0bbf0a79a976cdfa0195fd157da902 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 30 Jan 2024 19:39:15 +0530 Subject: [PATCH 270/378] wifi: mac80211: update beacon counters per link basis Currently, function to update beacon counter uses deflink to fetch the beacon and then update the counter. However, with MLO, there is a need to update the counter for the beacon in a particular link. Add support to use link_id in order to fetch the beacon from a particular link data during beacon update counter. Signed-off-by: Aditya Kumar Singh Link: https://msgid.link/20240130140918.1172387-3-quic_adisi@quicinc.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/mac.c | 2 +- drivers/net/wireless/ath/ath11k/mac.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 2 +- include/net/mac80211.h | 4 +++- net/mac80211/tx.c | 14 +++++++++++--- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index fc503db2fd8e..b80220406104 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2035,7 +2035,7 @@ static void ath10k_mac_vif_ap_csa_count_down(struct ath10k_vif *arvif) return; if (!ieee80211_beacon_cntdwn_is_complete(vif)) { - ieee80211_beacon_update_cntdwn(vif); + ieee80211_beacon_update_cntdwn(vif, 0); ret = ath10k_mac_setup_bcn_tmpl(arvif); if (ret) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index bbf4d1f4d310..f7cab50bdfd1 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1586,7 +1586,7 @@ void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif) arvif->bcca_zero_sent = false; if (vif->bss_conf.color_change_active) - ieee80211_beacon_update_cntdwn(vif); + ieee80211_beacon_update_cntdwn(vif, 0); ath11k_mac_setup_bcn_tmpl(arvif); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 61269c7b1934..3dea06b28472 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1467,7 +1467,7 @@ static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm, mvmvif->csa_countdown = true; if (!ieee80211_beacon_cntdwn_is_complete(csa_vif)) { - int c = ieee80211_beacon_update_cntdwn(csa_vif); + int c = ieee80211_beacon_update_cntdwn(csa_vif, 0); iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif, &csa_vif->bss_conf); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 54aa4a06c878..8acee7ce3aa9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5526,6 +5526,7 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, /** * ieee80211_beacon_update_cntdwn - request mac80211 to decrement the beacon countdown * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @link_id: valid link_id during MLO or 0 for non-MLO * * The beacon counter should be updated after each beacon transmission. * This function is called implicitly when @@ -5535,7 +5536,8 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, * * Return: new countdown value */ -u8 ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif); +u8 ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif, + unsigned int link_id); /** * ieee80211_beacon_set_cntdwn - request mac80211 to set beacon countdown diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 098b32947c2b..876de3ba98ba 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -5027,16 +5027,24 @@ static u8 __ieee80211_beacon_update_cntdwn(struct beacon_data *beacon) return beacon->cntdwn_current_counter; } -u8 ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif) +u8 ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif, unsigned int link_id) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_link_data *link; struct beacon_data *beacon = NULL; u8 count = 0; + if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS)) + return 0; + rcu_read_lock(); + link = rcu_dereference(sdata->link[link_id]); + if (!link) + goto unlock; + if (sdata->vif.type == NL80211_IFTYPE_AP) - beacon = rcu_dereference(sdata->deflink.u.ap.beacon); + beacon = rcu_dereference(link->u.ap.beacon); else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) beacon = rcu_dereference(sdata->u.ibss.presp); else if (ieee80211_vif_is_mesh(&sdata->vif)) @@ -5277,7 +5285,7 @@ ieee80211_beacon_get_ap(struct ieee80211_hw *hw, if (beacon->cntdwn_counter_offsets[0]) { if (!is_template) - ieee80211_beacon_update_cntdwn(vif); + ieee80211_beacon_update_cntdwn(vif, link->link_id); ieee80211_set_beacon_cntdwn(sdata, beacon, link); } From a3a637a6c07189fab06a06c1f435331da652383b Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 30 Jan 2024 19:39:16 +0530 Subject: [PATCH 271/378] wifi: mac80211: handle set csa/after_csa beacon on per link basis In order to support CSA with MLO, there is a need to handle the functions ieee80211_set_csa_beacon() and ieee80211_set_after_csa_beacon() on a per link basis. Implement this by making the function argument accept the the link data instead of the sdata. Currently, deflink would only be passed. Proper link data will be passed in a subsequent patch. Signed-off-by: Aditya Kumar Singh Link: https://msgid.link/20240130140918.1172387-4-quic_adisi@quicinc.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 17b445491881..0c0f418c3652 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3590,20 +3590,21 @@ void ieee80211_channel_switch_disconnect(struct ieee80211_vif *vif, bool block_t } EXPORT_SYMBOL(ieee80211_channel_switch_disconnect); -static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, +static int ieee80211_set_after_csa_beacon(struct ieee80211_link_data *link_data, u64 *changed) { + struct ieee80211_sub_if_data *sdata = link_data->sdata; int err; switch (sdata->vif.type) { case NL80211_IFTYPE_AP: - if (!sdata->deflink.u.ap.next_beacon) + if (!link_data->u.ap.next_beacon) return -EINVAL; - err = ieee80211_assign_beacon(sdata, &sdata->deflink, - sdata->deflink.u.ap.next_beacon, + err = ieee80211_assign_beacon(sdata, link_data, + link_data->u.ap.next_beacon, NULL, NULL, changed); - ieee80211_free_next_beacon(&sdata->deflink); + ieee80211_free_next_beacon(link_data); if (err < 0) return err; @@ -3662,7 +3663,7 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) sdata->vif.bss_conf.csa_active = false; - err = ieee80211_set_after_csa_beacon(sdata, &changed); + err = ieee80211_set_after_csa_beacon(&sdata->deflink, &changed); if (err) return err; @@ -3714,18 +3715,19 @@ void ieee80211_csa_finalize_work(struct wiphy *wiphy, struct wiphy_work *work) ieee80211_csa_finalize(link); } -static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, +static int ieee80211_set_csa_beacon(struct ieee80211_link_data *link_data, struct cfg80211_csa_settings *params, u64 *changed) { + struct ieee80211_sub_if_data *sdata = link_data->sdata; struct ieee80211_csa_settings csa = {}; int err; switch (sdata->vif.type) { case NL80211_IFTYPE_AP: - sdata->deflink.u.ap.next_beacon = + link_data->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after); - if (!sdata->deflink.u.ap.next_beacon) + if (!link_data->u.ap.next_beacon) return -ENOMEM; /* @@ -3751,7 +3753,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, IEEE80211_MAX_CNTDWN_COUNTERS_NUM) || (params->n_counter_offsets_presp > IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) { - ieee80211_free_next_beacon(&sdata->deflink); + ieee80211_free_next_beacon(link_data); return -EINVAL; } @@ -3761,11 +3763,11 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, csa.n_counter_offsets_presp = params->n_counter_offsets_presp; csa.count = params->count; - err = ieee80211_assign_beacon(sdata, &sdata->deflink, + err = ieee80211_assign_beacon(sdata, link_data, ¶ms->beacon_csa, &csa, NULL, changed); if (err < 0) { - ieee80211_free_next_beacon(&sdata->deflink); + ieee80211_free_next_beacon(link_data); return err; } @@ -3925,7 +3927,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (sdata->vif.bss_conf.color_change_active) ieee80211_color_change_abort(sdata); - err = ieee80211_set_csa_beacon(sdata, params, &changed); + err = ieee80211_set_csa_beacon(&sdata->deflink, params, &changed); if (err) { ieee80211_link_unreserve_chanctx(&sdata->deflink); goto out; From 1a96bb4e8a7953a7cf8e277c4eea29907bb5a140 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 30 Jan 2024 19:39:17 +0530 Subject: [PATCH 272/378] wifi: mac80211: start and finalize channel switch on link basis Add changes to start a channel switch as well as finalize it on link basis in order to support CSA with MLO as well. Signed-off-by: Aditya Kumar Singh Link: https://msgid.link/20240130140918.1172387-5-quic_adisi@quicinc.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 59 +++++++++++++++++++++++++++------------------ net/mac80211/link.c | 2 ++ 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0c0f418c3652..d30a64cf95cd 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3633,6 +3633,7 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) { struct ieee80211_sub_if_data *sdata = link_data->sdata; struct ieee80211_local *local = sdata->local; + struct ieee80211_bss_conf *link_conf = link_data->conf; u64 changed = 0; int err; @@ -3654,16 +3655,16 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) if (link_data->reserved_ready) return 0; - return ieee80211_link_use_reserved_context(&sdata->deflink); + return ieee80211_link_use_reserved_context(link_data); } - if (!cfg80211_chandef_identical(&link_data->conf->chanreq.oper, + if (!cfg80211_chandef_identical(&link_conf->chanreq.oper, &link_data->csa_chanreq.oper)) return -EINVAL; - sdata->vif.bss_conf.csa_active = false; + link_conf->csa_active = false; - err = ieee80211_set_after_csa_beacon(&sdata->deflink, &changed); + err = ieee80211_set_after_csa_beacon(link_data, &changed); if (err) return err; @@ -3690,7 +3691,8 @@ static void ieee80211_csa_finalize(struct ieee80211_link_data *link_data) struct ieee80211_sub_if_data *sdata = link_data->sdata; if (__ieee80211_csa_finalize(link_data)) { - sdata_info(sdata, "failed to finalize CSA, disconnecting\n"); + sdata_info(sdata, "failed to finalize CSA on link %d, disconnecting\n", + link_data->link_id); cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev, GFP_KERNEL); } @@ -3867,7 +3869,10 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel_switch ch_switch; struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *chanctx; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_data *link_data; u64 changed = 0; + u8 link_id = params->link_id; int err; lockdep_assert_wiphy(local->hw.wiphy); @@ -3878,15 +3883,23 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (sdata->wdev.cac_started) return -EBUSY; - if (chanreq.oper.punctured && !sdata->vif.bss_conf.eht_support) + if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS)) + return -EINVAL; + + link_data = wiphy_dereference(wiphy, sdata->link[link_id]); + if (!link_data) + return -ENOLINK; + + link_conf = link_data->conf; + + if (chanreq.oper.punctured && !link_conf->eht_support) return -EINVAL; /* don't allow another channel switch if one is already active. */ - if (sdata->vif.bss_conf.csa_active) + if (link_conf->csa_active) return -EBUSY; - conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, - lockdep_is_held(&local->hw.wiphy->mtx)); + conf = wiphy_dereference(wiphy, link_conf->chanctx_conf); if (!conf) { err = -EBUSY; goto out; @@ -3910,7 +3923,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (err) goto out; - err = ieee80211_link_reserve_chanctx(&sdata->deflink, &chanreq, + err = ieee80211_link_reserve_chanctx(link_data, &chanreq, chanctx->mode, params->radar_required); if (err) @@ -3919,40 +3932,38 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, /* if reservation is invalid then this will fail */ err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0); if (err) { - ieee80211_link_unreserve_chanctx(&sdata->deflink); + ieee80211_link_unreserve_chanctx(link_data); goto out; } /* if there is a color change in progress, abort it */ - if (sdata->vif.bss_conf.color_change_active) + if (link_conf->color_change_active) ieee80211_color_change_abort(sdata); - err = ieee80211_set_csa_beacon(&sdata->deflink, params, &changed); + err = ieee80211_set_csa_beacon(link_data, params, &changed); if (err) { - ieee80211_link_unreserve_chanctx(&sdata->deflink); + ieee80211_link_unreserve_chanctx(link_data); goto out; } - sdata->deflink.csa_chanreq = chanreq; - sdata->deflink.csa_block_tx = params->block_tx; - sdata->vif.bss_conf.csa_active = true; + link_data->csa_chanreq = chanreq; + link_data->csa_block_tx = params->block_tx; + link_conf->csa_active = true; - if (sdata->deflink.csa_block_tx) + if (link_data->csa_block_tx) ieee80211_stop_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); cfg80211_ch_switch_started_notify(sdata->dev, - &sdata->deflink.csa_chanreq.oper, 0, + &link_data->csa_chanreq.oper, 0, params->count, params->block_tx); if (changed) { - ieee80211_link_info_change_notify(sdata, &sdata->deflink, - changed); - drv_channel_switch_beacon(sdata, - &sdata->deflink.csa_chanreq.oper); + ieee80211_link_info_change_notify(sdata, link_data, changed); + drv_channel_switch_beacon(sdata, &link_data->csa_chanreq.oper); } else { /* if the beacon didn't change, we can finalize immediately */ - ieee80211_csa_finalize(&sdata->deflink); + ieee80211_csa_finalize(link_data); } out: diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 4f19d6479bef..87a413374ece 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -73,6 +73,8 @@ void ieee80211_link_stop(struct ieee80211_link_data *link) ieee80211_mgd_stop_link(link); cancel_delayed_work_sync(&link->color_collision_detect_work); + wiphy_work_cancel(link->sdata->local->hw.wiphy, + &link->csa_finalize_work); ieee80211_link_release_channel(link); } From 04ada8599c35ecb2cf16c94eb118d227630d06ee Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 30 Jan 2024 19:39:18 +0530 Subject: [PATCH 273/378] wifi: mac80211: add support to call csa_finish on a link Currently ieee80211_csa_finish() function finalizes CSA by scheduling a finalizing worker using the deflink. With MLO, there is a need to do it on a given link basis. Pass link ID of the link on which CSA needs to be finalized. Signed-off-by: Aditya Kumar Singh Link: https://msgid.link/20240130140918.1172387-6-quic_adisi@quicinc.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/mac.c | 2 +- drivers/net/wireless/ath/ath10k/wmi.c | 2 +- drivers/net/wireless/ath/ath11k/wmi.c | 2 +- drivers/net/wireless/ath/ath12k/wmi.c | 2 +- drivers/net/wireless/ath/ath9k/beacon.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 4 ++-- .../net/wireless/intel/iwlwifi/mvm/time-event.c | 2 +- drivers/net/wireless/mediatek/mt76/mac80211.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7615/mcu.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 2 +- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 2 +- drivers/net/wireless/ti/wlcore/event.c | 2 +- drivers/net/wireless/virtual/mac80211_hwsim.c | 2 +- include/net/mac80211.h | 3 ++- net/mac80211/cfg.c | 15 +++++++++++++-- 17 files changed, 31 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b80220406104..41053219ee95 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2047,7 +2047,7 @@ static void ath10k_mac_vif_ap_csa_count_down(struct ath10k_vif *arvif) ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n", ret); } else { - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, 0); } } diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 9e2f0a50aaea..ddf15717d504 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3885,7 +3885,7 @@ void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) */ if (arvif->vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(arvif->vif)) { - ieee80211_csa_finish(arvif->vif); + ieee80211_csa_finish(arvif->vif, 0); continue; } diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 688ee20528a0..3c9f3b0bcfaa 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -8226,7 +8226,7 @@ ath11k_wmi_process_csa_switch_count_event(struct ath11k_base *ab, } if (arvif->is_up && arvif->vif->bss_conf.csa_active) - ieee80211_csa_finish(arvif->vif); + ieee80211_csa_finish(arvif->vif, 0); } rcu_read_unlock(); } diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 2fa724e5851a..75c3a0d9b9ec 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -6448,7 +6448,7 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab, } if (arvif->is_up && arvif->vif->bss_conf.csa_active) - ieee80211_csa_finish(arvif->vif); + ieee80211_csa_finish(arvif->vif, 0); } rcu_read_unlock(); } diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index ee72faac2f1d..4e48407138b2 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -368,7 +368,7 @@ bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif) if (!ieee80211_beacon_cntdwn_is_complete(vif)) return false; - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, 0); return true; } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index 533471e69400..8179d35dc310 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -517,7 +517,7 @@ bool ath9k_htc_csa_is_finished(struct ath9k_htc_priv *priv) if (!ieee80211_beacon_cntdwn_is_complete(vif)) return false; - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, 0); priv->csa_vif = NULL; return true; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 3dea06b28472..7cb6bc97b26c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1486,7 +1486,7 @@ static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm, } } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) { /* we don't have CSA NoA scheduled yet, switch now */ - ieee80211_csa_finish(csa_vif); + ieee80211_csa_finish(csa_vif, 0); RCU_INIT_POINTER(mvm->csa_vif, NULL); } } @@ -1836,7 +1836,7 @@ void iwl_mvm_channel_switch_start_notif(struct iwl_mvm *mvm, msecs_to_jiffies(IWL_MVM_CS_UNBLOCK_TX_TIMEOUT * csa_vif->bss_conf.beacon_int)); - ieee80211_csa_finish(csa_vif); + ieee80211_csa_finish(csa_vif, 0); rcu_read_unlock(); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 60ec5ca6927c..b7461bc32527 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -168,7 +168,7 @@ static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm) goto out_unlock; } - ieee80211_csa_finish(csa_vif); + ieee80211_csa_finish(csa_vif, 0); rcu_read_unlock(); diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 8a3a90d1bfac..8bf82755ca4c 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1614,7 +1614,7 @@ static void __mt76_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) { if (vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(vif)) - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, 0); } void mt76_csa_finish(struct mt76_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c index ae34d019e588..c807bd8d928d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c @@ -353,7 +353,7 @@ static void mt7615_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) { if (vif->bss_conf.csa_active) - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, 0); } static void diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index df1ad6d4e12d..d90f98c50039 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -228,7 +228,7 @@ mt7915_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION) return; - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, 0); } static void diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 699be57309c2..3ec813077dc2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -341,7 +341,7 @@ mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION) return; - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, 0); } static void diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index b0c1db726d7a..66bf92c164c3 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -5740,7 +5740,7 @@ static void rtl8xxxu_update_beacon_work_callback(struct work_struct *work) if (vif->bss_conf.csa_active) { if (ieee80211_beacon_cntdwn_is_complete(vif)) { - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, 0); return; } schedule_delayed_work(&priv->update_beacon_work, diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 1e082d039b82..2499dc908305 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -233,7 +233,7 @@ void wlcore_event_channel_switch(struct wl1271 *wl, cancel_delayed_work(&wlvif->channel_switch_work); } else { set_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags); - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, 0); } } } diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 907c0842fee7..c337afd8bee2 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -2303,7 +2303,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, } if (link_conf->csa_active && ieee80211_beacon_cntdwn_is_complete(vif)) - ieee80211_csa_finish(vif); + ieee80211_csa_finish(vif, link_id); } static enum hrtimer_restart diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8acee7ce3aa9..45d905b17a65 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5555,12 +5555,13 @@ void ieee80211_beacon_set_cntdwn(struct ieee80211_vif *vif, u8 counter); /** * ieee80211_csa_finish - notify mac80211 about channel switch * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @link_id: valid link_id during MLO or 0 for non-MLO * * After a channel switch announcement was scheduled and the counter in this * announcement hits 1, this function must be called by the driver to * notify mac80211 that the channel can be changed. */ -void ieee80211_csa_finish(struct ieee80211_vif *vif); +void ieee80211_csa_finish(struct ieee80211_vif *vif, unsigned int link_id); /** * ieee80211_beacon_cntdwn_is_complete - find out if countdown reached 1 diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d30a64cf95cd..156a4215dcda 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3547,13 +3547,24 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) return new_beacon; } -void ieee80211_csa_finish(struct ieee80211_vif *vif) +void ieee80211_csa_finish(struct ieee80211_vif *vif, unsigned int link_id) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; + struct ieee80211_link_data *link_data; + + if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS)) + return; rcu_read_lock(); + link_data = rcu_dereference(sdata->link[link_id]); + if (WARN_ON(!link_data)) { + rcu_read_unlock(); + return; + } + + /* TODO: MBSSID with MLO changes */ if (vif->mbssid_tx_vif == vif) { /* Trigger ieee80211_csa_finish() on the non-transmitting * interfaces when channel switch is received on @@ -3572,7 +3583,7 @@ void ieee80211_csa_finish(struct ieee80211_vif *vif) &iter->deflink.csa_finalize_work); } } - wiphy_work_queue(local->hw.wiphy, &sdata->deflink.csa_finalize_work); + wiphy_work_queue(local->hw.wiphy, &link_data->csa_finalize_work); rcu_read_unlock(); } From 80b0c88033ff8a9a0d6817ccdeebd512bef06cc0 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 5 Feb 2024 00:06:04 +0200 Subject: [PATCH 274/378] wifi: iwlwifi: add HONOR to PPAG approved list Add HONOR to the list of the OEMs that are allowed to use the PPAG feature Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://msgid.link/20240204235836.3498abc62910.I156c34206c58ff26e73f705cbda6f1a49b88edda@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 21b90278d1f2..597735a660a2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -94,6 +94,11 @@ static const struct dmi_system_id dmi_ppag_approved_list[] = { DMI_MATCH(DMI_SYS_VENDOR, "Razer"), }, }, + { .ident = "Honor", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + }, + }, {} }; From a20ac99b5f5ee768866b42d28138d90d89158042 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 00:06:05 +0200 Subject: [PATCH 275/378] wifi: iwlwifi: pcie: don't allow hw-rfkill to stop device on gen2 On new devices the HW rfkill shutdown doesn't need to be handled "as fast as possible", so disallow the immediate shutdown mode here via documentation and a warning. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240204235836.794c5387e67e.I064365428815ec3135afa345fbbde78449b60203@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h | 4 +++- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 9 +++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h index 3dc618a7c70f..1ca82f3e4ebf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h @@ -68,9 +68,11 @@ struct iwl_cfg; * Must be atomic and called with BH disabled. * @queue_not_full: notifies that a HW queue is not full any more. * Must be atomic and called with BH disabled. - * @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that + * @hw_rf_kill: notifies of a change in the HW rf kill switch. True means that * the radio is killed. Return %true if the device should be stopped by * the transport immediately after the call. May sleep. + * Note that this must not return %true for newer devices using gen2 PCIe + * transport. * @free_skb: allows the transport layer to free skbs that haven't been * reclaimed by the op_mode. This can happen when the driver is freed and * there are Tx packets pending in the transport layer. diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index b5756e168f49..6c76b2dd6878 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1484,12 +1484,9 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq) IWL_WARN(trans, "reporting RF_KILL (radio %s)\n", state ? "disabled" : "enabled"); - if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) { - if (trans->trans_cfg->gen2) - _iwl_trans_pcie_gen2_stop_device(trans); - else - _iwl_trans_pcie_stop_device(trans, from_irq); - } + if (iwl_op_mode_hw_rf_kill(trans->op_mode, state) && + !WARN_ON(trans->trans_cfg->gen2)) + _iwl_trans_pcie_stop_device(trans, from_irq); } void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans, From 3d801a7591824aa29fdb0774e0881890d4a773f1 Mon Sep 17 00:00:00 2001 From: Anjaneyulu Date: Mon, 5 Feb 2024 00:06:06 +0200 Subject: [PATCH 276/378] wifi: iwlwifi: Add support for PPAG cmd v5 and PPAG revision 3 Add support for - PPAG revision 3 in BIOS to enable PPAG in UHB - PPAG command version 5, this command allows OEM to control enablement of PPAG for LPI for UHB mode in USA and ETSI countries. Signed-off-by: Anjaneyulu Signed-off-by: Miri Korenblit Link: https://msgid.link/20240204235836.d17425824b11.If2c1b29e3c579f4135383681af2d625cfe2cffcd@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 9 +++-- .../net/wireless/intel/iwlwifi/fw/api/power.h | 40 +++++++++++++++++-- .../wireless/intel/iwlwifi/fw/regulatory.c | 20 ++++++---- .../wireless/intel/iwlwifi/fw/regulatory.h | 9 ++++- .../net/wireless/intel/iwlwifi/fw/runtime.h | 2 +- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 3 +- drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 3 +- 7 files changed, 66 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 9afb1b1d6aea..82ecea00f2b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -817,15 +817,15 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) if (IS_ERR(data)) return PTR_ERR(data); - /* try to read ppag table rev 2 or 1 (both have the same data size) */ + /* try to read ppag table rev 3, 2 or 1 (all have the same data size) */ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev); if (!IS_ERR(wifi_pkg)) { - if (tbl_rev == 1 || tbl_rev == 2) { + if (tbl_rev >= 1 && tbl_rev <= 3) { num_sub_bands = IWL_NUM_SUB_BANDS_V2; IWL_DEBUG_RADIO(fwrt, - "Reading PPAG table v2 (tbl_rev=%d)\n", + "Reading PPAG table (tbl_rev=%d)\n", tbl_rev); goto read_table; } else { @@ -857,7 +857,8 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) goto out_free; } - fwrt->ppag_flags = flags->integer.value & IWL_PPAG_ETSI_CHINA_MASK; + fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value, + fwrt->ppag_ver); /* * read, verify gain values and save them into the PPAG table. diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index 040d83fa5424..0bf38243f88a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -505,14 +505,41 @@ struct iwl_geo_tx_power_profiles_resp { __le32 profile_idx; } __packed; /* PER_CHAIN_LIMIT_OFFSET_RSP */ +/** + * enum iwl_ppag_flags - PPAG enable masks + * @IWL_PPAG_ETSI_MASK: enable PPAG in ETSI + * @IWL_PPAG_CHINA_MASK: enable PPAG in China + * @IWL_PPAG_ETSI_LPI_UHB_MASK: enable LPI in ETSI for UHB + * @IWL_PPAG_ETSI_VLP_UHB_MASK: enable VLP in ETSI for UHB + * @IWL_PPAG_ETSI_SP_UHB_MASK: enable SP in ETSI for UHB + * @IWL_PPAG_USA_LPI_UHB_MASK: enable LPI in USA for UHB + * @IWL_PPAG_USA_VLP_UHB_MASK: enable VLP in USA for UHB + * @IWL_PPAG_USA_SP_UHB_MASK: enable SP in USA for UHB + * @IWL_PPAG_CANADA_LPI_UHB_MASK: enable LPI in CANADA for UHB + * @IWL_PPAG_CANADA_VLP_UHB_MASK: enable VLP in CANADA for UHB + * @IWL_PPAG_CANADA_SP_UHB_MASK: enable SP in CANADA for UHB + */ +enum iwl_ppag_flags { + IWL_PPAG_ETSI_MASK = BIT(0), + IWL_PPAG_CHINA_MASK = BIT(1), + IWL_PPAG_ETSI_LPI_UHB_MASK = BIT(2), + IWL_PPAG_ETSI_VLP_UHB_MASK = BIT(3), + IWL_PPAG_ETSI_SP_UHB_MASK = BIT(4), + IWL_PPAG_USA_LPI_UHB_MASK = BIT(5), + IWL_PPAG_USA_VLP_UHB_MASK = BIT(6), + IWL_PPAG_USA_SP_UHB_MASK = BIT(7), + IWL_PPAG_CANADA_LPI_UHB_MASK = BIT(8), + IWL_PPAG_CANADA_VLP_UHB_MASK = BIT(9), + IWL_PPAG_CANADA_SP_UHB_MASK = BIT(10), +}; + /** * union iwl_ppag_table_cmd - union for all versions of PPAG command * @v1: version 1 * @v2: version 2 - * - * @flags: bit 0 - indicates enablement of PPAG for ETSI - * bit 1 - indicates enablement of PPAG for CHINA BIOS - * bit 1 can be used only in v3 (identical to v2) + * version 3, 4 and 5 are the same structure as v2, + * but has a different format of the flags bitmap + * @flags: values from &enum iwl_ppag_flags * @gain: table of antenna gain values per chain and sub-band * @reserved: reserved */ @@ -529,6 +556,11 @@ union iwl_ppag_table_cmd { } v2; } __packed; +#define IWL_PPAG_CMD_V4_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK) +#define IWL_PPAG_CMD_V5_MASK (IWL_PPAG_CMD_V4_MASK | \ + IWL_PPAG_ETSI_LPI_UHB_MASK | \ + IWL_PPAG_USA_LPI_UHB_MASK) + #define MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE 26 #define MCC_TO_SAR_OFFSET_TABLE_COL_SIZE 13 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 597735a660a2..36d506463e0e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -320,7 +320,7 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD), 1); /* - * Starting from ver 4, driver needs to send the PPAG CMD regradless + * Starting from ver 4, driver needs to send the PPAG CMD regardless * if PPAG is enabled/disabled or valid/invalid. */ send_ppag_always = cmd_ver > 3; @@ -341,18 +341,18 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, num_sub_bands = IWL_NUM_SUB_BANDS_V1; gain = cmd->v1.gain[0]; *cmd_size = sizeof(cmd->v1); - if (fwrt->ppag_ver == 1 || fwrt->ppag_ver == 2) { + if (fwrt->ppag_ver >= 1) { /* in this case FW supports revision 0 */ IWL_DEBUG_RADIO(fwrt, "PPAG table rev is %d, send truncated table\n", fwrt->ppag_ver); } - } else if (cmd_ver >= 2 && cmd_ver <= 4) { + } else if (cmd_ver >= 2 && cmd_ver <= 5) { num_sub_bands = IWL_NUM_SUB_BANDS_V2; gain = cmd->v2.gain[0]; *cmd_size = sizeof(cmd->v2); if (fwrt->ppag_ver == 0) { - /* in this case FW supports revisions 1 or 2 */ + /* in this case FW supports revisions 1,2 or 3 */ IWL_DEBUG_RADIO(fwrt, "PPAG table rev is 0, send padded table\n"); } @@ -364,11 +364,17 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, /* ppag mode */ IWL_DEBUG_RADIO(fwrt, "PPAG MODE bits were read from bios: %d\n", - cmd->v1.flags); + le32_to_cpu(cmd->v1.flags)); + + if (cmd_ver == 5) + cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V5_MASK); + else if (cmd_ver < 5) + cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V4_MASK); + if ((cmd_ver == 1 && !fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) || - (cmd_ver == 2 && fwrt->ppag_ver == 2)) { + (cmd_ver == 2 && fwrt->ppag_ver >= 2)) { cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n"); } else { @@ -377,7 +383,7 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, IWL_DEBUG_RADIO(fwrt, "PPAG MODE bits going to be sent: %d\n", - cmd->v1.flags); + le32_to_cpu(cmd->v1.flags)); for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { for (j = 0; j < num_sub_bands; j++) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 52389f82cbb9..28e774766847 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -37,7 +37,7 @@ #define IWL_PPAG_MAX_HB 40 #define IWL_PPAG_ETSI_CHINA_MASK 3 -#define IWL_PPAG_ETSI_MASK BIT(0) +#define IWL_PPAG_REV3_MASK 0x7FF #define IWL_WTAS_BLACK_LIST_MAX 16 #define IWL_WTAS_ENABLED_MSK 0x1 @@ -189,4 +189,11 @@ __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); int iwl_bios_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, u32 *value); + +static inline u32 iwl_bios_get_ppag_flags(const u32 ppag_modes, + const u8 ppag_ver) +{ + return ppag_modes & (ppag_ver < 3 ? IWL_PPAG_ETSI_CHINA_MASK : + IWL_PPAG_REV3_MASK); +} #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 16d9ea6dd386..bd0c9b2224e1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -174,7 +174,7 @@ struct iwl_fw_runtime { bool geo_enabled; struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; u32 ppag_flags; - u32 ppag_ver; + u8 ppag_ver; #ifdef CONFIG_ACPI struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 3c4c99eed110..5148e8049867 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -541,7 +541,8 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) } fwrt->ppag_ver = data->revision; - fwrt->ppag_flags = data->ppag_modes & IWL_PPAG_ETSI_CHINA_MASK; + fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes, + fwrt->ppag_ver); BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains)); memcpy(&fwrt->ppag_chains, &data->ppag_chains, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 1f7c3f4c2901..39053290bd59 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -116,8 +116,7 @@ struct uefi_cnv_var_wgds { /* * struct uefi_cnv_var_ppag - PPAG table as defined in UEFI * @revision: the revision of the table - * @ppag_modes: bit 0 - PPAG is enabled/disabled in ETSI, - * bit 1 - PPAG is enabled/disabled in China + * @ppag_modes: values from &enum iwl_ppag_flags * @ppag_chains: the PPAG values per chain and band */ struct uefi_cnv_var_ppag { From e047e0e3cc8b647eecf6ce644d5dacba44700d94 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 00:06:07 +0200 Subject: [PATCH 277/378] wifi: iwlwifi: mvm: const-ify chandef pointers In much of the PHY context handling code the chandef coming from mac80211 is read-only, mark them const to make that clearer. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240204235836.e7fbd3e26d85.I72d72e61dc5f5fc76c53e32cb60b66237eaedec3@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 10 +++++----- drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index eb30c299a71e..37195cb5f70b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1810,18 +1810,18 @@ void iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm, /* MVM PHY */ struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm); int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, + const struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic); int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, + const struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic); void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt); void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt); int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm); -u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef); -u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef); +u8 iwl_mvm_get_channel_width(const struct cfg80211_chan_def *chandef); +u8 iwl_mvm_get_ctrl_pos(const struct cfg80211_chan_def *chandef); int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, u8 chains_static, u8 chains_dynamic); @@ -2530,7 +2530,7 @@ static inline void iwl_mvm_set_chan_info(struct iwl_mvm *mvm, static inline void iwl_mvm_set_chan_info_chandef(struct iwl_mvm *mvm, struct iwl_fw_channel_info *ci, - struct cfg80211_chan_def *chandef) + const struct cfg80211_chan_def *chandef) { enum nl80211_band band = chandef->chan->band; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index 8bf778503b74..bac655834f32 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -9,7 +9,7 @@ #include "mvm.h" /* Maps the driver specific channel width definition to the fw values */ -u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) +u8 iwl_mvm_get_channel_width(const struct cfg80211_chan_def *chandef) { switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: @@ -33,7 +33,7 @@ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) * Maps the driver specific control channel position (relative to the center * freq) definitions to the the fw values */ -u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef) +u8 iwl_mvm_get_ctrl_pos(const struct cfg80211_chan_def *chandef) { int offs = chandef->chan->center_freq - chandef->center_freq1; int abs_offs = abs(offs); @@ -116,7 +116,7 @@ static void iwl_mvm_phy_ctxt_set_rxchain(struct iwl_mvm *mvm, static void iwl_mvm_phy_ctxt_cmd_data_v1(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct iwl_phy_context_cmd_v1 *cmd, - struct cfg80211_chan_def *chandef, + const struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { struct iwl_phy_context_cmd_tail *tail = @@ -137,7 +137,7 @@ static void iwl_mvm_phy_ctxt_cmd_data_v1(struct iwl_mvm *mvm, static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct iwl_phy_context_cmd *cmd, - struct cfg80211_chan_def *chandef, + const struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { cmd->lmac_id = cpu_to_le32(iwl_mvm_get_lmac_id(mvm, @@ -197,7 +197,7 @@ int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, */ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, + const struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic, u32 action) { @@ -254,7 +254,7 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, * Send a command to add a PHY context based on the current HW configuration. */ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, + const struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { int ret; @@ -300,7 +300,7 @@ void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) * changed. */ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, + const struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY; From f94c24386d04ec242207bdbdc59ccb1b0b3cfc3c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 5 Feb 2024 00:06:08 +0200 Subject: [PATCH 278/378] wifi: iwlwifi: adjust rx_phyinfo debugfs to MLO This debugfs entry is used to configure the rx_phyinfo. Currently we are sending the phy cmd only for the deflink. Change it to send the cmd for all active links of the vif Signed-off-by: Miri Korenblit Reviewed-by: Gregory Greenman Link: https://msgid.link/20240204235836.a68ee2b6cb58.Iddc47c608ec990b12be0ae5b1ee89bcf6beb0f6a@changeid Signed-off-by: Johannes Berg --- .../wireless/intel/iwlwifi/mvm/debugfs-vif.c | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index e8b881596baf..2b96cf9eac72 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -578,34 +578,46 @@ static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; - struct ieee80211_chanctx_conf *chanctx_conf; - struct iwl_mvm_phy_ctxt *phy_ctxt; + struct ieee80211_bss_conf *link_conf; u16 value; - int ret; + int link_id, ret = -EINVAL; ret = kstrtou16(buf, 0, &value); if (ret) return ret; mutex_lock(&mvm->mutex); - rcu_read_lock(); - - chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf); - /* make sure the channel context is assigned */ - if (!chanctx_conf) { - rcu_read_unlock(); - mutex_unlock(&mvm->mutex); - return -EINVAL; - } - - phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv]; - rcu_read_unlock(); mvm->dbgfs_rx_phyinfo = value; - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def, - chanctx_conf->rx_chains_static, - chanctx_conf->rx_chains_dynamic); + for_each_vif_active_link(vif, link_conf, link_id) { + struct ieee80211_chanctx_conf *chanctx_conf; + struct cfg80211_chan_def min_def; + struct iwl_mvm_phy_ctxt *phy_ctxt; + u8 chains_static, chains_dynamic; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(link_conf->chanctx_conf); + if (!chanctx_conf) { + rcu_read_unlock(); + continue; + } + /* A command can't be sent with RCU lock held, so copy + * everything here and use it after unlocking + */ + min_def = chanctx_conf->min_def; + chains_static = chanctx_conf->rx_chains_static; + chains_dynamic = chanctx_conf->rx_chains_dynamic; + rcu_read_unlock(); + + phy_ctxt = mvmvif->link[link_id]->phy_ctxt; + if (!phy_ctxt) + continue; + + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &min_def, + chains_static, chains_dynamic); + } + mutex_unlock(&mvm->mutex); return ret ?: count; From 814cdd7c37525133e54c667ca3cae6461ded93dd Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 5 Feb 2024 00:06:09 +0200 Subject: [PATCH 279/378] wifi: iwlwifi: read mac step from aux register in BZ, the mac step is not updated to the HW REV CSR. For BZ-I, read it from the CNVI aux register For BZ-U always take B step. Signed-off-by: Miri Korenblit Link: https://msgid.link/20240204235836.dcc18b533f13.I0a6267fa0a142744bcf7500b45f667b596b492c5@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 6 +++++- .../wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c | 2 +- .../net/wireless/intel/iwlwifi/pcie/ctxt-info.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index c1c7d44f421b..a7d44df06eab 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -368,7 +368,11 @@ enum { WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK = 0x80000000, }; -#define CNVI_AUX_MISC_CHIP 0xA200B0 +#define CNVI_AUX_MISC_CHIP 0xA200B0 +#define CNVI_AUX_MISC_CHIP_MAC_STEP(_val) (((_val) & 0xf000000) >> 24) +#define CNVI_AUX_MISC_CHIP_PROD_TYPE(_val) ((_val) & 0xfff) +#define CNVI_AUX_MISC_CHIP_PROD_TYPE_BZ_U 0x930 + #define CNVR_AUX_MISC_CHIP 0xA2B800 #define CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM 0xA29890 #define CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR 0xA29938 diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c index fa4a14546860..c8fc8b4fd85c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c @@ -119,7 +119,7 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, prph_sc_ctrl->version.version = 0; prph_sc_ctrl->version.mac_id = - cpu_to_le16((u16)iwl_read32(trans, CSR_HW_REV)); + cpu_to_le16((u16)trans->hw_rev); prph_sc_ctrl->version.size = cpu_to_le16(sizeof(*prph_scratch) / 4); control_flags |= IWL_PRPH_SCRATCH_MTR_MODE; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index 5f55efe64bf5..0fa92704cd14 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2023 Intel Corporation */ #include "iwl-trans.h" #include "iwl-fh.h" @@ -180,7 +180,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, ctxt_info->version.version = 0; ctxt_info->version.mac_id = - cpu_to_le16((u16)iwl_read32(trans, CSR_HW_REV)); + cpu_to_le16((u16)trans->hw_rev); /* size is in DWs */ ctxt_info->version.size = cpu_to_le16(sizeof(*ctxt_info) / 4); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index bbc8dc390bdc..1ed67b76b516 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1155,6 +1155,20 @@ static void get_crf_id(struct iwl_trans *iwl_trans) iwl_trans->hw_cnv_id = iwl_read_prph_no_grab(iwl_trans, CNVI_AUX_MISC_CHIP); + /* In BZ, the MAC step must be read from the CNVI aux register */ + if (CSR_HW_REV_TYPE(iwl_trans->hw_rev) == IWL_CFG_MAC_TYPE_BZ) { + u8 step = CNVI_AUX_MISC_CHIP_MAC_STEP(iwl_trans->hw_cnv_id); + + /* For BZ-U, take B step also when A step is indicated */ + if ((CNVI_AUX_MISC_CHIP_PROD_TYPE(iwl_trans->hw_cnv_id) == + CNVI_AUX_MISC_CHIP_PROD_TYPE_BZ_U) && + step == SILICON_A_STEP) + step = SILICON_B_STEP; + + iwl_trans->hw_rev_step = step; + iwl_trans->hw_rev |= step; + } + /* Read cdb info (also contains the jacket info if needed in the future */ iwl_trans->hw_wfpm_id = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR); From 83f57c936b6e210d517fb5fa526895234bc974e8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 00:06:10 +0200 Subject: [PATCH 280/378] wifi: iwlwifi: mvm: remove EHT code from mac80211.c The code here is the pre-MLD API, but of course older FW that doesn't support MLD APIs cannot support EHT. Remove some code that shouldn't be there. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240204235836.bde5a9d87759.I4c69dd94416f92b0f1f53dd57dafecbec643600d@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 65293b45a98f..7b57ae9f8ad3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2611,9 +2611,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, */ if (changes & BSS_CHANGED_ASSOC && vif->cfg.assoc) { if ((vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) || - (vif->bss_conf.eht_support && - !iwlwifi_mod_params.disable_11be)) + !iwlwifi_mod_params.disable_11ax)) iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->deflink.ap_sta_id); iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); @@ -2622,10 +2620,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, /* Update MU EDCA params */ if (changes & BSS_CHANGED_QOS && mvmvif->associated && vif->cfg.assoc && - ((vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) || - (vif->bss_conf.eht_support && - !iwlwifi_mod_params.disable_11be))) + (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax)) iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->deflink.ap_sta_id); /* @@ -3737,10 +3732,8 @@ iwl_mvm_sta_state_auth_to_assoc(struct ieee80211_hw *hw, * the default bss_conf */ if (!mvm->mld_api_is_used && - ((vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) || - (vif->bss_conf.eht_support && - !iwlwifi_mod_params.disable_11be))) + (vif->bss_conf.he_support && + !iwlwifi_mod_params.disable_11ax)) iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->deflink.sta_id); } else if (vif->type == NL80211_IFTYPE_STATION) { iwl_mvm_vif_set_he_support(hw, vif, sta, true); From 318b3fac347cc307b39412cd8133488a83cd73a9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 00:06:11 +0200 Subject: [PATCH 281/378] wifi: iwlwifi: use system_unbound_wq for debug dump This can take some time, so it's better to use the unbound workqueue. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240204235836.75c8d2286f81.I478e9faf422f22ae66c0a113003fea83565c5692@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index e7ff043f9f0a..db6d7013df66 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -2878,7 +2878,8 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n", le32_to_cpu(desc->trig_desc.type)); - schedule_delayed_work(&wk_data->wk, usecs_to_jiffies(delay)); + queue_delayed_work(system_unbound_wq, &wk_data->wk, + usecs_to_jiffies(delay)); return 0; } @@ -3180,7 +3181,9 @@ int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, if (sync) iwl_fw_dbg_collect_sync(fwrt, idx); else - schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); + queue_delayed_work(system_unbound_wq, + &fwrt->dump.wks[idx].wk, + usecs_to_jiffies(delay)); return 0; } From 449619744df11873f4b4e534502e1f10653b57ce Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 5 Feb 2024 00:06:12 +0200 Subject: [PATCH 282/378] wifi: iwlwifi: mvm: don't support reduced tx power on ack for new devices This is no longer supported by the firmware. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://msgid.link/20240204235836.768d56206093.I737872ff19f0dbeefca42a239d673f05b9ac06f0@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 2c34c55ca5f4..535edb51d1c0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -181,6 +181,9 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, struct iwl_mvm_sta *mvmsta; u32 value; + if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + return 0; + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); if (!mvmsta) return 0; From f51d6431824f0afb9f73d68971d154c47c26b86a Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 5 Feb 2024 00:06:13 +0200 Subject: [PATCH 283/378] wifi: iwlwifi: support EHT for WH sku_cap_11be_enable should be set to true also for WH. Fixes: e1374ed25324 ("wifi: iwlwifi: Add support for new CNVi (SC)") Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://msgid.link/20240204235836.a6d4097cbaca.I8b00fa7b6226b4116cd91f70fb0b15e79b4dee5a@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index c30187d77b3c..d2f133255ee6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -2125,7 +2125,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED); nvm->sku_cap_mimo_disabled = !!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED); - if (CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM) + if (CSR_HW_RFID_TYPE(trans->hw_rf_id) >= IWL_CFG_RF_TYPE_FM) nvm->sku_cap_11be_enable = true; /* Initialize PHY sku data */ From f863afbd301ef2a29364316dd14a73699d0bc673 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 00:06:17 +0200 Subject: [PATCH 284/378] wifi: iwlwifi: mvm: remove one queue sync on BA session stop As documented in the comment, this queue sync was here to ensure that an async IWL_MVM_RXQ_NSSN_SYNC queue sync won't race with setting up a new BA session with the same BAID. However, we no longer do IWL_MVM_RXQ_NSSN_SYNC queue sync, so we can remove this as well. Signed-off-by: Johannes Berg Reviewed-by: Grumbach, Emmanuel Signed-off-by: Miri Korenblit Link: https://msgid.link/20240204235836.0a09ab337b54.I0dfe239dc30577a2ff23f910b10e9957364ccc78@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 7e9f3a670212..f3efbec38253 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2015, 2018-2023 Intel Corporation + * Copyright (C) 2012-2015, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -2989,16 +2989,6 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, RCU_INIT_POINTER(mvm->baid_map[baid], NULL); kfree_rcu(baid_data, rcu_head); IWL_DEBUG_HT(mvm, "BAID %d is free\n", baid); - - /* - * After we've deleted it, do another queue sync - * so if an IWL_MVM_RXQ_NSSN_SYNC was concurrently - * running it won't find a new session in the old - * BAID. It can find the NULL pointer for the BAID, - * but we must not have it find a different session. - */ - iwl_mvm_sync_rx_queues_internal(mvm, IWL_MVM_RXQ_EMPTY, - true, NULL, 0); } return 0; From 8b720901d97d45d477121a406cb120cee6291b55 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Tue, 6 Feb 2024 13:57:03 +0200 Subject: [PATCH 285/378] wifi: iwlwifi: mvm: advertise support for protected ranging negotiation Advertise support for protected ranging negotiation if the firmware supports it. Signed-off-by: Avraham Stern Reviewed-by: Luciano Coelho Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206135637.9bb7e13ad18c.I578af1c9836e91069ce318b265bd221f42955992@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 7b57ae9f8ad3..fa7d86917741 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -705,6 +705,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) } } + if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(LOCATION_GROUP, + TOF_RANGE_REQ_CMD), + IWL_FW_CMD_VER_UNKNOWN) >= 11) { + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE); + } + mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; #ifdef CONFIG_PM_SLEEP From 4d951e265c116c38862dcb1f59af5803762d4048 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Mon, 5 Feb 2024 00:06:15 +0200 Subject: [PATCH 286/378] wifi: iwlwifi: mvm: Declare support for secure LTF measurement Declare support for secure LTF measurement if the FW supports it. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://msgid.link/20240204235836.f20d2437c06f.I479df8ab543db2d05c413119ad3eb3936cc86294@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/file.h | 3 ++- drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c | 6 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index b216da7d95fc..b7ef0882dbad 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -385,6 +385,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * complete to FW. * @IWL_UCODE_TLV_CAPA_SPP_AMSDU_SUPPORT: Support SPP (signaling and payload * protected) A-MSDU. + * @IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT: Support secure LTF measurement. * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -483,7 +484,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_STA_EXP_MFP_SUPPORT = (__force iwl_ucode_tlv_capa_t)114, IWL_UCODE_TLV_CAPA_SNIFF_VALIDATE_SUPPORT = (__force iwl_ucode_tlv_capa_t)116, IWL_UCODE_TLV_CAPA_CHINA_22_REG_SUPPORT = (__force iwl_ucode_tlv_capa_t)117, - + IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT = (__force iwl_ucode_tlv_capa_t)121, NUM_IWL_UCODE_TLV_CAPA /* * This construction make both sparse (which cannot increment the previous diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c index f72ca38d7c0e..dca36b0662c7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c @@ -349,6 +349,12 @@ int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm, } if (hltk && hltk_len) { + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT)) { + IWL_ERR(mvm, "No support for secure LTF measurement\n"); + return -EINVAL; + } + hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher); if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) { IWL_ERR(mvm, "invalid cipher: %u\n", cipher); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index fa7d86917741..40e97e812f5f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -710,6 +710,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IWL_FW_CMD_VER_UNKNOWN) >= 11) { wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE); + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT)) + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_SECURE_LTF); } mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; From f4eedfd88b62819bedad7d92571febef9093bc72 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 21:21:01 +0200 Subject: [PATCH 287/378] wifi: iwlwifi: mvm: expand queue sync warning messages It's a bit tricky to understand what's going on here, add more data to the warning messages to make that clearer. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.1df82a509636.I2f71811569a5c48eb166c4caa779af2d6160ad33@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 7 ++++--- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 40e97e812f5f..3622f67588fe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -6097,8 +6097,9 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, iwl_mvm_is_radio_killed(mvm), HZ); WARN_ONCE(!ret && !iwl_mvm_is_radio_killed(mvm), - "queue sync: failed to sync, state is 0x%lx\n", - mvm->queue_sync_state); + "queue sync: failed to sync, state is 0x%lx, cookie %d\n", + mvm->queue_sync_state, + mvm->queue_sync_cookie); } out: diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 67062fe40152..7979b7952a79 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -688,11 +688,11 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi, return; len -= sizeof(*notif) + sizeof(*internal_notif); - if (internal_notif->sync && - mvm->queue_sync_cookie != internal_notif->cookie) { - WARN_ONCE(1, "Received expired RX queue sync message\n"); + if (WARN_ONCE(internal_notif->sync && + mvm->queue_sync_cookie != internal_notif->cookie, + "Received expired RX queue sync message (cookie %d but wanted %d, queue %d)\n", + internal_notif->cookie, mvm->queue_sync_cookie, queue)) return; - } switch (internal_notif->type) { case IWL_MVM_RXQ_EMPTY: From 87f690f5a9030a694dbf9d794ee940998118a195 Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Mon, 5 Feb 2024 21:21:02 +0200 Subject: [PATCH 288/378] wifi: iwlwifi: mvm: define RX queue sync timeout as a macro define the timeout on RX queues notification as a macro so it will be clearer. Signed-off-by: Shaul Triebitz Reviewed-by: Luciano Coelho Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.a6985ea87751.Iafb7ae13aa58d66512e4b3fa6c75149c75cbc305@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 3622f67588fe..f18c77d88f3a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -6046,6 +6046,7 @@ void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, } } +#define SYNC_RX_QUEUE_TIMEOUT (HZ) void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, enum iwl_mvm_rxq_notif_type type, bool sync, @@ -6095,7 +6096,7 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, ret = wait_event_timeout(mvm->rx_sync_waitq, READ_ONCE(mvm->queue_sync_state) == 0 || iwl_mvm_is_radio_killed(mvm), - HZ); + SYNC_RX_QUEUE_TIMEOUT); WARN_ONCE(!ret && !iwl_mvm_is_radio_killed(mvm), "queue sync: failed to sync, state is 0x%lx, cookie %d\n", mvm->queue_sync_state, From 0dd2b42c2c096e39da4c35927db4ed1f6c587bea Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 21:21:03 +0200 Subject: [PATCH 289/378] wifi: iwlwifi: mvm: don't abort queue sync in CT-kill CT kill should stop doing a lot of TX etc. to cool down the NIC, but we don't stop all commands from going to the NIC, and as such we shouldn't abort queue sync, since it can get confused if we do, warning that we do it twice at the same time etc. Only stop it when we'd also not send it in the first place. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.4e0745e2cd97.I311dc623ce68de6a2da3c21c8d84a387844f714a@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 16 +++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index f18c77d88f3a..ab1219f4a5dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -6095,9 +6095,9 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); ret = wait_event_timeout(mvm->rx_sync_waitq, READ_ONCE(mvm->queue_sync_state) == 0 || - iwl_mvm_is_radio_killed(mvm), + iwl_mvm_is_radio_hw_killed(mvm), SYNC_RX_QUEUE_TIMEOUT); - WARN_ONCE(!ret && !iwl_mvm_is_radio_killed(mvm), + WARN_ONCE(!ret && !iwl_mvm_is_radio_hw_killed(mvm), "queue sync: failed to sync, state is 0x%lx, cookie %d\n", mvm->queue_sync_state, mvm->queue_sync_cookie); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index ad4146d3345a..77dce70eccc4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1828,12 +1828,8 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int hw_queue) static void iwl_mvm_set_rfkill_state(struct iwl_mvm *mvm) { - bool state = iwl_mvm_is_radio_killed(mvm); - - if (state) - wake_up(&mvm->rx_sync_waitq); - - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state); + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, + iwl_mvm_is_radio_killed(mvm)); } void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) @@ -1858,10 +1854,12 @@ static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) bool rfkill_safe_init_done = READ_ONCE(mvm->rfkill_safe_init_done); bool unified = iwl_mvm_has_unified_ucode(mvm); - if (state) + if (state) { set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - else + wake_up(&mvm->rx_sync_waitq); + } else { clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); + } iwl_mvm_set_rfkill_state(mvm); From 74f4cd71070538bd9a8b6686fc53e7b77d510afa Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 5 Feb 2024 21:21:04 +0200 Subject: [PATCH 290/378] wifi: iwlwifi: take SGOM and UATS code out of ACPI ifdef The BIOS tables SGOM and UATS are read from UEFI, but require additional tables (WGDS and DSM func 3, respectively) which used to be read from ACPI only, so the code handling those tables had to be under ifdef ACPI. But now the driver reads those tables (WGDS and DSM) from both ACPI and UEFI, so SGOM and UATS code shouldn't be under ifdef ACPI anymore. Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.dcaa3325773f.I649079c842369dcae3a362842322deca422a61d5@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 4 +--- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 4 +--- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 14 +------------- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index bd0c9b2224e1..b2bc4fd37abf 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #ifndef __iwl_fw_runtime_h__ #define __iwl_fw_runtime_h__ @@ -175,11 +175,9 @@ struct iwl_fw_runtime { struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; u32 ppag_flags; u8 ppag_ver; -#ifdef CONFIG_ACPI struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; struct iwl_uats_table_cmd uats_table; -#endif u8 uefi_tables_lock_status; bool uats_enabled; }; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 5148e8049867..e81fc0129b9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2021-2023 Intel Corporation + * Copyright(c) 2021-2024 Intel Corporation */ #include "iwl-drv.h" @@ -324,7 +324,6 @@ void iwl_uefi_get_step_table(struct iwl_trans *trans) } IWL_EXPORT_SYMBOL(iwl_uefi_get_step_table); -#ifdef CONFIG_ACPI static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data, struct iwl_fw_runtime *fwrt) { @@ -412,7 +411,6 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, return 0; } IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table); -#endif /* CONFIG_ACPI */ static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt, struct uefi_sar_profile *uefi_sar_prof, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index b596a1a83750..5381afdd4021 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -488,7 +488,6 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, #endif /* CONFIG_ACPI */ } -#if defined(CONFIG_ACPI) && defined(CONFIG_EFI) static void iwl_mvm_uats_init(struct iwl_mvm *mvm) { u8 cmd_ver; @@ -568,17 +567,6 @@ static int iwl_mvm_sgom_init(struct iwl_mvm *mvm) return ret; } -#else - -static int iwl_mvm_sgom_init(struct iwl_mvm *mvm) -{ - return 0; -} - -static void iwl_mvm_uats_init(struct iwl_mvm *mvm) -{ -} -#endif static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) { From 556c7cd721b5262579ba1710c3b4e7ffdb5573ac Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 5 Feb 2024 21:21:05 +0200 Subject: [PATCH 291/378] wifi: iwlwifi: properly check if link is active Before sending SESSION PROTECTION cmd the driver verifies that the link for which the cmd is going to be sent is active. The existing code is checking it only for MLD vifs, but also the deflink (in non-MLD vifs) needs to be active in order the have a session protection for it. Fix this by checking if the link is active also for non-MLD vifs Fixes: 135065837310 ("wifi: iwlwifi: support link_id in SESSION_PROTECTION cmd") Signed-off-by: Miri Korenblit Reviewed-by: Johannes Berg Link: https://msgid.link/20240205211151.c61820f14ca6.Ibbe0f848f3e71f64313d21642650b6e4bfbe4b39@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index b7461bc32527..2956127fd11b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -706,8 +706,7 @@ static int iwl_mvm_get_session_prot_id(struct iwl_mvm *mvm, "Invalid link ID for session protection: %u\n", link_id)) return -EINVAL; - if (WARN(ieee80211_vif_is_mld(vif) && - !(vif->active_links & BIT(link_id)), + if (WARN(!mvmvif->link[link_id]->active, "Session Protection on an inactive link: %u\n", link_id)) return -EINVAL; From b7acc059a728b591f1a27da0bfd460b0513fda7d Mon Sep 17 00:00:00 2001 From: Daniel Amosi Date: Mon, 5 Feb 2024 21:21:07 +0200 Subject: [PATCH 292/378] wifi: iwlwifi: mvm: Keep connection in case of missed beacons during RX The client needs to disconnect from AP in case of more than 19 missed beacons only if no data is coming from that AP, otherwise it needs to stay connected. Signed-off-by: Daniel Amosi Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.080195242c18.Ib166fc4e46666165a88e673a4a196cb8f18fdec4@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 14 +++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 7cb6bc97b26c..cc592e288188 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -1626,8 +1626,16 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, * TODO: the threshold should be adjusted based on latency conditions, * and/or in case of a CS flow on one of the other AP vifs. */ - if (rx_missed_bcon > IWL_MVM_MISSED_BEACONS_THRESHOLD_LONG) { - iwl_mvm_connection_loss(mvm, vif, "missed beacons"); + if (rx_missed_bcon >= IWL_MVM_MISSED_BEACONS_THRESHOLD_LONG) { + if (rx_missed_bcon_since_rx >= IWL_MVM_MISSED_BEACONS_SINCE_RX_THOLD) { + iwl_mvm_connection_loss(mvm, vif, "missed beacons"); + } else { + IWL_WARN(mvm, + "missed beacons exceeds threshold, but receiving data. Stay connected, Expect bugs.\n"); + IWL_WARN(mvm, + "missed_beacons:%d, missed_beacons_since_rx:%d\n", + rx_missed_bcon, rx_missed_bcon_since_rx); + } } else if (rx_missed_bcon_since_rx > IWL_MVM_MISSED_BEACONS_THRESHOLD) { if (!iwl_mvm_has_new_tx_api(mvm)) ieee80211_beacon_loss(vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 37195cb5f70b..937d0bd9c531 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -40,8 +40,9 @@ #define IWL_MVM_MAX_ADDRESSES 5 /* RSSI offset for WkP */ #define IWL_RSSI_OFFSET 50 +#define IWL_MVM_MISSED_BEACONS_SINCE_RX_THOLD 4 #define IWL_MVM_MISSED_BEACONS_THRESHOLD 8 -#define IWL_MVM_MISSED_BEACONS_THRESHOLD_LONG 16 +#define IWL_MVM_MISSED_BEACONS_THRESHOLD_LONG 19 /* A TimeUnit is 1024 microsecond */ #define MSEC_TO_TU(_msec) (_msec*1000/1024) From f05ef3497f260d0ab75aed45205bfe90beb3bd90 Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Mon, 5 Feb 2024 21:21:09 +0200 Subject: [PATCH 293/378] wifi: iwlwifi: mvm: fix the key PN index When waking from D3 (and a GTK rekey happened during D3), the key itself is saved in iwl_wowlan_status_data::gtk array, but the PN is saved in iwl_wowlan_status_data::gtk_seq array. The indices (of the same key) might differ in both arrays. Fix using the gtk array index in the gtk_seq array. Rather, iterate and search for the correct key in the gtk_seq array. Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.bdd0511c007d.I3325288c64c010a4d008ac4429de1c2b14ef764c@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 6396fbde03c4..994387eac6f7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1958,7 +1958,7 @@ static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status, struct ieee80211_vif *vif, struct iwl_mvm *mvm, u32 gtk_cipher) { - int i; + int i, j; struct ieee80211_key_conf *key; struct { struct ieee80211_key_conf conf; @@ -2002,7 +2002,15 @@ static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status, key = ieee80211_gtk_rekey_add(vif, &conf.conf); if (IS_ERR(key)) return false; - iwl_mvm_set_key_rx_seq_idx(key, status, i); + + for (j = 0; j < ARRAY_SIZE(status->gtk_seq); j++) { + if (!status->gtk_seq[j].valid || + status->gtk_seq[j].key_id != key->keyidx) + continue; + iwl_mvm_set_key_rx_seq_idx(key, status, j); + break; + } + WARN_ON(j == ARRAY_SIZE(status->gtk_seq)); } return true; From 066425b6c8d25d9b8d6cf49e6d0910d8cb2d06fe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 21:21:10 +0200 Subject: [PATCH 294/378] wifi: iwlwifi: mvm: combine condition/warning WARN() returns the value of the condition, so it's nicer to combine the warning and the if. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.44f63334760e.If0a2cf347a8676a3830c5c3183a257fe11f31419@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 7979b7952a79..7c0da3b8ad77 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -649,10 +649,8 @@ static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm, rcu_read_lock(); ba_data = rcu_dereference(mvm->baid_map[baid]); - if (!ba_data) { - WARN(true, "BAID %d not found in map\n", baid); + if (WARN(!ba_data, "BAID %d not found in map\n", baid)) goto out; - } /* pick any STA ID to find the pointer */ sta_id = ffs(ba_data->sta_mask) - 1; From 1b3741ea4089ff7e87ea722004c0dc54adcbdf25 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 21:21:11 +0200 Subject: [PATCH 295/378] wifi: iwlwifi: mvm: limit pseudo-D3 to 60 seconds With unlimited pseudo-D3, we can get stuck here in the read if the firmware never wakes up. All of our testing infrastructure however will anyway give up after at most a minute, so there's no value in that. Limit this to about a minute to avoid getting stuck with the RTNL held forever, which basically makes the machine unusable and then we can't even understand what caused the failure. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.ca55b3a7fa8d.Id746846f187442ebc689416d2688f2bd9278c0e9@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 994387eac6f7..af449cb9f967 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -3389,6 +3389,7 @@ static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; + unsigned long end = jiffies + 60 * HZ; u32 pme_asserted; while (true) { @@ -3402,6 +3403,12 @@ static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, if (msleep_interruptible(100)) break; + + if (time_is_before_jiffies(end)) { + IWL_ERR(mvm, + "ending pseudo-D3 with timeout after ~60 seconds\n"); + return -ETIMEDOUT; + } } return 0; From c4302c0f2dd31cd071ae8d49370b49bd78a22a9b Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Mon, 5 Feb 2024 21:21:12 +0200 Subject: [PATCH 296/378] wifi: iwlwifi: mvm: always update keys in D3 exit If during D3 there was both a GTK rekey and a disconnection, when waking up, we must first update the new keys and then disconnect. The reason is that when disconnecting we first need to remove the keys. Trying to remove invalid keys results in firmware assert. Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.68cf3974b5d7.Iac9b71a1906ab973aba9baadc9e923b63c0b4945@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index af449cb9f967..89030647e639 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2100,7 +2100,6 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, .status = status, }; int i; - u32 disconnection_reasons = IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; @@ -2108,9 +2107,6 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, if (!status || !vif->bss_conf.bssid) return false; - if (status->wakeup_reasons & disconnection_reasons) - return false; - if (iwl_mvm_lookup_wowlan_status_ver(mvm) > 6 || iwl_fw_lookup_notif_ver(mvm->fw, PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION, @@ -2171,6 +2167,9 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, mvmvif->seqno = status->non_qos_seq_ctr + 0x10; } + if (status->wakeup_reasons & disconnection_reasons) + return false; + return true; } From 0c1c91604f3e3fc41f4d77dcfc3753860a9a32c9 Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Mon, 5 Feb 2024 21:21:13 +0200 Subject: [PATCH 297/378] wifi: iwlwifi: mvm: avoid garbage iPN After waking from D3, we set the iPN given by the firmware. For some reason, CIPHER_SUITE_AES_CMAC was missed. That caused copying garbage to the iPN - causing false replays. (since 'seq' is on the stack, and the iPN from the firmware was not copied into it, it contains garbage which later is copied to the iPN key). Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.2be5b35be30f.I99db8700d01092d22a6d76f1fc1bd5916c9df784@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 89030647e639..5bc08c1d207a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1866,9 +1866,12 @@ iwl_mvm_d3_set_igtk_bigtk_ipn(const struct iwl_multicast_key_data *key, memcpy(seq->aes_gmac.pn, key->ipn, sizeof(seq->aes_gmac.pn)); break; case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_AES_CMAC: BUILD_BUG_ON(sizeof(seq->aes_cmac.pn) != sizeof(key->ipn)); memcpy(seq->aes_cmac.pn, key->ipn, sizeof(seq->aes_cmac.pn)); break; + default: + WARN_ON(1); } } From 2e0e766bd8a7f14f10c3e70b8203c4c1e6d9ec76 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 21:21:14 +0200 Subject: [PATCH 298/378] wifi: iwlwifi: mvm: fix erroneous queue index mask When retrieving the queue index ("SCD SSN") from the TX response, it's currently masked with 0xFFF. However, now that we have queues longer than 4k, that became wrong, so make the mask depend on the hardware family. This fixes an issue where if we get a single frame reclaim while in the top half of an 8k long queue, we'd reclaim-wrap the queue twice (once on this and then again on the next non-single reclaim) which at least triggers the WARN_ON_ONCE() in iwl_txq_reclaim(), but could have other negative side effects (such as unmapping a frame that wasn't transmitted yet, and then taking an IOMMU fault) as well. Fixes: 7b3e42ea2ead ("iwlwifi: support multiple tfd queue max sizes for different devices") Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.4148a6ef54e0.I733a70f679c25f9f99097a8dcb3a1f8165da6997@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 58d1f283d628..f401cd6ef3ac 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1612,12 +1612,18 @@ static void iwl_mvm_tx_status_check_trigger(struct iwl_mvm *mvm, * of the batch. This is why the SSN of the SCD is written at the end of the * whole struct at a variable offset. This function knows how to cope with the * variable offset and returns the SSN of the SCD. + * + * For 22000-series and lower, this is just 12 bits. For later, 16 bits. */ static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm *mvm, struct iwl_mvm_tx_resp *tx_resp) { - return le32_to_cpup((__le32 *)iwl_mvm_get_agg_status(mvm, tx_resp) + - tx_resp->frame_count) & 0xfff; + u32 val = le32_to_cpup((__le32 *)iwl_mvm_get_agg_status(mvm, tx_resp) + + tx_resp->frame_count); + + if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + return val & 0xFFFF; + return val & 0xFFF; } static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, From c82a950f63a32c3148db1c6e4a3bd7140a11a95d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Feb 2024 21:21:15 +0200 Subject: [PATCH 299/378] wifi: iwlwifi: mvm: don't do duplicate detection for nullfunc packets For non-QoS nullfunc packets we currently do the duplicate detection, which seems a bit wrong. Fix the code to check for _any_ instead of just _qos_ nullfunc. Also remove setting the RX_FLAG_DUP_VALIDATED flag, we haven't done anything here; in particular, we haven't checked for multicast in an MLO scenario. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.4fea3bd2d4a6.Ib80764f4581d875cff08469016894f7c817c3828@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 7c0da3b8ad77..b7639e429889 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -519,11 +519,9 @@ static bool iwl_mvm_is_dup(struct ieee80211_sta *sta, int queue, * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") */ if (ieee80211_is_ctl(hdr->frame_control) || - ieee80211_is_qos_nullfunc(hdr->frame_control) || - is_multicast_ether_addr(hdr->addr1)) { - rx_status->flag |= RX_FLAG_DUP_VALIDATED; + ieee80211_is_any_nullfunc(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) return false; - } if (ieee80211_is_data_qos(hdr->frame_control)) { /* frame has qos control */ From 38a3241f2f7d6e863c71cd6f5fabf2a000d89c9f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 18:02:05 +0200 Subject: [PATCH 300/378] wifi: iwlwifi: fw: allow vmalloc for PNVM image This image can be pretty big (I've seen order-7 allocations!), and we later have to copy it to DMA memory (in newer FW even there it won't need to be contiguous), so we can easily deal with it being in vmalloc. Use kvmemdup()/kvfree() for it. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.9b4c06b5d533.Idf699b36ec95ee36f530355cd2cb1da297a098f1@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index 053174f00e49..1195e708caa9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2020-2023 Intel Corporation + * Copyright(c) 2020-2024 Intel Corporation */ #include "iwl-drv.h" @@ -252,7 +252,7 @@ static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len) } new_len = pnvm->size; - *data = kmemdup(pnvm->data, pnvm->size, GFP_KERNEL); + *data = kvmemdup(pnvm->data, pnvm->size, GFP_KERNEL); release_firmware(pnvm); if (!*data) @@ -275,8 +275,8 @@ static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len) if (*len >= sizeof(*package)) { /* we need only the data */ *len -= sizeof(*package); - image = kmemdup(package->data, - *len, GFP_KERNEL); + image = kvmemdup(package->data, + *len, GFP_KERNEL); } /* * free package regardless of whether kmemdup @@ -333,7 +333,7 @@ static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans, set: iwl_trans_set_pnvm(trans, capa); free: - kfree(data); + kvfree(data); kfree(pnvm_data); } From e35f316bce9e5733c9826120c1838f4c447b2c4c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 6 Feb 2024 18:02:06 +0200 Subject: [PATCH 301/378] wifi: iwlwifi: mvm: don't set the MFP flag for the GTK The firmware doesn't need the MFP flag for the GTK, it can even make the firmware crash. in case the AP is configured with: group cipher TKIP and MFPC. We would send the GTK with cipher = TKIP and MFP which is of course not possible. Fixes: 5c75a208c244 ("wifi: iwlwifi: mvm: support new key API") Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.2f2c602ab3c6.If13b2e2fa532381d985c07df130bee1478046c89@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/mvm/mld-key.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c index bbd37a95d4c8..8a38fc4b0b0f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022 - 2023 Intel Corporation + * Copyright (C) 2022 - 2024 Intel Corporation */ #include #include @@ -62,11 +62,13 @@ u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, struct ieee80211_key_conf *keyconf) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool pairwise = keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE; + bool igtk = keyconf->keyidx == 4 || keyconf->keyidx == 5; u32 flags = 0; lockdep_assert_held(&mvm->mutex); - if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + if (!pairwise) flags |= IWL_SEC_KEY_FLAG_MCAST_KEY; switch (keyconf->cipher) { @@ -96,12 +98,14 @@ u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, if (!sta && vif->type == NL80211_IFTYPE_STATION) sta = mvmvif->ap_sta; - /* Set the MFP flag also for an AP interface where the key is an IGTK - * key as in such a case the station would always be NULL + /* + * If we are installing an iGTK (in AP or STA mode), we need to tell + * the firmware this key will en/decrypt MGMT frames. + * Same goes if we are installing a pairwise key for an MFP station. + * In case we're installing a groupwise key (which is not an iGTK), + * then, we will not use this key for MGMT frames. */ - if ((!IS_ERR_OR_NULL(sta) && sta->mfp) || - (vif->type == NL80211_IFTYPE_AP && - (keyconf->keyidx == 4 || keyconf->keyidx == 5))) + if ((!IS_ERR_OR_NULL(sta) && sta->mfp && pairwise) || igtk) flags |= IWL_SEC_KEY_FLAG_MFP; if (keyconf->flags & IEEE80211_KEY_FLAG_SPP_AMSDU) From ff04f78ce38d79bdbf6b1fd276a6a2d035e21951 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 6 Feb 2024 18:02:07 +0200 Subject: [PATCH 302/378] wifi: iwlwifi: mvm: don't send the smart fifo command if not needed Newer firmware versions no longer needs this command. Don't send it if the firmware advertises it does not need it. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.527595995aa0.I0381bef1dc815945f2ec194fecc657e5c75bb2ec@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/file.h | 5 ++++- drivers/net/wireless/intel/iwlwifi/mvm/sf.c | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index b7ef0882dbad..08d9aeaedd99 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2008-2014, 2018-2023 Intel Corporation + * Copyright (C) 2008-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -247,6 +247,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * logic. * @IWL_UCODE_TLV_API_INT_DBG_BUF_CLEAR: Firmware supports clearing the debug * internal buffer + * @IWL_UCODE_TLV_API_SMART_FIFO_OFFLOAD: Firmware doesn't need the host to + * configure the smart fifo * * @NUM_IWL_UCODE_TLV_API: number of bits used */ @@ -287,6 +289,7 @@ enum iwl_ucode_tlv_api { /* API Set 2 */ IWL_UCODE_TLV_API_NO_HOST_DISABLE_TX = (__force iwl_ucode_tlv_api_t)66, IWL_UCODE_TLV_API_INT_DBG_BUF_CLEAR = (__force iwl_ucode_tlv_api_t)67, + IWL_UCODE_TLV_API_SMART_FIFO_OFFLOAD = (__force iwl_ucode_tlv_api_t)68, NUM_IWL_UCODE_TLV_API /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c index 30d4233595e8..16285ae7cae9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2013-2014, 2018-2019, 2022-2023 Intel Corporation + * Copyright (C) 2013-2014, 2018-2019, 2022-2024 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH */ #include "mvm.h" @@ -232,6 +232,9 @@ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif, }; struct ieee80211_sta *sta = NULL; + if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_SMART_FIFO_OFFLOAD)) + return 0; /* * Ignore the call if we are in HW Restart flow, or if the handled * vif is a p2p device. From 5f4e0994996fa08d57711b5b247a0cb3085ec66a Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Tue, 6 Feb 2024 18:02:08 +0200 Subject: [PATCH 303/378] wifi: iwlwifi: pcie: Add new PCI device id and CNVI Add the support for a new PCIE device-id 0x272E and a new CNVI type. Signed-off-by: Mukesh Sisodiya Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.506db9b4a664.Ia2e3a77b880c449ac0e8d20b8cea25e6f07f1b81@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 38 +++++++++++++++++-- .../net/wireless/intel/iwlwifi/iwl-config.h | 8 +++- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 15 +++++++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index e0679093ed8e..be98a174a311 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #include #include @@ -33,6 +33,10 @@ #define IWL_SC_A_GF_A_FW_PRE "iwlwifi-sc-a0-gf-a0" #define IWL_SC_A_GF4_A_FW_PRE "iwlwifi-sc-a0-gf4-a0" #define IWL_SC_A_WH_A_FW_PRE "iwlwifi-sc-a0-wh-a0" +#define IWL_SC2_A_FM_C_FW_PRE "iwlwifi-sc2-a0-fm-c0" +#define IWL_SC2_A_WH_A_FW_PRE "iwlwifi-sc2-a0-wh-a0" +#define IWL_SC2F_A_FM_C_FW_PRE "iwlwifi-sc2f-a0-fm-c0" +#define IWL_SC2F_A_WH_A_FW_PRE "iwlwifi-sc2f-a0-wh-a0" #define IWL_SC_A_FM_B_FW_MODULE_FIRMWARE(api) \ IWL_SC_A_FM_B_FW_PRE "-" __stringify(api) ".ucode" @@ -48,6 +52,14 @@ IWL_SC_A_GF4_A_FW_PRE "-" __stringify(api) ".ucode" #define IWL_SC_A_WH_A_FW_MODULE_FIRMWARE(api) \ IWL_SC_A_WH_A_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_SC2_A_FM_C_FW_MODULE_FIRMWARE(api) \ + IWL_SC2_A_FM_C_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_SC2_A_WH_A_FW_MODULE_FIRMWARE(api) \ + IWL_SC2_A_WH_A_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_SC2F_A_FM_C_FW_MODULE_FIRMWARE(api) \ + IWL_SC2F_A_FM_C_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_SC2F_A_WH_A_FW_MODULE_FIRMWARE(api) \ + IWL_SC2F_A_WH_A_FW_PRE "-" __stringify(api) ".ucode" static const struct iwl_base_params iwl_sc_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, @@ -124,6 +136,9 @@ static const struct iwl_base_params iwl_sc_base_params = { #define IWL_DEVICE_SC \ IWL_DEVICE_BZ_COMMON, \ + .uhb_supported = true, \ + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ + .num_rbds = IWL_NUM_RBDS_SC_EHT, \ .ht_params = &iwl_22000_ht_params /* @@ -149,10 +164,21 @@ const char iwl_sc_name[] = "Intel(R) TBD Sc device"; const struct iwl_cfg iwl_cfg_sc = { .fw_name_mac = "sc", - .uhb_supported = true, IWL_DEVICE_SC, - .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, - .num_rbds = IWL_NUM_RBDS_SC_EHT, +}; + +const char iwl_sc2_name[] = "Intel(R) TBD Sc2 device"; + +const struct iwl_cfg iwl_cfg_sc2 = { + .fw_name_mac = "sc2", + IWL_DEVICE_SC, +}; + +const char iwl_sc2f_name[] = "Intel(R) TBD Sc2f device"; + +const struct iwl_cfg iwl_cfg_sc2f = { + .fw_name_mac = "sc2f", + IWL_DEVICE_SC, }; MODULE_FIRMWARE(IWL_SC_A_FM_B_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); @@ -162,3 +188,7 @@ MODULE_FIRMWARE(IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_SC_A_GF_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_SC_A_GF4_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_SC_A_WH_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_SC2_A_FM_C_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_SC2_A_WH_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_SC2F_A_FM_C_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_SC2F_A_WH_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 97e73443316a..6aa4f7f9c708 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2005-2014, 2018-2021 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #ifndef __IWL_CONFIG_H__ #define __IWL_CONFIG_H__ @@ -419,6 +419,8 @@ struct iwl_cfg { #define IWL_CFG_MAC_TYPE_BZ 0x46 #define IWL_CFG_MAC_TYPE_GL 0x47 #define IWL_CFG_MAC_TYPE_SC 0x48 +#define IWL_CFG_MAC_TYPE_SC2 0x49 +#define IWL_CFG_MAC_TYPE_SC2F 0x4A #define IWL_CFG_RF_TYPE_TH 0x105 #define IWL_CFG_RF_TYPE_TH1 0x108 @@ -541,6 +543,8 @@ extern const char iwl_ax411_name[]; extern const char iwl_bz_name[]; extern const char iwl_mtp_name[]; extern const char iwl_sc_name[]; +extern const char iwl_sc2_name[]; +extern const char iwl_sc2f_name[]; #if IS_ENABLED(CONFIG_IWLDVM) extern const struct iwl_cfg iwl5300_agn_cfg; extern const struct iwl_cfg iwl5100_agn_cfg; @@ -646,6 +650,8 @@ extern const struct iwl_cfg iwl_cfg_bz; extern const struct iwl_cfg iwl_cfg_gl; extern const struct iwl_cfg iwl_cfg_sc; +extern const struct iwl_cfg iwl_cfg_sc2; +extern const struct iwl_cfg iwl_cfg_sc2f; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 1ed67b76b516..4a657036b9d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -509,6 +509,9 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* Sc devices */ {IWL_PCI_DEVICE(0xE440, PCI_ANY_ID, iwl_sc_trans_cfg)}, + {IWL_PCI_DEVICE(0xE340, PCI_ANY_ID, iwl_sc_trans_cfg)}, + {IWL_PCI_DEVICE(0xD340, PCI_ANY_ID, iwl_sc_trans_cfg)}, + {IWL_PCI_DEVICE(0x6E70, PCI_ANY_ID, iwl_sc_trans_cfg)}, #endif /* CONFIG_IWLMVM */ {0} @@ -1121,6 +1124,16 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc, iwl_sc_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc2, iwl_sc2_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc2f, iwl_sc2f_name), #endif /* CONFIG_IWLMVM */ }; EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table); From d5bd4041cd70faf26fc9a54bd6f172537bbe77f3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 18:02:09 +0200 Subject: [PATCH 304/378] wifi: iwlwifi: mvm: don't set replay counters to 0xff The firmware (later) actually uses the values even for keys that are invalid as far as the host is concerned, later in rekeying, and then only sets the low 48 bits since the PNs are only 48 bits over the air. It does, however, compare the full 64 bits later, obviously causing problems. Remove the memset and use kzalloc instead to avoid any old heap data leaking to the firmware. We already init all the other fields in the struct anyway. This leaves the data set to zero for any unused fields, so the firmware can look at them safely even if they're not used right now. Fixes: 79e561f0f05a ("iwlwifi: mvm: d3: implement RSC command version 5") Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.462101146fef.I10f3855b99417af4247cff04af78dcbc6cb75c9c@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 5bc08c1d207a..e1c77276557d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -461,12 +461,10 @@ static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm, struct wowlan_key_rsc_v5_data data = {}; int i; - data.rsc = kmalloc(sizeof(*data.rsc), GFP_KERNEL); + data.rsc = kzalloc(sizeof(*data.rsc), GFP_KERNEL); if (!data.rsc) return -ENOMEM; - memset(data.rsc, 0xff, sizeof(*data.rsc)); - for (i = 0; i < ARRAY_SIZE(data.rsc->mcast_key_id_map); i++) data.rsc->mcast_key_id_map[i] = IWL_MCAST_KEY_MAP_INVALID; From 87f5b5f2c036cdf1ed2269b1d11f005b9bd0b2dc Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 18:02:10 +0200 Subject: [PATCH 305/378] wifi: iwlwifi: mvm: remove flags for enable/disable beacon filter The flags argument to enable/disable beacon filtering functions is unused and always zero, so just remove it. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.2c739c1034a5.I8619949ad4ebd31593d10ece371ebdc6c48db98f@changeid Signed-off-by: Johannes Berg --- .../wireless/intel/iwlwifi/mvm/debugfs-vif.c | 6 ++-- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 18 ++++++------ .../wireless/intel/iwlwifi/mvm/mld-mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 6 ++-- .../net/wireless/intel/iwlwifi/mvm/power.c | 29 ++++++++----------- 5 files changed, 27 insertions(+), 34 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index 2b96cf9eac72..aa3c9c2cbd7f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -381,9 +381,9 @@ static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf, mutex_lock(&mvm->mutex); iwl_dbgfs_update_bf(vif, param, value); if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) - ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); + ret = iwl_mvm_disable_beacon_filter(mvm, vif); else - ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); + ret = iwl_mvm_enable_beacon_filter(mvm, vif); mutex_unlock(&mvm->mutex); return ret ?: count; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index ab1219f4a5dc..576b90da94ff 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1448,7 +1448,7 @@ int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw, if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) { - ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); + ret = iwl_mvm_enable_beacon_filter(mvm, vif); if (ret) goto out_unlock; @@ -1632,7 +1632,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, goto out_remove_mac; /* beacon filtering */ - ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); + ret = iwl_mvm_disable_beacon_filter(mvm, vif); if (ret) goto out_remove_mac; @@ -2575,7 +2575,7 @@ iwl_mvm_bss_info_changed_station_common(struct iwl_mvm *mvm, iwl_mvm_stop_session_protection(mvm, vif); iwl_mvm_sf_update(mvm, vif, false); - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif)); } if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS | @@ -2596,7 +2596,7 @@ iwl_mvm_bss_info_changed_station_common(struct iwl_mvm *mvm, /* FIXME: need to update per link when FW API will * support it */ - ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); + ret = iwl_mvm_enable_beacon_filter(mvm, vif); if (ret) IWL_ERR(mvm, "failed to update CQM thresholds\n"); @@ -3797,7 +3797,7 @@ iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm, NL80211_TDLS_ENABLE_LINK); } else { /* enable beacon filtering */ - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif)); mvmvif->authorized = 1; @@ -3855,7 +3855,7 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm, mvmvif->authorized = 0; /* disable beacon filtering */ - iwl_mvm_disable_beacon_filter(mvm, vif, 0); + iwl_mvm_disable_beacon_filter(mvm, vif); } return 0; @@ -5299,8 +5299,8 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, return -EINVAL; if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])) - return iwl_mvm_enable_beacon_filter(mvm, vif, 0); - return iwl_mvm_disable_beacon_filter(mvm, vif, 0); + return iwl_mvm_enable_beacon_filter(mvm, vif); + return iwl_mvm_disable_beacon_filter(mvm, vif); } return -EOPNOTSUPP; @@ -5384,7 +5384,7 @@ static int iwl_mvm_old_pre_chan_sw_sta(struct iwl_mvm *mvm, iwl_mvm_csa_client_absent(mvm, vif); if (mvmvif->bf_data.bf_enabled) { - int ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); + int ret = iwl_mvm_disable_beacon_filter(mvm, vif); if (ret) return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 7d89f50fbb10..021592f69f38 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -47,7 +47,7 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, goto out_unlock; /* beacon filtering */ - ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); + ret = iwl_mvm_disable_beacon_filter(mvm, vif); if (ret) goto out_remove_mac; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 937d0bd9c531..6000c79ce4a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2145,11 +2145,9 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, {} #endif int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags); + struct ieee80211_vif *vif); int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags); + struct ieee80211_vif *vif); /* SMPS */ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum iwl_mvm_smps_type_request req_type, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index 1b9b06e0443f..41e68aa6bec8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2019, 2021-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2024 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -20,8 +20,7 @@ static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, - struct iwl_beacon_filter_cmd *cmd, - u32 flags) + struct iwl_beacon_filter_cmd *cmd) { u16 len; @@ -62,7 +61,7 @@ int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, len = offsetof(struct iwl_beacon_filter_cmd, bf_threshold_absolute_low); - return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags, + return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, 0, len, cmd); } @@ -813,8 +812,7 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd, - u32 cmd_flags) + struct iwl_beacon_filter_cmd *cmd) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; @@ -825,7 +823,7 @@ static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd); iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd); - ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags); + ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd); if (!ret) mvmvif->bf_data.bf_enabled = true; @@ -834,20 +832,18 @@ static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, } int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags) + struct ieee80211_vif *vif) { struct iwl_beacon_filter_cmd cmd = { IWL_BF_CMD_CONFIG_DEFAULTS, .bf_enable_beacon_filter = cpu_to_le32(1), }; - return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags); + return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd); } static int _iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags) + struct ieee80211_vif *vif) { struct iwl_beacon_filter_cmd cmd = {}; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -856,7 +852,7 @@ static int _iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; - ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags); + ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); if (!ret) mvmvif->bf_data.bf_enabled = false; @@ -865,10 +861,9 @@ static int _iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, } int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags) + struct ieee80211_vif *vif) { - return _iwl_mvm_disable_beacon_filter(mvm, vif, flags); + return _iwl_mvm_disable_beacon_filter(mvm, vif); } static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm) @@ -919,7 +914,7 @@ static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm, !vif->cfg.ps || iwl_mvm_vif_low_latency(mvmvif)); - return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0); + return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd); } int iwl_mvm_power_update_ps(struct iwl_mvm *mvm) From ae6d30a715210de16682369269cb6c25134ddbe2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 18:02:11 +0200 Subject: [PATCH 306/378] wifi: iwlwifi: mvm: show skb_mac_gso_segment() failure reason If this warning triggers we don't really know why, print out the return value so we can see it. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.b1b907741e85.Ib8ee9c90bd8f1af69969981ff0c63e9cc3123e1f@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index f401cd6ef3ac..cc73d7d7de7d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -939,9 +939,15 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, next = skb_gso_segment(skb, netdev_flags); skb_shinfo(skb)->gso_size = mss; skb_shinfo(skb)->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6; - if (WARN_ON_ONCE(IS_ERR(next))) - return -EINVAL; - else if (next) + + if (IS_ERR(next) && PTR_ERR(next) == -ENOMEM) + return -ENOMEM; + + if (WARN_ONCE(IS_ERR(next), + "skb_gso_segment error: %d\n", (int)PTR_ERR(next))) + return PTR_ERR(next); + + if (next) consume_skb(skb); skb_list_walk_safe(next, tmp, next) { From dbc396244a5e1ce51e90abd3acc98de707445d6d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 18:02:13 +0200 Subject: [PATCH 307/378] wifi: iwlwifi: mvm: move BA notif messages before action This is always a bit confusing, the code first does all the reclaim (with its own debug messages), and _then_ prints it got a BA notification from firmware. Turn that around. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.825245e0803f.Ic607c57f43eb7c7ff122ffee8f3994fd040d578f@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index cc73d7d7de7d..4981bb1f0251 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -2162,6 +2162,12 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) tfd_cnt, pkt_len)) return; + IWL_DEBUG_TX_REPLY(mvm, + "BA_NOTIFICATION Received from sta_id = %d, flags %x, sent:%d, acked:%d\n", + sta_id, le32_to_cpu(ba_res->flags), + le16_to_cpu(ba_res->txed), + le16_to_cpu(ba_res->done)); + rcu_read_lock(); mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, sta_id); @@ -2197,12 +2203,6 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) iwl_mvm_tx_airtime(mvm, mvmsta, le32_to_cpu(ba_res->wireless_time)); rcu_read_unlock(); - - IWL_DEBUG_TX_REPLY(mvm, - "BA_NOTIFICATION Received from sta_id = %d, flags %x, sent:%d, acked:%d\n", - sta_id, le32_to_cpu(ba_res->flags), - le16_to_cpu(ba_res->txed), - le16_to_cpu(ba_res->done)); return; } @@ -2234,9 +2234,6 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) rcu_read_unlock(); - iwl_mvm_tx_reclaim(mvm, sta_id, tid, txq, index, &ba_info, - tid_data->rate_n_flags, false); - IWL_DEBUG_TX_REPLY(mvm, "BA_NOTIFICATION Received from %pM, sta_id = %d\n", ba_notif->sta_addr, ba_notif->sta_id); @@ -2249,6 +2246,9 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) IWL_DEBUG_TX_REPLY(mvm, "reduced txp from ba notif %d\n", ba_notif->reduced_txp); + + iwl_mvm_tx_reclaim(mvm, sta_id, tid, txq, index, &ba_info, + tid_data->rate_n_flags, false); } /* From 4dbc306e0736685c5298073bed19cbf9f4b4cb92 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 18:02:14 +0200 Subject: [PATCH 308/378] wifi: iwlwifi: queue: improve warning for no skb in reclaim We've seen this warning trigger, and while the reason is probably obvious, I haven't been able to see it yet. Add more information to the warning message to help identify the cause. Also print out both index and SSN for all the messages. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.581427dc81fc.I9a109d02b4349807dce521c693ecd3516ec58cc0@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/queue/tx.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/queue/tx.c b/drivers/net/wireless/intel/iwlwifi/queue/tx.c index ba0419bc1765..d3bde2d010b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/queue/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/queue/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2020-2023 Intel Corporation + * Copyright (C) 2020-2024 Intel Corporation */ #include #include @@ -1602,8 +1602,8 @@ void iwl_txq_reclaim(struct iwl_trans *trans, int txq_id, int ssn, if (read_ptr == tfd_num) goto out; - IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", - txq_id, txq->read_ptr, tfd_num, ssn); + IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d (%d) -> %d (%d)\n", + txq_id, read_ptr, txq->read_ptr, tfd_num, ssn); /*Since we free until index _not_ inclusive, the one before index is * the last we will free. This one must be used */ @@ -1631,7 +1631,8 @@ void iwl_txq_reclaim(struct iwl_trans *trans, int txq_id, int ssn, read_ptr = iwl_txq_get_cmd_index(txq, txq->read_ptr)) { struct sk_buff *skb = txq->entries[read_ptr].skb; - if (WARN_ON_ONCE(!skb)) + if (WARN_ONCE(!skb, "no SKB at %d (%d) on queue %d\n", + read_ptr, txq->read_ptr, txq_id)) continue; iwl_txq_free_tso_page(trans, skb); From d4655db0a1e11eeacc55c44c81121c83b087982e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 9 Feb 2024 08:04:23 +0100 Subject: [PATCH 309/378] wifi: cfg80211: fix kernel-doc for cfg80211_chandef_primary This was still referring to cfg80211_chandef_primary_freq(), fix it. Reported-by: Stephen Rothwell Fixes: b82730bf57b5 ("wifi: cfg80211/mac80211: move puncturing into chandef") Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d4c83ea3213d..f52f989a54ad 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1053,7 +1053,7 @@ cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef); /** - * cfg80211_chandef_primary_freq - calculate primary 40/80/160 MHz freq + * cfg80211_chandef_primary - calculate primary 40/80/160 MHz freq * @chandef: chandef to calculate for * @primary_chan_width: primary channel width to calculate center for * @punctured: punctured sub-channel bitmap, will be recalculated From bad9d21110037fa346426566d34dcffffc166fc6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 12 Feb 2024 12:23:36 +0100 Subject: [PATCH 310/378] wifi: iwlwifi: fix #ifdef CONFIG_ACPI check The #ifdef check around the function definition for two functions was changed without also changing the one on the declaration: drivers/net/wireless/intel/iwlwifi/fw/uefi.c:359:6: error: redefinition of 'iwl_uefi_get_sgom_table' 359 | void iwl_uefi_get_sgom_table(struct iwl_trans *trans, | ^~~~~~~~~~~~~~~~~~~~~~~ In file included from drivers/net/wireless/intel/iwlwifi/fw/uefi.c:11: drivers/net/wireless/intel/iwlwifi/fw/uefi.h:294:6: note: previous definition of 'iwl_uefi_get_sgom_table' with type 'void(struct iwl_trans *, struct iwl_fw_runtime *)' 294 | void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) | ^~~~~~~~~~~~~~~~~~~~~~~ drivers/net/wireless/intel/iwlwifi/fw/uefi.c:392:5: error: redefinition of 'iwl_uefi_get_uats_table' 392 | int iwl_uefi_get_uats_table(struct iwl_trans *trans, | ^~~~~~~~~~~~~~~~~~~~~~~ drivers/net/wireless/intel/iwlwifi/fw/uefi.h:299:5: note: previous definition of 'iwl_uefi_get_uats_table' with type 'int(struct iwl_trans *, struct iwl_fw_runtime *)' 299 | int iwl_uefi_get_uats_table(struct iwl_trans *trans, | ^~~~~~~~~~~~~~~~~~~~~~~ Adapt it by merging the declarations into the existing #ifdef block. Fixes: 74f4cd710705 ("wifi: iwlwifi: take SGOM and UATS code out of ACPI ifdef") Signed-off-by: Arnd Bergmann Link: https://msgid.link/20240212112343.1148931-1-arnd@kernel.org Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 39053290bd59..303cc299d1bc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -204,6 +204,9 @@ int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk); int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, u32 *value); +void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_uats_table(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -283,13 +286,7 @@ static inline int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, { return -ENOENT; } -#endif /* CONFIG_EFI */ -#if defined(CONFIG_EFI) && defined(CONFIG_ACPI) -void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); -int iwl_uefi_get_uats_table(struct iwl_trans *trans, - struct iwl_fw_runtime *fwrt); -#else static inline void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { @@ -301,5 +298,5 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, { return 0; } -#endif +#endif /* CONFIG_EFI */ #endif /* __iwl_fw_uefi__ */ From 425c33264e151578a269c9c0ab9e0fb1d616f518 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Wed, 10 Jan 2024 14:53:11 +0300 Subject: [PATCH 311/378] wifi: mwifiex: use kstrtoX_from_user() in debugfs handlers Use convenient 'kstrtou32_from_user()' in 'mwifiex_verext_write()' and 'kstrtobool_from_user()' in 'mwifiex_timeshare_coex_write()', respectively. Compile tested only. Signed-off-by: Dmitry Antipov Signed-off-by: Kalle Valo Link: https://msgid.link/20240110115314.421298-1-dmantipov@yandex.ru --- .../net/wireless/marvell/mwifiex/debugfs.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c index d14a0f4c1b6d..9deaf59dcb62 100644 --- a/drivers/net/wireless/marvell/mwifiex/debugfs.c +++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c @@ -566,14 +566,8 @@ mwifiex_verext_write(struct file *file, const char __user *ubuf, int ret; u32 versionstrsel; struct mwifiex_private *priv = (void *)file->private_data; - char buf[16]; - memset(buf, 0, sizeof(buf)); - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; - - ret = kstrtou32(buf, 10, &versionstrsel); + ret = kstrtou32_from_user(ubuf, count, 10, &versionstrsel); if (ret) return ret; @@ -874,19 +868,14 @@ mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf, { bool timeshare_coex; struct mwifiex_private *priv = file->private_data; - char kbuf[16]; int ret; if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) return -EOPNOTSUPP; - memset(kbuf, 0, sizeof(kbuf)); - - if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count))) - return -EFAULT; - - if (kstrtobool(kbuf, ×hare_coex)) - return -EINVAL; + ret = kstrtobool_from_user(ubuf, count, ×hare_coex); + if (ret) + return ret; ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, HostCmd_ACT_GEN_SET, 0, ×hare_coex, true); From a8e5fefa91236fd0d51464bf8156d738f0dfab9d Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Mon, 15 Jan 2024 15:56:30 +0100 Subject: [PATCH 312/378] wifi: wilc1000: set preamble size to auto as default in wilc_init_fw_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WILC driver currently applies some default configuration whenever the firmware is initialized, and sets the default preamble size to short. However, despite this passed option, firmware is also able to successfully connect to access points only using long preamble, so this setting does not really enforce short preambles and is misleading regarding applied configuration. Update default configuration and make it match the firmware behavior by passing the existing WILC_FW_PREAMBLE_AUTO value (2 instead of 0). The updated setting does not really alter firmware behavior since it is still capable to connect to both short preamble and long preamble access points, but at list the setting now expresses for real the corresponding firmware behavior. More info: it has been implemented to address the transmission (Tx) blackout issue observed in the 802.11b mode. The modification has no impact on the other modes, which will continue to work as they did in the previous implementation. This change will allow the 802.11b transmission (2, 5.5, 11Mbps) to use long preamble. Signed-off-by: Ajay Singh Signed-off-by: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240115-wilc_1000_fixes-v1-1-54d29463a738@bootlin.com --- drivers/net/wireless/microchip/wilc1000/netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index ef22bf6bf86a..a8b5c8dec69c 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -284,7 +284,7 @@ static int wilc_init_fw_config(struct net_device *dev, struct wilc_vif *vif) if (!wilc_wlan_cfg_set(vif, 0, WID_11G_OPERATING_MODE, &b, 1, 0, 0)) goto fail; - b = WILC_FW_PREAMBLE_SHORT; + b = WILC_FW_PREAMBLE_AUTO; if (!wilc_wlan_cfg_set(vif, 0, WID_PREAMBLE, &b, 1, 0, 0)) goto fail; From 188045a85614433c8aa176f7899fcfb7c7183408 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 6 Feb 2024 11:06:19 +0800 Subject: [PATCH 313/378] wifi: rtw89: drop TIMING_BEACON_ONLY and sync beacon TSF by self Some of our calculation during concurrent mode depend on last beacon TSF. Originally, we just set IEEE80211_HW_TIMING_BEACON_ONLY and get what we want from mac80211. But, IEEE80211_HW_TIMING_BEACON_ONLY will be restricted once we declare MLO. Since we are about to consider the MLO stuffs, so sync beacon TSF by ourselves now and unset IEEE80211_HW_TIMING_BEACON_ONLY. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240206030624.23382-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 9 +-------- drivers/net/wireless/realtek/rtw89/core.c | 18 ++++++++++++++---- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac80211.c | 1 + 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 21449cb9b069..2a95f9db83f9 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -320,19 +320,12 @@ int rtw89_iterate_mcc_roles(struct rtw89_dev *rtwdev, return 0; } -/* For now, IEEE80211_HW_TIMING_BEACON_ONLY can make things simple to ensure - * correctness of MCC calculation logic below. We have noticed that once driver - * declares WIPHY_FLAG_SUPPORTS_MLO, the use of IEEE80211_HW_TIMING_BEACON_ONLY - * will be restricted. We will make an alternative in driver when it is ready - * for MLO. - */ static u32 rtw89_mcc_get_tbtt_ofst(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role, u64 tsf) { struct rtw89_vif *rtwvif = role->rtwvif; - struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); u32 bcn_intvl_us = ieee80211_tu_to_usec(role->beacon_interval); - u64 sync_tsf = vif->bss_conf.sync_tsf; + u64 sync_tsf = READ_ONCE(rtwvif->sync_bcn_tsf); u32 remainder; if (tsf < sync_tsf) { diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 650c507c8ed3..95ace26a8f66 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1868,6 +1868,17 @@ static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, ieee80211_queue_work(rtwdev->hw, &rtwdev->cancel_6ghz_probe_work); } +static void rtw89_vif_sync_bcn_tsf(struct rtw89_vif *rtwvif, + struct ieee80211_hdr *hdr, size_t len) +{ + struct ieee80211_mgmt *mgmt = (typeof(mgmt))hdr; + + if (len < offsetof(typeof(*mgmt), u.beacon.variable)) + return; + + WRITE_ONCE(rtwvif->sync_bcn_tsf, le64_to_cpu(mgmt->u.beacon.timestamp)); +} + static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { @@ -1898,8 +1909,10 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, return; if (ieee80211_is_beacon(hdr->frame_control)) { - if (vif->type == NL80211_IFTYPE_STATION) + if (vif->type == NL80211_IFTYPE_STATION) { + rtw89_vif_sync_bcn_tsf(rtwvif, hdr, skb->len); rtw89_fw_h2c_rssi_offload(rtwdev, phy_ppdu); + } pkt_stat->beacon_nr++; } @@ -4447,9 +4460,6 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); ieee80211_hw_set(hw, WANT_MONITOR_VIF); - /* ref: description of rtw89_mcc_get_tbtt_ofst() in chan.c */ - ieee80211_hw_set(hw, TIMING_BEACON_ONLY); - if (chip->support_bandwidths & BIT(NL80211_CHAN_WIDTH_160)) ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 713383b6d818..75269c1d94cc 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3044,6 +3044,7 @@ struct rtw89_vif { u8 bcn_hit_cond; u8 hit_rule; u8 last_noa_nr; + u64 sync_bcn_tsf; bool offchan; bool trigger; bool lsig_txop; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index b61c5be8cae3..31d1ffb16e83 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -449,6 +449,7 @@ static void rtw89_ops_bss_info_changed(struct ieee80211_hw *hw, ether_addr_copy(rtwvif->bssid, conf->bssid); rtw89_cam_bssid_changed(rtwdev, rtwvif); rtw89_fw_h2c_cam(rtwdev, rtwvif, NULL, NULL); + WRITE_ONCE(rtwvif->sync_bcn_tsf, 0); } if (changed & BSS_CHANGED_BEACON) From 4f0beeefcce8ad1035d063a012db6cf510aa1ed1 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 6 Feb 2024 11:06:20 +0800 Subject: [PATCH 314/378] wifi: rtw89: chan: add sub-entity swap function to cover replacing Originally, we replaced sub-entity of index 0 with another one in some cases. However, we will need a swap here in following implementations. So, we introduce it ahead and change code from replacing to swapping. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240206030624.23382-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 47 +++++++++++++++++------ 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 2a95f9db83f9..11d46878f51e 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -1893,6 +1893,41 @@ void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev) rtw89_queue_chanctx_work(rtwdev); } +static void rtw89_swap_sub_entity(struct rtw89_dev *rtwdev, + enum rtw89_sub_entity_idx idx1, + enum rtw89_sub_entity_idx idx2) +{ + struct rtw89_hal *hal = &rtwdev->hal; + struct rtw89_sub_entity tmp; + struct rtw89_vif *rtwvif; + u8 cur; + + if (idx1 == idx2) + return; + + hal->sub[idx1].cfg->idx = idx2; + hal->sub[idx2].cfg->idx = idx1; + + tmp = hal->sub[idx1]; + hal->sub[idx1] = hal->sub[idx2]; + hal->sub[idx2] = tmp; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + if (!rtwvif->chanctx_assigned) + continue; + if (rtwvif->sub_entity_idx == idx1) + rtwvif->sub_entity_idx = idx2; + else if (rtwvif->sub_entity_idx == idx2) + rtwvif->sub_entity_idx = idx1; + } + + cur = atomic_read(&hal->roc_entity_idx); + if (cur == idx1) + atomic_set(&hal->roc_entity_idx, idx2); + else if (cur == idx2) + atomic_set(&hal->roc_entity_idx, idx1); +} + int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev, struct ieee80211_chanctx_conf *ctx) { @@ -1918,7 +1953,6 @@ void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev, struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; enum rtw89_entity_mode mode; - struct rtw89_vif *rtwvif; u8 drop, roll; drop = cfg->idx; @@ -1934,16 +1968,7 @@ void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev, /* RTW89_SUB_ENTITY_0 is going to release, and another exists. * Make another roll down to RTW89_SUB_ENTITY_0 to replace. */ - hal->sub[roll].cfg->idx = RTW89_SUB_ENTITY_0; - hal->sub[RTW89_SUB_ENTITY_0] = hal->sub[roll]; - - rtw89_for_each_rtwvif(rtwdev, rtwvif) { - if (rtwvif->sub_entity_idx == roll) - rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0; - } - - atomic_cmpxchg(&hal->roc_entity_idx, roll, RTW89_SUB_ENTITY_0); - + rtw89_swap_sub_entity(rtwdev, RTW89_SUB_ENTITY_0, roll); drop = roll; out: From ab12a3bfbf775182dc86ef8f70eab487c5deb761 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 6 Feb 2024 11:06:21 +0800 Subject: [PATCH 315/378] wifi: rtw89: chan: tweak bitmap recalc ahead before MLO Originally, we just declared two sub-entity, and according to rolling down mechanism, we ensured that index 0 contained sub-entity as long as there are sub-entity. So, we could use for-loop after deciding the last index. But, we are preparing to expand num of sub-entity for MLO. Then, there won't be just two sub-entity. And, there might be holes between two bits in the bitmap. So, we cannot simply do for-loop as before. Instead, we need to follow the set bits. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240206030624.23382-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 11d46878f51e..6a666a92b59b 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -214,31 +214,32 @@ void rtw89_entity_init(struct rtw89_dev *rtwdev) enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) { + DECLARE_BITMAP(recalc_map, NUM_OF_RTW89_SUB_ENTITY) = {}; struct rtw89_hal *hal = &rtwdev->hal; const struct cfg80211_chan_def *chandef; enum rtw89_entity_mode mode; struct rtw89_chan chan; u8 weight; - u8 last; u8 idx; lockdep_assert_held(&rtwdev->mutex); + bitmap_copy(recalc_map, hal->entity_map, NUM_OF_RTW89_SUB_ENTITY); + weight = bitmap_weight(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY); switch (weight) { default: rtw89_warn(rtwdev, "unknown ent chan weight: %d\n", weight); - bitmap_zero(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY); + bitmap_zero(recalc_map, NUM_OF_RTW89_SUB_ENTITY); fallthrough; case 0: rtw89_config_default_chandef(rtwdev); + set_bit(RTW89_SUB_ENTITY_0, recalc_map); fallthrough; case 1: - last = RTW89_SUB_ENTITY_0; mode = RTW89_ENTITY_MODE_SCC; break; case 2: - last = RTW89_SUB_ENTITY_1; mode = rtw89_get_entity_mode(rtwdev); if (mode == RTW89_ENTITY_MODE_MCC) break; @@ -247,7 +248,7 @@ enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) break; } - for (idx = 0; idx <= last; idx++) { + for_each_set_bit(idx, recalc_map, NUM_OF_RTW89_SUB_ENTITY) { chandef = rtw89_chandef_get(rtwdev, idx); rtw89_get_channel_params(chandef, &chan); if (chan.channel == 0) { From d79fa0a6d8c229435e48d239dc1f0a0f39235fc8 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 6 Feb 2024 11:06:22 +0800 Subject: [PATCH 316/378] wifi: rtw89: chan: tweak weight recalc ahead before MLO Originally, we consider weight only based on how many chanctxs that mac80211 sets. However, we need to consider both active chanctxs and active interfaces to distinguish MCC (multiple channel concurrent) from impending MLO. Although the logic of handling is extended, for now, behavior might not be different under current condition. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240206030624.23382-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 51 ++++++++++++++++++++--- drivers/net/wireless/realtek/rtw89/chan.h | 5 +++ drivers/net/wireless/realtek/rtw89/core.h | 6 ++- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 6a666a92b59b..57fabc05dab9 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -212,24 +212,51 @@ void rtw89_entity_init(struct rtw89_dev *rtwdev) rtw89_config_default_chandef(rtwdev); } +static void rtw89_entity_calculate_weight(struct rtw89_dev *rtwdev, + struct rtw89_entity_weight *w) +{ + struct rtw89_hal *hal = &rtwdev->hal; + const struct rtw89_chanctx_cfg *cfg; + struct rtw89_vif *rtwvif; + int idx; + + for_each_set_bit(idx, hal->entity_map, NUM_OF_RTW89_SUB_ENTITY) { + cfg = hal->sub[idx].cfg; + if (!cfg) { + /* doesn't run with chanctx ops; one channel at most */ + w->active_chanctxs = 1; + break; + } + + if (cfg->ref_count > 0) + w->active_chanctxs++; + } + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + if (rtwvif->chanctx_assigned) + w->active_roles++; + } +} + enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) { DECLARE_BITMAP(recalc_map, NUM_OF_RTW89_SUB_ENTITY) = {}; struct rtw89_hal *hal = &rtwdev->hal; const struct cfg80211_chan_def *chandef; + struct rtw89_entity_weight w = {}; enum rtw89_entity_mode mode; struct rtw89_chan chan; - u8 weight; u8 idx; lockdep_assert_held(&rtwdev->mutex); bitmap_copy(recalc_map, hal->entity_map, NUM_OF_RTW89_SUB_ENTITY); - weight = bitmap_weight(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY); - switch (weight) { + rtw89_entity_calculate_weight(rtwdev, &w); + switch (w.active_chanctxs) { default: - rtw89_warn(rtwdev, "unknown ent chan weight: %d\n", weight); + rtw89_warn(rtwdev, "unknown ent chanctxs weight: %d\n", + w.active_chanctxs); bitmap_zero(recalc_map, NUM_OF_RTW89_SUB_ENTITY); fallthrough; case 0: @@ -239,7 +266,14 @@ enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) case 1: mode = RTW89_ENTITY_MODE_SCC; break; - case 2: + case 2 ... NUM_OF_RTW89_SUB_ENTITY: + if (w.active_roles != NUM_OF_RTW89_MCC_ROLES) { + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "unhandled ent: %d chanctxs %d roles\n", + w.active_chanctxs, w.active_roles); + return RTW89_ENTITY_MODE_UNHANDLED; + } + mode = rtw89_get_entity_mode(rtwdev); if (mode == RTW89_ENTITY_MODE_MCC) break; @@ -582,6 +616,9 @@ static int rtw89_mcc_fill_all_roles(struct rtw89_dev *rtwdev) int ret; rtw89_for_each_rtwvif(rtwdev, rtwvif) { + if (!rtwvif->chanctx_assigned) + continue; + if (sel.bind_vif[rtwvif->sub_entity_idx]) { rtw89_warn(rtwdev, "MCC skip extra vif on chanctx[%d]\n", @@ -2007,6 +2044,7 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, rtwvif->sub_entity_idx = cfg->idx; rtwvif->chanctx_assigned = true; + cfg->ref_count++; return 0; } @@ -2014,6 +2052,9 @@ void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, struct ieee80211_chanctx_conf *ctx) { + struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; + rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0; rtwvif->chanctx_assigned = false; + cfg->ref_count--; } diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h index 9b98d8f4ee9d..ffa412f281f3 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.h +++ b/drivers/net/wireless/realtek/rtw89/chan.h @@ -38,6 +38,11 @@ enum rtw89_chanctx_pause_reasons { RTW89_CHANCTX_PAUSE_REASON_ROC, }; +struct rtw89_entity_weight { + unsigned int active_chanctxs; + unsigned int active_roles; +}; + static inline bool rtw89_get_entity_state(struct rtw89_dev *rtwdev) { struct rtw89_hal *hal = &rtwdev->hal; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 75269c1d94cc..d98dcf12bef7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4136,6 +4136,7 @@ struct rtw89_tas_info { struct rtw89_chanctx_cfg { enum rtw89_sub_entity_idx idx; + int ref_count; }; enum rtw89_chanctx_changes { @@ -4155,13 +4156,16 @@ enum rtw89_entity_mode { RTW89_ENTITY_MODE_MCC, NUM_OF_RTW89_ENTITY_MODE, - RTW89_ENTITY_MODE_INVALID = NUM_OF_RTW89_ENTITY_MODE, + RTW89_ENTITY_MODE_INVALID = -EINVAL, + RTW89_ENTITY_MODE_UNHANDLED = -ESRCH, }; struct rtw89_sub_entity { struct cfg80211_chan_def chandef; struct rtw89_chan chan; struct rtw89_chan_rcd rcd; + + /* only assigned when running with chanctx_ops */ struct rtw89_chanctx_cfg *cfg; }; From 1ae9fbaf22ee82bb3ed47ac12ea92dbb7c51769f Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 6 Feb 2024 11:06:23 +0800 Subject: [PATCH 317/378] wifi: rtw89: chan: move handling from add/remove to assign/unassign for MLO After MLO, we will need to consider not only active chanctx but also active interfaces (roles) to decide entity things. So in advance, we move handling from chanctx_ops::add/remove to chanctx_ops::assign_vif/unassign_vif. Then, we can recalculate and aware active interfaces' changes. For now, behavior should not be really different, since active chanctx and active interface are one-to-one mapping before MLO. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240206030624.23382-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 75 +++++++++++++---------- drivers/net/wireless/realtek/rtw89/core.c | 5 +- drivers/net/wireless/realtek/rtw89/core.h | 2 +- 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 57fabc05dab9..71fe0d3ab3b0 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -1979,7 +1979,6 @@ int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev, return -ENOENT; rtw89_config_entity_chandef(rtwdev, idx, &ctx->def); - rtw89_set_channel(rtwdev); cfg->idx = idx; hal->sub[idx].cfg = cfg; return 0; @@ -1990,37 +1989,8 @@ void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; - enum rtw89_entity_mode mode; - u8 drop, roll; - drop = cfg->idx; - if (drop != RTW89_SUB_ENTITY_0) - goto out; - - roll = find_next_bit(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY, drop + 1); - - /* Follow rtw89_config_default_chandef() when rtw89_entity_recalc(). */ - if (roll == NUM_OF_RTW89_SUB_ENTITY) - goto out; - - /* RTW89_SUB_ENTITY_0 is going to release, and another exists. - * Make another roll down to RTW89_SUB_ENTITY_0 to replace. - */ - rtw89_swap_sub_entity(rtwdev, RTW89_SUB_ENTITY_0, roll); - drop = roll; - -out: - mode = rtw89_get_entity_mode(rtwdev); - switch (mode) { - case RTW89_ENTITY_MODE_MCC: - rtw89_mcc_stop(rtwdev); - break; - default: - break; - } - - clear_bit(drop, hal->entity_map); - rtw89_set_channel(rtwdev); + clear_bit(cfg->idx, hal->entity_map); } void rtw89_chanctx_ops_change(struct rtw89_dev *rtwdev, @@ -2045,7 +2015,8 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, rtwvif->sub_entity_idx = cfg->idx; rtwvif->chanctx_assigned = true; cfg->ref_count++; - return 0; + + return rtw89_set_channel(rtwdev); } void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev, @@ -2053,8 +2024,48 @@ void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev, struct ieee80211_chanctx_conf *ctx) { struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; + struct rtw89_hal *hal = &rtwdev->hal; + struct rtw89_entity_weight w = {}; + enum rtw89_sub_entity_idx roll; + enum rtw89_entity_mode cur; rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0; rtwvif->chanctx_assigned = false; cfg->ref_count--; + + if (cfg->ref_count != 0) + goto out; + + if (cfg->idx != RTW89_SUB_ENTITY_0) + goto out; + + roll = find_next_bit(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY, + cfg->idx + 1); + /* Follow rtw89_config_default_chandef() when rtw89_entity_recalc(). */ + if (roll == NUM_OF_RTW89_SUB_ENTITY) + goto out; + + /* RTW89_SUB_ENTITY_0 is going to release, and another exists. + * Make another roll down to RTW89_SUB_ENTITY_0 to replace. + */ + rtw89_swap_sub_entity(rtwdev, cfg->idx, roll); + +out: + rtw89_entity_calculate_weight(rtwdev, &w); + + cur = rtw89_get_entity_mode(rtwdev); + switch (cur) { + case RTW89_ENTITY_MODE_MCC: + /* If still multi-roles, re-plan MCC for chanctx changes. + * Otherwise, just stop MCC. + */ + rtw89_mcc_stop(rtwdev); + if (w.active_roles == NUM_OF_RTW89_MCC_ROLES) + rtw89_mcc_start(rtwdev); + break; + default: + break; + } + + rtw89_set_channel(rtwdev); } diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 95ace26a8f66..6441d99cd6c0 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -372,7 +372,7 @@ void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev) chip->ops->set_txpwr(rtwdev, chan, phy_idx); } -void rtw89_set_channel(struct rtw89_dev *rtwdev) +int rtw89_set_channel(struct rtw89_dev *rtwdev) { struct rtw89_hal *hal = &rtwdev->hal; const struct rtw89_chip_info *chip = rtwdev->chip; @@ -399,7 +399,7 @@ void rtw89_set_channel(struct rtw89_dev *rtwdev) break; default: WARN(1, "Invalid ent mode: %d\n", mode); - return; + return -EINVAL; } roc_idx = atomic_read(&hal->roc_entity_idx); @@ -426,6 +426,7 @@ void rtw89_set_channel(struct rtw89_dev *rtwdev) } rtw89_set_entity_state(rtwdev, true); + return 0; } void rtw89_get_channel(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d98dcf12bef7..462c4b03c76b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6045,7 +6045,7 @@ void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev); void rtw89_get_default_chandef(struct cfg80211_chan_def *chandef); void rtw89_get_channel_params(const struct cfg80211_chan_def *chandef, struct rtw89_chan *chan); -void rtw89_set_channel(struct rtw89_dev *rtwdev); +int rtw89_set_channel(struct rtw89_dev *rtwdev); void rtw89_get_channel(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, struct rtw89_chan *chan); u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size); From 162bf67f74c71d56889c1c938d7901cfb76e2cbd Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 6 Feb 2024 11:06:24 +0800 Subject: [PATCH 318/378] wifi: rtw89: chan: MCC take reconfig into account During mac80211 reconfig, chanctx ops of multiple channels might not be called in order as normal cases. However, we expect the first active chanctx always to be put at our sub entity index 0. So, if it does not, we do a swap there. Besides, reconfig won't allocate a new chanctx object. So, we should reset the reference count when ops add chanctx. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240206030624.23382-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 71fe0d3ab3b0..7b9baf4db70f 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -1980,6 +1980,7 @@ int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev, rtw89_config_entity_chandef(rtwdev, idx, &ctx->def); cfg->idx = idx; + cfg->ref_count = 0; hal->sub[idx].cfg = cfg; return 0; } @@ -2011,11 +2012,23 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, struct ieee80211_chanctx_conf *ctx) { struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; + struct rtw89_entity_weight w = {}; rtwvif->sub_entity_idx = cfg->idx; rtwvif->chanctx_assigned = true; cfg->ref_count++; + if (cfg->idx == RTW89_SUB_ENTITY_0) + goto out; + + rtw89_entity_calculate_weight(rtwdev, &w); + if (w.active_chanctxs != 1) + goto out; + + /* put the first active chanctx at RTW89_SUB_ENTITY_0 */ + rtw89_swap_sub_entity(rtwdev, cfg->idx, RTW89_SUB_ENTITY_0); + +out: return rtw89_set_channel(rtwdev); } From c08a986344a5eb80ae3651f33d0f386cd9e97252 Mon Sep 17 00:00:00 2001 From: David Mosberger-Tang Date: Wed, 7 Feb 2024 05:07:42 +0000 Subject: [PATCH 319/378] wifi: wilc1000: correct CRC7 calculation Document ATWILC1000/ATWILC3000 Baremetal Wi-Fi/BLE Link Controller Software Design Guide https://tinyurl.com/yer2xhyc says that bit 0 of the CRC7 code must always be a 1. I confirmed that today with a logic analyzer: setting bit 0 causes wilc1000 to accept a command with CRC7 enabled, whereas clearing bit 0 causes wilc1000 to reject the command with a CRC error. Signed-off-by: David Mosberger-Tang Signed-off-by: Kalle Valo Link: https://msgid.link/20240207050736.2717641-1-davidm@egauge.net --- drivers/net/wireless/microchip/wilc1000/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/microchip/wilc1000/spi.c b/drivers/net/wireless/microchip/wilc1000/spi.c index eaf4dda9c540..c92ee4b73a74 100644 --- a/drivers/net/wireless/microchip/wilc1000/spi.c +++ b/drivers/net/wireless/microchip/wilc1000/spi.c @@ -473,7 +473,7 @@ static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz) ********************************************/ static u8 wilc_get_crc7(u8 *buffer, u32 len) { - return crc7_be(0xfe, buffer, len); + return crc7_be(0xfe, buffer, len) | 0x01; } static int wilc_spi_single_read(struct wilc *wilc, u8 cmd, u32 adr, void *b, From 14ddc470ba22057f7734245a6a521d764f8a7fbe Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 7 Feb 2024 02:30:33 -0800 Subject: [PATCH 320/378] wifi: mwifiex: Refactor 1-element array into flexible array in struct mwifiex_ie_types_chan_list_param_set struct mwifiex_ie_types_chan_list_param_set::chan_scan_param is treated as a flexible array, so convert it into one so that it doesn't trip the array bounds sanitizer[1]. Only a few places were using sizeof() on the whole struct, so adjust those to follow the calculation pattern to avoid including the trailing single element. Examining binary output differences doesn't appear to show any literal size values changing, though it is obfuscated a bit by the compiler adjusting register usage and stack spill slots, etc. Link: https://github.com/KSPP/linux/issues/51 [1] Cc: Brian Norris Cc: Kalle Valo Cc: Dmitry Antipov Cc: Johannes Berg Cc: zuoqilin Cc: Ruan Jinjie Cc: Thomas Gleixner Cc: Christophe JAILLET Cc: Gustavo A. R. Silva Cc: linux-wireless@vger.kernel.org Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://msgid.link/20240207103024.make.423-kees@kernel.org --- drivers/net/wireless/marvell/mwifiex/11n.c | 12 +++++------- drivers/net/wireless/marvell/mwifiex/fw.h | 2 +- drivers/net/wireless/marvell/mwifiex/scan.c | 14 ++++++-------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c index 90e401100898..c0c635e74bc5 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.c +++ b/drivers/net/wireless/marvell/mwifiex/11n.c @@ -392,12 +392,10 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, chan_list = (struct mwifiex_ie_types_chan_list_param_set *) *buffer; - memset(chan_list, 0, - sizeof(struct mwifiex_ie_types_chan_list_param_set)); + memset(chan_list, 0, struct_size(chan_list, chan_scan_param, 1)); chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - chan_list->header.len = cpu_to_le16( - sizeof(struct mwifiex_ie_types_chan_list_param_set) - - sizeof(struct mwifiex_ie_types_header)); + chan_list->header.len = + cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); chan_list->chan_scan_param[0].chan_number = bss_desc->bcn_ht_oper->primary_chan; chan_list->chan_scan_param[0].radio_type = @@ -411,8 +409,8 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, (bss_desc->bcn_ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); - *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set); - ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set); + *buffer += struct_size(chan_list, chan_scan_param, 1); + ret_len += struct_size(chan_list, chan_scan_param, 1); } if (bss_desc->bcn_bss_co_2040) { diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index 62f3c9a52a1d..3adc447b715f 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -770,7 +770,7 @@ struct mwifiex_chan_scan_param_set { struct mwifiex_ie_types_chan_list_param_set { struct mwifiex_ie_types_header header; - struct mwifiex_chan_scan_param_set chan_scan_param[1]; + struct mwifiex_chan_scan_param_set chan_scan_param[]; } __packed; struct mwifiex_ie_types_rxba_sync { diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index a2ddac363b10..0326b121747c 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -664,15 +664,14 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, /* Copy the current channel TLV to the command being prepared */ - memcpy(chan_tlv_out->chan_scan_param + tlv_idx, + memcpy(&chan_tlv_out->chan_scan_param[tlv_idx], tmp_chan_list, - sizeof(chan_tlv_out->chan_scan_param)); + sizeof(*chan_tlv_out->chan_scan_param)); /* Increment the TLV header length by the size appended */ le16_unaligned_add_cpu(&chan_tlv_out->header.len, - sizeof( - chan_tlv_out->chan_scan_param)); + sizeof(*chan_tlv_out->chan_scan_param)); /* * The tlv buffer length is set to the number of bytes @@ -2369,12 +2368,11 @@ int mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv, chan_idx < MWIFIEX_BG_SCAN_CHAN_MAX && bgscan_cfg_in->chan_list[chan_idx].chan_number; chan_idx++) { - temp_chan = chan_list_tlv->chan_scan_param + chan_idx; + temp_chan = &chan_list_tlv->chan_scan_param[chan_idx]; /* Increment the TLV header length by size appended */ le16_unaligned_add_cpu(&chan_list_tlv->header.len, - sizeof( - chan_list_tlv->chan_scan_param)); + sizeof(*chan_list_tlv->chan_scan_param)); temp_chan->chan_number = bgscan_cfg_in->chan_list[chan_idx].chan_number; @@ -2413,7 +2411,7 @@ int mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv, chan_scan_param); le16_unaligned_add_cpu(&chan_list_tlv->header.len, chan_num * - sizeof(chan_list_tlv->chan_scan_param[0])); + sizeof(*chan_list_tlv->chan_scan_param)); } tlv_pos += (sizeof(chan_list_tlv->header) From f20073f50dfd1e1232e44834c74db718ffd2149b Mon Sep 17 00:00:00 2001 From: Alexey Berezhok Date: Thu, 8 Feb 2024 11:51:21 +0300 Subject: [PATCH 321/378] wifi: brcmfmac: do not cast hidden SSID attribute value to boolean In 'brcmf_cfg80211_start_ap()', not assume that NL80211_HIDDEN_SSID_NOT_IN_USE is zero but prefer an explicit check instead. Compile tested only. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Alexey Berezhok Signed-off-by: Kalle Valo Link: https://msgid.link/20240208085121.2430-1-a@bayrepo.ru --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index d0cb39278cb9..adf8a14feb49 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5274,7 +5274,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, settings->hidden_ssid); if (err) { bphy_err(drvr, "%s closednet error (%d)\n", - settings->hidden_ssid ? + (settings->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE) ? "enabled" : "disabled", err); goto exit; From db84b758541f0925a5c8263ea0af1656fe38412f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 9 Feb 2024 14:52:19 +0800 Subject: [PATCH 322/378] wifi: rtw89: correct PHY register offset for PHY-1 PHY-1 can be seen as a copy of PHY-0, and the difference is their base register address, so add a function to get offset to access PHY-1. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 13 +++++++++---- drivers/net/wireless/realtek/rtw89/phy.h | 1 + drivers/net/wireless/realtek/rtw89/phy_be.c | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 9a8f5b764617..7c2f0ba996b1 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -13,6 +13,13 @@ #include "txrx.h" #include "util.h" +static u32 rtw89_phy0_phy1_offset(struct rtw89_dev *rtwdev, u32 addr) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + + return phy->phy0_phy1_offset(rtwdev, addr); +} + static u16 get_max_amsdu_len(struct rtw89_dev *rtwdev, const struct rtw89_ra_report *report) { @@ -1633,14 +1640,11 @@ static void rtw89_phy_init_rf_nctl(struct rtw89_dev *rtwdev) rtw89_rfk_parser(rtwdev, chip->nctl_post_table); } -static u32 rtw89_phy0_phy1_offset(struct rtw89_dev *rtwdev, u32 addr) +static u32 rtw89_phy0_phy1_offset_ax(struct rtw89_dev *rtwdev, u32 addr) { u32 phy_page = addr >> 8; u32 ofst = 0; - if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) - return addr < 0x10000 ? 0x20000 : 0; - switch (phy_page) { case 0x6: case 0x7: @@ -6392,6 +6396,7 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_ax = { .ccx = &rtw89_ccx_regs_ax, .physts = &rtw89_physts_regs_ax, .cfo = &rtw89_cfo_regs_ax, + .phy0_phy1_offset = rtw89_phy0_phy1_offset_ax, .config_bb_gain = rtw89_phy_config_bb_gain_ax, .preinit_rf_nctl = rtw89_phy_preinit_rf_nctl_ax, .bb_wrap_init = NULL, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index d80ddc723e86..76234daab896 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -510,6 +510,7 @@ struct rtw89_phy_gen_def { const struct rtw89_ccx_regs *ccx; const struct rtw89_physts_regs *physts; const struct rtw89_cfo_regs *cfo; + u32 (*phy0_phy1_offset)(struct rtw89_dev *rtwdev, u32 addr); void (*config_bb_gain)(struct rtw89_dev *rtwdev, const struct rtw89_reg2_def *reg, enum rtw89_rf_path rf_path, diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 6849438a5f3c..be0148f2b96f 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -78,6 +78,24 @@ static const struct rtw89_cfo_regs rtw89_cfo_regs_be = { .valid_0_mask = B_DCFO_OPT_EN_V1, }; +static u32 rtw89_phy0_phy1_offset_be(struct rtw89_dev *rtwdev, u32 addr) +{ + u32 phy_page = addr >> 8; + u32 ofst = 0; + + if ((phy_page >= 0x4 && phy_page <= 0xF) || + (phy_page >= 0x20 && phy_page <= 0x2B) || + (phy_page >= 0x40 && phy_page <= 0x4f) || + (phy_page >= 0x60 && phy_page <= 0x6f) || + (phy_page >= 0xE4 && phy_page <= 0xE5) || + (phy_page >= 0xE8 && phy_page <= 0xED)) + ofst = 0x1000; + else + ofst = 0x0; + + return ofst; +} + union rtw89_phy_bb_gain_arg_be { u32 addr; struct { @@ -952,6 +970,7 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be = { .ccx = &rtw89_ccx_regs_be, .physts = &rtw89_physts_regs_be, .cfo = &rtw89_cfo_regs_be, + .phy0_phy1_offset = rtw89_phy0_phy1_offset_be, .config_bb_gain = rtw89_phy_config_bb_gain_be, .preinit_rf_nctl = rtw89_phy_preinit_rf_nctl_be, .bb_wrap_init = rtw89_phy_bb_wrap_init_be, From e10cd2ddd89e8b3e61b49247067e79f7debec2f1 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 9 Feb 2024 14:52:20 +0800 Subject: [PATCH 323/378] wifi: rtw89: load BB parameters to PHY-1 We are going to support MLO/DBCC, so need to load parameter table to PHY-1 as well. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 29 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 7c2f0ba996b1..81f73821e3fc 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1025,22 +1025,30 @@ static void rtw89_phy_config_bb_reg(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, void *extra_data) { - if (reg->addr == 0xfe) + u32 addr; + + if (reg->addr == 0xfe) { mdelay(50); - else if (reg->addr == 0xfd) + } else if (reg->addr == 0xfd) { mdelay(5); - else if (reg->addr == 0xfc) + } else if (reg->addr == 0xfc) { mdelay(1); - else if (reg->addr == 0xfb) + } else if (reg->addr == 0xfb) { udelay(50); - else if (reg->addr == 0xfa) + } else if (reg->addr == 0xfa) { udelay(5); - else if (reg->addr == 0xf9) + } else if (reg->addr == 0xf9) { udelay(1); - else if (reg->data == BYPASS_CR_DATA) + } else if (reg->data == BYPASS_CR_DATA) { rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "Bypass CR 0x%x\n", reg->addr); - else - rtw89_phy_write32(rtwdev, reg->addr, reg->data); + } else { + addr = reg->addr; + + if ((uintptr_t)extra_data == RTW89_PHY_1) + addr += rtw89_phy0_phy1_offset(rtwdev, reg->addr); + + rtw89_phy_write32(rtwdev, addr, reg->data); + } } union rtw89_phy_bb_gain_arg { @@ -1554,6 +1562,9 @@ void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev) bb_table = elm_info->bb_tbl ? elm_info->bb_tbl : chip->bb_table; rtw89_phy_init_reg(rtwdev, bb_table, rtw89_phy_config_bb_reg, NULL); + if (rtwdev->dbcc_en) + rtw89_phy_init_reg(rtwdev, bb_table, rtw89_phy_config_bb_reg, + (void *)RTW89_PHY_1); rtw89_chip_init_txpwr_unit(rtwdev, RTW89_PHY_0); bb_gain_table = elm_info->bb_gain ? elm_info->bb_gain : chip->bb_gain_table; From b6e65d18bc2e124d8ac017ad26ace0f5852fe51d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 9 Feb 2024 14:52:21 +0800 Subject: [PATCH 324/378] wifi: rtw89: mac: return held quota of DLE when changing MAC-1 DLE (data link engine) could hold quota when we are going to enable/disable MAC-1 block, so trigger hardware to return all held quota. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 19 ++++-- drivers/net/wireless/realtek/rtw89/mac.h | 4 +- drivers/net/wireless/realtek/rtw89/mac_be.c | 70 ++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/reg.h | 10 +++ 4 files changed, 95 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index ef4c492003d8..716e2192b774 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -3192,13 +3192,11 @@ static int set_cpuio_ax(struct rtw89_dev *rtwdev, return 0; } -int rtw89_mac_dle_quota_change(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode) +int rtw89_mac_dle_quota_change(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode, + bool band1_en) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; const struct rtw89_dle_mem *cfg; - struct rtw89_cpuio_ctrl ctrl_para = {0}; - u16 pkt_id; - int ret; cfg = get_dle_mem_cfg(rtwdev, mode); if (!cfg) { @@ -3213,6 +3211,16 @@ int rtw89_mac_dle_quota_change(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mod dle_quota_cfg(rtwdev, cfg, INVALID_QT_WCPU); + return mac->dle_quota_change(rtwdev, band1_en); +} + +static int dle_quota_change_ax(struct rtw89_dev *rtwdev, bool band1_en) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + struct rtw89_cpuio_ctrl ctrl_para = {0}; + u16 pkt_id; + int ret; + ret = mac->dle_buf_req(rtwdev, 0x20, true, &pkt_id); if (ret) { rtw89_err(rtwdev, "[ERR]WDE DLE buf req\n"); @@ -3301,7 +3309,7 @@ static int band1_enable_ax(struct rtw89_dev *rtwdev) return ret; } - ret = rtw89_mac_dle_quota_change(rtwdev, rtwdev->mac.qta_mode); + ret = rtw89_mac_dle_quota_change(rtwdev, rtwdev->mac.qta_mode, true); if (ret) { rtw89_err(rtwdev, "[ERR]DLE quota change %d\n", ret); return ret; @@ -6218,6 +6226,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .wde_quota_cfg = wde_quota_cfg_ax, .ple_quota_cfg = ple_quota_cfg_ax, .set_cpuio = set_cpuio_ax, + .dle_quota_change = dle_quota_change_ax, .disable_cpu = rtw89_mac_disable_cpu_ax, .fwdl_enable_wcpu = rtw89_mac_enable_cpu_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 7aea57804e93..b0a3b2a9eb5b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -932,6 +932,7 @@ struct rtw89_mac_gen_def { const struct rtw89_ple_quota *max_cfg); int (*set_cpuio)(struct rtw89_dev *rtwdev, struct rtw89_cpuio_ctrl *ctrl_para, bool wd); + int (*dle_quota_change)(struct rtw89_dev *rtwdev, bool band1_en); void (*disable_cpu)(struct rtw89_dev *rtwdev); int (*fwdl_enable_wcpu)(struct rtw89_dev *rtwdev, u8 boot_reason, @@ -1388,7 +1389,8 @@ int rtw89_mac_resize_ple_rx_quota(struct rtw89_dev *rtwdev, bool wow); int rtw89_mac_ptk_drop_by_band_and_wait(struct rtw89_dev *rtwdev, enum rtw89_mac_idx band); void rtw89_mac_hw_mgnt_sec(struct rtw89_dev *rtwdev, bool wow); -int rtw89_mac_dle_quota_change(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode); +int rtw89_mac_dle_quota_change(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode, + bool band1_en); int rtw89_mac_get_dle_rsvd_qt_cfg(struct rtw89_dev *rtwdev, enum rtw89_mac_dle_rsvd_qt_type type, struct rtw89_mac_dle_rsvd_qt_cfg *cfg); diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 13e6fa7a3ae8..e2e0a7549b53 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1449,6 +1449,71 @@ static int set_cpuio_be(struct rtw89_dev *rtwdev, return 0; } +static int dle_upd_qta_aval_page_be(struct rtw89_dev *rtwdev, + enum rtw89_mac_dle_ctrl_type type, + enum rtw89_mac_dle_ple_quota_id quota_id) +{ + u32 val; + + if (type == DLE_CTRL_TYPE_WDE) { + rtw89_write32_mask(rtwdev, R_BE_WDE_BUFMGN_CTL, + B_BE_WDE_AVAL_UPD_QTAID_MASK, quota_id); + rtw89_write32_set(rtwdev, R_BE_WDE_BUFMGN_CTL, B_BE_WDE_AVAL_UPD_REQ); + + return read_poll_timeout(rtw89_read32, val, + !(val & B_BE_WDE_AVAL_UPD_REQ), + 1, 2000, false, rtwdev, R_BE_WDE_BUFMGN_CTL); + } else if (type == DLE_CTRL_TYPE_PLE) { + rtw89_write32_mask(rtwdev, R_BE_PLE_BUFMGN_CTL, + B_BE_PLE_AVAL_UPD_QTAID_MASK, quota_id); + rtw89_write32_set(rtwdev, R_BE_PLE_BUFMGN_CTL, B_BE_PLE_AVAL_UPD_REQ); + + return read_poll_timeout(rtw89_read32, val, + !(val & B_BE_PLE_AVAL_UPD_REQ), + 1, 2000, false, rtwdev, R_BE_PLE_BUFMGN_CTL); + } + + rtw89_warn(rtwdev, "%s wrong type %d\n", __func__, type); + return -EINVAL; +} + +static int dle_quota_change_be(struct rtw89_dev *rtwdev, bool band1_en) +{ + int ret; + + if (band1_en) { + ret = dle_upd_qta_aval_page_be(rtwdev, DLE_CTRL_TYPE_PLE, + PLE_QTAID_B0_TXPL); + if (ret) { + rtw89_err(rtwdev, "update PLE B0 TX avail page fail %d\n", ret); + return ret; + } + + ret = dle_upd_qta_aval_page_be(rtwdev, DLE_CTRL_TYPE_PLE, + PLE_QTAID_CMAC0_RX); + if (ret) { + rtw89_err(rtwdev, "update PLE CMAC0 RX avail page fail %d\n", ret); + return ret; + } + } else { + ret = dle_upd_qta_aval_page_be(rtwdev, DLE_CTRL_TYPE_PLE, + PLE_QTAID_B1_TXPL); + if (ret) { + rtw89_err(rtwdev, "update PLE B1 TX avail page fail %d\n", ret); + return ret; + } + + ret = dle_upd_qta_aval_page_be(rtwdev, DLE_CTRL_TYPE_PLE, + PLE_QTAID_CMAC1_RX); + if (ret) { + rtw89_err(rtwdev, "update PLE CMAC1 RX avail page fail %d\n", ret); + return ret; + } + } + + return 0; +} + static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx, enum rtw89_qta_mode mode) { @@ -1538,7 +1603,7 @@ static int band1_enable_be(struct rtw89_dev *rtwdev) return ret; } - ret = rtw89_mac_dle_quota_change(rtwdev, rtwdev->mac.qta_mode); + ret = rtw89_mac_dle_quota_change(rtwdev, rtwdev->mac.qta_mode, true); if (ret) { rtw89_err(rtwdev, "[ERR]DLE quota change %d\n", ret); return ret; @@ -1593,7 +1658,7 @@ static int band1_disable_be(struct rtw89_dev *rtwdev) return ret; } - ret = rtw89_mac_dle_quota_change(rtwdev, rtwdev->mac.qta_mode); + ret = rtw89_mac_dle_quota_change(rtwdev, rtwdev->mac.qta_mode, false); if (ret) { rtw89_err(rtwdev, "[ERR]DLE quota change %d\n", ret); return ret; @@ -2347,6 +2412,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .wde_quota_cfg = wde_quota_cfg_be, .ple_quota_cfg = ple_quota_cfg_be, .set_cpuio = set_cpuio_be, + .dle_quota_change = dle_quota_change_be, .disable_cpu = rtw89_mac_disable_cpu_be, .fwdl_enable_wcpu = rtw89_mac_fwdl_enable_wcpu_be, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 6368b2b32c0c..20b538526541 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -5020,6 +5020,11 @@ #define B_BE_WDE_START_BOUND_MASK GENMASK(14, 8) #define B_BE_WDE_PAGE_SEL_MASK GENMASK(1, 0) +#define R_BE_WDE_BUFMGN_CTL 0x8C10 +#define B_BE_WDE_AVAL_UPD_REQ BIT(29) +#define B_BE_WDE_AVAL_UPD_QTAID_MASK GENMASK(27, 24) +#define B_BE_WDE_BUFMGN_FRZTMR_MODE BIT(0) + #define R_BE_WDE_ERR_IMR 0x8C38 #define B_BE_WDE_DATCHN_CAMREQ_ERR_INT_EN BIT(29) #define B_BE_WDE_DATCHN_ADRERR_ERR_INT_EN BIT(28) @@ -5136,6 +5141,11 @@ #define B_BE_PLE_START_BOUND_MASK GENMASK(14, 8) #define B_BE_PLE_PAGE_SEL_MASK GENMASK(1, 0) +#define R_BE_PLE_BUFMGN_CTL 0x9010 +#define B_BE_PLE_AVAL_UPD_REQ BIT(29) +#define B_BE_PLE_AVAL_UPD_QTAID_MASK GENMASK(27, 24) +#define B_BE_PLE_BUFMGN_FRZTMR_MODE BIT(0) + #define R_BE_PLE_ERR_IMR 0x9038 #define B_BE_PLE_DATCHN_CAMREQ_ERR_INT_EN BIT(29) #define B_BE_PLE_DATCHN_ADRERR_ERR_INT_EN BIT(28) From b204d2475266ad4b917419bd8c3d5cc1f75111b8 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 9 Feb 2024 14:52:22 +0800 Subject: [PATCH 325/378] wifi: rtw89: mac: correct MUEDCA setting for MAC-1 Consider mac_idx as an argument to set this register to disable QoS NULL update MUEDCA timer. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index e2e0a7549b53..fdbfb76f97ee 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -995,7 +995,8 @@ static int tmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) { u32 reg; - rtw89_write32_clr(rtwdev, R_BE_TB_PPDU_CTRL, B_BE_QOSNULL_UPD_MUEDCA_EN); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_TB_PPDU_CTRL, mac_idx); + rtw89_write32_clr(rtwdev, reg, B_BE_QOSNULL_UPD_MUEDCA_EN); reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_WMTX_TCR_BE_4, mac_idx); rtw89_write32_mask(rtwdev, reg, B_BE_EHT_HE_PPDU_4XLTF_ZLD_USTIMER_MASK, 0x12); From fecf6b57fbc7560ee5f6e9771ac1f2653e4cc49d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 9 Feb 2024 14:52:23 +0800 Subject: [PATCH 326/378] wifi: rtw89: mac: reset PHY-1 hardware when going to enable/disable When going to use PHY-1, reset the hardware to make it work properly. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index fdbfb76f97ee..f3c82751993c 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1546,6 +1546,13 @@ static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx, static int dbcc_bb_ctrl_be(struct rtw89_dev *rtwdev, bool bb1_en) { + u32 set = B_BE_FEN_BB1PLAT_RSTB | B_BE_FEN_BB1_IP_RSTN; + + if (bb1_en) + rtw89_write32_set(rtwdev, R_BE_FEN_RST_ENABLE, set); + else + rtw89_write32_clr(rtwdev, R_BE_FEN_RST_ENABLE, set); + return 0; } From 505b57d08f72dc67c97bf743bb33ca5c95755def Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 9 Feb 2024 14:52:24 +0800 Subject: [PATCH 327/378] wifi: rtw89: use PLCP information to match BSS_COLOR and AID Hardware can use spatial reuse to reduce interference in OBSS environment, and originally use MAC header to match BSS color and AID. Change to use PLCP to match them earlier to prevent margin timing. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 3 +++ drivers/net/wireless/realtek/rtw89/mac_be.c | 3 +++ drivers/net/wireless/realtek/rtw89/reg.h | 14 ++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 716e2192b774..71e9b9d7b430 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -2537,6 +2537,9 @@ static int spatial_reuse_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_RX_SR_CTRL, mac_idx); rtw89_write8_clr(rtwdev, reg, B_AX_SR_EN); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_BSSID_SRC_CTRL, mac_idx); + rtw89_write8_set(rtwdev, reg, B_AX_PLCP_SRC_EN); + return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index f3c82751993c..f4b51183e3f4 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -988,6 +988,9 @@ static int spatial_reuse_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RX_SR_CTRL, mac_idx); rtw89_write8_clr(rtwdev, reg, B_BE_SR_EN | B_BE_SR_CTRL_PLCP_EN); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_BSSID_SRC_CTRL, mac_idx); + rtw89_write8_set(rtwdev, reg, B_BE_PLCP_SRC_EN); + return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 20b538526541..31bdc7616749 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3246,6 +3246,13 @@ #define R_AX_RX_SR_CTRL_C1 0xEE4A #define B_AX_SR_EN BIT(0) +#define R_AX_BSSID_SRC_CTRL 0xCE4B +#define R_AX_BSSID_SRC_CTRL_C1 0xEE4B +#define B_AX_BSSID_MATCH BIT(3) +#define B_AX_PARTIAL_AID_MATCH BIT(2) +#define B_AX_BSSCOLOR_MATCH BIT(1) +#define B_AX_PLCP_SRC_EN BIT(0) + #define R_AX_CSIRPT_OPTION 0xCE64 #define R_AX_CSIRPT_OPTION_C1 0xEE64 #define B_AX_CSIPRT_HESU_AID_EN BIT(25) @@ -7208,6 +7215,13 @@ #define B_BE_SR_CTRL_PLCP_EN BIT(1) #define B_BE_SR_EN BIT(0) +#define R_BE_BSSID_SRC_CTRL 0x1144B +#define R_BE_BSSID_SRC_CTRL_C1 0x1544B +#define B_BE_BSSID_MATCH BIT(3) +#define B_BE_PARTIAL_AID_MATCH BIT(2) +#define B_BE_BSSCOLOR_MATCH BIT(1) +#define B_BE_PLCP_SRC_EN BIT(0) + #define R_BE_CSIRPT_OPTION 0x11464 #define R_BE_CSIRPT_OPTION_C1 0x15464 #define B_BE_CSIPRT_EHTSU_AID_EN BIT(26) From 49ea98235ada68ee1e2cad660bbb2e8e2cd87670 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 9 Feb 2024 14:52:25 +0800 Subject: [PATCH 328/378] wifi: rtw89: differentiate narrow_bw_ru_dis setting according to chip gen When there are OBSS that cannot interpret 26-tone RU transmissions, we should disable 26-tone RU HE TB PPDU transmissions. So, add registers accordingly. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 12 +++++++++--- drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/mac_be.c | 4 ++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 71e9b9d7b430..8a1d3e6e81a9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4619,6 +4619,7 @@ void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif) { struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; struct ieee80211_hw *hw = rtwdev->hw; bool tolerated = true; u32 reg; @@ -4633,11 +4634,12 @@ void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, rtw89_mac_check_he_obss_narrow_bw_ru_iter, &tolerated); - reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_RXTRIG_TEST_USER_2, rtwvif->mac_idx); + reg = rtw89_mac_reg_by_idx(rtwdev, mac->narrow_bw_ru_dis.addr, + rtwvif->mac_idx); if (tolerated) - rtw89_write32_clr(rtwdev, reg, B_AX_RXTRIG_RU26_DIS); + rtw89_write32_clr(rtwdev, reg, mac->narrow_bw_ru_dis.mask); else - rtw89_write32_set(rtwdev, reg, B_AX_RXTRIG_RU26_DIS); + rtw89_write32_set(rtwdev, reg, mac->narrow_bw_ru_dis.mask); } void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) @@ -6206,6 +6208,10 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .mask = B_AX_BFMEE_HT_NDPA_EN | B_AX_BFMEE_VHT_NDPA_EN | B_AX_BFMEE_HE_NDPA_EN, }, + .narrow_bw_ru_dis = { + .addr = R_AX_RXTRIG_TEST_USER_2, + .mask = B_AX_RXTRIG_RU26_DIS, + }, .check_mac_en = rtw89_mac_check_mac_en_ax, .sys_init = sys_init_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index b0a3b2a9eb5b..c5ebac1d5990 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -898,6 +898,7 @@ struct rtw89_mac_gen_def { struct rtw89_reg_def muedca_ctrl; struct rtw89_reg_def bfee_ctrl; + struct rtw89_reg_def narrow_bw_ru_dis; int (*check_mac_en)(struct rtw89_dev *rtwdev, u8 band, enum rtw89_mac_hwmod_sel sel); diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index f4b51183e3f4..6388c56a3c90 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2400,6 +2400,10 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .mask = B_BE_BFMEE_HT_NDPA_EN | B_BE_BFMEE_VHT_NDPA_EN | B_BE_BFMEE_HE_NDPA_EN | B_BE_BFMEE_EHT_NDPA_EN, }, + .narrow_bw_ru_dis = { + .addr = R_BE_RXTRIG_TEST_USER_2, + .mask = B_BE_RXTRIG_RU26_DIS, + }, .check_mac_en = rtw89_mac_check_mac_en_be, .sys_init = sys_init_be, From ef95df598622a399b62b69b764682c21543b1d63 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 9 Feb 2024 14:52:26 +0800 Subject: [PATCH 329/378] wifi: rtw89: 8922a: correct register definition and merge IO for ctrl_nbtg_bt_tx() ctrl_nbtg_bt_tx is used to control AGC settings under non-shared path condition, which is affected by BT TX. To speed up IO, merge continual bit mask into one IO. Also, correct a register definition. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 6 +++- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 30 +++++-------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 31bdc7616749..26aa2d9bd526 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8077,14 +8077,16 @@ #define R_S1_ADDCK 0x3E00 #define B_S1_ADDCK_I GENMASK(9, 0) #define B_S1_ADDCK_Q GENMASK(19, 10) -#define R_OP1DB_A 0x406B +#define R_OP1DB_A 0x40B0 #define B_OP1DB_A GENMASK(31, 24) #define R_OP1DB1_A 0x40BC +#define B_TIA10_A GENMASK(15, 0) #define B_TIA1_A GENMASK(15, 8) #define B_TIA0_A GENMASK(7, 0) #define R_BKOFF_A 0x40E0 #define B_BKOFF_IBADC_A GENMASK(23, 18) #define R_BACKOFF_A 0x40E4 +#define B_LNA_IBADC_A GENMASK(29, 18) #define B_BACKOFF_LNA_A GENMASK(29, 24) #define B_BACKOFF_IBADC_A GENMASK(23, 18) #define R_RXBY_WBADC_A 0x40F4 @@ -8140,11 +8142,13 @@ #define R_LNA_OP 0x44B0 #define B_LNA6 GENMASK(31, 24) #define R_LNA_TIA 0x44BC +#define B_TIA10_B GENMASK(15, 0) #define B_TIA1_B GENMASK(15, 8) #define B_TIA0_B GENMASK(7, 0) #define R_BKOFF_B 0x44E0 #define B_BKOFF_IBADC_B GENMASK(23, 18) #define R_BACKOFF_B 0x44E4 +#define B_LNA_IBADC_B GENMASK(29, 18) #define B_BACKOFF_LNA_B GENMASK(29, 24) #define B_BACKOFF_IBADC_B GENMASK(23, 18) #define R_RXBY_WBADC_B 0x44F4 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 02ec4d27011f..f7b81daa0b03 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1526,11 +1526,8 @@ static void rtw8922a_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en, 0x0, phy_idx); rtw89_phy_write32_idx(rtwdev, R_BT_SHARE_A, B_BT_TRK_OFF_A, 0x0, phy_idx); rtw89_phy_write32_idx(rtwdev, R_OP1DB_A, B_OP1DB_A, 0x80, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_OP1DB1_A, B_TIA0_A, 0x80, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_OP1DB1_A, B_TIA1_A, 0x80, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_BACKOFF_A, B_BACKOFF_IBADC_A, - 0x34, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_BACKOFF_A, B_BACKOFF_LNA_A, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_OP1DB1_A, B_TIA10_A, 0x8080, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BACKOFF_A, B_LNA_IBADC_A, 0x34, phy_idx); rtw89_phy_write32_idx(rtwdev, R_BKOFF_A, B_BKOFF_IBADC_A, 0x34, phy_idx); rtw89_phy_write32_idx(rtwdev, R_FORCE_FIR_B, B_FORCE_FIR_B, 0x3, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RXBY_WBADC_B, B_RXBY_WBADC_B, @@ -1539,11 +1536,8 @@ static void rtw8922a_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en, 0x0, phy_idx); rtw89_phy_write32_idx(rtwdev, R_BT_SHARE_B, B_BT_TRK_OFF_B, 0x0, phy_idx); rtw89_phy_write32_idx(rtwdev, R_LNA_OP, B_LNA6, 0x80, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_LNA_TIA, B_TIA0_B, 0x80, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_LNA_TIA, B_TIA1_B, 0x80, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_BACKOFF_B, B_BACKOFF_IBADC_B, - 0x34, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_BACKOFF_B, B_BACKOFF_LNA_B, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_LNA_TIA, B_TIA10_B, 0x8080, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BACKOFF_B, B_LNA_IBADC_B, 0x34, phy_idx); rtw89_phy_write32_idx(rtwdev, R_BKOFF_B, B_BKOFF_IBADC_B, 0x34, phy_idx); } else { rtw89_phy_write32_idx(rtwdev, R_FORCE_FIR_A, B_FORCE_FIR_A, 0x0, phy_idx); @@ -1553,12 +1547,8 @@ static void rtw8922a_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en, 0x1, phy_idx); rtw89_phy_write32_idx(rtwdev, R_BT_SHARE_A, B_BT_TRK_OFF_A, 0x1, phy_idx); rtw89_phy_write32_idx(rtwdev, R_OP1DB_A, B_OP1DB_A, 0x1a, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_OP1DB1_A, B_TIA0_A, 0x2a, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_OP1DB1_A, B_TIA1_A, 0x2a, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_BACKOFF_A, B_BACKOFF_IBADC_A, - 0x26, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_BACKOFF_A, B_BACKOFF_LNA_A, - 0x1e, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_OP1DB1_A, B_TIA10_A, 0x2a2a, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BACKOFF_A, B_LNA_IBADC_A, 0x7a6, phy_idx); rtw89_phy_write32_idx(rtwdev, R_BKOFF_A, B_BKOFF_IBADC_A, 0x26, phy_idx); rtw89_phy_write32_idx(rtwdev, R_FORCE_FIR_B, B_FORCE_FIR_B, 0x0, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RXBY_WBADC_B, B_RXBY_WBADC_B, @@ -1567,12 +1557,8 @@ static void rtw8922a_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en, 0x1, phy_idx); rtw89_phy_write32_idx(rtwdev, R_BT_SHARE_B, B_BT_TRK_OFF_B, 0x1, phy_idx); rtw89_phy_write32_idx(rtwdev, R_LNA_OP, B_LNA6, 0x20, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_LNA_TIA, B_TIA0_B, 0x30, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_LNA_TIA, B_TIA1_B, 0x2a, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_BACKOFF_B, B_BACKOFF_IBADC_B, - 0x26, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_BACKOFF_B, B_BACKOFF_LNA_B, - 0x1e, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_LNA_TIA, B_TIA10_B, 0x2a30, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BACKOFF_B, B_LNA_IBADC_B, 0x7a6, phy_idx); rtw89_phy_write32_idx(rtwdev, R_BKOFF_B, B_BKOFF_IBADC_B, 0x26, phy_idx); } } From 598481c6eb20b29088caa0c69085904b2ca08674 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Fri, 9 Feb 2024 14:52:27 +0800 Subject: [PATCH 330/378] wifi: rtw89: 8922a: implement AP mode related reg for BE generation Modify reg for BE generation when AP stop, otherwise have warning messages "Polling beacon packet empty fail". Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 3 +++ drivers/net/wireless/realtek/rtw89/mac.c | 13 +++++++++---- drivers/net/wireless/realtek/rtw89/mac_be.c | 3 +++ drivers/net/wireless/realtek/rtw89/reg.h | 6 ++++++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 462c4b03c76b..0e451245a65a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -958,6 +958,9 @@ struct rtw89_port_reg { u32 mbssid; u32 mbssid_drop; u32 tsf_sync; + u32 ptcl_dbg; + u32 ptcl_dbg_info; + u32 bcn_drop_all; u32 hiq_win[RTW89_PORT_NUM]; }; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 8a1d3e6e81a9..9e9a05cc49de 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4037,6 +4037,9 @@ static const struct rtw89_port_reg rtw89_port_base_ax = { .mbssid = R_AX_MBSSID_CTRL, .mbssid_drop = R_AX_MBSSID_DROP_0, .tsf_sync = R_AX_PORT0_TSF_SYNC, + .ptcl_dbg = R_AX_PTCL_DBG, + .ptcl_dbg_info = R_AX_PTCL_DBG_INFO, + .bcn_drop_all = R_AX_BCN_DROP_ALL0, .hiq_win = {R_AX_P0MB_HGQ_WINDOW_CFG_0, R_AX_PORT_HGQ_WINDOW_CFG, R_AX_PORT_HGQ_WINDOW_CFG + 1, R_AX_PORT_HGQ_WINDOW_CFG + 2, R_AX_PORT_HGQ_WINDOW_CFG + 3}, @@ -4045,13 +4048,15 @@ static const struct rtw89_port_reg rtw89_port_base_ax = { static void rtw89_mac_check_packet_ctrl(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, u8 type) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + const struct rtw89_port_reg *p = mac->port_base; u8 mask = B_AX_PTCL_DBG_INFO_MASK_BY_PORT(rtwvif->port); u32 reg_info, reg_ctrl; u32 val; int ret; - reg_info = rtw89_mac_reg_by_idx(rtwdev, R_AX_PTCL_DBG_INFO, rtwvif->mac_idx); - reg_ctrl = rtw89_mac_reg_by_idx(rtwdev, R_AX_PTCL_DBG, rtwvif->mac_idx); + reg_info = rtw89_mac_reg_by_idx(rtwdev, p->ptcl_dbg_info, rtwvif->mac_idx); + reg_ctrl = rtw89_mac_reg_by_idx(rtwdev, p->ptcl_dbg, rtwvif->mac_idx); rtw89_write32_mask(rtwdev, reg_ctrl, B_AX_PTCL_DBG_SEL_MASK, type); rtw89_write32_set(rtwdev, reg_ctrl, B_AX_PTCL_DBG_EN); @@ -4068,7 +4073,7 @@ static void rtw89_mac_bcn_drop(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvi const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; const struct rtw89_port_reg *p = mac->port_base; - rtw89_write32_set(rtwdev, R_AX_BCN_DROP_ALL0, BIT(rtwvif->port)); + rtw89_write32_set(rtwdev, p->bcn_drop_all, BIT(rtwvif->port)); rtw89_write32_port_mask(rtwdev, rtwvif, p->tbtt_prohib, B_AX_TBTT_SETUP_MASK, 1); rtw89_write32_port_mask(rtwdev, rtwvif, p->bcn_area, B_AX_BCN_MSK_AREA_MASK, 0); rtw89_write32_port_mask(rtwdev, rtwvif, p->tbtt_prohib, B_AX_TBTT_HOLD_MASK, 0); @@ -4081,7 +4086,7 @@ static void rtw89_mac_bcn_drop(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvi if (rtwvif->port == RTW89_PORT_0) rtw89_mac_check_packet_ctrl(rtwdev, rtwvif, AX_PTCL_DBG_BCNQ_NUM1); - rtw89_write32_clr(rtwdev, R_AX_BCN_DROP_ALL0, BIT(rtwvif->port)); + rtw89_write32_clr(rtwdev, p->bcn_drop_all, BIT(rtwvif->port)); rtw89_write32_port_clr(rtwdev, rtwvif, p->port_cfg, B_AX_TBTT_PROHIB_EN); fsleep(2000); } diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 6388c56a3c90..6447353d35d3 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -52,6 +52,9 @@ static const struct rtw89_port_reg rtw89_port_base_be = { .mbssid = R_BE_MBSSID_CTRL, .mbssid_drop = R_BE_MBSSID_DROP_0, .tsf_sync = R_BE_PORT_0_TSF_SYNC, + .ptcl_dbg = R_BE_PTCL_DBG, + .ptcl_dbg_info = R_BE_PTCL_DBG_INFO, + .bcn_drop_all = R_BE_BCN_DROP_ALL0, .hiq_win = {R_BE_P0MB_HGQ_WINDOW_CFG_0, R_BE_PORT_HGQ_WINDOW_CFG, R_BE_PORT_HGQ_WINDOW_CFG + 1, R_BE_PORT_HGQ_WINDOW_CFG + 2, R_BE_PORT_HGQ_WINDOW_CFG + 3}, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 26aa2d9bd526..23a09efabab7 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -6347,6 +6347,8 @@ #define R_BE_TSFTR_HIGH_P0_C1 0x1443C #define B_BE_TSFTR_HIGH_P0_MASK GENMASK(31, 0) +#define R_BE_BCN_DROP_ALL0 0x10560 + #define R_BE_MBSSID_CTRL 0x10568 #define R_BE_MBSSID_CTRL_C1 0x14568 #define B_BE_MBSSID_MODE_SEL BIT(20) @@ -6533,6 +6535,10 @@ #define B_BE_PTCL_DROP BIT(5) #define B_BE_PTCL_TX_QUEUE_IDX_MASK GENMASK(4, 0) +#define R_BE_PTCL_DBG_INFO 0x108F0 + +#define R_BE_PTCL_DBG 0x108F4 + #define R_BE_RX_ERROR_FLAG 0x10C00 #define R_BE_RX_ERROR_FLAG_C1 0x14C00 #define B_BE_RX_CSI_NOT_RELEASE_ERROR BIT(31) From 5f9c264f8e0904729edb861eafbec081299237eb Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 9 Feb 2024 14:52:28 +0800 Subject: [PATCH 331/378] wifi: rtw89: reference quota mode when setting Tx power Reference the current quota mode to avoid misleading warnings. This patch is required after supporting DBCC quota mode. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 3 +-- drivers/net/wireless/realtek/rtw89/mac_be.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 9e9a05cc49de..296576a634e7 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5221,8 +5221,7 @@ bool rtw89_mac_get_txpwr_cr_ax(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u32 reg_base, u32 *cr) { - const struct rtw89_dle_mem *dle_mem = rtwdev->chip->dle_mem; - enum rtw89_qta_mode mode = dle_mem->mode; + enum rtw89_qta_mode mode = rtwdev->mac.qta_mode; u32 addr = rtw89_mac_reg_by_idx(rtwdev, reg_base, phy_idx); if (addr < R_AX_PWR_RATE_CTRL || addr > CMAC1_END_ADDR_AX) { diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 6447353d35d3..320e88229971 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1896,8 +1896,7 @@ static bool rtw89_mac_get_txpwr_cr_be(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u32 reg_base, u32 *cr) { - const struct rtw89_dle_mem *dle_mem = rtwdev->chip->dle_mem; - enum rtw89_qta_mode mode = dle_mem->mode; + enum rtw89_qta_mode mode = rtwdev->mac.qta_mode; int ret; ret = rtw89_mac_check_mac_en(rtwdev, (enum rtw89_mac_idx)phy_idx, From 4ae8ac201ddb1665ad3ef96d583f73ad51415ab7 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 9 Feb 2024 14:52:29 +0800 Subject: [PATCH 332/378] wifi: rtw89: change qutoa to DBCC by default for WiFi 7 chips Since WiFi 7 is expected to support MLO, so we should enable MAC-0/1 and PHY-0/1. By default, set dbcc_en=true, change quota to DBCC mode, and set MLO mode to 2 + 0 that means we only use 2x2 connection on MAC/PHY-0 for now. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240209065229.34515-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 8 +++++++- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 8 ++++---- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 7 +++++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 6441d99cd6c0..f697e3d898e6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4075,7 +4075,6 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) { int ret; - rtwdev->mac.qta_mode = RTW89_QTA_SCC; ret = rtw89_mac_init(rtwdev); if (ret) { rtw89_err(rtwdev, "mac init fail, ret:%d\n", ret); @@ -4213,6 +4212,13 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) rtwdev->hal.rx_fltr = DEFAULT_AX_RX_FLTR; rtwdev->dbcc_en = false; rtwdev->mlo_dbcc_mode = MLO_DBCC_NOT_SUPPORT; + rtwdev->mac.qta_mode = RTW89_QTA_SCC; + + if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) { + rtwdev->dbcc_en = true; + rtwdev->mac.qta_mode = RTW89_QTA_DBCC; + rtwdev->mlo_dbcc_mode = MLO_2_PLUS_0_1RF; + } INIT_WORK(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work); INIT_WORK(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0e451245a65a..e3913efedc28 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3306,6 +3306,7 @@ struct rtw89_scan_option { enum rtw89_qta_mode { RTW89_QTA_SCC, + RTW89_QTA_DBCC, RTW89_QTA_DLFW, RTW89_QTA_WOW, diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 296576a634e7..3ea50d49e12f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1625,7 +1625,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_size19 = {RTW89_WDE_PG_64, 3328, 0,}, /* PCIE */ .ple_size0 = {RTW89_PLE_PG_128, 1520, 16,}, - .ple_size0_v1 = {RTW89_PLE_PG_128, 2672, 256, 212992,}, + .ple_size0_v1 = {RTW89_PLE_PG_128, 2688, 240, 212992,}, .ple_size3_v1 = {RTW89_PLE_PG_128, 2928, 0, 212992,}, /* DLFW */ .ple_size4 = {RTW89_PLE_PG_128, 64, 1472,}, @@ -1650,8 +1650,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt17 = {0, 0, 0, 0,}, /* 8852C PCIE SCC */ .wde_qt18 = {3228, 60, 0, 40,}, - .ple_qt0 = {320, 0, 32, 16, 13, 13, 292, 0, 32, 18, 1, 4, 0,}, - .ple_qt1 = {320, 0, 32, 16, 1944, 1944, 2223, 0, 1963, 1949, 1, 1935, 0,}, + .ple_qt0 = {320, 320, 32, 16, 13, 13, 292, 292, 64, 18, 1, 4, 0,}, + .ple_qt1 = {320, 320, 32, 16, 1316, 1316, 1595, 1595, 1367, 1321, 1, 1307, 0,}, /* PCIE SCC */ .ple_qt4 = {264, 0, 16, 20, 26, 13, 356, 0, 32, 40, 8,}, /* PCIE SCC */ @@ -1677,7 +1677,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt_52b_wow = {147, 0, 16, 20, 157, 13, 133, 0, 172, 14, 24, 0,}, /* 8851B PCIE WOW */ .ple_qt_51b_wow = {147, 0, 16, 20, 157, 13, 133, 0, 172, 14, 24, 0,}, - .ple_rsvd_qt0 = {2, 112, 56, 6, 6, 6, 6, 0, 0, 62,}, + .ple_rsvd_qt0 = {2, 107, 107, 6, 6, 6, 6, 0, 0, 0,}, .ple_rsvd_qt1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, .rsvd0_size0 = {212992, 0,}, .rsvd1_size0 = {587776, 2048,}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index f7b81daa0b03..a4b7d2e79638 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -47,6 +47,8 @@ static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_pcie = { static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_pcie[] = { [RTW89_QTA_SCC] = {rtw8922a_hfc_chcfg_pcie, &rtw8922a_hfc_pubcfg_pcie, &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, + [RTW89_QTA_DBCC] = {rtw8922a_hfc_chcfg_pcie, &rtw8922a_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, RTW89_HCIFC_POH}, [RTW89_QTA_INVALID] = {NULL}, @@ -58,6 +60,11 @@ static const struct rtw89_dle_mem rtw8922a_dle_mem_pcie[] = { &rtw89_mac_size.wde_qt0_v1, &rtw89_mac_size.ple_qt0, &rtw89_mac_size.ple_qt1, &rtw89_mac_size.ple_rsvd_qt0, &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size0_v1, + &rtw89_mac_size.ple_size0_v1, &rtw89_mac_size.wde_qt0_v1, + &rtw89_mac_size.wde_qt0_v1, &rtw89_mac_size.ple_qt0, + &rtw89_mac_size.ple_qt1, &rtw89_mac_size.ple_rsvd_qt0, + &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size4_v1, &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, From 6f656131f6988709e3bf828c3ad992032b717c2e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 8 Feb 2024 12:01:43 +0100 Subject: [PATCH 333/378] wifi: mac80211: remove gfp parameter from ieee80211_obss_color_collision_notify Get rid of gfp parameter from ieee80211_obss_color_collision_notify since it is no longer used. Signed-off-by: Lorenzo Bianconi Reviewed-by: Jeff Johnson Acked-by: Jeff Johnson Link: https://msgid.link/f91e1c78896408ac556586ba8c99e4e389aeba02.1707389901.git.lorenzo@kernel.org Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath11k/wmi.c | 3 +-- include/net/mac80211.h | 3 +-- net/mac80211/cfg.c | 2 +- net/mac80211/rx.c | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 3c9f3b0bcfaa..1943e636faef 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -4020,8 +4020,7 @@ ath11k_wmi_obss_color_collision_event(struct ath11k_base *ab, struct sk_buff *sk switch (ev->evt_type) { case WMI_BSS_COLOR_COLLISION_DETECTION: - ieee80211_obss_color_collision_notify(arvif->vif, ev->obss_color_bitmap, - GFP_KERNEL); + ieee80211_obss_color_collision_notify(arvif->vif, ev->obss_color_bitmap); ath11k_dbg(ab, ATH11K_DBG_WMI, "OBSS color collision detected vdev:%d, event:%d, bitmap:%08llx\n", ev->vdev_id, ev->evt_type, ev->obss_color_bitmap); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 45d905b17a65..fc223761e3af 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -7490,11 +7490,10 @@ ieee80211_get_unsol_bcast_probe_resp_tmpl(struct ieee80211_hw *hw, * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @color_bitmap: a 64 bit bitmap representing the colors that the local BSS is * aware of. - * @gfp: allocation flags */ void ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif, - u64 color_bitmap, gfp_t gfp); + u64 color_bitmap); /** * ieee80211_is_tx_data - check if frame is a data frame diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 156a4215dcda..6f7c96c358f4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4765,7 +4765,7 @@ EXPORT_SYMBOL_GPL(ieee80211_color_change_finish); void ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif, - u64 color_bitmap, gfp_t gfp) + u64 color_bitmap) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_link_data *link = &sdata->deflink; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9902ea69af0a..c1f850138405 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3386,8 +3386,7 @@ ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx) IEEE80211_HE_OPERATION_BSS_COLOR_MASK); if (color == bss_conf->he_bss_color.color) ieee80211_obss_color_collision_notify(&rx->sdata->vif, - BIT_ULL(color), - GFP_ATOMIC); + BIT_ULL(color)); } } From f6ca96aa51a4ae1b3a416fbe85acdf1197c405a6 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Mon, 5 Feb 2024 21:59:50 +0530 Subject: [PATCH 334/378] wifi: cfg80211: add support for link id attribute in NL80211_CMD_DEL_STATION Currently whenever NL80211_CMD_DEL_STATION command is called without any MAC address, all stations present on that interface are flushed. However with MLO there is a need to flush such stations only which are using at least a particular link from the AP MLD interface. For example - 2 GHz and 5 GHz are part of an AP MLD. To this interface, following stations are connected - 1. One non-EHT STA on 2 GHz link. 2. One non-EHT STA on 5 GHz link. 3. One Multi-Link STA having 2 GHz and 5 GHz as active links. Now if currently, NL80211_CMD_DEL_STATION is issued by the 2 GHz link without any MAC address, it would flush all station entries. However, flushing of station entry #2 at least is not desireable since it is connected to 5 GHz link alone. Hence, add an option to pass link ID as well in the command so that if link ID is passed, stations using that passed link ID alone would be flushed and others will not. So after this, station entries #1 and #3 alone would be flushed and #2 will remain as it is. Signed-off-by: Aditya Kumar Singh Link: https://msgid.link/20240205162952.1697646-2-quic_adisi@quicinc.com [clarify documentation] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ include/uapi/linux/nl80211.h | 3 ++- net/wireless/nl80211.c | 19 ++++++++++++++++++- net/wireless/trace.h | 7 +++++-- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f52f989a54ad..62894b024e88 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1780,11 +1780,15 @@ struct station_parameters { * @subtype: Management frame subtype to use for indicating removal * (10 = Disassociation, 12 = Deauthentication) * @reason_code: Reason code for the Disassociation/Deauthentication frame + * @link_id: Link ID indicating a link that stations to be flushed must be + * using; valid only for MLO, but can also be -1 for MLO to really + * remove all stations. */ struct station_del_parameters { const u8 *mac; u8 subtype; u16 reason_code; + int link_id; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 853ac538a686..805bfe712971 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -438,7 +438,8 @@ * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type * of disconnection indication should be sent to the station * (Deauthentication or Disassociation frame and reason code for that - * frame). + * frame). %NL80211_ATTR_MLO_LINK_ID can be used optionally to remove + * stations connected and using at least that link as one of its links. * * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e1106ae35e21..5ca545753810 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7624,14 +7624,16 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; struct station_del_parameters params; + int link_id = nl80211_link_id_or_invalid(info->attrs); memset(¶ms, 0, sizeof(params)); if (info->attrs[NL80211_ATTR_MAC]) params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]); - switch (dev->ieee80211_ptr->iftype) { + switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: @@ -7672,6 +7674,17 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID; } + /* Link ID not expected in case of non-ML operation */ + if (!wdev->valid_links && link_id != -1) + return -EINVAL; + + /* If given, a valid link ID should be passed during MLO */ + if (wdev->valid_links && link_id >= 0 && + !(wdev->valid_links & BIT(link_id))) + return -EINVAL; + + params.link_id = link_id; + return rdev_del_station(rdev, dev, ¶ms); } @@ -16773,6 +16786,10 @@ static const struct genl_small_ops nl80211_small_ops[] = { .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .doit = nl80211_del_station, .flags = GENL_UNS_ADMIN_PERM, + /* cannot use NL80211_FLAG_MLO_VALID_LINK_ID, depends on + * whether MAC address is passed or not. If MAC address is + * passed, then even during MLO, link ID is not required. + */ .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), }, { diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 194ea2471717..361331c29116 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -867,6 +867,7 @@ DECLARE_EVENT_CLASS(station_del, MAC_ENTRY(sta_mac) __field(u8, subtype) __field(u16, reason_code) + __field(int, link_id) ), TP_fast_assign( WIPHY_ASSIGN; @@ -874,11 +875,13 @@ DECLARE_EVENT_CLASS(station_del, MAC_ASSIGN(sta_mac, params->mac); __entry->subtype = params->subtype; __entry->reason_code = params->reason_code; + __entry->link_id = params->link_id; ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: %pM" - ", subtype: %u, reason_code: %u", + ", subtype: %u, reason_code: %u, link_id: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sta_mac, - __entry->subtype, __entry->reason_code) + __entry->subtype, __entry->reason_code, + __entry->link_id) ); DEFINE_EVENT(station_del, rdev_del_station, From ec67d6e0d491d2a2df270ddcb7aa44db0984e11c Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Mon, 5 Feb 2024 21:59:51 +0530 Subject: [PATCH 335/378] wifi: mac80211: flush only stations using requests links Whenever sta_flush() function is invoked, all STAs present in that interface are flushed. In case of MLO, it is desirable to only flush such STAs that are at least using a given link id as one of their links. Add support for this by making change in the __sta_info_flush API argument to accept a link ID. And then, only if the STA is using the given link as one of its links, it would be flushed. Signed-off-by: Aditya Kumar Singh Link: https://msgid.link/20240205162952.1697646-3-quic_adisi@quicinc.com [reword commit message, in particular this isn't about "active" links] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 4 ++-- net/mac80211/ibss.c | 4 ++-- net/mac80211/iface.c | 2 +- net/mac80211/mesh.c | 2 +- net/mac80211/mlme.c | 2 +- net/mac80211/ocb.c | 2 +- net/mac80211/sta_info.c | 21 ++++++++++++++------- net/mac80211/sta_info.h | 14 +++++++++++--- 8 files changed, 33 insertions(+), 18 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 6f7c96c358f4..41fca30c896e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1616,7 +1616,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, link_conf->ema_ap = false; link_conf->bssid_indicator = 0; - __sta_info_flush(sdata, true); + __sta_info_flush(sdata, true, -1); ieee80211_free_keys(sdata, true); link_conf->enable_beacon = false; @@ -2096,7 +2096,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, if (params->mac) return sta_info_destroy_addr_bss(sdata, params->mac); - sta_info_flush(sdata); + sta_info_flush(sdata, params->link_id); return 0; } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 27cc9ddd0432..7ace5cdc6c26 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -237,7 +237,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, drv_reset_tsf(local, sdata); if (!ether_addr_equal(ifibss->bssid, bssid)) - sta_info_flush(sdata); + sta_info_flush(sdata, -1); /* if merging, indicate to driver that we leave the old IBSS */ if (sdata->vif.cfg.ibss_joined) { @@ -682,7 +682,7 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) ifibss->state = IEEE80211_IBSS_MLME_SEARCH; - sta_info_flush(sdata); + sta_info_flush(sdata, -1); spin_lock_bh(&ifibss->incomplete_lock); while (!list_empty(&ifibss->incomplete_stations)) { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 227c8dc3fbe5..b75b83a5142b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -511,7 +511,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do * would have removed them, but in other modes there shouldn't * be any stations. */ - flushed = sta_info_flush(sdata); + flushed = sta_info_flush(sdata, -1); WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN && flushed > 0); /* don't count this interface for allmulti while it is down */ diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 49f79512c144..32475da98d73 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1214,7 +1214,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) netif_carrier_off(sdata->dev); /* flush STAs and mpaths on this iface */ - sta_info_flush(sdata); + sta_info_flush(sdata, -1); ieee80211_free_keys(sdata, true); mesh_path_flush_by_iface(sdata); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index dd97f402a624..e1554666d706 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3151,7 +3151,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sdata->vif.cfg.ssid_len = 0; /* remove AP and TDLS peers */ - sta_info_flush(sdata); + sta_info_flush(sdata, -1); /* finally reset all BSS / config parameters */ if (!ieee80211_vif_is_mld(&sdata->vif)) diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c index 2dd4a2196af4..9ef14e475c90 100644 --- a/net/mac80211/ocb.c +++ b/net/mac80211/ocb.c @@ -208,7 +208,7 @@ int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata) lockdep_assert_wiphy(sdata->local->hw.wiphy); ifocb->joined = false; - sta_info_flush(sdata); + sta_info_flush(sdata, -1); spin_lock_bh(&ifocb->incomplete_lock); while (!list_empty(&ifocb->incomplete_stations)) { diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4391d8dd634b..da5fdd6f5c85 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1566,7 +1566,8 @@ void sta_info_stop(struct ieee80211_local *local) } -int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans) +int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans, + int link_id) { struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; @@ -1580,12 +1581,18 @@ int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans) WARN_ON(vlans && !sdata->bss); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { - if (sdata == sta->sdata || - (vlans && sdata->bss == sta->sdata->bss)) { - if (!WARN_ON(__sta_info_destroy_part1(sta))) - list_add(&sta->free_list, &free_list); - ret++; - } + if (sdata != sta->sdata && + (!vlans || sdata->bss != sta->sdata->bss)) + continue; + + if (link_id >= 0 && sta->sta.valid_links && + !(sta->sta.valid_links & BIT(link_id))) + continue; + + if (!WARN_ON(__sta_info_destroy_part1(sta))) + list_add(&sta->free_list, &free_list); + + ret++; } if (!list_empty(&free_list)) { diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5ef1554f991f..f03731a5bbee 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -886,8 +886,12 @@ void sta_info_stop(struct ieee80211_local *local); * * @sdata: sdata to remove all stations from * @vlans: if the given interface is an AP interface, also flush VLANs + * @link_id: if given (>=0), all those STA entries using @link_id only + * will be removed. If -1 is passed, all STA entries will be + * removed. */ -int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans); +int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans, + int link_id); /** * sta_info_flush - flush matching STA entries from the STA table @@ -895,10 +899,14 @@ int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans); * Returns the number of removed STA entries. * * @sdata: sdata to remove all stations from + * @link_id: if given (>=0), all those STA entries using @link_id only + * will be removed. If -1 is passed, all STA entries will be + * removed. */ -static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) +static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata, + int link_id) { - return __sta_info_flush(sdata, false); + return __sta_info_flush(sdata, false, link_id); } void sta_set_rate_info_tx(struct sta_info *sta, From 16405bd7fd2ea8553bc87a5aa9720e3014e91df6 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Mon, 5 Feb 2024 21:59:52 +0530 Subject: [PATCH 336/378] wifi: mac80211: remove only own link stations during stop_ap Currently, whenever AP link is brought down via ieee80211_stop_ap() function, all stations connected to the sdata are flushed. However, in case of MLO there is a requirement to flush only stations connected to that link and not all. For instance - Consider 2 GHz and 5 GHz are AP MLD. Now due to some reason 5 GHz link of this AP is going down (link removal or any other case). All stations connected, even legacy stations connected to 2 GHz link AP would also be flushed. Flushing of other link stations is not desirable. Fix this issue by passing self link ID to sta_flush() function. This would then only remove the stations which are still using the passed link ID as their link sta. Other stations will not be affected. Signed-off-by: Aditya Kumar Singh Link: https://msgid.link/20240205162952.1697646-4-quic_adisi@quicinc.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 41fca30c896e..0744113f3535 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1616,7 +1616,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, link_conf->ema_ap = false; link_conf->bssid_indicator = 0; - __sta_info_flush(sdata, true, -1); + __sta_info_flush(sdata, true, link_id); ieee80211_free_keys(sdata, true); link_conf->enable_beacon = false; From 675516f55db27b351334c9027407e40b1e72818b Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Tue, 6 Feb 2024 16:54:04 +0200 Subject: [PATCH 337/378] wifi: mac80211_hwsim: Add 160MHz bw range to regdom_custom_04 This allows testing 160MHz channels with DFS concurrent. While at it, remove the TODO for adding a module param to enable NL80211_EXT_FEATURE_DFS_CONCURRENT. This is not really needed as mac80211_hwsim still needs to be loaded with custom regdom. Signed-off-by: Andrei Otcheretianski Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206164849.1b9955e511f0.I5e5315e3a047db3677bfb5ead003a3a4f7d29b13@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index c337afd8bee2..0554946ed30e 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -196,8 +196,11 @@ static const struct ieee80211_regdomain hwsim_world_regdom_custom_04 = { .reg_rules = { REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0), REG_RULE(2484 - 10, 2484 + 10, 40, 0, 20, 0), - REG_RULE(5150 - 10, 5240 + 10, 80, 0, 30, 0), + REG_RULE(5150 - 10, 5240 + 10, 80, 0, 30, NL80211_RRF_AUTO_BW), REG_RULE(5260 - 10, 5320 + 10, 80, 0, 30, + NL80211_RRF_DFS_CONCURRENT | NL80211_RRF_DFS | + NL80211_RRF_AUTO_BW), + REG_RULE(5500 - 10, 5720 + 10, 160, 0, 30, NL80211_RRF_DFS_CONCURRENT | NL80211_RRF_DFS), REG_RULE(5745 - 10, 5825 + 10, 80, 0, 30, 0), REG_RULE(5855 - 10, 5925 + 10, 80, 0, 33, 0), @@ -5390,7 +5393,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, schedule_timeout_interruptible(1); } - /* TODO: Add param */ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_DFS_CONCURRENT); From 7b5e25b8baebc02db728bfbdc3080be863144c7b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 16:54:07 +0200 Subject: [PATCH 338/378] wifi: cfg80211: rename UHB to 6 GHz UHB stands for "Ultra High Band", but this term doesn't really exist in the spec. Rename all occurrences to "6 GHz", but keep a few defines for userspace API compatibility. Link: https://msgid.link/20240206164849.c9cfb9400839.I153db3b951934a1d84409c17fbe1f1d1782543fa@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 10 +++++----- include/uapi/linux/nl80211.h | 31 ++++++++++++++++++++----------- net/wireless/nl80211.c | 8 ++++---- net/wireless/reg.c | 10 +++++----- net/wireless/scan.c | 8 ++++---- 5 files changed, 38 insertions(+), 29 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 62894b024e88..7bb8484e859e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -118,9 +118,9 @@ struct wiphy; * restrictions. * @IEEE80211_CHAN_NO_EHT: EHT operation is not permitted on this channel. * @IEEE80211_CHAN_DFS_CONCURRENT: See %NL80211_RRF_DFS_CONCURRENT - * @IEEE80211_CHAN_NO_UHB_VLP_CLIENT: Client connection with VLP AP + * @IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT: Client connection with VLP AP * not permitted using this channel - * @IEEE80211_CHAN_NO_UHB_AFC_CLIENT: Client connection with AFC AP + * @IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT: Client connection with AFC AP * not permitted using this channel */ enum ieee80211_channel_flags { @@ -146,8 +146,8 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_NO_320MHZ = 1<<19, IEEE80211_CHAN_NO_EHT = 1<<20, IEEE80211_CHAN_DFS_CONCURRENT = 1<<21, - IEEE80211_CHAN_NO_UHB_VLP_CLIENT= 1<<22, - IEEE80211_CHAN_NO_UHB_AFC_CLIENT= 1<<23, + IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT = 1<<22, + IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT = 1<<23, }; #define IEEE80211_CHAN_NO_HT40 \ @@ -4935,7 +4935,7 @@ struct cfg80211_ops { * enum wiphy_flags - wiphy capability flags * * @WIPHY_FLAG_SPLIT_SCAN_6GHZ: if set to true, the scan request will be split - * into two, first for legacy bands and second for UHB. + * into two, first for legacy bands and second for 6 GHz. * @WIPHY_FLAG_NETNS_OK: if not set, do not allow changing the netns of this * wiphy at all * @WIPHY_FLAG_PS_ON_BY_DEFAULT: if set to true, powersave will be enabled diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 805bfe712971..13fa10804909 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -11,7 +11,7 @@ * Copyright 2008 Jouni Malinen * Copyright 2008 Colin McCabe * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -4269,9 +4269,9 @@ enum nl80211_wmm_rule { * allowed for peer-to-peer or adhoc communication under the control * of a DFS master which operates on the same channel (FCC-594280 D01 * Section B.3). Should be used together with %NL80211_RRF_DFS only. - * @NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT: Client connection to VLP AP + * @NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP * not allowed using this channel - * @NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT: Client connection to AFC AP + * @NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP * not allowed using this channel * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined @@ -4313,8 +4313,8 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_EHT, NL80211_FREQUENCY_ATTR_PSD, NL80211_FREQUENCY_ATTR_DFS_CONCURRENT, - NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT, - NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT, + NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT, + NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -4327,6 +4327,10 @@ enum nl80211_frequency_attr { #define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR #define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \ NL80211_FREQUENCY_ATTR_IR_CONCURRENT +#define NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT \ + NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT +#define NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT \ + NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT /** * enum nl80211_bitrate_attr - bitrate attributes @@ -4517,8 +4521,8 @@ enum nl80211_sched_scan_match_attr { peer-to-peer or adhoc communication under the control of a DFS master which operates on the same channel (FCC-594280 D01 Section B.3). Should be used together with %NL80211_RRF_DFS only. - * @NL80211_RRF_NO_UHB_VLP_CLIENT: Client connection to VLP AP not allowed - * @NL80211_RRF_NO_UHB_AFC_CLIENT: Client connection to AFC AP not allowed + * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed + * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed */ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = 1<<0, @@ -4541,8 +4545,8 @@ enum nl80211_reg_rule_flags { NL80211_RRF_NO_EHT = 1<<19, NL80211_RRF_PSD = 1<<20, NL80211_RRF_DFS_CONCURRENT = 1<<21, - NL80211_RRF_NO_UHB_VLP_CLIENT = 1<<22, - NL80211_RRF_NO_UHB_AFC_CLIENT = 1<<23, + NL80211_RRF_NO_6GHZ_VLP_CLIENT = 1<<22, + NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1<<23, }; #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR @@ -4551,6 +4555,8 @@ enum nl80211_reg_rule_flags { #define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\ NL80211_RRF_NO_HT40PLUS) #define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT +#define NL80211_RRF_NO_UHB_VLP_CLIENT NL80211_RRF_NO_6GHZ_VLP_CLIENT +#define NL80211_RRF_NO_UHB_AFC_CLIENT NL80211_RRF_NO_6GHZ_AFC_CLIENT /* For backport compatibility with older userspace */ #define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) @@ -5098,14 +5104,17 @@ enum nl80211_bss_use_for { * BSS isn't possible * @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't * supported by the device, and this BSS entry represents one. - * @NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH: STA is not supporting + * @NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH: STA is not supporting * the AP power type (SP, VLP, AP) that the AP uses. */ enum nl80211_bss_cannot_use_reasons { NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY = 1 << 0, - NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH = 1 << 1, + NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH = 1 << 1, }; +#define NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH \ + NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH + /** * enum nl80211_bss - netlink attributes for a BSS * diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5ca545753810..3b3b511f9f69 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1198,11 +1198,11 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, if ((chan->flags & IEEE80211_CHAN_DFS_CONCURRENT) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DFS_CONCURRENT)) goto nla_put_failure; - if ((chan->flags & IEEE80211_CHAN_NO_UHB_VLP_CLIENT) && - nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT)) + if ((chan->flags & IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT)) goto nla_put_failure; - if ((chan->flags & IEEE80211_CHAN_NO_UHB_AFC_CLIENT) && - nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT)) + if ((chan->flags & IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT)) goto nla_put_failure; } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2741b626919a..50cadbad485f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -5,7 +5,7 @@ * Copyright 2008-2011 Luis R. Rodriguez * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2023 Intel Corporation + * Copyright (C) 2018 - 2024 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1595,10 +1595,10 @@ static u32 map_regdom_flags(u32 rd_flags) channel_flags |= IEEE80211_CHAN_NO_EHT; if (rd_flags & NL80211_RRF_DFS_CONCURRENT) channel_flags |= IEEE80211_CHAN_DFS_CONCURRENT; - if (rd_flags & NL80211_RRF_NO_UHB_VLP_CLIENT) - channel_flags |= IEEE80211_CHAN_NO_UHB_VLP_CLIENT; - if (rd_flags & NL80211_RRF_NO_UHB_AFC_CLIENT) - channel_flags |= IEEE80211_CHAN_NO_UHB_AFC_CLIENT; + if (rd_flags & NL80211_RRF_NO_6GHZ_VLP_CLIENT) + channel_flags |= IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT; + if (rd_flags & NL80211_RRF_NO_6GHZ_AFC_CLIENT) + channel_flags |= IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT; if (rd_flags & NL80211_RRF_PSD) channel_flags |= IEEE80211_CHAN_PSD; return channel_flags; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 04039e9dbd05..88e8b25c073a 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -5,7 +5,7 @@ * Copyright 2008 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2016 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #include #include @@ -3041,9 +3041,9 @@ static bool cfg80211_uhb_power_type_valid(const u8 *ie, case IEEE80211_6GHZ_CTRL_REG_LPI_AP: return true; case IEEE80211_6GHZ_CTRL_REG_SP_AP: - return !(flags & IEEE80211_CHAN_NO_UHB_AFC_CLIENT); + return !(flags & IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT); case IEEE80211_6GHZ_CTRL_REG_VLP_AP: - return !(flags & IEEE80211_CHAN_NO_UHB_VLP_CLIENT); + return !(flags & IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT); } } return false; @@ -3112,7 +3112,7 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy, data->restrict_use = 1; data->use_for = 0; data->cannot_use_reasons = - NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH; + NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH; } if (ext) { From a110a3b79177ddd7e7295671df97fb5386406835 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 16:54:08 +0200 Subject: [PATCH 339/378] wifi: cfg80211: optionally support monitor on disabled channels If the hardware supports a disabled channel, it may in some cases be possible to use monitor mode (without any transmit) on it when it's otherwise disabled. Add a new channel flag IEEE80211_CHAN_CAN_MONITOR that makes it possible for a driver to indicate such a thing. Make it per channel so drivers could have a choice with it, perhaps it's only possible on some channels, perhaps some channels are not supported at all, but still there and marked disabled. In _nl80211_parse_chandef() simplify the code and check only for an unknown channel, _cfg80211_chandef_usable() will later check for IEEE80211_CHAN_DISABLED anyway. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206164849.87fad3a21a09.I9116b2fdc2e2c9fd59a9273a64db7fcb41fc0328@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ include/uapi/linux/nl80211.h | 4 ++++ net/wireless/chan.c | 26 +++++++++++++++++++------- net/wireless/core.h | 5 ++++- net/wireless/nl80211.c | 27 ++++++++++++++++++--------- 5 files changed, 49 insertions(+), 17 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7bb8484e859e..0a3151587556 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -122,6 +122,9 @@ struct wiphy; * not permitted using this channel * @IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT: Client connection with AFC AP * not permitted using this channel + * @IEEE80211_CHAN_CAN_MONITOR: This channel can be used for monitor + * mode even in the presence of other (regulatory) restrictions, + * even if it is otherwise disabled. */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = 1<<0, @@ -148,6 +151,7 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_DFS_CONCURRENT = 1<<21, IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT = 1<<22, IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT = 1<<23, + IEEE80211_CHAN_CAN_MONITOR = 1<<24, }; #define IEEE80211_CHAN_NO_HT40 \ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 13fa10804909..546cc176c2a9 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4273,6 +4273,9 @@ enum nl80211_wmm_rule { * not allowed using this channel * @NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP * not allowed using this channel + * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor + * mode despite other (regulatory) restrictions, even if the channel is + * otherwise completely disabled. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -4315,6 +4318,7 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_DFS_CONCURRENT, NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT, NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT, + NL80211_FREQUENCY_ATTR_CAN_MONITOR, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, diff --git a/net/wireless/chan.c b/net/wireless/chan.c index e2ce89afa9ff..3414b2c3abcc 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -1145,7 +1145,7 @@ EXPORT_SYMBOL(cfg80211_chandef_dfs_cac_time); static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, u32 center_freq, u32 bandwidth, - u32 prohibited_flags) + u32 prohibited_flags, bool monitor) { struct ieee80211_channel *c; u32 freq, start_freq, end_freq; @@ -1155,7 +1155,11 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) { c = ieee80211_get_channel_khz(wiphy, freq); - if (!c || c->flags & prohibited_flags) + if (!c) + return false; + if (monitor && c->flags & IEEE80211_CHAN_CAN_MONITOR) + continue; + if (c->flags & prohibited_flags) return false; } @@ -1215,9 +1219,9 @@ static bool cfg80211_edmg_usable(struct wiphy *wiphy, u8 edmg_channels, return true; } -bool cfg80211_chandef_usable(struct wiphy *wiphy, - const struct cfg80211_chan_def *chandef, - u32 prohibited_flags) +bool _cfg80211_chandef_usable(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 prohibited_flags, bool monitor) { struct ieee80211_sta_ht_cap *ht_cap; struct ieee80211_sta_vht_cap *vht_cap; @@ -1379,14 +1383,22 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, if (!cfg80211_secondary_chans_ok(wiphy, ieee80211_chandef_to_khz(chandef), - width, prohibited_flags)) + width, prohibited_flags, monitor)) return false; if (!chandef->center_freq2) return true; return cfg80211_secondary_chans_ok(wiphy, MHZ_TO_KHZ(chandef->center_freq2), - width, prohibited_flags); + width, prohibited_flags, monitor); +} + +bool cfg80211_chandef_usable(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 prohibited_flags) +{ + return _cfg80211_chandef_usable(wiphy, chandef, prohibited_flags, + false); } EXPORT_SYMBOL(cfg80211_chandef_usable); diff --git a/net/wireless/core.h b/net/wireless/core.h index debf63e6c61f..118f2f619828 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -3,7 +3,7 @@ * Wireless configuration interface internals. * * Copyright 2006-2010 Johannes Berg - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #ifndef __NET_WIRELESS_CORE_H #define __NET_WIRELESS_CORE_H @@ -492,6 +492,9 @@ bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, bool cfg80211_wdev_on_sub_chan(struct wireless_dev *wdev, struct ieee80211_channel *chan, bool primary_only); +bool _cfg80211_chandef_usable(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 prohibited_flags, bool monitor); static inline unsigned int elapsed_jiffies_msecs(unsigned long start) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 3b3b511f9f69..612ca99fbf39 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3218,9 +3218,9 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) wdev->iftype == NL80211_IFTYPE_P2P_GO; } -int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, - struct genl_info *info, - struct cfg80211_chan_def *chandef) +static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev, + struct genl_info *info, bool monitor, + struct cfg80211_chan_def *chandef) { struct netlink_ext_ack *extack = info->extack; struct nlattr **attrs = info->attrs; @@ -3245,10 +3245,9 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, chandef->freq1_offset = control_freq % 1000; chandef->center_freq2 = 0; - /* Primary channel not allowed */ - if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED) { + if (!chandef->chan) { NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ], - "Channel is disabled"); + "Unknown channel"); return -EINVAL; } @@ -3343,8 +3342,9 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, return -EINVAL; } - if (!cfg80211_chandef_usable(&rdev->wiphy, chandef, - IEEE80211_CHAN_DISABLED)) { + if (!_cfg80211_chandef_usable(&rdev->wiphy, chandef, + IEEE80211_CHAN_DISABLED, + monitor)) { NL_SET_ERR_MSG(extack, "(extension) channel is disabled"); return -EINVAL; } @@ -3359,6 +3359,13 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, return 0; } +int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, + struct genl_info *info, + struct cfg80211_chan_def *chandef) +{ + return _nl80211_parse_chandef(rdev, info, false, chandef); +} + static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, struct net_device *dev, struct genl_info *info, @@ -3383,7 +3390,9 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, link_id = 0; } - result = nl80211_parse_chandef(rdev, info, &chandef); + result = _nl80211_parse_chandef(rdev, info, + iftype == NL80211_IFTYPE_MONITOR, + &chandef); if (result) return result; From 49c17da387bb069d802b9be538f0f07d08e1c2b8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Feb 2024 16:54:09 +0200 Subject: [PATCH 340/378] wifi: mac80211: drop injection on disabled-chan monitor If the driver uses the new IEEE80211_CHAN_CAN_MONITOR, we may monitor on channels that are, e.g. via regulatory, otherwise considered disabled. However, we really shouldn't transmit on them, so prevent that. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206164849.9c03dcf67dbe.Ib86a851c274c440908c663f6dd774b79bfc3965d@changeid Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 876de3ba98ba..373275d18f5a 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -5,7 +5,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation * * Transmit and frame generation functions. */ @@ -2393,6 +2393,14 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, else goto fail_rcu; + /* + * If driver/HW supports IEEE80211_CHAN_CAN_MONITOR we still + * shouldn't transmit on disabled channels. + */ + if (!cfg80211_chandef_usable(local->hw.wiphy, chandef, + IEEE80211_CHAN_DISABLED)) + goto fail_rcu; + /* * Frame injection is not allowed if beaconing is not allowed * or if we need radar detection. Beaconing is usually not allowed when From 93d9f26db5b34ec4d2d5056aeb8819cbef35519c Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Tue, 6 Feb 2024 16:54:10 +0200 Subject: [PATCH 341/378] wifi: nl80211: allow reporting wakeup for unprot deauth/disassoc Add a report reason for waking up due to an unprotected deauth/disassoc when MFP is used. If setting wowlan to wake on disconnection, and an unprotected deatuh/disassoc arrived (in MFP), some drivers might want to report wakeup due to unprotected deauth/disassoc, rather than dissassociation. Add support for that. Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206164849.8dc9ad531a17.I7f8e926adf927f762e11aaa3458f6354665c7fc5@changeid Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 546cc176c2a9..f23ecbdd84a2 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -5757,6 +5757,8 @@ struct nl80211_pattern_support { * %NL80211_ATTR_SCAN_FREQUENCIES contains more than one * frequency, it means that the match occurred in more than one * channel. + * @NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC: For wakeup reporting only. + * Wake up happened due to unprotected deauth or disassoc frame in MFP. * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number * @@ -5784,6 +5786,7 @@ enum nl80211_wowlan_triggers { NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, NL80211_WOWLAN_TRIG_NET_DETECT, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS, + NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC, /* keep last */ NUM_NL80211_WOWLAN_TRIG, From a64be8296e31f432d4a9df4db684cc8a250eb81c Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Tue, 6 Feb 2024 16:54:11 +0200 Subject: [PATCH 342/378] wifi: cfg80211: report unprotected deauth/disassoc in wowlan Add to cfg80211_wowlan_wakeup another wakeup reason - unprot_deauth_disassoc. To be set to true if the woke up was due to an unprotected deauth or disassoc frame in MFP. In that case report WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC. Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206164849.a3d739850d03.I8f52a21c4f36d1af1f8068bed79e2f9cbf8289ef@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 ++++- net/wireless/nl80211.c | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0a3151587556..93e9abb7fc3d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3612,12 +3612,15 @@ struct cfg80211_wowlan_nd_info { * @tcp_connlost: TCP connection lost or failed to establish * @tcp_nomoretokens: TCP data ran out of tokens * @net_detect: if not %NULL, woke up because of net detect + * @unprot_deauth_disassoc: woke up due to unprotected deauth or + * disassoc frame (in MFP). */ struct cfg80211_wowlan_wakeup { bool disconnect, magic_pkt, gtk_rekey_failure, eap_identity_req, four_way_handshake, rfkill_release, packet_80211, - tcp_match, tcp_connlost, tcp_nomoretokens; + tcp_match, tcp_connlost, tcp_nomoretokens, + unprot_deauth_disassoc; s32 pattern_idx; u32 packet_present_len, packet_len; const void *packet; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 612ca99fbf39..5f18cbf7cc3d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -19851,6 +19851,11 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS)) goto free_msg; + if (wakeup->unprot_deauth_disassoc && + nla_put_flag(msg, + NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC)) + goto free_msg; + if (wakeup->packet) { u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211; u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN; From 24e5252c590dd8fd9ed702e0fbe426eda78641cd Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Tue, 6 Feb 2024 18:02:12 +0200 Subject: [PATCH 343/378] wifi: iwlwifi: iwlmvm: handle unprotected deauth/disassoc in d3 In MFP, do not disconnect if an unprotected deauth or disassoc was received during D3. For that, need to configure wowlan with MFP (IS_11W_ASSOC). Now, in case of an unprotected deauth/disassoc, the wakeup reason returned by the firmware will be: IWL_WAKEUP_BY_11W_UNPROTECTED_DEAUTH_OR_DISASSOC (and not IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH which will cause a disconnection). Also, report this reason to cfg80211. In another patch, the driver will send an SA query. Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://msgid.link/20240206175739.fde438a22e3f.I3c8497520aaa95a22febff727b0ad08146965d47@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index e1c77276557d..26c01d740d0d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -925,6 +925,9 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, wowlan_config_cmd->flags = ENABLE_L3_FILTERING | ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING; + if (ap_sta->mfp) + wowlan_config_cmd->flags |= IS_11W_ASSOC; + if (iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_CONFIGURATION, 0) < 6) { /* Query the last used seqno and set it */ int ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); @@ -1511,6 +1514,9 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET) wakeup.tcp_match = true; + if (reasons & IWL_WAKEUP_BY_11W_UNPROTECTED_DEAUTH_OR_DISASSOC) + wakeup.unprot_deauth_disassoc = true; + if (status->wake_packet) { int pktsize = status->wake_packet_bufsize; int pktlen = status->wake_packet_length; From 0d2fc8821a7d667180ce27732697105db843a1b9 Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Thu, 8 Feb 2024 18:58:35 +0200 Subject: [PATCH 344/378] wifi: iwlwifi: nvm: parse the VLP/AFC bit from regulatory 6 GHz STA supports different power types as LPI, SP, VLP. and this information is provided by regulatory info. Add support in driver to parse the power type capability in regulatory info from FW and set it to the channel flags. Signed-off-by: Mukesh Sisodiya Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.9c6a4acabdb3.I501de5c0d86b9702bf61158a2e91c954a1da9a2a@changeid Signed-off-by: Johannes Berg --- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 33 ++++++++++++++++--- .../wireless/intel/iwlwifi/iwl-nvm-parse.h | 2 +- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 +- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index d2f133255ee6..baa39a18087a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -156,6 +156,8 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = { * @NVM_CHANNEL_80MHZ: 80 MHz channel okay * @NVM_CHANNEL_160MHZ: 160 MHz channel okay * @NVM_CHANNEL_DC_HIGH: DC HIGH required/allowed (?) + * @NVM_CHANNEL_VLP: client support connection to UHB VLP AP + * @NVM_CHANNEL_AFC: client support connection to UHB AFC AP */ enum iwl_nvm_channel_flags { NVM_CHANNEL_VALID = BIT(0), @@ -170,6 +172,8 @@ enum iwl_nvm_channel_flags { NVM_CHANNEL_80MHZ = BIT(10), NVM_CHANNEL_160MHZ = BIT(11), NVM_CHANNEL_DC_HIGH = BIT(12), + NVM_CHANNEL_VLP = BIT(13), + NVM_CHANNEL_AFC = BIT(14), }; /** @@ -309,7 +313,7 @@ static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level, /* Note: already can print up to 101 characters, 110 is the limit! */ IWL_DEBUG_DEV(dev, level, - "Ch. %d: 0x%x:%s%s%s%s%s%s%s%s%s%s%s%s\n", + "Ch. %d: 0x%x:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", chan, flags, CHECK_AND_PRINT_I(VALID), CHECK_AND_PRINT_I(IBSS), @@ -322,7 +326,9 @@ static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level, CHECK_AND_PRINT_I(40MHZ), CHECK_AND_PRINT_I(80MHZ), CHECK_AND_PRINT_I(160MHZ), - CHECK_AND_PRINT_I(DC_HIGH)); + CHECK_AND_PRINT_I(DC_HIGH), + CHECK_AND_PRINT_I(VLP), + CHECK_AND_PRINT_I(AFC)); #undef CHECK_AND_PRINT_I } @@ -366,6 +372,12 @@ static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, enum nl80211_band band, (flags & IEEE80211_CHAN_NO_IR)) flags |= IEEE80211_CHAN_IR_CONCURRENT; + /* Set the AP type for the UHB case. */ + if (!(nvm_flags & NVM_CHANNEL_VLP)) + flags |= IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT; + if (!(nvm_flags & NVM_CHANNEL_AFC)) + flags |= IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT; + return flags; } @@ -1600,7 +1612,8 @@ IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan, int ch_idx, u16 nvm_flags, struct iwl_reg_capa reg_capa, - const struct iwl_cfg *cfg) + const struct iwl_cfg *cfg, + bool uats_enabled) { u32 flags = NL80211_RRF_NO_HT40; @@ -1645,6 +1658,16 @@ static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan, flags &= ~NL80211_RRF_NO_IR; } } + + /* Set the AP type for the UHB case. */ + if (uats_enabled) { + if (!(nvm_flags & NVM_CHANNEL_VLP)) + flags |= NL80211_RRF_NO_6GHZ_VLP_CLIENT; + + if (!(nvm_flags & NVM_CHANNEL_AFC)) + flags |= NL80211_RRF_NO_6GHZ_AFC_CLIENT; + } + /* * reg_capa is per regulatory domain so apply it for every channel */ @@ -1699,7 +1722,7 @@ static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver) struct ieee80211_regdomain * iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, int num_of_ch, __le32 *channels, u16 fw_mcc, - u16 geo_info, u32 cap, u8 resp_ver) + u16 geo_info, u32 cap, u8 resp_ver, bool uats_enabled) { int ch_idx; u16 ch_flags; @@ -1765,7 +1788,7 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, reg_rule_flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, ch_flags, reg_capa, - cfg); + cfg, uats_enabled); /* we can't continue the same rule */ if (ch_idx == 0 || prev_reg_rule_flags != reg_rule_flags || diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 651ed25b683b..fd9c3bed9407 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -50,7 +50,7 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, struct ieee80211_regdomain * iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, int num_of_ch, __le32 *channels, u16 fw_mcc, - u16 geo_info, u32 cap, u8 resp_ver); + u16 geo_info, u32 cap, u8 resp_ver, bool uats_enabled); /** * struct iwl_nvm_section - describes an NVM section in memory. diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 576b90da94ff..8503c5b6c756 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -138,7 +138,8 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, resp->channels, __le16_to_cpu(resp->mcc), __le16_to_cpu(resp->geo_info), - le32_to_cpu(resp->cap), resp_ver); + le32_to_cpu(resp->cap), resp_ver, + mvm->fwrt.uats_enabled); /* Store the return source id */ src_id = resp->source_id; if (IS_ERR_OR_NULL(regd)) { From 07da4a1b2a59d1392fc61ed12692602b89c33e8a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Feb 2024 18:58:36 +0200 Subject: [PATCH 345/378] wifi: iwlwifi: mvm: work around A-MSDU size problem The firmware will now start with 1500 byte A-MSDU size rather than 3500 as before, and that seems to cause some really hard to debug problems. Keep A-MSDU disabled if the size is less than 2000 to disable this for now. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.3dcd0a1767d0.I450d35f3085b3b04a96dd1e1e7d8c27bda9ce8f5@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index 71d92635d6d7..00860feefa7a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #include "rs.h" #include "fw-api.h" @@ -479,9 +479,15 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, } if (flags & IWL_TLC_NOTIF_FLAG_AMSDU && !mvm_link_sta->orig_amsdu_len) { + u32 enabled = le32_to_cpu(notif->amsdu_enabled); u16 size = le32_to_cpu(notif->amsdu_size); int i; + if (size < 2000) { + size = 0; + enabled = 0; + } + if (link_sta->agg.max_amsdu_len < size) { /* * In debug link_sta->agg.max_amsdu_len < size @@ -492,7 +498,7 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, goto out; } - mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled); + mvmsta->amsdu_enabled = enabled; mvmsta->max_amsdu_len = size; link_sta->agg.max_rc_amsdu_len = mvmsta->max_amsdu_len; From 59214747f26a6c1b3616ef75f9eec7543ddba8e4 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 8 Feb 2024 18:58:37 +0200 Subject: [PATCH 346/378] wifi: iwlwifi: mvm: Extend support for P2P service discovery New additions to the P2P specification use action frames to extend the P2P device discovery and service discovery. Thus, configure the P2P Device link to accept all management frames. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.7ae41234de7b.Ie0b08d4b965409ef6df5505396927567fb899d52@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c index f313a8d771e4..4dc692c2c449 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022 - 2023 Intel Corporation + * Copyright (C) 2022 - 2024 Intel Corporation */ #include "mvm.h" @@ -205,8 +205,11 @@ static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, cmd.p2p_dev.is_disc_extended = iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif); - /* Override the filter flags to accept only probe requests */ - cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); + /* Override the filter flags to accept all management frames. This is + * needed to support both P2P device discovery using probe requests and + * P2P service discovery using action frames + */ + cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT); return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); } From 4cdb86487e3eaddb4b3a7df30ae709e810aac84b Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 8 Feb 2024 18:58:38 +0200 Subject: [PATCH 347/378] wifi: iwlwifi: mvm: Fix the listener MAC filter flags One of the flags was from the wrong API. Fixes: 9be162a7b670 ("wifi: iwlwifi: mvm: add support for the new MAC CTXT command") Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.a338c30ec4e9.Ic2813cdeba4443c692d462fc4859392f069d7e33@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c index 4dc692c2c449..bb7851042177 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c @@ -167,7 +167,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_PROMISC | - MAC_FILTER_IN_CONTROL_AND_MGMT | + MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT | MAC_CFG_FILTER_ACCEPT_BEACON | MAC_CFG_FILTER_ACCEPT_PROBE_REQ | MAC_CFG_FILTER_ACCEPT_GRP); From 41c5f4707d9d54255f5bfa90d184eac68f06a2f6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Feb 2024 18:58:39 +0200 Subject: [PATCH 348/378] wifi: iwlwifi: api: fix constant version to match FW The versioning here comes from the firmware, so it should be the same as in the firmware, fix that. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.fbcb99d896b3.Ibf018d22ca673565cb9028adabd04d4804231ac0@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/mac.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h index 55882190251c..545826973a80 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022, 2024 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_mac_h__ @@ -431,8 +431,8 @@ enum iwl_he_pkt_ext_constellations { }; #define MAX_HE_SUPP_NSS 2 -#define MAX_CHANNEL_BW_INDX_API_D_VER_2 4 -#define MAX_CHANNEL_BW_INDX_API_D_VER_3 5 +#define MAX_CHANNEL_BW_INDX_API_D_VER_1 4 +#define MAX_CHANNEL_BW_INDX_API_D_VER_2 5 /** * struct iwl_he_pkt_ext_v1 - QAM thresholds @@ -455,7 +455,7 @@ enum iwl_he_pkt_ext_constellations { * (0-low_th, 1-high_th) */ struct iwl_he_pkt_ext_v1 { - u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_2][2]; + u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_1][2]; } __packed; /* PKT_EXT_DOT11AX_API_S_VER_1 */ /** @@ -480,7 +480,7 @@ struct iwl_he_pkt_ext_v1 { * (0-low_th, 1-high_th) */ struct iwl_he_pkt_ext_v2 { - u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_3][2]; + u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_2][2]; } __packed; /* PKT_EXT_DOT11AX_API_S_VER_2 */ /** From 8efadbc3882b8f9084869c5da9660d49cd62c060 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Feb 2024 18:58:40 +0200 Subject: [PATCH 349/378] wifi: iwlwifi: don't use TRUE/FALSE with bool With C99 bool we really also should use true/false, not the upper-case variants, wherever they may actually be coming from. Fix that. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.5732dd306ee9.Ifc07c026ac3779429e3dc949e96c9437e89f7bf9@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 10 +++++----- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 6cfcf1c14eaf..561d0c261123 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -1245,7 +1245,7 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync, } } - fwrt->trans->dbg.restart_required = FALSE; + fwrt->trans->dbg.restart_required = false; IWL_DEBUG_FW(fwrt, "WRT: tp %d, reset_fw %d\n", tp, dump_data.trig->reset_fw); IWL_DEBUG_FW(fwrt, @@ -1255,22 +1255,22 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync, if (fwrt->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000) { - fwrt->trans->dbg.restart_required = TRUE; + fwrt->trans->dbg.restart_required = true; } else if (tp == IWL_FW_INI_TIME_POINT_FW_ASSERT && fwrt->trans->dbg.last_tp_resetfw == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) { - fwrt->trans->dbg.restart_required = FALSE; + fwrt->trans->dbg.restart_required = false; fwrt->trans->dbg.last_tp_resetfw = 0xFF; IWL_DEBUG_FW(fwrt, "WRT: FW_ASSERT due to reset_fw_mode-no restart\n"); } else if (le32_to_cpu(dump_data.trig->reset_fw) == IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW) { IWL_DEBUG_FW(fwrt, "WRT: stop and reload firmware\n"); - fwrt->trans->dbg.restart_required = TRUE; + fwrt->trans->dbg.restart_required = true; } else if (le32_to_cpu(dump_data.trig->reset_fw) == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) { IWL_DEBUG_FW(fwrt, "WRT: stop only and no reload firmware\n"); - fwrt->trans->dbg.restart_required = FALSE; + fwrt->trans->dbg.restart_required = false; fwrt->trans->dbg.last_tp_resetfw = le32_to_cpu(dump_data.trig->reset_fw); } else if (le32_to_cpu(dump_data.trig->reset_fw) == diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 26c01d740d0d..b6a9896bce25 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2997,7 +2997,7 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm, if (results->matched_profiles) { memcpy(results->matches, notif->matches, matches_len); - d3_data->nd_results_valid = TRUE; + d3_data->nd_results_valid = true; } /* no scan should be active at this point */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 5381afdd4021..e1c2b7fc92ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1323,7 +1323,7 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) if (le32_to_cpu(cmd.oem_uhb_allow_bitmap) & IWL_UATS_VLP_AP_SUPPORTED || le32_to_cpu(cmd.oem_uhb_allow_bitmap) & IWL_UATS_AFC_AP_SUPPORTED) - mvm->fwrt.uats_enabled = TRUE; + mvm->fwrt.uats_enabled = true; } void iwl_mvm_get_bios_tables(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 77dce70eccc4..a93981cb9714 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1993,7 +1993,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) ieee80211_restart_hw(mvm->hw); } else if (mvm->fwrt.trans->dbg.restart_required) { IWL_DEBUG_INFO(mvm, "FW restart requested after debug collection\n"); - mvm->fwrt.trans->dbg.restart_required = FALSE; + mvm->fwrt.trans->dbg.restart_required = false; ieee80211_restart_hw(mvm->hw); } else if (mvm->trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000) { ieee80211_restart_hw(mvm->hw); From 8cb3a308ceb19f8f74114c618b9e1778be498780 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Feb 2024 18:58:41 +0200 Subject: [PATCH 350/378] wifi: iwlwifi: mvm: fix thermal kernel-doc This was misnamed, fix it. Also add a space to make it look cleaner. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.5eb9f05fbfe2.Id0a4df70f21e7e6d079a7a2084b748ab499b828c@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 6000c79ce4a5..83263d510a45 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -540,8 +540,8 @@ struct iwl_mvm_tt_mgmt { #ifdef CONFIG_THERMAL /** - *struct iwl_mvm_thermal_device - thermal zone related data - * @temp_trips: temperature thresholds for report + * struct iwl_mvm_thermal_device - thermal zone related data + * @trips: temperature thresholds for report * @fw_trips_index: keep indexes to original array - temp_trips * @tzone: thermal zone device data */ From ac71795bfdc988b2dc305fcf91bc6d712c3603be Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Feb 2024 18:58:42 +0200 Subject: [PATCH 351/378] wifi: iwlwifi: error-dump: fix kernel-doc issues Add missing and rename mismatched kernel-doc descriptions. Also just remove the unused IWL_FW_ERROR_DUMP_MAX constant. Signed-off-by: Johannes Berg Reviewed-by: Miriam Rachel Korenblit Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.b4706117c97b.I5151b055dcf23ccab3ea7cd7d654aeb621cd5119@changeid Signed-off-by: Johannes Berg --- .../wireless/intel/iwlwifi/fw/error-dump.h | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index 06d6f7f66430..5c76e3b94968 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2014, 2018-2022 Intel Corporation + * Copyright (C) 2014, 2018-2024 Intel Corporation * Copyright (C) 2014-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -16,7 +16,7 @@ /** * enum iwl_fw_error_dump_type - types of data in the dump file * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0 - * @IWL_FW_ERROR_DUMP_RXF: + * @IWL_FW_ERROR_DUMP_RXF: RX FIFO contents * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as * &struct iwl_fw_error_dump_txcmd packets * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info @@ -24,21 +24,24 @@ * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several * sections like this in a single file. + * @IWL_FW_ERROR_DUMP_TXF: TX FIFO contents * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers * @IWL_FW_ERROR_DUMP_MEM: chunk of memory * @IWL_FW_ERROR_DUMP_ERROR_INFO: description of what triggered this dump. * Structured as &struct iwl_fw_error_dump_trigger_desc. * @IWL_FW_ERROR_DUMP_RB: the content of an RB structured as * &struct iwl_fw_error_dump_rb - * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were + * @IWL_FW_ERROR_DUMP_PAGING: UMAC's image memory segments which were * paged to the DRAM. * @IWL_FW_ERROR_DUMP_RADIO_REG: Dump the radio registers. + * @IWL_FW_ERROR_DUMP_INTERNAL_TXF: internal TX FIFO data * @IWL_FW_ERROR_DUMP_EXTERNAL: used only by external code utilities, and * for that reason is not in use in any other place in the Linux Wi-Fi * stack. * @IWL_FW_ERROR_DUMP_MEM_CFG: the addresses and sizes of fifos in the smem, * which we get from the fw after ALIVE. The content is structured as * &struct iwl_fw_error_dump_smem_cfg. + * @IWL_FW_ERROR_DUMP_D3_DEBUG_DATA: D3 debug data */ enum iwl_fw_error_dump_type { /* 0 is deprecated */ @@ -59,8 +62,6 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_EXTERNAL = 15, /* Do not move */ IWL_FW_ERROR_DUMP_MEM_CFG = 16, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA = 17, - - IWL_FW_ERROR_DUMP_MAX, }; /** @@ -442,7 +443,7 @@ struct iwl_fw_ini_err_table_dump { * struct iwl_fw_error_dump_rb - content of an Receive Buffer * @index: the index of the Receive Buffer in the Rx queue * @rxq: the RB's Rx queue - * @reserved: + * @reserved: reserved * @data: the content of the Receive Buffer */ struct iwl_fw_error_dump_rb { @@ -488,7 +489,7 @@ struct iwl_fw_ini_special_device_memory { * struct iwl_fw_error_dump_paging - content of the UMAC's image page * block on DRAM * @index: the index of the page block - * @reserved: + * @reserved: reserved * @data: the content of the page block */ struct iwl_fw_error_dump_paging { @@ -511,6 +512,7 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) /** * enum iwl_fw_dbg_trigger - triggers available * + * @FW_DBG_TRIGGER_INVALID: invalid trigger value * @FW_DBG_TRIGGER_USER: trigger log collection by user * This should not be defined as a trigger to the driver, but a value the * driver should set to indicate that the trigger was initiated by the @@ -530,14 +532,15 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related * events. * @FW_DBG_TRIGGER_BA: trigger log collection upon BlockAck related events. - * @FW_DBG_TX_LATENCY: trigger log collection when the tx latency goes above a - * threshold. - * @FW_DBG_TDLS: trigger log collection upon TDLS related events. + * @FW_DBG_TRIGGER_TX_LATENCY: trigger log collection when the tx latency + * goes above a threshold. + * @FW_DBG_TRIGGER_TDLS: trigger log collection upon TDLS related events. * @FW_DBG_TRIGGER_TX_STATUS: trigger log collection upon tx status when * the firmware sends a tx reply. * @FW_DBG_TRIGGER_ALIVE_TIMEOUT: trigger log collection if alive flow timeouts * @FW_DBG_TRIGGER_DRIVER: trigger log collection upon a flow failure * in the driver. + * @FW_DBG_TRIGGER_MAX: beyond triggers, number for sizing arrays etc. */ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_INVALID = 0, From ecf7e563031d2685e9d3e6a870d6a7fc60d537f6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Feb 2024 18:58:43 +0200 Subject: [PATCH 352/378] wifi: iwlwifi: api: dbg-tlv: fix up kernel-doc Some things are misnamed or missing, fix that. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.895a2daa0e17.I4d4bdc4ebaf4bfef113a7e6c83848f5a4fb52977@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index 394747deb269..47c914de2992 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #ifndef __iwl_fw_dbg_tlv_h__ #define __iwl_fw_dbg_tlv_h__ @@ -319,7 +319,7 @@ struct iwl_fw_ini_conf_set_tlv { * @IWL_FW_INI_CONFIG_SET_TYPE_CSR: for CSR configuration * @IWL_FW_INI_CONFIG_SET_TYPE_DBGC_DRAM_ADDR: for DBGC_DRAM_ADDR configuration * @IWL_FW_INI_CONFIG_SET_TYPE_PERIPH_SCRATCH_HWM: for PERIPH SCRATCH HWM configuration - * @IWL_FW_INI_ALLOCATION_NUM: max number of configuration supported + * @IWL_FW_INI_CONFIG_SET_TYPE_MAX_NUM: max number of configuration supported */ enum iwl_fw_ini_config_set_type { @@ -360,6 +360,7 @@ enum iwl_fw_ini_allocation_id { * @IWL_FW_INI_LOCATION_SRAM_PATH: SRAM location * @IWL_FW_INI_LOCATION_DRAM_PATH: DRAM location * @IWL_FW_INI_LOCATION_NPK_PATH: NPK location + * @IWL_FW_INI_LOCATION_NUM: number of valid locations */ enum iwl_fw_ini_buffer_location { IWL_FW_INI_LOCATION_INVALID, @@ -439,6 +440,7 @@ enum iwl_fw_ini_region_device_memory_subtype { * Hard coded time points in which the driver can send hcmd or perform dump * collection * + * @IWL_FW_INI_TIME_POINT_INVALID: invalid timepoint * @IWL_FW_INI_TIME_POINT_EARLY: pre loading the FW * @IWL_FW_INI_TIME_POINT_AFTER_ALIVE: first cmd from host after alive notif * @IWL_FW_INI_TIME_POINT_POST_INIT: last cmd in series of init sequence @@ -553,7 +555,7 @@ enum iwl_fw_ini_dump_policy { * enum iwl_fw_ini_dump_type - Determines dump type based on size defined by FW. * * @IWL_FW_INI_DUMP_BRIEF : only dump the most important regions - * @IWL_FW_INI_DEBUG_MEDIUM: dump more regions than "brief", but not all regions + * @IWL_FW_INI_DUMP_MEDIUM: dump more regions than "brief", but not all regions * @IWL_FW_INI_DUMP_VERBOSE : dump all regions */ enum iwl_fw_ini_dump_type { From f16368a157008ac0755aa9457bc71a44e5e8a5f3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Feb 2024 18:58:44 +0200 Subject: [PATCH 353/378] wifi: iwlwifi: fw: file: clean up kernel-doc Add missing kernel-doc and otherwise fix things. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.c41fddd32c18.I1978ed9aa0484b37504f2bd4614ae0f620821f81@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/file.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 08d9aeaedd99..f69d29e531c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -216,6 +216,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * ADD_MODIFY_STA_KEY_API_S_VER_2. * @IWL_UCODE_TLV_API_STA_TYPE: This ucode supports station type assignement. * @IWL_UCODE_TLV_API_NAN2_VER2: This ucode supports NAN API version 2 + * @IWL_UCODE_TLV_API_ADAPTIVE_DWELL: support for adaptive dwell in scanning * @IWL_UCODE_TLV_API_NEW_RX_STATS: should new RX STATISTICS API be used * @IWL_UCODE_TLV_API_QUOTA_LOW_LATENCY: Quota command includes a field * indicating low latency direction. @@ -239,10 +240,15 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * SCAN_OFFLOAD_PROFILES_QUERY_RSP_S. * @IWL_UCODE_TLV_API_MBSSID_HE: This ucode supports v2 of * STA_CONTEXT_DOT11AX_API_S + * @IWL_UCODE_TLV_API_FTM_RTT_ACCURACY: version 7 of the range response API + * is supported by FW, this indicates the RTT confidence value * @IWL_UCODE_TLV_API_SAR_TABLE_VER: This ucode supports different sar * version tables. * @IWL_UCODE_TLV_API_REDUCED_SCAN_CONFIG: This ucode supports v3 of - * SCAN_CONFIG_DB_CMD_API_S. + * SCAN_CONFIG_DB_CMD_API_S. + * @IWL_UCODE_TLV_API_ADWELL_HB_DEF_N_AP: support for setting adaptive dwell + * number of APs in the 5 GHz band + * @IWL_UCODE_TLV_API_BAND_IN_RX_DATA: FW reports band number in RX notification * @IWL_UCODE_TLV_API_NO_HOST_DISABLE_TX: Firmware offloaded the station disable tx * logic. * @IWL_UCODE_TLV_API_INT_DBG_BUF_CLEAR: Firmware supports clearing the debug @@ -573,6 +579,7 @@ enum iwl_fw_dbg_reg_operator { * struct iwl_fw_dbg_reg_op - an operation on a register * * @op: &enum iwl_fw_dbg_reg_operator + * @reserved: reserved * @addr: offset of the register * @val: value */ @@ -619,6 +626,7 @@ struct iwl_fw_dbg_mem_seg_tlv { * @version: version of the TLV - currently 0 * @monitor_mode: &enum iwl_fw_dbg_monitor_mode * @size_power: buffer size will be 2^(size_power + 11) + * @reserved: reserved * @base_reg: addr of the base addr register (PRPH) * @end_reg: addr of the end addr register (PRPH) * @write_ptr_reg: the addr of the reg of the write pointer @@ -729,6 +737,8 @@ enum iwl_fw_dbg_trigger_vif_type { * @trig_dis_ms: the time, in milliseconds, after an occurrence of this * trigger in which another occurrence should be ignored. * @flags: &enum iwl_fw_dbg_trigger_flags + * @reserved: reserved (for alignment) + * @data: trigger data */ struct iwl_fw_dbg_trigger_tlv { __le32 id; @@ -769,7 +779,7 @@ struct iwl_fw_dbg_trigger_missed_bcon { /** * struct iwl_fw_dbg_trigger_cmd - configures trigger for messages from FW. - * cmds: the list of commands to trigger the collection on + * @cmds: the list of commands to trigger the collection on */ struct iwl_fw_dbg_trigger_cmd { struct cmd { @@ -779,7 +789,7 @@ struct iwl_fw_dbg_trigger_cmd { } __packed; /** - * iwl_fw_dbg_trigger_stats - configures trigger for statistics + * struct iwl_fw_dbg_trigger_stats - configures trigger for statistics * @stop_offset: the offset of the value to be monitored * @stop_threshold: the threshold above which to collect * @start_offset: the offset of the value to be monitored From d8af46dec1ff0141f66253336f5a52cbb237d18a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Feb 2024 18:58:45 +0200 Subject: [PATCH 354/378] wifi: iwlwifi: iwl-trans.h: clean up kernel-doc Add missing kernel-doc, fix annotations, etc. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.a66b5cad363b.I3ee4522ac34c3e5984fce5c1cb677fb3db7a965b@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/iwl-trans.h | 64 +++++++++++++++---- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 3047ffc24415..b93cef7b2330 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -519,6 +519,7 @@ struct iwl_pnvm_image { * Must be atomic * @reclaim: free packet until ssn. Returns a list of freed packets. * Must be atomic + * @set_q_ptrs: set queue pointers internally, after D3 when HW state changed * @txq_enable: setup a queue. To setup an AC queue, use the * iwl_trans_ac_txq_enable wrapper. fw_alive must have been called before * this one. The op_mode must not configure the HCMD queue. The scheduler @@ -528,6 +529,8 @@ struct iwl_pnvm_image { * hardware scheduler bug. May sleep. * @txq_disable: de-configure a Tx queue to send AMPDUs * Must be atomic + * @txq_alloc: Allocate a new TX queue, may sleep. + * @txq_free: Free a previously allocated TX queue. * @txq_set_shared_mode: change Tx queue shared/unshared marking * @wait_tx_queues_empty: wait until tx queues are empty. May sleep. * @wait_txq_empty: wait until specific tx queue is empty. May sleep. @@ -547,23 +550,27 @@ struct iwl_pnvm_image { * the op_mode. May be called several times before start_fw, can't be * called after that. * @set_pmi: set the power pmi state + * @sw_reset: trigger software reset of the NIC * @grab_nic_access: wake the NIC to be able to access non-HBUS regs. * Sleeping is not allowed between grab_nic_access and * release_nic_access. * @release_nic_access: let the NIC go to sleep. The "flags" parameter * must be the same one that was sent before to the grab_nic_access. - * @set_bits_mask - set SRAM register according to value and mask. + * @set_bits_mask: set SRAM register according to value and mask. * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last * TX'ed commands and similar. The buffer will be vfree'd by the caller. * Note that the transport must fill in the proper file headers. * @debugfs_cleanup: used in the driver unload flow to make a proper cleanup * of the trans debugfs + * @sync_nmi: trigger a firmware NMI and wait for it to complete * @load_pnvm: save the pnvm data in DRAM * @set_pnvm: set the pnvm data in the prph scratch buffer, inside the * context info. * @load_reduce_power: copy reduce power table to the corresponding DRAM memory * @set_reduce_power: set reduce power table addresses in the sratch buffer * @interrupts: disable/enable interrupts to transport + * @imr_dma_data: set up IMR DMA + * @rxq_dma_data: retrieve RX queue DMA data, see @struct iwl_trans_rxq_dma_data */ struct iwl_trans_ops { @@ -775,7 +782,7 @@ struct iwl_self_init_dram { * @imr_size: imr dram size received from fw * @sram_addr: sram address from debug tlv * @sram_size: sram size from debug tlv - * @imr2sram_remainbyte`: size remained after each dma transfer + * @imr2sram_remainbyte: size remained after each dma transfer * @imr_curr_addr: current dst address used during dma transfer * @imr_base_addr: imr address received from fw */ @@ -822,12 +829,16 @@ struct iwl_pc_data { * @fw_mon: DRAM buffer for firmware monitor * @hw_error: equals true if hw error interrupt was received from the FW * @ini_dest: debug monitor destination uses &enum iwl_fw_ini_buffer_location + * @unsupported_region_msk: unsupported regions out of active_regions * @active_regions: active regions * @debug_info_tlv_list: list of debug info TLVs * @time_point: array of debug time points * @periodic_trig_list: periodic triggers list * @domains_bitmap: bitmap of active domains other than &IWL_FW_INI_DOMAIN_ALWAYS_ON * @ucode_preset: preset based on ucode + * @restart_required: indicates debug restart is required + * @last_tp_resetfw: last handling of reset during debug timepoint + * @imr_data: IMR debug data allocation * @dump_file_name_ext: dump file name extension * @dump_file_name_ext_valid: dump file name extension if valid or not * @num_pc: number of program counter for cpu @@ -930,6 +941,7 @@ struct iwl_pcie_first_tb_buf { * @wd_timeout: queue watchdog timeout (jiffies) - per queue * @frozen: tx stuck queue timer is frozen * @frozen_expiry_remainder: remember how long until the timer fires + * @block: queue is blocked * @bc_tbl: byte count table of the queue (relevant only for gen2 transport) * @write_ptr: 1-st empty entry (index) host_w * @read_ptr: last used entry (index) host_r @@ -938,6 +950,8 @@ struct iwl_pcie_first_tb_buf { * @id: queue id * @low_mark: low watermark, resume queue if free space more than this * @high_mark: high watermark, stop queue if free space less than this + * @overflow_q: overflow queue for handling frames that didn't fit on HW queue + * @overflow_tx: need to transmit from overflow * * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame * descriptors) and required locking structures. @@ -990,10 +1004,19 @@ struct iwl_txq { * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) * @page_offs: offset from skb->cb to mac header page pointer * @dev_cmd_offs: offset from skb->cb to iwl_device_tx_cmd pointer - * @queue_used - bit mask of used queues - * @queue_stopped - bit mask of stopped queues + * @queue_used: bit mask of used queues + * @queue_stopped: bit mask of stopped queues + * @txq: array of TXQ data structures representing the TXQs * @scd_bc_tbls: gen1 pointer to the byte count table of the scheduler * @queue_alloc_cmd_ver: queue allocation command version + * @bc_pool: bytecount DMA allocations pool + * @bc_tbl_size: bytecount table size + * @tso_hdr_page: page allocated (per CPU) for A-MSDU headers when doing TSO + * (and similar usage) + * @tfd: TFD data + * @tfd.max_tbs: max number of buffers per TFD + * @tfd.size: TFD size + * @tfd.addr_size: TFD/TB address size */ struct iwl_trans_txqs { unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_TVQM_QUEUES)]; @@ -1026,27 +1049,35 @@ struct iwl_trans_txqs { /** * struct iwl_trans - transport common data * - * @csme_own - true if we couldn't get ownership on the device - * @ops - pointer to iwl_trans_ops - * @op_mode - pointer to the op_mode + * @csme_own: true if we couldn't get ownership on the device + * @ops: pointer to iwl_trans_ops + * @op_mode: pointer to the op_mode * @trans_cfg: the trans-specific configuration part - * @cfg - pointer to the configuration - * @drv - pointer to iwl_drv + * @cfg: pointer to the configuration + * @drv: pointer to iwl_drv + * @state: current device state * @status: a bit-mask of transport status flags - * @dev - pointer to struct device * that represents the device + * @dev: pointer to struct device * that represents the device * @max_skb_frags: maximum number of fragments an SKB can have when transmitted. * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported. - * @hw_rf_id a u32 with the device RF ID - * @hw_crf_id a u32 with the device CRF ID - * @hw_wfpm_id a u32 with the device wfpm ID + * @hw_rf_id: a u32 with the device RF ID + * @hw_cnv_id: a u32 with the device CNV ID + * @hw_crf_id: a u32 with the device CRF ID + * @hw_wfpm_id: a u32 with the device wfpm ID * @hw_id: a u32 with the ID of the device / sub-device. * Set during transport allocation. * @hw_id_str: a string with info about HW ID. Set during transport allocation. + * @sku_id: the SKU identifier (for PNVM matching) + * @pnvm_loaded: indicates PNVM was loaded + * @hw_rev: the revision data of the HW * @hw_rev_step: The mac step of the HW * @pm_support: set to true in start_hw if link pm is supported * @ltr_enabled: set to true if the LTR is enabled * @fail_to_parse_pnvm_image: set to true if pnvm parsing failed + * @reduce_power_loaded: indicates reduced power section was loaded * @failed_to_load_reduce_power_image: set to true if pnvm loading failed + * @command_groups: pointer to command group name list array + * @command_groups_size: array size of @command_groups * @wide_cmd_header: true when ucode supports wide command header format * @wait_command_queue: wait queue for sync commands * @num_rx_queues: number of RX queues allocated by the transport; @@ -1055,13 +1086,19 @@ struct iwl_trans_txqs { * @iml: a pointer to the image loader itself * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. * The user should use iwl_trans_{alloc,free}_tx_cmd. + * @dev_cmd_pool_name: name for the TX command allocation pool + * @dbgfs_dir: iwlwifi debugfs base dir for this device + * @sync_cmd_lockdep_map: lockdep map for checking sync commands * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before * starting the firmware, used for tracing * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the * start of the 802.11 header in the @rx_mpdu_cmd + * @dbg: additional debug data, see &struct iwl_trans_debug + * @init_dram: FW initialization DMA data * @system_pm_mode: the system-wide power management mode in use. * This mode is set dynamically, depending on the WoWLAN values * configured from the userspace at runtime. + * @name: the device name * @txqs: transport tx queues data. * @mbx_addr_0_step: step address data 0 * @mbx_addr_1_step: step address data 1 @@ -1071,6 +1108,7 @@ struct iwl_trans_txqs { * @reduced_cap_sku: reduced capability supported SKU * @no_160: device not supporting 160 MHz * @step_urm: STEP is in URM, no support for MCS>9 in 320 MHz + * @trans_specific: data for the specific transport this is allocated for/with */ struct iwl_trans { bool csme_own; From d34637a986d6105875f3a02cdd16bd26d5811c38 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 8 Feb 2024 18:58:46 +0200 Subject: [PATCH 355/378] wifi: iwlwifi: bump FW API to 89 for AX/BZ/SC devices Start supporting API version 89 for new devices. Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.b5d0c18c3dad.I55d5bd15638970d27b30b38e9ef47cddf6ba715e@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/cfg/ax210.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c index 456c7fff60a0..2e530fc928a4 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #include #include @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_AX210_UCODE_API_MAX 88 +#define IWL_AX210_UCODE_API_MAX 89 /* Lowest firmware API version supported */ #define IWL_AX210_UCODE_API_MIN 59 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index c858f355701e..14c5cc265fe3 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #include #include @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_BZ_UCODE_API_MAX 88 +#define IWL_BZ_UCODE_API_MAX 89 /* Lowest firmware API version supported */ #define IWL_BZ_UCODE_API_MIN 80 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index be98a174a311..dbbcb2d0968c 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_SC_UCODE_API_MAX 88 +#define IWL_SC_UCODE_API_MAX 89 /* Lowest firmware API version supported */ #define IWL_SC_UCODE_API_MIN 82 From 182094411e29fa76b6651f31fd7b941780a45c56 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Feb 2024 18:58:47 +0200 Subject: [PATCH 356/378] wifi: iwlwifi: mvm: check own capabilities for EMLSR There may be different hardware or configurations supported, so check for our own EMLSR capability before allowing it to be used, in addition to checking the AP's. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240208185302.036443611696.If33caabd7cf372834287863b40b2d6d1ef1ca3f7@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 021592f69f38..084314bf6f36 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -1275,6 +1275,7 @@ static bool iwl_mvm_can_enter_esr(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int primary_link = iwl_mvm_mld_get_primary_link(mvm, vif, desired_links); + const struct wiphy_iftype_ext_capab *ext_capa; bool ret = true; int link_id; @@ -1284,6 +1285,12 @@ static bool iwl_mvm_can_enter_esr(struct iwl_mvm *mvm, if (!(vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP)) return false; + ext_capa = cfg80211_get_iftype_ext_capa(mvm->hw->wiphy, + ieee80211_vif_type_p2p(vif)); + if (!ext_capa || + !(ext_capa->eml_capabilities & IEEE80211_EML_CAP_EMLSR_SUPP)) + return false; + for_each_set_bit(link_id, &desired_links, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_bss_conf *link_conf = link_conf_dereference_protected(vif, link_id); From 35c1bbd93c4e6969b3ac238b48a8bdff3e223ed8 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 5 Feb 2024 21:21:06 +0200 Subject: [PATCH 357/378] wifi: iwlwifi: mvm: remove IWL_MVM_STATUS_NEED_FLUSH_P2P This is set when a P2P ROC ends, and uses as an indication inside iwl_mvm_roc_done_wk that the resources used for this ROC (sta/link) needs to be flushed/deactivated (respectively). But we also have IWL_MVM_STATUS_ROC_RUNNING, which is set whenever P2P ROC starts, and is not even used in iwl_mvm_roc_done_wk. Use IWL_MVM_STATUS_ROC_RUNNING as an indicator, and remove the redundant bit. While at it, add a call to synchronize_net also for the AUX ROC case, which is missing in the existing code. Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.0494f75de311.Ic4aacacf7581a5c9046c4f1df87cbb67470853e7@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 +- .../wireless/intel/iwlwifi/mvm/time-event.c | 89 ++++++++----------- 2 files changed, 38 insertions(+), 55 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 83263d510a45..ce78c21883e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1221,7 +1221,6 @@ struct iwl_mvm { * @IWL_MVM_STATUS_IN_HW_RESTART: HW restart is active * @IWL_MVM_STATUS_ROC_AUX_RUNNING: AUX remain-on-channel is running * @IWL_MVM_STATUS_FIRMWARE_RUNNING: firmware is running - * @IWL_MVM_STATUS_NEED_FLUSH_P2P: need to flush P2P bcast STA * @IWL_MVM_STATUS_IN_D3: in D3 (or at least about to go into it) * @IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE: suppress one error log * if this is set, when intentionally triggered @@ -1236,7 +1235,6 @@ enum iwl_mvm_status { IWL_MVM_STATUS_IN_HW_RESTART, IWL_MVM_STATUS_ROC_AUX_RUNNING, IWL_MVM_STATUS_FIRMWARE_RUNNING, - IWL_MVM_STATUS_NEED_FLUSH_P2P, IWL_MVM_STATUS_IN_D3, IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, IWL_MVM_STATUS_STARTING, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 43732da7ba39..a03eceec70e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -45,32 +45,24 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, te_data->link_id = -1; } -void iwl_mvm_roc_done_wk(struct work_struct *wk) +static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm) { - struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk); - /* * Clear the ROC_RUNNING status bit. * This will cause the TX path to drop offchannel transmissions. * That would also be done by mac80211, but it is racy, in particular - * in the case that the time event actually completed in the firmware - * (which is handled in iwl_mvm_te_handle_notif). - */ - clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); - - synchronize_net(); - - /* - * Flush the offchannel queue -- this is called when the time + * in the case that the time event actually completed in the firmware. + * + * Also flush the offchannel queue -- this is called when the time * event finishes or is canceled, so that frames queued for it * won't get stuck on the queue and be transmitted in the next * time event. */ - - mutex_lock(&mvm->mutex); - if (test_and_clear_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) { + if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) { struct iwl_mvm_vif *mvmvif; + synchronize_net(); + /* * NB: access to this pointer would be racy, but the flush bit * can only be set when we had a P2P-Device VIF, and we have a @@ -105,21 +97,16 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk) } } - /* - * Clear the ROC_AUX_RUNNING status bit. - * This will cause the TX path to drop offchannel transmissions. - * That would also be done by mac80211, but it is racy, in particular - * in the case that the time event actually completed in the firmware - * (which is handled in iwl_mvm_te_handle_notif). - */ + /* Do the same for AUX ROC */ if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) { - /* do the same in case of hot spot 2.0 */ + synchronize_net(); + iwl_mvm_flush_sta(mvm, mvm->aux_sta.sta_id, mvm->aux_sta.tfd_queue_msk); if (mvm->mld_api_is_used) { iwl_mvm_mld_rm_aux_sta(mvm); - goto out_unlock; + return; } /* In newer version of this command an aux station is added only @@ -128,8 +115,14 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk) if (iwl_mvm_has_new_station_api(mvm->fw)) iwl_mvm_rm_aux_sta(mvm); } +} -out_unlock: +void iwl_mvm_roc_done_wk(struct work_struct *wk) +{ + struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk); + + mutex_lock(&mvm->mutex); + iwl_mvm_cleanup_roc(mvm); mutex_unlock(&mvm->mutex); } @@ -294,18 +287,6 @@ static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, } } -static void iwl_mvm_p2p_roc_finished(struct iwl_mvm *mvm) -{ - /* - * If the IWL_MVM_STATUS_NEED_FLUSH_P2P is already set, then the - * roc_done_wk is already scheduled or running, so don't schedule it - * again to avoid a race where the roc_done_wk clears this bit after - * it is set here, affecting the next run of the roc_done_wk. - */ - if (!test_and_set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) - iwl_mvm_roc_finished(mvm); -} - /* * Handles a FW notification for an event that is known to the driver. * @@ -357,7 +338,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, switch (te_data->vif->type) { case NL80211_IFTYPE_P2P_DEVICE: ieee80211_remain_on_channel_expired(mvm->hw); - iwl_mvm_p2p_roc_finished(mvm); + iwl_mvm_roc_finished(mvm); break; case NL80211_IFTYPE_STATION: /* @@ -782,7 +763,7 @@ static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, iwl_mvm_cancel_session_protection(mvm, vif, id, link_id); if (iftype == NL80211_IFTYPE_P2P_DEVICE) { - iwl_mvm_p2p_roc_finished(mvm); + iwl_mvm_roc_finished(mvm); } } return false; @@ -972,7 +953,7 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, /* End TE, notify mac80211 */ mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID; mvmvif->time_event_data.link_id = -1; - iwl_mvm_p2p_roc_finished(mvm); + iwl_mvm_roc_finished(mvm); ieee80211_remain_on_channel_expired(mvm->hw); } else if (le32_to_cpu(notif->start)) { if (WARN_ON(mvmvif->time_event_data.id != @@ -1244,17 +1225,13 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { mvmvif = iwl_mvm_vif_from_mac80211(vif); - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) iwl_mvm_cancel_session_protection(mvm, vif, mvmvif->time_event_data.id, mvmvif->time_event_data.link_id); - iwl_mvm_p2p_roc_finished(mvm); - } else { + else iwl_mvm_roc_station_remove(mvm, mvmvif); - iwl_mvm_roc_finished(mvm); - } - - return; + goto cleanup_roc; } te_data = iwl_mvm_get_roc_te(mvm); @@ -1265,13 +1242,21 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); - if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { + if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) iwl_mvm_remove_time_event(mvm, mvmvif, te_data); - iwl_mvm_p2p_roc_finished(mvm); - } else { + else iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data); - iwl_mvm_roc_finished(mvm); - } + +cleanup_roc: + /* + * In case we get here before the ROC event started, + * (so the status bit isn't set) set it here so iwl_mvm_cleanup_roc will + * cleanup things properly + */ + set_bit(vif->type == NL80211_IFTYPE_P2P_DEVICE ? + IWL_MVM_STATUS_ROC_RUNNING : IWL_MVM_STATUS_ROC_AUX_RUNNING, + &mvm->status); + iwl_mvm_cleanup_roc(mvm); } void iwl_mvm_remove_csa_period(struct iwl_mvm *mvm, From 77770189921e38597a42744c7032b8222538cdf3 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 5 Feb 2024 21:21:08 +0200 Subject: [PATCH 358/378] wifi: iwlwifi: cancel session protection only if there is one mac80211 might (due to an unavoidable race) cancel a ROC that has already expired. In that case the driver should not send the session protection cmd to cancel the ROC. When session protection is supported, the te_data::id field is reused to save the configuration id. Check it before sending the cmd. Signed-off-by: Miri Korenblit Link: https://msgid.link/20240205211151.30176bf869d9.Id811c20d3746b870cbe0c946bbfe1c0ab0a290cb@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/mvm/time-event.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index a03eceec70e9..89b1c7a87660 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -1224,13 +1224,21 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { mvmvif = iwl_mvm_vif_from_mac80211(vif); + te_data = &mvmvif->time_event_data; + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + if (te_data->id >= SESSION_PROTECT_CONF_MAX_ID) { + IWL_DEBUG_TE(mvm, + "No remain on channel event\n"); + return; + } - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) iwl_mvm_cancel_session_protection(mvm, vif, - mvmvif->time_event_data.id, - mvmvif->time_event_data.link_id); - else + te_data->id, + te_data->link_id); + } else { iwl_mvm_roc_station_remove(mvm, mvmvif); + } goto cleanup_roc; } From 414532d8aa8915d9aebd01c6b5aa54bdfd98da71 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 14 Feb 2024 20:08:43 +0100 Subject: [PATCH 359/378] wifi: cfg80211: use IEEE80211_MAX_MESH_ID_LEN appropriately Even if that's the same as IEEE80211_MAX_SSID_LEN, we really should just use IEEE80211_MAX_MESH_ID_LEN for mesh, rather than having the BUILD_BUG_ON()s. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- net/wireless/nl80211.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 93e9abb7fc3d..57c2298af35b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -6223,7 +6223,7 @@ struct wireless_dev { int beacon_interval; struct cfg80211_chan_def preset_chandef; struct cfg80211_chan_def chandef; - u8 id[IEEE80211_MAX_SSID_LEN]; + u8 id[IEEE80211_MAX_MESH_ID_LEN]; u8 id_len, id_up_len; } mesh; struct { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5f18cbf7cc3d..dd9a092b6bab 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4205,8 +4205,6 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) if (netif_running(dev)) return -EBUSY; - BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != - IEEE80211_MAX_MESH_ID_LEN); wdev->u.mesh.id_up_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); memcpy(wdev->u.mesh.id, @@ -4312,8 +4310,6 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_MESH_POINT: if (!info->attrs[NL80211_ATTR_MESH_ID]) break; - BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != - IEEE80211_MAX_MESH_ID_LEN); wdev->u.mesh.id_up_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); memcpy(wdev->u.mesh.id, From cb5942b77c05d54310a0420cac12935e9b6aa21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= Date: Mon, 12 Feb 2024 13:57:37 +0100 Subject: [PATCH 360/378] wifi: wilc1000: prevent use-after-free on vif when cleaning up all interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wilc_netdev_cleanup currently triggers a KASAN warning, which can be observed on interface registration error path, or simply by removing the module/unbinding device from driver: echo spi0.1 > /sys/bus/spi/drivers/wilc1000_spi/unbind ================================================================== BUG: KASAN: slab-use-after-free in wilc_netdev_cleanup+0x508/0x5cc Read of size 4 at addr c54d1ce8 by task sh/86 CPU: 0 PID: 86 Comm: sh Not tainted 6.8.0-rc1+ #117 Hardware name: Atmel SAMA5 unwind_backtrace from show_stack+0x18/0x1c show_stack from dump_stack_lvl+0x34/0x58 dump_stack_lvl from print_report+0x154/0x500 print_report from kasan_report+0xac/0xd8 kasan_report from wilc_netdev_cleanup+0x508/0x5cc wilc_netdev_cleanup from wilc_bus_remove+0xc8/0xec wilc_bus_remove from spi_remove+0x8c/0xac spi_remove from device_release_driver_internal+0x434/0x5f8 device_release_driver_internal from unbind_store+0xbc/0x108 unbind_store from kernfs_fop_write_iter+0x398/0x584 kernfs_fop_write_iter from vfs_write+0x728/0xf88 vfs_write from ksys_write+0x110/0x1e4 ksys_write from ret_fast_syscall+0x0/0x1c [...] Allocated by task 1: kasan_save_track+0x30/0x5c __kasan_kmalloc+0x8c/0x94 __kmalloc_node+0x1cc/0x3e4 kvmalloc_node+0x48/0x180 alloc_netdev_mqs+0x68/0x11dc alloc_etherdev_mqs+0x28/0x34 wilc_netdev_ifc_init+0x34/0x8ec wilc_cfg80211_init+0x690/0x910 wilc_bus_probe+0xe0/0x4a0 spi_probe+0x158/0x1b0 really_probe+0x270/0xdf4 __driver_probe_device+0x1dc/0x580 driver_probe_device+0x60/0x140 __driver_attach+0x228/0x5d4 bus_for_each_dev+0x13c/0x1a8 bus_add_driver+0x2a0/0x608 driver_register+0x24c/0x578 do_one_initcall+0x180/0x310 kernel_init_freeable+0x424/0x484 kernel_init+0x20/0x148 ret_from_fork+0x14/0x28 Freed by task 86: kasan_save_track+0x30/0x5c kasan_save_free_info+0x38/0x58 __kasan_slab_free+0xe4/0x140 kfree+0xb0/0x238 device_release+0xc0/0x2a8 kobject_put+0x1d4/0x46c netdev_run_todo+0x8fc/0x11d0 wilc_netdev_cleanup+0x1e4/0x5cc wilc_bus_remove+0xc8/0xec spi_remove+0x8c/0xac device_release_driver_internal+0x434/0x5f8 unbind_store+0xbc/0x108 kernfs_fop_write_iter+0x398/0x584 vfs_write+0x728/0xf88 ksys_write+0x110/0x1e4 ret_fast_syscall+0x0/0x1c [...] David Mosberger-Tan initial investigation [1] showed that this use-after-free is due to netdevice unregistration during vif list traversal. When unregistering a net device, since the needs_free_netdev has been set to true during registration, the netdevice object is also freed, and as a consequence, the corresponding vif object too, since it is attached to it as private netdevice data. The next occurrence of the loop then tries to access freed vif pointer to the list to move forward in the list. Fix this use-after-free thanks to two mechanisms: - navigate in the list with list_for_each_entry_safe, which allows to safely modify the list as we go through each element. For each element, remove it from the list with list_del_rcu - make sure to wait for RCU grace period end after each vif removal to make sure it is safe to free the corresponding vif too (through unregister_netdev) Since we are in a RCU "modifier" path (not a "reader" path), and because such path is expected not to be concurrent to any other modifier (we are using the vif_mutex lock), we do not need to use RCU list API, that's why we can benefit from list_for_each_entry_safe. [1] https://lore.kernel.org/linux-wireless/ab077dbe58b1ea5de0a3b2ca21f275a07af967d2.camel@egauge.net/ Fixes: 8399918f3056 ("staging: wilc1000: use RCU list to maintain vif interfaces list") Signed-off-by: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240212-wilc_rework_deinit-v1-1-9203ae56c27f@bootlin.com --- .../net/wireless/microchip/wilc1000/netdev.c | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index a8b5c8dec69c..22f461f477f1 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -890,8 +890,7 @@ static const struct net_device_ops wilc_netdev_ops = { void wilc_netdev_cleanup(struct wilc *wilc) { - struct wilc_vif *vif; - int srcu_idx, ifc_cnt = 0; + struct wilc_vif *vif, *vif_tmp; if (!wilc) return; @@ -901,32 +900,19 @@ void wilc_netdev_cleanup(struct wilc *wilc) wilc->firmware = NULL; } - srcu_idx = srcu_read_lock(&wilc->srcu); - list_for_each_entry_rcu(vif, &wilc->vif_list, list) { - if (vif->ndev) - unregister_netdev(vif->ndev); - } - srcu_read_unlock(&wilc->srcu, srcu_idx); - - wilc_wfi_deinit_mon_interface(wilc, false); - destroy_workqueue(wilc->hif_workqueue); - - while (ifc_cnt < WILC_NUM_CONCURRENT_IFC) { + list_for_each_entry_safe(vif, vif_tmp, &wilc->vif_list, list) { mutex_lock(&wilc->vif_mutex); - if (wilc->vif_num <= 0) { - mutex_unlock(&wilc->vif_mutex); - break; - } - vif = wilc_get_wl_to_vif(wilc); - if (!IS_ERR(vif)) - list_del_rcu(&vif->list); - + list_del_rcu(&vif->list); wilc->vif_num--; mutex_unlock(&wilc->vif_mutex); synchronize_srcu(&wilc->srcu); - ifc_cnt++; + if (vif->ndev) + unregister_netdev(vif->ndev); } + wilc_wfi_deinit_mon_interface(wilc, false); + destroy_workqueue(wilc->hif_workqueue); + wilc_wlan_cfg_deinit(wilc); wlan_deinit_locks(wilc); wiphy_unregister(wilc->wiphy); From dd2f633eafa4ce5b9cb2ee09bf5d709535a02e79 Mon Sep 17 00:00:00 2001 From: David Mosberger-Tang Date: Mon, 12 Feb 2024 20:22:30 +0000 Subject: [PATCH 361/378] wifi: wilc1000: validate chip id during bus probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the driver created a net device (typically wlan0) as soon as the module was loaded. This commit changes the driver to follow normal Linux convention of creating the net device only when bus probing detects a supported chip. Signed-off-by: David Mosberger-Tang Tested-By: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240212202057.3468714-1-davidm@egauge.net --- drivers/net/wireless/microchip/wilc1000/spi.c | 69 +++++++++++++++---- .../net/wireless/microchip/wilc1000/wlan.c | 5 -- .../net/wireless/microchip/wilc1000/wlan.h | 5 ++ 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/spi.c b/drivers/net/wireless/microchip/wilc1000/spi.c index c92ee4b73a74..3c4451535c8a 100644 --- a/drivers/net/wireless/microchip/wilc1000/spi.c +++ b/drivers/net/wireless/microchip/wilc1000/spi.c @@ -42,7 +42,7 @@ MODULE_PARM_DESC(enable_crc16, #define WILC_SPI_RSP_HDR_EXTRA_DATA 8 struct wilc_spi { - bool isinit; /* true if SPI protocol has been configured */ + bool isinit; /* true if wilc_spi_init was successful */ bool probing_crc; /* true if we're probing chip's CRC config */ bool crc7_enabled; /* true if crc7 is currently enabled */ bool crc16_enabled; /* true if crc16 is currently enabled */ @@ -55,6 +55,8 @@ struct wilc_spi { static const struct wilc_hif_func wilc_hif_spi; static int wilc_spi_reset(struct wilc *wilc); +static int wilc_spi_configure_bus_protocol(struct wilc *wilc); +static int wilc_validate_chipid(struct wilc *wilc); /******************************************** * @@ -232,8 +234,27 @@ static int wilc_bus_probe(struct spi_device *spi) } clk_prepare_enable(wilc->rtc_clk); + dev_info(&spi->dev, "Selected CRC config: crc7=%s, crc16=%s\n", + enable_crc7 ? "on" : "off", enable_crc16 ? "on" : "off"); + + /* we need power to configure the bus protocol and to read the chip id: */ + + wilc_wlan_power(wilc, true); + + ret = wilc_spi_configure_bus_protocol(wilc); + if (ret) + goto power_down; + + ret = wilc_validate_chipid(wilc); + if (ret) + goto power_down; + + wilc_wlan_power(wilc, false); return 0; +power_down: + clk_disable_unprepare(wilc->rtc_clk); + wilc_wlan_power(wilc, false); netdev_cleanup: wilc_netdev_cleanup(wilc); free: @@ -1102,26 +1123,34 @@ static int wilc_spi_deinit(struct wilc *wilc) static int wilc_spi_init(struct wilc *wilc, bool resume) { - struct spi_device *spi = to_spi_device(wilc->dev); struct wilc_spi *spi_priv = wilc->bus_data; - u32 reg; - u32 chipid; - int ret, i; + int ret; if (spi_priv->isinit) { /* Confirm we can read chipid register without error: */ - ret = wilc_spi_read_reg(wilc, WILC_CHIPID, &chipid); - if (ret == 0) + if (wilc_validate_chipid(wilc) == 0) return 0; - - dev_err(&spi->dev, "Fail cmd read chip id...\n"); } wilc_wlan_power(wilc, true); - /* - * configure protocol - */ + ret = wilc_spi_configure_bus_protocol(wilc); + if (ret) { + wilc_wlan_power(wilc, false); + return ret; + } + + spi_priv->isinit = true; + + return 0; +} + +static int wilc_spi_configure_bus_protocol(struct wilc *wilc) +{ + struct spi_device *spi = to_spi_device(wilc->dev); + struct wilc_spi *spi_priv = wilc->bus_data; + u32 reg; + int ret, i; /* * Infer the CRC settings that are currently in effect. This @@ -1173,6 +1202,15 @@ static int wilc_spi_init(struct wilc *wilc, bool resume) spi_priv->probing_crc = false; + return 0; +} + +static int wilc_validate_chipid(struct wilc *wilc) +{ + struct spi_device *spi = to_spi_device(wilc->dev); + u32 chipid; + int ret; + /* * make sure can read chip id without protocol error */ @@ -1181,9 +1219,10 @@ static int wilc_spi_init(struct wilc *wilc, bool resume) dev_err(&spi->dev, "Fail cmd read chip id...\n"); return ret; } - - spi_priv->isinit = true; - + if (!is_wilc1000(chipid)) { + dev_err(&spi->dev, "Unknown chip id 0x%x\n", chipid); + return -ENODEV; + } return 0; } diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c index 6b2f2269ddf8..68be233c36ce 100644 --- a/drivers/net/wireless/microchip/wilc1000/wlan.c +++ b/drivers/net/wireless/microchip/wilc1000/wlan.c @@ -12,11 +12,6 @@ #define WAKE_UP_TRIAL_RETRY 10000 -static inline bool is_wilc1000(u32 id) -{ - return (id & (~WILC_CHIP_REV_FIELD)) == WILC_1000_BASE_ID; -} - static inline void acquire_bus(struct wilc *wilc, enum bus_acquire acquire) { mutex_lock(&wilc->hif_cs); diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.h b/drivers/net/wireless/microchip/wilc1000/wlan.h index f02775f7e41f..54643d8fef04 100644 --- a/drivers/net/wireless/microchip/wilc1000/wlan.h +++ b/drivers/net/wireless/microchip/wilc1000/wlan.h @@ -409,6 +409,11 @@ struct wilc_cfg_rsp { struct wilc_vif; +static inline bool is_wilc1000(u32 id) +{ + return (id & (~WILC_CHIP_REV_FIELD)) == WILC_1000_BASE_ID; +} + int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer, u32 buffer_size); int wilc_wlan_start(struct wilc *wilc); From 6ca3b88c320b5933b4371f940f70aba382e12ecf Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 13 Feb 2024 15:35:10 +0800 Subject: [PATCH 362/378] wifi: rtw89: fw: add definition of H2C command and C2H event for MRC series For Wi-Fi 7 chips, FW supports MRC (multi-role concurrent) functions including H2C commands and C2H events. We can consider FW MRC functions as a superset of FW MCC (multi-channel concurrent) functions. And, MRC functions can take MLO things into account. Basically before MLO, SW can also manipulate FW MRC to work original SW MCC flow. So, we add them first and implement the handling in the following. And then, SW MCC will call different series of FW functions according to chip later. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240213073514.23796-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.h | 172 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 24 +++- 2 files changed, 190 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 5609e5f7d7eb..409f62cea0d7 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3385,6 +3385,143 @@ inline void RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_Y(void *cmd, u32 val) le32p_replace_bits((__le32 *)cmd + 4, val, GENMASK(31, 0)); } +enum rtw89_h2c_mrc_sch_types { + RTW89_H2C_MRC_SCH_BAND0_ONLY = 0, + RTW89_H2C_MRC_SCH_BAND1_ONLY = 1, + RTW89_H2C_MRC_SCH_DUAL_BAND = 2, +}; + +enum rtw89_h2c_mrc_role_types { + RTW89_H2C_MRC_ROLE_WIFI = 0, + RTW89_H2C_MRC_ROLE_BT = 1, + RTW89_H2C_MRC_ROLE_EMPTY = 2, +}; + +struct rtw89_h2c_mrc_add_role { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 macid_main_bitmap; + __le32 macid_paired_bitmap; +} __packed; + +#define RTW89_H2C_MRC_ADD_ROLE_W0_MACID GENMASK(15, 0) +#define RTW89_H2C_MRC_ADD_ROLE_W0_ROLE_TYPE GENMASK(23, 16) +#define RTW89_H2C_MRC_ADD_ROLE_W0_IS_MASTER BIT(24) +#define RTW89_H2C_MRC_ADD_ROLE_W0_IS_ALT_ROLE BIT(25) +#define RTW89_H2C_MRC_ADD_ROLE_W0_TX_NULL_EN BIT(26) +#define RTW89_H2C_MRC_ADD_ROLE_W0_ROLE_ALT_EN BIT(27) +#define RTW89_H2C_MRC_ADD_ROLE_W1_CENTRAL_CH_SEG GENMASK(7, 0) +#define RTW89_H2C_MRC_ADD_ROLE_W1_PRI_CH GENMASK(15, 8) +#define RTW89_H2C_MRC_ADD_ROLE_W1_BW GENMASK(19, 16) +#define RTW89_H2C_MRC_ADD_ROLE_W1_CH_BAND_TYPE GENMASK(21, 20) +#define RTW89_H2C_MRC_ADD_ROLE_W1_RFK_BY_PASS BIT(22) +#define RTW89_H2C_MRC_ADD_ROLE_W1_CAN_BTC BIT(23) +#define RTW89_H2C_MRC_ADD_ROLE_W1_NULL_EARLY GENMASK(31, 24) +#define RTW89_H2C_MRC_ADD_ROLE_W2_ALT_PERIOD GENMASK(7, 0) +#define RTW89_H2C_MRC_ADD_ROLE_W2_ALT_ROLE_TYPE GENMASK(15, 8) +#define RTW89_H2C_MRC_ADD_ROLE_W2_ALT_ROLE_MACID GENMASK(23, 16) + +struct rtw89_h2c_mrc_add_slot { + __le32 w0; + __le32 w1; + struct rtw89_h2c_mrc_add_role roles[]; +} __packed; + +#define RTW89_H2C_MRC_ADD_SLOT_W0_DURATION GENMASK(15, 0) +#define RTW89_H2C_MRC_ADD_SLOT_W0_COURTESY_EN BIT(17) +#define RTW89_H2C_MRC_ADD_SLOT_W0_ROLE_NUM GENMASK(31, 24) +#define RTW89_H2C_MRC_ADD_SLOT_W1_COURTESY_PERIOD GENMASK(7, 0) +#define RTW89_H2C_MRC_ADD_SLOT_W1_COURTESY_TARGET GENMASK(15, 8) + +struct rtw89_h2c_mrc_add { + __le32 w0; + /* Logically append flexible struct rtw89_h2c_mrc_add_slot, but there + * are other flexible array inside it. We cannot access them correctly + * through this struct. So, in case misusing, we don't really declare + * it here. + */ +} __packed; + +#define RTW89_H2C_MRC_ADD_W0_SCH_IDX GENMASK(3, 0) +#define RTW89_H2C_MRC_ADD_W0_SCH_TYPE GENMASK(7, 4) +#define RTW89_H2C_MRC_ADD_W0_SLOT_NUM GENMASK(15, 8) +#define RTW89_H2C_MRC_ADD_W0_BTC_IN_SCH BIT(16) + +enum rtw89_h2c_mrc_start_actions { + RTW89_H2C_MRC_START_ACTION_START_NEW = 0, + RTW89_H2C_MRC_START_ACTION_REPLACE_OLD = 1, +}; + +struct rtw89_h2c_mrc_start { + __le32 w0; + __le32 start_tsf_low; + __le32 start_tsf_high; +} __packed; + +#define RTW89_H2C_MRC_START_W0_SCH_IDX GENMASK(3, 0) +#define RTW89_H2C_MRC_START_W0_OLD_SCH_IDX GENMASK(7, 4) +#define RTW89_H2C_MRC_START_W0_ACTION GENMASK(15, 8) + +struct rtw89_h2c_mrc_del { + __le32 w0; +} __packed; + +#define RTW89_H2C_MRC_DEL_W0_SCH_IDX GENMASK(3, 0) +#define RTW89_H2C_MRC_DEL_W0_DEL_ALL BIT(4) +#define RTW89_H2C_MRC_DEL_W0_STOP_ONLY BIT(5) +#define RTW89_H2C_MRC_DEL_W0_SPECIFIC_ROLE_EN BIT(6) +#define RTW89_H2C_MRC_DEL_W0_STOP_SLOT_IDX GENMASK(15, 8) +#define RTW89_H2C_MRC_DEL_W0_SPECIFIC_ROLE_MACID GENMASK(31, 16) + +struct rtw89_h2c_mrc_req_tsf { + u8 req_tsf_num; + u8 infos[] __counted_by(req_tsf_num); +} __packed; + +#define RTW89_H2C_MRC_REQ_TSF_INFO_BAND GENMASK(3, 0) +#define RTW89_H2C_MRC_REQ_TSF_INFO_PORT GENMASK(7, 4) + +enum rtw89_h2c_mrc_upd_bitmap_actions { + RTW89_H2C_MRC_UPD_BITMAP_ACTION_DEL = 0, + RTW89_H2C_MRC_UPD_BITMAP_ACTION_ADD = 1, +}; + +struct rtw89_h2c_mrc_upd_bitmap { + __le32 w0; + __le32 w1; +} __packed; + +#define RTW89_H2C_MRC_UPD_BITMAP_W0_SCH_IDX GENMASK(3, 0) +#define RTW89_H2C_MRC_UPD_BITMAP_W0_ACTION BIT(4) +#define RTW89_H2C_MRC_UPD_BITMAP_W0_MACID GENMASK(31, 16) +#define RTW89_H2C_MRC_UPD_BITMAP_W1_CLIENT_MACID GENMASK(15, 0) + +struct rtw89_h2c_mrc_sync { + __le32 w0; + __le32 w1; +} __packed; + +#define RTW89_H2C_MRC_SYNC_W0_SYNC_EN BIT(0) +#define RTW89_H2C_MRC_SYNC_W0_SRC_PORT GENMASK(11, 8) +#define RTW89_H2C_MRC_SYNC_W0_SRC_BAND GENMASK(15, 12) +#define RTW89_H2C_MRC_SYNC_W0_DEST_PORT GENMASK(19, 16) +#define RTW89_H2C_MRC_SYNC_W0_DEST_BAND GENMASK(23, 20) +#define RTW89_H2C_MRC_SYNC_W1_OFFSET GENMASK(15, 0) + +struct rtw89_h2c_mrc_upd_duration { + __le32 w0; + __le32 start_tsf_low; + __le32 start_tsf_high; + __le32 slots[]; +} __packed; + +#define RTW89_H2C_MRC_UPD_DURATION_W0_SCH_IDX GENMASK(3, 0) +#define RTW89_H2C_MRC_UPD_DURATION_W0_SLOT_NUM GENMASK(15, 8) +#define RTW89_H2C_MRC_UPD_DURATION_W0_BTC_IN_SCH BIT(16) +#define RTW89_H2C_MRC_UPD_DURATION_SLOT_SLOT_IDX GENMASK(7, 0) +#define RTW89_H2C_MRC_UPD_DURATION_SLOT_DURATION GENMASK(31, 16) + #define RTW89_C2H_HEADER_LEN 8 struct rtw89_c2h_hdr { @@ -3573,6 +3710,29 @@ static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE) #define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0)) +struct rtw89_c2h_mrc_tsf_rpt_info { + __le32 tsf_low; + __le32 tsf_high; +} __packed; + +struct rtw89_c2h_mrc_tsf_rpt { + struct rtw89_c2h_hdr hdr; + __le32 w2; + struct rtw89_c2h_mrc_tsf_rpt_info infos[]; +} __packed; + +#define RTW89_C2H_MRC_TSF_RPT_W2_REQ_TSF_NUM GENMASK(7, 0) + +struct rtw89_c2h_mrc_status_rpt { + struct rtw89_c2h_hdr hdr; + __le32 w2; + __le32 tsf_low; + __le32 tsf_high; +} __packed; + +#define RTW89_C2H_MRC_STATUS_RPT_W2_STATUS GENMASK(5, 0) +#define RTW89_C2H_MRC_STATUS_RPT_W2_SCH_IDX GENMASK(7, 6) + struct rtw89_c2h_pkt_ofld_rsp { __le32 w0; __le32 w1; @@ -3959,6 +4119,18 @@ enum rtw89_mcc_h2c_func { #define RTW89_MCC_WAIT_COND(group, func) \ ((group) * NUM_OF_RTW89_MCC_H2C_FUNC + (func)) +/* CLASS 24 - MRC */ +#define H2C_CL_MRC 0x18 +enum rtw89_mrc_h2c_func { + H2C_FUNC_MRC_REQ_TSF = 0x0, + H2C_FUNC_ADD_MRC = 0x1, + H2C_FUNC_START_MRC = 0x2, + H2C_FUNC_DEL_MRC = 0x3, + H2C_FUNC_MRC_SYNC = 0x4, + H2C_FUNC_MRC_UPD_DURATION = 0x5, + H2C_FUNC_MRC_UPD_BITMAP = 0x6, +}; + #define H2C_CAT_OUTSRC 0x2 #define H2C_CL_OUTSRC_RA 0x1 diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index c5ebac1d5990..12318d850475 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -406,13 +406,19 @@ enum rtw89_mac_c2h_mcc_func { NUM_OF_RTW89_MAC_C2H_FUNC_MCC, }; +enum rtw89_mac_c2h_mrc_func { + RTW89_MAC_C2H_FUNC_MRC_TSF_RPT = 0, + RTW89_MAC_C2H_FUNC_MRC_STATUS_RPT = 1, +}; + enum rtw89_mac_c2h_class { - RTW89_MAC_C2H_CLASS_INFO, - RTW89_MAC_C2H_CLASS_OFLD, - RTW89_MAC_C2H_CLASS_TWT, - RTW89_MAC_C2H_CLASS_WOW, - RTW89_MAC_C2H_CLASS_MCC, - RTW89_MAC_C2H_CLASS_FWDBG, + RTW89_MAC_C2H_CLASS_INFO = 0x0, + RTW89_MAC_C2H_CLASS_OFLD = 0x1, + RTW89_MAC_C2H_CLASS_TWT = 0x2, + RTW89_MAC_C2H_CLASS_WOW = 0x3, + RTW89_MAC_C2H_CLASS_MCC = 0x4, + RTW89_MAC_C2H_CLASS_FWDBG = 0x5, + RTW89_MAC_C2H_CLASS_MRC = 0xe, RTW89_MAC_C2H_CLASS_MAX, }; @@ -441,6 +447,12 @@ enum rtw89_mac_mcc_status { RTW89_MAC_MCC_TXNULL1_FAIL = 27, }; +enum rtw89_mac_mrc_status { + RTW89_MAC_MRC_START_SCH_OK = 0, + RTW89_MAC_MRC_STOP_SCH_OK = 1, + RTW89_MAC_MRC_DEL_SCH_OK = 2, +}; + struct rtw89_mac_ax_coex { #define RTW89_MAC_AX_COEX_RTK_MODE 0 #define RTW89_MAC_AX_COEX_CSR_MODE 1 From b8e59e55345878747b6de944e785728fb215b26c Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 13 Feb 2024 15:35:11 +0800 Subject: [PATCH 363/378] wifi: rtw89: mac: implement MRC C2H event handling Add handling of MRC (multiple role concurrent) C2H events including TSF report and status report. Parse report data and then complete the corresponding H2C commands, which will be implemented in the following. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240213073514.23796-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 +- drivers/net/wireless/realtek/rtw89/fw.h | 18 +++++ drivers/net/wireless/realtek/rtw89/mac.c | 91 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 2 + 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index e3913efedc28..6269ae2539b2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3918,7 +3918,7 @@ enum rtw89_host_rpr_mode { RTW89_RPR_MODE_STF }; -#define RTW89_COMPLETION_BUF_SIZE 24 +#define RTW89_COMPLETION_BUF_SIZE 40 #define RTW89_WAIT_COND_IDLE UINT_MAX struct rtw89_completion_data { diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 409f62cea0d7..d44df3897dd2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3474,6 +3474,8 @@ struct rtw89_h2c_mrc_del { #define RTW89_H2C_MRC_DEL_W0_STOP_SLOT_IDX GENMASK(15, 8) #define RTW89_H2C_MRC_DEL_W0_SPECIFIC_ROLE_MACID GENMASK(31, 16) +#define RTW89_MAC_MRC_MAX_REQ_TSF_NUM 2 + struct rtw89_h2c_mrc_req_tsf { u8 req_tsf_num; u8 infos[] __counted_by(req_tsf_num); @@ -3710,6 +3712,13 @@ static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE) #define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0)) +struct rtw89_mac_mrc_tsf_rpt { + unsigned int num; + u64 tsfs[RTW89_MAC_MRC_MAX_REQ_TSF_NUM]; +}; + +static_assert(sizeof(struct rtw89_mac_mrc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE); + struct rtw89_c2h_mrc_tsf_rpt_info { __le32 tsf_low; __le32 tsf_high; @@ -4129,8 +4138,17 @@ enum rtw89_mrc_h2c_func { H2C_FUNC_MRC_SYNC = 0x4, H2C_FUNC_MRC_UPD_DURATION = 0x5, H2C_FUNC_MRC_UPD_BITMAP = 0x6, + + NUM_OF_RTW89_MRC_H2C_FUNC, }; +/* can consider MRC's sch_idx as MCC's group */ +#define RTW89_MRC_WAIT_COND(sch_idx, func) \ + ((sch_idx) * NUM_OF_RTW89_MRC_H2C_FUNC + (func)) + +#define RTW89_MRC_WAIT_COND_REQ_TSF \ + RTW89_MRC_WAIT_COND(0 /* don't care */, H2C_FUNC_MRC_REQ_TSF) + #define H2C_CAT_OUTSRC 0x2 #define H2C_CL_OUTSRC_RA 0x1 diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 3ea50d49e12f..908245ac46bd 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5099,6 +5099,84 @@ rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data); } +static void +rtw89_mac_c2h_mrc_tsf_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + const struct rtw89_c2h_mrc_tsf_rpt *c2h_rpt; + struct rtw89_completion_data data = {}; + struct rtw89_mac_mrc_tsf_rpt *rpt; + unsigned int i; + + c2h_rpt = (const struct rtw89_c2h_mrc_tsf_rpt *)c2h->data; + rpt = (struct rtw89_mac_mrc_tsf_rpt *)data.buf; + rpt->num = min_t(u8, RTW89_MAC_MRC_MAX_REQ_TSF_NUM, + le32_get_bits(c2h_rpt->w2, + RTW89_C2H_MRC_TSF_RPT_W2_REQ_TSF_NUM)); + + for (i = 0; i < rpt->num; i++) { + u32 tsf_high = le32_to_cpu(c2h_rpt->infos[i].tsf_high); + u32 tsf_low = le32_to_cpu(c2h_rpt->infos[i].tsf_low); + + rpt->tsfs[i] = (u64)tsf_high << 32 | tsf_low; + + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC C2H TSF RPT: index %u> %llu\n", + i, rpt->tsfs[i]); + } + + rtw89_complete_cond(wait, RTW89_MRC_WAIT_COND_REQ_TSF, &data); +} + +static void +rtw89_mac_c2h_mrc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + const struct rtw89_c2h_mrc_status_rpt *c2h_rpt; + struct rtw89_completion_data data = {}; + enum rtw89_mac_mrc_status status; + unsigned int cond; + bool next = false; + u32 tsf_high; + u32 tsf_low; + u8 sch_idx; + u8 func; + + c2h_rpt = (const struct rtw89_c2h_mrc_status_rpt *)c2h->data; + sch_idx = le32_get_bits(c2h_rpt->w2, RTW89_C2H_MRC_STATUS_RPT_W2_SCH_IDX); + status = le32_get_bits(c2h_rpt->w2, RTW89_C2H_MRC_STATUS_RPT_W2_STATUS); + tsf_high = le32_to_cpu(c2h_rpt->tsf_high); + tsf_low = le32_to_cpu(c2h_rpt->tsf_low); + + switch (status) { + case RTW89_MAC_MRC_START_SCH_OK: + func = H2C_FUNC_START_MRC; + break; + case RTW89_MAC_MRC_STOP_SCH_OK: + /* H2C_FUNC_DEL_MRC without STOP_ONLY, so wait for DEL_SCH_OK */ + func = H2C_FUNC_DEL_MRC; + next = true; + break; + case RTW89_MAC_MRC_DEL_SCH_OK: + func = H2C_FUNC_DEL_MRC; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "invalid MRC C2H STS RPT: status %d\n", status); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC C2H STS RPT: sch_idx %d, status %d, tsf %llu\n", + sch_idx, status, (u64)tsf_high << 32 | tsf_low); + + if (next) + return; + + cond = RTW89_MRC_WAIT_COND(sch_idx, func); + rtw89_complete_cond(wait, cond, &data); +} + static void (* const rtw89_mac_c2h_ofld_handler[])(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) = { @@ -5130,6 +5208,13 @@ void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev, [RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT] = rtw89_mac_c2h_mcc_status_rpt, }; +static +void (* const rtw89_mac_c2h_mrc_handler[])(struct rtw89_dev *rtwdev, + struct sk_buff *c2h, u32 len) = { + [RTW89_MAC_C2H_FUNC_MRC_TSF_RPT] = rtw89_mac_c2h_mrc_tsf_rpt, + [RTW89_MAC_C2H_FUNC_MRC_STATUS_RPT] = rtw89_mac_c2h_mrc_status_rpt, +}; + static void rtw89_mac_c2h_scanofld_rsp_atomic(struct rtw89_dev *rtwdev, struct sk_buff *skb) { @@ -5180,6 +5265,8 @@ bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, struct sk_buff *c2h, } case RTW89_MAC_C2H_CLASS_MCC: return true; + case RTW89_MAC_C2H_CLASS_MRC: + return true; } } @@ -5202,6 +5289,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC) handler = rtw89_mac_c2h_mcc_handler[func]; break; + case RTW89_MAC_C2H_CLASS_MRC: + if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MRC) + handler = rtw89_mac_c2h_mrc_handler[func]; + break; case RTW89_MAC_C2H_CLASS_FWDBG: return; default: diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 12318d850475..db95509fad2f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -409,6 +409,8 @@ enum rtw89_mac_c2h_mcc_func { enum rtw89_mac_c2h_mrc_func { RTW89_MAC_C2H_FUNC_MRC_TSF_RPT = 0, RTW89_MAC_C2H_FUNC_MRC_STATUS_RPT = 1, + + NUM_OF_RTW89_MAC_C2H_FUNC_MRC, }; enum rtw89_mac_c2h_class { From 9de7829aa6fa78402f4efef60d919b1f8b54bbdb Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 13 Feb 2024 15:35:12 +0800 Subject: [PATCH 364/378] wifi: rtw89: fw: implement MRC H2C command functions Implement MRC (multiple role concurrent) H2C commands. Mainly deal with H2C format, LE type built from CPU value, default setting on some fields, and then sending the command to FW. Besides, MRC start, MRC delete, and MRC request TSF need to wait for a report from C2H events. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240213073514.23796-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 366 ++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 94 ++++++ 2 files changed, 460 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 540ea16f048e..2ab45d0878f7 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6669,6 +6669,372 @@ int rtw89_fw_h2c_mcc_set_duration(struct rtw89_dev *rtwdev, return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); } +static +u32 rtw89_fw_h2c_mrc_add_slot(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_add_slot_arg *slot_arg, + struct rtw89_h2c_mrc_add_slot *slot_h2c) +{ + bool fill_h2c = !!slot_h2c; + unsigned int i; + + if (!fill_h2c) + goto calc_len; + + slot_h2c->w0 = le32_encode_bits(slot_arg->duration, + RTW89_H2C_MRC_ADD_SLOT_W0_DURATION) | + le32_encode_bits(slot_arg->courtesy_en, + RTW89_H2C_MRC_ADD_SLOT_W0_COURTESY_EN) | + le32_encode_bits(slot_arg->role_num, + RTW89_H2C_MRC_ADD_SLOT_W0_ROLE_NUM); + slot_h2c->w1 = le32_encode_bits(slot_arg->courtesy_period, + RTW89_H2C_MRC_ADD_SLOT_W1_COURTESY_PERIOD) | + le32_encode_bits(slot_arg->courtesy_target, + RTW89_H2C_MRC_ADD_SLOT_W1_COURTESY_TARGET); + + for (i = 0; i < slot_arg->role_num; i++) { + slot_h2c->roles[i].w0 = + le32_encode_bits(slot_arg->roles[i].macid, + RTW89_H2C_MRC_ADD_ROLE_W0_MACID) | + le32_encode_bits(slot_arg->roles[i].role_type, + RTW89_H2C_MRC_ADD_ROLE_W0_ROLE_TYPE) | + le32_encode_bits(slot_arg->roles[i].is_master, + RTW89_H2C_MRC_ADD_ROLE_W0_IS_MASTER) | + le32_encode_bits(slot_arg->roles[i].en_tx_null, + RTW89_H2C_MRC_ADD_ROLE_W0_TX_NULL_EN) | + le32_encode_bits(false, + RTW89_H2C_MRC_ADD_ROLE_W0_IS_ALT_ROLE) | + le32_encode_bits(false, + RTW89_H2C_MRC_ADD_ROLE_W0_ROLE_ALT_EN); + slot_h2c->roles[i].w1 = + le32_encode_bits(slot_arg->roles[i].central_ch, + RTW89_H2C_MRC_ADD_ROLE_W1_CENTRAL_CH_SEG) | + le32_encode_bits(slot_arg->roles[i].primary_ch, + RTW89_H2C_MRC_ADD_ROLE_W1_PRI_CH) | + le32_encode_bits(slot_arg->roles[i].bw, + RTW89_H2C_MRC_ADD_ROLE_W1_BW) | + le32_encode_bits(slot_arg->roles[i].band, + RTW89_H2C_MRC_ADD_ROLE_W1_CH_BAND_TYPE) | + le32_encode_bits(slot_arg->roles[i].null_early, + RTW89_H2C_MRC_ADD_ROLE_W1_NULL_EARLY) | + le32_encode_bits(false, + RTW89_H2C_MRC_ADD_ROLE_W1_RFK_BY_PASS) | + le32_encode_bits(true, + RTW89_H2C_MRC_ADD_ROLE_W1_CAN_BTC); + slot_h2c->roles[i].macid_main_bitmap = + cpu_to_le32(slot_arg->roles[i].macid_main_bitmap); + slot_h2c->roles[i].macid_paired_bitmap = + cpu_to_le32(slot_arg->roles[i].macid_paired_bitmap); + } + +calc_len: + return struct_size(slot_h2c, roles, slot_arg->role_num); +} + +int rtw89_fw_h2c_mrc_add(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_add_arg *arg) +{ + struct rtw89_h2c_mrc_add *h2c_head; + struct sk_buff *skb; + unsigned int i; + void *tmp; + u32 len; + int ret; + + len = sizeof(*h2c_head); + for (i = 0; i < arg->slot_num; i++) + len += rtw89_fw_h2c_mrc_add_slot(rtwdev, &arg->slots[i], NULL); + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for mrc add\n"); + return -ENOMEM; + } + + skb_put(skb, len); + tmp = skb->data; + + h2c_head = tmp; + h2c_head->w0 = le32_encode_bits(arg->sch_idx, + RTW89_H2C_MRC_ADD_W0_SCH_IDX) | + le32_encode_bits(arg->sch_type, + RTW89_H2C_MRC_ADD_W0_SCH_TYPE) | + le32_encode_bits(arg->slot_num, + RTW89_H2C_MRC_ADD_W0_SLOT_NUM) | + le32_encode_bits(arg->btc_in_sch, + RTW89_H2C_MRC_ADD_W0_BTC_IN_SCH); + + tmp += sizeof(*h2c_head); + for (i = 0; i < arg->slot_num; i++) + tmp += rtw89_fw_h2c_mrc_add_slot(rtwdev, &arg->slots[i], tmp); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MRC, + H2C_FUNC_ADD_MRC, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + dev_kfree_skb_any(skb); + return -EBUSY; + } + + return 0; +} + +int rtw89_fw_h2c_mrc_start(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_start_arg *arg) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct rtw89_h2c_mrc_start *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for mrc start\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_mrc_start *)skb->data; + + h2c->w0 = le32_encode_bits(arg->sch_idx, + RTW89_H2C_MRC_START_W0_SCH_IDX) | + le32_encode_bits(arg->old_sch_idx, + RTW89_H2C_MRC_START_W0_OLD_SCH_IDX) | + le32_encode_bits(arg->action, + RTW89_H2C_MRC_START_W0_ACTION); + + h2c->start_tsf_high = cpu_to_le32(arg->start_tsf >> 32); + h2c->start_tsf_low = cpu_to_le32(arg->start_tsf); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MRC, + H2C_FUNC_START_MRC, 0, 0, + len); + + cond = RTW89_MRC_WAIT_COND(arg->sch_idx, H2C_FUNC_START_MRC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +int rtw89_fw_h2c_mrc_del(struct rtw89_dev *rtwdev, u8 sch_idx) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct rtw89_h2c_mrc_del *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for mrc del\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_mrc_del *)skb->data; + + h2c->w0 = le32_encode_bits(sch_idx, RTW89_H2C_MRC_DEL_W0_SCH_IDX); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MRC, + H2C_FUNC_DEL_MRC, 0, 0, + len); + + cond = RTW89_MRC_WAIT_COND(sch_idx, H2C_FUNC_DEL_MRC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +int rtw89_fw_h2c_mrc_req_tsf(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_req_tsf_arg *arg, + struct rtw89_mac_mrc_tsf_rpt *rpt) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct rtw89_h2c_mrc_req_tsf *h2c; + struct rtw89_mac_mrc_tsf_rpt *tmp; + struct sk_buff *skb; + unsigned int i; + u32 len; + int ret; + + len = struct_size(h2c, infos, arg->num); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for mrc req tsf\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_mrc_req_tsf *)skb->data; + + h2c->req_tsf_num = arg->num; + for (i = 0; i < arg->num; i++) + h2c->infos[i] = + u8_encode_bits(arg->infos[i].band, + RTW89_H2C_MRC_REQ_TSF_INFO_BAND) | + u8_encode_bits(arg->infos[i].port, + RTW89_H2C_MRC_REQ_TSF_INFO_PORT); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MRC, + H2C_FUNC_MRC_REQ_TSF, 0, 0, + len); + + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, RTW89_MRC_WAIT_COND_REQ_TSF); + if (ret) + return ret; + + tmp = (struct rtw89_mac_mrc_tsf_rpt *)wait->data.buf; + *rpt = *tmp; + + return 0; +} + +int rtw89_fw_h2c_mrc_upd_bitmap(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_upd_bitmap_arg *arg) +{ + struct rtw89_h2c_mrc_upd_bitmap *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for mrc upd bitmap\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_mrc_upd_bitmap *)skb->data; + + h2c->w0 = le32_encode_bits(arg->sch_idx, + RTW89_H2C_MRC_UPD_BITMAP_W0_SCH_IDX) | + le32_encode_bits(arg->action, + RTW89_H2C_MRC_UPD_BITMAP_W0_ACTION) | + le32_encode_bits(arg->macid, + RTW89_H2C_MRC_UPD_BITMAP_W0_MACID); + h2c->w1 = le32_encode_bits(arg->client_macid, + RTW89_H2C_MRC_UPD_BITMAP_W1_CLIENT_MACID); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MRC, + H2C_FUNC_MRC_UPD_BITMAP, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + dev_kfree_skb_any(skb); + return -EBUSY; + } + + return 0; +} + +int rtw89_fw_h2c_mrc_sync(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_sync_arg *arg) +{ + struct rtw89_h2c_mrc_sync *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for mrc sync\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_mrc_sync *)skb->data; + + h2c->w0 = le32_encode_bits(true, RTW89_H2C_MRC_SYNC_W0_SYNC_EN) | + le32_encode_bits(arg->src.port, + RTW89_H2C_MRC_SYNC_W0_SRC_PORT) | + le32_encode_bits(arg->src.band, + RTW89_H2C_MRC_SYNC_W0_SRC_BAND) | + le32_encode_bits(arg->dest.port, + RTW89_H2C_MRC_SYNC_W0_DEST_PORT) | + le32_encode_bits(arg->dest.band, + RTW89_H2C_MRC_SYNC_W0_DEST_BAND); + h2c->w1 = le32_encode_bits(arg->offset, RTW89_H2C_MRC_SYNC_W1_OFFSET); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MRC, + H2C_FUNC_MRC_SYNC, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + dev_kfree_skb_any(skb); + return -EBUSY; + } + + return 0; +} + +int rtw89_fw_h2c_mrc_upd_duration(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_upd_duration_arg *arg) +{ + struct rtw89_h2c_mrc_upd_duration *h2c; + struct sk_buff *skb; + unsigned int i; + u32 len; + int ret; + + len = struct_size(h2c, slots, arg->slot_num); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for mrc upd duration\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_mrc_upd_duration *)skb->data; + + h2c->w0 = le32_encode_bits(arg->sch_idx, + RTW89_H2C_MRC_UPD_DURATION_W0_SCH_IDX) | + le32_encode_bits(arg->slot_num, + RTW89_H2C_MRC_UPD_DURATION_W0_SLOT_NUM) | + le32_encode_bits(false, + RTW89_H2C_MRC_UPD_DURATION_W0_BTC_IN_SCH); + + h2c->start_tsf_high = cpu_to_le32(arg->start_tsf >> 32); + h2c->start_tsf_low = cpu_to_le32(arg->start_tsf); + + for (i = 0; i < arg->slot_num; i++) { + h2c->slots[i] = + le32_encode_bits(arg->slots[i].slot_idx, + RTW89_H2C_MRC_UPD_DURATION_SLOT_SLOT_IDX) | + le32_encode_bits(arg->slots[i].duration, + RTW89_H2C_MRC_UPD_DURATION_SLOT_DURATION); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MRC, + H2C_FUNC_MRC_UPD_DURATION, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + dev_kfree_skb_any(skb); + return -EBUSY; + } + + return 0; +} + static bool __fw_txpwr_entry_zero_ext(const void *ext_ptr, u8 ext_len) { static const u8 zeros[U8_MAX] = {}; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index d44df3897dd2..9c5464dcc081 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3397,6 +3397,45 @@ enum rtw89_h2c_mrc_role_types { RTW89_H2C_MRC_ROLE_EMPTY = 2, }; +#define RTW89_MAC_MRC_MAX_ADD_SLOT_NUM 3 +#define RTW89_MAC_MRC_MAX_ADD_ROLE_NUM_PER_SLOT 1 /* before MLO */ + +struct rtw89_fw_mrc_add_slot_arg { + u16 duration; /* unit: TU */ + bool courtesy_en; + u8 courtesy_period; + u8 courtesy_target; /* slot idx */ + + unsigned int role_num; + struct { + enum rtw89_h2c_mrc_role_types role_type; + bool is_master; + bool en_tx_null; + enum rtw89_band band; + enum rtw89_bandwidth bw; + u8 macid; + u8 central_ch; + u8 primary_ch; + u8 null_early; /* unit: TU */ + + /* if MLD, for macid: [0, chip::support_mld_num) + * otherwise, for macid: [0, 32) + */ + u32 macid_main_bitmap; + /* for MLD, bit X maps to macid: X + chip::support_mld_num */ + u32 macid_paired_bitmap; + } roles[RTW89_MAC_MRC_MAX_ADD_ROLE_NUM_PER_SLOT]; +}; + +struct rtw89_fw_mrc_add_arg { + u8 sch_idx; + enum rtw89_h2c_mrc_sch_types sch_type; + bool btc_in_sch; + + unsigned int slot_num; + struct rtw89_fw_mrc_add_slot_arg slots[RTW89_MAC_MRC_MAX_ADD_SLOT_NUM]; +}; + struct rtw89_h2c_mrc_add_role { __le32 w0; __le32 w1; @@ -3453,6 +3492,13 @@ enum rtw89_h2c_mrc_start_actions { RTW89_H2C_MRC_START_ACTION_REPLACE_OLD = 1, }; +struct rtw89_fw_mrc_start_arg { + u8 sch_idx; + u8 old_sch_idx; + u64 start_tsf; + enum rtw89_h2c_mrc_start_actions action; +}; + struct rtw89_h2c_mrc_start { __le32 w0; __le32 start_tsf_low; @@ -3476,6 +3522,14 @@ struct rtw89_h2c_mrc_del { #define RTW89_MAC_MRC_MAX_REQ_TSF_NUM 2 +struct rtw89_fw_mrc_req_tsf_arg { + unsigned int num; + struct { + u8 band; + u8 port; + } infos[RTW89_MAC_MRC_MAX_REQ_TSF_NUM]; +}; + struct rtw89_h2c_mrc_req_tsf { u8 req_tsf_num; u8 infos[] __counted_by(req_tsf_num); @@ -3489,6 +3543,13 @@ enum rtw89_h2c_mrc_upd_bitmap_actions { RTW89_H2C_MRC_UPD_BITMAP_ACTION_ADD = 1, }; +struct rtw89_fw_mrc_upd_bitmap_arg { + u8 sch_idx; + u8 macid; + u8 client_macid; + enum rtw89_h2c_mrc_upd_bitmap_actions action; +}; + struct rtw89_h2c_mrc_upd_bitmap { __le32 w0; __le32 w1; @@ -3499,6 +3560,14 @@ struct rtw89_h2c_mrc_upd_bitmap { #define RTW89_H2C_MRC_UPD_BITMAP_W0_MACID GENMASK(31, 16) #define RTW89_H2C_MRC_UPD_BITMAP_W1_CLIENT_MACID GENMASK(15, 0) +struct rtw89_fw_mrc_sync_arg { + u8 offset; /* unit: TU */ + struct { + u8 band; + u8 port; + } src, dest; +}; + struct rtw89_h2c_mrc_sync { __le32 w0; __le32 w1; @@ -3511,6 +3580,17 @@ struct rtw89_h2c_mrc_sync { #define RTW89_H2C_MRC_SYNC_W0_DEST_BAND GENMASK(23, 20) #define RTW89_H2C_MRC_SYNC_W1_OFFSET GENMASK(15, 0) +struct rtw89_fw_mrc_upd_duration_arg { + u8 sch_idx; + u64 start_tsf; + + unsigned int slot_num; + struct { + u8 slot_idx; + u16 duration; /* unit: TU */ + } slots[RTW89_MAC_MRC_MAX_ADD_SLOT_NUM]; +}; + struct rtw89_h2c_mrc_upd_duration { __le32 w0; __le32 start_tsf_low; @@ -4565,6 +4645,20 @@ int rtw89_fw_h2c_mcc_sync(struct rtw89_dev *rtwdev, u8 group, u8 source, u8 target, u8 offset); int rtw89_fw_h2c_mcc_set_duration(struct rtw89_dev *rtwdev, const struct rtw89_fw_mcc_duration *p); +int rtw89_fw_h2c_mrc_add(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_add_arg *arg); +int rtw89_fw_h2c_mrc_start(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_start_arg *arg); +int rtw89_fw_h2c_mrc_del(struct rtw89_dev *rtwdev, u8 sch_idx); +int rtw89_fw_h2c_mrc_req_tsf(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_req_tsf_arg *arg, + struct rtw89_mac_mrc_tsf_rpt *rpt); +int rtw89_fw_h2c_mrc_upd_bitmap(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_upd_bitmap_arg *arg); +int rtw89_fw_h2c_mrc_sync(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_sync_arg *arg); +int rtw89_fw_h2c_mrc_upd_duration(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mrc_upd_duration_arg *arg); static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev) { From f931cce310e0beb2d4b9f2b60b3e3ce69ff87dfb Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 13 Feb 2024 15:35:13 +0800 Subject: [PATCH 365/378] wifi: rtw89: chan: support MCC on Wi-Fi 7 chips On Wi-Fi 7 chips, concurrent stuffs are supported by FW MRC series (multi-role concurrent) functions. And, driver has implemented the corresponding SW handling in patches in front of this one. Now, we extend SW MCC (multi-channel concurrent) flow to work on Wi-Fi 7 chips. In SW point of view, things look as below. | SW | | FW func | | | | H2C/C2H | -------------------------------------------- | | ax | | | /----| FW MCC func | | MCC | -- chip --+ | | | \----| FW MRC func | | | be | Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240213073514.23796-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 440 ++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 7 + 2 files changed, 420 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 7b9baf4db70f..051a3cad6101 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -322,6 +322,13 @@ static void rtw89_chanctx_notify(struct rtw89_dev *rtwdev, } } +static bool rtw89_concurrent_via_mrc(struct rtw89_dev *rtwdev) +{ + enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; + + return chip_gen == RTW89_CHIP_BE; +} + /* This function centrally manages how MCC roles are sorted and iterated. * And, it guarantees that ordered_idx is less than NUM_OF_RTW89_MCC_ROLES. * So, if data needs to pass an array for ordered_idx, the array can declare @@ -374,16 +381,13 @@ static u32 rtw89_mcc_get_tbtt_ofst(struct rtw89_dev *rtwdev, return remainder; } -static u16 rtw89_mcc_get_bcn_ofst(struct rtw89_dev *rtwdev) +static int __mcc_fw_req_tsf(struct rtw89_dev *rtwdev, u64 *tsf_ref, u64 *tsf_aux) { struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_role *ref = &mcc->role_ref; struct rtw89_mcc_role *aux = &mcc->role_aux; struct rtw89_mac_mcc_tsf_rpt rpt = {}; struct rtw89_fw_mcc_tsf_req req = {}; - u32 bcn_intvl_ref_us = ieee80211_tu_to_usec(ref->beacon_interval); - u32 tbtt_ofst_ref, tbtt_ofst_aux; - u64 tsf_ref, tsf_aux; int ret; req.group = mcc->group; @@ -393,11 +397,63 @@ static u16 rtw89_mcc_get_bcn_ofst(struct rtw89_dev *rtwdev) if (ret) { rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC h2c failed to request tsf: %d\n", ret); - return RTW89_MCC_DFLT_BCN_OFST_TIME; + return ret; } - tsf_ref = (u64)rpt.tsf_x_high << 32 | rpt.tsf_x_low; - tsf_aux = (u64)rpt.tsf_y_high << 32 | rpt.tsf_y_low; + *tsf_ref = (u64)rpt.tsf_x_high << 32 | rpt.tsf_x_low; + *tsf_aux = (u64)rpt.tsf_y_high << 32 | rpt.tsf_y_low; + + return 0; +} + +static int __mrc_fw_req_tsf(struct rtw89_dev *rtwdev, u64 *tsf_ref, u64 *tsf_aux) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_fw_mrc_req_tsf_arg arg = {}; + struct rtw89_mac_mrc_tsf_rpt rpt = {}; + int ret; + + BUILD_BUG_ON(RTW89_MAC_MRC_MAX_REQ_TSF_NUM < NUM_OF_RTW89_MCC_ROLES); + + arg.num = 2; + arg.infos[0].band = ref->rtwvif->mac_idx; + arg.infos[0].port = ref->rtwvif->port; + arg.infos[1].band = aux->rtwvif->mac_idx; + arg.infos[1].port = aux->rtwvif->port; + + ret = rtw89_fw_h2c_mrc_req_tsf(rtwdev, &arg, &rpt); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC h2c failed to request tsf: %d\n", ret); + return ret; + } + + *tsf_ref = rpt.tsfs[0]; + *tsf_aux = rpt.tsfs[1]; + + return 0; +} + +static u16 rtw89_mcc_get_bcn_ofst(struct rtw89_dev *rtwdev) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + u32 bcn_intvl_ref_us = ieee80211_tu_to_usec(ref->beacon_interval); + u32 tbtt_ofst_ref, tbtt_ofst_aux; + u64 tsf_ref, tsf_aux; + int ret; + + if (rtw89_concurrent_via_mrc(rtwdev)) + ret = __mrc_fw_req_tsf(rtwdev, &tsf_ref, &tsf_aux); + else + ret = __mcc_fw_req_tsf(rtwdev, &tsf_ref, &tsf_aux); + + if (ret) + return RTW89_MCC_DFLT_BCN_OFST_TIME; + tbtt_ofst_ref = rtw89_mcc_get_tbtt_ofst(rtwdev, ref, tsf_ref); tbtt_ofst_aux = rtw89_mcc_get_tbtt_ofst(rtwdev, aux, tsf_aux); @@ -420,6 +476,28 @@ void rtw89_mcc_role_fw_macid_bitmap_set_bit(struct rtw89_mcc_role *mcc_role, mcc_role->macid_bitmap[idx] |= BIT(pos); } +static +u32 rtw89_mcc_role_fw_macid_bitmap_to_u32(struct rtw89_mcc_role *mcc_role) +{ + unsigned int macid; + unsigned int i, j; + u32 bitmap = 0; + + for (i = 0; i < ARRAY_SIZE(mcc_role->macid_bitmap); i++) { + for (j = 0; j < 8; j++) { + macid = i * 8 + j; + if (macid >= 32) + goto out; + + if (mcc_role->macid_bitmap[i] & BIT(j)) + bitmap |= BIT(macid); + } + } + +out: + return bitmap; +} + static void rtw89_mcc_role_macid_sta_iter(void *data, struct ieee80211_sta *sta) { struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; @@ -1181,7 +1259,11 @@ static void rtw89_mcc_sync_tbtt(struct rtw89_dev *rtwdev, tsf_ofst_tgt = bcn_intvl_src_us - remainder; config->sync.macid_tgt = tgt->rtwvif->mac_id; + config->sync.band_tgt = tgt->rtwvif->mac_idx; + config->sync.port_tgt = tgt->rtwvif->port; config->sync.macid_src = src->rtwvif->mac_id; + config->sync.band_src = src->rtwvif->mac_idx; + config->sync.port_src = src->rtwvif->port; config->sync.offset = tsf_ofst_tgt / 1024; config->sync.enable = true; @@ -1328,6 +1410,37 @@ static int __mcc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *ro return 0; } +static +void __mrc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role, + struct rtw89_fw_mrc_add_arg *arg, u8 slot_idx) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_policy *policy = &role->policy; + struct rtw89_fw_mrc_add_slot_arg *slot_arg; + const struct rtw89_chan *chan; + + slot_arg = &arg->slots[slot_idx]; + role->slot_idx = slot_idx; + + slot_arg->duration = role->duration; + slot_arg->role_num = 1; + + chan = rtw89_chan_get(rtwdev, role->rtwvif->sub_entity_idx); + + slot_arg->roles[0].role_type = RTW89_H2C_MRC_ROLE_WIFI; + slot_arg->roles[0].is_master = role == ref; + slot_arg->roles[0].band = chan->band_type; + slot_arg->roles[0].bw = chan->band_width; + slot_arg->roles[0].central_ch = chan->channel; + slot_arg->roles[0].primary_ch = chan->primary_channel; + slot_arg->roles[0].en_tx_null = !policy->dis_tx_null; + slot_arg->roles[0].null_early = policy->tx_null_early; + slot_arg->roles[0].macid = role->rtwvif->mac_id; + slot_arg->roles[0].macid_main_bitmap = + rtw89_mcc_role_fw_macid_bitmap_to_u32(role); +} + static int __mcc_fw_add_bt_role(struct rtw89_dev *rtwdev) { struct rtw89_mcc_info *mcc = &rtwdev->mcc; @@ -1349,6 +1462,20 @@ static int __mcc_fw_add_bt_role(struct rtw89_dev *rtwdev) return 0; } +static +void __mrc_fw_add_bt_role(struct rtw89_dev *rtwdev, + struct rtw89_fw_mrc_add_arg *arg, u8 slot_idx) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_bt_role *bt_role = &mcc->bt_role; + struct rtw89_fw_mrc_add_slot_arg *slot_arg = &arg->slots[slot_idx]; + + slot_arg->duration = bt_role->duration; + slot_arg->role_num = 1; + + slot_arg->roles[0].role_type = RTW89_H2C_MRC_ROLE_BT; +} + static int __mcc_fw_start(struct rtw89_dev *rtwdev, bool replace) { struct rtw89_mcc_info *mcc = &rtwdev->mcc; @@ -1434,6 +1561,130 @@ static int __mcc_fw_start(struct rtw89_dev *rtwdev, bool replace) return 0; } +static void __mrc_fw_add_courtesy(struct rtw89_dev *rtwdev, + struct rtw89_fw_mrc_add_arg *arg) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_mcc_config *config = &mcc->config; + struct rtw89_mcc_pattern *pattern = &config->pattern; + struct rtw89_mcc_courtesy *courtesy = &pattern->courtesy; + struct rtw89_fw_mrc_add_slot_arg *slot_arg_src; + u8 slot_idx_tgt; + + if (!courtesy->enable) + return; + + if (courtesy->macid_src == ref->rtwvif->mac_id) { + slot_arg_src = &arg->slots[ref->slot_idx]; + slot_idx_tgt = aux->slot_idx; + } else { + slot_arg_src = &arg->slots[aux->slot_idx]; + slot_idx_tgt = ref->slot_idx; + } + + slot_arg_src->courtesy_target = slot_idx_tgt; + slot_arg_src->courtesy_period = courtesy->slot_num; + slot_arg_src->courtesy_en = true; +} + +static int __mrc_fw_start(struct rtw89_dev *rtwdev, bool replace) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_mcc_config *config = &mcc->config; + struct rtw89_mcc_pattern *pattern = &config->pattern; + struct rtw89_mcc_sync *sync = &config->sync; + struct rtw89_fw_mrc_start_arg start_arg = {}; + struct rtw89_fw_mrc_add_arg add_arg = {}; + int ret; + + BUILD_BUG_ON(RTW89_MAC_MRC_MAX_ADD_SLOT_NUM < + NUM_OF_RTW89_MCC_ROLES + 1 /* bt role */); + + if (replace) { + start_arg.old_sch_idx = mcc->group; + start_arg.action = RTW89_H2C_MRC_START_ACTION_REPLACE_OLD; + mcc->group = RTW89_MCC_NEXT_GROUP(mcc->group); + } + + add_arg.sch_idx = mcc->group; + add_arg.sch_type = RTW89_H2C_MRC_SCH_BAND0_ONLY; + + switch (pattern->plan) { + case RTW89_MCC_PLAN_TAIL_BT: + __mrc_fw_add_role(rtwdev, ref, &add_arg, 0); + __mrc_fw_add_role(rtwdev, aux, &add_arg, 1); + __mrc_fw_add_bt_role(rtwdev, &add_arg, 2); + + add_arg.slot_num = 3; + add_arg.btc_in_sch = true; + break; + case RTW89_MCC_PLAN_MID_BT: + __mrc_fw_add_role(rtwdev, ref, &add_arg, 0); + __mrc_fw_add_bt_role(rtwdev, &add_arg, 1); + __mrc_fw_add_role(rtwdev, aux, &add_arg, 2); + + add_arg.slot_num = 3; + add_arg.btc_in_sch = true; + break; + case RTW89_MCC_PLAN_NO_BT: + __mrc_fw_add_role(rtwdev, ref, &add_arg, 0); + __mrc_fw_add_role(rtwdev, aux, &add_arg, 1); + + add_arg.slot_num = 2; + add_arg.btc_in_sch = false; + break; + default: + rtw89_warn(rtwdev, "MCC unknown plan: %d\n", pattern->plan); + return -EFAULT; + } + + __mrc_fw_add_courtesy(rtwdev, &add_arg); + + ret = rtw89_fw_h2c_mrc_add(rtwdev, &add_arg); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC h2c failed to trigger add: %d\n", ret); + return ret; + } + + if (sync->enable) { + struct rtw89_fw_mrc_sync_arg sync_arg = { + .offset = sync->offset, + .src = { + .band = sync->band_src, + .port = sync->port_src, + }, + .dest = { + .band = sync->band_tgt, + .port = sync->port_tgt, + }, + }; + + ret = rtw89_fw_h2c_mrc_sync(rtwdev, &sync_arg); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC h2c failed to trigger sync: %d\n", ret); + return ret; + } + } + + start_arg.sch_idx = mcc->group; + start_arg.start_tsf = config->start_tsf; + + ret = rtw89_fw_h2c_mrc_start(rtwdev, &start_arg); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC h2c failed to trigger start: %d\n", ret); + return ret; + } + + return 0; +} + static int __mcc_fw_set_duration_no_bt(struct rtw89_dev *rtwdev, bool sync_changed) { struct rtw89_mcc_info *mcc = &rtwdev->mcc; @@ -1475,6 +1726,60 @@ static int __mcc_fw_set_duration_no_bt(struct rtw89_dev *rtwdev, bool sync_chang return 0; } +static int __mrc_fw_set_duration_no_bt(struct rtw89_dev *rtwdev, bool sync_changed) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_config *config = &mcc->config; + struct rtw89_mcc_sync *sync = &config->sync; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_fw_mrc_upd_duration_arg dur_arg = { + .sch_idx = mcc->group, + .start_tsf = config->start_tsf, + .slot_num = 2, + .slots[0] = { + .slot_idx = ref->slot_idx, + .duration = ref->duration, + }, + .slots[1] = { + .slot_idx = aux->slot_idx, + .duration = aux->duration, + }, + }; + struct rtw89_fw_mrc_sync_arg sync_arg = { + .offset = sync->offset, + .src = { + .band = sync->band_src, + .port = sync->port_src, + }, + .dest = { + .band = sync->band_tgt, + .port = sync->port_tgt, + }, + + }; + int ret; + + ret = rtw89_fw_h2c_mrc_upd_duration(rtwdev, &dur_arg); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC h2c failed to update duration: %d\n", ret); + return ret; + } + + if (!sync->enable || !sync_changed) + return 0; + + ret = rtw89_fw_h2c_mrc_sync(rtwdev, &sync_arg); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC h2c failed to trigger sync: %d\n", ret); + return ret; + } + + return 0; +} + static void rtw89_mcc_handle_beacon_noa(struct rtw89_dev *rtwdev, bool enable) { struct rtw89_mcc_info *mcc = &rtwdev->mcc; @@ -1593,7 +1898,11 @@ static int rtw89_mcc_start(struct rtw89_dev *rtwdev) if (ret) return ret; - ret = __mcc_fw_start(rtwdev, false); + if (rtw89_concurrent_via_mrc(rtwdev)) + ret = __mrc_fw_start(rtwdev, false); + else + ret = __mcc_fw_start(rtwdev, false); + if (ret) return ret; @@ -1611,16 +1920,23 @@ static void rtw89_mcc_stop(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC stop\n"); - ret = rtw89_fw_h2c_stop_mcc(rtwdev, mcc->group, - ref->rtwvif->mac_id, true); - if (ret) - rtw89_debug(rtwdev, RTW89_DBG_CHAN, - "MCC h2c failed to trigger stop: %d\n", ret); + if (rtw89_concurrent_via_mrc(rtwdev)) { + ret = rtw89_fw_h2c_mrc_del(rtwdev, mcc->group); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC h2c failed to trigger del: %d\n", ret); + } else { + ret = rtw89_fw_h2c_stop_mcc(rtwdev, mcc->group, + ref->rtwvif->mac_id, true); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MCC h2c failed to trigger stop: %d\n", ret); - ret = rtw89_fw_h2c_del_mcc_group(rtwdev, mcc->group, true); - if (ret) - rtw89_debug(rtwdev, RTW89_DBG_CHAN, - "MCC h2c failed to delete group: %d\n", ret); + ret = rtw89_fw_h2c_del_mcc_group(rtwdev, mcc->group, true); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MCC h2c failed to delete group: %d\n", ret); + } rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_STOP); @@ -1646,7 +1962,11 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev) if (old_cfg.pattern.plan != RTW89_MCC_PLAN_NO_BT || config->pattern.plan != RTW89_MCC_PLAN_NO_BT) { - ret = __mcc_fw_start(rtwdev, true); + if (rtw89_concurrent_via_mrc(rtwdev)) + ret = __mrc_fw_start(rtwdev, true); + else + ret = __mcc_fw_start(rtwdev, true); + if (ret) return ret; } else { @@ -1655,7 +1975,11 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev) else sync_changed = true; - ret = __mcc_fw_set_duration_no_bt(rtwdev, sync_changed); + if (rtw89_concurrent_via_mrc(rtwdev)) + ret = __mrc_fw_set_duration_no_bt(rtwdev, sync_changed); + else + ret = __mcc_fw_set_duration_no_bt(rtwdev, sync_changed); + if (ret) return ret; } @@ -1697,12 +2021,75 @@ static void rtw89_mcc_track(struct rtw89_dev *rtwdev) rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_BCN_OFFSET_CHANGE); } +static int __mcc_fw_upd_macid_bitmap(struct rtw89_dev *rtwdev, + struct rtw89_mcc_role *upd) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + int ret; + + ret = rtw89_fw_h2c_mcc_macid_bitmap(rtwdev, mcc->group, + upd->rtwvif->mac_id, + upd->macid_bitmap); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MCC h2c failed to update macid bitmap: %d\n", ret); + return ret; + } + + return 0; +} + +static int __mrc_fw_upd_macid_bitmap(struct rtw89_dev *rtwdev, + struct rtw89_mcc_role *cur, + struct rtw89_mcc_role *upd) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_fw_mrc_upd_bitmap_arg arg = {}; + u32 old = rtw89_mcc_role_fw_macid_bitmap_to_u32(cur); + u32 new = rtw89_mcc_role_fw_macid_bitmap_to_u32(upd); + u32 add = new & ~old; + u32 del = old & ~new; + int ret; + int i; + + arg.sch_idx = mcc->group; + arg.macid = upd->rtwvif->mac_id; + + for (i = 0; i < 32; i++) { + if (add & BIT(i)) { + arg.client_macid = i; + arg.action = RTW89_H2C_MRC_UPD_BITMAP_ACTION_ADD; + + ret = rtw89_fw_h2c_mrc_upd_bitmap(rtwdev, &arg); + if (ret) + goto err; + } + } + + for (i = 0; i < 32; i++) { + if (del & BIT(i)) { + arg.client_macid = i; + arg.action = RTW89_H2C_MRC_UPD_BITMAP_ACTION_DEL; + + ret = rtw89_fw_h2c_mrc_upd_bitmap(rtwdev, &arg); + if (ret) + goto err; + } + } + + return 0; + +err: + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MRC h2c failed to update bitmap: %d\n", ret); + return ret; +} + static int rtw89_mcc_upd_map_iterator(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *mcc_role, unsigned int ordered_idx, void *data) { - struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_role upd = { .rtwvif = mcc_role->rtwvif, }; @@ -1716,14 +2103,13 @@ static int rtw89_mcc_upd_map_iterator(struct rtw89_dev *rtwdev, sizeof(mcc_role->macid_bitmap)) == 0) return 0; - ret = rtw89_fw_h2c_mcc_macid_bitmap(rtwdev, mcc->group, - upd.rtwvif->mac_id, - upd.macid_bitmap); - if (ret) { - rtw89_debug(rtwdev, RTW89_DBG_CHAN, - "MCC h2c failed to update macid bitmap: %d\n", ret); + if (rtw89_concurrent_via_mrc(rtwdev)) + ret = __mrc_fw_upd_macid_bitmap(rtwdev, mcc_role, &upd); + else + ret = __mcc_fw_upd_macid_bitmap(rtwdev, &upd); + + if (ret) return ret; - } memcpy(mcc_role->macid_bitmap, upd.macid_bitmap, sizeof(mcc_role->macid_bitmap)); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 6269ae2539b2..d62d23015c48 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4868,6 +4868,9 @@ struct rtw89_mcc_role { struct rtw89_mcc_policy policy; struct rtw89_mcc_limit limit; + /* only valid when running with FW MRC mechanism */ + u8 slot_idx; + /* byte-array in LE order for FW */ u8 macid_bitmap[BITS_TO_BYTES(RTW89_MAX_MAC_ID_NUM)]; @@ -4911,7 +4914,11 @@ struct rtw89_mcc_sync { bool enable; u16 offset; /* TU */ u8 macid_src; + u8 band_src; + u8 port_src; u8 macid_tgt; + u8 band_tgt; + u8 port_tgt; }; struct rtw89_mcc_config { From 441a6014d0243a432a96e23266896d2761116735 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 13 Feb 2024 15:35:14 +0800 Subject: [PATCH 366/378] wifi: rtw89: 8922a: declare to support two chanctx We are going to allow MCC (multi-channel concurrency) on RTL8922A. So, increase 8922a::support_chanctx_num up to 2 first. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240213073514.23796-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index a4b7d2e79638..2f1e7767d58a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1759,7 +1759,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .dig_table = NULL, .dig_regs = &rtw8922a_dig_regs, .tssi_dbw_table = NULL, - .support_chanctx_num = 1, + .support_chanctx_num = 2, .support_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ) | BIT(NL80211_BAND_6GHZ), From 63d94f7496233600186303f1eee000ab2ffc920a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 13 Feb 2024 20:25:56 +0800 Subject: [PATCH 367/378] wifi: rtw89: fw: remove unnecessary rcu_read_unlock() for punctured The rcu_read_unlock() is accidentally added, and sparse warn: drivers/net/wireless/realtek/rtw89/fw.c:2807:17: warning: context imbalance in 'rtw89_fw_h2c_assoc_cmac_tbl_g7' - unexpected unlock Fixes: b82730bf57b5 ("wifi: cfg80211/mac80211: move puncturing into chandef") Cc: Johannes Berg Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240213122556.9593-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 2ab45d0878f7..63897351ca15 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2808,7 +2808,6 @@ int rtw89_fw_h2c_assoc_cmac_tbl_g7(struct rtw89_dev *rtwdev, h2c->w4 |= le32_encode_bits(~punct, CCTLINFO_G7_W4_ACT_SUBCH_CBW); - rcu_read_unlock(); h2c->m4 |= cpu_to_le32(CCTLINFO_G7_W4_ACT_SUBCH_CBW); } From bcfcbf23a98ca19ba5931914801b5939e0d17bda Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Tue, 13 Feb 2024 16:33:02 +0200 Subject: [PATCH 368/378] wifi: rtlwifi: rtl8192cu: Fix 2T2R chip type detection rtl8192cu handles 1T1R devices (RTL8188CUS), 1T2R devices (RTL8191CU), and 2T2R devices (RTL8192CU). The 2T2R devices were incorrectly detected as 1T2R because of a mistake in the IS_92C_1T2R macro. The visible effect of this is that the firmware was allowed to use TX rates only up to MCS7. Fix the IS_92C_1T2R macro. Now my 2T2R device has much better upload speed. Before: 46 Mbps. After: 82 Mbps. Also fix a debug message which was printing "RF_1T1R" even for 1T2R chips. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/ed960059-5c77-422d-ac4e-fe9fc9d0d296@gmail.com --- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h | 2 +- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h index 91e4427ab022..4757f93b84e4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h @@ -11,7 +11,7 @@ #define CHIP_VENDOR_UMC_B_CUT BIT(6) #define IS_92C_1T2R(version) \ - (((version) & CHIP_92C) && ((version) & CHIP_92C_1T2R)) + (((version) & CHIP_92C_1T2R) == CHIP_92C_1T2R) #define IS_VENDOR_UMC(version) \ (((version) & CHIP_VENDOR_UMC) ? true : false) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c index 4ff0d4118193..a76f2dc8a977 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c @@ -101,7 +101,8 @@ void rtl92c_read_chip_version(struct ieee80211_hw *hw) rtlphy->rf_type = RF_1T1R; rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD, "Chip RF Type: %s\n", - rtlphy->rf_type == RF_2T2R ? "RF_2T2R" : "RF_1T1R"); + rtlphy->rf_type == RF_2T2R ? "RF_2T2R" : + rtlphy->rf_type == RF_1T2R ? "RF_1T2R" : "RF_1T1R"); if (get_rf_type(rtlphy) == RF_1T1R) rtlpriv->dm.rfpath_rxenable[0] = true; else From 42ffccd0a36e099dea3d3272c5d62a0454ded1f0 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Tue, 13 Feb 2024 16:33:11 +0200 Subject: [PATCH 369/378] wifi: rtlwifi: rtl_usb: Store the endpoint addresses And use the stored addresses in rtl8192cu instead of hardcoding them. This is what the vendor drivers do. Perhaps this is not strictly necessary for RTL8192CU devices. However, the dual mac version of RTL8192DU has two USB interfaces, each with its own set of endpoints. Hardcoding their addresses in the upcoming rtl8192du driver would require making some assumptions which I'm not qualified to make. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/7b6a602a-6101-4bab-958d-bcff4d565b40@gmail.com --- .../wireless/realtek/rtlwifi/rtl8192cu/sw.c | 1 - .../wireless/realtek/rtlwifi/rtl8192cu/trx.c | 77 ++++++++++--------- .../wireless/realtek/rtlwifi/rtl8192cu/trx.h | 1 - drivers/net/wireless/realtek/rtlwifi/usb.c | 31 ++++++-- drivers/net/wireless/realtek/rtlwifi/usb.h | 2 + drivers/net/wireless/realtek/rtlwifi/wifi.h | 1 - 6 files changed, 68 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c index 9f4cf09090d6..48be7e346efc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c @@ -145,7 +145,6 @@ MODULE_PARM_DESC(debug_mask, "Set debug mask (default 0)"); static struct rtl_hal_usbint_cfg rtl92cu_interface_cfg = { /* rx */ - .in_ep_num = RTL92C_USB_BULK_IN_NUM, .rx_urb_num = RTL92C_NUM_RX_URBS, .rx_max_size = RTL92C_SIZE_MAX_RX_BUFFER, .usb_rx_hdl = rtl8192cu_rx_hdl, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c index e5c81c1c63c0..4856ed40005b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c @@ -79,68 +79,75 @@ static int configvernoutep(struct ieee80211_hw *hw) static void twooutepmapping(struct ieee80211_hw *hw, bool is_chip8, bool bwificfg, struct rtl_ep_map *ep_map) { + struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); + struct rtl_usb *rtlusb = rtl_usbdev(usb_priv); struct rtl_priv *rtlpriv = rtl_priv(hw); if (bwificfg) { /* for WMM */ rtl_dbg(rtlpriv, COMP_INIT, DBG_DMESG, "USB Chip-B & WMM Setting.....\n"); - ep_map->ep_mapping[RTL_TXQ_BE] = 2; - ep_map->ep_mapping[RTL_TXQ_BK] = 3; - ep_map->ep_mapping[RTL_TXQ_VI] = 3; - ep_map->ep_mapping[RTL_TXQ_VO] = 2; - ep_map->ep_mapping[RTL_TXQ_MGT] = 2; - ep_map->ep_mapping[RTL_TXQ_BCN] = 2; - ep_map->ep_mapping[RTL_TXQ_HI] = 2; + ep_map->ep_mapping[RTL_TXQ_BE] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_BK] = rtlusb->out_eps[1]; + ep_map->ep_mapping[RTL_TXQ_VI] = rtlusb->out_eps[1]; + ep_map->ep_mapping[RTL_TXQ_VO] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_MGT] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_BCN] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_HI] = rtlusb->out_eps[0]; } else { /* typical setting */ rtl_dbg(rtlpriv, COMP_INIT, DBG_DMESG, "USB typical Setting.....\n"); - ep_map->ep_mapping[RTL_TXQ_BE] = 3; - ep_map->ep_mapping[RTL_TXQ_BK] = 3; - ep_map->ep_mapping[RTL_TXQ_VI] = 2; - ep_map->ep_mapping[RTL_TXQ_VO] = 2; - ep_map->ep_mapping[RTL_TXQ_MGT] = 2; - ep_map->ep_mapping[RTL_TXQ_BCN] = 2; - ep_map->ep_mapping[RTL_TXQ_HI] = 2; + ep_map->ep_mapping[RTL_TXQ_BE] = rtlusb->out_eps[1]; + ep_map->ep_mapping[RTL_TXQ_BK] = rtlusb->out_eps[1]; + ep_map->ep_mapping[RTL_TXQ_VI] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_VO] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_MGT] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_BCN] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_HI] = rtlusb->out_eps[0]; } } static void threeoutepmapping(struct ieee80211_hw *hw, bool bwificfg, struct rtl_ep_map *ep_map) { + struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); + struct rtl_usb *rtlusb = rtl_usbdev(usb_priv); struct rtl_priv *rtlpriv = rtl_priv(hw); if (bwificfg) { /* for WMM */ rtl_dbg(rtlpriv, COMP_INIT, DBG_DMESG, "USB 3EP Setting for WMM.....\n"); - ep_map->ep_mapping[RTL_TXQ_BE] = 5; - ep_map->ep_mapping[RTL_TXQ_BK] = 3; - ep_map->ep_mapping[RTL_TXQ_VI] = 3; - ep_map->ep_mapping[RTL_TXQ_VO] = 2; - ep_map->ep_mapping[RTL_TXQ_MGT] = 2; - ep_map->ep_mapping[RTL_TXQ_BCN] = 2; - ep_map->ep_mapping[RTL_TXQ_HI] = 2; + ep_map->ep_mapping[RTL_TXQ_BE] = rtlusb->out_eps[2]; + ep_map->ep_mapping[RTL_TXQ_BK] = rtlusb->out_eps[1]; + ep_map->ep_mapping[RTL_TXQ_VI] = rtlusb->out_eps[1]; + ep_map->ep_mapping[RTL_TXQ_VO] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_MGT] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_BCN] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_HI] = rtlusb->out_eps[0]; } else { /* typical setting */ rtl_dbg(rtlpriv, COMP_INIT, DBG_DMESG, "USB 3EP Setting for typical.....\n"); - ep_map->ep_mapping[RTL_TXQ_BE] = 5; - ep_map->ep_mapping[RTL_TXQ_BK] = 5; - ep_map->ep_mapping[RTL_TXQ_VI] = 3; - ep_map->ep_mapping[RTL_TXQ_VO] = 2; - ep_map->ep_mapping[RTL_TXQ_MGT] = 2; - ep_map->ep_mapping[RTL_TXQ_BCN] = 2; - ep_map->ep_mapping[RTL_TXQ_HI] = 2; + ep_map->ep_mapping[RTL_TXQ_BE] = rtlusb->out_eps[2]; + ep_map->ep_mapping[RTL_TXQ_BK] = rtlusb->out_eps[2]; + ep_map->ep_mapping[RTL_TXQ_VI] = rtlusb->out_eps[1]; + ep_map->ep_mapping[RTL_TXQ_VO] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_MGT] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_BCN] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_HI] = rtlusb->out_eps[0]; } } static void oneoutepmapping(struct ieee80211_hw *hw, struct rtl_ep_map *ep_map) { - ep_map->ep_mapping[RTL_TXQ_BE] = 2; - ep_map->ep_mapping[RTL_TXQ_BK] = 2; - ep_map->ep_mapping[RTL_TXQ_VI] = 2; - ep_map->ep_mapping[RTL_TXQ_VO] = 2; - ep_map->ep_mapping[RTL_TXQ_MGT] = 2; - ep_map->ep_mapping[RTL_TXQ_BCN] = 2; - ep_map->ep_mapping[RTL_TXQ_HI] = 2; + struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); + struct rtl_usb *rtlusb = rtl_usbdev(usb_priv); + + ep_map->ep_mapping[RTL_TXQ_BE] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_BK] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_VI] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_VO] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_MGT] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_BCN] = rtlusb->out_eps[0]; + ep_map->ep_mapping[RTL_TXQ_HI] = rtlusb->out_eps[0]; } static int _out_ep_mapping(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h index 5f81cab205cc..8678fa0043f4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h @@ -4,7 +4,6 @@ #ifndef __RTL92CU_TRX_H__ #define __RTL92CU_TRX_H__ -#define RTL92C_USB_BULK_IN_NUM 1 #define RTL92C_NUM_RX_URBS 8 #define RTL92C_NUM_TX_URBS 32 diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c index 1fc480fe18ad..6e8c87a2fae4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.c +++ b/drivers/net/wireless/realtek/rtlwifi/usb.c @@ -216,7 +216,6 @@ static int _rtl_usb_init_rx(struct ieee80211_hw *hw) rtlusb->rx_max_size = rtlpriv->cfg->usb_interface_cfg->rx_max_size; rtlusb->rx_urb_num = rtlpriv->cfg->usb_interface_cfg->rx_urb_num; - rtlusb->in_ep = rtlpriv->cfg->usb_interface_cfg->in_ep_num; rtlusb->usb_rx_hdl = rtlpriv->cfg->usb_interface_cfg->usb_rx_hdl; rtlusb->usb_rx_segregate_hdl = rtlpriv->cfg->usb_interface_cfg->usb_rx_segregate_hdl; @@ -248,20 +247,38 @@ static int _rtl_usb_init(struct ieee80211_hw *hw) pep_desc = &usb_intf->cur_altsetting->endpoint[epidx].desc; - if (usb_endpoint_dir_in(pep_desc)) + if (usb_endpoint_dir_in(pep_desc)) { + if (usb_endpoint_xfer_bulk(pep_desc)) { + /* The vendor drivers assume there is only one + * bulk in ep and that it's the first in ep. + */ + if (rtlusb->in_ep_nums == 0) + rtlusb->in_ep = usb_endpoint_num(pep_desc); + else + pr_warn("%s: bulk in endpoint is not the first in endpoint\n", + __func__); + } + rtlusb->in_ep_nums++; - else if (usb_endpoint_dir_out(pep_desc)) + } else if (usb_endpoint_dir_out(pep_desc)) { + if (rtlusb->out_ep_nums < RTL_USB_MAX_BULKOUT_NUM) { + if (usb_endpoint_xfer_bulk(pep_desc)) + rtlusb->out_eps[rtlusb->out_ep_nums] = + usb_endpoint_num(pep_desc); + } else { + pr_warn("%s: found more bulk out endpoints than the expected %d\n", + __func__, RTL_USB_MAX_BULKOUT_NUM); + } + rtlusb->out_ep_nums++; + } rtl_dbg(rtlpriv, COMP_INIT, DBG_DMESG, "USB EP(0x%02x), MaxPacketSize=%d, Interval=%d\n", pep_desc->bEndpointAddress, pep_desc->wMaxPacketSize, pep_desc->bInterval); } - if (rtlusb->in_ep_nums < rtlpriv->cfg->usb_interface_cfg->in_ep_num) { - pr_err("Too few input end points found\n"); - return -EINVAL; - } + if (rtlusb->out_ep_nums == 0) { pr_err("No output end points found\n"); return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.h b/drivers/net/wireless/realtek/rtlwifi/usb.h index 3bf85b23eec1..12529afc0510 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.h +++ b/drivers/net/wireless/realtek/rtlwifi/usb.h @@ -19,6 +19,7 @@ #define RTL_USB_MAX_TXQ_NUM 4 /* max tx queue */ #define RTL_USB_MAX_EP_NUM 6 /* max ep number */ +#define RTL_USB_MAX_BULKOUT_NUM 4 #define RTL_USB_MAX_TX_URBS_NUM 8 enum rtl_txq { @@ -94,6 +95,7 @@ struct rtl_usb { /* Tx */ u8 out_ep_nums ; + u8 out_eps[RTL_USB_MAX_BULKOUT_NUM]; u8 out_queue_sel; struct rtl_ep_map ep_map; diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index 3821f6e31447..01df00a43cdb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -2356,7 +2356,6 @@ struct rtl_mod_params { struct rtl_hal_usbint_cfg { /* data - rx */ - u32 in_ep_num; u32 rx_urb_num; u32 rx_max_size; From e1ea6db35fc3ba5ff063f097385e9f7a88c25356 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 13 Feb 2024 11:05:37 +0100 Subject: [PATCH 370/378] wifi: brcmsmac: avoid function pointer casts An old cleanup went a little too far and causes a warning with clang-16 and higher as it breaks control flow integrity (KCFI) rules: drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c:64:34: error: cast from 'void (*)(struct brcms_phy *)' to 'void (*)(void *)' converts to incompatible function type [-Werror,-Wcast-function-type-strict] 64 | brcms_init_timer(physhim->wl, (void (*)(void *))fn, | ^~~~~~~~~~~~~~~~~~~~ Change this one instance back to passing a void pointer so it can be used with the timer callback interface. Fixes: d89a4c80601d ("staging: brcm80211: removed void * from softmac phy") Signed-off-by: Arnd Bergmann Acked-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://msgid.link/20240213100548.457854-1-arnd@kernel.org --- .../net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c | 3 ++- drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c | 5 ++--- drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c index 07f83ff5a54a..a27d6f0b8819 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c @@ -383,8 +383,9 @@ struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp) return sh; } -static void wlc_phy_timercb_phycal(struct brcms_phy *pi) +static void wlc_phy_timercb_phycal(void *ptr) { + struct brcms_phy *pi = ptr; uint delay = 5; if (PHY_PERICAL_MPHASE_PENDING(pi)) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c index a0de5db0cd64..b72381791536 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c @@ -57,12 +57,11 @@ void wlc_phy_shim_detach(struct phy_shim_info *physhim) } struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, - void (*fn)(struct brcms_phy *pi), + void (*fn)(void *pi), void *arg, const char *name) { return (struct wlapi_timer *) - brcms_init_timer(physhim->wl, (void (*)(void *))fn, - arg, name); + brcms_init_timer(physhim->wl, fn, arg, name); } void wlapi_free_timer(struct wlapi_timer *t) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h index dd8774717ade..27d0934e600e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h @@ -131,7 +131,7 @@ void wlc_phy_shim_detach(struct phy_shim_info *physhim); /* PHY to WL utility functions */ struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, - void (*fn)(struct brcms_phy *pi), + void (*fn)(void *pi), void *arg, const char *name); void wlapi_free_timer(struct wlapi_timer *t); void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic); From ca1e1163889395cae54c4f051c301a8a6d6de311 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 15 Feb 2024 13:57:38 +0800 Subject: [PATCH 371/378] wifi: rtw89: 8922a: add set_channel MAC part To set channel, add a function to get TXSB (TX subband) that is hardware index to indicate primary channel. Then, configure band, channel, bandwidth and TXSB via registers. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240215055741.14148-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 47 ++++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 2 + drivers/net/wireless/realtek/rtw89/reg.h | 33 ++++++- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 92 +++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 81f73821e3fc..dfbf59895e4e 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -725,6 +725,53 @@ u8 rtw89_phy_get_txsc(struct rtw89_dev *rtwdev, } EXPORT_SYMBOL(rtw89_phy_get_txsc); +u8 rtw89_phy_get_txsb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, + enum rtw89_bandwidth dbw) +{ + enum rtw89_bandwidth cbw = chan->band_width; + u8 pri_ch = chan->primary_channel; + u8 central_ch = chan->channel; + u8 txsb_idx = 0; + + if (cbw == dbw || cbw == RTW89_CHANNEL_WIDTH_20) + return txsb_idx; + + switch (cbw) { + case RTW89_CHANNEL_WIDTH_40: + txsb_idx = pri_ch > central_ch ? 1 : 0; + break; + case RTW89_CHANNEL_WIDTH_80: + if (dbw == RTW89_CHANNEL_WIDTH_20) + txsb_idx = (pri_ch - central_ch + 6) / 4; + else + txsb_idx = pri_ch > central_ch ? 1 : 0; + break; + case RTW89_CHANNEL_WIDTH_160: + if (dbw == RTW89_CHANNEL_WIDTH_20) + txsb_idx = (pri_ch - central_ch + 14) / 4; + else if (dbw == RTW89_CHANNEL_WIDTH_40) + txsb_idx = (pri_ch - central_ch + 12) / 8; + else + txsb_idx = pri_ch > central_ch ? 1 : 0; + break; + case RTW89_CHANNEL_WIDTH_320: + if (dbw == RTW89_CHANNEL_WIDTH_20) + txsb_idx = (pri_ch - central_ch + 30) / 4; + else if (dbw == RTW89_CHANNEL_WIDTH_40) + txsb_idx = (pri_ch - central_ch + 28) / 8; + else if (dbw == RTW89_CHANNEL_WIDTH_80) + txsb_idx = (pri_ch - central_ch + 24) / 16; + else + txsb_idx = pri_ch > central_ch ? 1 : 0; + break; + default: + break; + } + + return txsb_idx; +} +EXPORT_SYMBOL(rtw89_phy_get_txsb); + static bool rtw89_phy_check_swsi_busy(struct rtw89_dev *rtwdev) { return !!rtw89_phy_read32_mask(rtwdev, R_SWSI_V1, B_SWSI_W_BUSY_V1) || diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 76234daab896..de19f1c7f931 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -778,6 +778,8 @@ void rtw89_phy_write_reg3_tbl(struct rtw89_dev *rtwdev, u8 rtw89_phy_get_txsc(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_bandwidth dbw); +u8 rtw89_phy_get_txsb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, + enum rtw89_bandwidth dbw); u32 rtw89_phy_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask); u32 rtw89_phy_read_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 23a09efabab7..ae637203ee8a 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -5986,6 +5986,16 @@ B_BE_RMAC_CKEN | B_BE_TXTIME_CKEN | B_BE_RESP_PKTCTL_CKEN | \ B_BE_SIGB_CKEN) +#define R_BE_WMAC_RFMOD 0x10010 +#define R_BE_WMAC_RFMOD_C1 0x14010 +#define B_BE_CMAC_ASSERTION BIT(31) +#define B_BE_WMAC_RFMOD_MASK GENMASK(2, 0) +#define BE_WMAC_RFMOD_20M 0 +#define BE_WMAC_RFMOD_40M 1 +#define BE_WMAC_RFMOD_80M 2 +#define BE_WMAC_RFMOD_160M 3 +#define BE_WMAC_RFMOD_320M 4 + #define R_BE_TX_SUB_BAND_VALUE 0x10088 #define R_BE_TX_SUB_BAND_VALUE_C1 0x14088 #define B_BE_PRI20_BITMAP_MASK GENMASK(31, 16) @@ -6122,6 +6132,13 @@ #define B_BE_MACTX_LATENCY_MASK GENMASK(10, 8) #define B_BE_PREBKF_TIME_MASK GENMASK(4, 0) +#define R_BE_PREBKF_CFG_1 0x1033C +#define R_BE_PREBKF_CFG_1_C1 0x1433C +#define B_BE_SIFS_TIMEOUT_TB_AGGR_MASK GENMASK(31, 24) +#define B_BE_SIFS_PREBKF_MASK GENMASK(23, 16) +#define B_BE_SIFS_TIMEOUT_T2_MASK GENMASK(14, 8) +#define B_BE_SIFS_MACTXEN_T1_MASK GENMASK(6, 0) + #define R_BE_CCA_CFG_0 0x10340 #define R_BE_CCA_CFG_0_C1 0x14340 #define B_BE_R_SIFS_AGGR_TIME_V1_MASK GENMASK(31, 24) @@ -6163,9 +6180,12 @@ #define R_BE_MUEDCA_EN 0x10370 #define R_BE_MUEDCA_EN_C1 0x14370 +#define B_BE_SIFS_TIMEOUT_TB_T2_MASK GENMASK(30, 24) +#define B_BE_SIFS_MACTXEN_TB_T1_MASK GENMASK(22, 16) #define B_BE_MUEDCA_WMM_SEL BIT(8) -#define B_BE_SET_MUEDCATIMER_TF_1 BIT(5) +#define B_BE_SET_MUEDCATIMER_TF_MASK GENMASK(5, 4) #define B_BE_SET_MUEDCATIMER_TF_0 BIT(4) +#define B_BE_MUEDCA_EN_MASK GENMASK(1, 0) #define B_BE_MUEDCA_EN_0 BIT(0) #define R_BE_CTN_DRV_TXEN 0x10398 @@ -6419,6 +6439,17 @@ #define B_BE_SPEC_SIFS_OFDM_PTCL_MASK GENMASK(15, 8) #define B_BE_SPEC_SIFS_CCK_PTCL_MASK GENMASK(7, 0) +#define R_BE_TXRATE_CHK 0x10828 +#define R_BE_TXRATE_CHK_C1 0x14828 +#define B_BE_LATENCY_PADDING_PKT_TH_MASK GENMASK(31, 24) +#define B_BE_PLCP_FETCH_BUFF_MASK GENMASK(23, 16) +#define B_BE_OFDM_CCK_ERR_PROC BIT(6) +#define B_BE_PKT_LAST_TX BIT(5) +#define B_BE_BAND_MODE BIT(4) +#define B_BE_MAX_TXNSS_MASK GENMASK(3, 2) +#define B_BE_RTS_LIMIT_IN_OFDM6 BIT(1) +#define B_BE_CHECK_CCK_EN BIT(0) + #define R_BE_MBSSID_DROP_0 0x1083C #define R_BE_MBSSID_DROP_0_C1 0x1483C #define B_BE_GI_LTF_FB_SEL BIT(30) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 2f1e7767d58a..ac6fed211070 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -771,6 +771,97 @@ static void rtw8922a_power_trim(struct rtw89_dev *rtwdev) rtw8922a_pad_bias_trim(rtwdev); } +static void rtw8922a_set_channel_mac(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + u8 mac_idx) +{ + u32 sub_carr = rtw89_mac_reg_by_idx(rtwdev, R_BE_TX_SUB_BAND_VALUE, mac_idx); + u32 chk_rate = rtw89_mac_reg_by_idx(rtwdev, R_BE_TXRATE_CHK, mac_idx); + u32 rf_mod = rtw89_mac_reg_by_idx(rtwdev, R_BE_WMAC_RFMOD, mac_idx); + u8 txsb20 = 0, txsb40 = 0, txsb80 = 0; + u8 rf_mod_val, chk_rate_mask; + u32 txsb; + u32 reg; + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_160: + txsb80 = rtw89_phy_get_txsb(rtwdev, chan, RTW89_CHANNEL_WIDTH_80); + fallthrough; + case RTW89_CHANNEL_WIDTH_80: + txsb40 = rtw89_phy_get_txsb(rtwdev, chan, RTW89_CHANNEL_WIDTH_40); + fallthrough; + case RTW89_CHANNEL_WIDTH_40: + txsb20 = rtw89_phy_get_txsb(rtwdev, chan, RTW89_CHANNEL_WIDTH_20); + break; + default: + break; + } + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_160: + rf_mod_val = BE_WMAC_RFMOD_160M; + txsb = u32_encode_bits(txsb20, B_BE_TXSB_20M_MASK) | + u32_encode_bits(txsb40, B_BE_TXSB_40M_MASK) | + u32_encode_bits(txsb80, B_BE_TXSB_80M_MASK); + break; + case RTW89_CHANNEL_WIDTH_80: + rf_mod_val = BE_WMAC_RFMOD_80M; + txsb = u32_encode_bits(txsb20, B_BE_TXSB_20M_MASK) | + u32_encode_bits(txsb40, B_BE_TXSB_40M_MASK); + break; + case RTW89_CHANNEL_WIDTH_40: + rf_mod_val = BE_WMAC_RFMOD_40M; + txsb = u32_encode_bits(txsb20, B_BE_TXSB_20M_MASK); + break; + case RTW89_CHANNEL_WIDTH_20: + default: + rf_mod_val = BE_WMAC_RFMOD_20M; + txsb = 0; + break; + } + + if (txsb20 <= BE_PRI20_BITMAP_MAX) + txsb |= u32_encode_bits(BIT(txsb20), B_BE_PRI20_BITMAP_MASK); + + rtw89_write8_mask(rtwdev, rf_mod, B_BE_WMAC_RFMOD_MASK, rf_mod_val); + rtw89_write32(rtwdev, sub_carr, txsb); + + switch (chan->band_type) { + case RTW89_BAND_2G: + chk_rate_mask = B_BE_BAND_MODE; + break; + case RTW89_BAND_5G: + case RTW89_BAND_6G: + chk_rate_mask = B_BE_CHECK_CCK_EN | B_BE_RTS_LIMIT_IN_OFDM6; + break; + default: + rtw89_warn(rtwdev, "Invalid band_type:%d\n", chan->band_type); + return; + } + + rtw89_write8_clr(rtwdev, chk_rate, B_BE_BAND_MODE | B_BE_CHECK_CCK_EN | + B_BE_RTS_LIMIT_IN_OFDM6); + rtw89_write8_set(rtwdev, chk_rate, chk_rate_mask); + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_320: + case RTW89_CHANNEL_WIDTH_160: + case RTW89_CHANNEL_WIDTH_80: + case RTW89_CHANNEL_WIDTH_40: + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_PREBKF_CFG_1, mac_idx); + rtw89_write32_mask(rtwdev, reg, B_BE_SIFS_MACTXEN_T1_MASK, 0x41); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MUEDCA_EN, mac_idx); + rtw89_write32_mask(rtwdev, reg, B_BE_SIFS_MACTXEN_TB_T1_MASK, 0x41); + break; + default: + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_PREBKF_CFG_1, mac_idx); + rtw89_write32_mask(rtwdev, reg, B_BE_SIFS_MACTXEN_T1_MASK, 0x3f); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MUEDCA_EN, mac_idx); + rtw89_write32_mask(rtwdev, reg, B_BE_SIFS_MACTXEN_TB_T1_MASK, 0x3e); + break; + } +} + struct rtw8922a_bb_gain { u32 gain_g[BB_PATH_NUM_8922A]; u32 gain_a[BB_PATH_NUM_8922A]; @@ -1298,6 +1389,7 @@ static void rtw8922a_set_channel(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx, enum rtw89_phy_idx phy_idx) { + rtw8922a_set_channel_mac(rtwdev, chan, mac_idx); rtw8922a_set_channel_bb(rtwdev, chan, phy_idx); } From f59cb1a030989ca0e5638fd4de91afddfbdbf89f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 15 Feb 2024 13:57:39 +0800 Subject: [PATCH 372/378] wifi: rtw89: 8922a: add set_channel BB part In additional to configure band, channel and bandwidth registers, it also configure CCK support on 2GHZ band, spur elimination, and RX gain. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240215055741.14148-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 37 ++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 390 ++++++++++++++++++ 2 files changed, 427 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index ae637203ee8a..9f3d10766b04 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8448,6 +8448,9 @@ #define B_PATH1_5MDET_SB2 BIT(8) #define B_PATH1_5MDET_SB0 BIT(6) #define B_PATH1_5MDET_TH GENMASK(5, 0) +#define R_S0S1_CSI_WGT 0x4D34 +#define B_S0S1_CSI_WGT_EN BIT(0) +#define B_S0S1_CSI_WGT_TONE_IDX GENMASK(31, 20) #define R_CHINFO_ELM_SRC 0x4D84 #define B_CHINFO_ELM_BITMAP GENMASK(22, 0) #define B_CHINFO_SRC GENMASK(31, 30) @@ -8601,18 +8604,48 @@ #define B_S0_DACKQ8_K GENMASK(15, 8) #define R_DCFO_WEIGHT_V1 0x6244 #define B_DCFO_WEIGHT_MSK_V1 GENMASK(31, 28) +#define R_DAC_CLK 0x625C +#define B_DAC_CLK GENMASK(31, 30) #define R_DCFO_OPT_V1 0x6260 #define B_DCFO_OPT_EN_V1 BIT(17) #define R_TXFCTR 0x627C #define B_TXFCTR_THD GENMASK(19, 10) #define R_TXSCALE 0x6284 #define B_TXFCTR_EN BIT(19) +#define R_PCOEFF01 0x6684 +#define B_PCOEFF01 GENMASK(23, 0) +#define R_PCOEFF23 0x6688 +#define B_PCOEFF23 GENMASK(23, 0) +#define R_PCOEFF45 0x668c +#define B_PCOEFF45 GENMASK(23, 0) +#define R_PCOEFF67 0x6690 +#define B_PCOEFF67 GENMASK(23, 0) +#define R_PCOEFF89 0x6694 +#define B_PCOEFF89 GENMASK(23, 0) +#define R_PCOEFFAB 0x6698 +#define B_PCOEFFAB GENMASK(23, 0) +#define R_PCOEFFCD 0x669c +#define B_PCOEFFCD GENMASK(23, 0) +#define R_PCOEFFEF 0x66a0 +#define B_PCOEFFEF GENMASK(23, 0) +#define R_MGAIN_BIAS 0x672c +#define B_MGAIN_BIAS_BW20 GENMASK(3, 0) +#define B_MGAIN_BIAS_BW40 GENMASK(7, 4) +#define R_CCK_RPL_OFST 0x6750 +#define B_CCK_RPL_OFST GENMASK(7, 0) +#define R_BK_FC0INV 0x6758 +#define B_BK_FC0INV GENMASK(18, 0) +#define R_CCK_FC0INV 0x675c +#define B_CCK_FC0INV GENMASK(18, 0) #define R_SEG0R_EDCCA_LVL_BE 0x69EC #define R_SEG0R_PPDU_LVL_BE 0x69F0 #define R_SEGSND 0x6A14 #define B_SEGSND_EN BIT(31) #define R_DBCC 0x6B48 #define B_DBCC_EN BIT(0) +#define R_FC0 0x6B4C +#define B_BW40_2XFFT BIT(31) +#define B_FC0 GENMASK(12, 0) #define R_FC0INV_SBW 0x6B50 #define B_SMALLBW GENMASK(31, 30) #define B_RX_BT_SG0 GENMASK(25, 22) @@ -9040,6 +9073,10 @@ #define B_DACKN0_V GENMASK(21, 14) #define R_DACKN1_CTL 0xC224 #define B_DACKN1_V GENMASK(21, 14) +#define R_GAIN_MAP0 0xE44C +#define B_GAIN_MAP0_EN BIT(0) +#define R_GAIN_MAP1 0xE54C +#define B_GAIN_MAP1_EN BIT(0) #define R_GOTX_IQKDPK_C0 0xE464 #define R_GOTX_IQKDPK_C1 0xE564 #define B_GOTX_IQKDPK GENMASK(28, 27) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index ac6fed211070..908d78bbc92c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -11,6 +11,7 @@ #include "reg.h" #include "rtw8922a.h" #include "rtw8922a_rfk.h" +#include "util.h" #define RTW8922A_FW_FORMAT_MAX 0 #define RTW8922A_FW_BASENAME "rtw89/rtw8922a_fw" @@ -862,6 +863,37 @@ static void rtw8922a_set_channel_mac(struct rtw89_dev *rtwdev, } } +static const u32 rtw8922a_sco_barker_threshold[14] = { + 0x1fe4f, 0x1ff5e, 0x2006c, 0x2017b, 0x2028a, 0x20399, 0x204a8, 0x205b6, + 0x206c5, 0x207d4, 0x208e3, 0x209f2, 0x20b00, 0x20d8a +}; + +static const u32 rtw8922a_sco_cck_threshold[14] = { + 0x2bdac, 0x2bf21, 0x2c095, 0x2c209, 0x2c37e, 0x2c4f2, 0x2c666, 0x2c7db, + 0x2c94f, 0x2cac3, 0x2cc38, 0x2cdac, 0x2cf21, 0x2d29e +}; + +static int rtw8922a_ctrl_sco_cck(struct rtw89_dev *rtwdev, + u8 primary_ch, enum rtw89_bandwidth bw, + enum rtw89_phy_idx phy_idx) +{ + u8 ch_element; + + if (primary_ch >= 14) + return -EINVAL; + + ch_element = primary_ch - 1; + + rtw89_phy_write32_idx(rtwdev, R_BK_FC0INV, B_BK_FC0INV, + rtw8922a_sco_barker_threshold[ch_element], + phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CCK_FC0INV, B_CCK_FC0INV, + rtw8922a_sco_cck_threshold[ch_element], + phy_idx); + + return 0; +} + struct rtw8922a_bb_gain { u32 gain_g[BB_PATH_NUM_8922A]; u32 gain_a[BB_PATH_NUM_8922A]; @@ -1022,12 +1054,341 @@ static void rtw8922a_set_gain(struct rtw89_dev *rtwdev, rtw8922a_set_rpl_gain(rtwdev, chan, path, phy_idx); } +static void rtw8922a_set_rx_gain_normal_cck(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + s8 value = -gain->offset[path][RTW89_GAIN_OFFSET_2G_CCK]; /* S(8,2) */ + u8 fraction = value & 0x3; + + if (fraction) { + rtw89_phy_write32_mask(rtwdev, R_MGAIN_BIAS, B_MGAIN_BIAS_BW20, + (0x4 - fraction) << 1); + rtw89_phy_write32_mask(rtwdev, R_MGAIN_BIAS, B_MGAIN_BIAS_BW40, + (0x4 - fraction) << 1); + + value >>= 2; + rtw89_phy_write32_mask(rtwdev, R_CCK_RPL_OFST, B_CCK_RPL_OFST, + value + 1 + 0xdc); + } else { + rtw89_phy_write32_mask(rtwdev, R_MGAIN_BIAS, B_MGAIN_BIAS_BW20, 0); + rtw89_phy_write32_mask(rtwdev, R_MGAIN_BIAS, B_MGAIN_BIAS_BW40, 0); + + value >>= 2; + rtw89_phy_write32_mask(rtwdev, R_CCK_RPL_OFST, B_CCK_RPL_OFST, + value + 0xdc); + } +} + +static void rtw8922a_set_rx_gain_normal_ofdm(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path) +{ + static const u32 rssi_tb_bias_comp[2] = {0x41f8, 0x45f8}; + static const u32 rssi_tb_ext_comp[2] = {0x4208, 0x4608}; + static const u32 rssi_ofst_addr[2] = {0x40c8, 0x44c8}; + static const u32 rpl_bias_comp[2] = {0x41e8, 0x45e8}; + static const u32 rpl_ext_comp[2] = {0x41f8, 0x45f8}; + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + enum rtw89_gain_offset gain_band; + s8 v1, v2, v3; + s32 value; + + gain_band = rtw89_subband_to_gain_offset_band_of_ofdm(chan->subband_type); + value = gain->offset[path][gain_band]; + rtw89_phy_write32_mask(rtwdev, rssi_ofst_addr[path], 0xff000000, value + 0xF8); + + value *= -4; + v1 = clamp_t(s32, value, S8_MIN, S8_MAX); + value -= v1; + v2 = clamp_t(s32, value, S8_MIN, S8_MAX); + value -= v2; + v3 = clamp_t(s32, value, S8_MIN, S8_MAX); + + rtw89_phy_write32_mask(rtwdev, rpl_bias_comp[path], 0xff, v1); + rtw89_phy_write32_mask(rtwdev, rpl_ext_comp[path], 0xff, v2); + rtw89_phy_write32_mask(rtwdev, rpl_ext_comp[path], 0xff00, v3); + + rtw89_phy_write32_mask(rtwdev, rssi_tb_bias_comp[path], 0xff0000, v1); + rtw89_phy_write32_mask(rtwdev, rssi_tb_ext_comp[path], 0xff0000, v2); + rtw89_phy_write32_mask(rtwdev, rssi_tb_ext_comp[path], 0xff000000, v3); +} + +static void rtw8922a_set_rx_gain_normal(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + + if (!gain->offset_valid) + return; + + if (chan->band_type == RTW89_BAND_2G) + rtw8922a_set_rx_gain_normal_cck(rtwdev, chan, path); + + rtw8922a_set_rx_gain_normal_ofdm(rtwdev, chan, path); +} + +static void rtw8922a_set_cck_parameters(struct rtw89_dev *rtwdev, u8 central_ch, + enum rtw89_phy_idx phy_idx) +{ + if (central_ch == 14) { + rtw89_phy_write32_idx(rtwdev, R_PCOEFF01, B_PCOEFF01, 0x3b13ff, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF23, B_PCOEFF23, 0x1c42de, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF45, B_PCOEFF45, 0xfdb0ad, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF67, B_PCOEFF67, 0xf60f6e, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF89, B_PCOEFF89, 0xfd8f92, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFFAB, B_PCOEFFAB, 0x02d011, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFFCD, B_PCOEFFCD, 0x01c02c, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFFEF, B_PCOEFFEF, 0xfff00a, phy_idx); + } else { + rtw89_phy_write32_idx(rtwdev, R_PCOEFF01, B_PCOEFF01, 0x3a63ca, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF23, B_PCOEFF23, 0x2a833f, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF45, B_PCOEFF45, 0x1491f8, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF67, B_PCOEFF67, 0x03c0b0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF89, B_PCOEFF89, 0xfccff1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFFAB, B_PCOEFFAB, 0xfccfc3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFFCD, B_PCOEFFCD, 0xfebfdc, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFFEF, B_PCOEFFEF, 0xffdff7, phy_idx); + } +} + static void rtw8922a_ctrl_ch(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { + static const u32 band_sel[2] = {0x4160, 0x4560}; + u16 central_freq = chan->freq; + u8 central_ch = chan->channel; + u8 band = chan->band_type; + bool is_2g = band == RTW89_BAND_2G; + u8 chan_idx; + u8 path; + u8 sco; + + if (!central_freq) { + rtw89_warn(rtwdev, "Invalid central_freq\n"); + return; + } + rtw8922a_set_gain(rtwdev, chan, RF_PATH_A, phy_idx); rtw8922a_set_gain(rtwdev, chan, RF_PATH_B, phy_idx); + + for (path = RF_PATH_A; path < BB_PATH_NUM_8922A; path++) + rtw89_phy_write32_idx(rtwdev, band_sel[path], BIT((26)), is_2g, phy_idx); + + rtw8922a_set_rx_gain_normal(rtwdev, chan, RF_PATH_A); + rtw8922a_set_rx_gain_normal(rtwdev, chan, RF_PATH_B); + + rtw89_phy_write32_idx(rtwdev, R_FC0, B_FC0, central_freq, phy_idx); + sco = DIV_ROUND_CLOSEST(1 << 18, central_freq); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_FC0_INV, sco, phy_idx); + + if (band == RTW89_BAND_2G) + rtw8922a_set_cck_parameters(rtwdev, central_ch, phy_idx); + + chan_idx = rtw89_encode_chan_idx(rtwdev, chan->primary_channel, band); + rtw89_phy_write32_idx(rtwdev, R_MAC_PIN_SEL, B_CH_IDX_SEG0, chan_idx, phy_idx); +} + +static void +rtw8922a_ctrl_bw(struct rtw89_dev *rtwdev, u8 pri_sb, u8 bw, + enum rtw89_phy_idx phy_idx) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_5: + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_BW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_SMALLBW, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_PRICH, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_DAC_CLK, B_DAC_CLK, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP0, B_GAIN_MAP0_EN, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP1, B_GAIN_MAP1_EN, 0x0, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_10: + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_BW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_SMALLBW, 0x2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_PRICH, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_DAC_CLK, B_DAC_CLK, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP0, B_GAIN_MAP0_EN, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP1, B_GAIN_MAP1_EN, 0x0, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_20: + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_BW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_SMALLBW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_PRICH, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_DAC_CLK, B_DAC_CLK, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP0, B_GAIN_MAP0_EN, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP1, B_GAIN_MAP1_EN, 0x0, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_40: + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_BW, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_SMALLBW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_PRICH, pri_sb, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_DAC_CLK, B_DAC_CLK, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP0, B_GAIN_MAP0_EN, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP1, B_GAIN_MAP1_EN, 0x0, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_80: + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_BW, 0x2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_SMALLBW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_PRICH, pri_sb, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_DAC_CLK, B_DAC_CLK, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP0, B_GAIN_MAP0_EN, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP1, B_GAIN_MAP1_EN, 0x1, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_160: + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_BW, 0x3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FC0INV_SBW, B_SMALLBW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_ANT_CHBW, B_CHBW_PRICH, pri_sb, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_DAC_CLK, B_DAC_CLK, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP0, B_GAIN_MAP0_EN, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_MAP1, B_GAIN_MAP1_EN, 0x1, phy_idx); + break; + default: + rtw89_warn(rtwdev, "Fail to switch bw (bw:%d, pri_sb:%d)\n", bw, + pri_sb); + break; + } + + if (bw == RTW89_CHANNEL_WIDTH_40) + rtw89_phy_write32_idx(rtwdev, R_FC0, B_BW40_2XFFT, 1, phy_idx); + else + rtw89_phy_write32_idx(rtwdev, R_FC0, B_BW40_2XFFT, 0, phy_idx); +} + +static u32 rtw8922a_spur_freq(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan) +{ + return 0; +} + +#define CARRIER_SPACING_312_5 312500 /* 312.5 kHz */ +#define CARRIER_SPACING_78_125 78125 /* 78.125 kHz */ +#define MAX_TONE_NUM 2048 + +static void rtw8922a_set_csi_tone_idx(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + s32 freq_diff, csi_idx, csi_tone_idx; + u32 spur_freq; + + spur_freq = rtw8922a_spur_freq(rtwdev, chan); + if (spur_freq == 0) { + rtw89_phy_write32_idx(rtwdev, R_S0S1_CSI_WGT, B_S0S1_CSI_WGT_EN, + 0, phy_idx); + return; + } + + freq_diff = (spur_freq - chan->freq) * 1000000; + csi_idx = s32_div_u32_round_closest(freq_diff, CARRIER_SPACING_78_125); + s32_div_u32_round_down(csi_idx, MAX_TONE_NUM, &csi_tone_idx); + + rtw89_phy_write32_idx(rtwdev, R_S0S1_CSI_WGT, B_S0S1_CSI_WGT_TONE_IDX, + csi_tone_idx, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_S0S1_CSI_WGT, B_S0S1_CSI_WGT_EN, 1, phy_idx); +} + +static const struct rtw89_nbi_reg_def rtw8922a_nbi_reg_def[] = { + [RF_PATH_A] = { + .notch1_idx = {0x41a0, 0xFF}, + .notch1_frac_idx = {0x41a0, 0xC00}, + .notch1_en = {0x41a0, 0x1000}, + .notch2_idx = {0x41ac, 0xFF}, + .notch2_frac_idx = {0x41ac, 0xC00}, + .notch2_en = {0x41ac, 0x1000}, + }, + [RF_PATH_B] = { + .notch1_idx = {0x45a0, 0xFF}, + .notch1_frac_idx = {0x45a0, 0xC00}, + .notch1_en = {0x45a0, 0x1000}, + .notch2_idx = {0x45ac, 0xFF}, + .notch2_frac_idx = {0x45ac, 0xC00}, + .notch2_en = {0x45ac, 0x1000}, + }, +}; + +static void rtw8922a_set_nbi_tone_idx(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_nbi_reg_def *nbi = &rtw8922a_nbi_reg_def[path]; + s32 nbi_frac_idx, nbi_frac_tone_idx; + s32 nbi_idx, nbi_tone_idx; + bool notch2_chk = false; + u32 spur_freq, fc; + s32 freq_diff; + + spur_freq = rtw8922a_spur_freq(rtwdev, chan); + if (spur_freq == 0) { + rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr, + nbi->notch1_en.mask, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr, + nbi->notch2_en.mask, 0, phy_idx); + return; + } + + fc = chan->freq; + if (chan->band_width == RTW89_CHANNEL_WIDTH_160) { + fc = (spur_freq > fc) ? fc + 40 : fc - 40; + if ((fc > spur_freq && + chan->channel < chan->primary_channel) || + (fc < spur_freq && + chan->channel > chan->primary_channel)) + notch2_chk = true; + } + + freq_diff = (spur_freq - fc) * 1000000; + nbi_idx = s32_div_u32_round_down(freq_diff, CARRIER_SPACING_312_5, + &nbi_frac_idx); + + if (chan->band_width == RTW89_CHANNEL_WIDTH_20) { + s32_div_u32_round_down(nbi_idx + 32, 64, &nbi_tone_idx); + } else { + u16 tone_para = (chan->band_width == RTW89_CHANNEL_WIDTH_40) ? + 128 : 256; + + s32_div_u32_round_down(nbi_idx, tone_para, &nbi_tone_idx); + } + nbi_frac_tone_idx = + s32_div_u32_round_closest(nbi_frac_idx, CARRIER_SPACING_78_125); + + if (chan->band_width == RTW89_CHANNEL_WIDTH_160 && notch2_chk) { + rtw89_phy_write32_idx(rtwdev, nbi->notch2_idx.addr, + nbi->notch2_idx.mask, nbi_tone_idx, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_frac_idx.addr, + nbi->notch2_frac_idx.mask, nbi_frac_tone_idx, + phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr, + nbi->notch2_en.mask, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr, + nbi->notch2_en.mask, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr, + nbi->notch1_en.mask, 0, phy_idx); + } else { + rtw89_phy_write32_idx(rtwdev, nbi->notch1_idx.addr, + nbi->notch1_idx.mask, nbi_tone_idx, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch1_frac_idx.addr, + nbi->notch1_frac_idx.mask, nbi_frac_tone_idx, + phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr, + nbi->notch1_en.mask, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr, + nbi->notch1_en.mask, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr, + nbi->notch2_en.mask, 0, phy_idx); + } +} + +static void rtw8922a_spur_elimination(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + rtw8922a_set_csi_tone_idx(rtwdev, chan, phy_idx); + rtw8922a_set_nbi_tone_idx(rtwdev, chan, RF_PATH_A, phy_idx); + rtw8922a_set_nbi_tone_idx(rtwdev, chan, RF_PATH_B, phy_idx); } static void rtw8922a_ctrl_afe_dac(struct rtw89_dev *rtwdev, enum rtw89_bandwidth bw, @@ -1377,11 +1738,40 @@ static void rtw8922a_bb_sethw(struct rtw89_dev *rtwdev) rtw8922a_ctrl_mlo(rtwdev, rtwdev->mlo_dbcc_mode); } +static void rtw8922a_ctrl_cck_en(struct rtw89_dev *rtwdev, bool cck_en, + enum rtw89_phy_idx phy_idx) +{ + if (cck_en) { + rtw89_phy_write32_idx(rtwdev, R_RXCCA_BE1, B_RXCCA_BE1_DIS, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_UPD_CLK_ADC, B_ENABLE_CCK, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PD_ARBITER_OFF, B_PD_ARBITER_OFF, + 0, phy_idx); + } else { + rtw89_phy_write32_idx(rtwdev, R_RXCCA_BE1, B_RXCCA_BE1_DIS, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_UPD_CLK_ADC, B_ENABLE_CCK, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PD_ARBITER_OFF, B_PD_ARBITER_OFF, + 1, phy_idx); + } +} + static void rtw8922a_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { + bool cck_en = chan->band_type == RTW89_BAND_2G; + u8 pri_sb = chan->pri_sb_idx; + + if (cck_en) + rtw8922a_ctrl_sco_cck(rtwdev, chan->primary_channel, + chan->band_width, phy_idx); + rtw8922a_ctrl_ch(rtwdev, chan, phy_idx); + rtw8922a_ctrl_bw(rtwdev, pri_sb, chan->band_width, phy_idx); + rtw8922a_ctrl_cck_en(rtwdev, cck_en, phy_idx); + rtw8922a_spur_elimination(rtwdev, chan, phy_idx); + + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 1, phy_idx); + rtw8922a_tssi_reset(rtwdev, RF_PATH_AB, phy_idx); } static void rtw8922a_set_channel(struct rtw89_dev *rtwdev, From 2c681cbf6c3a76ef3cdd9b33c3b1644caab209d1 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 15 Feb 2024 13:57:40 +0800 Subject: [PATCH 373/378] wifi: rtw89: 8922a: add set_channel RF part Configure RF registers according to band, channel, bandwidth. Since this chip will support MLO, it needs check the operating mode to decide paths we are going to configure. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240215055741.14148-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 72 +++++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 4 + drivers/net/wireless/realtek/rtw89/reg.h | 8 ++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + .../net/wireless/realtek/rtw89/rtw8922a_rfk.c | 120 ++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8922a_rfk.h | 3 + 6 files changed, 208 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index dfbf59895e4e..12da63d64307 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -6378,6 +6378,78 @@ void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev) rtw89_phy_edcca_log(rtwdev); } +enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK] kpath dbcc_en: 0x%x, mode=0x%x, PHY%d\n", + rtwdev->dbcc_en, rtwdev->mlo_dbcc_mode, phy_idx); + + switch (rtwdev->mlo_dbcc_mode) { + case MLO_1_PLUS_1_1RF: + if (phy_idx == RTW89_PHY_0) + return RF_A; + else + return RF_B; + case MLO_1_PLUS_1_2RF: + if (phy_idx == RTW89_PHY_0) + return RF_A; + else + return RF_D; + case MLO_0_PLUS_2_1RF: + case MLO_2_PLUS_0_1RF: + if (phy_idx == RTW89_PHY_0) + return RF_AB; + else + return RF_AB; + case MLO_0_PLUS_2_2RF: + case MLO_2_PLUS_0_2RF: + case MLO_2_PLUS_2_2RF: + default: + if (phy_idx == RTW89_PHY_0) + return RF_AB; + else + return RF_CD; + } +} +EXPORT_SYMBOL(rtw89_phy_get_kpath); + +enum rtw89_rf_path rtw89_phy_get_syn_sel(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK] kpath dbcc_en: 0x%x, mode=0x%x, PHY%d\n", + rtwdev->dbcc_en, rtwdev->mlo_dbcc_mode, phy_idx); + + switch (rtwdev->mlo_dbcc_mode) { + case MLO_1_PLUS_1_1RF: + if (phy_idx == RTW89_PHY_0) + return RF_PATH_A; + else + return RF_PATH_B; + case MLO_1_PLUS_1_2RF: + if (phy_idx == RTW89_PHY_0) + return RF_PATH_A; + else + return RF_PATH_D; + case MLO_0_PLUS_2_1RF: + case MLO_2_PLUS_0_1RF: + if (phy_idx == RTW89_PHY_0) + return RF_PATH_A; + else + return RF_PATH_B; + case MLO_0_PLUS_2_2RF: + case MLO_2_PLUS_0_2RF: + case MLO_2_PLUS_2_2RF: + default: + if (phy_idx == RTW89_PHY_0) + return RF_PATH_A; + else + return RF_PATH_C; + } +} +EXPORT_SYMBOL(rtw89_phy_get_syn_sel); + static const struct rtw89_ccx_regs rtw89_ccx_regs_ax = { .setting_addr = R_CCX, .edcca_opt_mask = B_CCX_EDCCA_OPT_MSK, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index de19f1c7f931..082231ebbee5 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -945,5 +945,9 @@ void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx, void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, bool scan); void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev); void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev); +enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx); +enum rtw89_rf_path rtw89_phy_get_syn_sel(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx); #endif diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 9f3d10766b04..37ccd8ffa87a 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -7497,6 +7497,12 @@ #define CFGCH_BAND0_2G 0 #define CFGCH_BAND0_5G 1 #define CFGCH_BAND0_6G 0 +#define RR_CFGCH_BW_V2 GENMASK(12, 10) +#define CFGCH_BW_V2_20M 0 +#define CFGCH_BW_V2_40M 1 +#define CFGCH_BW_V2_80M 2 +#define CFGCH_BW_V2_160M 3 +#define CFGCH_BW_V2_320M 4 #define RR_CFGCH_BW GENMASK(11, 10) #define RR_CFGCH_CH GENMASK(7, 0) #define CFGCH_BW_20M 3 @@ -7683,6 +7689,8 @@ #define RR_MMD 0xd5 #define RR_MMD_RST_EN BIT(8) #define RR_MMD_RST_SYN BIT(6) +#define RR_SMD 0xd6 +#define RR_VCO2 BIT(19) #define RR_IQKPLL 0xdc #define RR_IQKPLL_MOD GENMASK(9, 8) #define RR_SYNLUT 0xdd diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 908d78bbc92c..7abd7256d1cb 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1781,6 +1781,7 @@ static void rtw8922a_set_channel(struct rtw89_dev *rtwdev, { rtw8922a_set_channel_mac(rtwdev, chan, mac_idx); rtw8922a_set_channel_bb(rtwdev, chan, phy_idx); + rtw8922a_set_channel_rf(rtwdev, chan, phy_idx); } static void rtw8922a_dfs_en_idx(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c index d8ef986e7877..72953b7358df 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c @@ -34,6 +34,126 @@ void rtw8922a_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx) } } +static +void rtw8922a_ctl_band_ch_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + u8 central_ch, enum rtw89_band band, + enum rtw89_bandwidth bw) +{ + const u32 rf_addr[2] = {RR_CFGCH, RR_CFGCH_V1}; + struct rtw89_hal *hal = &rtwdev->hal; + u32 rf_reg[RF_PATH_NUM_8922A][2]; + u8 synpath; + u32 rf18; + u8 kpath; + u8 path; + u8 i; + + rf_reg[RF_PATH_A][0] = rtw89_read_rf(rtwdev, RF_PATH_A, rf_addr[0], RFREG_MASK); + rf_reg[RF_PATH_A][1] = rtw89_read_rf(rtwdev, RF_PATH_A, rf_addr[1], RFREG_MASK); + rf_reg[RF_PATH_B][0] = rtw89_read_rf(rtwdev, RF_PATH_B, rf_addr[0], RFREG_MASK); + rf_reg[RF_PATH_B][1] = rtw89_read_rf(rtwdev, RF_PATH_B, rf_addr[1], RFREG_MASK); + + kpath = rtw89_phy_get_kpath(rtwdev, phy); + synpath = rtw89_phy_get_syn_sel(rtwdev, phy); + + rf18 = rtw89_read_rf(rtwdev, synpath, RR_CFGCH, RFREG_MASK); + if (rf18 == INV_RF_DATA) { + rtw89_warn(rtwdev, "[RFK] Invalid RF18 value\n"); + return; + } + + for (path = 0; path < RF_PATH_NUM_8922A; path++) { + if (!(kpath & BIT(path))) + continue; + + for (i = 0; i < 2; i++) { + if (rf_reg[path][i] == INV_RF_DATA) { + rtw89_warn(rtwdev, + "[RFK] Invalid RF_0x18 for Path-%d\n", path); + return; + } + + rf_reg[path][i] &= ~(RR_CFGCH_BAND1 | RR_CFGCH_BW | + RR_CFGCH_BAND0 | RR_CFGCH_CH); + rf_reg[path][i] |= u32_encode_bits(central_ch, RR_CFGCH_CH); + + if (band == RTW89_BAND_2G) + rtw89_write_rf(rtwdev, path, RR_SMD, RR_VCO2, 0x0); + else + rtw89_write_rf(rtwdev, path, RR_SMD, RR_VCO2, 0x1); + + switch (band) { + case RTW89_BAND_2G: + default: + break; + case RTW89_BAND_5G: + rf_reg[path][i] |= + u32_encode_bits(CFGCH_BAND1_5G, RR_CFGCH_BAND1) | + u32_encode_bits(CFGCH_BAND0_5G, RR_CFGCH_BAND0); + break; + case RTW89_BAND_6G: + rf_reg[path][i] |= + u32_encode_bits(CFGCH_BAND1_6G, RR_CFGCH_BAND1) | + u32_encode_bits(CFGCH_BAND0_6G, RR_CFGCH_BAND0); + break; + } + + switch (bw) { + case RTW89_CHANNEL_WIDTH_5: + case RTW89_CHANNEL_WIDTH_10: + case RTW89_CHANNEL_WIDTH_20: + default: + break; + case RTW89_CHANNEL_WIDTH_40: + rf_reg[path][i] |= + u32_encode_bits(CFGCH_BW_V2_40M, RR_CFGCH_BW_V2); + break; + case RTW89_CHANNEL_WIDTH_80: + rf_reg[path][i] |= + u32_encode_bits(CFGCH_BW_V2_80M, RR_CFGCH_BW_V2); + break; + case RTW89_CHANNEL_WIDTH_160: + rf_reg[path][i] |= + u32_encode_bits(CFGCH_BW_V2_160M, RR_CFGCH_BW_V2); + break; + case RTW89_CHANNEL_WIDTH_320: + rf_reg[path][i] |= + u32_encode_bits(CFGCH_BW_V2_320M, RR_CFGCH_BW_V2); + break; + } + + rtw89_write_rf(rtwdev, path, rf_addr[i], + RFREG_MASK, rf_reg[path][i]); + fsleep(100); + } + } + + if (hal->cv != CHIP_CAV) + return; + + if (band == RTW89_BAND_2G) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWE, RFREG_MASK, 0x80000); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWA, RFREG_MASK, 0x00003); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWD1, RFREG_MASK, 0x0c990); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWD0, RFREG_MASK, 0xebe38); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWE, RFREG_MASK, 0x00000); + } else { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWE, RFREG_MASK, 0x80000); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWA, RFREG_MASK, 0x00003); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWD1, RFREG_MASK, 0x0c190); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWD0, RFREG_MASK, 0xebe38); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWE, RFREG_MASK, 0x00000); + } +} + +void rtw8922a_set_channel_rf(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + rtw8922a_ctl_band_ch_bw(rtwdev, phy_idx, chan->channel, chan->band_type, + chan->band_width); +} + enum _rf_syn_pow { RF_SYN_ON_OFF, RF_SYN_OFF_ON, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h index de5fa6c74530..27a2ff8166d0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h @@ -8,6 +8,9 @@ #include "core.h" void rtw8922a_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx); +void rtw8922a_set_channel_rf(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx); void rtw8922a_rfk_hw_init(struct rtw89_dev *rtwdev); #endif From 03830bb909a064bd590748127367878cf1d50fb0 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 15 Feb 2024 13:57:41 +0800 Subject: [PATCH 374/378] wifi: rtw89: 8922a: add helper of set_channel Reset hardware state to prevent hardware stays at abnormal state during setting channel. Besides, add preparation for MLO/DBCC before setting channel, and reconfigure registers after that. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/20240215055741.14148-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 51 +++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8922a_rfk.c | 23 +++++++++ .../net/wireless/realtek/rtw89/rtw8922a_rfk.h | 2 + 3 files changed, 76 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 7abd7256d1cb..823f0d840df9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -1774,6 +1774,37 @@ static void rtw8922a_set_channel_bb(struct rtw89_dev *rtwdev, rtw8922a_tssi_reset(rtwdev, RF_PATH_AB, phy_idx); } +static void rtw8922a_pre_set_channel_bb(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + if (!rtwdev->dbcc_en) + return; + + if (phy_idx == RTW89_PHY_0) { + rtw89_phy_write32_mask(rtwdev, R_DBCC, B_DBCC_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0x6180); + rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0xBBAB); + rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0xABA9); + rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0xEBA9); + rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0xEAA9); + } else { + rtw89_phy_write32_mask(rtwdev, R_DBCC, B_DBCC_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0xBBAB); + rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0xAFFF); + rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0xEFFF); + rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0xEEFF); + } +} + +static void rtw8922a_post_set_channel_bb(struct rtw89_dev *rtwdev, + enum rtw89_mlo_dbcc_mode mode) +{ + if (!rtwdev->dbcc_en) + return; + + rtw8922a_ctrl_mlo(rtwdev, mode); +} + static void rtw8922a_set_channel(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_mac_idx mac_idx, @@ -1863,6 +1894,25 @@ void rtw8922a_hal_reset(struct rtw89_dev *rtwdev, } } +static void rtw8922a_set_channel_help(struct rtw89_dev *rtwdev, bool enter, + struct rtw89_channel_help_params *p, + const struct rtw89_chan *chan, + enum rtw89_mac_idx mac_idx, + enum rtw89_phy_idx phy_idx) +{ + if (enter) { + rtw8922a_pre_set_channel_bb(rtwdev, phy_idx); + rtw8922a_pre_set_channel_rf(rtwdev, phy_idx); + } + + rtw8922a_hal_reset(rtwdev, phy_idx, mac_idx, chan->band_type, &p->tx_en, enter); + + if (!enter) { + rtw8922a_post_set_channel_bb(rtwdev, rtwdev->mlo_dbcc_mode); + rtw8922a_post_set_channel_rf(rtwdev, phy_idx); + } +} + static void rtw8922a_rfk_init(struct rtw89_dev *rtwdev) { struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; @@ -2169,6 +2219,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .read_rf = rtw89_phy_read_rf_v2, .write_rf = rtw89_phy_write_rf_v2, .set_channel = rtw8922a_set_channel, + .set_channel_help = rtw8922a_set_channel_help, .read_efuse = rtw8922a_read_efuse, .read_phycap = rtw8922a_read_phycap, .fem_setup = NULL, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c index 72953b7358df..2a371829268c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c @@ -353,3 +353,26 @@ void rtw8922a_rfk_hw_init(struct rtw89_dev *rtwdev) rtw8922a_rfk_pll_init(rtwdev); } + +void rtw8922a_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + bool mlo_1_1; + + if (!rtwdev->dbcc_en) + return; + + mlo_1_1 = rtw89_is_mlo_1_1(rtwdev); + if (mlo_1_1) + rtw8922a_set_syn01(rtwdev, RF_SYN_ALLON); + else if (phy_idx == RTW89_PHY_0) + rtw8922a_set_syn01(rtwdev, RF_SYN_ON_OFF); + else + rtw8922a_set_syn01(rtwdev, RF_SYN_OFF_ON); + + fsleep(1000); +} + +void rtw8922a_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + rtw8922a_rfk_mlo_ctrl(rtwdev); +} diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h index 27a2ff8166d0..66bdd57c1eea 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h @@ -12,5 +12,7 @@ void rtw8922a_set_channel_rf(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx); void rtw8922a_rfk_hw_init(struct rtw89_dev *rtwdev); +void rtw8922a_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw8922a_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); #endif From 5d2dbccc2b3cec2db5af6b7305ca144b35200024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= Date: Thu, 15 Feb 2024 16:36:18 +0100 Subject: [PATCH 375/378] wifi: wilc1000: split deeply nested RCU list traversal in dedicated helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move netif_wake_queue and its surrounding RCU operations in a dedicated function to clarify wilc_txq_task and ease refactoring Signed-off-by: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240215-wilc_fix_rcu_usage-v1-1-f610e46c6f82@bootlin.com --- .../net/wireless/microchip/wilc1000/netdev.c | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index 22f461f477f1..62414ab8846e 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -140,6 +140,19 @@ int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc) return ret_val; } +static void wilc_wake_tx_queues(struct wilc *wl) +{ + int srcu_idx; + struct wilc_vif *ifc; + + srcu_idx = srcu_read_lock(&wl->srcu); + list_for_each_entry_rcu(ifc, &wl->vif_list, list) { + if (ifc->mac_opened && netif_queue_stopped(ifc->ndev)) + netif_wake_queue(ifc->ndev); + } + srcu_read_unlock(&wl->srcu, srcu_idx); +} + static int wilc_txq_task(void *vp) { int ret; @@ -160,17 +173,7 @@ static int wilc_txq_task(void *vp) do { ret = wilc_wlan_handle_txq(wl, &txq_count); if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) { - int srcu_idx; - struct wilc_vif *ifc; - - srcu_idx = srcu_read_lock(&wl->srcu); - list_for_each_entry_rcu(ifc, &wl->vif_list, - list) { - if (ifc->mac_opened && - netif_queue_stopped(ifc->ndev)) - netif_wake_queue(ifc->ndev); - } - srcu_read_unlock(&wl->srcu, srcu_idx); + wilc_wake_tx_queues(wl); } if (ret != WILC_VMM_ENTRY_FULL_RETRY) break; From 059d0e3876abacd3967d22b6c59ff1e52d1c10ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= Date: Thu, 15 Feb 2024 16:36:19 +0100 Subject: [PATCH 376/378] wifi: wilc1000: use SRCU instead of RCU for vif list traversal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enabling CONFIG_PROVE_RCU_LIST raises many warnings in wilc driver, even on some places already protected by a read critical section. An example of such case is in wilc_get_available_idx: ============================= WARNING: suspicious RCU usage 6.8.0-rc1+ #32 Not tainted ----------------------------- drivers/net/wireless/microchip/wilc1000/netdev.c:944 RCU-list traversed in non-reader section!! [...] stack backtrace: CPU: 0 PID: 26 Comm: kworker/0:3 Not tainted 6.8.0-rc1+ #32 Hardware name: Atmel SAMA5 Workqueue: events_freezable mmc_rescan unwind_backtrace from show_stack+0x18/0x1c show_stack from dump_stack_lvl+0x34/0x58 dump_stack_lvl from wilc_netdev_ifc_init+0x788/0x8ec wilc_netdev_ifc_init from wilc_cfg80211_init+0x690/0x910 wilc_cfg80211_init from wilc_sdio_probe+0x168/0x490 wilc_sdio_probe from sdio_bus_probe+0x230/0x3f4 sdio_bus_probe from really_probe+0x270/0xdf4 really_probe from __driver_probe_device+0x1dc/0x580 __driver_probe_device from driver_probe_device+0x60/0x140 driver_probe_device from __device_attach_driver+0x268/0x364 __device_attach_driver from bus_for_each_drv+0x15c/0x1cc bus_for_each_drv from __device_attach+0x1ec/0x3e8 __device_attach from bus_probe_device+0x190/0x1c0 bus_probe_device from device_add+0x10dc/0x18e4 device_add from sdio_add_func+0x1c0/0x2c0 sdio_add_func from mmc_attach_sdio+0xa08/0xe1c mmc_attach_sdio from mmc_rescan+0xa00/0xfe0 mmc_rescan from process_one_work+0x8d4/0x169c process_one_work from worker_thread+0x8cc/0x1340 worker_thread from kthread+0x448/0x510 kthread from ret_from_fork+0x14/0x28 This warning is due to the section being protected by a srcu critical read section, but the list traversal being done with classic RCU API. Fix the warning by using corresponding SRCU read lock/unlock APIs. While doing so, since we always manipulate the same list (managed through a pointer embedded in struct_wilc), add a macro to reduce the corresponding boilerplate in each call site. Signed-off-by: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240215-wilc_fix_rcu_usage-v1-2-f610e46c6f82@bootlin.com --- drivers/net/wireless/microchip/wilc1000/cfg80211.c | 2 +- drivers/net/wireless/microchip/wilc1000/hif.c | 2 +- drivers/net/wireless/microchip/wilc1000/netdev.c | 14 +++++++------- drivers/net/wireless/microchip/wilc1000/netdev.h | 6 ++++++ drivers/net/wireless/microchip/wilc1000/wlan.c | 2 +- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c index f03fd15c0c97..33f8e3a41937 100644 --- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c +++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c @@ -1518,7 +1518,7 @@ static struct wilc_vif *wilc_get_vif_from_type(struct wilc *wl, int type) { struct wilc_vif *vif; - list_for_each_entry_rcu(vif, &wl->vif_list, list) { + wilc_for_each_vif(wl, vif) { if (vif->iftype == type) return vif; } diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index d2b8c2630819..f3800aa9e9f8 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -107,7 +107,7 @@ static struct wilc_vif *wilc_get_vif_from_idx(struct wilc *wilc, int idx) if (index < 0 || index >= WILC_NUM_CONCURRENT_IFC) return NULL; - list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + wilc_for_each_vif(wilc, vif) { if (vif->idx == index) return vif; } diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index 62414ab8846e..96f239adc078 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -96,7 +96,7 @@ static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header) struct wilc_vif *vif; struct ieee80211_hdr *h = (struct ieee80211_hdr *)mac_header; - list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + wilc_for_each_vif(wilc, vif) { if (vif->iftype == WILC_STATION_MODE) if (ether_addr_equal_unaligned(h->addr2, vif->bssid)) { ndev = vif->ndev; @@ -132,7 +132,7 @@ int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc) struct wilc_vif *vif; srcu_idx = srcu_read_lock(&wilc->srcu); - list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + wilc_for_each_vif(wilc, vif) { if (!is_zero_ether_addr(vif->bssid)) ret_val++; } @@ -146,7 +146,7 @@ static void wilc_wake_tx_queues(struct wilc *wl) struct wilc_vif *ifc; srcu_idx = srcu_read_lock(&wl->srcu); - list_for_each_entry_rcu(ifc, &wl->vif_list, list) { + wilc_for_each_vif(wl, ifc) { if (ifc->mac_opened && netif_queue_stopped(ifc->ndev)) netif_wake_queue(ifc->ndev); } @@ -668,7 +668,7 @@ static int wilc_set_mac_addr(struct net_device *dev, void *p) /* Verify MAC Address is not already in use: */ srcu_idx = srcu_read_lock(&wilc->srcu); - list_for_each_entry_rcu(tmp_vif, &wilc->vif_list, list) { + wilc_for_each_vif(wilc, tmp_vif) { wilc_get_mac_address(tmp_vif, mac_addr); if (ether_addr_equal(addr->sa_data, mac_addr)) { if (vif != tmp_vif) { @@ -771,7 +771,7 @@ netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev) struct wilc_vif *vif; srcu_idx = srcu_read_lock(&wilc->srcu); - list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + wilc_for_each_vif(wilc, vif) { if (vif->mac_opened) netif_stop_queue(vif->ndev); } @@ -858,7 +858,7 @@ void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth) struct wilc_vif *vif; srcu_idx = srcu_read_lock(&wilc->srcu); - list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + wilc_for_each_vif(wilc, vif) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buff; u16 type = le16_to_cpup((__le16 *)buff); u32 type_bit = BIT(type >> 4); @@ -930,7 +930,7 @@ static u8 wilc_get_available_idx(struct wilc *wl) int srcu_idx; srcu_idx = srcu_read_lock(&wl->srcu); - list_for_each_entry_rcu(vif, &wl->vif_list, list) { + wilc_for_each_vif(wl, vif) { if (vif->idx == 0) idx = 1; else diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.h b/drivers/net/wireless/microchip/wilc1000/netdev.h index aafe3dc44ac6..5937d6d45695 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.h +++ b/drivers/net/wireless/microchip/wilc1000/netdev.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "hif.h" #include "wlan.h" @@ -29,6 +30,11 @@ #define TX_BACKOFF_WEIGHT_MS 1 +#define wilc_for_each_vif(w, v) \ + struct wilc *_w = w; \ + list_for_each_entry_srcu(v, &_w->vif_list, list, \ + srcu_read_lock_held(&_w->srcu)) + struct wilc_wfi_stats { unsigned long rx_packets; unsigned long tx_packets; diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c index 68be233c36ce..a9e872a7b2c3 100644 --- a/drivers/net/wireless/microchip/wilc1000/wlan.c +++ b/drivers/net/wireless/microchip/wilc1000/wlan.c @@ -725,7 +725,7 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) mutex_lock(&wilc->txq_add_to_head_cs); srcu_idx = srcu_read_lock(&wilc->srcu); - list_for_each_entry_rcu(vif, &wilc->vif_list, list) + wilc_for_each_vif(wilc, vif) wilc_wlan_txq_filter_dup_tcp_ack(vif->ndev); srcu_read_unlock(&wilc->srcu, srcu_idx); From 51e4aa8c449b4c3821170f5438d084bafb003bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= Date: Thu, 15 Feb 2024 16:36:20 +0100 Subject: [PATCH 377/378] wifi: wilc1000: fix declarations ordering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix reverse-christmas tree order in some functions before adding more variables Signed-off-by: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240215-wilc_fix_rcu_usage-v1-3-f610e46c6f82@bootlin.com --- drivers/net/wireless/microchip/wilc1000/hif.c | 16 ++++++++-------- drivers/net/wireless/microchip/wilc1000/netdev.c | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index f3800aa9e9f8..c42859a727c3 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -1567,11 +1567,11 @@ int wilc_deinit(struct wilc_vif *vif) void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) { - int result; - struct host_if_msg *msg; - int id; struct host_if_drv *hif_drv; + struct host_if_msg *msg; struct wilc_vif *vif; + int result; + int id; id = get_unaligned_le32(&buffer[length - 4]); vif = wilc_get_vif_from_idx(wilc, id); @@ -1608,11 +1608,11 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) { - int result; - struct host_if_msg *msg; - int id; struct host_if_drv *hif_drv; + struct host_if_msg *msg; struct wilc_vif *vif; + int result; + int id; mutex_lock(&wilc->deinit_lock); @@ -1654,10 +1654,10 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length) { - int result; - int id; struct host_if_drv *hif_drv; struct wilc_vif *vif; + int result; + int id; id = get_unaligned_le32(&buffer[length - 4]); vif = wilc_get_vif_from_idx(wilc, id); diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index 96f239adc078..092801d33915 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -814,12 +814,12 @@ static int wilc_mac_close(struct net_device *ndev) void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset) { - unsigned int frame_len = 0; - int stats; unsigned char *buff_to_send = NULL; - struct sk_buff *skb; struct net_device *wilc_netdev; + unsigned int frame_len = 0; struct wilc_vif *vif; + struct sk_buff *skb; + int stats; if (!wilc) return; From dd66185c23f71af36397bebfc99ede608dca07b6 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Thu, 15 Feb 2024 16:36:21 +0100 Subject: [PATCH 378/378] wifi: wilc1000: add missing read critical sections around vif list traversal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some code manipulating the vif list is still missing some srcu_read_lock / srcu_read_unlock, and so can trigger RCU warnings: ============================= WARNING: suspicious RCU usage 6.8.0-rc1+ #37 Not tainted ----------------------------- drivers/net/wireless/microchip/wilc1000/hif.c:110 RCU-list traversed without holding the required lock!! [...] stack backtrace: CPU: 0 PID: 6 Comm: kworker/0:0 Not tainted 6.8.0-rc1+ #37 Hardware name: Atmel SAMA5 Workqueue: events sdio_irq_work unwind_backtrace from show_stack+0x18/0x1c show_stack from dump_stack_lvl+0x34/0x58 dump_stack_lvl from wilc_get_vif_from_idx+0x158/0x180 wilc_get_vif_from_idx from wilc_network_info_received+0x80/0x48c wilc_network_info_received from wilc_handle_isr+0xa10/0xd30 wilc_handle_isr from wilc_sdio_interrupt+0x44/0x58 wilc_sdio_interrupt from process_sdio_pending_irqs+0x1c8/0x60c process_sdio_pending_irqs from sdio_irq_work+0x6c/0x14c sdio_irq_work from process_one_work+0x8d4/0x169c process_one_work from worker_thread+0x8cc/0x1340 worker_thread from kthread+0x448/0x510 kthread from ret_from_fork+0x14/0x28 Fix those warnings by adding the needed lock around the corresponding critical sections Signed-off-by: Ajay Singh Co-developed-by: Alexis Lothoré Signed-off-by: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240215-wilc_fix_rcu_usage-v1-4-f610e46c6f82@bootlin.com --- drivers/net/wireless/microchip/wilc1000/hif.c | 52 +++++++++++-------- .../net/wireless/microchip/wilc1000/netdev.c | 8 ++- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index c42859a727c3..f1085ccb7eed 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -1570,23 +1570,25 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) struct host_if_drv *hif_drv; struct host_if_msg *msg; struct wilc_vif *vif; + int srcu_idx; int result; int id; id = get_unaligned_le32(&buffer[length - 4]); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) - return; - hif_drv = vif->hif_drv; + goto out; + hif_drv = vif->hif_drv; if (!hif_drv) { netdev_err(vif->ndev, "driver not init[%p]\n", hif_drv); - return; + goto out; } msg = wilc_alloc_work(vif, handle_rcvd_ntwrk_info, false); if (IS_ERR(msg)) - return; + goto out; msg->body.net_info.frame_len = get_unaligned_le16(&buffer[6]) - 1; msg->body.net_info.rssi = buffer[8]; @@ -1595,7 +1597,7 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) GFP_KERNEL); if (!msg->body.net_info.mgmt) { kfree(msg); - return; + goto out; } result = wilc_enqueue_work(msg); @@ -1604,6 +1606,8 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) kfree(msg->body.net_info.mgmt); kfree(msg); } +out: + srcu_read_unlock(&wilc->srcu, srcu_idx); } void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) @@ -1611,36 +1615,32 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) struct host_if_drv *hif_drv; struct host_if_msg *msg; struct wilc_vif *vif; + int srcu_idx; int result; int id; mutex_lock(&wilc->deinit_lock); id = get_unaligned_le32(&buffer[length - 4]); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); - if (!vif) { - mutex_unlock(&wilc->deinit_lock); - return; - } + if (!vif) + goto out; hif_drv = vif->hif_drv; if (!hif_drv) { - mutex_unlock(&wilc->deinit_lock); - return; + goto out; } if (!hif_drv->conn_info.conn_result) { netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__); - mutex_unlock(&wilc->deinit_lock); - return; + goto out; } msg = wilc_alloc_work(vif, handle_rcvd_gnrl_async_info, false); - if (IS_ERR(msg)) { - mutex_unlock(&wilc->deinit_lock); - return; - } + if (IS_ERR(msg)) + goto out; msg->body.mac_info.status = buffer[7]; result = wilc_enqueue_work(msg); @@ -1648,7 +1648,8 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__); kfree(msg); } - +out: + srcu_read_unlock(&wilc->srcu, srcu_idx); mutex_unlock(&wilc->deinit_lock); } @@ -1656,24 +1657,27 @@ void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length) { struct host_if_drv *hif_drv; struct wilc_vif *vif; + int srcu_idx; int result; int id; id = get_unaligned_le32(&buffer[length - 4]); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) - return; - hif_drv = vif->hif_drv; + goto out; - if (!hif_drv) - return; + hif_drv = vif->hif_drv; + if (!hif_drv) { + goto out; + } if (hif_drv->usr_scan_req.scan_result) { struct host_if_msg *msg; msg = wilc_alloc_work(vif, handle_scan_complete, false); if (IS_ERR(msg)) - return; + goto out; result = wilc_enqueue_work(msg); if (result) { @@ -1682,6 +1686,8 @@ void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length) kfree(msg); } } +out: + srcu_read_unlock(&wilc->srcu, srcu_idx); } int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, u16 chan, diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index 092801d33915..710e29bea560 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -819,14 +819,16 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, unsigned int frame_len = 0; struct wilc_vif *vif; struct sk_buff *skb; + int srcu_idx; int stats; if (!wilc) return; + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_netdev = get_if_handler(wilc, buff); if (!wilc_netdev) - return; + goto out; buff += pkt_offset; vif = netdev_priv(wilc_netdev); @@ -837,7 +839,7 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, skb = dev_alloc_skb(frame_len); if (!skb) - return; + goto out; skb->dev = wilc_netdev; @@ -850,6 +852,8 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, stats = netif_rx(skb); netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats); } +out: + srcu_read_unlock(&wilc->srcu, srcu_idx); } void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth)