diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c index 6661fff326e0..987d57a01ddf 100644 --- a/drivers/net/wireless/ath/ath10k/usb.c +++ b/drivers/net/wireless/ath/ath10k/usb.c @@ -1016,7 +1016,6 @@ static int ath10k_usb_probe(struct usb_interface *interface, netif_napi_add(ar->napi_dev, &ar->napi, ath10k_usb_napi_poll); - usb_get_dev(dev); vendor_id = le16_to_cpu(dev->descriptor.idVendor); product_id = le16_to_cpu(dev->descriptor.idProduct); @@ -1055,12 +1054,10 @@ static int ath10k_usb_probe(struct usb_interface *interface, err: ath10k_core_destroy(ar); - usb_put_dev(dev); - return ret; } -static void ath10k_usb_remove(struct usb_interface *interface) +static void ath10k_usb_disconnect(struct usb_interface *interface) { struct ath10k_usb *ar_usb; @@ -1071,7 +1068,6 @@ static void ath10k_usb_remove(struct usb_interface *interface) ath10k_core_unregister(ar_usb->ar); netif_napi_del(&ar_usb->ar->napi); ath10k_usb_destroy(ar_usb->ar); - usb_put_dev(interface_to_usbdev(interface)); ath10k_core_destroy(ar_usb->ar); } @@ -1117,7 +1113,7 @@ static struct usb_driver ath10k_usb_driver = { .probe = ath10k_usb_probe, .suspend = ath10k_usb_pm_suspend, .resume = ath10k_usb_pm_resume, - .disconnect = ath10k_usb_remove, + .disconnect = ath10k_usb_disconnect, .id_table = ath10k_usb_ids, .supports_autosuspend = true, .disable_hub_initiated_lpm = 1, diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h index 8a040d03d27a..be9e31b3682d 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.h +++ b/drivers/net/wireless/ath/ath12k/ahb.h @@ -21,8 +21,8 @@ #define ATH12K_ROOTPD_READY_TIMEOUT (5 * HZ) #define ATH12K_RPROC_AFTER_POWERUP QCOM_SSR_AFTER_POWERUP #define ATH12K_AHB_FW_PREFIX "q6_fw" -#define ATH12K_AHB_FW_SUFFIX ".mdt" -#define ATH12K_AHB_FW2 "iu_fw.mdt" +#define ATH12K_AHB_FW_SUFFIX ".mbn" +#define ATH12K_AHB_FW2 "iu_fw.mbn" #define ATH12K_AHB_UPD_SWID 0x12 #define ATH12K_USERPD_SPAWN_TIMEOUT (5 * HZ) #define ATH12K_USERPD_READY_TIMEOUT (10 * HZ) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 760c76d6f0f4..59c193b24764 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -523,7 +523,7 @@ struct ath12k_sta { u16 links_map; u8 assoc_link_id; u16 ml_peer_id; - u8 num_peer; + u16 free_logical_link_idx_map; enum ieee80211_sta_state state; }; diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c index e71bb71a6020..9c19d9707abf 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.c +++ b/drivers/net/wireless/ath/ath12k/dp_htt.c @@ -205,16 +205,9 @@ ath12k_update_per_peer_tx_stats(struct ath12k_pdev_dp *dp_pdev, if (!(usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_RATE))) return; - if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) { + if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) is_ampdu = HTT_USR_CMPLTN_IS_AMPDU(usr_stats->cmpltn_cmn.flags); - tx_retry_failed = - __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_tried) - - __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_success); - tx_retry_count = - HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + - HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); - } if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS)) { @@ -223,10 +216,19 @@ ath12k_update_per_peer_tx_stats(struct ath12k_pdev_dp *dp_pdev, HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M); tid = le32_get_bits(usr_stats->ack_ba.info, HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM); - } - if (common->fes_duration_us) - tx_duration = le32_to_cpu(common->fes_duration_us); + if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) { + tx_retry_failed = + __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_tried) - + __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_success); + tx_retry_count = + HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + + HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); + } + + if (common->fes_duration_us) + tx_duration = le32_to_cpu(common->fes_duration_us); + } user_rate = &usr_stats->rate; flags = HTT_USR_RATE_PREAMBLE(user_rate->rate_flags); diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index 43e3880f8257..bf4f7dbae866 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -268,21 +268,28 @@ enum hal_rx_reception_type { }; enum hal_rx_legacy_rate { - HAL_RX_LEGACY_RATE_1_MBPS, - HAL_RX_LEGACY_RATE_2_MBPS, - HAL_RX_LEGACY_RATE_5_5_MBPS, - HAL_RX_LEGACY_RATE_6_MBPS, - HAL_RX_LEGACY_RATE_9_MBPS, - HAL_RX_LEGACY_RATE_11_MBPS, - HAL_RX_LEGACY_RATE_12_MBPS, - HAL_RX_LEGACY_RATE_18_MBPS, - HAL_RX_LEGACY_RATE_24_MBPS, - HAL_RX_LEGACY_RATE_36_MBPS, - HAL_RX_LEGACY_RATE_48_MBPS, - HAL_RX_LEGACY_RATE_54_MBPS, + HAL_RX_LEGACY_RATE_LP_1_MBPS, + HAL_RX_LEGACY_RATE_LP_2_MBPS, + HAL_RX_LEGACY_RATE_LP_5_5_MBPS, + HAL_RX_LEGACY_RATE_LP_11_MBPS, + HAL_RX_LEGACY_RATE_SP_2_MBPS, + HAL_RX_LEGACY_RATE_SP_5_5_MBPS, + HAL_RX_LEGACY_RATE_SP_11_MBPS, HAL_RX_LEGACY_RATE_INVALID, }; +enum hal_rx_legacy_rates_ofdm { + HAL_RX_LEGACY_RATE_OFDM_48_MBPS, + HAL_RX_LEGACY_RATE_OFDM_24_MBPS, + HAL_RX_LEGACY_RATE_OFDM_12_MBPS, + HAL_RX_LEGACY_RATE_OFDM_6_MBPS, + HAL_RX_LEGACY_RATE_OFDM_54_MBPS, + HAL_RX_LEGACY_RATE_OFDM_36_MBPS, + HAL_RX_LEGACY_RATE_OFDM_18_MBPS, + HAL_RX_LEGACY_RATE_OFDM_9_MBPS, + HAL_RX_LEGACY_RATE_OFDM_INVALID, +}; + enum hal_ring_type { HAL_REO_DST, HAL_REO_EXCEPTION, diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index a03881c73d68..553ec28b6aaa 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -164,30 +164,31 @@ static const struct ieee80211_channel ath12k_6ghz_channels[] = { CHAN6G(233, 7115, 0), }; +#define ATH12K_MAC_RATE_A_M(bps, code) \ + { .bitrate = (bps), .hw_value = (code),\ + .flags = IEEE80211_RATE_MANDATORY_A } + +#define ATH12K_MAC_RATE_B(bps, code, code_short) \ + { .bitrate = (bps), .hw_value = (code), .hw_value_short = (code_short),\ + .flags = IEEE80211_RATE_SHORT_PREAMBLE } + static struct ieee80211_rate ath12k_legacy_rates[] = { { .bitrate = 10, .hw_value = ATH12K_HW_RATE_CCK_LP_1M }, - { .bitrate = 20, - .hw_value = ATH12K_HW_RATE_CCK_LP_2M, - .hw_value_short = ATH12K_HW_RATE_CCK_SP_2M, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, - .hw_value = ATH12K_HW_RATE_CCK_LP_5_5M, - .hw_value_short = ATH12K_HW_RATE_CCK_SP_5_5M, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, - .hw_value = ATH12K_HW_RATE_CCK_LP_11M, - .hw_value_short = ATH12K_HW_RATE_CCK_SP_11M, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - - { .bitrate = 60, .hw_value = ATH12K_HW_RATE_OFDM_6M }, - { .bitrate = 90, .hw_value = ATH12K_HW_RATE_OFDM_9M }, - { .bitrate = 120, .hw_value = ATH12K_HW_RATE_OFDM_12M }, - { .bitrate = 180, .hw_value = ATH12K_HW_RATE_OFDM_18M }, - { .bitrate = 240, .hw_value = ATH12K_HW_RATE_OFDM_24M }, - { .bitrate = 360, .hw_value = ATH12K_HW_RATE_OFDM_36M }, - { .bitrate = 480, .hw_value = ATH12K_HW_RATE_OFDM_48M }, - { .bitrate = 540, .hw_value = ATH12K_HW_RATE_OFDM_54M }, + ATH12K_MAC_RATE_B(20, ATH12K_HW_RATE_CCK_LP_2M, + ATH12K_HW_RATE_CCK_SP_2M), + ATH12K_MAC_RATE_B(55, ATH12K_HW_RATE_CCK_LP_5_5M, + ATH12K_HW_RATE_CCK_SP_5_5M), + ATH12K_MAC_RATE_B(110, ATH12K_HW_RATE_CCK_LP_11M, + ATH12K_HW_RATE_CCK_SP_11M), + ATH12K_MAC_RATE_A_M(60, ATH12K_HW_RATE_OFDM_6M), + ATH12K_MAC_RATE_A_M(90, ATH12K_HW_RATE_OFDM_9M), + ATH12K_MAC_RATE_A_M(120, ATH12K_HW_RATE_OFDM_12M), + ATH12K_MAC_RATE_A_M(180, ATH12K_HW_RATE_OFDM_18M), + ATH12K_MAC_RATE_A_M(240, ATH12K_HW_RATE_OFDM_24M), + ATH12K_MAC_RATE_A_M(360, ATH12K_HW_RATE_OFDM_36M), + ATH12K_MAC_RATE_A_M(480, ATH12K_HW_RATE_OFDM_48M), + ATH12K_MAC_RATE_A_M(540, ATH12K_HW_RATE_OFDM_54M), }; static const int @@ -732,11 +733,17 @@ u8 ath12k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, if (ath12k_mac_bitrate_is_cck(rate->bitrate) != cck) continue; - if (rate->hw_value == hw_rate) + /* To handle 802.11a PPDU type */ + if ((!cck) && (rate->hw_value == hw_rate) && + (rate->flags & IEEE80211_RATE_MANDATORY_A)) return i; + /* To handle 802.11b short PPDU type */ else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE && rate->hw_value_short == hw_rate) return i; + /* To handle 802.11b long PPDU type */ + else if (rate->hw_value == hw_rate) + return i; } return 0; @@ -6786,6 +6793,8 @@ static void ath12k_mac_free_unassign_link_sta(struct ath12k_hw *ah, return; ahsta->links_map &= ~BIT(link_id); + ahsta->free_logical_link_idx_map |= BIT(arsta->link_idx); + rcu_assign_pointer(ahsta->link[link_id], NULL); synchronize_rcu(); @@ -7104,6 +7113,7 @@ static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah, struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta); struct ieee80211_link_sta *link_sta; struct ath12k_link_vif *arvif; + int link_idx; lockdep_assert_wiphy(ah->hw->wiphy); @@ -7122,8 +7132,16 @@ static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah, ether_addr_copy(arsta->addr, link_sta->addr); - /* logical index of the link sta in order of creation */ - arsta->link_idx = ahsta->num_peer++; + if (!ahsta->free_logical_link_idx_map) + return -ENOSPC; + + /* + * Allocate a logical link index by selecting the first available bit + * from the free logical index map + */ + link_idx = __ffs(ahsta->free_logical_link_idx_map); + ahsta->free_logical_link_idx_map &= ~BIT(link_idx); + arsta->link_idx = link_idx; arsta->link_id = link_id; ahsta->links_map |= BIT(arsta->link_id); @@ -7632,6 +7650,7 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, if (old_state == IEEE80211_STA_NOTEXIST && new_state == IEEE80211_STA_NONE) { memset(ahsta, 0, sizeof(*ahsta)); + ahsta->free_logical_link_idx_map = U16_MAX; arsta = &ahsta->deflink; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index c9cea597a92e..77f5d23be78d 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -405,6 +405,42 @@ ath12k_wifi7_dp_mon_hal_rx_parse_user_info(const struct hal_receive_user_info *r } } +static __always_inline u8 +ath12k_wifi7_hal_mon_map_legacy_rate_to_hw_rate(u8 rate) +{ + u8 ath12k_rate; + + /* Map hal_rx_legacy_rate to ath12k_hw_rate_cck */ + switch (rate) { + case HAL_RX_LEGACY_RATE_LP_1_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_LP_1M; + break; + case HAL_RX_LEGACY_RATE_LP_2_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_LP_2M; + break; + case HAL_RX_LEGACY_RATE_LP_5_5_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_LP_5_5M; + break; + case HAL_RX_LEGACY_RATE_LP_11_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_LP_11M; + break; + case HAL_RX_LEGACY_RATE_SP_2_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_SP_2M; + break; + case HAL_RX_LEGACY_RATE_SP_5_5_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_SP_5_5M; + break; + case HAL_RX_LEGACY_RATE_SP_11_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_SP_11M; + break; + default: + ath12k_rate = rate; + break; + } + + return ath12k_rate; +} + static void ath12k_wifi7_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb, struct hal_rx_mon_ppdu_info *ppdu_info) @@ -415,25 +451,32 @@ ath12k_wifi7_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb, rate = u32_get_bits(info0, HAL_RX_LSIG_B_INFO_INFO0_RATE); switch (rate) { case 1: - rate = HAL_RX_LEGACY_RATE_1_MBPS; + rate = HAL_RX_LEGACY_RATE_LP_1_MBPS; break; case 2: - case 5: - rate = HAL_RX_LEGACY_RATE_2_MBPS; + rate = HAL_RX_LEGACY_RATE_LP_2_MBPS; break; case 3: - case 6: - rate = HAL_RX_LEGACY_RATE_5_5_MBPS; + rate = HAL_RX_LEGACY_RATE_LP_5_5_MBPS; break; case 4: + rate = HAL_RX_LEGACY_RATE_LP_11_MBPS; + break; + case 5: + rate = HAL_RX_LEGACY_RATE_SP_2_MBPS; + break; + case 6: + rate = HAL_RX_LEGACY_RATE_SP_5_5_MBPS; + break; case 7: - rate = HAL_RX_LEGACY_RATE_11_MBPS; + rate = HAL_RX_LEGACY_RATE_SP_11_MBPS; break; default: rate = HAL_RX_LEGACY_RATE_INVALID; + break; } - ppdu_info->rate = rate; + ppdu_info->rate = ath12k_wifi7_hal_mon_map_legacy_rate_to_hw_rate(rate); ppdu_info->cck_flag = 1; } @@ -447,31 +490,32 @@ ath12k_wifi7_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga, rate = u32_get_bits(info0, HAL_RX_LSIG_A_INFO_INFO0_RATE); switch (rate) { case 8: - rate = HAL_RX_LEGACY_RATE_48_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_48_MBPS; break; case 9: - rate = HAL_RX_LEGACY_RATE_24_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_24_MBPS; break; case 10: - rate = HAL_RX_LEGACY_RATE_12_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_12_MBPS; break; case 11: - rate = HAL_RX_LEGACY_RATE_6_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_6_MBPS; break; case 12: - rate = HAL_RX_LEGACY_RATE_54_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_54_MBPS; break; case 13: - rate = HAL_RX_LEGACY_RATE_36_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_36_MBPS; break; case 14: - rate = HAL_RX_LEGACY_RATE_18_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_18_MBPS; break; case 15: - rate = HAL_RX_LEGACY_RATE_9_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_9_MBPS; break; default: - rate = HAL_RX_LEGACY_RATE_INVALID; + rate = HAL_RX_LEGACY_RATE_OFDM_INVALID; + break; } ppdu_info->rate = rate; diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index b93e33edf369..65a05a9520ff 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -10017,50 +10017,46 @@ static int ath12k_connect_pdev_htc_service(struct ath12k_base *ab, static int ath12k_wmi_send_unit_test_cmd(struct ath12k *ar, - struct wmi_unit_test_cmd ut_cmd, - u32 *test_args) + const struct wmi_unit_test_arg *ut) { struct ath12k_wmi_pdev *wmi = ar->wmi; struct wmi_unit_test_cmd *cmd; + int buf_len, arg_len; struct sk_buff *skb; struct wmi_tlv *tlv; + __le32 *ut_cmd_args; void *ptr; - u32 *ut_cmd_args; - int buf_len, arg_len; int ret; int i; - arg_len = sizeof(u32) * le32_to_cpu(ut_cmd.num_args); - buf_len = sizeof(ut_cmd) + arg_len + TLV_HDR_SIZE; + arg_len = sizeof(*ut_cmd_args) * ut->num_args; + buf_len = sizeof(*cmd) + arg_len + TLV_HDR_SIZE; skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len); if (!skb) return -ENOMEM; - cmd = (struct wmi_unit_test_cmd *)skb->data; + ptr = skb->data; + cmd = ptr; cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_UNIT_TEST_CMD, - sizeof(ut_cmd)); - - cmd->vdev_id = ut_cmd.vdev_id; - cmd->module_id = ut_cmd.module_id; - cmd->num_args = ut_cmd.num_args; - cmd->diag_token = ut_cmd.diag_token; - - ptr = skb->data + sizeof(ut_cmd); + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(ut->vdev_id); + cmd->module_id = cpu_to_le32(ut->module_id); + cmd->num_args = cpu_to_le32(ut->num_args); + cmd->diag_token = cpu_to_le32(ut->diag_token); + ptr += sizeof(*cmd); tlv = ptr; tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, arg_len); ptr += TLV_HDR_SIZE; - ut_cmd_args = ptr; - for (i = 0; i < le32_to_cpu(ut_cmd.num_args); i++) - ut_cmd_args[i] = test_args[i]; + for (i = 0; i < ut->num_args; i++) + ut_cmd_args[i] = cpu_to_le32(ut->args[i]); ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI unit test : module %d vdev %d n_args %d token %d\n", - cmd->module_id, cmd->vdev_id, cmd->num_args, - cmd->diag_token); + ut->module_id, ut->vdev_id, ut->num_args, ut->diag_token); ret = ath12k_wmi_cmd_send(wmi, skb, WMI_UNIT_TEST_CMDID); @@ -10076,8 +10072,7 @@ ath12k_wmi_send_unit_test_cmd(struct ath12k *ar, int ath12k_wmi_simulate_radar(struct ath12k *ar) { struct ath12k_link_vif *arvif; - u32 dfs_args[DFS_MAX_TEST_ARGS]; - struct wmi_unit_test_cmd wmi_ut; + struct wmi_unit_test_arg wmi_ut = {}; bool arvif_found = false; list_for_each_entry(arvif, &ar->arvifs, list) { @@ -10090,22 +10085,23 @@ int ath12k_wmi_simulate_radar(struct ath12k *ar) if (!arvif_found) return -EINVAL; - dfs_args[DFS_TEST_CMDID] = 0; - dfs_args[DFS_TEST_PDEV_ID] = ar->pdev->pdev_id; - /* Currently we could pass segment_id(b0 - b1), chirp(b2) + wmi_ut.args[DFS_TEST_CMDID] = 0; + wmi_ut.args[DFS_TEST_PDEV_ID] = ar->pdev->pdev_id; + /* + * Currently we could pass segment_id(b0 - b1), chirp(b2) * freq offset (b3 - b10) to unit test. For simulation * purpose this can be set to 0 which is valid. */ - dfs_args[DFS_TEST_RADAR_PARAM] = 0; + wmi_ut.args[DFS_TEST_RADAR_PARAM] = 0; - wmi_ut.vdev_id = cpu_to_le32(arvif->vdev_id); - wmi_ut.module_id = cpu_to_le32(DFS_UNIT_TEST_MODULE); - wmi_ut.num_args = cpu_to_le32(DFS_MAX_TEST_ARGS); - wmi_ut.diag_token = cpu_to_le32(DFS_UNIT_TEST_TOKEN); + wmi_ut.vdev_id = arvif->vdev_id; + wmi_ut.module_id = DFS_UNIT_TEST_MODULE; + wmi_ut.num_args = DFS_MAX_TEST_ARGS; + wmi_ut.diag_token = DFS_UNIT_TEST_TOKEN; ath12k_dbg(ar->ab, ATH12K_DBG_REG, "Triggering Radar Simulation\n"); - return ath12k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); + return ath12k_wmi_send_unit_test_cmd(ar, &wmi_ut); } int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 0bf0a7941cd3..5ba9b7d3a888 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -4193,7 +4193,6 @@ struct wmi_addba_clear_resp_cmd { struct ath12k_wmi_mac_addr_params peer_macaddr; } __packed; -#define DFS_PHYERR_UNIT_TEST_CMD 0 #define DFS_UNIT_TEST_MODULE 0x2b #define DFS_UNIT_TEST_TOKEN 0xAA @@ -4204,10 +4203,15 @@ enum dfs_test_args_idx { DFS_MAX_TEST_ARGS, }; -struct wmi_dfs_unit_test_arg { - u32 cmd_id; - u32 pdev_id; - u32 radar_param; +/* update if another test command requires more */ +#define WMI_UNIT_TEST_ARGS_MAX DFS_MAX_TEST_ARGS + +struct wmi_unit_test_arg { + u32 vdev_id; + u32 module_id; + u32 diag_token; + u32 num_args; + u32 args[WMI_UNIT_TEST_ARGS_MAX]; }; struct wmi_unit_test_cmd { diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 852e77e41bde..79c18f5ee02b 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -1124,8 +1124,6 @@ static int ath6kl_usb_probe(struct usb_interface *interface, int vendor_id, product_id; int ret = 0; - usb_get_dev(dev); - vendor_id = le16_to_cpu(dev->descriptor.idVendor); product_id = le16_to_cpu(dev->descriptor.idProduct); @@ -1143,11 +1141,8 @@ static int ath6kl_usb_probe(struct usb_interface *interface, ath6kl_dbg(ATH6KL_DBG_USB, "USB 1.1 Host\n"); ar_usb = ath6kl_usb_create(interface); - - if (ar_usb == NULL) { - ret = -ENOMEM; - goto err_usb_put; - } + if (ar_usb == NULL) + return -ENOMEM; ar = ath6kl_core_create(&ar_usb->udev->dev); if (ar == NULL) { @@ -1176,15 +1171,12 @@ static int ath6kl_usb_probe(struct usb_interface *interface, ath6kl_core_destroy(ar); err_usb_destroy: ath6kl_usb_destroy(ar_usb); -err_usb_put: - usb_put_dev(dev); return ret; } -static void ath6kl_usb_remove(struct usb_interface *interface) +static void ath6kl_usb_disconnect(struct usb_interface *interface) { - usb_put_dev(interface_to_usbdev(interface)); ath6kl_usb_device_detached(interface); } @@ -1235,7 +1227,7 @@ static struct usb_driver ath6kl_usb_driver = { .probe = ath6kl_usb_probe, .suspend = ath6kl_usb_pm_suspend, .resume = ath6kl_usb_pm_resume, - .disconnect = ath6kl_usb_remove, + .disconnect = ath6kl_usb_disconnect, .id_table = ath6kl_usb_ids, .supports_autosuspend = true, .disable_hub_initiated_lpm = 1, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 3080d82e25cc..8fbece3fdad9 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1630,16 +1630,6 @@ enum wmi_roam_mode { WMI_LOCK_BSS_MODE = 3, /* Lock to the current BSS */ }; -struct bss_bias { - u8 bssid[ETH_ALEN]; - s8 bias; -} __packed; - -struct bss_bias_info { - u8 num_bss; - struct bss_bias bss_bias[]; -} __packed; - struct low_rssi_scan_params { __le16 lrssi_scan_period; a_sle16 lrssi_scan_threshold; @@ -1652,7 +1642,6 @@ struct roam_ctrl_cmd { union { u8 bssid[ETH_ALEN]; /* WMI_FORCE_ROAM */ u8 roam_mode; /* WMI_SET_ROAM_MODE */ - struct bss_bias_info bss; /* WMI_SET_HOST_BIAS */ struct low_rssi_scan_params params; /* WMI_SET_LRSSI_SCAN_PARAMS */ } __packed info; diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 8533b88974b2..821909b81ea9 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1382,8 +1382,6 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, goto err_alloc; } - usb_get_dev(udev); - hif_dev->udev = udev; hif_dev->interface = interface; hif_dev->usb_device_id = id; @@ -1403,7 +1401,6 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, err_fw_req: usb_set_intfdata(interface, NULL); kfree(hif_dev); - usb_put_dev(udev); err_alloc: return ret; } @@ -1451,7 +1448,6 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface) kfree(hif_dev); dev_info(&udev->dev, "ath9k_htc: USB layer deinitialized\n"); - usb_put_dev(udev); } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/broadcom/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c index 3a8df7a18042..05da6987a845 100644 --- a/drivers/net/wireless/broadcom/b43/dma.c +++ b/drivers/net/wireless/broadcom/b43/dma.c @@ -837,18 +837,19 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, struct b43_dmaring *ring; int i, err; dma_addr_t dma_test; + size_t nr_slots; - ring = kzalloc_obj(*ring); + if (for_tx) + nr_slots = B43_TXRING_SLOTS; + else + nr_slots = B43_RXRING_SLOTS; + + ring = kzalloc_flex(*ring, meta, nr_slots); if (!ring) goto out; - ring->nr_slots = B43_RXRING_SLOTS; - if (for_tx) - ring->nr_slots = B43_TXRING_SLOTS; + ring->nr_slots = nr_slots; - ring->meta = kzalloc_objs(struct b43_dmadesc_meta, ring->nr_slots); - if (!ring->meta) - goto err_kfree_ring; for (i = 0; i < ring->nr_slots; i++) ring->meta->skb = B43_DMA_PTR_POISON; @@ -943,8 +944,6 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, err_kfree_txhdr_cache: kfree(ring->txhdr_cache); err_kfree_meta: - kfree(ring->meta); - err_kfree_ring: kfree(ring); ring = NULL; goto out; @@ -1004,7 +1003,6 @@ static void b43_destroy_dmaring(struct b43_dmaring *ring, free_ringmemory(ring); kfree(ring->txhdr_cache); - kfree(ring->meta); kfree(ring); } diff --git a/drivers/net/wireless/broadcom/b43/dma.h b/drivers/net/wireless/broadcom/b43/dma.h index c2a357219d4b..f9f65bbe2d76 100644 --- a/drivers/net/wireless/broadcom/b43/dma.h +++ b/drivers/net/wireless/broadcom/b43/dma.h @@ -228,8 +228,6 @@ struct b43_dmaring { const struct b43_dma_ops *ops; /* Kernel virtual base address of the ring memory. */ void *descbase; - /* Meta data about all descriptors. */ - struct b43_dmadesc_meta *meta; /* Cache of TX headers for each TX frame. * This is to avoid an allocation on each TX. * This is NULL for an RX ring. @@ -273,6 +271,8 @@ struct b43_dmaring { /* Statistics: Total number of TX plus all retries. */ u64 nr_total_packet_tries; #endif /* CONFIG_B43_DEBUG */ + /* Meta data about all descriptors. */ + struct b43_dmadesc_meta meta[] __counted_by(nr_slots); }; static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 77db8c75e6e2..3653ddbf3ce9 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 core release supported */ -#define IWL_BZ_UCODE_CORE_MAX 101 +#define IWL_BZ_UCODE_CORE_MAX 102 /* Lowest firmware API version supported */ #define IWL_BZ_UCODE_API_MIN 100 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index a279dcfd3083..83d893b10f8e 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -9,7 +9,7 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_DR_UCODE_CORE_MAX 101 +#define IWL_DR_UCODE_CORE_MAX 102 /* Lowest firmware API version supported */ #define IWL_DR_UCODE_API_MIN 100 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index ee00b2af7a1d..749d46dc0236 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 core release supported */ -#define IWL_SC_UCODE_CORE_MAX 101 +#define IWL_SC_UCODE_CORE_MAX 102 /* Lowest firmware API version supported */ #define IWL_SC_UCODE_API_MIN 100 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index de9aef0d924c..bf0f851a9075 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -504,7 +504,8 @@ iwl_acpi_parse_chains_table(union acpi_object *table, u8 num_chains, u8 num_sub_bands) { for (u8 chain = 0; chain < num_chains; chain++) { - for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM; + for (u8 subband = 0; + subband < ARRAY_SIZE(chains[chain].subbands); subband++) { /* if we don't have the values, use the default */ if (subband >= num_sub_bands) { @@ -534,7 +535,23 @@ int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt) if (IS_ERR(data)) return PTR_ERR(data); - /* start by trying to read revision 2 */ + /* start by trying to read revision 3 */ + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_WRDS_WIFI_DATA_SIZE_REV3, + &tbl_rev); + if (!IS_ERR(wifi_pkg)) { + if (tbl_rev != 3) { + ret = -EINVAL; + goto out_free; + } + + num_chains = ACPI_SAR_NUM_CHAINS_REV2; + num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV3; + + goto read_table; + } + + /* then try revision 2 */ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_WRDS_WIFI_DATA_SIZE_REV2, &tbl_rev); @@ -591,6 +608,13 @@ int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt) goto out_free; } + if (WARN_ON(num_chains * num_sub_bands > + ARRAY_SIZE(fwrt->sar_profiles[0].chains) * + ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) { + ret = -EINVAL; + goto out_free; + } + IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev); flags = wifi_pkg->package.elements[1].integer.value; @@ -624,7 +648,22 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) if (IS_ERR(data)) return PTR_ERR(data); - /* start by trying to read revision 2 */ + /* start by trying to read revision 3 */ + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_EWRD_WIFI_DATA_SIZE_REV3, + &tbl_rev); + if (!IS_ERR(wifi_pkg)) { + if (tbl_rev != 3) { + ret = -EINVAL; + goto out_free; + } + + num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV3; + + goto read_table; + } + + /* then try revision 2 */ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_EWRD_WIFI_DATA_SIZE_REV2, &tbl_rev); @@ -679,6 +718,13 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } + if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands > + ARRAY_SIZE(fwrt->sar_profiles[0].chains) * + ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) { + ret = -EINVAL; + goto out_free; + } + enabled = !!(wifi_pkg->package.elements[1].integer.value); n_profiles = wifi_pkg->package.elements[2].integer.value; @@ -721,6 +767,13 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) if (tbl_rev < 2) goto set_enabled; + if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * 2 * num_sub_bands > + ARRAY_SIZE(fwrt->sar_profiles[0].chains) * + ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) { + ret = -EINVAL; + goto out_free; + } + /* parse cdb chains for all profiles */ for (i = 0; i < n_profiles; i++) { struct iwl_sar_profile_chain *chains; @@ -759,6 +812,12 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) u8 profiles; u8 min_profiles; } rev_data[] = { + { + .revisions = BIT(4), + .bands = ACPI_GEO_NUM_BANDS_REV4, + .profiles = ACPI_NUM_GEO_PROFILES_REV3, + .min_profiles = BIOS_GEO_MIN_PROFILE_NUM, + }, { .revisions = BIT(3), .bands = ACPI_GEO_NUM_BANDS_REV2, @@ -812,6 +871,18 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) num_bands = rev_data[idx].bands; num_profiles = rev_data[idx].profiles; + if (WARN_ON(num_profiles > + ARRAY_SIZE(fwrt->geo_profiles))) { + ret = -EINVAL; + goto out_free; + } + + if (WARN_ON(num_bands > + ARRAY_SIZE(fwrt->geo_profiles[0].bands))) { + ret = -EINVAL; + goto out_free; + } + if (rev_data[idx].min_profiles) { /* read header that says # of profiles */ union acpi_object *entry; @@ -851,18 +922,20 @@ int iwl_acpi_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 < BIOS_GEO_MAX_NUM_BANDS; j++) { + struct iwl_geo_profile *prof = &fwrt->geo_profiles[i]; + + for (j = 0; j < ARRAY_SIZE(prof->bands); j++) { union acpi_object *entry; /* - * num_bands is either 2 or 3, if it's only 2 then - * fill the third band (6 GHz) with the values from - * 5 GHz (second band) + * num_bands is either 2 or 3 or 4, if it's lower + * than 4, fill the third band (6 GHz) with the values + * from 5 GHz (second band) */ if (j >= num_bands) { - fwrt->geo_profiles[i].bands[j].max = - fwrt->geo_profiles[i].bands[1].max; + prof->bands[j].max = prof->bands[1].max; } else { entry = &wifi_pkg->package.elements[entry_idx]; entry_idx++; @@ -872,15 +945,17 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) goto out_free; } - fwrt->geo_profiles[i].bands[j].max = + prof->bands[j].max = entry->integer.value; } - for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) { + for (k = 0; + k < ARRAY_SIZE(prof->bands[0].chains); + k++) { /* same here as above */ if (j >= num_bands) { - fwrt->geo_profiles[i].bands[j].chains[k] = - fwrt->geo_profiles[i].bands[1].chains[k]; + prof->bands[j].chains[k] = + prof->bands[1].chains[k]; } else { entry = &wifi_pkg->package.elements[entry_idx]; entry_idx++; @@ -890,7 +965,7 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) goto out_free; } - fwrt->geo_profiles[i].bands[j].chains[k] = + prof->bands[j].chains[k] = entry->integer.value; } } @@ -898,6 +973,7 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) } fwrt->geo_num_profiles = num_profiles; + fwrt->geo_bios_source = BIOS_SOURCE_ACPI; fwrt->geo_enabled = true; ret = 0; out_free: @@ -915,6 +991,22 @@ 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 5 */ + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_PPAG_WIFI_DATA_SIZE_V3, &tbl_rev); + if (!IS_ERR(wifi_pkg)) { + if (tbl_rev == 5) { + num_sub_bands = IWL_NUM_SUB_BANDS_V3; + IWL_DEBUG_RADIO(fwrt, + "Reading PPAG table (tbl_rev=%d)\n", + tbl_rev); + goto read_table; + } else { + ret = -EINVAL; + goto out_free; + } + } + /* try to read ppag table rev 1 to 4 (all have the same data size) */ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev); @@ -950,6 +1042,15 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) goto out_free; read_table: + if (WARN_ON_ONCE(num_sub_bands > + ARRAY_SIZE(fwrt->ppag_chains[0].subbands))) { + ret = -EINVAL; + goto out_free; + } + + BUILD_BUG_ON(ACPI_PPAG_NUM_CHAINS > + ARRAY_SIZE(fwrt->ppag_chains)); + fwrt->ppag_bios_rev = tbl_rev; flags = &wifi_pkg->package.elements[1]; @@ -966,7 +1067,7 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the * following sub-bands to High-Band (5GHz). */ - for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + for (i = 0; i < ACPI_PPAG_NUM_CHAINS; i++) { for (j = 0; j < num_sub_bands; j++) { union acpi_object *ent; @@ -980,6 +1081,7 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) } } + iwl_bios_print_ppag(fwrt, num_sub_bands); fwrt->ppag_bios_source = BIOS_SOURCE_ACPI; ret = 0; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 06cece4ea6d9..51a57e57de7a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -8,11 +8,6 @@ #include #include "fw/regulatory.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" @@ -44,6 +39,7 @@ #define ACPI_SAR_NUM_SUB_BANDS_REV0 5 #define ACPI_SAR_NUM_SUB_BANDS_REV1 11 #define ACPI_SAR_NUM_SUB_BANDS_REV2 11 +#define ACPI_SAR_NUM_SUB_BANDS_REV3 12 #define ACPI_WRDS_WIFI_DATA_SIZE_REV0 (ACPI_SAR_NUM_CHAINS_REV0 * \ ACPI_SAR_NUM_SUB_BANDS_REV0 + 2) @@ -51,6 +47,8 @@ ACPI_SAR_NUM_SUB_BANDS_REV1 + 2) #define ACPI_WRDS_WIFI_DATA_SIZE_REV2 (ACPI_SAR_NUM_CHAINS_REV2 * \ ACPI_SAR_NUM_SUB_BANDS_REV2 + 2) +#define ACPI_WRDS_WIFI_DATA_SIZE_REV3 (ACPI_SAR_NUM_CHAINS_REV2 * \ + ACPI_SAR_NUM_SUB_BANDS_REV3 + 2) #define ACPI_EWRD_WIFI_DATA_SIZE_REV0 ((ACPI_SAR_PROFILE_NUM - 1) * \ ACPI_SAR_NUM_CHAINS_REV0 * \ ACPI_SAR_NUM_SUB_BANDS_REV0 + 3) @@ -60,11 +58,15 @@ #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_EWRD_WIFI_DATA_SIZE_REV3 ((ACPI_SAR_PROFILE_NUM - 1) * \ + ACPI_SAR_NUM_CHAINS_REV2 * \ + ACPI_SAR_NUM_SUB_BANDS_REV3 + 3) #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 #define ACPI_GEO_NUM_BANDS_REV2 3 +#define ACPI_GEO_NUM_BANDS_REV4 4 #define ACPI_WRDD_WIFI_DATA_SIZE 2 #define ACPI_SPLC_WIFI_DATA_SIZE 2 @@ -96,10 +98,18 @@ */ #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) -#define ACPI_PPAG_WIFI_DATA_SIZE_V2 ((IWL_NUM_CHAIN_LIMITS * \ - IWL_NUM_SUB_BANDS_V2) + 2) +#define ACPI_PPAG_NUM_CHAINS 2 +#define ACPI_PPAG_NUM_BANDS_V1 5 +#define ACPI_PPAG_NUM_BANDS_V2 11 +#define ACPI_PPAG_NUM_BANDS_V3 12 +#define ACPI_PPAG_WIFI_DATA_SIZE_V1 ((ACPI_PPAG_NUM_CHAINS * \ + ACPI_PPAG_NUM_BANDS_V1) + 2) +#define ACPI_PPAG_WIFI_DATA_SIZE_V2 ((ACPI_PPAG_NUM_CHAINS * \ + ACPI_PPAG_NUM_BANDS_V2) + 2) + +/* used for ACPI PPAG table rev 5 */ +#define ACPI_PPAG_WIFI_DATA_SIZE_V3 ((ACPI_PPAG_NUM_CHAINS * \ + ACPI_PPAG_NUM_BANDS_V3) + 2) #define IWL_SAR_ENABLE_MSK BIT(0) #define IWL_REDUCE_POWER_FLAGS_POS 1 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 6a6e11a57dbf..06370c161fe4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -56,7 +56,8 @@ enum iwl_data_path_subcmd_ids { RFH_QUEUE_CONFIG_CMD = 0xD, /** - * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4 + * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4 or + * &struct iwl_tlc_config_cmd_v5 or &struct iwl_tlc_config_cmd. */ TLC_MNG_CONFIG_CMD = 0xF, 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 c7a833f8041a..b398c582b867 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -26,7 +26,7 @@ enum iwl_mac_conf_subcmd_ids { */ MISSED_VAP_NOTIF = 0xFA, /** - * @SESSION_PROTECTION_CMD: &struct iwl_mvm_session_prot_cmd + * @SESSION_PROTECTION_CMD: &struct iwl_session_prot_cmd */ SESSION_PROTECTION_CMD = 0x5, /** @@ -34,7 +34,8 @@ enum iwl_mac_conf_subcmd_ids { */ CANCEL_CHANNEL_SWITCH_CMD = 0x6, /** - * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd + * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd_v3 or + * &struct iwl_mac_config_cmd */ MAC_CONFIG_CMD = 0x8, /** @@ -42,7 +43,8 @@ enum iwl_mac_conf_subcmd_ids { */ LINK_CONFIG_CMD = 0x9, /** - * @STA_CONFIG_CMD: &struct iwl_sta_cfg_cmd + * @STA_CONFIG_CMD: &struct iwl_sta_cfg_cmd_v1, + * &struct iwl_sta_cfg_cmd_v2, or &struct iwl_sta_cfg_cmd */ STA_CONFIG_CMD = 0xA, /** @@ -356,7 +358,7 @@ struct iwl_mac_wifi_gen_support { } __packed; /** - * struct iwl_mac_config_cmd - command structure to configure MAC contexts in + * struct iwl_mac_config_cmd_v3 - command structure to configure MAC contexts in * MLD API for versions 2 and 3 * ( MAC_CONTEXT_CONFIG_CMD = 0x8 ) * @@ -375,7 +377,7 @@ struct iwl_mac_wifi_gen_support { * @client: client mac data * @p2p_dev: mac data for p2p device */ -struct iwl_mac_config_cmd { +struct iwl_mac_config_cmd_v3 { __le32 id_and_color; __le32 action; /* MAC_CONTEXT_TYPE_API_E */ @@ -393,7 +395,62 @@ struct iwl_mac_config_cmd { struct iwl_mac_client_data client; struct iwl_mac_p2p_dev_data p2p_dev; }; -} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2_VER_3 */ +} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2, _VER_3 */ + +/** + * struct iwl_mac_nan_data - NAN specific MAC data + * @ndi_addrs: extra NDI addresses being used + * @ndi_addrs_count: number of extra NDI addresses + */ +struct iwl_mac_nan_data { + struct { + u8 addr[ETH_ALEN]; + __le16 reserved; + } __packed ndi_addrs[2]; + __le32 ndi_addrs_count; +} __packed; /* MAC_CONTEXT_CONFIG_NAN_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_config_cmd - command structure to configure MAC contexts in + * MLD API for versions 4 + * ( MAC_CONTEXT_CONFIG_CMD = 0x8 ) + * + * @id_and_color: ID and color of the MAC + * @action: action to perform, see &enum iwl_ctxt_action + * @mac_type: one of &enum iwl_mac_types + * @local_mld_addr: mld address + * @reserved_for_local_mld_addr: reserved + * @filter_flags: combination of &enum iwl_mac_config_filter_flags + * @wifi_gen_v2: he/eht parameters as in cmd version 2 + * @wifi_gen: he/eht/uhr parameters as in cmd version 3 + * @nic_not_ack_enabled: mark that the NIC doesn't support receiving + * ACK-enabled AGG, (i.e. both BACK and non-BACK frames in single AGG). + * 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 + * @p2p_dev: mac data for p2p device + * @nan: NAN specific data (NAN data interface addresses) + */ +struct iwl_mac_config_cmd { + __le32 id_and_color; + __le32 action; + /* MAC_CONTEXT_TYPE_API_E */ + __le32 mac_type; + u8 local_mld_addr[6]; + __le16 reserved_for_local_mld_addr; + __le32 filter_flags; + union { + struct iwl_mac_wifi_gen_support_v2 wifi_gen_v2; + struct iwl_mac_wifi_gen_support wifi_gen; + }; + __le32 nic_not_ack_enabled; + /* MAC_CONTEXT_CONFIG_SPECIFIC_DATA_API_U_VER_3 */ + union { + struct iwl_mac_client_data client; + struct iwl_mac_p2p_dev_data p2p_dev; + struct iwl_mac_nan_data nan; + }; +} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_4 */ /** * enum iwl_link_ctx_modify_flags - indicate to the fw what fields are being @@ -652,6 +709,7 @@ struct iwl_link_config_cmd { */ #define IWL_FW_MAX_ACTIVE_LINKS_NUM 2 #define IWL_FW_MAX_LINK_ID 3 +#define IWL_FW_MAX_LINKS IWL_FW_MAX_LINK_ID + 1 /** * enum iwl_fw_sta_type - FW station types @@ -662,6 +720,13 @@ struct iwl_link_config_cmd { * @STATION_TYPE_MCAST: the station used for BCAST / MCAST in GO. Will be * suspended / resumed at the right timing depending on the clients' * power save state and the DTIM timing + * @STATION_TYPE_NAN_PEER_NMI: NAN management peer station type. A station + * of this type can have any number of links (even none) set in the + * link_mask. (Supported since version 3.) + * @STATION_TYPE_NAN_PEER_NDI: NAN data peer station type. A station + * of this type can have any number of links (even none) set in the + * link_mask. (Supported since version 3.) + * @STATION_TYPE_MAX: maximum number of FW station types * @STATION_TYPE_AUX: aux sta. In the FW there is no need for a special type * for the aux sta, so this type is only for driver - internal use. */ @@ -669,8 +734,11 @@ enum iwl_fw_sta_type { STATION_TYPE_PEER, STATION_TYPE_BCAST_MGMT, STATION_TYPE_MCAST, - STATION_TYPE_AUX, -}; /* STATION_TYPE_E_VER_1 */ + STATION_TYPE_NAN_PEER_NMI, + STATION_TYPE_NAN_PEER_NDI, + STATION_TYPE_MAX, + STATION_TYPE_AUX = STATION_TYPE_MAX /* this doesn't exist in FW */ +}; /* STATION_TYPE_E_VER_1, _VER_2 */ /** * struct iwl_sta_cfg_cmd_v1 - cmd structure to add a peer sta to the uCode's @@ -729,7 +797,7 @@ struct iwl_sta_cfg_cmd_v1 { } __packed; /* STA_CMD_API_S_VER_1 */ /** - * struct iwl_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's + * struct iwl_sta_cfg_cmd_v2 - cmd structure to add a peer sta to the uCode's * station table * ( STA_CONFIG_CMD = 0xA ) * @@ -769,7 +837,7 @@ struct iwl_sta_cfg_cmd_v1 { * @mic_compute_pad_delay: MIC compute time padding * @reserved: Reserved for alignment */ -struct iwl_sta_cfg_cmd { +struct iwl_sta_cfg_cmd_v2 { __le32 sta_id; __le32 link_id; u8 peer_mld_address[ETH_ALEN]; @@ -799,6 +867,83 @@ struct iwl_sta_cfg_cmd { u8 reserved[2]; } __packed; /* STA_CMD_API_S_VER_2 */ +/** + * struct iwl_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's + * station table + * ( STA_CONFIG_CMD = 0xA ) + * + * @sta_id: index of station in uCode's station table + * @link_mask: bitmap of link FW IDs used with this STA + * @peer_mld_address: the peers mld address + * @reserved_for_peer_mld_address: reserved + * @peer_link_address: the address of the link that is used to communicate + * with this sta + * @reserved_for_peer_link_address: reserved + * @station_type: type of this station. See &enum iwl_fw_sta_type + * @assoc_id: for GO only + * @beamform_flags: beam forming controls + * @mfp: indicates whether the STA uses management frame protection or not. + * @mimo: indicates whether the sta uses mimo or not + * @mimo_protection: indicates whether the sta uses mimo protection or not + * @ack_enabled: indicates that the AP supports receiving ACK- + * enabled AGG, i.e. both BACK and non-BACK frames in a single AGG + * @trig_rnd_alloc: indicates that trigger based random allocation + * is enabled according to UORA element existence + * @tx_ampdu_spacing: minimum A-MPDU spacing: + * 4 - 2us density, 5 - 4us density, 6 - 8us density, 7 - 16us density + * @tx_ampdu_max_size: maximum A-MPDU length: 0 - 8K, 1 - 16K, 2 - 32K, + * 3 - 64K, 4 - 128K, 5 - 256K, 6 - 512K, 7 - 1024K. + * @sp_length: the size of the SP in actual number of frames + * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver + * enabled ACs. + * @pkt_ext: optional, exists according to PPE-present bit in the HE/EHT-PHY + * capa + * @htc_flags: which features are supported in HTC + * @use_ldpc_x2_cw: Indicates whether to use LDPC with double CW + * @use_icf: Indicates whether to use ICF instead of RTS + * @dps_pad_time: DPS (Dynamic Power Save) padding delay resolution to ensure + * proper timing alignment + * @dps_trans_delay: DPS minimal time that takes the peer to return to low power + * @dps_enabled: flag indicating whether or not DPS is enabled + * @mic_prep_pad_delay: MIC prep time padding + * @mic_compute_pad_delay: MIC compute time padding + * @nmi_sta_id: for an NDI peer STA, the NMI peer STA ID it relates to + * @ndi_local_addr: for an NDI peer STA, the local NDI interface MAC address + * @reserved: Reserved for alignment + */ +struct iwl_sta_cfg_cmd { + __le32 sta_id; + __le32 link_mask; + u8 peer_mld_address[ETH_ALEN]; + __le16 reserved_for_peer_mld_address; + u8 peer_link_address[ETH_ALEN]; + __le16 reserved_for_peer_link_address; + __le32 station_type; + __le32 assoc_id; + __le32 beamform_flags; + __le32 mfp; + __le32 mimo; + __le32 mimo_protection; + __le32 ack_enabled; + __le32 trig_rnd_alloc; + __le32 tx_ampdu_spacing; + __le32 tx_ampdu_max_size; + __le32 sp_length; + __le32 uapsd_acs; + struct iwl_he_pkt_ext_v2 pkt_ext; + __le32 htc_flags; + u8 use_ldpc_x2_cw; + u8 use_icf; + u8 dps_pad_time; + u8 dps_trans_delay; + u8 dps_enabled; + u8 mic_prep_pad_delay; + u8 mic_compute_pad_delay; + u8 nmi_sta_id; + u8 ndi_local_addr[ETH_ALEN]; + u8 reserved[2]; +} __packed; /* STA_CMD_API_S_VER_3 */ + /** * struct iwl_aux_sta_cmd - command for AUX STA configuration * ( AUX_STA_CMD = 0xB ) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h index 2a174c00b712..439a4530ec9f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h @@ -57,8 +57,7 @@ enum iwl_mac_protection_flags { * @FW_MAC_TYPE_P2P_DEVICE: P2P Device * @FW_MAC_TYPE_P2P_STA: P2P client * @FW_MAC_TYPE_GO: P2P GO - * @FW_MAC_TYPE_TEST: ? - * @FW_MAC_TYPE_MAX: highest support MAC type + * @FW_MAC_TYPE_NAN: NAN (since version 4) */ enum iwl_mac_types { FW_MAC_TYPE_FIRST = 1, @@ -70,8 +69,7 @@ enum iwl_mac_types { FW_MAC_TYPE_P2P_DEVICE, FW_MAC_TYPE_P2P_STA, FW_MAC_TYPE_GO, - FW_MAC_TYPE_TEST, - FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST + FW_MAC_TYPE_NAN, }; /* MAC_CONTEXT_TYPE_API_E_VER_1 */ /** 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 bd6bf931866f..443a9a416325 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -204,7 +204,7 @@ struct iwl_nvm_get_info_phy { } __packed; /* REGULATORY_NVM_GET_INFO_PHY_SKU_SECTION_S_VER_1 */ #define IWL_NUM_CHANNELS_V1 51 -#define IWL_NUM_CHANNELS 110 +#define IWL_NUM_CHANNELS_V2 110 /** * struct iwl_nvm_get_info_regulatory_v1 - regulatory information @@ -227,7 +227,7 @@ struct iwl_nvm_get_info_regulatory_v1 { struct iwl_nvm_get_info_regulatory { __le32 lar_enabled; __le32 n_channels; - __le32 channel_profile[IWL_NUM_CHANNELS]; + __le32 channel_profile[IWL_NUM_CHANNELS_V2]; } __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_2 */ /** @@ -701,13 +701,23 @@ struct iwl_pnvm_init_complete_ntfy { #define UATS_TABLE_COL_SIZE 13 /** - * struct iwl_mcc_allowed_ap_type_cmd - struct for MCC_ALLOWED_AP_TYPE_CMD + * struct iwl_mcc_allowed_ap_type_cmd_v1 - struct for MCC_ALLOWED_AP_TYPE_CMD * @mcc_to_ap_type_map: mapping an MCC to 6 GHz AP type support (UATS) * @reserved: reserved */ -struct iwl_mcc_allowed_ap_type_cmd { +struct iwl_mcc_allowed_ap_type_cmd_v1 { u8 mcc_to_ap_type_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE]; __le16 reserved; } __packed; /* MCC_ALLOWED_AP_TYPE_CMD_API_S_VER_1 */ +/** + * struct iwl_mcc_allowed_ap_type_cmd - struct for MCC_ALLOWED_AP_TYPE_CMD + * @mcc_to_ap_type_map: mapping an MCC to 6 GHz AP type support (UATS) + * @mcc_to_ap_type_unii9_map: mapping an MCC to UNII-9 AP type support allowed + */ +struct iwl_mcc_allowed_ap_type_cmd { + u8 mcc_to_ap_type_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE]; + u8 mcc_to_ap_type_unii9_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE]; +} __packed; /* MCC_ALLOWED_AP_TYPE_CMD_API_S_VER_2 */ + #endif /* __iwl_fw_api_nvm_reg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index 0cd8a12e0f7c..a3f916630df2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -269,6 +269,7 @@ enum iwl_dev_tx_power_cmd_mode { #define IWL_NUM_CHAIN_LIMITS 2 #define IWL_NUM_SUB_BANDS_V1 5 #define IWL_NUM_SUB_BANDS_V2 11 +#define IWL_NUM_SUB_BANDS_V3 12 /** * struct iwl_dev_tx_power_common - Common part of the TX power reduction cmd @@ -425,24 +426,38 @@ struct iwl_dev_tx_power_cmd_v10 { __le32 flags; } __packed; /* TX_REDUCED_POWER_API_S_VER_10 */ +struct iwl_dev_tx_power_cmd_v11 { + __le16 per_chain[IWL_NUM_CHAIN_TABLES_V2][IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V3]; + u8 per_chain_restriction_changed; + u8 reserved; + __le32 timer_period; + __le32 flags; +} __packed; /* TX_REDUCED_POWER_API_S_VER_11 */ + /* * struct iwl_dev_tx_power_cmd - TX power reduction command (multiversion) * @common: common part of the command * @v9: version 9 part of the command * @v10: version 10 part of the command + * @v11: version 11 part of the command */ struct iwl_dev_tx_power_cmd { struct iwl_dev_tx_power_common common; union { struct iwl_dev_tx_power_cmd_v9 v9; struct iwl_dev_tx_power_cmd_v10 v10; + struct iwl_dev_tx_power_cmd_v11 v11; }; -} __packed; /* TX_REDUCED_POWER_API_S_VER_9_VER10 */ +} __packed; /* TX_REDUCED_POWER_API_S_VER_9 + * TX_REDUCED_POWER_API_S_VER_10 + * TX_REDUCED_POWER_API_S_VER_11 + */ #define IWL_NUM_GEO_PROFILES 3 #define IWL_NUM_GEO_PROFILES_V3 8 #define IWL_NUM_BANDS_PER_CHAIN_V1 2 #define IWL_NUM_BANDS_PER_CHAIN_V2 3 +#define IWL_NUM_BANDS_PER_CHAIN_V6 4 /** * enum iwl_geo_per_chain_offset_operation - type of operation @@ -524,12 +539,25 @@ struct iwl_geo_tx_power_profiles_cmd_v5 { __le32 table_revision; } __packed; /* PER_CHAIN_LIMIT_OFFSET_CMD_VER_5 */ +/** + * struct iwl_geo_tx_power_profiles_cmd_v6 - struct for PER_CHAIN_LIMIT_OFFSET_CMD cmd. + * @ops: operations, value from &enum iwl_geo_per_chain_offset_operation + * @table: offset profile per band. + * @bios_hdr: describes the revision and the source of the BIOS + */ +struct iwl_geo_tx_power_profiles_cmd_v6 { + __le32 ops; + struct iwl_per_chain_offset table[IWL_NUM_GEO_PROFILES_V3][IWL_NUM_BANDS_PER_CHAIN_V6]; + struct iwl_bios_config_hdr bios_hdr; +} __packed; /* PER_CHAIN_LIMIT_OFFSET_CMD_VER_6 */ + union iwl_geo_tx_power_profiles_cmd { struct iwl_geo_tx_power_profiles_cmd_v1 v1; struct iwl_geo_tx_power_profiles_cmd_v2 v2; struct iwl_geo_tx_power_profiles_cmd_v3 v3; struct iwl_geo_tx_power_profiles_cmd_v4 v4; struct iwl_geo_tx_power_profiles_cmd_v5 v5; + struct iwl_geo_tx_power_profiles_cmd_v6 v6; }; /** @@ -573,6 +601,7 @@ enum iwl_ppag_flags { * @v1: command version 1 structure. * @v5: command version 5 structure. * @v7: command version 7 structure. + * @v8: command version 8 structure. * @v1.flags: values from &enum iwl_ppag_flags * @v1.gain: table of antenna gain values per chain and sub-band * @v1.reserved: reserved @@ -581,6 +610,8 @@ enum iwl_ppag_flags { * @v7.ppag_config_info: see @struct bios_value_u32 * @v7.gain: table of antenna gain values per chain and sub-band * @v7.reserved: reserved + * @v8.ppag_config_info: see @struct bios_value_u32 + * @v8.gain: table of antenna gain values per chain and sub-band */ union iwl_ppag_table_cmd { struct { @@ -598,6 +629,10 @@ union iwl_ppag_table_cmd { s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2]; s8 reserved[2]; } __packed v7; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_7 */ + struct { + struct bios_value_u32 ppag_config_info; + s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V3]; + } __packed v8; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_8 */ } __packed; #define IWL_PPAG_CMD_V1_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index 60f0a4924ddf..c2bb400c834c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -985,6 +985,7 @@ struct iwl_scan_probe_params_v4 { } __packed; /* SCAN_PROBE_PARAMS_API_S_VER_4 */ #define SCAN_MAX_NUM_CHANS_V3 67 +#define SCAN_MAX_NUM_CHANS_V4 68 /** * struct iwl_scan_channel_params_v4 - channel params @@ -1027,6 +1028,24 @@ struct iwl_scan_channel_params_v7 { struct iwl_scan_channel_cfg_umac channel_config[SCAN_MAX_NUM_CHANS_V3]; } __packed; /* SCAN_CHANNEL_PARAMS_API_S_VER_6 */ +/** + * struct iwl_scan_channel_params_v8 - channel params + * @flags: channel flags &enum iwl_scan_channel_flags + * @count: num of channels in scan request + * @n_aps_override: override the number of APs the FW uses to calculate dwell + * time when adaptive dwell is used. + * Channel k will use n_aps_override[i] when BIT(20 + i) is set in + * channel_config[k].flags + * @channel_config: array of explicit channel configurations + * for 2.4Ghz and 5.2Ghz bands + */ +struct iwl_scan_channel_params_v8 { + u8 flags; + u8 count; + u8 n_aps_override[2]; + struct iwl_scan_channel_cfg_umac channel_config[SCAN_MAX_NUM_CHANS_V4]; +} __packed; /* SCAN_CHANNEL_PARAMS_API_S_VER_8 */ + /** * struct iwl_scan_general_params_v11 - channel params * @flags: &enum iwl_umac_scan_general_flags_v2 @@ -1109,6 +1128,20 @@ struct iwl_scan_req_params_v17 { struct iwl_scan_probe_params_v4 probe_params; } __packed; /* SCAN_REQUEST_PARAMS_API_S_VER_17 - 14 */ +/** + * struct iwl_scan_req_params_v18 - scan request parameters (v18) + * @general_params: &struct iwl_scan_general_params_v11 + * @channel_params: &struct iwl_scan_channel_params_v8 + * @periodic_params: &struct iwl_scan_periodic_parms_v1 + * @probe_params: &struct iwl_scan_probe_params_v4 + */ +struct iwl_scan_req_params_v18 { + struct iwl_scan_general_params_v11 general_params; + struct iwl_scan_channel_params_v8 channel_params; + struct iwl_scan_periodic_parms_v1 periodic_params; + struct iwl_scan_probe_params_v4 probe_params; +} __packed; /* SCAN_REQUEST_PARAMS_API_S_VER_18 */ + /** * struct iwl_scan_req_umac_v12 - scan request command (v12) * @uid: scan id, &enum iwl_umac_scan_uid_offsets @@ -1133,6 +1166,18 @@ struct iwl_scan_req_umac_v17 { struct iwl_scan_req_params_v17 scan_params; } __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_17 - 14 */ +/** + * struct iwl_scan_req_umac_v18 - scan request command (v18) + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @ooc_priority: out of channel priority - &enum iwl_scan_priority + * @scan_params: scan parameters + */ +struct iwl_scan_req_umac_v18 { + __le32 uid; + __le32 ooc_priority; + struct iwl_scan_req_params_v18 scan_params; +} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_18 */ + /** * struct iwl_umac_scan_abort - scan abort command * @uid: scan id, &enum iwl_umac_scan_uid_offsets diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h index 8d9a5058d5a5..68983f6a0026 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h @@ -598,7 +598,6 @@ struct iwl_stats_ntfy_per_sta { } __packed; /* STATISTICS_NTFY_PER_STA_API_S_VER_1 */ #define IWL_STATS_MAX_PHY_OPERATIONAL 3 -#define IWL_STATS_MAX_FW_LINKS (IWL_FW_MAX_LINK_ID + 1) /** * struct iwl_system_statistics_notif_oper - statistics notification @@ -610,7 +609,7 @@ struct iwl_stats_ntfy_per_sta { */ struct iwl_system_statistics_notif_oper { __le32 time_stamp; - struct iwl_stats_ntfy_per_link per_link[IWL_STATS_MAX_FW_LINKS]; + struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS]; struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL]; struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX]; } __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */ @@ -624,7 +623,7 @@ struct iwl_system_statistics_notif_oper { */ struct iwl_system_statistics_part1_notif_oper { __le32 time_stamp; - struct iwl_stats_ntfy_part1_per_link per_link[IWL_STATS_MAX_FW_LINKS]; + struct iwl_stats_ntfy_part1_per_link per_link[IWL_FW_MAX_LINKS]; __le32 per_phy_crc_error_stats[IWL_STATS_MAX_PHY_OPERATIONAL]; } __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_API_S_VER_4 */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 1f26d89fc908..0cffa5493704 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -2933,7 +2933,7 @@ 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)); - queue_delayed_work(system_unbound_wq, &wk_data->wk, + queue_delayed_work(system_dfl_wq, &wk_data->wk, usecs_to_jiffies(delay)); return 0; @@ -3236,7 +3236,7 @@ int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, if (sync) iwl_fw_dbg_collect_sync(fwrt, idx); else - queue_delayed_work(system_unbound_wq, + queue_delayed_work(system_dfl_wq, &fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c index ddd714cff2f4..c2af66899a78 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -128,19 +128,11 @@ static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt) IWL_ERR(fwrt, "0x%08X | %s\n", table.error_id, iwl_fw_lookup_assert_desc(table.error_id)); - IWL_ERR(fwrt, "0x%08X | umac branchlink1\n", table.blink1); - IWL_ERR(fwrt, "0x%08X | umac branchlink2\n", table.blink2); - IWL_ERR(fwrt, "0x%08X | umac interruptlink1\n", table.ilink1); IWL_ERR(fwrt, "0x%08X | umac interruptlink2\n", table.ilink2); IWL_ERR(fwrt, "0x%08X | umac data1\n", table.data1); IWL_ERR(fwrt, "0x%08X | umac data2\n", table.data2); IWL_ERR(fwrt, "0x%08X | umac data3\n", table.data3); - IWL_ERR(fwrt, "0x%08X | umac major\n", table.umac_major); - IWL_ERR(fwrt, "0x%08X | umac minor\n", table.umac_minor); - IWL_ERR(fwrt, "0x%08X | frame pointer\n", table.frame_pointer); - IWL_ERR(fwrt, "0x%08X | stack pointer\n", table.stack_pointer); IWL_ERR(fwrt, "0x%08X | last host cmd\n", table.cmd_header); - IWL_ERR(fwrt, "0x%08X | isr status reg\n", table.nic_isr_pref); } static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_num) @@ -200,39 +192,10 @@ static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_nu IWL_ERR(fwrt, "0x%08X | %-28s\n", table.error_id, iwl_fw_lookup_assert_desc(table.error_id)); - IWL_ERR(fwrt, "0x%08X | trm_hw_status0\n", table.trm_hw_status0); - IWL_ERR(fwrt, "0x%08X | trm_hw_status1\n", table.trm_hw_status1); - IWL_ERR(fwrt, "0x%08X | branchlink2\n", table.blink2); - IWL_ERR(fwrt, "0x%08X | interruptlink1\n", table.ilink1); IWL_ERR(fwrt, "0x%08X | interruptlink2\n", table.ilink2); IWL_ERR(fwrt, "0x%08X | data1\n", table.data1); IWL_ERR(fwrt, "0x%08X | data2\n", table.data2); IWL_ERR(fwrt, "0x%08X | data3\n", table.data3); - IWL_ERR(fwrt, "0x%08X | beacon time\n", table.bcon_time); - IWL_ERR(fwrt, "0x%08X | tsf low\n", table.tsf_low); - IWL_ERR(fwrt, "0x%08X | tsf hi\n", table.tsf_hi); - IWL_ERR(fwrt, "0x%08X | time gp1\n", table.gp1); - IWL_ERR(fwrt, "0x%08X | time gp2\n", table.gp2); - IWL_ERR(fwrt, "0x%08X | uCode revision type\n", table.fw_rev_type); - IWL_ERR(fwrt, "0x%08X | uCode version major\n", table.major); - IWL_ERR(fwrt, "0x%08X | uCode version minor\n", table.minor); - IWL_ERR(fwrt, "0x%08X | hw version\n", table.hw_ver); - IWL_ERR(fwrt, "0x%08X | board version\n", table.brd_ver); - IWL_ERR(fwrt, "0x%08X | hcmd\n", table.hcmd); - IWL_ERR(fwrt, "0x%08X | isr0\n", table.isr0); - IWL_ERR(fwrt, "0x%08X | isr1\n", table.isr1); - IWL_ERR(fwrt, "0x%08X | isr2\n", table.isr2); - IWL_ERR(fwrt, "0x%08X | isr3\n", table.isr3); - IWL_ERR(fwrt, "0x%08X | isr4\n", table.isr4); - IWL_ERR(fwrt, "0x%08X | last cmd Id\n", table.last_cmd_id); - IWL_ERR(fwrt, "0x%08X | wait_event\n", table.wait_event); - IWL_ERR(fwrt, "0x%08X | l2p_control\n", table.l2p_control); - IWL_ERR(fwrt, "0x%08X | l2p_duration\n", table.l2p_duration); - IWL_ERR(fwrt, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); - IWL_ERR(fwrt, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); - IWL_ERR(fwrt, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); - IWL_ERR(fwrt, "0x%08X | timestamp\n", table.u_timestamp); - IWL_ERR(fwrt, "0x%08X | flow_handler\n", table.flow_handler); } /* @@ -264,7 +227,6 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx) struct iwl_trans *trans = fwrt->trans; struct iwl_tcm_error_event_table table = {}; u32 base = fwrt->trans->dbg.tcm_error_event_table[idx]; - int i; u32 flag = idx ? IWL_ERROR_EVENT_TABLE_TCM2 : IWL_ERROR_EVENT_TABLE_TCM1; @@ -275,23 +237,10 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx) IWL_ERR(fwrt, "TCM%d status:\n", idx + 1); IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id); - IWL_ERR(fwrt, "0x%08X | tcm branchlink2\n", table.blink2); - IWL_ERR(fwrt, "0x%08X | tcm interruptlink1\n", table.ilink1); IWL_ERR(fwrt, "0x%08X | tcm interruptlink2\n", table.ilink2); IWL_ERR(fwrt, "0x%08X | tcm data1\n", table.data1); IWL_ERR(fwrt, "0x%08X | tcm data2\n", table.data2); IWL_ERR(fwrt, "0x%08X | tcm data3\n", table.data3); - IWL_ERR(fwrt, "0x%08X | tcm log PC\n", table.logpc); - IWL_ERR(fwrt, "0x%08X | tcm frame pointer\n", table.frame_pointer); - IWL_ERR(fwrt, "0x%08X | tcm stack pointer\n", table.stack_pointer); - IWL_ERR(fwrt, "0x%08X | tcm msg ID\n", table.msgid); - IWL_ERR(fwrt, "0x%08X | tcm ISR status\n", table.isr); - for (i = 0; i < ARRAY_SIZE(table.hw_status); i++) - IWL_ERR(fwrt, "0x%08X | tcm HW status[%d]\n", - table.hw_status[i], i); - for (i = 0; i < ARRAY_SIZE(table.sw_status); i++) - IWL_ERR(fwrt, "0x%08X | tcm SW status[%d]\n", - table.sw_status[i], i); } /* @@ -338,26 +287,10 @@ static void iwl_fwrt_dump_rcm_error_log(struct iwl_fw_runtime *fwrt, int idx) IWL_ERR(fwrt, "RCM%d status:\n", idx + 1); IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id); - IWL_ERR(fwrt, "0x%08X | rcm branchlink2\n", table.blink2); - IWL_ERR(fwrt, "0x%08X | rcm interruptlink1\n", table.ilink1); IWL_ERR(fwrt, "0x%08X | rcm interruptlink2\n", table.ilink2); IWL_ERR(fwrt, "0x%08X | rcm data1\n", table.data1); IWL_ERR(fwrt, "0x%08X | rcm data2\n", table.data2); IWL_ERR(fwrt, "0x%08X | rcm data3\n", table.data3); - IWL_ERR(fwrt, "0x%08X | rcm log PC\n", table.logpc); - IWL_ERR(fwrt, "0x%08X | rcm frame pointer\n", table.frame_pointer); - IWL_ERR(fwrt, "0x%08X | rcm stack pointer\n", table.stack_pointer); - IWL_ERR(fwrt, "0x%08X | rcm msg ID\n", table.msgid); - IWL_ERR(fwrt, "0x%08X | rcm ISR status\n", table.isr); - IWL_ERR(fwrt, "0x%08X | frame HW status\n", table.frame_hw_status); - IWL_ERR(fwrt, "0x%08X | LMAC-to-RCM request mbox\n", - table.mbx_lmac_to_rcm_req); - IWL_ERR(fwrt, "0x%08X | RCM-to-LMAC request mbox\n", - table.mbx_rcm_to_lmac_req); - IWL_ERR(fwrt, "0x%08X | MAC header control\n", table.mh_ctl); - IWL_ERR(fwrt, "0x%08X | MAC header addr1 low\n", table.mh_addr1_lo); - IWL_ERR(fwrt, "0x%08X | MAC header info\n", table.mh_info); - IWL_ERR(fwrt, "0x%08X | MAC header error\n", table.mh_err); } static void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 378788de1d74..f7a6f21267e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -103,6 +103,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_D3_KEK_KCK_ADDR = 67, IWL_UCODE_TLV_CURRENT_PC = 68, IWL_UCODE_TLV_FSEQ_BIN_VERSION = 72, + IWL_UCODE_TLV_CMD_BIOS_TABLE = 73, /* contains sub-sections like PNVM file does (did) */ IWL_UCODE_TLV_PNVM_DATA = 74, @@ -1040,6 +1041,20 @@ struct iwl_fw_cmd_version { u8 notif_ver; } __packed; +/** + * struct iwl_fw_cmd_bios_table - firmware command BIOS revision entry + * @cmd: command ID + * @group: group ID + * @max_acpi_revision: max supported ACPI revision of command. + * @max_uefi_revision: max supported UEFI revision of command. + */ +struct iwl_fw_cmd_bios_table { + u8 cmd; + u8 group; + u8 max_acpi_revision; + u8 max_uefi_revision; +} __packed; + struct iwl_fw_tcm_error_addr { __le32 addr; }; /* FW_TLV_TCM_ERROR_INFO_ADDRS_S */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.c b/drivers/net/wireless/intel/iwlwifi/fw/img.c index c2f4fc83a22c..3cc1e3ae0858 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.c @@ -1,11 +1,41 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright(c) 2019 - 2021 Intel Corporation - * Copyright(c) 2024 Intel Corporation + * Copyright(c) 2024 - 2025 Intel Corporation */ #include #include "img.h" +u8 iwl_fw_lookup_cmd_bios_supported_revision(const struct iwl_fw *fw, + enum bios_source table_source, + u32 cmd_id, u8 def) +{ + const struct iwl_fw_cmd_bios_table *entry; + /* prior to LONG_GROUP, we never used this CMD version API */ + u8 grp = iwl_cmd_groupid(cmd_id) ?: LONG_GROUP; + u8 cmd = iwl_cmd_opcode(cmd_id); + + if (table_source != BIOS_SOURCE_ACPI && + table_source != BIOS_SOURCE_UEFI) + return def; + + if (!fw->ucode_capa.cmd_bios_tables || + !fw->ucode_capa.n_cmd_bios_tables) + return def; + + entry = fw->ucode_capa.cmd_bios_tables; + for (int i = 0; i < fw->ucode_capa.n_cmd_bios_tables; i++, entry++) { + if (entry->group == grp && entry->cmd == cmd) { + if (table_source == BIOS_SOURCE_ACPI) + return entry->max_acpi_revision; + return entry->max_uefi_revision; + } + } + + return def; +} +EXPORT_SYMBOL_GPL(iwl_fw_lookup_cmd_bios_supported_revision); + u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def) { const struct iwl_fw_cmd_version *entry; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index 045a3e009429..94113d1db8e1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -9,6 +9,7 @@ #include #include "api/dbg-tlv.h" +#include "api/nvm-reg.h" #include "file.h" #include "error-dump.h" @@ -57,6 +58,9 @@ struct iwl_ucode_capabilities { const struct iwl_fw_cmd_version *cmd_versions; u32 n_cmd_versions; + + const struct iwl_fw_cmd_bios_table *cmd_bios_tables; + u32 n_cmd_bios_tables; }; static inline bool @@ -274,6 +278,10 @@ iwl_get_ucode_image(const struct iwl_fw *fw, enum iwl_ucode_type ucode_type) return &fw->img[ucode_type]; } +u8 iwl_fw_lookup_cmd_bios_supported_revision(const struct iwl_fw *fw, + enum bios_source table_source, + u32 cmd_id, u8 def); + u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def); u8 iwl_fw_lookup_notif_ver(const struct iwl_fw *fw, u8 grp, u8 cmd, u8 def); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 958e71a3c958..55128caac7ed 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -241,6 +241,10 @@ static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt, int profs[BIOS_SAR_NUM_CHAINS] = { prof_a, prof_b }; int i, j; + if (WARN_ON_ONCE(n_subbands > + ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) + return -EINVAL; + for (i = 0; i < BIOS_SAR_NUM_CHAINS; i++) { struct iwl_sar_profile *prof; @@ -300,132 +304,6 @@ 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; -} - -/* Utility function for iwlmvm and iwlxvt */ -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->info.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), 1); - /* - * 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; - - /* 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; - } - - 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); - cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V1_MASK); - if (fwrt->ppag_bios_rev >= 1) { - /* in this case FW supports revision 0 */ - IWL_DEBUG_RADIO(fwrt, - "PPAG table rev is %d, send truncated table\n", - fwrt->ppag_bios_rev); - } - } else if (cmd_ver == 5) { - num_sub_bands = IWL_NUM_SUB_BANDS_V2; - gain = cmd->v5.gain[0]; - *cmd_size = sizeof(cmd->v5); - cmd->v5.flags = cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V5_MASK); - if (fwrt->ppag_bios_rev == 0) { - /* in this case FW supports revisions 1,2 or 3 */ - IWL_DEBUG_RADIO(fwrt, - "PPAG table rev is 0, send padded table\n"); - } - } else if (cmd_ver == 7) { - num_sub_bands = IWL_NUM_SUB_BANDS_V2; - gain = cmd->v7.gain[0]; - *cmd_size = sizeof(cmd->v7); - cmd->v7.ppag_config_info.hdr.table_source = - fwrt->ppag_bios_source; - cmd->v7.ppag_config_info.hdr.table_revision = - fwrt->ppag_bios_rev; - cmd->v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags); - } 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", - fwrt->ppag_flags); - - if (cmd_ver == 1 && - !fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) { - 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"); - } - - /* The 'flags' field is the same in v1 and v5 so we can just - * use v1 to access it. - */ - IWL_DEBUG_RADIO(fwrt, - "PPAG MODE bits going to be sent: %d\n", - (cmd_ver < 7) ? le32_to_cpu(cmd->v1.flags) : - le32_to_cpu(cmd->v7.ppag_config_info.value)); - - 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, - "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)) { @@ -440,6 +318,27 @@ bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt) } IWL_EXPORT_SYMBOL(iwl_is_ppag_approved); +/* Print the PPAG table as read from BIOS */ +void iwl_bios_print_ppag(struct iwl_fw_runtime *fwrt, int n_subbands) +{ + int i, j; + + IWL_DEBUG_RADIO(fwrt, "PPAG table as read from BIOS:\n"); + IWL_DEBUG_RADIO(fwrt, "PPAG revision = %d\n", fwrt->ppag_bios_rev); + IWL_DEBUG_RADIO(fwrt, "PPAG flags = 0x%x\n", fwrt->ppag_flags); + + if (WARN_ON_ONCE(n_subbands > + ARRAY_SIZE(fwrt->ppag_chains[0].subbands))) + return; + + for (i = 0; i < ARRAY_SIZE(fwrt->ppag_chains); i++) + for (j = 0; j < n_subbands; j++) + IWL_DEBUG_RADIO(fwrt, + "ppag_chains[%d].subbands[%d] = %d\n", + i, j, + fwrt->ppag_chains[i].subbands[j]); +} + bool iwl_is_tas_approved(void) { return dmi_check_system(dmi_tas_approved_list); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 1489031687b7..6fffc032efd3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -21,10 +21,11 @@ */ #define BIOS_SAR_MAX_CHAINS_PER_PROFILE 4 #define BIOS_SAR_NUM_CHAINS 2 -#define BIOS_SAR_MAX_SUB_BANDS_NUM 11 +#define BIOS_SAR_MAX_SUB_BANDS_NUM 12 +#define BIOS_PPAG_MAX_SUB_BANDS_NUM 12 #define BIOS_GEO_NUM_CHAINS 2 -#define BIOS_GEO_MAX_NUM_BANDS 3 +#define BIOS_GEO_MAX_NUM_BANDS 4 #define BIOS_GEO_MAX_PROFILE_NUM 8 #define BIOS_GEO_MIN_PROFILE_NUM 3 @@ -100,7 +101,7 @@ struct iwl_geo_profile { /* Same thing as with SAR, all revisions fit in revision 2 */ struct iwl_ppag_chain { - s8 subbands[BIOS_SAR_MAX_SUB_BANDS_NUM]; + s8 subbands[BIOS_PPAG_MAX_SUB_BANDS_NUM]; }; struct iwl_tas_data { @@ -180,6 +181,9 @@ enum iwl_dsm_masks_reg { struct iwl_fw_runtime; +/* Print the PPAG table as read from BIOS */ +void iwl_bios_print_ppag(struct iwl_fw_runtime *fwrt, int n_subbands); + bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt); int iwl_sar_geo_fill_table(struct iwl_fw_runtime *fwrt, @@ -190,10 +194,6 @@ 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); bool iwl_is_tas_approved(void); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index ff186fb2e0da..d80ae610e56c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -106,13 +106,14 @@ struct iwl_txf_iter_data { * @cur_fw_img: current firmware image, must be maintained by * the driver by calling &iwl_fw_set_current_image() * @dump: debug dump data - * @uats_table: AP type table - * @uats_valid: is AP type table valid + * @ap_type_cmd: AP type tables (for enablement on 6 GHz) + * @ap_type_cmd_valid: if &ap_type_cmd is valid * @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 + * @geo_bios_source: see &enum bios_source * @phy_filters: specific phy filters as read from WPFC BIOS table * @ppag_bios_rev: PPAG BIOS revision * @ppag_bios_source: see &enum bios_source @@ -204,6 +205,7 @@ struct iwl_fw_runtime { u8 sar_chain_b_profile; u8 reduced_power_flags; struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM]; + enum bios_source geo_bios_source; u32 geo_rev; u32 geo_num_profiles; bool geo_enabled; @@ -213,8 +215,8 @@ struct iwl_fw_runtime { u8 ppag_bios_source; struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; - struct iwl_mcc_allowed_ap_type_cmd uats_table; - bool uats_valid; + struct iwl_mcc_allowed_ap_type_cmd ap_type_cmd; + bool ap_type_cmd_valid; u8 uefi_tables_lock_status; struct iwl_phy_specific_cfg phy_filters; enum bios_source dsm_source; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index a7ba86e06c09..2ef0a7a920ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -402,11 +402,11 @@ static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data, if (uats_data->revision != 1) return -EINVAL; - memcpy(fwrt->uats_table.mcc_to_ap_type_map, + memcpy(fwrt->ap_type_cmd.mcc_to_ap_type_map, uats_data->mcc_to_ap_type_map, - sizeof(fwrt->uats_table.mcc_to_ap_type_map)); + sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_map)); - fwrt->uats_valid = true; + fwrt->ap_type_cmd_valid = true; return 0; } @@ -429,12 +429,61 @@ void iwl_uefi_get_uats_table(struct iwl_trans *trans, } IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table); -static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt, - struct uefi_sar_profile *uefi_sar_prof, - u8 prof_index, bool enabled) +void iwl_uefi_get_uneb_table(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt) { - memcpy(&fwrt->sar_profiles[prof_index].chains, uefi_sar_prof, - sizeof(struct uefi_sar_profile)); + struct uefi_cnv_wlan_uneb_data *data; + + data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UNEB_NAME, + "UNEB", sizeof(*data), NULL); + if (IS_ERR(data)) + return; + + if (data->revision != 1) { + IWL_DEBUG_RADIO(fwrt, + "Cannot read UNEB table. rev is invalid\n"); + goto out; + } + + BUILD_BUG_ON(sizeof(data->mcc_to_ap_type_map) != + sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map)); + + memcpy(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map, + data->mcc_to_ap_type_map, + sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map)); + + fwrt->ap_type_cmd_valid = true; + +out: + kfree(data); +} +IWL_EXPORT_SYMBOL(iwl_uefi_get_uneb_table); + +static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt, + const u8 *vals, u8 prof_index, + u8 num_subbands, bool enabled) +{ + struct iwl_sar_profile *sar_prof = &fwrt->sar_profiles[prof_index]; + + /* + * Make sure fwrt has enough room to hold the data + * coming from the UEFI table + */ + if (WARN_ON(ARRAY_SIZE(sar_prof->chains) * + ARRAY_SIZE(sar_prof->chains[0].subbands) < + UEFI_SAR_MAX_CHAINS_PER_PROFILE * num_subbands)) + return; + + BUILD_BUG_ON(ARRAY_SIZE(sar_prof->chains) != + UEFI_SAR_MAX_CHAINS_PER_PROFILE); + + for (int chain = 0; + chain < UEFI_SAR_MAX_CHAINS_PER_PROFILE; + chain++) { + for (int subband = 0; subband < num_subbands; subband++) + sar_prof->chains[chain].subbands[subband] = + vals[chain * num_subbands + subband]; + } fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK; } @@ -442,24 +491,46 @@ static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt, int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt) { struct uefi_cnv_var_wrds *data; + unsigned long size; + unsigned long expected_size; + int num_subbands; int ret = 0; data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME, - "WRDS", sizeof(*data), NULL); + "WRDS", + UEFI_SAR_WRDS_TABLE_SIZE_REV2, + &size); + 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", + switch (data->revision) { + case 2: + expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV2; + num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2; + break; + case 3: + expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV3; + num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3; + break; + default: + IWL_DEBUG_RADIO(fwrt, + "Unsupported UEFI WRDS revision:%d\n", data->revision); + ret = -EINVAL; + goto out; + } + + if (size != expected_size) { + ret = -EINVAL; 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); + iwl_uefi_set_sar_profile(fwrt, data->vals, 0, + num_subbands, data->mode); out: kfree(data); return ret; @@ -468,21 +539,40 @@ int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt) int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt) { struct uefi_cnv_var_ewrd *data; + unsigned long expected_size; int i, ret = 0; + unsigned long size; + int num_subbands; + int profile_size; data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME, - "EWRD", sizeof(*data), NULL); + "EWRD", + UEFI_SAR_EWRD_TABLE_SIZE_REV2, + &size); 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", + switch (data->revision) { + case 2: + expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV2; + num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2; + profile_size = UEFI_SAR_PROFILE_SIZE_REV2; + break; + case 3: + expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV3; + num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3; + profile_size = UEFI_SAR_PROFILE_SIZE_REV3; + break; + default: + IWL_DEBUG_RADIO(fwrt, + "Unsupported UEFI EWRD revision:%d\n", data->revision); + ret = -EINVAL; goto out; } - if (data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) { + if (size != expected_size || + data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) { ret = -EINVAL; goto out; } @@ -492,8 +582,8 @@ int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt) * 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); + iwl_uefi_set_sar_profile(fwrt, &data->vals[i * profile_size], + i + 1, num_subbands, data->mode); out: kfree(data); @@ -503,20 +593,39 @@ int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt) int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) { struct uefi_cnv_var_wgds *data; - int i, ret = 0; + unsigned long expected_size; + unsigned long size; + int profile_size; + int n_subbands; + int ret = 0; data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME, - "WGDS", sizeof(*data), NULL); + "WGDS", UEFI_WGDS_TABLE_SIZE_REV3, + &size); if (IS_ERR(data)) return -EINVAL; - if (data->revision != IWL_UEFI_WGDS_REVISION) { + switch (data->revision) { + case 3: + expected_size = UEFI_WGDS_TABLE_SIZE_REV3; + n_subbands = UEFI_GEO_NUM_BANDS_REV3; + break; + case 4: + expected_size = UEFI_WGDS_TABLE_SIZE_REV4; + n_subbands = UEFI_GEO_NUM_BANDS_REV4; + break; + default: ret = -EINVAL; IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n", data->revision); goto out; } + if (size != expected_size) { + ret = -EINVAL; + goto out; + } + if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM || data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) { ret = -EINVAL; @@ -525,10 +634,31 @@ int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) goto out; } + if (WARN_ON(BIOS_GEO_MAX_PROFILE_NUM > + ARRAY_SIZE(fwrt->geo_profiles) || + n_subbands > ARRAY_SIZE(fwrt->geo_profiles[0].bands) || + BIOS_GEO_NUM_CHAINS > + ARRAY_SIZE(fwrt->geo_profiles[0].bands[0].chains))) { + ret = -EINVAL; + 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_bios_source = BIOS_SOURCE_UEFI; + profile_size = 3 * n_subbands; + for (int prof = 0; prof < data->num_profiles; prof++) { + const u8 *val = &data->vals[profile_size * prof]; + struct iwl_geo_profile *geo_prof = &fwrt->geo_profiles[prof]; + + for (int subband = 0; subband < n_subbands; subband++) { + geo_prof->bands[subband].max = *val++; + + for (int chain = 0; + chain < BIOS_GEO_NUM_CHAINS; + chain++) + geo_prof->bands[subband].chains[chain] = *val++; + } + } fwrt->geo_num_profiles = data->num_profiles; fwrt->geo_enabled = true; @@ -540,28 +670,66 @@ int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) { struct uefi_cnv_var_ppag *data; + int n_subbands; + u32 valid_rev; 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; + "PPAG", UEFI_PPAG_DATA_SIZE_V5, + NULL); + if (!IS_ERR(data)) { + n_subbands = UEFI_PPAG_SUB_BANDS_NUM_REV5; + valid_rev = BIT(5); - if (data->revision < IWL_UEFI_MIN_PPAG_REV || - data->revision > IWL_UEFI_MAX_PPAG_REV) { + goto parse_table; + } + + data = iwl_uefi_get_verified_variable(fwrt->trans, + IWL_UEFI_PPAG_NAME, + "PPAG", + UEFI_PPAG_DATA_SIZE_V4, + NULL); + if (!IS_ERR(data)) { + n_subbands = UEFI_PPAG_SUB_BANDS_NUM_REV4; + /* revisions 1-4 have all the same size */ + valid_rev = BIT(1) | BIT(2) | BIT(3) | BIT(4); + + goto parse_table; + } + + return -EINVAL; + +parse_table: + if (!(BIT(data->revision) & valid_rev)) { ret = -EINVAL; - IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n", + IWL_DEBUG_RADIO(fwrt, + "Unsupported UEFI PPAG revision:%d\n", data->revision); goto out; } + /* + * Make sure fwrt has enough room to hold + * data coming from the UEFI table + */ + if (WARN_ON(ARRAY_SIZE(fwrt->ppag_chains) * + ARRAY_SIZE(fwrt->ppag_chains[0].subbands) < + UEFI_PPAG_NUM_CHAINS * n_subbands)) { + ret = -EINVAL; + goto out; + } + fwrt->ppag_bios_rev = data->revision; fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes, fwrt->ppag_bios_rev); - BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains)); - memcpy(&fwrt->ppag_chains, &data->ppag_chains, - sizeof(data->ppag_chains)); + for (int chain = 0; chain < UEFI_PPAG_NUM_CHAINS; chain++) { + for (int subband = 0; subband < n_subbands; subband++) + fwrt->ppag_chains[chain].subbands[subband] = + data->vals[chain * n_subbands + subband]; + } + + iwl_bios_print_ppag(fwrt, n_subbands); fwrt->ppag_bios_source = BIOS_SOURCE_UEFI; out: kfree(data); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 349ac1505ad7..474f06db4d43 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -25,16 +25,12 @@ #define IWL_UEFI_PUNCTURING_NAME L"UefiCnvWlanPuncturing" #define IWL_UEFI_DSBR_NAME L"UefiCnvCommonDSBR" #define IWL_UEFI_WPFC_NAME L"WPFC" +#define IWL_UEFI_UNEB_NAME L"CnvUefiWlanUNEB" #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 -#define IWL_UEFI_MIN_PPAG_REV 1 -#define IWL_UEFI_MAX_PPAG_REV 4 #define IWL_UEFI_MIN_WTAS_REVISION 1 #define IWL_UEFI_MAX_WTAS_REVISION 2 #define IWL_UEFI_SPLC_REVISION 0 @@ -63,6 +59,9 @@ struct uefi_cnv_wlan_uats_data { u8 mcc_to_ap_type_map[IWL_UATS_MAP_SIZE - 1]; } __packed; +/* UNEB's layout is identical to UATS's */ +#define uefi_cnv_wlan_uneb_data uefi_cnv_wlan_uats_data + struct uefi_cnv_common_step_data { u8 revision; u8 step_mode; @@ -72,68 +71,135 @@ 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; +#define UEFI_PPAG_SUB_BANDS_NUM_REV4 11 +#define UEFI_PPAG_SUB_BANDS_NUM_REV5 12 +#define UEFI_PPAG_NUM_CHAINS 2 -/* +#define UEFI_SAR_SUB_BANDS_NUM_REV2 11 +#define UEFI_SAR_SUB_BANDS_NUM_REV3 12 + +#define UEFI_SAR_MAX_CHAINS_PER_PROFILE 4 + +#define UEFI_GEO_NUM_BANDS_REV3 3 +#define UEFI_GEO_NUM_BANDS_REV4 4 + +/** * 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 + * @vals: values for sar profile #1 as an array: + * vals[chain * num_of_subbands + subband] will return the right value. + * num_of_subbands depends on the revision. For revision 3, it is + * %UEFI_SAR_SUB_BANDS_NUM_REV3, for earlier revision, it is + * %UEFI_SAR_SUB_BANDS_NUM_REV2. + * The max number of chains is currently 2 */ struct uefi_cnv_var_wrds { u8 revision; u32 mode; - struct uefi_sar_profile sar_profile; + u8 vals[]; } __packed; -/* +#define UEFI_SAR_PROFILE_SIZE_REV2 \ + (sizeof(u8) * UEFI_SAR_MAX_CHAINS_PER_PROFILE * \ + UEFI_SAR_SUB_BANDS_NUM_REV2) + +#define UEFI_SAR_PROFILE_SIZE_REV3 \ + (sizeof(u8) * UEFI_SAR_MAX_CHAINS_PER_PROFILE * \ + UEFI_SAR_SUB_BANDS_NUM_REV3) + +#define UEFI_SAR_WRDS_TABLE_SIZE_REV2 \ + (offsetof(struct uefi_cnv_var_wrds, vals) + \ + UEFI_SAR_PROFILE_SIZE_REV2) + +#define UEFI_SAR_WRDS_TABLE_SIZE_REV3 \ + (offsetof(struct uefi_cnv_var_wrds, vals) + \ + UEFI_SAR_PROFILE_SIZE_REV3) + +/** * 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) + * @vals: the additional SAR profiles (#2-#4) as an array of SAR profiles. + * A SAR profile is defined the &struct uefi_cnv_var_wrds::vals. The size + * of each profile depends on the number of subbands which depends on the + * revision. This is explained in &struct uefi_cnv_var_wrds. */ struct uefi_cnv_var_ewrd { u8 revision; u32 mode; u32 num_profiles; - struct uefi_sar_profile sar_profiles[BIOS_SAR_MAX_PROFILE_NUM - 1]; + u8 vals[]; } __packed; -/* +#define UEFI_SAR_EWRD_TABLE_SIZE_REV2 \ + (offsetof(struct uefi_cnv_var_ewrd, vals) + \ + UEFI_SAR_PROFILE_SIZE_REV2 * (BIOS_SAR_MAX_PROFILE_NUM - 1)) + +#define UEFI_SAR_EWRD_TABLE_SIZE_REV3 \ + (offsetof(struct uefi_cnv_var_ewrd, vals) + \ + UEFI_SAR_PROFILE_SIZE_REV3 * (BIOS_SAR_MAX_PROFILE_NUM - 1)) + +/** * 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. + * @vals: a per-profile table of the offsets to add to SAR values. This is an + * array of profiles, each profile is an array of + * &struct iwl_geo_profile_band, one for each subband. + * There are %UEFI_GEO_NUM_BANDS_REV3 or %UEFI_GEO_NUM_BANDS_REV4 subbands + * depending on the revision. */ struct uefi_cnv_var_wgds { u8 revision; u8 num_profiles; - struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM]; + u8 vals[]; } __packed; -/* +/* struct iwl_geo_profile_band is 3 bytes-long, but since it is not packed, + * we can't use sizeof() + */ +#define UEFI_WGDS_PROFILE_SIZE_REV3 (sizeof(u8) * 3 * UEFI_GEO_NUM_BANDS_REV3) + +#define UEFI_WGDS_PROFILE_SIZE_REV4 (sizeof(u8) * 3 * UEFI_GEO_NUM_BANDS_REV4) + +#define UEFI_WGDS_TABLE_SIZE_REV3 \ + (offsetof(struct uefi_cnv_var_wgds, vals) + \ + UEFI_WGDS_PROFILE_SIZE_REV3 * BIOS_GEO_MAX_PROFILE_NUM) + +#define UEFI_WGDS_TABLE_SIZE_REV4 \ + (offsetof(struct uefi_cnv_var_wgds, vals) + \ + UEFI_WGDS_PROFILE_SIZE_REV4 * BIOS_GEO_MAX_PROFILE_NUM) + +/** * struct uefi_cnv_var_ppag - PPAG table as defined in UEFI * @revision: the revision of the table * @ppag_modes: values from &enum iwl_ppag_flags - * @ppag_chains: the PPAG values per chain and band + * @vals: the PPAG values per chain and band as an array. + * vals[chain * num_of_subbands + subband] will return the right value. + * num_of_subbands depends on the revision. For revision 5, it is + * %UEFI_PPAG_SUB_BANDS_NUM_REV5, for earlier revision it is + * %UEFI_PPAG_SUB_BANDS_NUM_REV4. + * the max number of chains is currently 2 */ struct uefi_cnv_var_ppag { u8 revision; u32 ppag_modes; - struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; + s8 vals[]; } __packed; -/* struct uefi_cnv_var_wtas - WTAS tabled as defined in UEFI +#define UEFI_PPAG_DATA_SIZE_V4 \ + (offsetof(struct uefi_cnv_var_ppag, vals) + \ + sizeof(s8) * UEFI_PPAG_NUM_CHAINS * UEFI_PPAG_SUB_BANDS_NUM_REV4) +#define UEFI_PPAG_DATA_SIZE_V5 \ + (offsetof(struct uefi_cnv_var_ppag, vals) + \ + sizeof(s8) * UEFI_PPAG_NUM_CHAINS * UEFI_PPAG_SUB_BANDS_NUM_REV5) + +/** + * 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 @@ -146,7 +212,8 @@ 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 +/** + * 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 */ @@ -155,7 +222,8 @@ struct uefi_cnv_var_splc { u32 default_pwr_limit; } __packed; -/* struct uefi_cnv_var_wrdd - WRDD table as defined in UEFI +/** + * 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 */ @@ -164,7 +232,8 @@ struct uefi_cnv_var_wrdd { u32 mcc; } __packed; -/* struct uefi_cnv_var_eckv - ECKV table as defined in UEFI +/** + * 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 */ @@ -175,7 +244,8 @@ struct uefi_cnv_var_eckv { #define UEFI_MAX_DSM_FUNCS 32 -/* struct uefi_cnv_var_general_cfg - DSM-like table as defined in UEFI +/** + * 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 */ @@ -185,7 +255,9 @@ struct uefi_cnv_var_general_cfg { } __packed; #define IWL_UEFI_WBEM_REV0_MASK (BIT(0) | BIT(1)) -/* struct uefi_cnv_wlan_wbem_data - Bandwidth enablement per MCC as defined + +/** + * struct uefi_cnv_wlan_wbem_data - Bandwidth enablement per MCC as defined * in UEFI * @revision: the revision of the table * @wbem_320mhz_per_mcc: enablement of 320MHz bandwidth per MCC @@ -274,6 +346,8 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); void iwl_uefi_get_uats_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); +void iwl_uefi_get_uneb_table(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt); int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt); int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value); int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt); @@ -373,6 +447,11 @@ iwl_uefi_get_uats_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { } +static inline void +iwl_uefi_get_uneb_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) +{ +} + static inline int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 45cf2bc68e41..5f40cd15e27f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -85,7 +85,6 @@ enum iwl_nvm_type { #define IWL_WATCHDOG_DISABLED 0 #define IWL_DEF_WD_TIMEOUT 2500 #define IWL_LONG_WD_TIMEOUT 10000 -#define IWL_MAX_WD_TIMEOUT 120000 #define IWL_DEFAULT_MAX_TX_POWER 22 #define IWL_TX_CSUM_NETIF_FLAGS (NETIF_F_IPV6_CSUM | NETIF_F_IP_CSUM |\ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 475b3e417efa..d5ded4d3a30b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -133,6 +133,7 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv) kfree(drv->fw.dbg.mem_tlv); kfree(drv->fw.iml); kfree(drv->fw.ucode_capa.cmd_versions); + kfree(drv->fw.ucode_capa.cmd_bios_tables); kfree(drv->fw.phy_integration_ver); kfree(drv->trans->dbg.pc_data); drv->trans->dbg.pc_data = NULL; @@ -1314,7 +1315,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(u32)) goto invalid_tlv_len; if (le32_to_cpup((const __le32 *)tlv_data) > - IWL_FW_MAX_LINK_ID + 1) { + IWL_FW_MAX_LINKS) { IWL_ERR(drv, "%d is an invalid number of links\n", le32_to_cpup((const __le32 *)tlv_data)); @@ -1426,6 +1427,26 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, return -ENOMEM; drv->fw.pnvm_size = tlv_len; break; + case IWL_UCODE_TLV_CMD_BIOS_TABLE: + if (tlv_len % sizeof(struct iwl_fw_cmd_bios_table)) { + IWL_ERR(drv, + "Invalid length for command bios table: %u\n", + tlv_len); + return -EINVAL; + } + + if (capa->cmd_bios_tables) { + IWL_ERR(drv, "Duplicate TLV type 0x%02X detected\n", + tlv_type); + return -EINVAL; + } + capa->cmd_bios_tables = kmemdup(tlv_data, tlv_len, + GFP_KERNEL); + if (!capa->cmd_bios_tables) + return -ENOMEM; + capa->n_cmd_bios_tables = + tlv_len / sizeof(struct iwl_fw_cmd_bios_table); + break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 6d235c417fdd..8f3f651451bb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -23,6 +23,8 @@ #include "fw/api/commands.h" #include "fw/api/cmdhdr.h" #include "fw/img.h" +#include "fw/dbg.h" + #include "mei/iwl-mei.h" /* NVM offsets (in words) definitions */ @@ -1702,6 +1704,11 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans, band); new_rule = false; + if (IWL_FW_CHECK(trans, !center_freq, + "Invalid channel %d (idx %d) in NVM\n", + nvm_chan[ch_idx], ch_idx)) + continue; + if (!(ch_flags & NVM_CHANNEL_VALID)) { iwl_nvm_print_channel_flags(dev, IWL_DL_LAR, nvm_chan[ch_idx], ch_flags); @@ -2031,7 +2038,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, if (empty_otp) IWL_INFO(trans, "OTP is empty\n"); - nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS); + nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V2); if (!nvm) { ret = -ENOMEM; goto out; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 89901786fd68..16b2c313e72b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -138,7 +138,7 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans) IWL_RESET_MODE_FUNC_RESET, IWL_RESET_MODE_PROD_RESET, }; - static const enum iwl_reset_mode escalation_list_sc[] = { + static const enum iwl_reset_mode escalation_list_top[] = { IWL_RESET_MODE_SW_RESET, IWL_RESET_MODE_REPROBE, IWL_RESET_MODE_REPROBE, @@ -159,14 +159,14 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans) if (trans->request_top_reset) { trans->request_top_reset = 0; - if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) + if (iwl_trans_is_top_reset_supported(trans)) return IWL_RESET_MODE_TOP_RESET; return IWL_RESET_MODE_PROD_RESET; } - if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) { - escalation_list = escalation_list_sc; - escalation_list_size = ARRAY_SIZE(escalation_list_sc); + if (iwl_trans_is_top_reset_supported(trans)) { + escalation_list = escalation_list_top; + escalation_list_size = ARRAY_SIZE(escalation_list_top); } else { escalation_list = escalation_list_old; escalation_list_size = ARRAY_SIZE(escalation_list_old); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 688f9fee2821..61e4f4776dcb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1088,7 +1088,7 @@ static inline void iwl_trans_schedule_reset(struct iwl_trans *trans, */ trans->restart.during_reset = test_bit(STATUS_IN_SW_RESET, &trans->status); - queue_delayed_work(system_unbound_wq, &trans->restart.wk, 0); + queue_delayed_work(system_dfl_wq, &trans->restart.wk, 0); } static inline void iwl_trans_fw_error(struct iwl_trans *trans, @@ -1258,4 +1258,22 @@ bool iwl_trans_is_pm_supported(struct iwl_trans *trans); bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans); +static inline bool iwl_trans_is_top_reset_supported(struct iwl_trans *trans) +{ + /* not supported before Sc family */ + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC) + return false; + + /* for Sc family only supported for Sc2/Sc2f */ + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_SC && + CSR_HW_REV_TYPE(trans->info.hw_rev) == IWL_CFG_MAC_TYPE_SC) + return false; + + /* so far these numbers are increasing - not before Pe */ + if (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) < IWL_CFG_RF_TYPE_PE) + return false; + + return true; +} + #endif /* __iwl_trans_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h index 5d23a618ae3c..e2a5eecc18c3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h @@ -36,7 +36,6 @@ #define IWL_MLD_PS_HEAVY_RX_THLD_PACKETS 8 #define IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC 30 -#define IWL_MLD_SCAN_EXPIRE_TIME_SEC 20 #define IWL_MLD_TPT_COUNT_WINDOW (5 * HZ) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c index 19da521a4bab..7b1fb84a641c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -513,7 +513,7 @@ static int iwl_mld_config_fw(struct iwl_mld *mld) return ret; iwl_mld_init_tas(mld); - iwl_mld_init_uats(mld); + iwl_mld_init_ap_type_tables(mld); return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index 29df747c8938..46c8d943fd55 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -61,20 +61,27 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif) static int iwl_mld_send_mac_cmd(struct iwl_mld *mld, struct iwl_mac_config_cmd *cmd) { + u16 cmd_id = WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD); + int len = sizeof(*cmd); int ret; lockdep_assert_wiphy(mld->wiphy); - ret = iwl_mld_send_cmd_pdu(mld, - WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), - cmd); + if (iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0) < 4) { + if (WARN_ON(cmd->mac_type == cpu_to_le32(FW_MAC_TYPE_NAN))) + return -EINVAL; + + len = sizeof(struct iwl_mac_config_cmd_v3); + } + + ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, len); if (ret) IWL_ERR(mld, "Failed to send MAC_CONFIG_CMD ret = %d\n", ret); return ret; } -int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif) +static int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif) { switch (vif->type) { case NL80211_IFTYPE_STATION: @@ -386,7 +393,7 @@ static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy, iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif)); } -IWL_MLD_ALLOC_FN(vif, vif) +static IWL_MLD_ALLOC_FN(vif, vif) /* Constructor function for struct iwl_mld_vif */ static void @@ -397,6 +404,7 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) lockdep_assert_wiphy(mld->wiphy); mld_vif->mld = mld; + mld_vif->fw_id = IWL_MLD_INVALID_FW_ID; mld_vif->roc_activity = ROC_NUM_ACTIVITIES; if (!mld->fw_status.in_hw_restart) { @@ -444,6 +452,10 @@ void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) lockdep_assert_wiphy(mld->wiphy); + /* NAN interface type is not known to FW */ + if (vif->type == NL80211_IFTYPE_NAN) + return; + iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE); if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif))) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index 62fca166afd1..8dfc79fed253 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_iface_h__ #define __iwl_mld_iface_h__ @@ -33,6 +33,7 @@ enum iwl_mld_cca_40mhz_wa_status { * there is an indication that a non-BSS interface is to be added. * @IWL_MLD_EMLSR_BLOCKED_TPT: throughput is too low to make EMLSR worthwhile * @IWL_MLD_EMLSR_BLOCKED_NAN: NAN is preventing EMLSR. + * @IWL_MLD_EMLSR_BLOCKED_TDLS: TDLS connection is preventing EMLSR. */ enum iwl_mld_emlsr_blocked { IWL_MLD_EMLSR_BLOCKED_PREVENTION = 0x1, @@ -42,6 +43,7 @@ enum iwl_mld_emlsr_blocked { IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS = 0x10, IWL_MLD_EMLSR_BLOCKED_TPT = 0x20, IWL_MLD_EMLSR_BLOCKED_NAN = 0x40, + IWL_MLD_EMLSR_BLOCKED_TDLS = 0x80, }; /** @@ -201,6 +203,15 @@ iwl_mld_vif_to_mac80211(struct iwl_mld_vif *mld_vif) return container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); } +/* Call only for interfaces that were added to the driver! */ +static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif) +{ + if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld_vif->mld->fw_id_to_vif))) + return false; + + return true; +} + #define iwl_mld_link_dereference_check(mld_vif, link_id) \ rcu_dereference_check((mld_vif)->link[link_id], \ lockdep_is_held(&mld_vif->mld->wiphy->mtx)) @@ -219,8 +230,6 @@ iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf) return iwl_mld_link_dereference_check(mld_vif, bss_conf->link_id); } -int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif); - /* Cleanup function for struct iwl_mld_vif, will be called in restart */ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif); int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index b5430e8a73d6..b66e84d2365f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -437,7 +437,7 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link) iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); } -IWL_MLD_ALLOC_FN(link, bss_conf) +static IWL_MLD_ALLOC_FN(link, bss_conf) /* Constructor function for struct iwl_mld_link */ static int diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index 9e4da8e4de93..ca691259fc5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -40,6 +40,7 @@ struct iwl_probe_resp_data { * @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS. * @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS. * @mon_sta: station used for TX injection in monitor interface. + * @last_cqm_rssi_event: rssi of the last cqm rssi event * @average_beacon_energy: average beacon energy for beacons received during * client connections * @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs, @@ -66,6 +67,7 @@ struct iwl_mld_link { struct iwl_mld_int_sta bcast_sta; struct iwl_mld_int_sta mcast_sta; struct iwl_mld_int_sta mon_sta; + int last_cqm_rssi_event; /* we can only have 2 GTK + 2 IGTK + 2 BIGTK active at a time */ struct ieee80211_key_conf *ap_early_keys[6]; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c index d39dd36b08e3..a4ddc32e2860 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "mld.h" #include "iface.h" @@ -77,9 +77,12 @@ static void iwl_mld_low_latency_iter(void *_data, u8 *mac, bool prev = mld_vif->low_latency_causes & LOW_LATENCY_TRAFFIC; bool low_latency; - if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->low_latency.result))) + if (!iwl_mld_vif_fw_id_valid(mld_vif)) return; + BUILD_BUG_ON(ARRAY_SIZE(mld->fw_id_to_vif) != + ARRAY_SIZE(mld->low_latency.result)); + low_latency = mld->low_latency.result[mld_vif->fw_id]; if (prev != low_latency) @@ -272,8 +275,10 @@ void iwl_mld_low_latency_update_counters(struct iwl_mld *mld, if (WARN_ON_ONCE(!mld->low_latency.pkts_counters)) return; - if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) || - queue >= mld->trans->info.num_rxqs)) + if (!iwl_mld_vif_fw_id_valid(mld_vif)) + return; + + if (WARN_ON_ONCE(queue >= mld->trans->info.num_rxqs)) return; if (mld->low_latency.stopped) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 0c53d6bd9651..e3aec814aa0d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -754,6 +754,30 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw, mld->monitor.phy.valid = false; } +static +int iwl_mld_mac80211_change_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, bool p2p) +{ + enum nl80211_iftype old_type = vif->type; + bool old_p2p = vif->p2p; + int ret; + + iwl_mld_mac80211_remove_interface(hw, vif); + + /* set the new type for adding it cleanly */ + vif->type = new_type; + vif->p2p = p2p; + + ret = iwl_mld_mac80211_add_interface(hw, vif); + + /* restore for mac80211, it will change it again */ + vif->type = old_type; + vif->p2p = old_p2p; + + return ret; +} + struct iwl_mld_mc_iter_data { struct iwl_mld *mld; int port_id; @@ -1124,6 +1148,8 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw, /* Now activate the link */ if (iwl_mld_can_activate_link(mld, vif, link)) { + iwl_mld_tlc_update_phy(mld, vif, link); + ret = iwl_mld_activate_link(mld, link); if (ret) goto err; @@ -1185,6 +1211,8 @@ void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, RCU_INIT_POINTER(mld_link->chan_ctx, NULL); + iwl_mld_tlc_update_phy(mld, vif, link); + /* in the non-MLO case, remove/re-add the link to clean up FW state. * In MLO, it'll be done in drv_change_vif_link */ @@ -1727,14 +1755,23 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, return -EBUSY; } - ret = iwl_mld_add_sta(mld, sta, vif, STATION_TYPE_PEER); + ret = iwl_mld_add_sta(mld, sta, vif); if (ret) return ret; - /* just added first TDLS STA, so disable PM */ - if (sta->tdls && tdls_count == 0) + /* just added first TDLS STA, so disable PM and block EMLSR */ + if (sta->tdls && tdls_count == 0) { iwl_mld_update_mac_power(mld, vif, false); + /* TDLS requires single-link operation with + * direct peer communication. + * Block and exit EMLSR when TDLS is established. + */ + iwl_mld_block_emlsr(mld, vif, + IWL_MLD_EMLSR_BLOCKED_TDLS, + iwl_mld_get_primary_link(vif)); + } + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) mld_vif->ap_sta = sta; @@ -1870,8 +1907,14 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld, iwl_mld_remove_sta(mld, sta); if (sta->tdls && iwl_mld_tdls_sta_count(mld) == 0) { - /* just removed last TDLS STA, so enable PM */ + /* just removed last TDLS STA, so enable PM + * and unblock EMLSR + */ iwl_mld_update_mac_power(mld, vif, false); + + /* Unblock EMLSR when TDLS connection is torn down */ + iwl_mld_unblock_emlsr(mld, vif, + IWL_MLD_EMLSR_BLOCKED_TDLS); } } else { return -EINVAL; @@ -2716,6 +2759,7 @@ const struct ieee80211_ops iwl_mld_hw_ops = { .get_antenna = iwl_mld_get_antenna, .set_antenna = iwl_mld_set_antenna, .add_interface = iwl_mld_mac80211_add_interface, + .change_interface = iwl_mld_mac80211_change_interface, .remove_interface = iwl_mld_mac80211_remove_interface, .conf_tx = iwl_mld_mac80211_conf_tx, .prepare_multicast = iwl_mld_mac80211_prepare_multicast, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h index 66c7a7d31409..606cb64f8ea4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -205,7 +205,7 @@ struct iwl_mld { /* Add here fields that need clean up on restart */ struct_group(zeroed_on_hw_restart, - struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINK_ID + 1]; + struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINKS]; struct ieee80211_vif __rcu *fw_id_to_vif[NUM_MAC_INDEX_DRIVER]; struct ieee80211_txq __rcu *fw_id_to_txq[IWL_MAX_TVQM_QUEUES]; u8 used_phy_ids: NUM_PHY_CTX; @@ -530,9 +530,9 @@ void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, #define IWL_MLD_INVALID_FW_ID 0xff #define IWL_MLD_ALLOC_FN(_type, _mac80211_type) \ -static int \ +int \ iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \ - u8 *fw_id, \ + u8 *fw_id, \ struct ieee80211_##_mac80211_type *mac80211_ptr) \ { \ u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8(); \ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index f842f5183223..9362e02d9e76 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -13,7 +13,8 @@ HOW(NON_BSS) \ HOW(TMP_NON_BSS) \ HOW(TPT) \ - HOW(NAN) + HOW(NAN) \ + HOW(TDLS) static const char * iwl_mld_get_emlsr_blocked_string(enum iwl_mld_emlsr_blocked blocked) @@ -110,7 +111,6 @@ void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy, } #define IWL_MLD_TRIGGER_LINK_SEL_TIME (HZ * IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC) -#define IWL_MLD_SCAN_EXPIRE_TIME (HZ * IWL_MLD_SCAN_EXPIRE_TIME_SEC) /* Exit reasons that can cause longer EMLSR prevention */ #define IWL_MLD_PREVENT_EMLSR_REASONS (IWL_MLD_EMLSR_EXIT_MISSED_BEACON | \ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.h b/drivers/net/wireless/intel/iwlwifi/mld/nan.h index c9c83d1012f0..c04d77208971 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/nan.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.h @@ -2,7 +2,8 @@ /* * Copyright (C) 2025 Intel Corporation */ - +#ifndef __iwl_mld_nan_h__ +#define __iwl_mld_nan_h__ #include #include @@ -26,3 +27,5 @@ bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld, bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt, u32 obj_id); + +#endif /* __iwl_mld_nan_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h index 0deaf179f07c..6887f9feaa5c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h @@ -32,9 +32,9 @@ struct iwl_mld_phy { }; static inline struct iwl_mld_phy * -iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *channel) +iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *chanctx) { - return (void *)channel->drv_priv; + return (void *)chanctx->drv_priv; } /* Cleanup function for struct iwl_mld_phy, will be called in restart */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c index c3318e84f4a2..49b0d9f8f865 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -405,7 +405,10 @@ int iwl_mld_set_tx_power(struct iwl_mld *mld, .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK), .common.pwr_restriction = cpu_to_le16(u_tx_power), }; - int len = sizeof(cmd.common) + sizeof(cmd.v10); + int len = sizeof(cmd.common) + sizeof(cmd.v11); + + if (iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 10) == 10) + len = sizeof(cmd.common) + sizeof(cmd.v10); if (WARN_ON(!mld_link)) return -ENODEV; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c index 231920425c06..c65f4b56a327 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -301,10 +301,12 @@ void iwl_mld_ptp_init(struct iwl_mld *mld) mld->ptp_data.ptp_clock = ptp_clock_register(&mld->ptp_data.ptp_clock_info, mld->dev); - if (IS_ERR_OR_NULL(mld->ptp_data.ptp_clock)) { + if (IS_ERR(mld->ptp_data.ptp_clock)) { IWL_ERR(mld, "Failed to register PHC clock (%ld)\n", PTR_ERR(mld->ptp_data.ptp_clock)); mld->ptp_data.ptp_clock = NULL; + } else if (!mld->ptp_data.ptp_clock) { + IWL_DEBUG_INFO(mld, "PTP module unavailable on this kernel\n"); } else { IWL_DEBUG_INFO(mld, "Registered PHC clock: %s, with index: %d\n", mld->ptp_data.ptp_clock_info.name, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index 6ab5a3410353..659243ada86c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -64,6 +64,7 @@ void iwl_mld_get_bios_tables(struct iwl_mld *mld) } iwl_uefi_get_uats_table(mld->trans, &mld->fwrt); + iwl_uefi_get_uneb_table(mld->trans, &mld->fwrt); iwl_bios_get_phy_filters(&mld->fwrt); } @@ -72,16 +73,36 @@ static int iwl_mld_geo_sar_init(struct iwl_mld *mld) { u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD); /* Only set to South Korea if the table revision is 1 */ - __le32 sk = cpu_to_le32(mld->fwrt.geo_rev == 1 ? 1 : 0); + u8 sk = mld->fwrt.geo_rev == 1 ? 1 : 0; union iwl_geo_tx_power_profiles_cmd cmd = { .v5.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES), - .v5.table_revision = sk, }; + u32 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0); + int n_subbands; + int cmd_size; int ret; - ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v5.table[0][0], - ARRAY_SIZE(cmd.v5.table[0]), - BIOS_GEO_MAX_PROFILE_NUM); + switch (cmd_ver) { + case 5: + n_subbands = ARRAY_SIZE(cmd.v5.table[0]); + cmd.v5.table_revision = cpu_to_le32(sk); + cmd_size = sizeof(cmd.v5); + break; + case 6: + n_subbands = ARRAY_SIZE(cmd.v6.table[0]); + cmd.v6.bios_hdr.table_revision = mld->fwrt.geo_rev; + cmd.v6.bios_hdr.table_source = mld->fwrt.geo_bios_source; + cmd_size = sizeof(cmd.v6); + break; + default: + WARN(false, "unsupported version: %d", cmd_ver); + return -EINVAL; + } + + BUILD_BUG_ON(offsetof(typeof(cmd), v6.table) != + offsetof(typeof(cmd), v5.table)); + ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v6.table[0][0], + n_subbands, BIOS_GEO_MAX_PROFILE_NUM); /* It is a valid scenario to not support SAR, or miss wgds table, * but in that case there is no need to send the command. @@ -89,28 +110,48 @@ static int iwl_mld_geo_sar_init(struct iwl_mld *mld) if (ret) return 0; - return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, sizeof(cmd.v5)); + return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_size); } int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b) { - u32 cmd_id = REDUCE_TX_POWER_CMD; struct iwl_dev_tx_power_cmd cmd = { .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), - .v10.flags = cpu_to_le32(mld->fwrt.reduced_power_flags), }; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, REDUCE_TX_POWER_CMD, 10); + int num_subbands; + int cmd_size; int ret; + switch (cmd_ver) { + case 10: + cmd.v10.flags = cpu_to_le32(mld->fwrt.reduced_power_flags); + cmd_size = sizeof(cmd.common) + sizeof(cmd.v10); + num_subbands = IWL_NUM_SUB_BANDS_V2; + break; + case 11: + cmd.v11.flags = cpu_to_le32(mld->fwrt.reduced_power_flags); + cmd_size = sizeof(cmd.common) + sizeof(cmd.v11); + num_subbands = IWL_NUM_SUB_BANDS_V3; + break; + default: + WARN_ONCE(1, "Bad version for REDUCE_TX_POWER_CMD: %d\n", + cmd_ver); + return -EOPNOTSUPP; + } + /* TODO: CDB - support IWL_NUM_CHAIN_TABLES_V2 */ - ret = iwl_sar_fill_profile(&mld->fwrt, &cmd.v10.per_chain[0][0][0], - IWL_NUM_CHAIN_TABLES, IWL_NUM_SUB_BANDS_V2, + /* v10 and v11 have the same position for per_chain */ + BUILD_BUG_ON(offsetof(typeof(cmd), v11.per_chain) != + offsetof(typeof(cmd), v10.per_chain)); + ret = iwl_sar_fill_profile(&mld->fwrt, &cmd.v11.per_chain[0][0][0], + IWL_NUM_CHAIN_TABLES, num_subbands, prof_a, prof_b); /* return on error or if the profile is disabled (positive number) */ if (ret) return ret; - return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, - sizeof(cmd.common) + sizeof(cmd.v10)); + return iwl_mld_send_cmd_pdu(mld, REDUCE_TX_POWER_CMD, &cmd, cmd_size); } int iwl_mld_init_sar(struct iwl_mld *mld) @@ -165,30 +206,86 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld) { struct iwl_fw_runtime *fwrt = &mld->fwrt; union iwl_ppag_table_cmd cmd = { - .v7.ppag_config_info.hdr.table_source = fwrt->ppag_bios_source, - .v7.ppag_config_info.hdr.table_revision = fwrt->ppag_bios_rev, - .v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags), + /* v7 and v8 have the same layout for the ppag_config_info */ + .v8.ppag_config_info.hdr.table_source = fwrt->ppag_bios_source, + .v8.ppag_config_info.hdr.table_revision = fwrt->ppag_bios_rev, + .v8.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags), }; + u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD); + int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 1); + int cmd_len = sizeof(cmd.v8); + u8 cmd_bios_rev; int ret; + BUILD_BUG_ON(offsetof(typeof(cmd), v8.ppag_config_info.hdr) != + offsetof(typeof(cmd), v7.ppag_config_info.hdr)); + BUILD_BUG_ON(offsetof(typeof(cmd), v8.gain) != + offsetof(typeof(cmd), v7.gain)); + + BUILD_BUG_ON(ARRAY_SIZE(cmd.v7.gain) > ARRAY_SIZE(fwrt->ppag_chains)); + BUILD_BUG_ON(ARRAY_SIZE(cmd.v7.gain[0]) > + ARRAY_SIZE(fwrt->ppag_chains[0].subbands)); + BUILD_BUG_ON(ARRAY_SIZE(cmd.v8.gain) > ARRAY_SIZE(fwrt->ppag_chains)); + BUILD_BUG_ON(ARRAY_SIZE(cmd.v8.gain[0]) > + ARRAY_SIZE(fwrt->ppag_chains[0].subbands)); + IWL_DEBUG_RADIO(fwrt, "PPAG MODE bits going to be sent: %d\n", fwrt->ppag_flags); - for (int chain = 0; chain < IWL_NUM_CHAIN_LIMITS; chain++) { - for (int subband = 0; subband < IWL_NUM_SUB_BANDS_V2; subband++) { - cmd.v7.gain[chain][subband] = - fwrt->ppag_chains[chain].subbands[subband]; - IWL_DEBUG_RADIO(fwrt, - "PPAG table: chain[%d] band[%d]: gain = %d\n", - chain, subband, cmd.v7.gain[chain][subband]); + /* Since ver 7 will be deprecated at some point, don't bother making + * this code generic for both ver 7 and ver 8: duplicate the code. + */ + if (cmd_ver == 7) { + for (int chain = 0; chain < ARRAY_SIZE(cmd.v7.gain); chain++) { + for (int subband = 0; + subband < ARRAY_SIZE(cmd.v7.gain[0]); + subband++) { + cmd.v7.gain[chain][subband] = + fwrt->ppag_chains[chain].subbands[subband]; + IWL_DEBUG_RADIO(fwrt, + "PPAG table: chain[%d] band[%d]: gain = %d\n", + chain, subband, + cmd.v7.gain[chain][subband]); + } } + cmd_len = sizeof(cmd.v7); + cmd_bios_rev = + iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw, + fwrt->ppag_bios_source, + cmd_id, 4); + } else if (cmd_ver == 8) { + for (int chain = 0; chain < ARRAY_SIZE(cmd.v8.gain); chain++) { + for (int subband = 0; + subband < ARRAY_SIZE(cmd.v8.gain[0]); + subband++) { + cmd.v8.gain[chain][subband] = + fwrt->ppag_chains[chain].subbands[subband]; + IWL_DEBUG_RADIO(fwrt, + "PPAG table: chain[%d] band[%d]: gain = %d\n", + chain, subband, + cmd.v8.gain[chain][subband]); + } + } + cmd_bios_rev = + iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw, + fwrt->ppag_bios_source, + cmd_id, 5); + } else { + WARN(1, "Bad version for PER_PLATFORM_ANT_GAIN_CMD %d\n", + cmd_ver); + return -EINVAL; + } + + if (cmd_bios_rev < fwrt->ppag_bios_rev) { + IWL_ERR(mld, + "BIOS revision compatibility check failed - Supported: %d, Current: %d\n", + cmd_bios_rev, fwrt->ppag_bios_rev); + return 0; } IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n"); - ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, - PER_PLATFORM_ANT_GAIN_CMD), - &cmd, sizeof(cmd.v7)); + ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_len); if (ret < 0) IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n", ret); @@ -352,21 +449,42 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) ret); } -void iwl_mld_init_uats(struct iwl_mld *mld) +void iwl_mld_init_ap_type_tables(struct iwl_mld *mld) { int ret; struct iwl_host_cmd cmd = { .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, MCC_ALLOWED_AP_TYPE_CMD), - .data[0] = &mld->fwrt.uats_table, - .len[0] = sizeof(mld->fwrt.uats_table), + .data[0] = &mld->fwrt.ap_type_cmd, + .len[0] = sizeof(mld->fwrt.ap_type_cmd), .dataflags[0] = IWL_HCMD_DFL_NOCOPY, }; - if (!mld->fwrt.uats_valid) + if (!mld->fwrt.ap_type_cmd_valid) return; - ret = iwl_mld_send_cmd(mld, &cmd); + if (iwl_fw_lookup_cmd_ver(mld->fw, cmd.id, 1) == 1) { + struct iwl_mcc_allowed_ap_type_cmd_v1 *cmd_v1 = + kzalloc(sizeof(*cmd_v1), GFP_KERNEL); + + if (!cmd_v1) + return; + + BUILD_BUG_ON(sizeof(mld->fwrt.ap_type_cmd.mcc_to_ap_type_map) != + sizeof(cmd_v1->mcc_to_ap_type_map)); + + memcpy(cmd_v1->mcc_to_ap_type_map, + mld->fwrt.ap_type_cmd.mcc_to_ap_type_map, + sizeof(mld->fwrt.ap_type_cmd.mcc_to_ap_type_map)); + + cmd.data[0] = cmd_v1; + cmd.len[0] = sizeof(*cmd_v1); + ret = iwl_mld_send_cmd(mld, &cmd); + kfree(cmd_v1); + } else { + ret = iwl_mld_send_cmd(mld, &cmd); + } + if (ret) IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n", ret); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h index 3b01c645adda..5498c19789f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h @@ -9,7 +9,7 @@ void iwl_mld_get_bios_tables(struct iwl_mld *mld); void iwl_mld_configure_lari(struct iwl_mld *mld); -void iwl_mld_init_uats(struct iwl_mld *mld); +void iwl_mld_init_ap_type_tables(struct iwl_mld *mld); void iwl_mld_init_tas(struct iwl_mld *mld); int iwl_mld_init_ppag(struct iwl_mld *mld); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c index 214dcfde2fb4..a2e586c6ea67 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -791,6 +791,9 @@ static void * iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len) { struct ieee80211_radiotap_tlv *tlv; + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + + rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; tlv = skb_put(skb, sizeof(*tlv)); tlv->type = cpu_to_le16(type); @@ -1234,8 +1237,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb, eht = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len); - rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; - switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) { case 0: if (he_type == RATE_MCS_HE_TYPE_TRIG) { @@ -1329,7 +1330,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb, static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld, struct sk_buff *skb) { - struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); struct ieee80211_radiotap_vendor_content *radiotap; const u16 vendor_data_len = sizeof(mld->monitor.cur_aid); @@ -1353,8 +1353,6 @@ static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld, /* fill the data now */ memcpy(radiotap->data, &mld->monitor.cur_aid, sizeof(mld->monitor.cur_aid)); - - rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; } #endif @@ -1362,7 +1360,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld, struct sk_buff *skb, struct iwl_rx_phy_air_sniffer_ntfy *ntfy) { - struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); struct ieee80211_radiotap_vendor_content *radiotap; const u16 vendor_data_len = sizeof(*ntfy); @@ -1382,8 +1379,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld, /* fill the data now */ memcpy(radiotap->data, ntfy, vendor_data_len); - - rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; } static void @@ -1407,6 +1402,7 @@ static void iwl_mld_set_rx_rate(struct iwl_mld *mld, u32 rate_n_flags = phy_data->rate_n_flags; u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK); u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + u32 he_type = u32_get_bits(rate_n_flags, RATE_MCS_HE_TYPE_MSK); bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK; /* bandwidth may be overridden to RU by PHY ntfy */ @@ -1481,6 +1477,12 @@ static void iwl_mld_set_rx_rate(struct iwl_mld *mld, rx_status->encoding = RX_ENC_EHT; iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status); break; + case RATE_MCS_MOD_TYPE_UHR: + rx_status->encoding = RX_ENC_UHR; + iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status); + if (he_type == RATE_MCS_HE_TYPE_UHR_ELR) + rx_status->uhr.elr = 1; + break; default: WARN_ON_ONCE(1); } @@ -2204,8 +2206,9 @@ void iwl_mld_sync_rx_queues(struct iwl_mld *mld, ret = wait_event_timeout(mld->rxq_sync.waitq, READ_ONCE(mld->rxq_sync.state) == 0, SYNC_RX_QUEUE_TIMEOUT); - WARN_ONCE(!ret, "RXQ sync failed: state=0x%lx, cookie=%d\n", - mld->rxq_sync.state, mld->rxq_sync.cookie); + IWL_FW_CHECK(mld, !ret, + "RXQ sync failed: state=0x%lx, cookie=%d\n", + mld->rxq_sync.state, mld->rxq_sync.cookie); out: mld->rxq_sync.state = 0; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index a1a4cf3ab3d3..7ed107fb0e8d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -47,8 +47,6 @@ /* adaptive dwell number of APs override mask for social channels */ #define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT BIT(21) -#define SCAN_TIMEOUT_MSEC (30000 * HZ) - /* minimal number of 2GHz and 5GHz channels in the regular scan request */ #define IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS 4 @@ -116,6 +114,13 @@ struct iwl_mld_scan_params { u8 bssid[ETH_ALEN] __aligned(2); }; +struct iwl_scan_req_params_ptrs { + struct iwl_scan_general_params_v11 *general_params; + struct iwl_scan_channel_params_v8 *channel_params; + struct iwl_scan_periodic_parms_v1 *periodic_params; + struct iwl_scan_probe_params_v4 *probe_params; +}; + struct iwl_mld_scan_respect_p2p_go_iter_data { struct ieee80211_vif *current_vif; bool p2p_go; @@ -512,9 +517,10 @@ iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld, static void iwl_mld_scan_cmd_set_dwell(struct iwl_mld *mld, - struct iwl_scan_general_params_v11 *gp, - struct iwl_mld_scan_params *params) + struct iwl_mld_scan_params *params, + struct iwl_scan_req_params_ptrs *scan_ptrs) { + struct iwl_scan_general_params_v11 *gp = scan_ptrs->general_params; const struct iwl_mld_scan_timing_params *timing = &scan_timing[params->type]; @@ -551,9 +557,10 @@ static void iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld, struct iwl_mld_scan_params *params, struct ieee80211_vif *vif, - struct iwl_scan_general_params_v11 *gp, + struct iwl_scan_req_params_ptrs *scan_ptrs, enum iwl_mld_scan_status scan_status) { + struct iwl_scan_general_params_v11 *gp = scan_ptrs->general_params; u16 gen_flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif, scan_status); u8 gen_flags2 = iwl_mld_scan_get_cmd_gen_flags2(mld, params, vif, @@ -566,7 +573,7 @@ iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld, gp->flags = cpu_to_le16(gen_flags); gp->flags2 = gen_flags2; - iwl_mld_scan_cmd_set_dwell(mld, gp, params); + iwl_mld_scan_cmd_set_dwell(mld, params, scan_ptrs); if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1) gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; @@ -577,9 +584,12 @@ iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld, static int iwl_mld_scan_cmd_set_sched_params(struct iwl_mld_scan_params *params, - struct iwl_scan_umac_schedule *schedule, - __le16 *delay) + struct iwl_scan_req_params_ptrs *scan_ptrs) { + struct iwl_scan_umac_schedule *schedule = + scan_ptrs->periodic_params->schedule; + __le16 *delay = &scan_ptrs->periodic_params->delay; + if (WARN_ON(!params->n_scan_plans || params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) return -EINVAL; @@ -657,11 +667,12 @@ iwl_mld_scan_cmd_build_ssids(struct iwl_mld_scan_params *params, static void iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params, - struct iwl_scan_probe_params_v4 *pp) + struct iwl_scan_req_params_ptrs *scan_ptrs) { int j, idex_s = 0, idex_b = 0; struct cfg80211_scan_6ghz_params *scan_6ghz_params = params->scan_6ghz_params; + struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params; for (j = 0; j < params->n_ssids && idex_s < SCAN_SHORT_SSID_MAX_SIZE; @@ -725,13 +736,15 @@ iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params, static void iwl_mld_scan_cmd_set_probe_params(struct iwl_mld_scan_params *params, - struct iwl_scan_probe_params_v4 *pp, + struct iwl_scan_req_params_ptrs *scan_ptrs, u32 *bitmap_ssid) { + struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params; + pp->preq = params->preq; if (params->scan_6ghz) { - iwl_mld_scan_fill_6g_chan_list(params, pp); + iwl_mld_scan_fill_6g_chan_list(params, scan_ptrs); return; } @@ -821,10 +834,12 @@ static u32 iwl_mld_scan_ch_n_aps_flag(enum nl80211_iftype vif_type, u8 ch_id) static void iwl_mld_scan_cmd_set_channels(struct iwl_mld *mld, struct ieee80211_channel **channels, - struct iwl_scan_channel_params_v7 *cp, + struct iwl_scan_req_params_ptrs *scan_ptrs, int n_channels, u32 flags, enum nl80211_iftype vif_type) { + struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params; + for (int i = 0; i < n_channels; i++) { enum nl80211_band band = channels[i]->band; struct iwl_scan_channel_cfg_umac *cfg = &cp->channel_config[i]; @@ -862,10 +877,11 @@ static u8 iwl_mld_scan_cfg_channels_6g(struct iwl_mld *mld, struct iwl_mld_scan_params *params, u32 n_channels, - struct iwl_scan_probe_params_v4 *pp, - struct iwl_scan_channel_params_v7 *cp, + struct iwl_scan_req_params_ptrs *scan_ptrs, enum nl80211_iftype vif_type) { + struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params; + struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params; struct cfg80211_scan_6ghz_params *scan_6ghz_params = params->scan_6ghz_params; u32 i; @@ -1063,25 +1079,23 @@ static int iwl_mld_scan_cmd_set_6ghz_chan_params(struct iwl_mld *mld, struct iwl_mld_scan_params *params, struct ieee80211_vif *vif, - struct iwl_scan_req_params_v17 *scan_p) + struct iwl_scan_req_params_ptrs *scan_ptrs) { - struct iwl_scan_channel_params_v7 *chan_p = &scan_p->channel_params; - struct iwl_scan_probe_params_v4 *probe_p = &scan_p->probe_params; + struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params; /* Explicitly clear the flags since most of them are not * relevant for 6 GHz scan. */ - chan_p->flags = 0; - chan_p->count = iwl_mld_scan_cfg_channels_6g(mld, params, - params->n_channels, - probe_p, chan_p, - vif->type); - if (!chan_p->count) + cp->flags = 0; + cp->count = iwl_mld_scan_cfg_channels_6g(mld, params, + params->n_channels, + scan_ptrs, vif->type); + if (!cp->count) return -EINVAL; if (!params->n_ssids || (params->n_ssids == 1 && !params->ssids[0].ssid_len)) - chan_p->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER; + cp->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER; return 0; } @@ -1090,12 +1104,12 @@ static int iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld, struct iwl_mld_scan_params *params, struct ieee80211_vif *vif, - struct iwl_scan_req_params_v17 *scan_p, + struct iwl_scan_req_params_ptrs *scan_ptrs, bool low_latency, enum iwl_mld_scan_status scan_status, u32 channel_cfg_flags) { - struct iwl_scan_channel_params_v7 *cp = &scan_p->channel_params; + struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params; struct ieee80211_supported_band *sband = &mld->nvm_data->bands[NL80211_BAND_6GHZ]; @@ -1107,14 +1121,14 @@ iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld, if (params->scan_6ghz) return iwl_mld_scan_cmd_set_6ghz_chan_params(mld, params, - vif, scan_p); + vif, scan_ptrs); /* relevant only for 2.4 GHz/5 GHz scan */ cp->flags = iwl_mld_scan_cmd_set_chan_flags(mld, params, vif, low_latency); cp->count = params->n_channels; - iwl_mld_scan_cmd_set_channels(mld, params->channels, cp, + iwl_mld_scan_cmd_set_channels(mld, params->channels, scan_ptrs, params->n_channels, channel_cfg_flags, vif->type); @@ -1144,47 +1158,146 @@ iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld, return 0; } +struct iwl_scan_umac_handler { + u8 version; + int (*handler)(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct iwl_mld_scan_params *params, + enum iwl_mld_scan_status scan_status, + int uid, u32 ooc_priority, bool low_latency); +}; + +#define IWL_SCAN_UMAC_HANDLER(_ver) { \ + .version = _ver, \ + .handler = iwl_mld_scan_umac_v##_ver, \ +} + +static int iwl_mld_scan_umac_common(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_scan_params *params, + struct iwl_scan_req_params_ptrs *scan_ptrs, + enum iwl_mld_scan_status scan_status, + bool low_latency) +{ + u32 bitmap_ssid = 0; + int ret; + + iwl_mld_scan_cmd_set_gen_params(mld, params, vif, scan_ptrs, + scan_status); + + ret = iwl_mld_scan_cmd_set_sched_params(params, scan_ptrs); + if (ret) + return ret; + + iwl_mld_scan_cmd_set_probe_params(params, scan_ptrs, &bitmap_ssid); + + return iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_ptrs, + low_latency, scan_status, + bitmap_ssid); +} + +static int iwl_mld_scan_umac_v18(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct iwl_mld_scan_params *params, + enum iwl_mld_scan_status scan_status, + int uid, u32 ooc_priority, bool low_latency) +{ + struct iwl_scan_req_umac_v18 *cmd = mld->scan.cmd; + struct iwl_scan_req_params_ptrs scan_ptrs = { + .general_params = &cmd->scan_params.general_params, + .probe_params = &cmd->scan_params.probe_params, + .channel_params = &cmd->scan_params.channel_params, + .periodic_params = &cmd->scan_params.periodic_params + }; + int ret; + + if (WARN_ON(params->n_channels > SCAN_MAX_NUM_CHANS_V4)) + return -EINVAL; + + cmd->uid = cpu_to_le32(uid); + cmd->ooc_priority = cpu_to_le32(ooc_priority); + + ret = iwl_mld_scan_umac_common(mld, vif, params, &scan_ptrs, + scan_status, low_latency); + if (ret) + return ret; + + return uid; +} + +static int iwl_mld_scan_umac_v17(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct iwl_mld_scan_params *params, + enum iwl_mld_scan_status scan_status, + int uid, u32 ooc_priority, bool low_latency) +{ + struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd; + struct iwl_scan_req_params_ptrs scan_ptrs = { + .general_params = &cmd->scan_params.general_params, + .probe_params = &cmd->scan_params.probe_params, + + /* struct iwl_scan_channel_params_v8 and struct + * iwl_scan_channel_params_v7 are almost identical. The only + * difference is that the newer version allows configuration of + * more channels. So casting here is ok as long as we ensure + * that we don't exceed the max number of channels supported by + * the older version (see the WARN_ON below). + */ + .channel_params = (struct iwl_scan_channel_params_v8 *) + &cmd->scan_params.channel_params, + .periodic_params = &cmd->scan_params.periodic_params + }; + int ret; + + if (WARN_ON(params->n_channels > SCAN_MAX_NUM_CHANS_V3)) + return -EINVAL; + + cmd->uid = cpu_to_le32(uid); + cmd->ooc_priority = cpu_to_le32(ooc_priority); + + ret = iwl_mld_scan_umac_common(mld, vif, params, &scan_ptrs, + scan_status, low_latency); + if (ret) + return ret; + + return uid; +} + +static const struct iwl_scan_umac_handler iwl_scan_umac_handlers[] = { + /* set the newest version first to shorten the list traverse time */ + IWL_SCAN_UMAC_HANDLER(18), + IWL_SCAN_UMAC_HANDLER(17), +}; + static int iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif, struct iwl_mld_scan_params *params, enum iwl_mld_scan_status scan_status, bool low_latency) { - struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd; - struct iwl_scan_req_params_v17 *scan_p = &cmd->scan_params; - u32 bitmap_ssid = 0; - int uid, ret; + int uid, err; + u32 ooc_priority; memset(mld->scan.cmd, 0, mld->scan.cmd_size); - - /* find a free UID entry */ uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE); if (uid < 0) return uid; - cmd->uid = cpu_to_le32(uid); - cmd->ooc_priority = - cpu_to_le32(iwl_mld_scan_ooc_priority(scan_status)); + ooc_priority = iwl_mld_scan_ooc_priority(scan_status); - iwl_mld_scan_cmd_set_gen_params(mld, params, vif, - &scan_p->general_params, scan_status); + for (size_t i = 0; i < ARRAY_SIZE(iwl_scan_umac_handlers); i++) { + const struct iwl_scan_umac_handler *ver_handler = + &iwl_scan_umac_handlers[i]; - ret = iwl_mld_scan_cmd_set_sched_params(params, - scan_p->periodic_params.schedule, - &scan_p->periodic_params.delay); - if (ret) - return ret; + if (ver_handler->version != mld->scan.cmd_ver) + continue; - iwl_mld_scan_cmd_set_probe_params(params, &scan_p->probe_params, - &bitmap_ssid); + err = ver_handler->handler(mld, vif, params, scan_status, + uid, ooc_priority, low_latency); + return err ? : uid; + } - ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_p, - low_latency, scan_status, - bitmap_ssid); - if (ret) - return ret; + IWL_ERR(mld, "No handler for UMAC scan cmd version %d\n", + mld->scan.cmd_ver); - return uid; + return -EINVAL; } static bool @@ -1942,9 +2055,7 @@ void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf = NULL; if (fw_link_id != IWL_MLD_INVALID_FW_ID) - link_conf = - wiphy_dereference(mld->wiphy, - mld->fw_id_to_bss_conf[fw_link_id]); + link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id); /* It is possible that by the time the scan is complete the * link was already removed and is not valid. @@ -2031,6 +2142,8 @@ int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld) if (scan_cmd_ver == 17) { scan_cmd_size = sizeof(struct iwl_scan_req_umac_v17); + } else if (scan_cmd_ver == 18) { + scan_cmd_size = sizeof(struct iwl_scan_req_umac_v18); } else { IWL_ERR(mld, "Unexpected scan cmd version %d\n", scan_cmd_ver); return -EINVAL; @@ -2041,6 +2154,7 @@ int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld) return -ENOMEM; mld->scan.cmd_size = scan_cmd_size; + mld->scan.cmd_ver = scan_cmd_ver; return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h index 69110f0cfc8e..772b3a02c4c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h @@ -109,6 +109,7 @@ enum iwl_mld_traffic_load { * @traffic_load.status: The current traffic load status, see * &enum iwl_mld_traffic_load * @cmd_size: size of %cmd. + * @cmd_ver: version of the scan command format. * @cmd: pointer to scan cmd buffer (allocated once in op mode start). * @last_6ghz_passive_jiffies: stores the last 6GHz passive scan time * in jiffies. @@ -134,6 +135,7 @@ struct iwl_mld_scan { /* And here fields that survive a fw restart */ size_t cmd_size; void *cmd; + u8 cmd_ver; unsigned long last_6ghz_passive_jiffies; unsigned long last_start_time_jiffies; u64 last_mlo_scan_time; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index 6b7a89e050e6..4c97d12ce2d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -398,12 +398,42 @@ static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta) return htc_flags; } +/* Note: modifies the command depending on FW command version */ static int iwl_mld_send_sta_cmd(struct iwl_mld *mld, - const struct iwl_sta_cfg_cmd *cmd) + struct iwl_sta_cfg_cmd *cmd) { - int ret = iwl_mld_send_cmd_pdu(mld, - WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), - cmd); + int cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD); + int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0); + int len = sizeof(*cmd); + int ret; + + if (cmd_ver < 2) { + IWL_ERR(mld, "Unsupported STA_CONFIG_CMD version %d\n", + cmd_ver); + return -EINVAL; + } else if (cmd_ver == 2) { + struct iwl_sta_cfg_cmd_v2 *cmd_v2 = (void *)cmd; + + if (WARN_ON(cmd->station_type == cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) || + cmd->station_type == cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) || + hweight32(le32_to_cpu(cmd->link_mask)) != 1)) + return -EINVAL; + /* + * These fields are located in a different place in the struct of v2. + * The assumption is that UHR won't be used with FW that has v2. + */ + if (WARN_ON(cmd->mic_prep_pad_delay || cmd->mic_compute_pad_delay)) + return -EINVAL; + + len = sizeof(struct iwl_sta_cfg_cmd_v2); + cmd_v2->link_id = cpu_to_le32(__ffs(le32_to_cpu(cmd->link_mask))); + } else if (WARN_ON(cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) && + cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) && + hweight32(le32_to_cpu(cmd->link_mask)) != 1)) { + return -EINVAL; + } + + ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, len); if (ret) IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret); return ret; @@ -431,8 +461,8 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, return -EINVAL; cmd.sta_id = cpu_to_le32(fw_id); + cmd.link_mask = cpu_to_le32(BIT(mld_link->fw_id)); cmd.station_type = cpu_to_le32(mld_sta->sta_type); - cmd.link_id = cpu_to_le32(mld_link->fw_id); memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN); memcpy(&cmd.peer_link_address, link_sta->addr, ETH_ALEN); @@ -498,7 +528,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, return iwl_mld_send_sta_cmd(mld, &cmd); } -IWL_MLD_ALLOC_FN(link_sta, link_sta) +static IWL_MLD_ALLOC_FN(link_sta, link_sta) static int iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta) @@ -725,14 +755,14 @@ iwl_mld_init_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, } int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, - struct ieee80211_vif *vif, enum iwl_fw_sta_type type) + struct ieee80211_vif *vif) { struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); struct ieee80211_link_sta *link_sta; int link_id; int ret; - ret = iwl_mld_init_sta(mld, sta, vif, type); + ret = iwl_mld_init_sta(mld, sta, vif, STATION_TYPE_PEER); if (ret) return ret; @@ -908,7 +938,7 @@ static void iwl_mld_count_mpdu(struct ieee80211_link_sta *link_sta, int queue, if (!(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT)) goto unlock; - for (int i = 0; i <= IWL_FW_MAX_LINK_ID; i++) + for (int i = 0; i < IWL_FW_MAX_LINKS; i++) total_mpdus += tx ? queue_counter->per_link[i].tx : queue_counter->per_link[i].rx; @@ -982,7 +1012,7 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld, return iwl_mld_send_aux_sta_cmd(mld, internal_sta); cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id); - cmd.link_id = cpu_to_le32(fw_link_id); + cmd.link_mask = cpu_to_le32(BIT(fw_link_id)); cmd.station_type = cpu_to_le32(internal_sta->sta_type); /* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP. diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h index 1897b121aae2..36288c2fb38c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h @@ -89,7 +89,7 @@ struct iwl_mld_per_link_mpdu_counter { */ struct iwl_mld_per_q_mpdu_counter { spinlock_t lock; - struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINK_ID + 1]; + struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINKS]; unsigned long window_start_time; } ____cacheline_aligned_in_smp; @@ -190,7 +190,7 @@ iwl_mld_link_sta_from_mac80211(struct ieee80211_link_sta *link_sta) } int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, - struct ieee80211_vif *vif, enum iwl_fw_sta_type type); + struct ieee80211_vif *vif); void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta); int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index 7b8709716324..54eb0ead78ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -369,15 +369,39 @@ static void iwl_mld_stats_recalc_traffic_load(struct iwl_mld *mld, static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig, struct ieee80211_bss_conf *bss_conf) { + struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld; int exit_emlsr_thresh; + int last_event; if (sig == 0) { IWL_DEBUG_RX(mld, "RSSI is 0 - skip signal based decision\n"); return; } - /* TODO: task=statistics handle CQM notifications */ + if (WARN_ON(!link)) + return; + + /* CQM Notification */ + if (vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) { + int thold = bss_conf->cqm_rssi_thold; + int hyst = bss_conf->cqm_rssi_hyst; + + last_event = link->last_cqm_rssi_event; + if (thold && sig < thold && + (last_event == 0 || sig < last_event - hyst)) { + link->last_cqm_rssi_event = sig; + ieee80211_cqm_rssi_notify(vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + sig, GFP_KERNEL); + } else if (sig > thold && + (last_event == 0 || sig > last_event + hyst)) { + link->last_cqm_rssi_event = sig; + ieee80211_cqm_rssi_notify(vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + sig, GFP_KERNEL); + } + } if (!iwl_mld_emlsr_active(vif)) { /* We're not in EMLSR and our signal is bad, @@ -407,14 +431,13 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld, u32 total_airtime_usec = 0; for (u32 fw_id = 0; - fw_id < ARRAY_SIZE(mld->fw_id_to_bss_conf); + fw_id < mld->fw->ucode_capa.num_links; fw_id++) { const struct iwl_stats_ntfy_per_link *link_stats; struct ieee80211_bss_conf *bss_conf; int sig; - bss_conf = wiphy_dereference(mld->wiphy, - mld->fw_id_to_bss_conf[fw_id]); + bss_conf = iwl_mld_fw_id_to_link_conf(mld, fw_id); if (!bss_conf || bss_conf->vif->type != NL80211_IFTYPE_STATION) continue; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c index 26cf27be762d..dce747270167 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -42,7 +42,7 @@ int iwlmld_kunit_test_init(struct kunit *test) iwl_construct_mld(mld, trans, cfg, fw, hw, NULL); fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX; - fw->ucode_capa.num_links = IWL_FW_MAX_LINK_ID + 1; + fw->ucode_capa.num_links = IWL_FW_MAX_LINKS; mld->fwrt.trans = trans; mld->fwrt.fw = fw; @@ -68,7 +68,7 @@ int iwlmld_kunit_test_init(struct kunit *test) return 0; } -IWL_MLD_ALLOC_FN(link, bss_conf) +static IWL_MLD_ALLOC_FN(link, bss_conf) static void iwlmld_kunit_init_link(struct ieee80211_vif *vif, struct ieee80211_bss_conf *link, @@ -94,7 +94,7 @@ static void iwlmld_kunit_init_link(struct ieee80211_vif *vif, rcu_assign_pointer(vif->link_conf[link_id], link); } -IWL_MLD_ALLOC_FN(vif, vif) +static IWL_MLD_ALLOC_FN(vif, vif) /* Helper function to add and initialize a VIF for KUnit tests */ struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type) @@ -199,7 +199,7 @@ void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, vif->active_links |= BIT(link->link_id); } -IWL_MLD_ALLOC_FN(link_sta, link_sta) +static IWL_MLD_ALLOC_FN(link_sta, link_sta) static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta, struct ieee80211_link_sta *link_sta, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c index 62a54c37a98c..78d6162d9297 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -9,6 +9,7 @@ #include "hcmd.h" #include "sta.h" #include "phy.h" +#include "iface.h" #include "fw/api/rs.h" #include "fw/api/context.h" @@ -36,7 +37,8 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, const struct ieee80211_sta_he_cap *own_he_cap, - const struct ieee80211_sta_eht_cap *own_eht_cap) + const struct ieee80211_sta_eht_cap *own_eht_cap, + const struct ieee80211_sta_uhr_cap *own_uhr_cap) { struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; @@ -90,6 +92,12 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK; } + if (link_sta->uhr_cap.has_uhr && own_uhr_cap && + link_sta->uhr_cap.phy.cap & IEEE80211_UHR_PHY_CAP_ELR_RX && + own_uhr_cap->phy.cap & IEEE80211_UHR_PHY_CAP_ELR_TX) + flags |= IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_1_5_MBPS_MSK | + IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_3_MBPS_MSK; + return cpu_to_le16(flags); } @@ -406,6 +414,7 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_supported_band *sband, const struct ieee80211_sta_he_cap *own_he_cap, const struct ieee80211_sta_eht_cap *own_eht_cap, + const struct ieee80211_sta_uhr_cap *own_uhr_cap, struct iwl_tlc_config_cmd *cmd) { int i; @@ -423,7 +432,16 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif, cmd->non_ht_rates = cpu_to_le16(non_ht_rates); cmd->mode = IWL_TLC_MNG_MODE_NON_HT; - if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) { + if (link_sta->uhr_cap.has_uhr && own_uhr_cap) { + cmd->mode = IWL_TLC_MNG_MODE_UHR; + /* + * FIXME: spec currently inherits from EHT but has no + * finer MCS bits. Once that's there, need to add them + * to the bitmaps (and maybe copy this to UHR, or so.) + */ + iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap, + own_eht_cap, cmd); + } else if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) { cmd->mode = IWL_TLC_MNG_MODE_EHT; iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap, own_eht_cap, cmd); @@ -513,19 +531,23 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, struct ieee80211_bss_conf *link) { struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); enum nl80211_band band = link->chanreq.oper.chan->band; struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band]; const struct ieee80211_sta_he_cap *own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); const struct ieee80211_sta_eht_cap *own_eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, vif); + const struct ieee80211_sta_uhr_cap *own_uhr_cap = + ieee80211_get_uhr_iftype_cap_vif(sband, vif); struct iwl_tlc_config_cmd cmd = { /* For AP mode, use 20 MHz until the STA is authorized */ .max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ? iwl_mld_fw_bw_from_sta_bw(link_sta) : IWL_TLC_MNG_CH_WIDTH_20MHZ, .flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta, - own_he_cap, own_eht_cap), + own_he_cap, own_eht_cap, + own_uhr_cap), .chains = iwl_mld_get_fw_chains(mld), .sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta), .max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len), @@ -546,7 +568,10 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, cmd.sta_mask = cpu_to_le32(BIT(fw_sta_id)); - chan_ctx = rcu_dereference_wiphy(mld->wiphy, link->chanctx_conf); + if (WARN_ON_ONCE(!mld_link)) + return; + + chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx); if (WARN_ON(!chan_ctx)) return; @@ -555,7 +580,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, iwl_mld_fill_supp_rates(mld, vif, link_sta, sband, own_he_cap, own_eht_cap, - &cmd); + own_uhr_cap, &cmd); if (cmd_ver == 6) { cmd_ptr = &cmd; @@ -638,6 +663,49 @@ void iwl_mld_config_tlc_link(struct iwl_mld *mld, iwl_mld_send_tlc_cmd(mld, vif, link_sta, link_conf); } +void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + struct ieee80211_chanctx_conf *chan_ctx; + int link_id = link_conf->link_id; + struct ieee80211_sta *sta; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return; + + chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx); + + for_each_station(sta, mld->hw) { + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_link_sta *mld_link_sta; + struct ieee80211_link_sta *link_sta; + + if (mld_sta->vif != vif) + continue; + + link_sta = link_sta_dereference_protected(sta, link_id); + if (!link_sta) + continue; + + mld_link_sta = iwl_mld_link_sta_dereference_check(mld_sta, + link_id); + + /* In recovery flow, the station may not be (yet) in the + * firmware, don't send a TLC command for a station the + * firmware does not know. + */ + if (!mld_link_sta || !mld_link_sta->in_fw) + continue; + + if (chan_ctx) + iwl_mld_config_tlc_link(mld, vif, link_conf, link_sta); + /* TODO: else, remove the TLC object in the firmware */ + } +} + void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h index c32f42e8840b..c7ff209c9ab6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h @@ -20,4 +20,7 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data); +void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf); + #endif /* __iwl_mld_tlc_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 43cf94c9a36b..0c643f0b7105 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -459,23 +459,18 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, static void iwl_mvm_uats_init(struct iwl_mvm *mvm) { + int cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, + MCC_ALLOWED_AP_TYPE_CMD); + struct iwl_mcc_allowed_ap_type_cmd_v1 cmd = {}; u8 cmd_ver; int ret; - struct iwl_host_cmd cmd = { - .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, - MCC_ALLOWED_AP_TYPE_CMD), - .flags = 0, - .data[0] = &mvm->fwrt.uats_table, - .len[0] = sizeof(mvm->fwrt.uats_table), - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - }; if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n"); return; } - cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id, + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, IWL_FW_CMD_VER_UNKNOWN); if (cmd_ver != 1) { IWL_DEBUG_RADIO(mvm, @@ -486,10 +481,17 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm) iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt); - if (!mvm->fwrt.uats_valid) + if (!mvm->fwrt.ap_type_cmd_valid) return; - ret = iwl_mvm_send_cmd(mvm, &cmd); + BUILD_BUG_ON(sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map) != + sizeof(cmd.mcc_to_ap_type_map)); + + memcpy(cmd.mcc_to_ap_type_map, + mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map, + sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map)); + + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); if (ret < 0) IWL_ERR(mvm, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n", ret); @@ -906,7 +908,7 @@ 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) { - union iwl_geo_tx_power_profiles_cmd geo_tx_cmd; + union iwl_geo_tx_power_profiles_cmd geo_tx_cmd = {}; struct iwl_geo_tx_power_profiles_resp *resp; u16 len; int ret; @@ -958,7 +960,7 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) { u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD); - union iwl_geo_tx_power_profiles_cmd cmd; + union iwl_geo_tx_power_profiles_cmd cmd = {}; u16 len; u32 n_bands; u32 n_profiles; @@ -1032,12 +1034,139 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); } +static bool iwl_mvm_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; +} + +static int iwl_mvm_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->info.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), 1); + /* + * 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; + + /* 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; + } + + 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); + cmd->v1.flags = + cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V1_MASK); + if (fwrt->ppag_bios_rev >= 1) { + /* in this case FW supports revision 0 */ + IWL_DEBUG_RADIO(fwrt, + "PPAG table rev is %d, send truncated table\n", + fwrt->ppag_bios_rev); + } + } else if (cmd_ver == 5) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = cmd->v5.gain[0]; + *cmd_size = sizeof(cmd->v5); + cmd->v5.flags = + cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V5_MASK); + if (fwrt->ppag_bios_rev == 0) { + /* in this case FW supports revisions 1,2 or 3 */ + IWL_DEBUG_RADIO(fwrt, + "PPAG table rev is 0, send padded table\n"); + } + } else if (cmd_ver == 7) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = cmd->v7.gain[0]; + *cmd_size = sizeof(cmd->v7); + cmd->v7.ppag_config_info.hdr.table_source = + fwrt->ppag_bios_source; + cmd->v7.ppag_config_info.hdr.table_revision = + fwrt->ppag_bios_rev; + cmd->v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags); + } 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", + fwrt->ppag_flags); + + if (cmd_ver == 1 && + !fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) { + 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"); + } + + /* The 'flags' field is the same in v1 and v5 so we can just + * use v1 to access it. + */ + IWL_DEBUG_RADIO(fwrt, + "PPAG MODE bits going to be sent: %d\n", + (cmd_ver < 7) ? le32_to_cpu(cmd->v1.flags) : + le32_to_cpu(cmd->v7.ppag_config_info.value)); + + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + for (j = 0; j < num_sub_bands; j++) { + if (!send_ppag_always && + !iwl_mvm_ppag_value_valid(fwrt, i, j)) + return -EINVAL; + + 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; +} + int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) { union iwl_ppag_table_cmd cmd; int ret, cmd_size; - ret = iwl_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size); + ret = iwl_mvm_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size); /* Not supporting PPAG table is a valid scenario */ if (ret < 0) return 0; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 090791fe0638..1ec9807e4827 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -6229,9 +6229,10 @@ 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, SYNC_RX_QUEUE_TIMEOUT); - WARN_ONCE(!ret, "queue sync: failed to sync, state is 0x%lx, cookie %d\n", - mvm->queue_sync_state, - mvm->queue_sync_cookie); + IWL_FW_CHECK(mvm, !ret, + "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/mld-key.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c index 9bb253dcf4a7..4869a5fa8abc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c @@ -121,52 +121,6 @@ struct iwl_mvm_sta_key_update_data { int err; }; -static void iwl_mvm_mld_update_sta_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, - void *_data) -{ - u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); - struct iwl_mvm_sta_key_update_data *data = _data; - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_sec_key_cmd cmd = { - .action = cpu_to_le32(FW_CTXT_ACTION_MODIFY), - .u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask), - .u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask), - .u.modify.key_id = cpu_to_le32(key->keyidx), - .u.modify.key_flags = - cpu_to_le32(iwl_mvm_get_sec_flags(mvm, vif, sta, key)), - }; - int err; - - /* only need to do this for pairwise keys (link_id == -1) */ - if (sta != data->sta || key->link_id >= 0) - return; - - err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); - - if (err) - data->err = err; -} - -int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - u32 old_sta_mask, - u32 new_sta_mask) -{ - struct iwl_mvm_sta_key_update_data data = { - .sta = sta, - .old_sta_mask = old_sta_mask, - .new_sta_mask = new_sta_mask, - }; - - ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_mld_update_sta_key, - &data); - return data.err; -} - static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask, u32 key_flags, u32 keyidx, u32 flags) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c index bf54b90a7c51..b65825747b9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c @@ -6,7 +6,7 @@ static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mac_config_cmd *cmd, + struct iwl_mac_config_cmd_v3 *cmd, int cmd_ver) { if (vif->type == NL80211_IFTYPE_AP) { @@ -24,7 +24,7 @@ static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm, static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mac_config_cmd *cmd, + struct iwl_mac_config_cmd_v3 *cmd, u32 action) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -83,7 +83,7 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, } static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm, - struct iwl_mac_config_cmd *cmd) + struct iwl_mac_config_cmd_v3 *cmd) { int ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), @@ -98,7 +98,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 action, bool force_assoc_off) { - struct iwl_mac_config_cmd cmd = {}; + struct iwl_mac_config_cmd_v3 cmd = {}; WARN_ON(vif->type != NL80211_IFTYPE_STATION); @@ -151,7 +151,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 action) { - struct iwl_mac_config_cmd cmd = {}; + struct iwl_mac_config_cmd_v3 cmd = {}; WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); @@ -170,7 +170,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 action) { - struct iwl_mac_config_cmd cmd = {}; + struct iwl_mac_config_cmd_v3 cmd = {}; WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); @@ -187,7 +187,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 action) { - struct iwl_mac_config_cmd cmd = {}; + struct iwl_mac_config_cmd_v3 cmd = {}; WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); @@ -210,7 +210,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm, u32 action) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_config_cmd cmd = {}; + struct iwl_mac_config_cmd_v3 cmd = {}; WARN_ON(vif->type != NL80211_IFTYPE_AP); @@ -286,7 +286,7 @@ int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm, int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_config_cmd cmd = { + struct iwl_mac_config_cmd_v3 cmd = { .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), .id_and_color = cpu_to_le32(mvmvif->id), }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 896ed9823021..f1dbfeae20bc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -886,133 +886,6 @@ static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops); } -static int -iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u16 old_links, u16 new_links, - struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) -{ - struct iwl_mvm_vif_link_info *new_link[IEEE80211_MLD_MAX_NUM_LINKS] = {}; - 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; - - for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - break; - - if (!(added & BIT(i))) - continue; - new_link[i] = kzalloc_obj(*new_link[i]); - if (!new_link[i]) { - err = -ENOMEM; - goto free; - } - - new_link[i]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; - iwl_mvm_init_link(new_link[i]); - } - - mutex_lock(&mvm->mutex); - - /* If we're in RESTART flow, the default link wasn't added in - * drv_add_interface(), and link[0] doesn't point to it. - */ - if (old_links == 0 && !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, - &mvm->status)) { - err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); - if (err) - goto out_err; - mvmvif->link[0] = NULL; - } - - for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - if (removed & BIT(i)) { - struct ieee80211_bss_conf *link_conf = old[i]; - - err = iwl_mvm_disable_link(mvm, vif, link_conf); - if (err) - goto out_err; - kfree(mvmvif->link[i]); - mvmvif->link[i] = NULL; - } else if (added & BIT(i)) { - struct ieee80211_bss_conf *link_conf; - - link_conf = link_conf_dereference_protected(vif, i); - if (WARN_ON(!link_conf)) - continue; - - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, - &mvm->status)) - mvmvif->link[i] = new_link[i]; - new_link[i] = NULL; - err = iwl_mvm_add_link(mvm, vif, link_conf); - if (err) - goto out_err; - } - } - - err = 0; - if (new_links == 0) { - mvmvif->link[0] = &mvmvif->deflink; - err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); - } - -out_err: - /* we really don't have a good way to roll back here ... */ - mutex_unlock(&mvm->mutex); - -free: - for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) - kfree(new_link[i]); - return err; -} - -static int -iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - u16 old_links, u16 new_links) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - guard(mvm)(mvm); - return iwl_mvm_mld_update_sta_links(mvm, vif, sta, old_links, new_links); -} - -static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u16 desired_links) -{ - int n_links = hweight16(desired_links); - - if (n_links <= 1) - return true; - - WARN_ON(1); - return false; -} - -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, @@ -1102,9 +975,4 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .link_sta_add_debugfs = iwl_mvm_link_sta_add_debugfs, #endif .set_hw_timestamp = iwl_mvm_set_hw_timestamp, - - .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, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c index 3359e02e151f..da7ed4639a93 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -20,7 +20,7 @@ u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm, - struct iwl_sta_cfg_cmd *cmd) + struct iwl_sta_cfg_cmd_v2 *cmd) { u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD); int cmd_len = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 0) > 1 ? @@ -41,7 +41,7 @@ static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, const u8 *addr, int link_id) { - struct iwl_sta_cfg_cmd cmd; + struct iwl_sta_cfg_cmd_v2 cmd; lockdep_assert_held(&mvm->mutex); @@ -416,7 +416,7 @@ static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_vif_link_info *link_info = mvm_vif->link[link_conf->link_id]; - struct iwl_sta_cfg_cmd cmd = { + struct iwl_sta_cfg_cmd_v2 cmd = { .sta_id = cpu_to_le32(mvm_link_sta->sta_id), .station_type = cpu_to_le32(mvm_sta->sta_type), }; @@ -913,288 +913,3 @@ void iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm *mvm, rcu_read_unlock(); } - -static int iwl_mvm_mld_update_sta_queues(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - u32 old_sta_mask, - u32 new_sta_mask) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_scd_queue_cfg_cmd cmd = { - .operation = cpu_to_le32(IWL_SCD_QUEUE_MODIFY), - .u.modify.old_sta_mask = cpu_to_le32(old_sta_mask), - .u.modify.new_sta_mask = cpu_to_le32(new_sta_mask), - }; - struct iwl_host_cmd hcmd = { - .id = WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD), - .len[0] = sizeof(cmd), - .data[0] = &cmd - }; - int tid; - int ret; - - lockdep_assert_held(&mvm->mutex); - - for (tid = 0; tid <= IWL_MAX_TID_COUNT; tid++) { - struct iwl_mvm_tid_data *tid_data = &mvm_sta->tid_data[tid]; - int txq_id = tid_data->txq_id; - - if (txq_id == IWL_MVM_INVALID_QUEUE) - continue; - - if (tid == IWL_MAX_TID_COUNT) - cmd.u.modify.tid = cpu_to_le32(IWL_MGMT_TID); - else - cmd.u.modify.tid = cpu_to_le32(tid); - - ret = iwl_mvm_send_cmd(mvm, &hcmd); - if (ret) - return ret; - } - - return 0; -} - -static int iwl_mvm_mld_update_sta_baids(struct iwl_mvm *mvm, - u32 old_sta_mask, - u32 new_sta_mask) -{ - struct iwl_rx_baid_cfg_cmd cmd = { - .action = cpu_to_le32(IWL_RX_BAID_ACTION_MODIFY), - .modify.old_sta_id_mask = cpu_to_le32(old_sta_mask), - .modify.new_sta_id_mask = cpu_to_le32(new_sta_mask), - }; - u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD); - int baid; - - /* mac80211 will remove sessions later, but we ignore all that */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - return 0; - - BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid)); - - for (baid = 0; baid < ARRAY_SIZE(mvm->baid_map); baid++) { - struct iwl_mvm_baid_data *data; - int ret; - - data = rcu_dereference_protected(mvm->baid_map[baid], - lockdep_is_held(&mvm->mutex)); - if (!data) - continue; - - if (!(data->sta_mask & old_sta_mask)) - continue; - - WARN_ONCE(data->sta_mask != old_sta_mask, - "BAID data for %d corrupted - expected 0x%x found 0x%x\n", - baid, old_sta_mask, data->sta_mask); - - cmd.modify.tid = cpu_to_le32(data->tid); - - ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_SEND_IN_RFKILL, - sizeof(cmd), &cmd); - data->sta_mask = new_sta_mask; - if (ret) - return ret; - } - - return 0; -} - -static int iwl_mvm_mld_update_sta_resources(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - u32 old_sta_mask, - u32 new_sta_mask) -{ - int ret; - - ret = iwl_mvm_mld_update_sta_queues(mvm, sta, - old_sta_mask, - new_sta_mask); - if (ret) - return ret; - - ret = iwl_mvm_mld_update_sta_keys(mvm, vif, sta, - old_sta_mask, - new_sta_mask); - if (ret) - return ret; - - return iwl_mvm_mld_update_sta_baids(mvm, old_sta_mask, new_sta_mask); -} - -int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - u16 old_links, u16 new_links) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_link_sta *mvm_sta_link; - struct iwl_mvm_vif_link_info *mvm_vif_link; - unsigned long links_to_add = ~old_links & new_links; - unsigned long links_to_rem = old_links & ~new_links; - unsigned long old_links_long = old_links; - u32 current_sta_mask = 0, sta_mask_added = 0, sta_mask_to_rem = 0; - unsigned long link_sta_added_to_fw = 0, link_sta_allocated = 0; - unsigned int link_id; - int ret; - - lockdep_assert_wiphy(mvm->hw->wiphy); - lockdep_assert_held(&mvm->mutex); - - for_each_set_bit(link_id, &old_links_long, - IEEE80211_MLD_MAX_NUM_LINKS) { - mvm_sta_link = - rcu_dereference_protected(mvm_sta->link[link_id], - lockdep_is_held(&mvm->mutex)); - - if (WARN_ON(!mvm_sta_link)) { - ret = -EINVAL; - goto err; - } - - current_sta_mask |= BIT(mvm_sta_link->sta_id); - if (links_to_rem & BIT(link_id)) - sta_mask_to_rem |= BIT(mvm_sta_link->sta_id); - } - - if (sta_mask_to_rem) { - ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta, - current_sta_mask, - current_sta_mask & - ~sta_mask_to_rem); - if (WARN_ON(ret)) - goto err; - - current_sta_mask &= ~sta_mask_to_rem; - } - - for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { - mvm_sta_link = - rcu_dereference_protected(mvm_sta->link[link_id], - lockdep_is_held(&mvm->mutex)); - mvm_vif_link = mvm_vif->link[link_id]; - - if (WARN_ON(!mvm_sta_link || !mvm_vif_link)) { - ret = -EINVAL; - goto err; - } - - ret = iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id); - if (WARN_ON(ret)) - goto err; - - if (vif->type == NL80211_IFTYPE_STATION) - mvm_vif_link->ap_sta_id = IWL_INVALID_STA; - - iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id); - } - - for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { - struct ieee80211_bss_conf *link_conf = - link_conf_dereference_protected(vif, link_id); - struct ieee80211_link_sta *link_sta = - link_sta_dereference_protected(sta, link_id); - mvm_vif_link = mvm_vif->link[link_id]; - - if (WARN_ON(!mvm_vif_link || !link_conf || !link_sta)) { - ret = -EINVAL; - goto err; - } - - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - struct iwl_mvm_link_sta *mvm_link_sta = - rcu_dereference_protected(mvm_sta->link[link_id], - lockdep_is_held(&mvm->mutex)); - u32 sta_id; - - if (WARN_ON(!mvm_link_sta)) { - ret = -EINVAL; - goto err; - } - - sta_id = mvm_link_sta->sta_id; - - rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta); - rcu_assign_pointer(mvm->fw_id_to_link_sta[sta_id], - link_sta); - } else { - if (WARN_ON(mvm_sta->link[link_id])) { - ret = -EINVAL; - goto err; - } - ret = iwl_mvm_mld_alloc_sta_link(mvm, vif, sta, - link_id); - if (WARN_ON(ret)) - goto err; - } - - link_sta->agg.max_rc_amsdu_len = 1; - ieee80211_sta_recalc_aggregates(sta); - - mvm_sta_link = - rcu_dereference_protected(mvm_sta->link[link_id], - lockdep_is_held(&mvm->mutex)); - - if (WARN_ON(!mvm_sta_link)) { - ret = -EINVAL; - goto err; - } - - if (vif->type == NL80211_IFTYPE_STATION) - iwl_mvm_mld_set_ap_sta_id(sta, mvm_vif_link, - mvm_sta_link); - - link_sta_allocated |= BIT(link_id); - - sta_mask_added |= BIT(mvm_sta_link->sta_id); - - ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf, - mvm_sta_link); - if (WARN_ON(ret)) - goto err; - - link_sta_added_to_fw |= BIT(link_id); - - iwl_mvm_rs_add_sta_link(mvm, mvm_sta_link); - - iwl_mvm_rs_rate_init(mvm, vif, sta, link_conf, link_sta, - link_conf->chanreq.oper.chan->band); - } - - if (sta_mask_added) { - ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta, - current_sta_mask, - current_sta_mask | - sta_mask_added); - if (WARN_ON(ret)) - goto err; - } - - return 0; - -err: - /* remove all already allocated stations in FW */ - for_each_set_bit(link_id, &link_sta_added_to_fw, - IEEE80211_MLD_MAX_NUM_LINKS) { - mvm_sta_link = - rcu_dereference_protected(mvm_sta->link[link_id], - lockdep_is_held(&mvm->mutex)); - - iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id); - } - - /* remove all already allocated station links in driver */ - for_each_set_bit(link_id, &link_sta_allocated, - IEEE80211_MLD_MAX_NUM_LINKS) { - mvm_sta_link = - rcu_dereference_protected(mvm_sta->link[link_id], - lockdep_is_held(&mvm->mutex)); - - iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id); - } - - return ret; -} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 46a9dfa58a53..402ba5dee8b2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2450,11 +2450,6 @@ void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_vif_link_info *link, unsigned int link_id); -int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - u32 old_sta_mask, - u32 new_sta_mask); int iwl_mvm_mld_send_key(struct iwl_mvm *mvm, u32 sta_mask, u32 key_flags, struct ieee80211_key_conf *keyconf); u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c index ad156b82eaa9..f7b620136c85 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c @@ -304,7 +304,9 @@ void iwl_mvm_ptp_init(struct iwl_mvm *mvm) IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n", PTR_ERR(mvm->ptp_data.ptp_clock)); mvm->ptp_data.ptp_clock = NULL; - } else if (mvm->ptp_data.ptp_clock) { + } else if (!mvm->ptp_data.ptp_clock) { + IWL_DEBUG_INFO(mvm, "PTP module unavailable on this kernel\n"); + } else { IWL_DEBUG_INFO(mvm, "Registered PHC clock: %s, with index: %d\n", mvm->ptp_data.ptp_clock_info.name, ptp_clock_index(mvm->ptp_data.ptp_clock)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index c25edc7c1813..ff099aec7886 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -637,10 +637,6 @@ void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, struct iwl_mvm_link_sta *mvm_sta_link, unsigned int link_id); int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id); -int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - u16 old_links, u16 new_links); u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int filter_link_id); int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c index 4945ebf19f6b..a7cd2e4ba1ae 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c @@ -234,7 +234,7 @@ void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) * Also convert TU to msec. */ delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int); - mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, + mod_delayed_work(system_percpu_wq, &mvm->tdls_cs.dwork, msecs_to_jiffies(delay)); iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE); @@ -548,7 +548,7 @@ iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, */ delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int); - mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, + mod_delayed_work(system_percpu_wq, &mvm->tdls_cs.dwork, msecs_to_jiffies(delay)); return 0; } @@ -659,6 +659,6 @@ iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, /* register a timeout in case we don't succeed in switching */ delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int * 1024 / 1000; - mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, + mod_delayed_work(system_percpu_wq, &mvm->tdls_cs.dwork, msecs_to_jiffies(delay)); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c index b15c5d486527..a50e845cea42 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c @@ -95,7 +95,9 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave) CSR_GP_CNTRL_REG_FLAG_INIT_DONE); } -void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) +static void +_iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans, + bool dump_on_timeout) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; @@ -133,7 +135,7 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) "timeout waiting for FW reset ACK (inta_hw=0x%x, reset_done %d)\n", inta_hw, reset_done); - if (!reset_done) { + if (!reset_done && dump_on_timeout) { struct iwl_fw_error_dump_mode mode = { .type = IWL_ERR_TYPE_RESET_HS_TIMEOUT, .context = IWL_ERR_CONTEXT_FROM_OPMODE, @@ -147,6 +149,11 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) trans_pcie->fw_reset_state = FW_RESET_IDLE; } +void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) +{ + _iwl_trans_pcie_fw_reset_handshake(trans, false); +} + static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -163,7 +170,7 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) * should assume that the firmware is already dead. */ trans->state = IWL_TRANS_NO_FW; - iwl_trans_pcie_fw_reset_handshake(trans); + _iwl_trans_pcie_fw_reset_handshake(trans, true); } trans_pcie->is_down = true; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 4560d92d76fe..a05f60f9224b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -3197,7 +3197,7 @@ static ssize_t iwl_dbgfs_reset_write(struct file *file, if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) return -EINVAL; if (mode == IWL_RESET_MODE_TOP_RESET) { - if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC) + if (!iwl_trans_is_top_reset_supported(trans)) return -EINVAL; trans->request_top_reset = 1; } diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c index 2b705bdb7993..05eee64706ea 100644 --- a/drivers/net/wireless/mediatek/mt76/channel.c +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -88,6 +88,9 @@ void mt76_change_chanctx(struct ieee80211_hw *hw, IEEE80211_CHANCTX_CHANGE_RADAR))) return; + if (phy->roc_vif) + mt76_abort_roc(phy); + cancel_delayed_work_sync(&phy->mac_work); mutex_lock(&dev->mutex); @@ -155,8 +158,6 @@ void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw, { struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; - struct mt76_vif_data *mvif = mlink->mvif; - int link_id = link_conf->link_id; struct mt76_phy *phy = ctx->phy; struct mt76_dev *dev = phy->dev; @@ -173,15 +174,8 @@ void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw, if (!mlink) goto out; - if (mlink != (struct mt76_vif_link *)vif->drv_priv) - rcu_assign_pointer(mvif->link[link_id], NULL); - dev->drv->vif_link_remove(phy, vif, link_conf, mlink); mlink->ctx = NULL; - - if (mlink != (struct mt76_vif_link *)vif->drv_priv) - kfree_rcu(mlink, rcu_head); - out: mutex_unlock(&dev->mutex); } @@ -254,6 +248,8 @@ int mt76_switch_vif_chanctx(struct ieee80211_hw *hw, continue; mlink->ctx = vifs->new_ctx; + if (mlink->beacon_mon_interval) + WRITE_ONCE(mlink->beacon_mon_last, jiffies); } out: @@ -324,9 +320,11 @@ void mt76_roc_complete(struct mt76_phy *phy) if (mlink) mlink->mvif->roc_phy = NULL; - if (phy->main_chandef.chan && - !test_bit(MT76_MCU_RESET, &dev->phy.state)) - mt76_set_channel(phy, &phy->main_chandef, false); + if (phy->chanctx && phy->main_chandef.chan && phy->offchannel && + !test_bit(MT76_MCU_RESET, &dev->phy.state)) { + __mt76_set_channel(phy, &phy->main_chandef, false); + mt76_offchannel_notify(phy, false); + } mt76_put_vif_phy_link(phy, phy->roc_vif, phy->roc_link); phy->roc_vif = NULL; phy->roc_link = NULL; @@ -364,12 +362,15 @@ int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt76_phy *phy = hw->priv; struct mt76_dev *dev = phy->dev; struct mt76_vif_link *mlink; + bool offchannel; int ret = 0; phy = dev->band_phys[chan->band]; if (!phy) return -EINVAL; + cancel_delayed_work_sync(&phy->mac_work); + mutex_lock(&dev->mutex); if (phy->roc_vif || dev->scan.phy == phy || @@ -387,8 +388,18 @@ int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mlink->mvif->roc_phy = phy; phy->roc_vif = vif; phy->roc_link = mlink; - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); - mt76_set_channel(phy, &chandef, true); + + offchannel = mt76_offchannel_chandef(phy, chan, &chandef); + if (offchannel) + mt76_offchannel_notify(phy, true); + ret = __mt76_set_channel(phy, &chandef, offchannel); + if (ret) { + mlink->mvif->roc_phy = NULL; + phy->roc_vif = NULL; + phy->roc_link = NULL; + mt76_put_vif_phy_link(phy, vif, mlink); + goto out; + } ieee80211_ready_on_channel(hw); ieee80211_queue_delayed_work(phy->hw, &phy->roc_work, msecs_to_jiffies(duration)); diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index f240016ed9f0..f8c2fe5f2f58 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -6,6 +6,7 @@ #include #include "mt76.h" #include "dma.h" +#include "mt76_connac.h" static struct mt76_txwi_cache * mt76_alloc_txwi(struct mt76_dev *dev) @@ -188,16 +189,18 @@ mt76_dma_queue_magic_cnt_init(struct mt76_dev *dev, struct mt76_queue *q) static void mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q) { - Q_WRITE(q, desc_base, q->desc_dma); - if ((q->flags & MT_QFLAG_WED_RRO_EN) && !mt76_npu_device_active(dev)) + if ((q->flags & MT_QFLAG_WED_RRO_EN) && + (!is_mt7992(dev) || !mt76_npu_device_active(dev))) Q_WRITE(q, ring_size, MT_DMA_RRO_EN | q->ndesc); else Q_WRITE(q, ring_size, q->ndesc); if (mt76_queue_is_npu_tx(q)) { - writel(q->desc_dma, &q->regs->desc_base); writel(q->ndesc, &q->regs->ring_size); + writel(q->desc_dma, &q->regs->desc_base); } + + Q_WRITE(q, desc_base, q->desc_dma); q->head = Q_READ(q, dma_idx); q->tail = q->head; } @@ -663,6 +666,8 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q, if (!t) goto free_skb; + t->phy_idx = phy->band_idx; + t->qid = qid; txwi = mt76_get_txwi_ptr(dev, t); skb->prev = skb->next = NULL; @@ -874,7 +879,16 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q) if (!buf) break; - if (!mt76_queue_is_wed_rro(q)) + if (mtk_wed_device_active(&dev->mmio.wed) && + mt76_queue_is_wed_rro(q)) + continue; + + if (mt76_npu_device_active(dev) && + mt76_queue_is_wed_rro(q)) + continue; + + if (!mt76_queue_is_wed_rro_rxdmad_c(q) && + !mt76_queue_is_wed_rro_ind(q)) mt76_put_page_pool_buf(buf, false); } while (1); @@ -915,6 +929,13 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid) mt76_queue_is_wed_rro(q)) return; + if (mt76_npu_device_active(dev) && + mt76_queue_is_wed_rro(q)) + return; + + if (mt76_queue_is_npu_txfree(q)) + return; + mt76_dma_sync_idx(dev, q); if (mt76_queue_is_npu(q)) mt76_npu_fill_rx_queue(dev, q); @@ -1168,10 +1189,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev) mt76_for_each_q_rx(dev, i) { struct mt76_queue *q = &dev->q_rx[i]; - if (mtk_wed_device_active(&dev->mmio.wed) && - mt76_queue_is_wed_rro(q)) - continue; - netif_napi_del(&dev->napi[i]); mt76_dma_rx_cleanup(dev, q); diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h index 4a63de6c5bf5..2a0226c83f3c 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.h +++ b/drivers/net/wireless/mediatek/mt76/dma.h @@ -174,7 +174,9 @@ void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, static inline void mt76_dma_reset_tx_queue(struct mt76_dev *dev, struct mt76_queue *q) { - dev->queue_ops->reset_q(dev, q, true); + bool reset_idx = q && !mt76_queue_is_npu_tx(q); + + dev->queue_ops->reset_q(dev, q, reset_idx); if (mtk_wed_device_active(&dev->mmio.wed)) mt76_wed_dma_setup(dev, q, true); } diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c index 573400d57ce7..afdb73661866 100644 --- a/drivers/net/wireless/mediatek/mt76/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/eeprom.c @@ -9,6 +9,13 @@ #include #include #include "mt76.h" +#include "mt76_connac.h" + +enum mt76_sku_type { + MT76_SKU_RATE, + MT76_SKU_BACKOFF, + MT76_SKU_BACKOFF_BF_OFFSET, +}; static int mt76_get_of_eeprom_data(struct mt76_dev *dev, void *eep, int len) { @@ -292,7 +299,6 @@ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan) } EXPORT_SYMBOL_GPL(mt76_find_channel_node); - static s8 mt76_get_txs_delta(struct device_node *np, u8 nss) { @@ -306,9 +312,24 @@ mt76_get_txs_delta(struct device_node *np, u8 nss) return be32_to_cpu(val[nss - 1]); } +static inline u8 mt76_backoff_n_chains(struct mt76_dev *dev, u8 idx) +{ + /* 0:1T1ss, 1:2T1ss, ..., 14:5T5ss */ + static const u8 connac3_table[] = { + 1, 2, 3, 4, 5, 2, 3, 4, 5, 3, 4, 5, 4, 5, 5}; + static const u8 connac2_table[] = { + 1, 2, 3, 4, 2, 3, 4, 3, 4, 4, 0, 0, 0, 0, 0}; + + if (idx >= ARRAY_SIZE(connac3_table)) + return 0; + + return is_mt799x(dev) ? connac3_table[idx] : connac2_table[idx]; +} + static void -mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const s8 *data, - s8 target_power, s8 nss_delta, s8 *max_power) +mt76_apply_array_limit(struct mt76_dev *dev, s8 *pwr, size_t pwr_len, + const s8 *data, s8 target_power, s8 nss_delta, + s8 *max_power, int n_chains, enum mt76_sku_type type) { int i; @@ -316,18 +337,51 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const s8 *data, return; for (i = 0; i < pwr_len; i++) { - pwr[i] = min_t(s8, target_power, data[i] + nss_delta); + u8 backoff_chain_idx = i; + int backoff_n_chains; + s8 backoff_delta; + s8 delta; + + switch (type) { + case MT76_SKU_RATE: + delta = 0; + backoff_delta = 0; + backoff_n_chains = 0; + break; + case MT76_SKU_BACKOFF_BF_OFFSET: + backoff_chain_idx += 1; + fallthrough; + case MT76_SKU_BACKOFF: + delta = mt76_tx_power_path_delta(n_chains); + backoff_n_chains = mt76_backoff_n_chains(dev, backoff_chain_idx); + backoff_delta = mt76_tx_power_path_delta(backoff_n_chains); + break; + default: + return; + } + + pwr[i] = min_t(s8, target_power + delta - backoff_delta, data[i] + nss_delta); + + /* used for padding, doesn't need to be considered */ + if (data[i] >= S8_MAX - 1) + continue; + + /* only consider backoff value for the configured chain number */ + if (type != MT76_SKU_RATE && n_chains != backoff_n_chains) + continue; + *max_power = max(*max_power, pwr[i]); } } static void -mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num, - const s8 *data, size_t len, s8 target_power, - s8 nss_delta) +mt76_apply_multi_array_limit(struct mt76_dev *dev, s8 *pwr, size_t pwr_len, + s8 pwr_num, const s8 *data, size_t len, + s8 target_power, s8 nss_delta, s8 *max_power, + int n_chains, enum mt76_sku_type type) { + static const int connac2_backoff_ru_idx = 2; int i, cur; - s8 max_power = -128; if (!data) return; @@ -337,8 +391,26 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num, if (len < pwr_len + 1) break; - mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1, - target_power, nss_delta, &max_power); + /* Each RU entry (RU26, RU52, RU106, BW20, ...) in the DTS + * corresponds to 10 stream combinations (1T1ss, 2T1ss, 3T1ss, + * 4T1ss, 2T2ss, 3T2ss, 4T2ss, 3T3ss, 4T3ss, 4T4ss). + * + * For beamforming tables: + * - In connac2, beamforming entries for BW20~BW160 and OFDM + * do not include 1T1ss. + * - In connac3, beamforming entries for BW20~BW160 and RU + * include 1T1ss, but OFDM beamforming does not include 1T1ss. + * + * Non-beamforming and RU entries for both connac2 and connac3 + * include 1T1ss. + */ + if (!is_mt799x(dev) && type == MT76_SKU_BACKOFF && + i > connac2_backoff_ru_idx) + type = MT76_SKU_BACKOFF_BF_OFFSET; + + mt76_apply_array_limit(dev, pwr + pwr_len * i, pwr_len, data + 1, + target_power, nss_delta, max_power, + n_chains, type); if (--cur > 0) continue; @@ -360,18 +432,11 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy, struct device_node *np; const s8 *val; char name[16]; - u32 mcs_rates = dev->drv->mcs_rates; - u32 ru_rates = ARRAY_SIZE(dest->ru[0]); char band; size_t len; - s8 max_power = 0; - s8 max_power_backoff = -127; + s8 max_power = -127; s8 txs_delta; int n_chains = hweight16(phy->chainmask); - s8 target_power_combine = target_power + mt76_tx_power_path_delta(n_chains); - - if (!mcs_rates) - mcs_rates = 10; memset(dest, target_power, sizeof(*dest) - sizeof(dest->path)); memset(&dest->path, 0, sizeof(dest->path)); @@ -409,46 +474,45 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy, txs_delta = mt76_get_txs_delta(np, hweight16(phy->chainmask)); val = mt76_get_of_array_s8(np, "rates-cck", &len, ARRAY_SIZE(dest->cck)); - mt76_apply_array_limit(dest->cck, ARRAY_SIZE(dest->cck), val, - target_power, txs_delta, &max_power); + mt76_apply_array_limit(dev, dest->cck, ARRAY_SIZE(dest->cck), val, + target_power, txs_delta, &max_power, n_chains, MT76_SKU_RATE); - val = mt76_get_of_array_s8(np, "rates-ofdm", - &len, ARRAY_SIZE(dest->ofdm)); - mt76_apply_array_limit(dest->ofdm, ARRAY_SIZE(dest->ofdm), val, - target_power, txs_delta, &max_power); + val = mt76_get_of_array_s8(np, "rates-ofdm", &len, ARRAY_SIZE(dest->ofdm)); + mt76_apply_array_limit(dev, dest->ofdm, ARRAY_SIZE(dest->ofdm), val, + target_power, txs_delta, &max_power, n_chains, MT76_SKU_RATE); - val = mt76_get_of_array_s8(np, "rates-mcs", &len, mcs_rates + 1); - mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]), - ARRAY_SIZE(dest->mcs), val, len, - target_power, txs_delta); + val = mt76_get_of_array_s8(np, "rates-mcs", &len, ARRAY_SIZE(dest->mcs[0]) + 1); + mt76_apply_multi_array_limit(dev, dest->mcs[0], ARRAY_SIZE(dest->mcs[0]), + ARRAY_SIZE(dest->mcs), val, len, target_power, + txs_delta, &max_power, n_chains, MT76_SKU_RATE); - val = mt76_get_of_array_s8(np, "rates-ru", &len, ru_rates + 1); - mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]), - ARRAY_SIZE(dest->ru), val, len, - target_power, txs_delta); + val = mt76_get_of_array_s8(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1); + mt76_apply_multi_array_limit(dev, dest->ru[0], ARRAY_SIZE(dest->ru[0]), + ARRAY_SIZE(dest->ru), val, len, target_power, + txs_delta, &max_power, n_chains, MT76_SKU_RATE); - max_power_backoff = max_power; val = mt76_get_of_array_s8(np, "paths-cck", &len, ARRAY_SIZE(dest->path.cck)); - mt76_apply_array_limit(dest->path.cck, ARRAY_SIZE(dest->path.cck), val, - target_power_combine, txs_delta, &max_power_backoff); + mt76_apply_array_limit(dev, dest->path.cck, ARRAY_SIZE(dest->path.cck), val, + target_power, txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF); val = mt76_get_of_array_s8(np, "paths-ofdm", &len, ARRAY_SIZE(dest->path.ofdm)); - mt76_apply_array_limit(dest->path.ofdm, ARRAY_SIZE(dest->path.ofdm), val, - target_power_combine, txs_delta, &max_power_backoff); + mt76_apply_array_limit(dev, dest->path.ofdm, ARRAY_SIZE(dest->path.ofdm), val, + target_power, txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF); val = mt76_get_of_array_s8(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest->path.ofdm_bf)); - mt76_apply_array_limit(dest->path.ofdm_bf, ARRAY_SIZE(dest->path.ofdm_bf), val, - target_power_combine, txs_delta, &max_power_backoff); + mt76_apply_array_limit(dev, dest->path.ofdm_bf, ARRAY_SIZE(dest->path.ofdm_bf), val, + target_power, txs_delta, &max_power, n_chains, + MT76_SKU_BACKOFF_BF_OFFSET); val = mt76_get_of_array_s8(np, "paths-ru", &len, ARRAY_SIZE(dest->path.ru[0]) + 1); - mt76_apply_multi_array_limit(dest->path.ru[0], ARRAY_SIZE(dest->path.ru[0]), - ARRAY_SIZE(dest->path.ru), val, len, - target_power_combine, txs_delta); + mt76_apply_multi_array_limit(dev, dest->path.ru[0], ARRAY_SIZE(dest->path.ru[0]), + ARRAY_SIZE(dest->path.ru), val, len, target_power, + txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF); val = mt76_get_of_array_s8(np, "paths-ru-bf", &len, ARRAY_SIZE(dest->path.ru_bf[0]) + 1); - mt76_apply_multi_array_limit(dest->path.ru_bf[0], ARRAY_SIZE(dest->path.ru_bf[0]), - ARRAY_SIZE(dest->path.ru_bf), val, len, - target_power_combine, txs_delta); + mt76_apply_multi_array_limit(dev, dest->path.ru_bf[0], ARRAY_SIZE(dest->path.ru_bf[0]), + ARRAY_SIZE(dest->path.ru_bf), val, len, target_power, + txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF); return max_power; } diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 75772979f438..4ae5e4715a9c 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -726,6 +726,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size, INIT_LIST_HEAD(&dev->rxwi_cache); dev->token_size = dev->drv->token_size; INIT_DELAYED_WORK(&dev->scan_work, mt76_scan_work); + spin_lock_init(&dev->scan_lock); for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) skb_queue_head_init(&dev->rx_skb[i]); @@ -970,6 +971,9 @@ bool mt76_has_tx_pending(struct mt76_phy *phy) return true; } + if (atomic_read(&phy->mgmt_tx_pending)) + return true; + return false; } EXPORT_SYMBOL_GPL(mt76_has_tx_pending); @@ -1030,9 +1034,10 @@ int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef, int timeout = HZ / 5; int ret; - set_bit(MT76_RESET, &phy->state); - mt76_worker_disable(&dev->tx_worker); + mt76_txq_schedule_pending(phy); + + set_bit(MT76_RESET, &phy->state); wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout); mt76_update_survey(phy); @@ -1716,6 +1721,16 @@ void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid) idr_destroy(&wcid->pktid); + /* Remove from sta_poll_list to prevent list corruption after reset. + * Without this, mt76_reset_device() reinitializes sta_poll_list but + * leaves wcid->poll_list with stale pointers, causing list corruption + * when mt76_wcid_add_poll() checks list_empty(). + */ + spin_lock_bh(&dev->sta_poll_lock); + if (!list_empty(&wcid->poll_list)) + list_del_init(&wcid->poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + spin_lock_bh(&phy->tx_lock); if (!list_empty(&wcid->tx_list)) @@ -2121,3 +2136,214 @@ u16 mt76_select_links(struct ieee80211_vif *vif, int max_active_links) return sel_links; } EXPORT_SYMBOL_GPL(mt76_select_links); + +struct mt76_offchannel_cb_data { + struct mt76_phy *phy; + bool offchannel; +}; + +static void +mt76_offchannel_send_nullfunc(struct mt76_offchannel_cb_data *data, + struct ieee80211_vif *vif, int link_id) +{ + struct mt76_phy *phy = data->phy; + struct ieee80211_tx_info *info; + struct ieee80211_sta *sta = NULL; + struct ieee80211_hdr *hdr; + struct mt76_wcid *wcid; + struct sk_buff *skb; + + skb = ieee80211_nullfunc_get(phy->hw, vif, link_id, true); + if (!skb) + return; + + hdr = (struct ieee80211_hdr *)skb->data; + if (data->offchannel) + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + + skb->priority = 7; + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + + if (!ieee80211_tx_prepare_skb(phy->hw, vif, skb, + phy->main_chandef.chan->band, + &sta)) + return; + + if (sta) + wcid = (struct mt76_wcid *)sta->drv_priv; + else + wcid = ((struct mt76_vif_link *)vif->drv_priv)->wcid; + + if (link_id >= 0) { + info = IEEE80211_SKB_CB(skb); + info->control.flags &= ~IEEE80211_TX_CTRL_MLO_LINK; + info->control.flags |= + u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK); + } + + mt76_tx(phy, sta, wcid, skb); +} + +static void +mt76_offchannel_notify_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt76_offchannel_cb_data *data = _data; + struct mt76_vif_link *mlink; + struct mt76_vif_data *mvif; + int link_id; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return; + + mlink = (struct mt76_vif_link *)vif->drv_priv; + mvif = mlink->mvif; + + if (!ieee80211_vif_is_mld(vif)) { + if (mt76_vif_link_phy(mlink) == data->phy) { + if (!data->offchannel && mlink->beacon_mon_interval) + WRITE_ONCE(mlink->beacon_mon_last, jiffies); + mt76_offchannel_send_nullfunc(data, vif, -1); + } + return; + } + + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + if (link_id == mvif->deflink_id) + mlink = (struct mt76_vif_link *)vif->drv_priv; + else + mlink = rcu_dereference(mvif->link[link_id]); + if (!mlink) + continue; + if (mt76_vif_link_phy(mlink) != data->phy) + continue; + + if (!data->offchannel && mlink->beacon_mon_interval) + WRITE_ONCE(mlink->beacon_mon_last, jiffies); + + mt76_offchannel_send_nullfunc(data, vif, link_id); + } +} + +void mt76_offchannel_notify(struct mt76_phy *phy, bool offchannel) +{ + struct mt76_offchannel_cb_data data = { + .phy = phy, + .offchannel = offchannel, + }; + + if (!phy->num_sta) + return; + + local_bh_disable(); + ieee80211_iterate_active_interfaces_atomic(phy->hw, + IEEE80211_IFACE_ITER_NORMAL, + mt76_offchannel_notify_iter, &data); + local_bh_enable(); +} +EXPORT_SYMBOL_GPL(mt76_offchannel_notify); + +struct mt76_rx_beacon_data { + struct mt76_phy *phy; + const u8 *bssid; +}; + +static void mt76_rx_beacon_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt76_rx_beacon_data *data = _data; + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + int link_id; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return; + + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + struct ieee80211_bss_conf *link_conf; + + if (link_id == mvif->deflink_id) + mlink = (struct mt76_vif_link *)vif->drv_priv; + else + mlink = rcu_dereference(mvif->link[link_id]); + if (!mlink || !mlink->beacon_mon_interval) + continue; + + if (mt76_vif_link_phy(mlink) != data->phy) + continue; + + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (!link_conf) + continue; + + if (!ether_addr_equal(link_conf->bssid, data->bssid) && + (!link_conf->nontransmitted || + !ether_addr_equal(link_conf->transmitter_bssid, + data->bssid))) + continue; + + WRITE_ONCE(mlink->beacon_mon_last, jiffies); + } +} + +void mt76_rx_beacon(struct mt76_phy *phy, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); + struct mt76_rx_beacon_data data = { + .phy = phy, + .bssid = hdr->addr3, + }; + + mt76_scan_rx_beacon(phy->dev, phy->chandef.chan); + + if (!phy->num_sta) + return; + + if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_ONLY_MONITOR)) + return; + + ieee80211_iterate_active_interfaces_atomic(phy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76_rx_beacon_iter, &data); +} +EXPORT_SYMBOL_GPL(mt76_rx_beacon); + +static void mt76_beacon_mon_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt76_phy *phy = data; + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + int link_id; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return; + + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + if (link_id == mvif->deflink_id) + mlink = (struct mt76_vif_link *)vif->drv_priv; + else + mlink = rcu_dereference(mvif->link[link_id]); + if (!mlink || !mlink->beacon_mon_interval) + continue; + + if (mt76_vif_link_phy(mlink) != phy) + continue; + + if (time_after(jiffies, + READ_ONCE(mlink->beacon_mon_last) + + MT76_BEACON_MON_MAX_MISS * mlink->beacon_mon_interval)) + ieee80211_beacon_loss(vif); + } +} + +void mt76_beacon_mon_check(struct mt76_phy *phy) +{ + if (phy->offchannel) + return; + + ieee80211_iterate_active_interfaces_atomic(phy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76_beacon_mon_iter, phy); +} +EXPORT_SYMBOL_GPL(mt76_beacon_mon_check); diff --git a/drivers/net/wireless/mediatek/mt76/mcu.c b/drivers/net/wireless/mediatek/mt76/mcu.c index 535c3d8a9cc0..cbfb3bbec503 100644 --- a/drivers/net/wireless/mediatek/mt76/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mcu.c @@ -98,7 +98,7 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb, /* orig skb might be needed for retry, mcu_skb_send_msg consumes it */ if (orig_skb) skb_get(orig_skb); - ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, &seq); + ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, wait_resp ? &seq : NULL); if (ret < 0) goto out; diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index d05e83ea1cac..527bef97e122 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -55,6 +55,8 @@ FIELD_PREP(MT_QFLAG_WED_RING, _n)) #define MT_NPU_Q_TX(_n) __MT_NPU_Q(MT76_WED_Q_TX, _n) #define MT_NPU_Q_RX(_n) __MT_NPU_Q(MT76_WED_Q_RX, _n) +#define MT_NPU_Q_TXFREE(_n) (FIELD_PREP(MT_QFLAG_WED_TYPE, MT76_WED_Q_TXFREE) | \ + FIELD_PREP(MT_QFLAG_WED_RING, _n)) struct mt76_dev; struct mt76_phy; @@ -362,6 +364,7 @@ enum mt76_wcid_flags { }; #define MT76_N_WCIDS 1088 +#define MT76_BEACON_MON_MAX_MISS 7 /* stored in ieee80211_tx_info::hw_queue */ #define MT_TX_HW_QUEUE_PHY GENMASK(3, 2) @@ -448,6 +451,7 @@ struct mt76_txwi_cache { }; u8 qid; + u8 phy_idx; }; struct mt76_rx_tid { @@ -540,7 +544,6 @@ struct mt76_driver_ops { u32 survey_flags; u16 txwi_size; u16 token_size; - u8 mcs_rates; unsigned int link_data_size; @@ -831,6 +834,8 @@ struct mt76_vif_link { u8 mcast_rates_idx; u8 beacon_rates_idx; bool offchannel; + unsigned long beacon_mon_last; + u16 beacon_mon_interval; struct ieee80211_chanctx_conf *ctx; struct mt76_wcid *wcid; struct mt76_vif_data *mvif; @@ -859,6 +864,8 @@ struct mt76_phy { struct list_head tx_list; struct mt76_queue *q_tx[__MT_TXQ_MAX]; + atomic_t mgmt_tx_pending; + struct cfg80211_chan_def chandef; struct cfg80211_chan_def main_chandef; bool offchannel; @@ -1002,6 +1009,7 @@ struct mt76_dev { u32 rxfilter; struct delayed_work scan_work; + spinlock_t scan_lock; struct { struct cfg80211_scan_request *req; struct ieee80211_channel *chan; @@ -1009,6 +1017,8 @@ struct mt76_dev { struct mt76_vif_link *mlink; struct mt76_phy *phy; int chan_idx; + bool beacon_wait; + bool beacon_received; } scan; #ifdef CONFIG_NL80211_TESTMODE @@ -1518,6 +1528,7 @@ void mt76_stop_tx_queues(struct mt76_phy *phy, struct ieee80211_sta *sta, void mt76_tx_check_agg_ssn(struct ieee80211_sta *sta, struct sk_buff *skb); void mt76_txq_schedule(struct mt76_phy *phy, enum mt76_txq_id qid); void mt76_txq_schedule_all(struct mt76_phy *phy); +void mt76_txq_schedule_pending(struct mt76_phy *phy); void mt76_tx_worker_run(struct mt76_dev *dev); void mt76_tx_worker(struct mt76_worker *w); void mt76_release_buffered_frames(struct ieee80211_hw *hw, @@ -1596,6 +1607,9 @@ int mt76_get_rate(struct mt76_dev *dev, int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *hw_req); void mt76_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan); +void mt76_rx_beacon(struct mt76_phy *phy, struct sk_buff *skb); +void mt76_beacon_mon_check(struct mt76_phy *phy); void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *mac); void mt76_sw_scan_complete(struct ieee80211_hw *hw, @@ -1649,6 +1663,9 @@ void mt76_npu_txdesc_cleanup(struct mt76_queue *q, int index); int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct net_device *dev, enum tc_setup_type type, void *type_data); +int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex, + u32 direction, u32 i_count_addr, + u32 o_status_addr, u32 o_count_addr); #else static inline void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb, u32 info) @@ -1707,6 +1724,13 @@ static inline int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, { return -EOPNOTSUPP; } + +static inline int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex, + u32 direction, u32 i_count_addr, + u32 o_status_addr, u32 o_count_addr) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_MT76_NPU */ static inline bool mt76_npu_device_active(struct mt76_dev *dev) @@ -1775,6 +1799,18 @@ void mt76_queue_tx_complete(struct mt76_dev *dev, struct mt76_queue *q, struct mt76_queue_entry *e); int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef, bool offchannel); + +static inline bool +mt76_offchannel_chandef(struct mt76_phy *phy, struct ieee80211_channel *chan, + struct cfg80211_chan_def *chandef) +{ + cfg80211_chandef_create(chandef, chan, NL80211_CHAN_HT20); + if (phy->main_chandef.chan != chan) + return true; + + *chandef = phy->main_chandef; + return false; +} int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef, bool offchannel); void mt76_scan_work(struct work_struct *work); @@ -1786,6 +1822,7 @@ struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif); void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink); +void mt76_offchannel_notify(struct mt76_phy *phy, bool offchannel); /* usb */ static inline bool mt76u_urb_error(struct urb *urb) @@ -1993,6 +2030,14 @@ static inline bool mt76_queue_is_npu_rx(struct mt76_queue *q) FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX; } +static inline bool mt76_queue_is_npu_txfree(struct mt76_queue *q) +{ + if (q->flags & MT_QFLAG_WED) + return false; + + return FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_TXFREE; +} + struct mt76_txwi_cache * mt76_token_release(struct mt76_dev *dev, int token, bool *wake); int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index 45992fdcec60..ce0051468501 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -1167,21 +1167,6 @@ void mt7615_mac_set_rates(struct mt7615_phy *phy, struct mt7615_sta *sta, } EXPORT_SYMBOL_GPL(mt7615_mac_set_rates); -void mt7615_mac_enable_rtscts(struct mt7615_dev *dev, - struct ieee80211_vif *vif, bool enable) -{ - struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - u32 addr; - - addr = mt7615_mac_wtbl_addr(dev, mvif->sta.wcid.idx) + 3 * 4; - - if (enable) - mt76_set(dev, addr, MT_WTBL_W3_RTS); - else - mt76_clear(dev, addr, MT_WTBL_W3_RTS); -} -EXPORT_SYMBOL_GPL(mt7615_mac_enable_rtscts); - static int mt7615_mac_wtbl_update_key(struct mt7615_dev *dev, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 727266892c3d..fc619acbb40d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -583,9 +583,6 @@ static void mt7615_bss_info_changed(struct ieee80211_hw *hw, } } - if (changed & BSS_CHANGED_ERP_CTS_PROT) - mt7615_mac_enable_rtscts(dev, vif, info->use_cts_prot); - if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) { mt7615_mcu_add_bss_info(phy, vif, NULL, true); mt7615_mcu_sta_add(phy, vif, NULL, true); @@ -598,6 +595,10 @@ static void mt7615_bss_info_changed(struct ieee80211_hw *hw, BSS_CHANGED_BEACON_ENABLED)) mt7615_mcu_add_beacon(dev, hw, vif, info->enable_beacon); + if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) + mt7615_mcu_set_protection(phy, vif, info->ht_operation_mode, + info->use_cts_prot); + if (changed & BSS_CHANGED_PS) mt76_connac_mcu_set_vif_ps(&dev->mt76, vif); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c index fc0054f8bd60..ff57ede87f71 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c @@ -2564,3 +2564,50 @@ int mt7615_mcu_set_roc(struct mt7615_phy *phy, struct ieee80211_vif *vif, return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_ROC), &req, sizeof(req), false); } + +int mt7615_mcu_set_protection(struct mt7615_phy *phy, struct ieee80211_vif *vif, + u8 ht_mode, bool use_cts_prot) +{ + struct mt7615_dev *dev = phy->dev; + struct { + u8 prot_idx; + u8 band; + u8 rsv[2]; + + bool long_nav; + bool prot_mm; + bool prot_gf; + bool prot_bw40; + bool prot_rifs; + bool prot_bw80; + bool prot_bw160; + u8 prot_erp_mask; + } __packed req = { + .prot_idx = 0x2, + .band = phy != &dev->phy, + }; + + switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + req.prot_mm = true; + req.prot_gf = true; + fallthrough; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + req.prot_bw40 = true; + break; + } + + if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + req.prot_gf = true; + + if (use_cts_prot) { + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + u8 i = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : mvif->mt76.omac_idx; + + req.prot_erp_mask = BIT(i); + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(PROTECT_CTRL), &req, + sizeof(req), true); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h index c93fd245c90f..391928405f32 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -467,8 +467,6 @@ void mt7615_mac_reset_counters(struct mt7615_phy *phy); void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy); void mt7615_mac_set_scs(struct mt7615_phy *phy, bool enable); void mt7615_mac_enable_nf(struct mt7615_dev *dev, bool ext_phy); -void mt7615_mac_enable_rtscts(struct mt7615_dev *dev, - struct ieee80211_vif *vif, bool enable); void mt7615_mac_sta_poll(struct mt7615_dev *dev); int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, @@ -523,7 +521,8 @@ int mt7615_mcu_set_sku_en(struct mt7615_phy *phy, bool enable); int mt7615_mcu_apply_rx_dcoc(struct mt7615_phy *phy); int mt7615_mcu_apply_tx_dpd(struct mt7615_phy *phy); int mt7615_dfs_init_radar_detector(struct mt7615_phy *phy); - +int mt7615_mcu_set_protection(struct mt7615_phy *phy, struct ieee80211_vif *vif, + u8 ht_mode, bool use_cts_prot); int mt7615_mcu_set_roc(struct mt7615_phy *phy, struct ieee80211_vif *vif, struct ieee80211_channel *chan, int duration); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h index eb3c24d51987..e4133e9181d0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h @@ -455,8 +455,6 @@ enum mt7615_reg_base { #define MT_WTBL_RIUCR3_RATE6 GENMASK(19, 8) #define MT_WTBL_RIUCR3_RATE7 GENMASK(31, 20) -#define MT_WTBL_W3_RTS BIT(22) - #define MT_WTBL_W5_CHANGE_BW_RATE GENMASK(7, 5) #define MT_WTBL_W5_SHORT_GI_20 BIT(8) #define MT_WTBL_W5_SHORT_GI_40 BIT(9) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 813d61bffc2c..51423c7740bd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -182,14 +182,20 @@ static inline bool is_mt7920(struct mt76_dev *dev) return mt76_chip(dev) == 0x7920; } +static inline bool is_mt7902(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7902; +} + static inline bool is_mt7922(struct mt76_dev *dev) { return mt76_chip(dev) == 0x7922; } -static inline bool is_mt7921(struct mt76_dev *dev) +static inline bool is_connac2(struct mt76_dev *dev) { - return mt76_chip(dev) == 0x7961 || is_mt7922(dev) || is_mt7920(dev); + return mt76_chip(dev) == 0x7961 || is_mt7922(dev) || is_mt7920(dev) || + is_mt7902(dev); } static inline bool is_mt7663(struct mt76_dev *dev) @@ -271,6 +277,7 @@ static inline bool is_mt76_fw_txp(struct mt76_dev *dev) case 0x7961: case 0x7920: case 0x7922: + case 0x7902: case 0x7925: case 0x7663: case 0x7622: diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index f946ddc20a47..0339e2e7ab60 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -173,7 +173,7 @@ void mt76_connac_write_hw_txp(struct mt76_dev *dev, txp->msdu_id[0] = cpu_to_le16(id | MT_MSDU_ID_VALID); - if (is_mt7663(dev) || is_mt7921(dev) || is_mt7925(dev)) + if (is_mt7663(dev) || is_connac2(dev) || is_mt7925(dev)) last_mask = MT_TXD_LEN_LAST; else last_mask = MT_TXD_LEN_AMSDU_LAST | @@ -217,7 +217,7 @@ mt76_connac_txp_skb_unmap_hw(struct mt76_dev *dev, u32 last_mask; int i; - if (is_mt7663(dev) || is_mt7921(dev) || is_mt7925(dev)) + if (is_mt7663(dev) || is_connac2(dev) || is_mt7925(dev)) last_mask = MT_TXD_LEN_LAST; else last_mask = MT_TXD_LEN_MSDU_LAST; @@ -309,7 +309,7 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy, chandef = mvif->ctx ? &mvif->ctx->def : &mphy->chandef; band = chandef->chan->band; - if (is_mt7921(mphy->dev)) { + if (is_connac2(mphy->dev)) { rateidx = ffs(conf->basic_rates) - 1; goto legacy; } @@ -548,7 +548,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, val = MT_TXD1_LONG_FORMAT | FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) | FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx); - if (!is_mt7921(dev)) + if (!is_connac2(dev)) val |= MT_TXD1_VTA; if (phy_idx || band_idx) val |= MT_TXD1_TGID; @@ -557,7 +557,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, txwi[2] = 0; val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, 15); - if (!is_mt7921(dev)) + if (!is_connac2(dev)) val |= MT_TXD3_SW_POWER_MGMT; if (key) val |= MT_TXD3_PROTECT_FRAME; @@ -599,7 +599,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, txwi[6] |= cpu_to_le32(val); txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); - if (!is_mt7921(dev)) { + if (!is_connac2(dev)) { u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask); if (!spe_idx) @@ -831,7 +831,7 @@ mt76_connac2_mac_decode_he_mu_radiotap(struct mt76_dev *dev, struct sk_buff *skb }; struct ieee80211_radiotap_he_mu *he_mu; - if (is_mt7921(dev)) { + if (is_connac2(dev)) { mu_known.flags1 |= HE_BITS(MU_FLAGS1_SIG_B_COMP_KNOWN); mu_known.flags2 |= HE_BITS(MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN); } @@ -1047,7 +1047,7 @@ int mt76_connac2_mac_fill_rx_rate(struct mt76_dev *dev, stbc = FIELD_GET(MT_PRXV_HT_STBC, v0); gi = FIELD_GET(MT_PRXV_HT_SGI, v0); *mode = FIELD_GET(MT_PRXV_TX_MODE, v0); - if (is_mt7921(dev)) + if (is_connac2(dev)) dcm = !!(idx & MT_PRXV_TX_DCM); else dcm = FIELD_GET(MT_PRXV_DCM, v0); @@ -1153,8 +1153,10 @@ void mt76_connac2_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi) return; wcid = (struct mt76_wcid *)sta->drv_priv; - if (!test_and_set_bit(tid, &wcid->ampdu_state)) - ieee80211_start_tx_ba_session(sta, tid, 0); + if (!test_and_set_bit(tid, &wcid->ampdu_state)) { + if (ieee80211_start_tx_ba_session(sta, tid, 0)) + clear_bit(tid, &wcid->ampdu_state); + } } EXPORT_SYMBOL_GPL(mt76_connac2_tx_check_aggr); @@ -1207,5 +1209,11 @@ void mt76_connac2_tx_token_put(struct mt76_dev *dev) } spin_unlock_bh(&dev->token_lock); idr_destroy(&dev->token); + + for (id = 0; id < __MT_MAX_BAND; id++) { + struct mt76_phy *phy = dev->phys[id]; + if (phy) + atomic_set(&phy->mgmt_tx_pending, 0); + } } EXPORT_SYMBOL_GPL(mt76_connac2_tx_token_put); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 0457712286d5..89bd52ea8bf7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -4,6 +4,7 @@ #include #include "mt76_connac2_mac.h" #include "mt76_connac_mcu.h" +#include "mt792x_regs.h" int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option) { @@ -65,7 +66,7 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, int cmd; if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || - (is_mt7921(dev) && addr == 0x900000) || + (is_connac2(dev) && addr == 0x900000) || (is_mt7925(dev) && (addr == 0x900000 || addr == 0xe0002800)) || (is_mt799x(dev) && addr == 0x900000)) cmd = MCU_CMD(PATCH_START_REQ); @@ -402,7 +403,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb, switch (vif->type) { case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: - if (vif->p2p && !is_mt7921(dev)) + if (vif->p2p && !is_connac2(dev)) conn_type = CONNECTION_P2P_GC; else conn_type = CONNECTION_INFRA_STA; @@ -410,7 +411,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb, basic->aid = cpu_to_le16(link_sta->sta->aid); break; case NL80211_IFTYPE_STATION: - if (vif->p2p && !is_mt7921(dev)) + if (vif->p2p && !is_connac2(dev)) conn_type = CONNECTION_P2P_GO; else conn_type = CONNECTION_INFRA_AP; @@ -874,7 +875,7 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb, struct sta_rec_vht *vht; int len; - len = is_mt7921(dev) ? sizeof(*vht) : sizeof(*vht) - 4; + len = is_connac2(dev) ? sizeof(*vht) : sizeof(*vht) - 4; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, len); vht = (struct sta_rec_vht *)tlv; vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap); @@ -885,7 +886,7 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb, /* starec uapsd */ mt76_connac_mcu_sta_uapsd(skb, vif, sta); - if (!is_mt7921(dev)) + if (!is_connac2(dev)) return; if (sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he) @@ -1295,8 +1296,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, wtbl_hdr); ret = mt76_connac_mcu_sta_wed_update(dev, skb); - if (ret) + if (ret) { + dev_kfree_skb(skb); return ret; + } ret = mt76_mcu_skb_send_msg(dev, skb, cmd, true); if (ret) @@ -1309,8 +1312,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, mt76_connac_mcu_sta_ba_tlv(skb, params, enable, tx); ret = mt76_connac_mcu_sta_wed_update(dev, skb); - if (ret) + if (ret) { + dev_kfree_skb(skb); return ret; + } return mt76_mcu_skb_send_msg(dev, skb, cmd, true); } @@ -1774,7 +1779,7 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, req->ssid_type_ext = n_ssids ? BIT(0) : 0; req->ssids_num = n_ssids; - duration = is_mt7921(phy->dev) ? 0 : MT76_CONNAC_SCAN_CHANNEL_TIME; + duration = is_connac2(phy->dev) ? 0 : MT76_CONNAC_SCAN_CHANNEL_TIME; /* increase channel time for passive scan */ if (!sreq->n_ssids) duration *= 2; @@ -1817,7 +1822,7 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, req->ies_len = cpu_to_le16(sreq->ie_len); } - if (is_mt7921(phy->dev)) + if (is_connac2(phy->dev)) req->scan_func |= SCAN_FUNC_SPLIT_SCAN; memcpy(req->bssid, sreq->bssid, ETH_ALEN); @@ -1893,7 +1898,7 @@ int mt76_connac_mcu_sched_scan_req(struct mt76_phy *phy, get_random_mask_addr(addr, sreq->mac_addr, sreq->mac_addr_mask); } - if (is_mt7921(phy->dev)) { + if (is_connac2(phy->dev)) { req->mt7921.bss_idx = mvif->idx; req->mt7921.delay = cpu_to_le32(sreq->delay); } @@ -2033,7 +2038,7 @@ mt76_connac_mcu_build_sku(struct mt76_dev *dev, s8 *sku, struct mt76_power_limits *limits, enum nl80211_band band) { - int max_power = is_mt7921(dev) ? 127 : 63; + int max_power = is_connac2(dev) ? 127 : 63; int i, offset = sizeof(limits->cck); memset(sku, max_power, MT_SKU_POWER_LIMIT); @@ -2061,7 +2066,7 @@ mt76_connac_mcu_build_sku(struct mt76_dev *dev, s8 *sku, offset += 12; } - if (!is_mt7921(dev)) + if (!is_connac2(dev)) return; /* he */ @@ -2117,7 +2122,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy, enum nl80211_band band) { struct mt76_dev *dev = phy->dev; - int sku_len, batch_len = is_mt7921(dev) ? 8 : 16; + int sku_len, batch_len = is_connac2(dev) ? 8 : 16; static const u8 chan_list_2ghz[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -2158,7 +2163,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy, if (!limits) return -ENOMEM; - sku_len = is_mt7921(dev) ? sizeof(sku_tlbv) : sizeof(sku_tlbv) - 92; + sku_len = is_connac2(dev) ? sizeof(sku_tlbv) : sizeof(sku_tlbv) - 92; tx_power = 2 * phy->hw->conf.power_level; if (!tx_power) tx_power = 127; @@ -2242,6 +2247,9 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy, false); if (err < 0) goto out; + + /* read a CR to avoid PSE buffer underflow */ + mt76_connac_mcu_reg_rr(dev, MT_PSE_BASE); } out: @@ -2764,12 +2772,16 @@ int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, return PTR_ERR(skb); ret = mt76_connac_mcu_sta_key_tlv(sta_key_conf, skb, key, cmd); - if (ret) + if (ret) { + dev_kfree_skb(skb); return ret; + } ret = mt76_connac_mcu_sta_wed_update(dev, skb); - if (ret) + if (ret) { + dev_kfree_skb(skb); return ret; + } return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); } @@ -3072,7 +3084,7 @@ static u32 mt76_connac2_get_data_mode(struct mt76_dev *dev, u32 info) { u32 mode = DL_MODE_NEED_RSP; - if ((!is_mt7921(dev) && !is_mt7925(dev)) || info == PATCH_SEC_NOT_SUPPORT) + if ((!is_connac2(dev) && !is_mt7925(dev)) || info == PATCH_SEC_NOT_SUPPORT) return mode; switch (FIELD_GET(PATCH_SEC_ENC_TYPE_MASK, info)) { diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 8d59cf43f0e2..ac5126ab68ff 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -628,6 +628,13 @@ struct sta_rec_tx_proc { __le32 flag; } __packed; +struct sta_rec_eml_op { + __le16 tag; + __le16 len; + u8 link_bitmap; + u8 link_ant_num[3]; +} __packed; + /* wtbl_rec */ struct wtbl_req_hdr { @@ -796,6 +803,7 @@ struct wtbl_raw { sizeof(struct sta_rec_he_6g_capa) + \ sizeof(struct sta_rec_pn_info) + \ sizeof(struct sta_rec_tx_proc) + \ + sizeof(struct sta_rec_eml_op) + \ sizeof(struct tlv) + \ MT76_CONNAC_WTBL_UPDATE_MAX_SIZE) @@ -832,6 +840,7 @@ enum { STA_REC_PN_INFO = 0x26, STA_REC_KEY_V3 = 0x27, STA_REC_HDRT = 0x28, + STA_REC_EML_OP = 0x29, STA_REC_HDR_TRANS = 0x2B, STA_REC_MAX_NUM }; @@ -1308,7 +1317,9 @@ enum { MCU_UNI_CMD_PER_STA_INFO = 0x6d, MCU_UNI_CMD_ALL_STA_INFO = 0x6e, MCU_UNI_CMD_ASSERT_DUMP = 0x6f, + MCU_UNI_CMD_EXT_EEPROM_CTRL = 0x74, MCU_UNI_CMD_RADIO_STATUS = 0x80, + MCU_UNI_CMD_MLD = 0x82, MCU_UNI_CMD_SDO = 0x88, }; @@ -1363,6 +1374,7 @@ enum { UNI_BSS_INFO_BASIC = 0, UNI_BSS_INFO_RA = 1, UNI_BSS_INFO_RLM = 2, + UNI_BSS_INFO_PROTECT_INFO = 3, UNI_BSS_INFO_BSS_COLOR = 4, UNI_BSS_INFO_HE_BASIC = 5, UNI_BSS_INFO_11V_MBSSID = 6, @@ -1383,6 +1395,7 @@ enum { UNI_BSS_INFO_MLD = 26, UNI_BSS_INFO_PM_DISABLE = 27, UNI_BSS_INFO_EHT = 30, + UNI_BSS_INFO_MLD_LINK_OP = 36, }; enum { @@ -1865,7 +1878,7 @@ mt76_connac_mcu_gen_dl_mode(struct mt76_dev *dev, u8 feature_set, bool is_wa) ret |= feature_set & FW_FEATURE_SET_ENCRYPT ? DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV : 0; - if (is_mt7921(dev) || is_mt7925(dev)) + if (is_connac2(dev) || is_mt7925(dev)) ret |= feature_set & FW_FEATURE_ENCRY_MODE ? DL_CONFIG_ENCRY_MODE_SEL : 0; ret |= FIELD_PREP(DL_MODE_KEY_IDX, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c index dd71c1c95cc9..dc7c03d23123 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c @@ -534,6 +534,7 @@ void mt76x02_reconfig_complete(struct ieee80211_hw *hw, return; clear_bit(MT76_RESTART, &dev->mphy.state); + ieee80211_wake_queues(hw); } EXPORT_SYMBOL_GPL(mt76x02_reconfig_complete); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 22443cbc74ad..250c2d2479b0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -1294,6 +1294,7 @@ int mt7915_register_device(struct mt7915_dev *dev) void mt7915_unregister_device(struct mt7915_dev *dev) { + cancel_work_sync(&dev->dump_work); mt7915_unregister_ext_phy(dev); mt7915_coredump_unregister(dev); mt7915_unregister_thermal(&dev->phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index cefe56c05731..cec2c4208255 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -232,19 +232,6 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) rcu_read_unlock(); } -void mt7915_mac_enable_rtscts(struct mt7915_dev *dev, - struct ieee80211_vif *vif, bool enable) -{ - struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - u32 addr; - - addr = mt7915_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5); - if (enable) - mt76_set(dev, addr, BIT(5)); - else - mt76_clear(dev, addr, BIT(5)); -} - static void mt7915_wed_check_ppe(struct mt7915_dev *dev, struct mt76_queue *q, struct mt7915_sta *msta, struct sk_buff *skb, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 90d5e79fbf74..e1d83052aa6d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -68,7 +68,7 @@ int mt7915_run(struct ieee80211_hw *hw) if (ret) goto out; - ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, + ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, MT7915_RTS_LEN_THRES, phy->mt76->band_idx); if (ret) goto out; @@ -633,8 +633,9 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw, if (set_sta == 1) mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_PORT_SECURE, false); - if (changed & BSS_CHANGED_ERP_CTS_PROT) - mt7915_mac_enable_rtscts(dev, vif, info->use_cts_prot); + if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) + mt7915_mcu_set_protection(phy, vif, info->ht_operation_mode, + info->use_cts_prot); if (changed & BSS_CHANGED_ERP_SLOT) { int slottime = 9; @@ -851,8 +852,10 @@ int mt7915_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, return mt7915_mcu_add_sta(dev, vif, sta, CONN_STATE_PORT_SECURE, false); case MT76_STA_EVENT_DISASSOC: + mutex_lock(&dev->mt76.mutex); for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++) mt7915_mac_twt_teardown_flow(dev, msta, i); + mutex_unlock(&dev->mt76.mutex); mt7915_mcu_add_sta(dev, vif, sta, CONN_STATE_DISCONNECT, false); msta->wcid.sta_disabled = 1; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 2d2f34aa465d..318c38149463 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1765,8 +1765,10 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, } out: ret = mt76_connac_mcu_sta_wed_update(&dev->mt76, skb); - if (ret) + if (ret) { + dev_kfree_skb(skb); return ret; + } return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(STA_REC_UPDATE), true); @@ -3954,6 +3956,68 @@ int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif, return ret; } +int mt7915_mcu_set_protection(struct mt7915_phy *phy, struct ieee80211_vif *vif, + u8 ht_mode, bool use_cts_prot) +{ + struct mt7915_dev *dev = phy->dev; + int len = sizeof(struct sta_req_hdr) + sizeof(struct bss_info_prot); + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct bss_info_prot *prot; + struct sk_buff *skb; + struct tlv *tlv; + enum { + PROT_NONMEMBER = BIT(1), + PROT_20MHZ = BIT(2), + PROT_NONHT_MIXED = BIT(3), + PROT_LEGACY_ERP = BIT(5), + PROT_NONGF_STA = BIT(7), + }; + u32 rts_threshold; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + NULL, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_PROTECT_INFO, + sizeof(*prot)); + prot = (struct bss_info_prot *)tlv; + + switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + prot->prot_mode = cpu_to_le32(PROT_NONMEMBER); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + prot->prot_mode = cpu_to_le32(PROT_20MHZ); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + prot->prot_mode = cpu_to_le32(PROT_NONHT_MIXED); + break; + } + + if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + prot->prot_mode |= cpu_to_le32(PROT_NONGF_STA); + + if (use_cts_prot) + prot->prot_mode |= cpu_to_le32(PROT_LEGACY_ERP); + + /* reuse current RTS setting */ + rts_threshold = phy->mt76->hw->wiphy->rts_threshold; + if (rts_threshold == (u32)-1) + prot->rts_len_thres = cpu_to_le32(MT7915_RTS_LEN_THRES); + else + prot->rts_len_thres = cpu_to_le32(rts_threshold); + + prot->rts_pkt_thres = 0x2; + + prot->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th); + if (!prot->he_rts_thres) + prot->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(BSS_INFO_UPDATE), true); +} + int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct cfg80211_he_bss_color *he_bss_color) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 3af11a075a2f..22f73a5ed425 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -399,6 +399,17 @@ struct bss_info_inband_discovery { __le16 prob_rsp_len; } __packed __aligned(4); +struct bss_info_prot { + __le16 tag; + __le16 len; + __le32 prot_type; + __le32 prot_mode; + __le32 rts_len_thres; + __le16 he_rts_thres; + u8 rts_pkt_thres; + u8 rsv[5]; +} __packed; + enum { BSS_INFO_BCN_CSA, BSS_INFO_BCN_BCC, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index b5c06201b707..bf1d915a3ca2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -84,6 +84,8 @@ #define MT7915_CRIT_TEMP 110 #define MT7915_MAX_TEMP 120 +#define MT7915_RTS_LEN_THRES 0x92b + struct mt7915_vif; struct mt7915_sta; struct mt7915_dfs_pulse; @@ -473,6 +475,8 @@ int mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *v u32 changed); int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int enable, u32 changed); +int mt7915_mcu_set_protection(struct mt7915_phy *phy, struct ieee80211_vif *vif, + u8 ht_mode, bool use_cts_prot); int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_he_obss_pd *he_obss_pd); int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index 29732315af1c..8e7790702191 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -302,7 +302,9 @@ int mt7921_register_device(struct mt792x_dev *dev) dev->pm.idle_timeout = MT792x_PM_TIMEOUT; dev->pm.stats.last_wake_event = jiffies; dev->pm.stats.last_doze_event = jiffies; - if (!mt76_is_usb(&dev->mt76)) { + + if (!mt76_is_usb(&dev->mt76) && + !is_mt7902(&dev->mt76)) { dev->pm.enable_user = true; dev->pm.enable = true; dev->pm.ds_enable_user = true; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 5fae9a6e273c..3d74fabe7408 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -371,12 +371,15 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev) { struct mt792x_phy *phy = &dev->phy; + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return; + timer_delete_sync(&phy->roc_timer); - cancel_work_sync(&phy->roc_work); - if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) - ieee80211_iterate_interfaces(mt76_hw(dev), - IEEE80211_IFACE_ITER_RESUME_ALL, - mt7921_roc_iter, (void *)phy); + cancel_work(&phy->roc_work); + + ieee80211_iterate_interfaces(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_roc_iter, (void *)phy); } EXPORT_SYMBOL_GPL(mt7921_roc_abort_sync); @@ -387,10 +390,11 @@ void mt7921_roc_work(struct work_struct *work) phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy, roc_work); - if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) - return; - mt792x_mutex_acquire(phy->dev); + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) { + mt792x_mutex_release(phy->dev); + return; + } ieee80211_iterate_active_interfaces(phy->mt76->hw, IEEE80211_IFACE_ITER_RESUME_ALL, mt7921_roc_iter, phy); @@ -796,7 +800,8 @@ mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif, bool is_add) } out: - mt7921_mcu_set_clc(dev, dev->mt76.alpha2, dev->country_ie_env); + if (vif->bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ) + mt7921_regd_update(dev); } int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, @@ -807,6 +812,9 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; int ret, idx; + if (sta->aid > MT7921_MAX_AID) + return -ENOENT; + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1); if (idx < 0) return -ENOSPC; @@ -850,6 +858,9 @@ int mt7921_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + if (sta->aid > MT7921_MAX_AID) + return -ENOENT; + if (ev != MT76_STA_EVENT_ASSOC) return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 833d0ab64230..8442dbd2ee23 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -1353,6 +1353,9 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, u16 len = le16_to_cpu(rule->len); u16 offset = len + sizeof(*rule); + if (buf_len < offset) + break; + pos += offset; buf_len -= offset; if (rule->alpha2[0] != alpha2[0] || diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 83fc7f49ff84..64f60c4fc60c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -7,6 +7,8 @@ #include "../mt792x.h" #include "regs.h" +#define MT7921_MAX_AID 20 + #define MT7921_TX_RING_SIZE 2048 #define MT7921_TX_MCU_RING_SIZE 256 #define MT7921_TX_FWDL_RING_SIZE 128 @@ -15,6 +17,9 @@ #define MT7921_RX_MCU_RING_SIZE 8 #define MT7921_RX_MCU_WA_RING_SIZE 512 +/* MT7902 Rx Ring0 is for both Rx Event and Tx Done Event */ +#define MT7902_RX_MCU_RING_SIZE 512 + #define MT7921_EEPROM_SIZE 3584 #define MT7921_TOKEN_SIZE 8192 @@ -117,6 +122,17 @@ enum mt7921_rxq_id { MT7921_RXQ_MCU_WM = 0, }; +/* MT7902 assigns its MCU-WM TXQ at index 15 */ +enum mt7902_txq_id { + MT7902_TXQ_MCU_WM = 15, +}; + +struct mt7921_dma_layout { + u8 mcu_wm_txq; + u16 mcu_rxdone_ring_size; + bool has_mcu_wa; +}; + enum { MT7921_CLC_POWER, MT7921_CLC_CHAN, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index ec9686183251..7a790ddf43bb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -26,6 +26,8 @@ static const struct pci_device_id mt7921_pci_device_table[] = { .driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM }, { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7920), .driver_data = (kernel_ulong_t)MT7920_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7902), + .driver_data = (kernel_ulong_t)MT7902_FIRMWARE_WM }, { }, }; @@ -167,8 +169,29 @@ static u32 mt7921_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) static int mt7921_dma_init(struct mt792x_dev *dev) { + struct mt7921_dma_layout layout = { + /* General case: MT7921 / MT7922 /MT7920 */ + .mcu_wm_txq = MT7921_TXQ_MCU_WM, + .mcu_rxdone_ring_size = MT7921_RX_MCU_RING_SIZE, + .has_mcu_wa = true, + }; + bool is_mt7902; int ret; + is_mt7902 = mt7921_l1_rr(dev, MT_HW_CHIPID) == 0x7902; + + /* + * MT7902 special case: + * - MCU-WM TXQ uses index 15 + * - RX Ring0 is larger and shared for event/TX-done + * - MT7902 does not use the MCU_WA ring + */ + if (is_mt7902) { + layout.mcu_wm_txq = MT7902_TXQ_MCU_WM; + layout.mcu_rxdone_ring_size = MT7902_RX_MCU_RING_SIZE; + layout.has_mcu_wa = false; + } + mt76_dma_attach(&dev->mt76); ret = mt792x_dma_disable(dev, true); @@ -185,7 +208,7 @@ static int mt7921_dma_init(struct mt792x_dev *dev) mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4); /* command to WM */ - ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT7921_TXQ_MCU_WM, + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, layout.mcu_wm_txq, MT7921_TX_MCU_RING_SIZE, MT_TX_RING_BASE); if (ret) return ret; @@ -199,18 +222,20 @@ static int mt7921_dma_init(struct mt792x_dev *dev) /* event from WM before firmware download */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], MT7921_RXQ_MCU_WM, - MT7921_RX_MCU_RING_SIZE, + layout.mcu_rxdone_ring_size, MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE); if (ret) return ret; - /* Change mcu queue after firmware download */ - ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], - MT7921_RXQ_MCU_WM, - MT7921_RX_MCU_WA_RING_SIZE, - MT_RX_BUF_SIZE, MT_WFDMA0(0x540)); - if (ret) - return ret; + if (layout.has_mcu_wa) { + /* Change mcu queue after firmware download */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + MT7921_RXQ_MCU_WM, + MT7921_RX_MCU_WA_RING_SIZE, + MT_RX_BUF_SIZE, MT_WFDMA0(0x540)); + if (ret) + return ret; + } /* rx data */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], @@ -276,6 +301,7 @@ static int mt7921_pci_probe(struct pci_dev *pdev, struct mt76_bus_ops *bus_ops; struct mt792x_dev *dev; struct mt76_dev *mdev; + void __iomem *regs; u16 cmd, chipid; u8 features; int ret; @@ -284,10 +310,6 @@ static int mt7921_pci_probe(struct pci_dev *pdev, if (ret) return ret; - ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); - if (ret) - return ret; - pci_read_config_word(pdev, PCI_COMMAND, &cmd); if (!(cmd & PCI_COMMAND_MEMORY)) { cmd |= PCI_COMMAND_MEMORY; @@ -321,11 +343,29 @@ static int mt7921_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mdev); + regs = pcim_iomap_region(pdev, 0, pci_name(pdev)); + if (IS_ERR(regs)) + return PTR_ERR(regs); + dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &mt7921_pcie_ops; dev->irq_map = &irq_map; - mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); + mt76_mmio_init(&dev->mt76, regs); + + if (id->device == 0x7902) { + struct mt792x_irq_map *map; + + /* MT7902 needs a mutable copy because wm2_complete_mask differs */ + map = devm_kmemdup(&pdev->dev, &irq_map, + sizeof(irq_map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + map->rx.wm2_complete_mask = 0; + dev->irq_map = map; + } + tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); dev->phy.dev = dev; @@ -579,6 +619,8 @@ MODULE_FIRMWARE(MT7921_FIRMWARE_WM); MODULE_FIRMWARE(MT7921_ROM_PATCH); MODULE_FIRMWARE(MT7922_FIRMWARE_WM); MODULE_FIRMWARE(MT7922_ROM_PATCH); +MODULE_FIRMWARE(MT7902_FIRMWARE_WM); +MODULE_FIRMWARE(MT7902_ROM_PATCH); MODULE_AUTHOR("Sean Wang "); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("MediaTek MT7921E (PCIe) wireless driver"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c index 5ec084432ae3..0db7acb3a637 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c @@ -71,9 +71,9 @@ int mt7921e_mac_reset(struct mt792x_dev *dev) mt76_txq_schedule_all(&dev->mphy); mt76_worker_disable(&dev->mt76.tx_worker); - napi_disable(&dev->mt76.napi[MT_RXQ_MAIN]); - napi_disable(&dev->mt76.napi[MT_RXQ_MCU]); - napi_disable(&dev->mt76.napi[MT_RXQ_MCU_WA]); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_disable(&dev->mt76.napi[i]); + } napi_disable(&dev->mt76.tx_napi); mt76_connac2_tx_token_put(&dev->mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c index 3421e53dc948..9150f185716c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -19,6 +19,8 @@ static const struct sdio_device_id mt7921s_table[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901), .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7902), + .driver_data = (kernel_ulong_t)MT7902_FIRMWARE_WM }, { } /* Terminating entry */ }; @@ -317,6 +319,8 @@ static int mt7921s_resume(struct device *__dev) MODULE_DEVICE_TABLE(sdio, mt7921s_table); MODULE_FIRMWARE(MT7921_FIRMWARE_WM); MODULE_FIRMWARE(MT7921_ROM_PATCH); +MODULE_FIRMWARE(MT7902_FIRMWARE_WM); +MODULE_FIRMWARE(MT7902_ROM_PATCH); static DEFINE_SIMPLE_DEV_PM_OPS(mt7921s_pm_ops, mt7921s_suspend, mt7921s_resume); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index 3ce5d6fcc69d..c0c5cb9aff75 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -91,6 +91,8 @@ int mt7925_mac_init(struct mt792x_dev *dev) mt7925_mac_init_basic_rates(dev); + memzero_explicit(&dev->mt76.alpha2, sizeof(dev->mt76.alpha2)); + return 0; } EXPORT_SYMBOL_GPL(mt7925_mac_init); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index caaf71c31480..c47bd812b66b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -804,8 +804,8 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, txwi[5] = cpu_to_le32(val); val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT, 1); - if (!ieee80211_vif_is_mld(vif) || - (q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0)) + if (vif && (!ieee80211_vif_is_mld(vif) || + (q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0))) val |= MT_TXD6_DIS_MAT; txwi[6] = cpu_to_le32(val); txwi[7] = 0; @@ -846,11 +846,14 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, bool is_8023; u16 fc, tid; + if (!sta) + return; + link_sta = rcu_dereference(sta->link[wcid->link_id]); if (!link_sta) return; - if (!sta || !(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he)) + if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he)) return; tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; @@ -882,8 +885,10 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, else mlink = &msta->deflink; - if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state)) - ieee80211_start_tx_ba_session(sta, tid, 0); + if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state)) { + if (ieee80211_start_tx_ba_session(sta, tid, 0)) + clear_bit(tid, &mlink->wcid.ampdu_state); + } } static bool @@ -1280,7 +1285,8 @@ mt7925_vif_connect_iter(void *priv, u8 *mac, if (vif->type == NL80211_IFTYPE_AP) { mt76_connac_mcu_uni_add_bss(dev->phy.mt76, vif, &mvif->sta.deflink.wcid, true, NULL); - mt7925_mcu_sta_update(dev, NULL, vif, true, + mt7925_mcu_sta_update(dev, NULL, vif, + &mvif->sta.deflink, true, MT76_STA_INFO_STATE_NONE); mt7925_mcu_uni_add_beacon_offload(dev, hw, vif, true); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 2d358a96640c..73d3722739d0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -245,6 +245,7 @@ int mt7925_init_mlo_caps(struct mt792x_phy *phy) { struct wiphy *wiphy = phy->mt76->hw->wiphy; static const u8 ext_capa_sta[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, }; @@ -438,6 +439,9 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) if (phy->chip_cap & MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN) vif->driver_flags |= IEEE80211_VIF_SUPPORTS_CQM_RSSI; + INIT_WORK(&mvif->csa_work, mt7925_csa_work); + timer_setup(&mvif->csa_timer, mt792x_csa_timer, 0); + out: mt792x_mutex_release(dev); @@ -457,12 +461,16 @@ void mt7925_roc_abort_sync(struct mt792x_dev *dev) { struct mt792x_phy *phy = &dev->phy; + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return; + timer_delete_sync(&phy->roc_timer); - cancel_work_sync(&phy->roc_work); - if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) - ieee80211_iterate_interfaces(mt76_hw(dev), - IEEE80211_IFACE_ITER_RESUME_ALL, - mt7925_roc_iter, (void *)phy); + + cancel_work(&phy->roc_work); + + ieee80211_iterate_interfaces(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_roc_iter, (void *)phy); } EXPORT_SYMBOL_GPL(mt7925_roc_abort_sync); @@ -541,7 +549,7 @@ static int mt7925_set_mlo_roc(struct mt792x_phy *phy, phy->roc_grant = false; - err = mt7925_mcu_set_mlo_roc(mconf, sel_links, 5, ++phy->roc_token_id); + err = mt7925_mcu_set_mlo_roc(phy, mconf, sel_links, 5, ++phy->roc_token_id); if (err < 0) { clear_bit(MT76_STATE_ROC, &phy->mt76->state); goto out; @@ -586,7 +594,8 @@ static int mt7925_cancel_remain_on_channel(struct ieee80211_hw *hw, static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, int link_id) + struct ieee80211_key_conf *key, int link_id, + struct mt792x_link_sta *mlink) { struct mt792x_dev *dev = mt792x_hw_dev(hw); struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; @@ -595,7 +604,6 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_bss_conf *link_conf; struct ieee80211_link_sta *link_sta; int idx = key->keyidx, err = 0; - struct mt792x_link_sta *mlink; struct mt792x_bss_conf *mconf; struct mt76_wcid *wcid; u8 *wcid_keyidx; @@ -603,7 +611,6 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, link_conf = mt792x_vif_to_bss_conf(vif, link_id); link_sta = sta ? mt792x_sta_to_link_sta(vif, sta, link_id) : NULL; mconf = mt792x_vif_to_link(mvif, link_id); - mlink = mt792x_sta_to_link(msta, link_id); wcid = &mlink->wcid; wcid_keyidx = &wcid->hw_key_idx; @@ -671,6 +678,7 @@ static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct mt792x_sta *msta = sta ? (struct mt792x_sta *)sta->drv_priv : &mvif->sta; + struct mt792x_link_sta *mlink; int err; /* The hardware does not support per-STA RX GTK, fallback @@ -692,12 +700,16 @@ static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, add = key->link_id != -1 ? BIT(key->link_id) : msta->valid_links; for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { - err = mt7925_set_link_key(hw, cmd, vif, sta, key, link_id); + mlink = mt792x_sta_to_link(msta, link_id); + err = mt7925_set_link_key(hw, cmd, vif, sta, key, link_id, + mlink); if (err < 0) break; } } else { - err = mt7925_set_link_key(hw, cmd, vif, sta, key, vif->bss_conf.link_id); + mlink = mt792x_sta_to_link(msta, vif->bss_conf.link_id); + err = mt7925_set_link_key(hw, cmd, vif, sta, key, + vif->bss_conf.link_id, mlink); } mt792x_mutex_release(dev); @@ -842,20 +854,24 @@ mt7925_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif, static int mt7925_mac_link_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_link_sta *link_sta) + struct ieee80211_link_sta *link_sta, + struct mt792x_link_sta *mlink) { struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct ieee80211_bss_conf *link_conf; struct mt792x_bss_conf *mconf; u8 link_id = link_sta->link_id; - struct mt792x_link_sta *mlink; + bool wcid_published = false; struct mt792x_sta *msta; struct mt76_wcid *wcid; + bool pm_woken = false; int ret, idx; msta = (struct mt792x_sta *)link_sta->sta->drv_priv; - mlink = mt792x_sta_to_link(msta, link_id); + + if (WARN_ON_ONCE(!mlink)) + return -EINVAL; idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1); if (idx < 0) @@ -874,14 +890,15 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev, wcid = &mlink->wcid; ewma_signal_init(&wcid->rssi); rcu_assign_pointer(dev->mt76.wcid[wcid->idx], wcid); - mt76_wcid_init(wcid, 0); + wcid_published = true; ewma_avg_signal_init(&mlink->avg_ack_signal); memset(mlink->airtime_ac, 0, sizeof(msta->deflink.airtime_ac)); ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm); if (ret) - return ret; + goto out_wcid; + pm_woken = true; mt7925_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -890,41 +907,125 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev, /* should update bss info before STA add */ if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) { - if (ieee80211_vif_is_mld(vif)) - mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, - link_conf, link_sta, link_sta != mlink->pri_link); - else - mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, - link_conf, link_sta, false); + struct mt792x_link_sta *mlink_bc; + + mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id); + + if (ieee80211_vif_is_mld(vif)) { + ret = mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx, + link_conf, link_sta, + mlink_bc->wcid.idx, mlink->wcid.idx, + link_sta != mlink->pri_link); + if (ret) + goto out_pm; + } else { + ret = mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx, + link_conf, link_sta, + mlink_bc->wcid.idx, mlink->wcid.idx, + false); + if (ret) + goto out_pm; + } } if (ieee80211_vif_is_mld(vif) && link_sta == mlink->pri_link) { - ret = mt7925_mcu_sta_update(dev, link_sta, vif, true, + ret = mt7925_mcu_sta_update(dev, link_sta, vif, + mlink, true, MT76_STA_INFO_STATE_NONE); if (ret) - return ret; + goto out_pm; } else if (ieee80211_vif_is_mld(vif) && link_sta != mlink->pri_link) { - ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif, - true, MT76_STA_INFO_STATE_ASSOC); - if (ret) - return ret; + struct mt792x_link_sta *pri_mlink; + struct mt76_wcid *pri_wcid; - ret = mt7925_mcu_sta_update(dev, link_sta, vif, true, + /* alternative lookup via def_wcid */ + pri_wcid = mlink->wcid.def_wcid; + + pri_mlink = pri_wcid ? + container_of(pri_wcid, struct mt792x_link_sta, wcid) : + NULL; + + if (WARN_ON_ONCE(!pri_mlink)) { + ret = -EINVAL; + goto out_pm; + } + + ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif, + pri_mlink, true, MT76_STA_INFO_STATE_ASSOC); if (ret) - return ret; + goto out_pm; + + ret = mt7925_mcu_sta_update(dev, link_sta, vif, + mlink, true, + MT76_STA_INFO_STATE_ASSOC); + if (ret) + goto out_pm; } else { - ret = mt7925_mcu_sta_update(dev, link_sta, vif, true, + ret = mt7925_mcu_sta_update(dev, link_sta, vif, + mlink, true, MT76_STA_INFO_STATE_NONE); if (ret) - return ret; + goto out_pm; } mt76_connac_power_save_sched(&dev->mphy, &dev->pm); return 0; + +out_pm: + if (pm_woken) + mt76_connac_power_save_sched(&dev->mphy, &dev->pm); +out_wcid: + if (wcid_published) { + u16 idx = wcid->idx; + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + mt76_wcid_cleanup(mdev, wcid); + mt76_wcid_mask_clear(mdev->wcid_mask, wcid->idx); + } + return ret; +} + +/* + * Host-only unwind for sta_add_links() failures. + * + * If add_links fail due to MCU/firmware timeouts; calling the full remove + * path would send more firmware commands and may hang again. So only rollback + * host-published state here (msta->link/valid_links, dev->mt76.wcid[idx]) and + * free mlink objects (RCU-safe). Firmware state is left for reset/recovery. + */ +static void +mt7925_mac_sta_unwind_links_host(struct mt792x_dev *dev, + struct ieee80211_sta *sta, + unsigned long links) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + unsigned int link_id; + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt792x_link_sta *mlink; + u16 idx; + + mlink = rcu_replace_pointer(msta->link[link_id], NULL, + lockdep_is_held(&dev->mt76.mutex)); + if (!mlink) + continue; + + msta->valid_links &= ~BIT(link_id); + if (msta->deflink_id == link_id) + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; + + idx = mlink->wcid.idx; + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + mt76_wcid_cleanup(&dev->mt76, &mlink->wcid); + mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx); + + if (mlink != &msta->deflink) + kfree_rcu(mlink, rcu_head); + } } static int @@ -932,34 +1033,50 @@ mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, unsigned long new_links) { struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + unsigned long added_links = 0; unsigned int link_id; int err = 0; for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_link_sta *link_sta; struct mt792x_link_sta *mlink; + bool is_deflink = false; if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) { mlink = &msta->deflink; - msta->deflink_id = link_id; + is_deflink = true; } else { - mlink = devm_kzalloc(dev->mt76.dev, sizeof(*mlink), GFP_KERNEL); + mlink = kzalloc(sizeof(*mlink), GFP_KERNEL); if (!mlink) { err = -ENOMEM; break; } } - msta->valid_links |= BIT(link_id); - rcu_assign_pointer(msta->link[link_id], mlink); mlink->sta = msta; mlink->pri_link = &sta->deflink; mlink->wcid.def_wcid = &msta->deflink.wcid; link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); - mt7925_mac_link_sta_add(&dev->mt76, vif, link_sta); + err = mt7925_mac_link_sta_add(&dev->mt76, vif, link_sta, mlink); + if (err) { + if (!is_deflink) + kfree_rcu(mlink, rcu_head); + break; + } + + if (is_deflink) + msta->deflink_id = link_id; + + rcu_assign_pointer(msta->link[link_id], mlink); + msta->valid_links |= BIT(link_id); + + added_links |= BIT(link_id); } + if (err && added_links) + mt7925_mac_sta_unwind_links_host(dev, sta, added_links); + return err; } @@ -981,7 +1098,8 @@ int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, err = mt7925_mac_sta_add_links(dev, vif, sta, sta->valid_links); } else { - err = mt7925_mac_link_sta_add(mdev, vif, &sta->deflink); + err = mt7925_mac_link_sta_add(mdev, vif, &sta->deflink, + &msta->deflink); } return err; @@ -1030,11 +1148,11 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev, struct mt792x_link_sta *mlink; struct mt792x_sta *msta; + mt792x_mutex_acquire(dev); + msta = (struct mt792x_sta *)link_sta->sta->drv_priv; mlink = mt792x_sta_to_link(msta, link_sta->link_id); - mt792x_mutex_acquire(dev); - if (ieee80211_vif_is_mld(vif)) { link_conf = mt792x_vif_to_bss_conf(vif, msta->deflink_id); } else { @@ -1055,7 +1173,8 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); memset(mlink->airtime_ac, 0, sizeof(mlink->airtime_ac)); - mt7925_mcu_sta_update(dev, link_sta, vif, true, MT76_STA_INFO_STATE_ASSOC); + mt7925_mcu_sta_update(dev, link_sta, vif, mlink, true, + MT76_STA_INFO_STATE_ASSOC); mt792x_mutex_release(dev); } @@ -1083,23 +1202,21 @@ EXPORT_SYMBOL_GPL(mt7925_mac_sta_event); static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_link_sta *link_sta) + struct ieee80211_link_sta *link_sta, + struct mt792x_link_sta *mlink) { struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt76_wcid *wcid = &mlink->wcid; struct ieee80211_bss_conf *link_conf; u8 link_id = link_sta->link_id; - struct mt792x_link_sta *mlink; - struct mt792x_sta *msta; - - msta = (struct mt792x_sta *)link_sta->sta->drv_priv; - mlink = mt792x_sta_to_link(msta, link_id); + u16 idx = wcid->idx; mt7925_roc_abort_sync(dev); - mt76_connac_free_pending_tx_skbs(&dev->pm, &mlink->wcid); + mt76_connac_free_pending_tx_skbs(&dev->pm, wcid); mt76_connac_pm_wake(&dev->mphy, &dev->pm); - mt7925_mcu_sta_update(dev, link_sta, vif, false, + mt7925_mcu_sta_update(dev, link_sta, vif, mlink, false, MT76_STA_INFO_STATE_NONE); mt7925_mac_wtbl_update(dev, mlink->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -1123,6 +1240,10 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev, list_del_init(&mlink->wcid.poll_list); spin_unlock_bh(&mdev->sta_poll_lock); + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + mt76_wcid_cleanup(mdev, wcid); + mt76_wcid_mask_clear(mdev->wcid_mask, idx); + mt76_connac_power_save_sched(&dev->mphy, &dev->pm); } @@ -1132,7 +1253,6 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, { struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; struct mt76_dev *mdev = &dev->mt76; - struct mt76_wcid *wcid; unsigned int link_id; /* clean up bss before starec */ @@ -1171,22 +1291,19 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, if (!link_sta) continue; - mlink = mt792x_sta_to_link(msta, link_id); + mlink = rcu_replace_pointer(msta->link[link_id], NULL, + lockdep_is_held(&mdev->mutex)); if (!mlink) continue; - mt7925_mac_link_sta_remove(&dev->mt76, vif, link_sta); - - wcid = &mlink->wcid; - rcu_assign_pointer(msta->link[link_id], NULL); msta->valid_links &= ~BIT(link_id); mlink->sta = NULL; mlink->pri_link = NULL; - if (link_sta != mlink->pri_link) { - mt76_wcid_cleanup(mdev, wcid); - mt76_wcid_mask_clear(mdev->wcid_mask, wcid->idx); - } + mt7925_mac_link_sta_remove(&dev->mt76, vif, link_sta, mlink); + + if (mlink != &msta->deflink) + kfree_rcu(mlink, rcu_head); if (msta->deflink_id == link_id) msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; @@ -1323,10 +1440,18 @@ void mt7925_mlo_pm_work(struct work_struct *work) void mt7925_scan_work(struct work_struct *work) { struct mt792x_phy *phy; + struct mt792x_dev *dev; + struct mt76_connac_pm *pm; phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy, scan_work.work); + dev = phy->dev; + pm = &dev->pm; + + if (pm->suspended) + return; + while (true) { struct sk_buff *skb; struct tlv *tlv; @@ -1544,8 +1669,10 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw, valid = ieee80211_vif_is_mld(vif) ? mvif->valid_links : BIT(0); for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt792x_bss_conf *mconf; struct mt792x_link_sta *mlink; + mconf = mt792x_vif_to_link(mvif, i); mlink = mt792x_sta_to_link(msta, i); if (enabled) @@ -1556,7 +1683,7 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw, if (!mlink->wcid.sta) continue; - mt7925_mcu_wtbl_update_hdr_trans(dev, vif, sta, i); + mt7925_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink); } mt792x_mutex_release(dev); @@ -1716,7 +1843,8 @@ mt7925_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (err) goto out; - err = mt7925_mcu_sta_update(dev, NULL, vif, true, + err = mt7925_mcu_sta_update(dev, NULL, vif, + &mvif->sta.deflink, true, MT76_STA_INFO_STATE_NONE); out: mt792x_mutex_release(dev); @@ -1749,6 +1877,10 @@ static int mt7925_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + dev->new_ctx = ctx; + return 0; } @@ -1756,6 +1888,11 @@ static void mt7925_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + if (dev->new_ctx == ctx) + dev->new_ctx = NULL; + } static void @@ -1850,7 +1987,8 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw, mt792x_mutex_acquire(dev); if (changed & BSS_CHANGED_ASSOC) { - mt7925_mcu_sta_update(dev, NULL, vif, true, + mt7925_mcu_sta_update(dev, NULL, vif, + &mvif->sta.deflink, true, MT76_STA_INFO_STATE_ASSOC); mt7925_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc); @@ -1894,10 +2032,8 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw, struct mt792x_phy *phy = mt792x_hw_phy(hw); struct mt792x_dev *dev = mt792x_hw_dev(hw); struct mt792x_bss_conf *mconf; - struct ieee80211_bss_conf *link_conf; mconf = mt792x_vif_to_link(mvif, info->link_id); - link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id); mt792x_mutex_acquire(dev); @@ -1939,10 +2075,6 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw, mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS; } - if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING) - mt7925_mcu_set_eht_pp(mvif->phy->mt76, &mconf->mt76, - link_conf, NULL); - if (changed & BSS_CHANGED_CQM) mt7925_mcu_set_rssimonitor(dev, vif); @@ -2144,6 +2276,11 @@ static void mt7925_unassign_vif_chanctx(struct ieee80211_hw *hw, mctx->bss_conf = NULL; mconf->mt76.ctx = NULL; mutex_unlock(&dev->mt76.mutex); + + if (link_conf->csa_active) { + timer_delete_sync(&mvif->csa_timer); + cancel_work_sync(&mvif->csa_work); + } } static void mt7925_rfkill_poll(struct ieee80211_hw *hw) @@ -2158,6 +2295,121 @@ static void mt7925_rfkill_poll(struct ieee80211_hw *hw) wiphy_rfkill_set_hw_state(hw->wiphy, ret == 0); } +static int mt7925_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + return mt7925_assign_vif_chanctx(hw, vifs->vif, vifs->link_conf, + vifs->new_ctx); +} + +void mt7925_csa_work(struct work_struct *work) +{ + struct mt792x_vif *mvif; + struct mt792x_dev *dev; + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link_conf; + struct mt792x_bss_conf *mconf; + u8 link_id, roc_rtype; + int ret = 0; + + mvif = (struct mt792x_vif *)container_of(work, struct mt792x_vif, + csa_work); + dev = mvif->phy->dev; + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + + if (ieee80211_vif_is_mld(vif)) + return; + + if (!dev->new_ctx) + return; + + link_id = 0; + mconf = &mvif->bss_conf; + link_conf = &vif->bss_conf; + roc_rtype = MT7925_ROC_REQ_JOIN; + + mt792x_mutex_acquire(dev); + ret = mt7925_set_roc(mvif->phy, mconf, dev->new_ctx->def.chan, + 4000, roc_rtype); + mt792x_mutex_release(dev); + if (!ret) { + mt792x_mutex_acquire(dev); + ret = mt7925_mcu_set_chctx(mvif->phy->mt76, &mconf->mt76, link_conf, + dev->new_ctx); + mt792x_mutex_release(dev); + + mt7925_abort_roc(mvif->phy, mconf); + } + + ieee80211_chswitch_done(vif, !ret, link_id); +} + +static int mt7925_pre_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + if (ieee80211_vif_is_mld(vif)) + return -EOPNOTSUPP; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return -EOPNOTSUPP; + + if (!cfg80211_chandef_usable(hw->wiphy, &chsw->chandef, + IEEE80211_CHAN_DISABLED)) + return -EOPNOTSUPP; + + return 0; +} + +static void mt7925_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + u16 beacon_interval; + + if (ieee80211_vif_is_mld(vif)) + return; + + beacon_interval = vif->bss_conf.beacon_int; + + mvif->csa_timer.expires = TU_TO_EXP_TIME(beacon_interval * chsw->count); + add_timer(&mvif->csa_timer); +} + +static void mt7925_abort_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + timer_delete_sync(&mvif->csa_timer); + cancel_work_sync(&mvif->csa_work); +} + +static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + u16 beacon_interval; + + if (ieee80211_vif_is_mld(vif)) + return; + + beacon_interval = vif->bss_conf.beacon_int; + + if (cfg80211_chandef_identical(&chsw->chandef, + &dev->new_ctx->def) && + chsw->count) { + mod_timer(&mvif->csa_timer, + TU_TO_EXP_TIME(beacon_interval * chsw->count)); + } +} + const struct ieee80211_ops mt7925_ops = { .tx = mt792x_tx, .start = mt7925_start, @@ -2221,6 +2473,12 @@ const struct ieee80211_ops mt7925_ops = { .change_vif_links = mt7925_change_vif_links, .change_sta_links = mt7925_change_sta_links, .rfkill_poll = mt7925_rfkill_poll, + + .switch_vif_chanctx = mt7925_switch_vif_chanctx, + .pre_channel_switch = mt7925_pre_channel_switch, + .channel_switch = mt7925_channel_switch, + .abort_channel_switch = mt7925_abort_channel_switch, + .channel_switch_rx_beacon = mt7925_channel_switch_rx_beacon, }; EXPORT_SYMBOL_GPL(mt7925_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index cf0fdea45cf7..37cdf3e8a067 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -1066,9 +1066,8 @@ EXPORT_SYMBOL_GPL(mt7925_run_firmware); static void mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, - struct ieee80211_link_sta *link_sta) + struct mt792x_link_sta *mlink) { - struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct sta_rec_hdr_trans *hdr_trans; struct mt76_wcid *wcid; struct tlv *tlv; @@ -1082,19 +1081,11 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb, else hdr_trans->from_ds = true; - if (link_sta) { - struct mt792x_sta *msta = (struct mt792x_sta *)link_sta->sta->drv_priv; - struct mt792x_link_sta *mlink; - - mlink = mt792x_sta_to_link(msta, link_sta->link_id); - wcid = &mlink->wcid; - } else { - wcid = &mvif->sta.deflink.wcid; - } - - if (!wcid) + if (WARN_ON_ONCE(!mlink)) return; + wcid = &mlink->wcid; + hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) { hdr_trans->to_ds = true; @@ -1104,30 +1095,18 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb, int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - int link_id) + struct mt792x_bss_conf *mconf, + struct mt792x_link_sta *mlink) { - struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - struct ieee80211_link_sta *link_sta = sta ? &sta->deflink : NULL; - struct mt792x_link_sta *mlink; - struct mt792x_bss_conf *mconf; - struct mt792x_sta *msta; struct sk_buff *skb; - msta = sta ? (struct mt792x_sta *)sta->drv_priv : &mvif->sta; - - mlink = mt792x_sta_to_link(msta, link_id); - link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); - mconf = mt792x_vif_to_link(mvif, link_id); - skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mlink->wcid, MT7925_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); - /* starec hdr trans */ - mt7925_mcu_sta_hdr_trans_tlv(skb, vif, link_sta); + mt7925_mcu_sta_hdr_trans_tlv(skb, vif, mlink); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } @@ -1288,14 +1267,16 @@ int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, return PTR_ERR(skb); ret = mt7925_mcu_sta_key_tlv(wcid, sta_key_conf, skb, key, cmd, msta); - if (ret) + if (ret) { + dev_kfree_skb(skb); return ret; + } return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); } -int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links, - int duration, u8 token_id) +int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, + u16 sel_links, int duration, u8 token_id) { struct mt792x_vif *mvif = mconf->vif; struct ieee80211_vif *vif = container_of((void *)mvif, @@ -1330,6 +1311,8 @@ int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links, .roc[1].len = cpu_to_le16(sizeof(struct roc_acquire_tlv)) }; + struct wiphy *wiphy = phy->mt76->hw->wiphy; + if (!mconf || hweight16(vif->valid_links) < 2 || hweight16(sel_links) != 2) return -EPERM; @@ -1352,7 +1335,8 @@ int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links, is_AG_band |= links[i].chan->band == NL80211_BAND_2GHZ; } - if (vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP) + if (!(wiphy->iftype_ext_capab[0].mld_capa_and_ops & + IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS)) type = is_AG_band ? MT7925_ROC_REQ_MLSR_AG : MT7925_ROC_REQ_MLSR_AA; else @@ -1721,10 +1705,9 @@ mt7925_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) static void mt7925_mcu_sta_amsdu_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, - struct ieee80211_link_sta *link_sta) + struct ieee80211_link_sta *link_sta, + struct mt792x_link_sta *mlink) { - struct mt792x_sta *msta = (struct mt792x_sta *)link_sta->sta->drv_priv; - struct mt792x_link_sta *mlink; struct sta_rec_amsdu *amsdu; struct tlv *tlv; @@ -1740,7 +1723,6 @@ mt7925_mcu_sta_amsdu_tlv(struct sk_buff *skb, amsdu->max_amsdu_num = 8; amsdu->amsdu_en = true; - mlink = mt792x_sta_to_link(msta, link_sta->link_id); mlink->wcid.amsdu = true; switch (link_sta->agg.max_amsdu_len) { @@ -1911,36 +1893,53 @@ mt7925_mcu_sta_eht_mld_tlv(struct sk_buff *skb, static void mt7925_mcu_sta_mld_tlv(struct sk_buff *skb, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct mt792x_bss_conf *mconf, + struct mt792x_link_sta *mlink) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; - unsigned long valid = mvif->valid_links; - struct mt792x_bss_conf *mconf; - struct mt792x_link_sta *mlink; + struct mt792x_dev *dev = mvif->phy->dev; + struct mt792x_bss_conf *mconf_pri; struct sta_rec_mld *mld; struct tlv *tlv; - int i, cnt = 0; + u8 cnt = 0; + + /* Primary link always uses driver's deflink WCID. */ + mconf_pri = (msta->deflink_id != IEEE80211_LINK_UNSPECIFIED) ? + mt792x_vif_to_link(mvif, msta->deflink_id) : NULL; + + /* If caller is operating on deflink, reuse its mconf as primary. */ + if (!mconf_pri && mlink == &msta->deflink) + mconf_pri = mconf; + + if (!mconf_pri) { + dev_warn_ratelimited(dev->mt76.dev, + "mt7925: MLD_TLV_LINK skip (no primary mconf) sta=%pM\n", + sta->addr); + return; + } tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD, sizeof(*mld)); mld = (struct sta_rec_mld *)tlv; memcpy(mld->mac_addr, sta->addr, ETH_ALEN); + mld->primary_id = cpu_to_le16(msta->deflink.wcid.idx); mld->wlan_id = cpu_to_le16(msta->deflink.wcid.idx); - mld->link_num = min_t(u8, hweight16(mvif->valid_links), 2); - for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { - if (cnt == mld->link_num) - break; + /* Always encode primary link first. */ + mld->link[cnt].wlan_id = cpu_to_le16(msta->deflink.wcid.idx); + mld->link[cnt++].bss_idx = mconf_pri->mt76.idx; - mconf = mt792x_vif_to_link(mvif, i); - mlink = mt792x_sta_to_link(msta, i); + /* Optionally encode the currently-updated secondary link. */ + if (mlink && mlink != &msta->deflink && mconf) { + mld->secondary_id = cpu_to_le16(mlink->wcid.idx); mld->link[cnt].wlan_id = cpu_to_le16(mlink->wcid.idx); mld->link[cnt++].bss_idx = mconf->mt76.idx; - - if (mlink != &msta->deflink) - mld->secondary_id = cpu_to_le16(mlink->wcid.idx); } + + mld->link_num = cnt; } static void @@ -1961,10 +1960,12 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy, struct mt792x_vif *mvif = (struct mt792x_vif *)info->vif->drv_priv; struct mt76_dev *dev = phy->dev; struct mt792x_bss_conf *mconf; + struct mt792x_link_sta *mlink; struct sk_buff *skb; int conn_state; mconf = mt792x_vif_to_link(mvif, info->wcid->link_id); + mlink = container_of(info->wcid, struct mt792x_link_sta, wcid); skb = __mt76_connac_mcu_alloc_sta_req(dev, &mconf->mt76, info->wcid, MT7925_STA_UPDATE_MAX_SIZE); @@ -1982,7 +1983,7 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy, mt7925_mcu_sta_ht_tlv(skb, info->link_sta); mt7925_mcu_sta_vht_tlv(skb, info->link_sta); mt76_connac_mcu_sta_uapsd(skb, info->vif, info->link_sta->sta); - mt7925_mcu_sta_amsdu_tlv(skb, info->vif, info->link_sta); + mt7925_mcu_sta_amsdu_tlv(skb, info->vif, info->link_sta, mlink); mt7925_mcu_sta_he_tlv(skb, info->link_sta); mt7925_mcu_sta_he_6g_tlv(skb, info->link_sta); mt7925_mcu_sta_eht_tlv(skb, info->link_sta); @@ -1993,7 +1994,10 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy, info->state); if (info->state != MT76_STA_INFO_STATE_NONE) { - mt7925_mcu_sta_mld_tlv(skb, info->vif, info->link_sta->sta); + mt7925_mcu_sta_mld_tlv(skb, info->vif, + info->link_sta->sta, + mconf, mlink); + mt7925_mcu_sta_eht_mld_tlv(skb, info->vif, info->link_sta->sta); } } @@ -2003,7 +2007,10 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy, mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, sizeof(struct tlv)); } else { - mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta); + if (!info->link_sta) + mlink = &mvif->sta.deflink; + + mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, mlink); } return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true); @@ -2011,7 +2018,9 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy, int mt7925_mcu_sta_update(struct mt792x_dev *dev, struct ieee80211_link_sta *link_sta, - struct ieee80211_vif *vif, bool enable, + struct ieee80211_vif *vif, + struct mt792x_link_sta *mlink, + bool enable, enum mt76_sta_info_state state) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; @@ -2026,14 +2035,8 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev, .offload_fw = true, .rcpi = to_rcpi(rssi), }; - struct mt792x_sta *msta; - struct mt792x_link_sta *mlink; - if (link_sta) { - msta = (struct mt792x_sta *)link_sta->sta->drv_priv; - mlink = mt792x_sta_to_link(msta, link_sta->link_id); - } - info.wcid = link_sta ? &mlink->wcid : &mvif->sta.deflink.wcid; + info.wcid = &mlink->wcid; info.newly = state != MT76_STA_INFO_STATE_ASSOC; return mt7925_mcu_sta_cmd(&dev->mphy, &info); @@ -2470,7 +2473,9 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, struct ieee80211_chanctx_conf *ctx, - struct mt76_phy *phy, u16 wlan_idx, + struct mt76_phy *phy, + u16 bmc_tx_wlan_idx, + u16 sta_wlan_idx, bool enable) { struct ieee80211_vif *vif = link_conf->vif; @@ -2479,7 +2484,6 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb, &link_conf->chanreq.oper; enum nl80211_band band = chandef->chan->band; struct mt76_connac_bss_basic_tlv *basic_req; - struct mt792x_link_sta *mlink; struct tlv *tlv; int conn_type; u8 idx; @@ -2503,20 +2507,9 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb, basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, link_sta); basic_req->bcn_interval = cpu_to_le16(link_conf->beacon_int); basic_req->dtim_period = link_conf->dtim_period; - basic_req->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx); + basic_req->bmc_tx_wlan_idx = cpu_to_le16(bmc_tx_wlan_idx); basic_req->link_idx = mconf->mt76.idx; - - if (link_sta) { - struct mt792x_sta *msta; - - msta = (struct mt792x_sta *)link_sta->sta->drv_priv; - mlink = mt792x_sta_to_link(msta, link_sta->link_id); - - } else { - mlink = &mconf->vif->sta.deflink; - } - - basic_req->sta_idx = cpu_to_le16(mlink->wcid.idx); + basic_req->sta_idx = cpu_to_le16(sta_wlan_idx); basic_req->omac_idx = mconf->mt76.omac_idx; basic_req->band_idx = mconf->mt76.band_idx; basic_req->wmm_idx = mconf->mt76.wmm_idx; @@ -2823,16 +2816,16 @@ void mt7925_mcu_del_dev(struct mt76_dev *mdev, &dev_req, sizeof(dev_req), true); } -int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, - struct ieee80211_chanctx_conf *ctx, - struct ieee80211_bss_conf *link_conf, - struct ieee80211_link_sta *link_sta, - int enable) +int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy, + struct ieee80211_chanctx_conf *ctx, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + u16 bmc_tx_wlan_idx, + u16 sta_wlan_idx, + int enable) { - struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct mt792x_dev *dev = phy->dev; - struct mt792x_link_sta *mlink_bc; struct sk_buff *skb; skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, @@ -2840,11 +2833,9 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, if (IS_ERR(skb)) return PTR_ERR(skb); - mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id); - /* bss_basic must be first */ mt7925_mcu_bss_basic_tlv(skb, link_conf, link_sta, ctx, phy->mt76, - mlink_bc->wcid.idx, enable); + bmc_tx_wlan_idx, sta_wlan_idx, enable); mt7925_mcu_bss_sec_tlv(skb, link_conf); mt7925_mcu_bss_bmc_tlv(skb, phy, ctx, link_conf); mt7925_mcu_bss_qos_tlv(skb, link_conf); @@ -2865,6 +2856,33 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, MCU_UNI_CMD(BSS_INFO_UPDATE), true); } +int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, + struct ieee80211_chanctx_conf *ctx, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + int enable) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct mt792x_link_sta *mlink_bc; + struct mt792x_link_sta *mlink; + + mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id); + + if (link_sta) { + struct mt792x_sta *msta = (void *)link_sta->sta->drv_priv; + + mlink = mt792x_sta_to_link(msta, link_sta->link_id); + if (WARN_ON(!mlink)) + return -EINVAL; + } else { + mlink = &mconf->vif->sta.deflink; + } + + return mt7925_mcu_add_bss_info_sta(phy, ctx, link_conf, link_sta, + mlink_bc->wcid.idx, mlink->wcid.idx, enable); +} + int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable) { struct mt76_dev *mdev = phy->dev; @@ -3375,7 +3393,6 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, u8 rsvd[64]; } __packed req = { .tag = cpu_to_le16(0x3), - .len = cpu_to_le16(sizeof(req) - 4), .idx = idx, .env = env_cap, @@ -3404,6 +3421,7 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, memcpy(req.type, rule->type, 2); req.size = cpu_to_le16(seg->len); + req.len = cpu_to_le16(sizeof(req) + seg->len - 4); dev->phy.clc_chan_conf = clc->ver == 1 ? 0xff : rule->flag; skb = __mt76_mcu_msg_alloc(&dev->mt76, &req, le16_to_cpu(req.size) + sizeof(req), @@ -3727,7 +3745,7 @@ mt7925_mcu_rate_txpower_band(struct mt76_phy *phy, memcpy(tx_power_tlv->alpha2, dev->alpha2, sizeof(dev->alpha2)); tx_power_tlv->n_chan = num_ch; tx_power_tlv->tag = cpu_to_le16(0x1); - tx_power_tlv->len = cpu_to_le16(sizeof(*tx_power_tlv)); + tx_power_tlv->len = cpu_to_le16(msg_len); switch (band) { case NL80211_BAND_2GHZ: diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h index e09e0600534a..56e2772f3ffe 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -693,6 +693,13 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, int enable); +int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy, + struct ieee80211_chanctx_conf *ctx, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + u16 bmc_tx_wlan_idx, + u16 sta_wlan_idx, + int enable); int mt7925_mcu_set_timing(struct mt792x_phy *phy, struct ieee80211_bss_conf *link_conf); int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 6b9bf1b89032..46b480f7d813 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -250,7 +250,9 @@ int mt7925_mcu_set_bss_pm(struct mt792x_dev *dev, bool enable); int mt7925_mcu_sta_update(struct mt792x_dev *dev, struct ieee80211_link_sta *link_sta, - struct ieee80211_vif *vif, bool enable, + struct ieee80211_vif *vif, + struct mt792x_link_sta *mlink, + bool enable, enum mt76_sta_info_state state); int mt7925_mcu_set_chan_info(struct mt792x_phy *phy, u16 tag); int mt7925_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_bss_conf *bss_conf); @@ -298,6 +300,7 @@ int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, void mt7925_mlo_pm_work(struct work_struct *work); void mt7925_scan_work(struct work_struct *work); void mt7925_roc_work(struct work_struct *work); +void mt7925_csa_work(struct work_struct *work); int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_bss_conf *link_conf); void mt7925_coredump_work(struct work_struct *work); @@ -349,8 +352,8 @@ int mt7925_set_tx_sar_pwr(struct ieee80211_hw *hw, int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set); int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap env_cap); -int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links, - int duration, u8 token_id); +int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, + u16 sel_links, int duration, u8 token_id); int mt7925_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, struct ieee80211_channel *chan, int duration, enum mt7925_roc_req type, u8 token_id); @@ -367,8 +370,8 @@ int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val); int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - int link_id); + struct mt792x_bss_conf *mconf, + struct mt792x_link_sta *mlink); int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy); int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c index 292087e882d1..16f56ee879d4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c @@ -232,7 +232,8 @@ int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2) dev->regd_user) return -EINVAL; - if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0') + if ((mdev->alpha2[0] && mdev->alpha2[0] != '0') && + (mdev->alpha2[1] && mdev->alpha2[1] != '0')) return 0; /* do not need to update the same country twice */ diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 8388638ed550..4ff93f2cd624 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -41,11 +41,13 @@ #define MT792x_MCU_INIT_RETRY_COUNT 10 #define MT792x_WFSYS_INIT_RETRY_COUNT 2 +#define MT7902_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7902_1.bin" #define MT7920_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1a.bin" #define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin" #define MT7922_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7922_1.bin" #define MT7925_FIRMWARE_WM "mediatek/mt7925/WIFI_RAM_CODE_MT7925_1_1.bin" +#define MT7902_ROM_PATCH "mediatek/WIFI_MT7902_patch_mcu_1_1_hdr.bin" #define MT7920_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1a_2_hdr.bin" #define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin" #define MT7922_ROM_PATCH "mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin" @@ -95,6 +97,7 @@ DECLARE_EWMA(avg_signal, 10, 8) struct mt792x_link_sta { struct mt76_wcid wcid; /* must be first */ + struct rcu_head rcu_head; u32 airtime_ac[8]; @@ -448,6 +451,8 @@ void mt792x_config_mac_addr_list(struct mt792x_dev *dev); static inline char *mt792x_ram_name(struct mt792x_dev *dev) { switch (mt76_chip(&dev->mt76)) { + case 0x7902: + return MT7902_FIRMWARE_WM; case 0x7920: return MT7920_FIRMWARE_WM; case 0x7922: @@ -462,6 +467,8 @@ static inline char *mt792x_ram_name(struct mt792x_dev *dev) static inline char *mt792x_patch_name(struct mt792x_dev *dev) { switch (mt76_chip(&dev->mt76)) { + case 0x7902: + return MT7902_ROM_PATCH; case 0x7920: return MT7920_ROM_PATCH; case 0x7922: diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index f2ed16feb6c1..152cfcca2f90 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -151,7 +151,7 @@ void mt792x_stop(struct ieee80211_hw *hw, bool suspend) cancel_work_sync(&dev->reset_work); mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); - if (is_mt7921(&dev->mt76)) { + if (is_connac2(&dev->mt76)) { mt792x_mutex_acquire(dev); mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, false, false); mt792x_mutex_release(dev); @@ -691,9 +691,8 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID); - if (is_mt7921(&dev->mt76)) { - ieee80211_hw_set(hw, CHANCTX_STA_CSA); - } + ieee80211_hw_set(hw, CHANCTX_STA_CSA); + if (dev->pm.enable) ieee80211_hw_set(hw, CONNECTION_MONITOR); @@ -927,6 +926,13 @@ int mt792x_load_firmware(struct mt792x_dev *dev) { int ret; + mt76_connac_mcu_restart(&dev->mt76); + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC_FW_STATE, + MT_TOP_MISC2_FW_PWR_ON, 1000)) + dev_warn(dev->mt76.dev, + "MCU is not ready for firmware download\n"); + ret = mt76_connac2_load_patch(&dev->mt76, mt792x_patch_name(dev)); if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index 1ddec7788b66..002aece857b2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -103,6 +103,22 @@ static void mt792x_dma_prefetch(struct mt792x_dev *dev) mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0400, 0x10)); mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0500, 0x4)); mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0540, 0x4)); + } else if (is_mt7902(&dev->mt76)) { + /* rx ring */ + mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4)); + /* tx ring */ + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0140, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0180, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x01c0, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x0200, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x0240, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING6_EXT_CTRL, PREFETCH(0x0280, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x02c0, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0300, 0x4)); } else { /* rx ring */ mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0, 0x4)); @@ -356,7 +372,7 @@ EXPORT_SYMBOL_GPL(mt792x_poll_rx); int mt792x_wfsys_reset(struct mt792x_dev *dev) { - u32 addr = is_mt7921(&dev->mt76) ? 0x18000140 : 0x7c000140; + u32 addr = is_connac2(&dev->mt76) ? 0x18000140 : 0x7c000140; mt76_clear(dev, addr, WFSYS_SW_RST_B); msleep(50); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c index 71dec93094eb..888e5a505673 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c @@ -375,7 +375,7 @@ void mt792x_pm_power_save_work(struct work_struct *work) } if (!mt792x_mcu_fw_pmctrl(dev)) { - cancel_delayed_work_sync(&mphy->mac_work); + cancel_delayed_work(&mphy->mac_work); return; } out: diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h index acf627aed609..d2a8b2b0df32 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h @@ -25,6 +25,8 @@ #define MT_PLE_AC_QEMPTY(_n) MT_PLE(0x500 + 0x40 * (_n)) #define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2)) +#define MT_PSE_BASE 0x820c8000 + /* TMAC: band 0(0x21000), band 1(0xa1000) */ #define MT_WF_TMAC_BASE(_band) ((_band) ? 0x820f4000 : 0x820e4000) #define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs)) @@ -390,6 +392,10 @@ #define MT_CBTOP_RGU_WF_SUBSYS_RST MT_CBTOP_RGU(0x600) #define MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH BIT(0) +#define MT7925_CBTOP_RGU_WF_SUBSYS_RST 0x70028600 +#define MT7925_WFSYS_INIT_DONE_ADDR 0x184c1604 +#define MT7925_WFSYS_INIT_DONE 0x00001d1e + #define MT_HW_BOUND 0x70010020 #define MT_HW_CHIPID 0x70010200 #define MT_HW_REV 0x70010204 diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 552808458138..47827d1c5ccb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -206,6 +206,33 @@ static void mt792xu_epctl_rst_opt(struct mt792x_dev *dev, bool reset) mt792xu_uhw_wr(&dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT, val); } +struct mt792xu_wfsys_desc { + u32 rst_reg; + u32 done_reg; + u32 done_mask; + u32 done_val; + u32 delay_ms; + bool need_status_sel; +}; + +static const struct mt792xu_wfsys_desc mt7921_wfsys_desc = { + .rst_reg = MT_CBTOP_RGU_WF_SUBSYS_RST, + .done_reg = MT_UDMA_CONN_INFRA_STATUS, + .done_mask = MT_UDMA_CONN_WFSYS_INIT_DONE, + .done_val = MT_UDMA_CONN_WFSYS_INIT_DONE, + .delay_ms = 0, + .need_status_sel = true, +}; + +static const struct mt792xu_wfsys_desc mt7925_wfsys_desc = { + .rst_reg = MT7925_CBTOP_RGU_WF_SUBSYS_RST, + .done_reg = MT7925_WFSYS_INIT_DONE_ADDR, + .done_mask = U32_MAX, + .done_val = MT7925_WFSYS_INIT_DONE, + .delay_ms = 20, + .need_status_sel = false, +}; + int mt792xu_dma_init(struct mt792x_dev *dev, bool resume) { int err; @@ -236,25 +263,33 @@ EXPORT_SYMBOL_GPL(mt792xu_dma_init); int mt792xu_wfsys_reset(struct mt792x_dev *dev) { + const struct mt792xu_wfsys_desc *desc = is_mt7925(&dev->mt76) ? + &mt7925_wfsys_desc : + &mt7921_wfsys_desc; u32 val; int i; mt792xu_epctl_rst_opt(dev, false); - val = mt792xu_uhw_rr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST); + val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg); val |= MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; - mt792xu_uhw_wr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val); + mt792xu_uhw_wr(&dev->mt76, desc->rst_reg, val); - usleep_range(10, 20); + if (desc->delay_ms) + msleep(desc->delay_ms); + else + usleep_range(10, 20); - val = mt792xu_uhw_rr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST); + val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg); val &= ~MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; - mt792xu_uhw_wr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val); + mt792xu_uhw_wr(&dev->mt76, desc->rst_reg, val); + + if (desc->need_status_sel) + mt792xu_uhw_wr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS_SEL, 0); - mt792xu_uhw_wr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS_SEL, 0); for (i = 0; i < MT792x_WFSYS_INIT_RETRY_COUNT; i++) { - val = mt792xu_uhw_rr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS); - if (val & MT_UDMA_CONN_WFSYS_INIT_DONE) + val = mt792xu_uhw_rr(&dev->mt76, desc->done_reg); + if ((val & desc->done_mask) == desc->done_val) break; msleep(100); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c index 76d623b2cafb..34af800964d1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -226,14 +226,23 @@ mt7996_radar_trigger(void *data, u64 val) #define RADAR_BACKGROUND 2 struct mt7996_dev *dev = data; struct mt7996_phy *phy = mt7996_band_phy(dev, NL80211_BAND_5GHZ); - int rdd_idx; + struct cfg80211_chan_def *chandef; + int rdd_idx, ret; if (!phy || !val || val > RADAR_BACKGROUND) return -EINVAL; - if (val == RADAR_BACKGROUND && !dev->rdd2_phy) { - dev_err(dev->mt76.dev, "Background radar is not enabled\n"); - return -EINVAL; + if (test_bit(MT76_SCANNING, &phy->mt76->state)) + return -EBUSY; + + if (val == RADAR_BACKGROUND) { + if (!dev->rdd2_phy || !cfg80211_chandef_valid(&dev->rdd2_chandef)) { + dev_err(dev->mt76.dev, "Background radar is not enabled\n"); + return -EINVAL; + } + chandef = &dev->rdd2_chandef; + } else { + chandef = &phy->mt76->chandef; } rdd_idx = mt7996_get_rdd_idx(phy, val == RADAR_BACKGROUND); @@ -242,6 +251,11 @@ mt7996_radar_trigger(void *data, u64 val) return -EINVAL; } + ret = cfg80211_chandef_dfs_required(dev->mt76.hw->wiphy, chandef, + NL80211_IFTYPE_AP); + if (ret <= 0) + return ret; + return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, rdd_idx, 0); } @@ -626,13 +640,18 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_vif *mvif = msta->vif; - struct mt7996_dev *dev = mvif->deflink.phy->dev; + struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); struct ieee80211_link_sta *link_sta; struct seq_file *s = data; struct ieee80211_vif *vif; + struct mt7996_dev *dev; unsigned int link_id; + if (!phy) + return; + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + dev = phy->dev; rcu_read_lock(); @@ -979,13 +998,17 @@ static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file, #define LONG_PREAMBLE 1 struct ieee80211_link_sta *link_sta = file->private_data; struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv; - struct mt7996_dev *dev = msta->vif->deflink.phy->dev; + struct mt7996_phy *link_phy = mt7996_vif_link_phy(&msta->vif->deflink); struct mt7996_sta_link *msta_link; struct ra_rate phy = {}; + struct mt7996_dev *dev; char buf[100]; int ret; u16 gi, ltf; + if (!link_phy) + return -EINVAL; + if (count >= sizeof(buf)) return -EINVAL; @@ -1008,6 +1031,7 @@ static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file, * spe - off: 0, on: 1 * ltf - 1xltf: 0, 2xltf: 1, 4xltf: 2 */ + dev = link_phy->dev; if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu %hhu %hhu %hhu %hu", &phy.mode, &phy.bw, &phy.mcs, &phy.nss, &gi, &phy.preamble, &phy.stbc, &phy.ldpc, &phy.spe, <f) != 10) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c index 274b273df1ee..8f5d297dafce 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c @@ -128,15 +128,27 @@ static void mt7996_dma_config(struct mt7996_dev *dev) /* data tx queue */ if (is_mt7996(&dev->mt76)) { - TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0); if (dev->hif2) { - /* default bn1:ring19 bn2:ring21 */ - TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, - MT7996_TXQ_BAND1); - TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, - MT7996_TXQ_BAND2); + if (mt76_npu_device_active(&dev->mt76)) { + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND2, + MT7996_TXQ_BAND2); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7996_TXQ_BAND0); + TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, + MT7996_TXQ_BAND1); + } else { + /* default bn1:ring19 bn2:ring21 */ + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7996_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, + MT7996_TXQ_BAND1); + TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, + MT7996_TXQ_BAND2); + } } else { /* single pcie bn0/1:ring18 bn2:ring19 */ + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7996_TXQ_BAND0); TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1); } @@ -350,6 +362,9 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset) if (!mt7996_has_wa(dev) || mt76_npu_device_active(&dev->mt76)) irq_mask &= ~(MT_INT_RX(MT_RXQ_MAIN_WA) | MT_INT_RX(MT_RXQ_BAND1_WA)); + if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76)) + irq_mask &= ~(MT_INT_RX(MT_RXQ_TXFREE_BAND0) | + MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND2)); irq_mask = reset ? MT_INT_MCU_CMD : irq_mask; mt7996_irq_enable(dev, irq_mask); @@ -430,39 +445,48 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset) MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 | MT_WFDMA_HOST_CONFIG_BAND2_PCIE1); - if (is_mt7996(&dev->mt76)) - mt76_set(dev, MT_WFDMA_HOST_CONFIG, - MT_WFDMA_HOST_CONFIG_BAND2_PCIE1); - else + if (is_mt7996(&dev->mt76)) { + if (mt76_npu_device_active(&dev->mt76)) + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_BAND0_PCIE1); + else + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_BAND2_PCIE1); + } else { mt76_set(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_BAND1_PCIE1); + } /* AXI read outstanding number */ mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL, MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14); - if (dev->hif2->speed < PCIE_SPEED_5_0GT || - (dev->hif2->speed == PCIE_SPEED_5_0GT && - dev->hif2->width < PCIE_LNK_X2)) { - mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs, - WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, - FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, - 0x1)); - mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2, - MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, - FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, - 0x1)); - } else if (dev->hif2->speed < PCIE_SPEED_8_0GT || - (dev->hif2->speed == PCIE_SPEED_8_0GT && - dev->hif2->width < PCIE_LNK_X2)) { - mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs, - WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, - FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, - 0x2)); - mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2, - MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, - FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, - 0x2)); + if (!is_mt7996(&dev->mt76) || + !mt76_npu_device_active(&dev->mt76)) { + if (dev->hif2->speed < PCIE_SPEED_5_0GT || + (dev->hif2->speed == PCIE_SPEED_5_0GT && + dev->hif2->width < PCIE_LNK_X2)) { + mt76_rmw(dev, + WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, + FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, + 0x1)); + mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2, + MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, + FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, + 0x1)); + } else if (dev->hif2->speed < PCIE_SPEED_8_0GT || + (dev->hif2->speed == PCIE_SPEED_8_0GT && + dev->hif2->width < PCIE_LNK_X2)) { + mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, + FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, + 0x2)); + mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2, + MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, + FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, + 0x2)); + } } /* WFDMA rx threshold */ @@ -497,7 +521,7 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset) int mt7996_dma_rro_init(struct mt7996_dev *dev) { struct mt76_dev *mdev = &dev->mt76; - u32 irq_mask; + u32 size; int ret; if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) { @@ -524,7 +548,8 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev) mt76_queue_reset(dev, &mdev->q_rx[MT_RXQ_RRO_RXDMAD_C], true); } - goto start_hw_rro; + + return 0; } /* ind cmd */ @@ -545,10 +570,12 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev) if (mtk_wed_device_active(&mdev->mmio.wed) && mtk_wed_get_rx_capa(&mdev->mmio.wed)) mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0].wed = &mdev->mmio.wed; + + size = is_mt7996(mdev) && mt76_npu_device_active(mdev) + ? MT7996_NPU_RX_RING_SIZE / 4 : MT7996_RX_RING_SIZE; ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0], MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND0), - MT7996_RX_RING_SIZE, - MT7996_RX_MSDU_PAGE_SIZE, + size, MT7996_RX_MSDU_PAGE_SIZE, MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND0)); if (ret) return ret; @@ -560,10 +587,12 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev) if (mtk_wed_device_active(&mdev->mmio.wed) && mtk_wed_get_rx_capa(&mdev->mmio.wed)) mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].wed = &mdev->mmio.wed; + + size = is_mt7996(mdev) && mt76_npu_device_active(mdev) + ? MT7996_NPU_RX_RING_SIZE / 2 : MT7996_RX_RING_SIZE; ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1], MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND1), - MT7996_RX_RING_SIZE, - MT7996_RX_MSDU_PAGE_SIZE, + size, MT7996_RX_MSDU_PAGE_SIZE, MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND1)); if (ret) return ret; @@ -576,52 +605,60 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev) if (mtk_wed_device_active(&mdev->mmio.wed) && mtk_wed_get_rx_capa(&mdev->mmio.wed)) mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2].wed = &mdev->mmio.wed; + + size = is_mt7996(mdev) && mt76_npu_device_active(mdev) + ? MT7996_NPU_RX_RING_SIZE : MT7996_RX_RING_SIZE; ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2], MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND2), - MT7996_RX_RING_SIZE, - MT7996_RX_MSDU_PAGE_SIZE, + size, MT7996_RX_MSDU_PAGE_SIZE, MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND2)); if (ret) return ret; } -start_hw_rro: - if (mtk_wed_device_active(&mdev->mmio.wed)) { - irq_mask = mdev->mmio.irqmask | + return 0; +} + +void mt7996_dma_rro_start(struct mt7996_dev *dev) +{ + u32 irq_mask; + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { + irq_mask = dev->mt76.mmio.irqmask | MT_INT_TX_DONE_BAND2; mt76_wr(dev, MT_INT_MASK_CSR, irq_mask); - mtk_wed_device_start_hw_rro(&mdev->mmio.wed, irq_mask, false); + mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, irq_mask, + false); mt7996_irq_enable(dev, irq_mask); - } else { - if (is_mt7996(&dev->mt76)) { - mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND1, - mt76_dma_rx_poll); - mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND2, - mt76_dma_rx_poll); - mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND2, - mt76_dma_rx_poll); - } else { - mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND1, - mt76_dma_rx_poll); - } - - mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND0, mt76_dma_rx_poll); - if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) { - mt76_queue_rx_init(dev, MT_RXQ_RRO_RXDMAD_C, - mt76_dma_rx_poll); - } else { - mt76_queue_rx_init(dev, MT_RXQ_RRO_IND, - mt76_dma_rx_poll); - mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND0, - mt76_dma_rx_poll); - } - - if (!mt76_npu_device_active(&dev->mt76)) - mt7996_irq_enable(dev, MT_INT_RRO_RX_DONE); + return; } - return 0; + if (is_mt7996(&dev->mt76)) { + mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND1, + mt76_dma_rx_poll); + mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND2, + mt76_dma_rx_poll); + mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND2, + mt76_dma_rx_poll); + } else { + mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND1, + mt76_dma_rx_poll); + } + + mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND0, mt76_dma_rx_poll); + if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) { + mt76_queue_rx_init(dev, MT_RXQ_RRO_RXDMAD_C, + mt76_dma_rx_poll); + } else { + mt76_queue_rx_init(dev, MT_RXQ_RRO_IND, + mt76_dma_rx_poll); + mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND0, + mt76_dma_rx_poll); + } + + if (!mt76_npu_device_active(&dev->mt76)) + mt7996_irq_enable(dev, MT_INT_RRO_RX_DONE); } int mt7996_dma_init(struct mt7996_dev *dev) @@ -642,11 +679,16 @@ int mt7996_dma_init(struct mt7996_dev *dev) mt7996_dma_disable(dev, true); /* init tx queue */ - ret = mt7996_init_tx_queues(&dev->phy, - MT_TXQ_ID(dev->mphy.band_idx), - MT7996_TX_RING_SIZE, - MT_TXQ_RING_BASE(0), - wed); + if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76)) + ret = mt7996_init_tx_queues(&dev->phy, MT_TXQ_ID(0), + MT7996_NPU_TX_RING_SIZE, + MT_TXQ_RING_BASE(0) + hif1_ofs, + NULL); + else + ret = mt7996_init_tx_queues(&dev->phy, + MT_TXQ_ID(dev->mphy.band_idx), + MT7996_TX_RING_SIZE, + MT_TXQ_RING_BASE(0), wed); if (ret) return ret; @@ -714,6 +756,9 @@ int mt7996_dma_init(struct mt7996_dev *dev) (is_mt7992(&dev->mt76)))) { dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE; dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed; + } else if (is_mt7992(&dev->mt76) && + mt76_npu_device_active(&dev->mt76)) { + dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_NPU_Q_TXFREE(0); } if (mt7996_has_wa(dev)) { @@ -846,6 +891,8 @@ int mt7996_dma_init(struct mt7996_dev *dev) /* tx free notify event from WA for band0 */ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE; dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed; + } else if (mt76_npu_device_active(&dev->mt76)) { + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_NPU_Q_TXFREE(0); } ret = mt76_queue_alloc(dev, @@ -859,16 +906,21 @@ int mt7996_dma_init(struct mt7996_dev *dev) } if (mt7996_band_valid(dev, MT_BAND2)) { + u32 size; + /* rx rro data queue for band2 */ dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags = MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN; if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) dev->mt76.q_rx[MT_RXQ_RRO_BAND2].wed = wed; + + size = is_mt7996(&dev->mt76) && + mt76_npu_device_active(&dev->mt76) + ? MT7996_NPU_RX_RING_SIZE : MT7996_RX_RING_SIZE; ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2], MT_RXQ_ID(MT_RXQ_RRO_BAND2), - MT7996_RX_RING_SIZE, - MT7996_RX_BUF_SIZE, + size, MT7996_RX_BUF_SIZE, MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND2) + hif1_ofs); if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c index 8f60772913b4..ac05f7d75d63 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c @@ -33,6 +33,8 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev) if (dev->var.fem == MT7996_FEM_INT) return MT7992_EEPROM_DEFAULT_23_INT; return MT7992_EEPROM_DEFAULT_23; + case MT7992_VAR_TYPE_24: + return MT7992_EEPROM_DEFAULT_24; case MT7992_VAR_TYPE_44: default: if (dev->var.fem == MT7996_FEM_INT) @@ -153,7 +155,7 @@ mt7996_eeprom_check_or_use_default(struct mt7996_dev *dev, bool use_default) dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n"); memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE); - dev->flash_mode = true; + dev->eeprom_mode = EEPROM_MODE_DEFAULT_BIN; out: release_firmware(fw); @@ -163,26 +165,31 @@ mt7996_eeprom_check_or_use_default(struct mt7996_dev *dev, bool use_default) static int mt7996_eeprom_load(struct mt7996_dev *dev) { + u32 eeprom_blk_size, block_num; bool use_default = false; - int ret; + int ret, i; ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE); if (ret < 0) return ret; if (ret && !mt7996_check_eeprom(dev)) { - dev->flash_mode = true; + dev->eeprom_mode = EEPROM_MODE_FLASH; goto out; } - if (!dev->flash_mode) { - u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE; - u32 block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size); + memset(dev->mt76.eeprom.data, 0, MT7996_EEPROM_SIZE); + if (mt7996_has_ext_eeprom(dev)) { + /* external eeprom mode */ + dev->eeprom_mode = EEPROM_MODE_EXT; + eeprom_blk_size = MT7996_EXT_EEPROM_BLOCK_SIZE; + } else { u8 free_block_num; - int i; - memset(dev->mt76.eeprom.data, 0, MT7996_EEPROM_SIZE); - ret = mt7996_mcu_get_eeprom_free_block(dev, &free_block_num); + /* efuse mode */ + dev->eeprom_mode = EEPROM_MODE_EFUSE; + eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE; + ret = mt7996_mcu_get_efuse_free_block(dev, &free_block_num); if (ret < 0) return ret; @@ -191,27 +198,29 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev) use_default = true; goto out; } + } - /* check if eeprom data from fw is valid */ - if (mt7996_mcu_get_eeprom(dev, 0, NULL, 0) || - mt7996_check_eeprom(dev)) { + /* check if eeprom data from fw is valid */ + if (mt7996_mcu_get_eeprom(dev, 0, NULL, eeprom_blk_size, + dev->eeprom_mode) || + mt7996_check_eeprom(dev)) { + use_default = true; + goto out; + } + + /* read eeprom data from fw */ + block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size); + for (i = 1; i < block_num; i++) { + u32 len = eeprom_blk_size; + + if (i == block_num - 1) + len = MT7996_EEPROM_SIZE % eeprom_blk_size; + ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, + NULL, len, dev->eeprom_mode); + if (ret && ret != -EINVAL) { use_default = true; goto out; } - - /* read eeprom data from fw */ - for (i = 1; i < block_num; i++) { - u32 len = eeprom_blk_size; - - if (i == block_num - 1) - len = MT7996_EEPROM_SIZE % eeprom_blk_size; - ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, - NULL, len); - if (ret && ret != -EINVAL) { - use_default = true; - goto out; - } - } } out: @@ -385,7 +394,8 @@ bool mt7996_eeprom_has_background_radar(struct mt7996_dev *dev) return false; break; case MT7992_DEVICE_ID: - if (dev->var.type == MT7992_VAR_TYPE_23) + if (dev->var.type == MT7992_VAR_TYPE_23 || + dev->var.type == MT7992_VAR_TYPE_24) return false; break; case MT7990_DEVICE_ID: { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index 00a8286bd136..d6f9aa1ab52d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -34,6 +34,20 @@ static const struct ieee80211_iface_combination if_comb_global = { BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_160), + .beacon_int_min_gcd = 100, +}; + +static const struct ieee80211_iface_combination if_comb_global_7992 = { + .limits = &if_limits_global, + .n_limits = 1, + .max_interfaces = 32, + .num_different_channels = MT7996_MAX_RADIOS - 1, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160), + .beacon_int_min_gcd = 100, }; static const struct ieee80211_iface_limit if_limits[] = { @@ -85,6 +99,7 @@ static const struct wiphy_iftype_ext_capab iftypes_ext_capa[] = { .extended_capabilities_mask = if_types_ext_capa_ap, .extended_capabilities_len = sizeof(if_types_ext_capa_ap), .mld_capa_and_ops = + FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_FREQ_SEP_TYPE_IND, 1) | FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS, MT7996_MAX_RADIOS - 1), }, @@ -485,7 +500,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed) hw->vif_data_size = sizeof(struct mt7996_vif); hw->chanctx_data_size = sizeof(struct mt76_chanctx); - wiphy->iface_combinations = &if_comb_global; + wiphy->iface_combinations = is_mt7996(&dev->mt76) ? &if_comb_global : + &if_comb_global_7992; wiphy->n_iface_combinations = 1; wiphy->radio = dev->radios; @@ -521,8 +537,10 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed) ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD); ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR); ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, CHANCTX_STA_CSA); hw->max_tx_fragments = 4; + wiphy->txq_memory_limit = 32 << 20; /* 32 MiB */ /* init led callbacks */ if (IS_ENABLED(CONFIG_MT76_LEDS)) { @@ -592,7 +610,7 @@ static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev) void mt7996_mac_init(struct mt7996_dev *dev) { #define HIF_TXD_V2_1 0x21 - int i; + int i, rx_path_type; mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT); @@ -606,11 +624,16 @@ void mt7996_mac_init(struct mt7996_dev *dev) } /* rro module init */ - if (dev->hif2) + if (dev->hif2) { + if (mt76_npu_device_active(&dev->mt76)) + rx_path_type = is_mt7996(&dev->mt76) ? 6 : 8; + else + rx_path_type = is_mt7996(&dev->mt76) ? 2 : 7; mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, - is_mt7996(&dev->mt76) ? 2 : 7); - else + rx_path_type); + } else { mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 0); + } if (mt7996_has_hwrro(dev)) { u16 timeout; @@ -668,8 +691,9 @@ static int mt7996_register_phy(struct mt7996_dev *dev, enum mt76_band_id band) return 0; if (dev->hif2 && - ((is_mt7996(&dev->mt76) && band == MT_BAND2) || - (is_mt7992(&dev->mt76) && band == MT_BAND1))) { + ((is_mt7992(&dev->mt76) && band == MT_BAND1) || + (is_mt7996(&dev->mt76) && band == MT_BAND2 && + !mt76_npu_device_active(&dev->mt76)))) { hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); wed = &dev->mt76.mmio.wed_hif2; } @@ -709,14 +733,19 @@ static int mt7996_register_phy(struct mt7996_dev *dev, enum mt76_band_id band) /* init wiphy according to mphy and phy */ mt7996_init_wiphy_band(mphy->hw, phy); - if (is_mt7996(&dev->mt76) && !dev->hif2 && band == MT_BAND1) { + if (is_mt7996(&dev->mt76) && + ((band == MT_BAND1 && !dev->hif2) || + (band == MT_BAND2 && mt76_npu_device_active(&dev->mt76)))) { int i; for (i = 0; i <= MT_TXQ_PSD; i++) - mphy->q_tx[i] = dev->mt76.phys[MT_BAND0]->q_tx[0]; + mphy->q_tx[i] = dev->mt76.phys[band - 1]->q_tx[0]; } else { - ret = mt7996_init_tx_queues(mphy->priv, MT_TXQ_ID(band), - MT7996_TX_RING_SIZE, + int size = is_mt7996(&dev->mt76) && + mt76_npu_device_active(&dev->mt76) + ? MT7996_NPU_TX_RING_SIZE / 2 : MT7996_TX_RING_SIZE; + + ret = mt7996_init_tx_queues(mphy->priv, MT_TXQ_ID(band), size, MT_TXQ_RING_BASE(band) + hif1_ofs, wed); if (ret) @@ -756,15 +785,41 @@ static void mt7996_init_work(struct work_struct *work) mt7996_mcu_set_eeprom(dev); mt7996_mac_init(dev); mt7996_txbf_init(dev); + + if (!is_mt7990(&dev->mt76)) + mt7996_mcu_set_dup_wtbl(dev); } void mt7996_wfsys_reset(struct mt7996_dev *dev) { - mt76_set(dev, MT_WF_SUBSYS_RST, 0x1); + if (!is_mt7990(&dev->mt76)) { + mt76_set(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + + mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + + return; + } + + if (!dev->recovery.hw_full_reset) + return; + + mt76_set(dev, MT_WF_SUBSYS_RST, + MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT | + MT_WF_SUBSYS_RST_BYPASS_WFDMA_SLP_PROT | + MT_WF_SUBSYS_RST_BYPASS_WFDMA2_SLP_PROT); + mt76_rmw(dev, MT_WF_SUBSYS_RST, + MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT_CYCLE, + u32_encode_bits(0x20, MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT_CYCLE)); + mt76_clear(dev, MT_WF_L05_RST, MT_WF_L05_RST_WF_RST_MASK); + mt76_set(dev, MT_WF_SUBSYS_RST, MT_WF_SUBSYS_RST_WHOLE_PATH_RST); msleep(20); - mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1); - msleep(20); + if (mt76_poll(dev, MT_WF_L05_RST, MT_WF_L05_RST_WF_RST_MASK, 0x1a, 1000)) + return; + + dev_err(dev->mt76.dev, "wfsys reset fail\n"); } static void mt7996_rro_hw_init_v3(struct mt7996_dev *dev) @@ -842,8 +897,7 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev) } } else { /* set emul 3.0 function */ - mt76_wr(dev, MT_RRO_3_0_EMU_CONF, - MT_RRO_3_0_EMU_CONF_EN_MASK); + mt76_set(dev, MT_RRO_3_0_EMU_CONF, MT_RRO_3_0_EMU_CONF_EN_MASK); mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0, dev->wed_rro.addr_elem[0].phy_addr); @@ -935,6 +989,12 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev) addr++; } + if (is_mt7996(&dev->mt76) && + mt76_npu_device_active(&dev->mt76)) + mt76_npu_send_txrx_addr(&dev->mt76, 0, i, + dev->wed_rro.addr_elem[i].phy_addr, + 0, 0); + #ifdef CONFIG_NET_MEDIATEK_SOC_WED if (mtk_wed_device_active(&dev->mt76.mmio.wed) && mtk_wed_get_rx_capa(&dev->mt76.mmio.wed)) { @@ -995,6 +1055,10 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev) addr++; } + if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76)) + mt76_npu_send_txrx_addr(&dev->mt76, 1, 0, + dev->wed_rro.session.phy_addr, 0, 0); + mt7996_rro_hw_init(dev); return mt7996_dma_rro_init(dev); @@ -1081,8 +1145,12 @@ static void mt7996_wed_rro_work(struct work_struct *work) list); list_del_init(&e->list); - if (mt76_npu_device_active(&dev->mt76)) + if (mt76_npu_device_active(&dev->mt76)) { + if (is_mt7996(&dev->mt76)) + mt76_npu_send_txrx_addr(&dev->mt76, 3, e->id, + 0, 0, 0); goto reset_session; + } for (i = 0; i < MT7996_RRO_WINDOW_MAX_LEN; i++) { void *ptr = dev->wed_rro.session.ptr; @@ -1129,7 +1197,7 @@ static int mt7996_variant_type_init(struct mt7996_dev *dev) else if (u32_get_bits(val, MT_PAD_GPIO_ADIE_COMB_7992)) var_type = MT7992_VAR_TYPE_44; else - return -EINVAL; + var_type = MT7992_VAR_TYPE_24; break; case MT7990_DEVICE_ID: var_type = MT7990_VAR_TYPE_23; @@ -1163,7 +1231,8 @@ static int mt7996_variant_fem_init(struct mt7996_dev *dev) if (ret) return ret; - ret = mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf, sizeof(buf)); + ret = mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf, sizeof(buf), + EEPROM_MODE_EFUSE); if (ret && ret != -EINVAL) return ret; @@ -1696,6 +1765,8 @@ int mt7996_register_device(struct mt7996_dev *dev) if (ret) return ret; + mt7996_dma_rro_start(dev); + ret = mt76_register_device(&dev->mt76, true, mt76_rates, ARRAY_SIZE(mt76_rates)); if (ret) @@ -1726,6 +1797,7 @@ int mt7996_register_device(struct mt7996_dev *dev) void mt7996_unregister_device(struct mt7996_dev *dev) { + cancel_work_sync(&dev->dump_work); cancel_work_sync(&dev->wed_rro.work); mt7996_unregister_phy(mt7996_phy3(dev)); mt7996_unregister_phy(mt7996_phy2(dev)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 84cbf36b493c..e2a83da3a09c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -13,45 +13,6 @@ #define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2) -static const struct mt7996_dfs_radar_spec etsi_radar_specs = { - .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, - .radar_pattern = { - [5] = { 1, 0, 6, 32, 28, 0, 990, 5010, 17, 1, 1 }, - [6] = { 1, 0, 9, 32, 28, 0, 615, 5010, 27, 1, 1 }, - [7] = { 1, 0, 15, 32, 28, 0, 240, 445, 27, 1, 1 }, - [8] = { 1, 0, 12, 32, 28, 0, 240, 510, 42, 1, 1 }, - [9] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 12, 32, 28, { }, 126 }, - [10] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 15, 32, 24, { }, 126 }, - [11] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 18, 32, 28, { }, 54 }, - [12] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 27, 32, 24, { }, 54 }, - }, -}; - -static const struct mt7996_dfs_radar_spec fcc_radar_specs = { - .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, - .radar_pattern = { - [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 }, - [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 }, - [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 }, - [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 }, - [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 }, - }, -}; - -static const struct mt7996_dfs_radar_spec jp_radar_specs = { - .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, - .radar_pattern = { - [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 }, - [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 }, - [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 }, - [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 }, - [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 }, - [13] = { 1, 0, 7, 32, 28, 0, 3836, 3856, 14, 1, 1 }, - [14] = { 1, 0, 6, 32, 28, 0, 615, 5010, 110, 1, 1 }, - [15] = { 1, 1, 0, 0, 0, 0, 15, 5010, 110, 0, 0, 12, 32, 28 }, - }, -}; - static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, u16 idx, u8 band_idx) { @@ -527,7 +488,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q, !(csum_status & (BIT(0) | BIT(2) | BIT(3)))) skb->ip_summed = CHECKSUM_UNNECESSARY; - if (rxd1 & MT_RXD3_NORMAL_FCS_ERR) + if (rxd3 & MT_RXD3_NORMAL_FCS_ERR) status->flag |= RX_FLAG_FAILED_FCS_CRC; if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) @@ -700,6 +661,8 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q, hdr = mt76_skb_get_hdr(skb); fc = hdr->frame_control; + if (ieee80211_is_beacon(fc)) + mt76_rx_beacon(mphy, skb); if (ieee80211_is_data_qos(fc)) { u8 *qos = ieee80211_get_qos_ctl(hdr); @@ -1139,10 +1102,10 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, * req */ if (le32_to_cpu(ptr[7]) & MT_TXD7_MAC_TXD) { - u32 val; + u32 val, mac_txp_size = sizeof(struct mt76_connac_hw_txp); ptr = (__le32 *)(txwi + MT_TXD_SIZE); - memset((void *)ptr, 0, sizeof(struct mt76_connac_fw_txp)); + memset((void *)ptr, 0, mac_txp_size); val = FIELD_PREP(MT_TXP0_TOKEN_ID0, id) | MT_TXP0_TOKEN_ID0_VALID_MASK; @@ -1161,6 +1124,8 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, tx_info->buf[1].addr >> 32); #endif ptr[3] = cpu_to_le32(val); + + tx_info->buf[0].len = MT_TXD_SIZE + mac_txp_size; } else { struct mt76_connac_txp_common *txp; @@ -1270,8 +1235,9 @@ mt7996_tx_check_aggr(struct ieee80211_link_sta *link_sta, if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA))) return; - if (!test_and_set_bit(tid, &wcid->ampdu_state)) - ieee80211_start_tx_ba_session(link_sta->sta, tid, 0); + if (!test_and_set_bit(tid, &wcid->ampdu_state) && + ieee80211_start_tx_ba_session(link_sta->sta, tid, 0)) + clear_bit(tid, &wcid->ampdu_state); } static void @@ -2203,9 +2169,14 @@ mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif) for_each_vif_active_link(vif, link_conf, link_id) { struct mt7996_vif_link *link; + struct mt7996_phy *link_phy; link = mt7996_vif_link(dev, vif, link_id); - if (!link || link->phy != phy) + if (!link) + continue; + + link_phy = mt7996_vif_link_phy(link); + if (link_phy != phy) continue; mt7996_mcu_add_beacon(dev->mt76.hw, vif, link_conf, @@ -2248,6 +2219,12 @@ void mt7996_tx_token_put(struct mt7996_dev *dev) } spin_unlock_bh(&dev->mt76.token_lock); idr_destroy(&dev->mt76.token); + + for (id = 0; id < __MT_MAX_BAND; id++) { + struct mt76_phy *phy = dev->mt76.phys[id]; + if (phy) + atomic_set(&phy->mgmt_tx_pending, 0); + } } static int @@ -2395,23 +2372,8 @@ mt7996_mac_reset_sta_iter(void *data, struct ieee80211_sta *sta) struct mt7996_dev *dev = data; int i; - for (i = 0; i < ARRAY_SIZE(msta->link); i++) { - struct mt7996_sta_link *msta_link = NULL; - - msta_link = rcu_replace_pointer(msta->link[i], msta_link, - lockdep_is_held(&dev->mt76.mutex)); - if (!msta_link) - continue; - - mt7996_mac_sta_deinit_link(dev, msta_link); - - if (msta->deflink_id == i) { - msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; - continue; - } - - kfree_rcu(msta_link, rcu_head); - } + for (i = 0; i < ARRAY_SIZE(msta->link); i++) + mt7996_mac_sta_remove_link(dev, sta, i, true); } static void @@ -2544,6 +2506,7 @@ void mt7996_mac_reset_work(struct work_struct *work) if (mtk_wed_device_active(&dev->mt76.mmio.wed)) mtk_wed_device_stop(&dev->mt76.mmio.wed); + mt7996_npu_hw_stop(dev); ieee80211_stop_queues(mt76_hw(dev)); set_bit(MT76_RESET, &dev->mphy.state); @@ -2564,14 +2527,19 @@ void mt7996_mac_reset_work(struct work_struct *work) mt76_queue_is_wed_rro(&dev->mt76.q_rx[i])) continue; + if (mt76_npu_device_active(&dev->mt76) && + mt76_queue_is_wed_rro(&dev->mt76.q_rx[i])) + continue; + + if (mt76_queue_is_npu_txfree(&dev->mt76.q_rx[i])) + continue; + napi_disable(&dev->mt76.napi[i]); } napi_disable(&dev->mt76.tx_napi); mutex_lock(&dev->mt76.mutex); - mt7996_npu_hw_stop(dev); - mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED); if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { @@ -2591,7 +2559,7 @@ void mt7996_mac_reset_work(struct work_struct *work) mt7996_dma_start(dev, false, false); if (!is_mt7996(&dev->mt76) && dev->mt76.hwrro_mode == MT76_HWRRO_V3) - mt76_wr(dev, MT_RRO_3_0_EMU_CONF, MT_RRO_3_0_EMU_CONF_EN_MASK); + mt76_set(dev, MT_RRO_3_0_EMU_CONF, MT_RRO_3_0_EMU_CONF_EN_MASK); if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { u32 wed_irq_mask = MT_INT_TX_DONE_BAND2 | @@ -2610,6 +2578,8 @@ void mt7996_mac_reset_work(struct work_struct *work) MT_INT_TX_RX_DONE_EXT); } + __mt7996_npu_hw_init(dev); + clear_bit(MT76_MCU_RESET, &dev->mphy.state); mt7996_for_each_phy(dev, phy) clear_bit(MT76_RESET, &phy->mt76->state); @@ -2619,6 +2589,13 @@ void mt7996_mac_reset_work(struct work_struct *work) mt76_queue_is_wed_rro(&dev->mt76.q_rx[i])) continue; + if (mt76_npu_device_active(&dev->mt76) && + mt76_queue_is_wed_rro(&dev->mt76.q_rx[i])) + continue; + + if (mt76_queue_is_npu_txfree(&dev->mt76.q_rx[i])) + continue; + napi_enable(&dev->mt76.napi[i]); local_bh_disable(); napi_schedule(&dev->mt76.napi[i]); @@ -2639,8 +2616,6 @@ void mt7996_mac_reset_work(struct work_struct *work) mutex_unlock(&dev->mt76.mutex); - mt7996_npu_hw_init(dev); - mt7996_for_each_phy(dev, phy) ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, MT7996_WATCHDOG_TIME); @@ -2737,6 +2712,11 @@ void mt7996_reset(struct mt7996_dev *dev) return; } + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA) { + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + } + queue_work(dev->mt76.wq, &dev->reset_work); wake_up(&dev->reset_wait); } @@ -2955,6 +2935,7 @@ void mt7996_mac_work(struct work_struct *work) mutex_unlock(&mphy->dev->mutex); + mt76_beacon_mon_check(mphy); mt76_tx_status_check(mphy->dev, false); ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, @@ -2974,7 +2955,7 @@ static void mt7996_dfs_stop_radar_detector(struct mt7996_phy *phy) static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int rdd_idx) { - int err, region; + int region; switch (dev->mt76.region) { case NL80211_DFS_ETSI: @@ -2989,11 +2970,7 @@ static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int rdd_idx) break; } - err = mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region); - if (err < 0) - return err; - - return mt7996_mcu_rdd_cmd(dev, RDD_DET_MODE, rdd_idx, 1); + return mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region); } static int mt7996_dfs_start_radar_detector(struct mt7996_phy *phy) @@ -3015,40 +2992,6 @@ static int mt7996_dfs_start_radar_detector(struct mt7996_phy *phy) return err; } -static int -mt7996_dfs_init_radar_specs(struct mt7996_phy *phy) -{ - const struct mt7996_dfs_radar_spec *radar_specs; - struct mt7996_dev *dev = phy->dev; - int err, i; - - switch (dev->mt76.region) { - case NL80211_DFS_FCC: - radar_specs = &fcc_radar_specs; - err = mt7996_mcu_set_fcc5_lpn(dev, 8); - if (err < 0) - return err; - break; - case NL80211_DFS_ETSI: - radar_specs = &etsi_radar_specs; - break; - case NL80211_DFS_JP: - radar_specs = &jp_radar_specs; - break; - default: - return -EINVAL; - } - - for (i = 0; i < ARRAY_SIZE(radar_specs->radar_pattern); i++) { - err = mt7996_mcu_set_radar_th(dev, i, - &radar_specs->radar_pattern[i]); - if (err < 0) - return err; - } - - return mt7996_mcu_set_pulse_th(dev, &radar_specs->pulse_th); -} - int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy) { struct mt7996_dev *dev = phy->dev; @@ -3068,10 +3011,6 @@ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy) goto stop; if (prev_state <= MT_DFS_STATE_DISABLED) { - err = mt7996_dfs_init_radar_specs(phy); - if (err < 0) - return err; - err = mt7996_dfs_start_radar_detector(phy); if (err < 0) return err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.h b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h index 4eca37b013fc..70ee30f32f88 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h @@ -37,9 +37,4 @@ struct mt7996_dfs_pattern { u32 min_stgpr_diff; } __packed; -struct mt7996_dfs_radar_spec { - struct mt7996_dfs_pulse pulse_th; - struct mt7996_dfs_pattern radar_pattern[16]; -}; - #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index f16135f0b7f9..a8a6552d49f6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -56,7 +56,7 @@ static int mt7996_start(struct ieee80211_hw *hw) mutex_lock(&dev->mt76.mutex); ret = mt7996_mcu_set_hdr_trans(dev, true); - if (!ret && is_mt7992(&dev->mt76)) { + if (!ret && !is_mt7996(&dev->mt76)) { u8 queue = mt76_connac_lmac_mapping(IEEE80211_AC_VI); ret = mt7996_mcu_cp_support(dev, queue); @@ -79,6 +79,7 @@ static void mt7996_stop_phy(struct mt7996_phy *phy) mutex_lock(&dev->mt76.mutex); + mt7996_mcu_rdd_resume_tx(phy); mt7996_mcu_set_radio_en(phy, false); clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); @@ -238,10 +239,13 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, link_conf = &vif->bss_conf; if (cmd == SET_KEY && !sta && !link->mt76.cipher) { + struct mt7996_phy *phy = mt7996_vif_link_phy(link); + link->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); - mt7996_mcu_add_bss_info(link->phy, vif, link_conf, - &link->mt76, msta_link, true); + if (phy) + mt7996_mcu_add_bss_info(phy, vif, link_conf, + &link->mt76, msta_link, true); } if (cmd == SET_KEY) @@ -300,9 +304,16 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, .cmd = SET_KEY, .link_id = link_conf->link_id, }; - struct mt76_txq *mtxq; int mld_idx, idx, ret; + if ((mvif->mt76.valid_links & BIT(link_conf->link_id)) && + !mlink->offchannel) { + if (vif->type == NL80211_IFTYPE_AP) + return mt7996_mcu_mld_link_oper(dev, link_conf, link, + true); + return 0; + } + mlink->idx = __ffs64(~dev->mt76.vif_mask); if (mlink->idx >= mt7996_max_interface_num(dev)) return -ENOSPC; @@ -316,7 +327,6 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, return -ENOSPC; link->mld_idx = mld_idx; - link->phy = phy; mlink->omac_idx = idx; mlink->band_idx = band_idx; mlink->wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3; @@ -343,11 +353,6 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); - if (vif->txq) { - mtxq = (struct mt76_txq *)vif->txq->drv_priv; - mtxq->wcid = idx; - } - if (vif->type != NL80211_IFTYPE_AP && (!mlink->omac_idx || mlink->omac_idx > 3)) vif->offload_flags = 0; @@ -370,13 +375,81 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it); - if (!mlink->wcid->offchannel && - mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED) - mvif->mt76.deflink_id = link_conf->link_id; + if (!mlink->wcid->offchannel) { + if (vif->txq && + mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED) { + struct mt76_txq *mtxq; + + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mvif->mt76.deflink_id = link_conf->link_id; + mtxq->wcid = idx; + } + mvif->mt76.valid_links |= BIT(link_conf->link_id); + } + + if (vif->type == NL80211_IFTYPE_STATION) { + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + + if (vif->cfg.assoc && link_conf->beacon_int) { + mlink->beacon_mon_interval = + msecs_to_jiffies(ieee80211_tu_to_usec( + link_conf->beacon_int) / 1000); + WRITE_ONCE(mlink->beacon_mon_last, jiffies); + } + } return 0; } +static void mt7996_vif_link_destroy(struct mt7996_phy *phy, + struct mt7996_vif_link *link, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link = &link->msta_link; + unsigned int link_id = msta_link->wcid.link_id; + struct mt76_vif_link *mlink = &link->mt76; + struct mt7996_key_iter_data it = { + .cmd = SET_KEY, + .link_id = link_id, + }; + struct mt7996_dev *dev = phy->dev; + int idx = msta_link->wcid.idx; + + if (!link_conf) + link_conf = &vif->bss_conf; + + if (!mlink->wcid->offchannel) + ieee80211_iter_keys(phy->mt76->hw, vif, mt7996_key_iter, &it); + + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, + CONN_STATE_DISCONNECT, false); + mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, false); + mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, false); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + + dev->mt76.vif_mask &= ~BIT_ULL(mlink->idx); + dev->mld_idx_mask &= ~BIT_ULL(link->mld_idx); + phy->omac_mask &= ~BIT_ULL(mlink->omac_idx); + if (!mlink->wcid->offchannel) + mvif->mt76.valid_links &= ~BIT(link_id); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&msta_link->wcid.poll_list)) + list_del_init(&msta_link->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid); + + if (mlink != (struct mt76_vif_link *)vif->drv_priv && + !mlink->wcid->offchannel) { + rcu_assign_pointer(mlink->mvif->link[link_id], NULL); + kfree_rcu(mlink, rcu_head); + } +} + void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct mt76_vif_link *mlink) @@ -384,49 +457,43 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76); struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_sta_link *msta_link = &link->msta_link; + unsigned int link_id = msta_link->wcid.link_id; struct mt7996_phy *phy = mphy->priv; - struct mt7996_dev *dev = phy->dev; - struct mt7996_key_iter_data it = { - .cmd = SET_KEY, - .link_id = link_conf->link_id, - }; - int idx = msta_link->wcid.idx; - if (!mlink->wcid->offchannel) - ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it); + /* Hw requires to destroy active links tearing down the interface, so + * postpone it removing the interface. + */ + if (mlink->wcid->offchannel) { + mt7996_vif_link_destroy(phy, link, vif, link_conf); + } else { + if (vif->type == NL80211_IFTYPE_AP) { + mt7996_mcu_mld_reconf_stop_link(phy->dev, vif, + BIT(link_id)); + mt7996_mcu_mld_link_oper(phy->dev, link_conf, link, + false); + } - mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, - CONN_STATE_DISCONNECT, false); - mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, false); + if (vif->txq && mvif->mt76.deflink_id == link_id) { + struct ieee80211_bss_conf *iter; + struct mt76_txq *mtxq; - mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, false); + mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED; + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + /* Primary link will be removed, look for a new one */ + for_each_vif_active_link(vif, iter, link_id) { + if (link_id == msta_link->wcid.link_id) + continue; - rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + link = mt7996_vif_link(phy->dev, vif, link_id); + if (!link) + continue; - if (!mlink->wcid->offchannel && - mvif->mt76.deflink_id == link_conf->link_id) { - struct ieee80211_bss_conf *iter; - unsigned int link_id; - - mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED; - for_each_vif_active_link(vif, iter, link_id) { - if (link_id != IEEE80211_LINK_UNSPECIFIED) { + mtxq->wcid = link->msta_link.wcid.idx; mvif->mt76.deflink_id = link_id; break; } } } - - dev->mt76.vif_mask &= ~BIT_ULL(mlink->idx); - dev->mld_idx_mask &= ~BIT_ULL(link->mld_idx); - phy->omac_mask &= ~BIT_ULL(mlink->omac_idx); - - spin_lock_bh(&dev->mt76.sta_poll_lock); - if (!list_empty(&msta_link->wcid.poll_list)) - list_del_init(&msta_link->wcid.poll_list); - spin_unlock_bh(&dev->mt76.sta_poll_lock); - - mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid); } static void mt7996_phy_set_rxfilter(struct mt7996_phy *phy) @@ -472,6 +539,8 @@ static void mt7996_set_monitor(struct mt7996_phy *phy, bool enabled) mt76_rmw_field(dev, MT_DMA_DCR0(phy->mt76->band_idx), MT_DMA_DCR0_RXD_G5_EN, enabled); + mt76_rmw_field(dev, MT_MDP_DCR0, + MT_MDP_DCR0_RX_HDR_TRANS_EN, !enabled); mt7996_phy_set_rxfilter(phy); mt7996_mcu_set_sniffer_mode(phy, enabled); } @@ -530,10 +599,29 @@ static void mt7996_remove_iter(void *data, u8 *mac, struct ieee80211_vif *vif) static void mt7996_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + unsigned long rem_links = mvif->mt76.valid_links; struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_radio_data rdata = {}; + unsigned int link_id; int i; + /* Remove all active links */ + for_each_set_bit(link_id, &rem_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_vif_link *link; + struct mt7996_phy *phy; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + phy = __mt7996_phy(dev, link->msta_link.wcid.phy_idx); + if (!phy) + continue; + + mt7996_vif_link_destroy(phy, link, vif, NULL); + } + ieee80211_iterate_active_interfaces_mtx(hw, 0, mt7996_remove_iter, &rdata); mt76_vif_cleanup(&dev->mt76, vif); @@ -811,15 +899,24 @@ mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, for_each_vif_active_link(vif, link_conf, link_id) { struct mt7996_vif_link *link; + struct mt7996_phy *phy; link = mt7996_vif_link(dev, vif, link_id); if (!link) continue; - if (!link->phy) + if (vif->type == NL80211_IFTYPE_STATION) { + link->mt76.beacon_mon_interval = + msecs_to_jiffies(ieee80211_tu_to_usec( + link_conf->beacon_int) / 1000); + WRITE_ONCE(link->mt76.beacon_mon_last, jiffies); + } + + phy = mt7996_vif_link_phy(link); + if (!phy) continue; - mt7996_mcu_add_bss_info(link->phy, vif, link_conf, + mt7996_mcu_add_bss_info(phy, vif, link_conf, &link->mt76, &link->msta_link, true); mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, @@ -828,6 +925,20 @@ mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } } + if ((changed & BSS_CHANGED_ASSOC) && !vif->cfg.assoc && + vif->type == NL80211_IFTYPE_STATION) { + struct ieee80211_bss_conf *link_conf; + unsigned long link_id; + + for_each_vif_active_link(vif, link_conf, link_id) { + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (link) + link->mt76.beacon_mon_interval = 0; + } + } + mutex_unlock(&dev->mt76.mutex); } @@ -864,6 +975,10 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, !!(changed & BSS_CHANGED_BSSID)); } + if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) + mt7996_mcu_set_protection(phy, link, info->ht_operation_mode, + info->use_cts_prot); + if (changed & BSS_CHANGED_ERP_SLOT) { int slottime = info->use_short_slot ? 9 : 20; @@ -927,12 +1042,84 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw, struct cfg80211_chan_def *chandef) { struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_band_phy(dev, chandef->chan->band); + struct ieee80211_bss_conf *link_conf; + unsigned int link_id; mutex_lock(&dev->mt76.mutex); - mt7996_mcu_add_beacon(hw, vif, &vif->bss_conf, vif->bss_conf.enable_beacon); + + for_each_vif_active_link(vif, link_conf, link_id) { + struct mt7996_vif_link *link; + struct mt7996_phy *link_phy; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + link_phy = mt7996_vif_link_phy(link); + if (link_phy != phy) + continue; + + /* Reset beacon when channel switch triggered during CAC to let + * FW correctly perform CSA countdown + */ + if (!cfg80211_reg_can_beacon(hw->wiphy, &phy->mt76->chandef, + vif->type)) + mt7996_mcu_add_beacon(hw, vif, link_conf, false); + + mt7996_mcu_add_beacon(hw, vif, link_conf, true); + break; + } + mutex_unlock(&dev->mt76.mutex); } +static int +mt7996_post_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct cfg80211_chan_def *chandef = &link_conf->chanreq.oper; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_band_phy(dev, chandef->chan->band); + struct mt7996_vif_link *link; + int ret = -EINVAL; + + mutex_lock(&dev->mt76.mutex); + + link = mt7996_vif_conf_link(dev, vif, link_conf); + if (!link) + goto out; + + ret = mt7996_mcu_update_bss_rfch(phy, link); + if (ret) + goto out; + + ieee80211_iterate_stations_mtx(hw, mt7996_mcu_update_sta_rec_bw, link); + + ret = mt7996_mcu_rdd_resume_tx(phy); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7996_sta_init_txq_wcid(struct ieee80211_sta *sta, int idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { + struct mt76_txq *mtxq; + + if (!sta->txq[i]) + continue; + + mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv; + mtxq->wcid = idx; + } +} + static int mt7996_mac_sta_init_link(struct mt7996_dev *dev, struct ieee80211_bss_conf *link_conf, @@ -941,30 +1128,22 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev, { struct ieee80211_sta *sta = link_sta->sta; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_phy *phy = link->phy; + struct mt7996_phy *phy = mt7996_vif_link_phy(link); struct mt7996_sta_link *msta_link; int idx; + if (!phy) + return -EINVAL; + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); if (idx < 0) return -ENOSPC; if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) { - int i; - msta_link = &msta->deflink; msta->deflink_id = link_id; msta->seclink_id = msta->deflink_id; - - for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { - struct mt76_txq *mtxq; - - if (!sta->txq[i]) - continue; - - mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv; - mtxq->wcid = idx; - } + mt7996_sta_init_txq_wcid(sta, idx); } else { msta_link = kzalloc_obj(*msta_link); if (!msta_link) @@ -1000,9 +1179,17 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev, return 0; } -void mt7996_mac_sta_deinit_link(struct mt7996_dev *dev, - struct mt7996_sta_link *msta_link) +void mt7996_mac_sta_remove_link(struct mt7996_dev *dev, + struct ieee80211_sta *sta, + unsigned int link_id, bool flush) { + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + return; + spin_lock_bh(&dev->mt76.sta_poll_lock); if (!list_empty(&msta_link->wcid.poll_list)) list_del_init(&msta_link->wcid.poll_list); @@ -1011,49 +1198,59 @@ void mt7996_mac_sta_deinit_link(struct mt7996_dev *dev, spin_unlock_bh(&dev->mt76.sta_poll_lock); mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid); - mt76_wcid_mask_clear(dev->mt76.wcid_mask, msta_link->wcid.idx); -} -static void -mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, unsigned long links) -{ - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt76_dev *mdev = &dev->mt76; - unsigned int link_id; - - for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { - struct mt7996_sta_link *msta_link = NULL; - struct mt7996_vif_link *link; - struct mt76_phy *mphy; - - msta_link = rcu_replace_pointer(msta->link[link_id], msta_link, - lockdep_is_held(&mdev->mutex)); - if (!msta_link) - continue; + if (msta_link->wcid.link_valid) { + struct mt7996_phy *phy; mt7996_mac_wtbl_update(dev, msta_link->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); - mt7996_mac_sta_deinit_link(dev, msta_link); - link = mt7996_vif_link(dev, vif, link_id); - if (!link) - continue; + phy = __mt7996_phy(dev, msta_link->wcid.phy_idx); + if (phy) + phy->mt76->num_sta--; - mphy = mt76_vif_link_phy(&link->mt76); - if (!mphy) - continue; - - mphy->num_sta--; if (msta->deflink_id == link_id) { msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; - continue; - } else if (msta->seclink_id == link_id) { - msta->seclink_id = IEEE80211_LINK_UNSPECIFIED; - } + if (msta->seclink_id == link_id) { + /* no secondary link available */ + msta->seclink_id = msta->deflink_id; + } else { + struct mt7996_sta_link *msta_seclink; - kfree_rcu(msta_link, rcu_head); + /* switch to the secondary link */ + msta_seclink = mt76_dereference( + msta->link[msta->seclink_id], + &dev->mt76); + if (msta_seclink) { + msta->deflink_id = msta->seclink_id; + mt7996_sta_init_txq_wcid(sta, + msta_seclink->wcid.idx); + } + } + } else if (msta->seclink_id == link_id) { + msta->seclink_id = msta->deflink_id; + } + msta_link->wcid.link_valid = false; } + + if (flush) { + rcu_assign_pointer(msta->link[link_id], NULL); + rcu_assign_pointer(dev->mt76.wcid[msta_link->wcid.idx], NULL); + mt76_wcid_mask_clear(dev->mt76.wcid_mask, msta_link->wcid.idx); + if (msta_link != &msta->deflink) + kfree_rcu(msta_link, rcu_head); + } +} + +static void +mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, unsigned long links, + bool flush) +{ + unsigned int link_id; + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) + mt7996_mac_sta_remove_link(dev, sta, link_id, flush); } static int @@ -1067,11 +1264,15 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_bss_conf *link_conf; struct ieee80211_link_sta *link_sta; + struct mt7996_sta_link *msta_link; struct mt7996_vif_link *link; struct mt76_phy *mphy; - if (rcu_access_pointer(msta->link[link_id])) + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (msta_link) { + msta_link->wcid.link_valid = true; continue; + } link_conf = link_conf_dereference_protected(vif, link_id); if (!link_conf) { @@ -1108,7 +1309,7 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, return 0; error_unlink: - mt7996_mac_sta_remove_links(dev, vif, sta, new_links); + mt7996_mac_sta_remove_links(dev, vif, sta, new_links, true); return err; } @@ -1125,7 +1326,7 @@ mt7996_mac_sta_change_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mutex_lock(&dev->mt76.mutex); - mt7996_mac_sta_remove_links(dev, vif, sta, rem); + mt7996_mac_sta_remove_links(dev, vif, sta, rem, false); ret = mt7996_mac_sta_add_links(dev, vif, sta, add); mutex_unlock(&dev->mt76.mutex); @@ -1234,10 +1435,12 @@ static void mt7996_mac_sta_remove(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { - unsigned long links = sta->valid_links ? sta->valid_links : BIT(0); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + int i; mutex_lock(&dev->mt76.mutex); - mt7996_mac_sta_remove_links(dev, vif, sta, links); + for (i = 0; i < ARRAY_SIZE(msta->link); i++) + mt7996_mac_sta_remove_link(dev, sta, i, true); mutex_unlock(&dev->mt76.mutex); } @@ -1489,8 +1692,8 @@ mt7996_get_stats(struct ieee80211_hw *hw, u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link) { + struct mt7996_phy *phy = mt7996_vif_link_phy(link); struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt7996_phy *phy = link->phy; union { u64 t64; u32 t32[2]; @@ -1549,7 +1752,7 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : link->mt76.omac_idx; - phy = link->phy; + phy = mt7996_vif_link_phy(link); if (!phy) goto unlock; @@ -1583,7 +1786,7 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!link) goto unlock; - phy = link->phy; + phy = mt7996_vif_link_phy(link); if (!phy) goto unlock; @@ -1713,9 +1916,14 @@ static void mt7996_link_rate_ctrl_update(void *data, struct mt7996_sta_link *msta_link) { struct mt7996_sta *msta = msta_link->sta; - struct mt7996_dev *dev = msta->vif->deflink.phy->dev; + struct mt7996_phy *phy = mt7996_vif_link_phy(&msta->vif->deflink); + struct mt7996_dev *dev; u32 *changed = data; + if (!phy) + return; + + dev = phy->dev; spin_lock_bh(&dev->mt76.sta_poll_lock); msta_link->changed |= *changed; @@ -2204,6 +2412,10 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, path->mtk_wdma.wdma_idx = wed->wdma_idx; else #endif + if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76) && + msta_link->wcid.phy_idx == MT_BAND2) + path->mtk_wdma.wdma_idx = 1; + else path->mtk_wdma.wdma_idx = link->mt76.band_idx; path->mtk_wdma.bss = link->mt76.idx; path->mtk_wdma.queue = 0; @@ -2275,6 +2487,21 @@ mt7996_reconfig_complete(struct ieee80211_hw *hw, MT7996_WATCHDOG_TIME); } +static int +mt7996_set_eml_op_mode(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_eml_params *eml_params) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + int ret; + + mutex_lock(&dev->mt76.mutex); + ret = mt7996_mcu_set_emlsr_mode(dev, vif, sta, eml_params); + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + const struct ieee80211_ops mt7996_ops = { .add_chanctx = mt76_add_chanctx, .remove_chanctx = mt76_remove_chanctx, @@ -2306,6 +2533,7 @@ const struct ieee80211_ops mt7996_ops = { .release_buffered_frames = mt76_release_buffered_frames, .get_txpower = mt7996_get_txpower, .channel_switch_beacon = mt7996_channel_switch_beacon, + .post_channel_switch = mt7996_post_channel_switch, .get_stats = mt7996_get_stats, .get_et_sset_count = mt7996_get_et_sset_count, .get_et_stats = mt7996_get_et_stats, @@ -2337,4 +2565,5 @@ const struct ieee80211_ops mt7996_ops = { .change_vif_links = mt7996_change_vif_links, .change_sta_links = mt7996_mac_sta_change_links, .reconfig_complete = mt7996_reconfig_complete, + .set_eml_op_mode = mt7996_set_eml_op_mode, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index b4422a4754cd..16420375112d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -18,6 +18,9 @@ case MT7992_VAR_TYPE_23: \ _fw = MT7992_##name##_23; \ break; \ + case MT7992_VAR_TYPE_24: \ + _fw = MT7992_##name##_24; \ + break; \ default: \ _fw = MT7992_##name; \ } \ @@ -125,9 +128,16 @@ mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta, struct mt7996_vif_link *link, __le16 *he_mcs, u16 mcs_map) { + struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76); int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; - enum nl80211_band band = link->phy->mt76->chandef.chan->band; - const u16 *mask = link->bitrate_mask.control[band].he_mcs; + enum nl80211_band band; + const u16 *mask; + + if (!mphy) + return; + + band = mphy->chandef.chan->band; + mask = link->bitrate_mask.control[band].he_mcs; for (nss = 0; nss < max_nss; nss++) { int mcs; @@ -209,6 +219,7 @@ static int mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd, struct sk_buff *skb, int seq) { + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt7996_mcu_rxd *rxd; struct mt7996_mcu_uni_event *event; int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); @@ -217,6 +228,14 @@ mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd, if (!skb) { dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", cmd, seq); + + if (!test_and_set_bit(MT76_MCU_RESET, &dev->mphy.state)) { + dev->recovery.restart = true; + wake_up(&dev->mt76.mcu.wait); + queue_work(dev->mt76.wq, &dev->reset_work); + wake_up(&dev->reset_wait); + } + return -ETIMEDOUT; } @@ -233,7 +252,7 @@ mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd, event = (struct mt7996_mcu_uni_event *)skb->data; ret = le32_to_cpu(event->status); /* skip invalid event */ - if (mcu_cmd != event->cid) + if (mcu_cmd != le16_to_cpu(event->cid)) ret = -EAGAIN; } else { skb_pull(skb, sizeof(struct mt7996_mcu_rxd)); @@ -259,7 +278,8 @@ mt7996_mcu_set_timeout(struct mt76_dev *mdev, int cmd) mdev->mcu.timeout = 2 * HZ; return; case MCU_UNI_CMD_EFUSE_CTRL: - mdev->mcu.timeout = 20 * HZ; + case MCU_UNI_CMD_EXT_EEPROM_CTRL: + mdev->mcu.timeout = 30 * HZ; return; default: break; @@ -313,13 +333,12 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, uni_txd->pkt_type = MCU_PKT_ID; uni_txd->seq = seq; - if (cmd & __MCU_CMD_FIELD_QUERY) - uni_txd->option = MCU_CMD_UNI_QUERY_ACK; - else - uni_txd->option = MCU_CMD_UNI_EXT_ACK; + uni_txd->option = MCU_CMD_UNI; + if (!(cmd & __MCU_CMD_FIELD_QUERY)) + uni_txd->option |= MCU_CMD_SET; - if (mcu_cmd == MCU_UNI_CMD_SDO) - uni_txd->option &= ~MCU_CMD_ACK; + if (wait_seq) + uni_txd->option |= MCU_CMD_ACK; if ((cmd & __MCU_CMD_FIELD_WA) && (cmd & __MCU_CMD_FIELD_WM)) uni_txd->s2d_index = MCU_S2D_H2CN; @@ -390,13 +409,117 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3) sizeof(req), false); } +struct mt7996_mcu_countdown_data { + struct mt76_phy *mphy; + u8 omac_idx; +}; + static void mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) { - if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION) + struct mt7996_mcu_countdown_data *cdata = (void *)priv; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct ieee80211_bss_conf *link_conf = NULL; + unsigned long valid_links = vif->valid_links ?: BIT(0); + unsigned int link_id; + + if (vif->type == NL80211_IFTYPE_STATION) return; - ieee80211_csa_finish(vif, 0); + for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt76_vif_link *mlink = + rcu_dereference(mvif->mt76.link[link_id]); + + if (mlink && mlink->band_idx == cdata->mphy->band_idx && + mlink->omac_idx == cdata->omac_idx) { + link_conf = rcu_dereference(vif->link_conf[link_id]); + break; + } + } + + if (!link_conf || !link_conf->csa_active) + return; + + ieee80211_csa_finish(vif, link_conf->link_id); +} + +static void +mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt7996_mcu_countdown_data *cdata = (void *)priv; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct ieee80211_bss_conf *link_conf = NULL; + unsigned long valid_links = vif->valid_links ?: BIT(0); + unsigned int link_id; + + if (vif->type == NL80211_IFTYPE_STATION) + return; + + for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt76_vif_link *mlink = + rcu_dereference(mvif->mt76.link[link_id]); + + if (mlink && mlink->band_idx == cdata->mphy->band_idx && + mlink->omac_idx == cdata->omac_idx) { + link_conf = rcu_dereference(vif->link_conf[link_id]); + break; + } + } + + if (!link_conf || !link_conf->color_change_active) + return; + + ieee80211_color_change_finish(vif, link_conf->link_id); +} + +static void +mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb) +{ +#define UNI_EVENT_IE_COUNTDOWN_CSA 0 +#define UNI_EVENT_IE_COUNTDOWN_BCC 1 + struct header { + u8 band; + u8 rsv[3]; + }; + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + const char *data = (char *)&rxd[1], *tail; + struct header *hdr = (struct header *)data; + struct tlv *tlv = (struct tlv *)(data + 4); + struct mt7996_mcu_countdown_notify *event; + struct mt7996_mcu_countdown_data cdata; + + if (hdr->band >= ARRAY_SIZE(dev->mt76.phys)) + return; + + cdata.mphy = dev->mt76.phys[hdr->band]; + if (!cdata.mphy) + return; + + tail = skb->data + skb->len; + data += sizeof(*hdr); + while (data + sizeof(*tlv) < tail && le16_to_cpu(tlv->len)) { + event = (struct mt7996_mcu_countdown_notify *)tlv->data; + + cdata.omac_idx = event->omac_idx; + + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_IE_COUNTDOWN_CSA: + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_csa_finish, &cdata); + break; + case UNI_EVENT_IE_COUNTDOWN_BCC: + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_cca_finish, &cdata); + break; + default: + break; + } + + data += le16_to_cpu(tlv->len); + tlv = (struct tlv *)data; + } } static void @@ -416,24 +539,32 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb) break; case MT_RDD_IDX_BACKGROUND: if (!dev->rdd2_phy) - return; + goto err; mphy = dev->rdd2_phy->mt76; break; default: - dev_err(dev->mt76.dev, "Unknown RDD idx %d\n", r->rdd_idx); - return; + goto err; } if (!mphy) - return; + goto err; - if (r->rdd_idx == MT_RDD_IDX_BACKGROUND) + if (r->rdd_idx == MT_RDD_IDX_BACKGROUND) { cfg80211_background_radar_event(mphy->hw->wiphy, &dev->rdd2_chandef, GFP_ATOMIC); - else + } else { + struct mt7996_phy *phy = mphy->priv; + + phy->rdd_tx_paused = true; ieee80211_radar_detected(mphy->hw, NULL); + } dev->hw_pattern++; + + return; + +err: + dev_err(dev->mt76.dev, "Invalid RDD idx %d\n", r->rdd_idx); } static void @@ -476,57 +607,6 @@ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb) wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data); } -static void -mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) -{ - if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION) - return; - - ieee80211_color_change_finish(vif, 0); -} - -static void -mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb) -{ -#define UNI_EVENT_IE_COUNTDOWN_CSA 0 -#define UNI_EVENT_IE_COUNTDOWN_BCC 1 - struct header { - u8 band; - u8 rsv[3]; - }; - struct mt76_phy *mphy = &dev->mt76.phy; - struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; - const char *data = (char *)&rxd[1], *tail; - struct header *hdr = (struct header *)data; - struct tlv *tlv = (struct tlv *)(data + 4); - - if (hdr->band >= ARRAY_SIZE(dev->mt76.phys)) - return; - - if (hdr->band && dev->mt76.phys[hdr->band]) - mphy = dev->mt76.phys[hdr->band]; - - tail = skb->data + skb->len; - data += sizeof(struct header); - while (data + sizeof(struct tlv) < tail && le16_to_cpu(tlv->len)) { - switch (le16_to_cpu(tlv->tag)) { - case UNI_EVENT_IE_COUNTDOWN_CSA: - ieee80211_iterate_active_interfaces_atomic(mphy->hw, - IEEE80211_IFACE_ITER_RESUME_ALL, - mt7996_mcu_csa_finish, mphy->hw); - break; - case UNI_EVENT_IE_COUNTDOWN_BCC: - ieee80211_iterate_active_interfaces_atomic(mphy->hw, - IEEE80211_IFACE_ITER_RESUME_ALL, - mt7996_mcu_cca_finish, mphy->hw); - break; - } - - data += le16_to_cpu(tlv->len); - tlv = (struct tlv *)data; - } -} - static int mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rate) { @@ -1170,6 +1250,123 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); } +int mt7996_mcu_update_bss_rfch(struct mt7996_phy *phy, struct mt7996_vif_link *link) +{ + struct mt7996_dev *dev = phy->dev; + struct sk_buff *skb; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7996_mcu_bss_rfch_tlv(skb, phy); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *link, + u8 ht_mode, bool use_cts_prot) +{ + struct mt7996_dev *dev = phy->dev; + struct bss_prot_tlv *prot; + struct sk_buff *skb; + struct tlv *tlv; + enum { + PROT_NONMEMBER = BIT(1), + PROT_20MHZ = BIT(2), + PROT_NONHT_MIXED = BIT(3), + PROT_LEGACY_ERP = BIT(5), + PROT_NONGF_STA = BIT(7), + }; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_PROTECT_INFO, + sizeof(*prot)); + prot = (struct bss_prot_tlv *)tlv; + + switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + prot->prot_mode = cpu_to_le32(PROT_NONMEMBER); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + prot->prot_mode = cpu_to_le32(PROT_20MHZ); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + prot->prot_mode = cpu_to_le32(PROT_NONHT_MIXED); + break; + } + + if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + prot->prot_mode |= cpu_to_le32(PROT_NONGF_STA); + + if (use_cts_prot) + prot->prot_mode |= cpu_to_le32(PROT_LEGACY_ERP); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_set_emlsr_mode(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_eml_params *eml_params) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link; + struct sta_rec_eml_op *eml_op; + struct mt7996_vif_link *link; + struct sk_buff *skb; + struct tlv *tlv; + + msta_link = mt76_dereference(msta->link[eml_params->link_id], + &dev->mt76); + if (!msta_link) + return -EINVAL; + + link = mt7996_vif_link(dev, vif, eml_params->link_id); + if (!link) + return -EINVAL; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EML_OP, sizeof(*eml_op)); + eml_op = (struct sta_rec_eml_op *)tlv; + eml_op->link_bitmap = 0; + + if (eml_params->control & IEEE80211_EML_CTRL_EMLSR_MODE) { + unsigned long link_bitmap = eml_params->link_bitmap; + unsigned int link_id; + + for_each_set_bit(link_id, &link_bitmap, + IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt76_phy *mphy; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + mphy = mt76_vif_link_phy(&link->mt76); + if (!mphy) + continue; + + eml_op->link_bitmap |= BIT(mphy->band_idx); + } + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf) { @@ -1779,9 +1976,8 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, #define EBF_MODE BIT(0) #define IBF_MODE BIT(1) #define BF_MAT_ORDER 4 + struct mt7996_phy *phy = mt7996_vif_link_phy(link); struct ieee80211_vif *vif = link_conf->vif; - struct mt7996_phy *phy = link->phy; - int tx_ant = hweight16(phy->mt76->chainmask) - 1; struct sta_rec_bf *bf; struct tlv *tlv; static const u8 matrix[BF_MAT_ORDER][BF_MAT_ORDER] = { @@ -1790,8 +1986,12 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, {2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */ {3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */ }; + int tx_ant; bool ebf; + if (!phy) + return; + if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he)) return; @@ -1807,17 +2007,18 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, * ht: iBF only, since mac80211 lacks of eBF support */ if (link_sta->eht_cap.has_eht) - mt7996_mcu_sta_bfer_eht(link_sta, vif, link->phy, bf, ebf); + mt7996_mcu_sta_bfer_eht(link_sta, vif, phy, bf, ebf); else if (link_sta->he_cap.has_he) - mt7996_mcu_sta_bfer_he(link_sta, vif, link->phy, bf, ebf); + mt7996_mcu_sta_bfer_he(link_sta, vif, phy, bf, ebf); else if (link_sta->vht_cap.vht_supported) - mt7996_mcu_sta_bfer_vht(link_sta, link->phy, bf, ebf); + mt7996_mcu_sta_bfer_vht(link_sta, phy, bf, ebf); else if (link_sta->ht_cap.ht_supported) - mt7996_mcu_sta_bfer_ht(link_sta, link->phy, bf, ebf); + mt7996_mcu_sta_bfer_ht(link_sta, phy, bf, ebf); else return; bf->bf_cap = ebf ? EBF_MODE : (dev->ibf ? IBF_MODE : 0); + tx_ant = hweight16(phy->mt76->chainmask) - 1; if (is_mt7992(&dev->mt76) && tx_ant == 4) bf->bf_cap |= IBF_MODE; @@ -1849,11 +2050,14 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_link_sta *link_sta, struct mt7996_vif_link *link) { - struct mt7996_phy *phy = link->phy; - int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + struct mt7996_phy *phy = mt7996_vif_link_phy(link); struct sta_rec_bfee *bfee; struct tlv *tlv; u8 nrow = 0; + int tx_ant; + + if (!phy) + return; if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he)) return; @@ -1877,6 +2081,7 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb, } /* reply with identity matrix to avoid 2x2 BF negative gain */ + tx_ant = hweight8(phy->mt76->antenna_mask) - 1; bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2); } @@ -2060,6 +2265,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta, struct ieee80211_sta *sta; int ret, nrates = 0, idx; enum nl80211_band band; + struct mt76_phy *mphy; bool has_he; #define __sta_phy_bitrate_mask_check(_mcs, _gi, _ht, _he) \ @@ -2093,7 +2299,11 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta, if (!link_sta) goto error_unlock; - band = link->phy->mt76->chandef.chan->band; + mphy = mt76_vif_link_phy(&link->mt76); + if (!mphy) + goto error_unlock; + + band = mphy->chandef.chan->band; has_he = link_sta->he_cap.has_he; mask = link->bitrate_mask; idx = msta_link->wcid.idx; @@ -2173,18 +2383,25 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, struct mt7996_vif_link *link) { #define INIT_RCPI 180 - struct mt76_phy *mphy = link->phy->mt76; - struct cfg80211_chan_def *chandef = &mphy->chandef; + struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76); struct cfg80211_bitrate_mask *mask = &link->bitrate_mask; u32 cap = link_sta->sta->wme ? STA_CAP_WMM : 0; - enum nl80211_band band = chandef->chan->band; + struct cfg80211_chan_def *chandef; struct sta_rec_ra_uni *ra; + enum nl80211_band band; struct tlv *tlv; - u32 supp_rate = link_sta->supp_rates[band]; + u32 supp_rate; + + if (!mphy) + return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); ra = (struct sta_rec_ra_uni *)tlv; + chandef = &mphy->chandef; + band = chandef->chan->band; + supp_rate = link_sta->supp_rates[band]; + ra->valid = true; ra->auto_rate = true; ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, link_sta); @@ -2366,6 +2583,72 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct mt7996_vif_link *link, sizeof(req), true); } +int mt7996_mcu_mld_reconf_stop_link(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + u16 removed_links) +{ + unsigned long rem_links = removed_links; + struct mld_reconf_stop_link *sl; + struct mld_req_hdr hdr = {}; + unsigned int link_id; + struct sk_buff *skb; + struct tlv *tlv; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(hdr) + sizeof(*sl)); + if (!skb) + return -ENOMEM; + + memcpy(hdr.mld_addr, vif->addr, ETH_ALEN); + skb_put_data(skb, &hdr, sizeof(hdr)); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_CMD_MLD_RECONF_STOP_LINK, + sizeof(*sl)); + sl = (struct mld_reconf_stop_link *)tlv; + sl->link_bitmap = cpu_to_le16(removed_links); + + for_each_set_bit(link_id, &rem_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + sl->bss_idx[link_id] = link->mt76.idx; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(MLD), + true); +} + +int mt7996_mcu_mld_link_oper(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, bool add) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct bss_mld_link_op_tlv *mld_op; + struct sk_buff *skb; + struct tlv *tlv; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD_LINK_OP, + sizeof(*mld_op)); + mld_op = (struct bss_mld_link_op_tlv *)tlv; + mld_op->link_operation = add; + mld_op->own_mld_id = link->mld_idx; + mld_op->link_id = link_conf->link_id; + mld_op->group_mld_id = add ? mvif->mld_group_idx : 0xff; + mld_op->remap_idx = add ? mvif->mld_remap_idx : 0xff; + memcpy(mld_op->mac_addr, vif->addr, ETH_ALEN); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + static void mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, @@ -2529,6 +2812,53 @@ int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } +void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta) +{ + struct mt7996_vif_link *link = (struct mt7996_vif_link *)data; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_phy *phy = mt7996_vif_link_phy(link); + struct mt7996_sta_link *msta_link; + struct mt7996_dev *dev; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct ieee80211_vif *vif; + struct sk_buff *skb; + int link_id; + + if (!phy) + return; + + if (link->mt76.mvif != &msta->vif->mt76) + return; + + dev = phy->dev; + link_id = link->msta_link.wcid.link_id; + link_sta = link_sta_dereference_protected(sta, link_id); + if (!link_sta) + return; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + return; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + link_conf = link_conf_dereference_protected(vif, link_id); + if (!link_conf) + return; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return; + + mt7996_mcu_sta_bfer_tlv(dev, skb, link_conf, link_sta, link); + mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, link_conf, link_sta, link); + + mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + static int mt7996_mcu_sta_key_tlv(struct mt76_dev *dev, struct mt76_wcid *wcid, struct sk_buff *skb, @@ -2676,6 +3006,16 @@ mt7996_mcu_beacon_cntdwn(struct sk_buff *rskb, struct sk_buff *skb, info = (struct bss_bcn_cntdwn_tlv *)tlv; info->cnt = skb->data[offs->cntdwn_counter_offs[0]]; + + /* abort the CCA countdown when starting CSA countdown */ + if (csa) { + struct bss_bcn_cntdwn_tlv *cca_info; + + tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_BCC, + sizeof(*cca_info)); + cca_info = (struct bss_bcn_cntdwn_tlv *)tlv; + cca_info->cca.abort = true; + } } static void @@ -2768,6 +3108,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_vif_link *link = mt7996_vif_conf_link(dev, vif, link_conf); + struct mt76_phy *mphy = link ? mt76_vif_link_phy(&link->mt76) : NULL; struct mt76_vif_link *mlink = link ? &link->mt76 : NULL; struct ieee80211_mutable_offsets offs; struct ieee80211_tx_info *info; @@ -2782,7 +3123,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!mlink) return -EINVAL; - if (link->phy && link->phy->mt76->offchannel) + if (mphy && mphy->offchannel) enabled = false; rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, @@ -2833,9 +3174,9 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, { #define OFFLOAD_TX_MODE_SU BIT(0) #define OFFLOAD_TX_MODE_MU BIT(1) + struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76); struct ieee80211_vif *vif = link_conf->vif; struct ieee80211_hw *hw = mt76_hw(dev); - struct mt7996_phy *phy = link->phy; struct mt76_wcid *wcid = &dev->mt76.global_wcid; struct bss_inband_discovery_tlv *discov; struct ieee80211_tx_info *info; @@ -2846,10 +3187,10 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, u8 *buf, interval; int len; - if (!phy) + if (!mphy) return -EINVAL; - chandef = &phy->mt76->chandef; + chandef = &mphy->chandef; band = chandef->chan->band; if (link_conf->nontransmitted) @@ -2887,7 +3228,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, info = IEEE80211_SKB_CB(skb); info->control.vif = vif; info->band = band; - info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx); + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, mphy->band_idx); len = ALIGN(sizeof(*discov) + MT_TXD_SIZE + skb->len, 4); tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, len); @@ -3742,8 +4083,7 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag) if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR) req.switch_reason = CH_SWITCH_NORMAL; - else if (phy->mt76->offchannel || - phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE) + else if (phy->mt76->offchannel || !phy->mt76->chanctx) req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, NL80211_IFTYPE_AP)) @@ -3764,23 +4104,173 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag) &req, sizeof(req), true); } -static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev) +static int +mt7996_mcu_get_cal_free_data(struct mt7996_dev *dev) +{ +#define MT_EE_7977BN_OFFSET (0x1200 - 0x500) + struct cal_free_data { + u16 adie_offs; + u16 eep_offs; + }; + static const struct cal_free_data cal_7975[] = { + { 0x5cd, 0x451 }, { 0x5cf, 0x453 }, { 0x5d1, 0x455 }, + { 0x5d3, 0x457 }, { 0x6c0, 0x44c }, { 0x6c1, 0x44d }, + { 0x6c2, 0x44e }, { 0x6c3, 0x44f }, { 0x7a1, 0xba1 }, + { 0x7a6, 0xba6 }, { 0x7a8, 0xba8 }, { 0x7aa, 0xbaa }, + }; + static const struct cal_free_data cal_7976[] = { + { 0x4c, 0x44c }, { 0x4d, 0x44d }, { 0x4e, 0x44e }, + { 0x4f, 0x44f }, { 0x50, 0x450 }, { 0x51, 0x451 }, + { 0x53, 0x453 }, { 0x55, 0x455 }, { 0x57, 0x457 }, + { 0x59, 0x459 }, { 0x70, 0x470 }, { 0x71, 0x471 }, + { 0x790, 0xb90 }, { 0x791, 0xb91 }, { 0x794, 0xb94 }, + { 0x795, 0xb95 }, { 0x7a6, 0xba6 }, { 0x7a8, 0xba8 }, + { 0x7aa, 0xbaa }, + }; + static const struct cal_free_data cal_7977[] = { + { 0x4c, 0x124c }, { 0x4d, 0x124d }, { 0x4e, 0x124e }, + { 0x4f, 0x124f }, { 0x50, 0x1250 }, { 0x51, 0x1251 }, + { 0x53, 0x1253 }, { 0x55, 0x1255 }, { 0x57, 0x1257 }, + { 0x59, 0x1259 }, { 0x69, 0x1269 }, { 0x6a, 0x126a }, + { 0x7a, 0x127a }, { 0x7b, 0x127b }, { 0x7c, 0x127c }, + { 0x7d, 0x127d }, { 0x7e, 0x127e }, + }; + static const struct cal_free_data cal_7978[] = { + { 0x91, 0xb91 }, { 0x95, 0xb95 }, { 0x100, 0x480 }, + { 0x102, 0x482 }, { 0x104, 0x484 }, { 0x106, 0x486 }, + { 0x107, 0x487 }, { 0x108, 0x488 }, { 0x109, 0x489 }, + { 0x10a, 0x48a }, { 0x10b, 0x48b }, { 0x10c, 0x48c }, + { 0x10e, 0x48e }, { 0x110, 0x490 }, + }; + static const struct cal_free_data cal_7979[] = { + { 0x4c, 0x124c }, { 0x4d, 0x124d }, { 0x4e, 0x124e }, + { 0x4f, 0x124f }, { 0x50, 0x1250 }, { 0x51, 0x1251 }, + { 0x53, 0x1253 }, { 0x55, 0x1255 }, { 0x57, 0x1257 }, + { 0x59, 0x1259 }, { 0x69, 0x1269 }, { 0x6a, 0x126a }, + { 0x7a, 0x127a }, { 0x7b, 0x127b }, { 0x7c, 0x127c }, + { 0x7e, 0x127e }, { 0x80, 0x1280 }, + }; + const struct cal_free_data *cal_arr[__MT_MAX_BAND]; + u16 cal_arr_len[__MT_MAX_BAND] = {}; + u8 *eeprom = (u8 *)dev->mt76.eeprom.data; + int band, i, ret; + +#define CAL_ARR(_band, _adie) do { \ + cal_arr[_band] = cal_##_adie; \ + cal_arr_len[_band] = ARRAY_SIZE(cal_##_adie); \ + } while (0) + + switch (mt76_chip(&dev->mt76)) { + case MT7996_DEVICE_ID: + /* adie 0 */ + if (dev->var.fem == MT7996_FEM_INT && + dev->var.type != MT7996_VAR_TYPE_233) + CAL_ARR(0, 7975); + else + CAL_ARR(0, 7976); + + /* adie 1 */ + if (dev->var.type == MT7996_VAR_TYPE_444) + CAL_ARR(1, 7977); + + /* adie 2 */ + CAL_ARR(2, 7977); + break; + case MT7992_DEVICE_ID: + /* adie 0 */ + if (dev->var.type == MT7992_VAR_TYPE_44 && + dev->var.fem != MT7996_FEM_EXT) + CAL_ARR(0, 7975); + else if (dev->var.type == MT7992_VAR_TYPE_24) + CAL_ARR(0, 7978); + else + CAL_ARR(0, 7976); + + /* adie 1 */ + if (dev->var.type == MT7992_VAR_TYPE_44 && + dev->var.fem != MT7996_FEM_INT) + CAL_ARR(1, 7977); + else if (dev->var.type != MT7992_VAR_TYPE_23) + CAL_ARR(1, 7979); + break; + case MT7990_DEVICE_ID: + /* adie 0 */ + CAL_ARR(0, 7976); + break; + default: + return -EINVAL; + } + + for (band = 0; band < __MT_MAX_BAND; band++) { + u8 buf[MT7996_EEPROM_BLOCK_SIZE]; + const struct cal_free_data *cal; + u16 prev_block_idx = -1; + u16 adie_base; + + if (!cal_arr_len[band]) + continue; + + if (band == MT_BAND0) + adie_base = MT7996_EFUSE_BASE_OFFS_ADIE0; + else if (band == MT_BAND1 && is_mt7992(&dev->mt76)) + adie_base = MT7992_EFUSE_BASE_OFFS_ADIE1; + else if (band == MT_BAND1) + adie_base = MT7996_EFUSE_BASE_OFFS_ADIE1; + else + adie_base = MT7996_EFUSE_BASE_OFFS_ADIE2; + + cal = cal_arr[band]; + for (i = 0; i < cal_arr_len[band]; i++) { + u16 adie_offset = cal[i].adie_offs + adie_base; + u16 eep_offset = cal[i].eep_offs; + u16 block_idx = adie_offset / MT7996_EEPROM_BLOCK_SIZE; + u16 offset = adie_offset % MT7996_EEPROM_BLOCK_SIZE; + + if (is_mt7996(&dev->mt76) && band == MT_BAND1 && + dev->var.type == MT7996_VAR_TYPE_444) + eep_offset -= MT_EE_7977BN_OFFSET; + + if (prev_block_idx != block_idx) { + memset(buf, 0, sizeof(buf)); + ret = mt7996_mcu_get_eeprom(dev, adie_offset, buf, + MT7996_EEPROM_BLOCK_SIZE, + EEPROM_MODE_EFUSE); + if (ret) { + if (ret != -EINVAL) + return ret; + prev_block_idx = -1; + continue; + } + } + eeprom[eep_offset] = buf[offset]; + prev_block_idx = block_idx; + } + } + + return 0; +} + +int mt7996_mcu_set_eeprom(struct mt7996_dev *dev) { #define MAX_PAGE_IDX_MASK GENMASK(7, 5) #define PAGE_IDX_MASK GENMASK(4, 2) #define PER_PAGE_SIZE 0x400 - struct mt7996_mcu_eeprom req = { + struct mt7996_mcu_eeprom_update req = { .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), .buffer_mode = EE_MODE_BUFFER }; u16 eeprom_size = MT7996_EEPROM_SIZE; u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE); u8 *eep = (u8 *)dev->mt76.eeprom.data; - int eep_len, i; + int ret, eep_len, i; + + ret = mt7996_mcu_get_cal_free_data(dev); + if (ret) + return ret; for (i = 0; i < total; i++, eep += eep_len) { struct sk_buff *skb; - int ret, msg_len; + int msg_len; if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE)) eep_len = eeprom_size % PER_PAGE_SIZE; @@ -3809,59 +4299,66 @@ static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev) return 0; } -int mt7996_mcu_set_eeprom(struct mt7996_dev *dev) +int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len, + enum mt7996_eeprom_mode mode) { - struct mt7996_mcu_eeprom req = { - .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), - .len = cpu_to_le16(sizeof(req) - 4), - .buffer_mode = EE_MODE_EFUSE, - .format = EE_FORMAT_WHOLE - }; - - if (dev->flash_mode) - return mt7996_mcu_set_eeprom_flash(dev); - - return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL), - &req, sizeof(req), true); -} - -int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len) -{ - struct { - u8 _rsv[4]; - - __le16 tag; - __le16 len; - __le32 addr; - __le32 valid; - u8 data[16]; - } __packed req = { - .tag = cpu_to_le16(UNI_EFUSE_ACCESS), - .len = cpu_to_le16(sizeof(req) - 4), - .addr = cpu_to_le32(round_down(offset, - MT7996_EEPROM_BLOCK_SIZE)), + struct mt7996_mcu_eeprom_access req = { + .info.len = cpu_to_le16(sizeof(req) - 4), }; + struct mt7996_mcu_eeprom_access_event *event; struct sk_buff *skb; - bool valid; - int ret; + int ret, cmd; + u32 addr; - ret = mt76_mcu_send_and_get_msg(&dev->mt76, - MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), - &req, sizeof(req), true, &skb); + switch (mode) { + case EEPROM_MODE_EFUSE: + addr = round_down(offset, MT7996_EEPROM_BLOCK_SIZE); + cmd = MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL); + req.info.tag = cpu_to_le16(UNI_EFUSE_ACCESS); + break; + case EEPROM_MODE_EXT: + addr = round_down(offset, MT7996_EXT_EEPROM_BLOCK_SIZE); + cmd = MCU_WM_UNI_CMD_QUERY(EXT_EEPROM_CTRL); + req.info.tag = cpu_to_le16(UNI_EXT_EEPROM_ACCESS); + req.eeprom.ext_eeprom.data_len = cpu_to_le32(buf_len); + break; + default: + return -EINVAL; + } + + req.info.addr = cpu_to_le32(addr); + ret = mt76_mcu_send_and_get_msg(&dev->mt76, cmd, &req, sizeof(req), + true, &skb); if (ret) return ret; - valid = le32_to_cpu(*(__le32 *)(skb->data + 16)); - if (valid) { - u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12)); + event = (struct mt7996_mcu_eeprom_access_event *)skb->data; + if (event->valid) { + u32 ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len); + + addr = le32_to_cpu(event->addr); if (!buf) buf = (u8 *)dev->mt76.eeprom.data + addr; - if (!buf_len || buf_len > MT7996_EEPROM_BLOCK_SIZE) - buf_len = MT7996_EEPROM_BLOCK_SIZE; - skb_pull(skb, 48); - memcpy(buf, skb->data, buf_len); + switch (mode) { + case EEPROM_MODE_EFUSE: + if (!buf_len || buf_len > MT7996_EEPROM_BLOCK_SIZE) + buf_len = MT7996_EEPROM_BLOCK_SIZE; + + memcpy(buf, event->eeprom.efuse, buf_len); + break; + case EEPROM_MODE_EXT: + if (!buf_len || buf_len > MT7996_EXT_EEPROM_BLOCK_SIZE) + buf_len = MT7996_EXT_EEPROM_BLOCK_SIZE; + + memcpy(buf, event->eeprom.ext_eeprom.data, + ret_len < buf_len ? ret_len : buf_len); + break; + default: + ret = -EINVAL; + break; + } } else { ret = -EINVAL; } @@ -3871,7 +4368,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l return ret; } -int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num) +int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num) { struct { u8 _rsv[4]; @@ -3903,7 +4400,6 @@ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num) int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap) { -#define NIC_CAP 3 #define UNI_EVENT_CHIP_CONFIG_EFUSE_VERSION 0x21 struct { u8 _rsv[4]; @@ -3911,7 +4407,7 @@ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap) __le16 tag; __le16 len; } __packed req = { - .tag = cpu_to_le16(NIC_CAP), + .tag = cpu_to_le16(UNI_CHIP_CONFIG_NIC_CAPA), .len = cpu_to_le16(sizeof(req) - 4), }; struct sk_buff *skb; @@ -4559,6 +5055,35 @@ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable) &req, sizeof(req), true); } +int mt7996_mcu_rdd_resume_tx(struct mt7996_phy *phy) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + u8 mac_enable; + u8 _rsv2[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_MAC_ENABLE_CTRL), + .len = cpu_to_le16(sizeof(req) - 4), + .mac_enable = 2, + }; + int ret; + + if (!phy->rdd_tx_paused) + return 0; + + ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); + if (!ret) + phy->rdd_tx_paused = false; + + return ret; +} + int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val) { struct { @@ -4889,3 +5414,25 @@ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode) return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(CP_SUPPORT), &cp_mode, sizeof(cp_mode), true); } + +int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev) +{ +#define DUP_WTBL_NUM 80 + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le16 base; + __le16 num; + u8 _rsv2[4]; + } __packed req = { + .tag = cpu_to_le16(UNI_CHIP_CONFIG_DUP_WTBL), + .len = cpu_to_le16(sizeof(req) - 4), + .base = cpu_to_le16(MT7996_WTBL_STA - DUP_WTBL_NUM + 1), + .num = cpu_to_le16(DUP_WTBL_NUM), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req, + sizeof(req), true); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index e0b83ac9f5e2..8902e16508b7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -25,8 +25,8 @@ struct mt7996_mcu_rxd { }; struct mt7996_mcu_uni_event { - u8 cid; - u8 __rsv[3]; + __le16 cid; + u8 __rsv[2]; __le32 status; /* 0: success, others: fail */ } __packed; @@ -52,12 +52,10 @@ struct mt7996_mcu_thermal_enable { u8 rsv[2]; } __packed; -struct mt7996_mcu_csa_notify { - struct mt7996_mcu_rxd rxd; - +struct mt7996_mcu_countdown_notify { u8 omac_idx; - u8 csa_count; - u8 band_idx; + u8 count; + u8 csa_failure_reason; /* 0: success, 1: beacon disabled */ u8 rsv; } __packed; @@ -147,7 +145,7 @@ struct mt7996_mcu_background_chain_ctrl { u8 rsv[2]; } __packed; -struct mt7996_mcu_eeprom { +struct mt7996_mcu_eeprom_update { u8 _rsv[4]; __le16 tag; @@ -157,6 +155,43 @@ struct mt7996_mcu_eeprom { __le16 buf_len; } __packed; +union eeprom_data { + struct { + __le32 data_len; + DECLARE_FLEX_ARRAY(u8, data); + } ext_eeprom; + DECLARE_FLEX_ARRAY(u8, efuse); +} __packed; + +struct mt7996_mcu_eeprom_info { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 addr; + __le32 valid; +} __packed; + +struct mt7996_mcu_eeprom_access { + struct mt7996_mcu_eeprom_info info; + union eeprom_data eeprom; +} __packed; + +struct mt7996_mcu_eeprom_access_event { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 version; + __le32 addr; + __le32 valid; + __le32 size; + __le32 magic_no; + __le32 type; + __le32 rsv[4]; + union eeprom_data eeprom; +} __packed; + struct mt7996_mcu_phy_rx_info { u8 category; u8 rate; @@ -414,7 +449,16 @@ struct bss_bcn_cntdwn_tlv { __le16 tag; __le16 len; u8 cnt; - u8 rsv[3]; + union { + struct { + bool static_pp; + bool abort; + } csa; + struct { + bool abort; + } cca; + }; + u8 rsv; } __packed; struct bss_bcn_mbss_tlv { @@ -474,6 +518,24 @@ struct bss_mld_tlv { u8 __rsv[2]; } __packed; +struct bss_prot_tlv { + __le16 tag; + __le16 len; + __le32 prot_mode; +} __packed; + +struct bss_mld_link_op_tlv { + __le16 tag; + __le16 len; + u8 group_mld_id; + u8 own_mld_id; + u8 mac_addr[ETH_ALEN]; + u8 remap_idx; + u8 link_operation; + u8 link_id; + u8 rsv[2]; +} __packed; + struct sta_rec_ht_uni { __le16 tag; __le16 len; @@ -647,6 +709,28 @@ struct mld_setup_link { u8 __rsv; } __packed; +struct mld_req_hdr { + u8 ver; + u8 mld_addr[ETH_ALEN]; + u8 mld_idx; + u8 flag; + u8 rsv[3]; + u8 buf[]; +} __packed; + +struct mld_reconf_stop_link { + __le16 tag; + __le16 len; + __le16 link_bitmap; + u8 rsv[2]; + u8 bss_idx[16]; +} __packed; + +enum { + UNI_CMD_MLD_RECONF_AP_REM_TIMER = 0x03, + UNI_CMD_MLD_RECONF_STOP_LINK = 0x04, +}; + struct hdr_trans_en { __le16 tag; __le16 len; @@ -791,6 +875,11 @@ enum { UNI_CHANNEL_RX_PATH, }; +enum { + UNI_CHIP_CONFIG_NIC_CAPA = 3, + UNI_CHIP_CONFIG_DUP_WTBL = 4, +}; + #define MT7996_BSS_UPDATE_MAX_SIZE (sizeof(struct bss_req_hdr) + \ sizeof(struct mt76_connac_bss_basic_tlv) + \ sizeof(struct bss_rlm_tlv) + \ @@ -837,6 +926,7 @@ enum { enum { UNI_BAND_CONFIG_RADIO_ENABLE, UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08, + UNI_BAND_CONFIG_MAC_ENABLE_CTRL = 0x0c, }; enum { @@ -856,6 +946,10 @@ enum { UNI_EFUSE_BUFFER_RD, }; +enum { + UNI_EXT_EEPROM_ACCESS = 1, +}; + enum { UNI_VOW_DRR_CTRL, UNI_VOW_RX_AT_AIRTIME_EN = 0x0b, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 7a884311800e..bdcf72457954 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -29,6 +29,10 @@ #define MT7996_RX_RING_SIZE 1536 #define MT7996_RX_MCU_RING_SIZE 512 #define MT7996_RX_MCU_RING_SIZE_WA 1024 +#define MT7996_NPU_TX_RING_SIZE 1024 +#define MT7996_NPU_RX_RING_SIZE 1024 +#define MT7996_NPU_TXD_SIZE 3 + /* scatter-gather of mcu event is not supported in connac3 */ #define MT7996_RX_MCU_BUF_SIZE (2048 + \ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) @@ -60,6 +64,11 @@ #define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin" #define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin" +#define MT7992_FIRMWARE_WA_24 "mediatek/mt7996/mt7992_wa_24.bin" +#define MT7992_FIRMWARE_WM_24 "mediatek/mt7996/mt7992_wm_24.bin" +#define MT7992_FIRMWARE_DSP_24 "mediatek/mt7996/mt7992_dsp_24.bin" +#define MT7992_ROM_PATCH_24 "mediatek/mt7996/mt7992_rom_patch_24.bin" + #define MT7990_FIRMWARE_WA "" #define MT7990_FIRMWARE_WM "mediatek/mt7996/mt7990_wm.bin" #define MT7990_FIRMWARE_DSP "" @@ -75,12 +84,14 @@ #define MT7992_EEPROM_DEFAULT_MIX "mediatek/mt7996/mt7992_eeprom_2i5e.bin" #define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23.bin" #define MT7992_EEPROM_DEFAULT_23_INT "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin" +#define MT7992_EEPROM_DEFAULT_24 "mediatek/mt7996/mt7992_eeprom_24_2i5i.bin" #define MT7990_EEPROM_DEFAULT "mediatek/mt7996/mt7990_eeprom.bin" #define MT7990_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7990_eeprom_2i5i.bin" #define MT7996_EEPROM_SIZE 7680 #define MT7996_EEPROM_BLOCK_SIZE 16 +#define MT7996_EXT_EEPROM_BLOCK_SIZE 1024 #define MT7996_TOKEN_SIZE 16384 #define MT7996_HW_TOKEN_SIZE 8192 @@ -153,6 +164,7 @@ enum mt7996_var_type { enum mt7992_var_type { MT7992_VAR_TYPE_44, MT7992_VAR_TYPE_23, + MT7992_VAR_TYPE_24, }; enum mt7990_var_type { @@ -165,6 +177,18 @@ enum mt7996_fem_type { MT7996_FEM_MIX, }; +enum mt7996_eeprom_mode { + EEPROM_MODE_DEFAULT_BIN, + EEPROM_MODE_EFUSE, + EEPROM_MODE_FLASH, + EEPROM_MODE_EXT, +}; + +#define MT7996_EFUSE_BASE_OFFS_ADIE0 0x400 +#define MT7996_EFUSE_BASE_OFFS_ADIE1 0x1e00 +#define MT7996_EFUSE_BASE_OFFS_ADIE2 0x1200 +#define MT7992_EFUSE_BASE_OFFS_ADIE1 0x1200 + enum mt7996_txq_id { MT7996_TXQ_FWDL = 16, MT7996_TXQ_MCU_WM, @@ -252,8 +276,6 @@ struct mt7996_vif_link { struct mt76_vif_link mt76; /* must be first */ struct mt7996_sta_link msta_link; - struct mt7996_phy *phy; - struct cfg80211_bitrate_mask bitrate_mask; u8 mld_idx; @@ -377,6 +399,7 @@ struct mt7996_phy { bool has_aux_rx; bool counter_reset; + bool rdd_tx_paused; }; struct mt7996_dev { @@ -436,7 +459,7 @@ struct mt7996_dev { u32 hw_pattern; - bool flash_mode:1; + u8 eeprom_mode; bool has_eht:1; struct { @@ -473,6 +496,8 @@ struct mt7996_dev { struct list_head page_map[MT7996_RRO_MSDU_PG_HASH_SIZE]; } wed_rro; + dma_addr_t npu_txd_addr[2 * MT7996_NPU_TXD_SIZE]; + bool ibf; u8 fw_debug_wm; u8 fw_debug_wa; @@ -669,6 +694,8 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct mt76_vif_link *mlink, struct mt7996_sta_link *msta_link, int enable); +int mt7996_mcu_update_bss_rfch(struct mt7996_phy *phy, + struct mt7996_vif_link *link); int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, @@ -678,6 +705,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link); +void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta); int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, struct ieee80211_vif *vif, bool enable); @@ -707,8 +735,9 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta, void *data, u8 link_id, u32 field); int mt7996_mcu_set_eeprom(struct mt7996_dev *dev); -int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len); -int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num); +int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len, + enum mt7996_eeprom_mode mode); +int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num); int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap); int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band); int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action); @@ -719,6 +748,8 @@ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index, const struct mt7996_dfs_pattern *pattern); int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable); int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val); +int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *link, + u8 ht_mode, bool use_cts_prot); int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf); int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch); @@ -726,6 +757,7 @@ int mt7996_mcu_get_temperature(struct mt7996_phy *phy); int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state); int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable); int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy); +int mt7996_mcu_rdd_resume_tx(struct mt7996_phy *phy); int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val); int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef); @@ -743,6 +775,13 @@ void mt7996_mcu_exit(struct mt7996_dev *dev); int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag); int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id); int mt7996_mcu_set_sniffer_mode(struct mt7996_phy *phy, bool enabled); +int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev); +int mt7996_mcu_mld_reconf_stop_link(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + u16 removed_links); +int mt7996_mcu_mld_link_oper(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, bool add); static inline bool mt7996_has_hwrro(struct mt7996_dev *dev) { @@ -802,6 +841,11 @@ static inline bool mt7996_has_wa(struct mt7996_dev *dev) return !is_mt7990(&dev->mt76); } +static inline bool mt7996_has_ext_eeprom(struct mt7996_dev *dev) +{ + return !is_mt7996(&dev->mt76); +} + void mt7996_mac_init(struct mt7996_dev *dev); u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw); bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); @@ -823,8 +867,9 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link, u8 flowid); -void mt7996_mac_sta_deinit_link(struct mt7996_dev *dev, - struct mt7996_sta_link *msta_link); +void mt7996_mac_sta_remove_link(struct mt7996_dev *dev, + struct ieee80211_sta *sta, + unsigned int link_id, bool flush); void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct ieee80211_twt_setup *twt); @@ -861,6 +906,10 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link); int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode); +int mt7996_mcu_set_emlsr_mode(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_eml_params *eml_params); #ifdef CONFIG_MAC80211_DEBUGFS void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); @@ -877,12 +926,19 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir); #endif int mt7996_dma_rro_init(struct mt7996_dev *dev); +void mt7996_dma_rro_start(struct mt7996_dev *dev); #ifdef CONFIG_MT7996_NPU +int __mt7996_npu_hw_init(struct mt7996_dev *dev); int mt7996_npu_hw_init(struct mt7996_dev *dev); int mt7996_npu_hw_stop(struct mt7996_dev *dev); int mt7996_npu_rx_queues_init(struct mt7996_dev *dev); #else +static inline int __mt7996_npu_hw_init(struct mt7996_dev *dev) +{ + return 0; +} + static inline int mt7996_npu_hw_init(struct mt7996_dev *dev) { return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/npu.c b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c index 29bb735da4cb..b8006b8729a1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/npu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c @@ -8,55 +8,20 @@ #include "mt7996.h" -static int mt7996_npu_offload_init(struct mt7996_dev *dev, - struct airoha_npu *npu) +static int mt7992_npu_txrx_offload_init(struct mt7996_dev *dev, + struct airoha_npu *npu) { + u32 hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0; phys_addr_t phy_addr = dev->mt76.mmio.phy_addr; - u32 val, hif1_ofs = 0, dma_addr; + u32 dma_addr; int i, err; - err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_NPU_VERSION, - &val, GFP_KERNEL); - if (err) { - dev_warn(dev->mt76.dev, "failed getting NPU fw version\n"); - return err; - } - - dev_info(dev->mt76.dev, "NPU version: %0d.%d\n", - (val >> 16) & 0xffff, val & 0xffff); - - err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE, - dev->mt76.mmio.npu_type, GFP_KERNEL); - if (err) { - dev_warn(dev->mt76.dev, - "failed setting NPU wlan PCIe port type\n"); - return err; - } - - if (dev->hif2) - hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); - for (i = MT_BAND0; i < MT_BAND2; i++) { - dma_addr = phy_addr; - if (i) - dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + 0x90 + - hif1_ofs; - else - dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND0) + 0x80; - - err = mt76_npu_send_msg(npu, i, WLAN_FUNC_SET_WAIT_PCIE_ADDR, - dma_addr, GFP_KERNEL); - if (err) { - dev_warn(dev->mt76.dev, - "failed setting NPU wlan PCIe desc addr\n"); - return err; - } - err = mt76_npu_send_msg(npu, i, WLAN_FUNC_SET_WAIT_DESC, MT7996_RX_RING_SIZE, GFP_KERNEL); if (err) { dev_warn(dev->mt76.dev, - "failed setting NPU wlan PCIe desc size\n"); + "failed setting NPU wlan rx desc size\n"); return err; } @@ -97,10 +62,173 @@ static int mt7996_npu_offload_init(struct mt7996_dev *dev, phy_addr + MT_RRO_ACK_SN_CTRL, GFP_KERNEL); if (err) { dev_warn(dev->mt76.dev, - "failed setting NPU wlan rro_ack_sn desc addr\n"); + "failed setting NPU wlan tx desc addr\n"); return err; } + return 0; +} + +static int mt7996_npu_txrx_offload_init(struct mt7996_dev *dev, + struct airoha_npu *npu) +{ + u32 hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0; + phys_addr_t phy_addr = dev->mt76.mmio.phy_addr; + u32 dma_addr; + int err; + + /* npu rx rro ring0 */ + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_DESC, + MT7996_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* npu rx rro ring1 */ + err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_DESC, + MT7996_NPU_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* msdu pg 2GHz */ + dma_addr = phy_addr + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND0) + 0xa0; + err = mt76_npu_send_msg(npu, 5, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 5, WLAN_FUNC_SET_WAIT_DESC, + MT7996_NPU_RX_RING_SIZE / 4, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* msdu pg 5GHz */ + dma_addr = phy_addr + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND1) + 0xb0; + err = mt76_npu_send_msg(npu, 6, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 6, WLAN_FUNC_SET_WAIT_DESC, + MT7996_NPU_RX_RING_SIZE / 2, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* msdu pg 6GHz */ + dma_addr = phy_addr + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND2) + 0xc0; + err = mt76_npu_send_msg(npu, 7, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 7, WLAN_FUNC_SET_WAIT_DESC, + MT7996_NPU_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* ind cmd ring */ + err = mt76_npu_send_msg(npu, 8, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + phy_addr + MT_RXQ_RRO_IND_RING_BASE, + GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 8, WLAN_FUNC_SET_WAIT_DESC, + MT7996_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 3, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, + phy_addr + MT_RRO_ACK_SN_CTRL, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx desc addr\n"); + return err; + } + + /* npu tx */ + dma_addr = phy_addr + MT_TXQ_RING_BASE(1) + 0x120; + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx desc addr\n"); + return err; + } + + dma_addr = phy_addr + MT_TXQ_RING_BASE(0) + 0x150 + hif1_ofs; + err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx desc addr\n"); + return err; + } + + return 0; +} + +static int mt7996_npu_offload_init(struct mt7996_dev *dev, + struct airoha_npu *npu) +{ + u32 val; + int err; + + err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_NPU_VERSION, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, "failed getting NPU fw version\n"); + return err; + } + + dev_info(dev->mt76.dev, "NPU version: %0d.%d\n", + (val >> 16) & 0xffff, val & 0xffff); + + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE, + dev->mt76.mmio.npu_type, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe port type\n"); + return err; + } + + if (is_mt7996(&dev->mt76)) + err = mt7996_npu_txrx_offload_init(dev, npu); + else + err = mt7992_npu_txrx_offload_init(dev, npu); + + if (err) + return err; + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, MT7996_HW_TOKEN_SIZE, GFP_KERNEL); if (err) @@ -111,6 +239,41 @@ static int mt7996_npu_offload_init(struct mt7996_dev *dev, return 0; } +static int mt7992_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu) +{ + u32 val; + int err; + + err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retrieving NPU wlan rx ring0 addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0].regs->desc_base); + + err = mt76_npu_get_msg(npu, 1, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retrieving NPU wlan rx ring1 addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1].regs->desc_base); + + err = mt76_npu_get_msg(npu, 9, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retrieving NPU wlan rxdmad_c ring addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_RXDMAD_C].regs->desc_base); + + return 0; +} + static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu) { u32 val; @@ -125,80 +288,107 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu) } writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0].regs->desc_base); - err = mt76_npu_get_msg(npu, 1, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + err = mt76_npu_get_msg(npu, 2, WLAN_FUNC_GET_WAIT_RXDESC_BASE, &val, GFP_KERNEL); if (err) { dev_warn(dev->mt76.dev, - "failed retriving NPU wlan rx ring1 addr\n"); + "failed retriving NPU wlan rx ring2 addr\n"); return err; } - writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1].regs->desc_base); + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2].regs->desc_base); - err = mt76_npu_get_msg(npu, 9, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + /* msdu pg ring */ + err = mt76_npu_get_msg(npu, 10, WLAN_FUNC_GET_WAIT_RXDESC_BASE, &val, GFP_KERNEL); if (err) { dev_warn(dev->mt76.dev, - "failed retriving NPU wlan rxdmad_c ring addr\n"); + "failed retriving NPU wlan msdu pg ring addr\n"); return err; } - writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_RXDMAD_C].regs->desc_base); + writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0].regs->desc_base); + + err = mt76_npu_get_msg(npu, 11, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retriving NPU wlan msdu pg ring addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1].regs->desc_base); + + err = mt76_npu_get_msg(npu, 12, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retriving NPU wlan msdu pg ring addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2].regs->desc_base); + + /* ind_cmd ring */ + err = mt76_npu_get_msg(npu, 8, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retriving NPU wlan ind_cmd ring addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_IND].regs->desc_base); return 0; } static int mt7996_npu_txd_init(struct mt7996_dev *dev, struct airoha_npu *npu) { - int i, err; + const enum mt76_band_id band_list[] = { + MT_BAND0, + is_mt7996(&dev->mt76) ? MT_BAND2 : MT_BAND1, + }; + int i, index = 0; - for (i = MT_BAND0; i < MT_BAND2; i++) { - dma_addr_t dma_addr; + BUILD_BUG_ON(ARRAY_SIZE(band_list) * 3 != + ARRAY_SIZE(dev->npu_txd_addr)); + + for (i = 0; i < ARRAY_SIZE(band_list); i++) { + int err, band = band_list[i], phy_id; u32 val; - err = mt76_npu_get_msg(npu, i + 5, + err = mt76_npu_get_msg(npu, band + 5, WLAN_FUNC_GET_WAIT_RXDESC_BASE, &val, GFP_KERNEL); if (err) { dev_warn(dev->mt76.dev, - "failed retriving NPU wlan tx ring addr\n"); + "failed retrieving NPU wlan tx ring addr\n"); return err; } - writel(val, &dev->mt76.phys[i]->q_tx[0]->regs->desc_base); - if (!dmam_alloc_coherent(dev->mt76.dma_dev, - 256 * MT7996_TX_RING_SIZE, - &dma_addr, GFP_KERNEL)) - return -ENOMEM; + phy_id = is_mt7996(&dev->mt76) ? band == MT_BAND0 ? 1 : 0 + : band; + writel(val, &dev->mt76.phys[phy_id]->q_tx[0]->regs->desc_base); - err = mt76_npu_send_msg(npu, i, + err = mt76_npu_send_msg(npu, band, WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, - dma_addr, GFP_KERNEL); + dev->npu_txd_addr[index++], GFP_KERNEL); if (err) { dev_warn(dev->mt76.dev, "failed setting NPU wlan queue buf addr\n"); return err; } - if (!dmam_alloc_coherent(dev->mt76.dma_dev, - 256 * MT7996_TX_RING_SIZE, - &dma_addr, GFP_KERNEL)) - return -ENOMEM; - - err = mt76_npu_send_msg(npu, i + 5, + err = mt76_npu_send_msg(npu, band + 5, WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, - dma_addr, GFP_KERNEL); + dev->npu_txd_addr[index++], + GFP_KERNEL); if (err) { dev_warn(dev->mt76.dev, "failed setting NPU wlan tx buf addr\n"); return err; } - if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * 1024, - &dma_addr, GFP_KERNEL)) - return -ENOMEM; - - err = mt76_npu_send_msg(npu, i + 10, + err = mt76_npu_send_msg(npu, band + 10, WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, - dma_addr, GFP_KERNEL); + dev->npu_txd_addr[index++], + GFP_KERNEL); if (err) { dev_warn(dev->mt76.dev, "failed setting NPU wlan tx buf base\n"); @@ -212,8 +402,9 @@ static int mt7996_npu_txd_init(struct mt7996_dev *dev, struct airoha_npu *npu) static int mt7996_npu_rx_event_init(struct mt7996_dev *dev, struct airoha_npu *npu) { - struct mt76_queue *q = &dev->mt76.q_rx[MT_RXQ_MAIN_WA]; + int qid = is_mt7996(&dev->mt76) ? MT_RXQ_TXFREE_BAND0 : MT_RXQ_MAIN_WA; phys_addr_t phy_addr = dev->mt76.mmio.phy_addr; + struct mt76_queue *q = &dev->mt76.q_rx[qid]; int err; err = mt76_npu_send_msg(npu, 0, @@ -233,7 +424,8 @@ static int mt7996_npu_rx_event_init(struct mt7996_dev *dev, return err; } - phy_addr += MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA) + 0x20; + phy_addr += MT_RXQ_RING_BASE(qid); + phy_addr += is_mt7996(&dev->mt76) ? 0x90 : 0x20; err = mt76_npu_send_msg(npu, 10, WLAN_FUNC_SET_WAIT_PCIE_ADDR, phy_addr, GFP_KERNEL); if (err) @@ -242,11 +434,54 @@ static int mt7996_npu_rx_event_init(struct mt7996_dev *dev, return err; } +static int mt7996_npu_set_pcie_addr(struct mt7996_dev *dev, + struct airoha_npu *npu) +{ + u32 hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0; + dma_addr_t dma_addr = dev->mt76.mmio.phy_addr; + int err; + + dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND0) + 0x80; + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + dma_addr = dev->mt76.mmio.phy_addr + hif1_ofs; + if (is_mt7996(&dev->mt76)) { + dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND2) + 0x60; + err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + } else { + dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + 0x90; + err = mt76_npu_send_msg(npu, 1, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + } + + if (err) + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + + return err; +} + static int mt7996_npu_tx_done_init(struct mt7996_dev *dev, struct airoha_npu *npu) { int err; + /* rro ring cpu idx */ + err = mt76_npu_send_msg(npu, 15, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + 0, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, 0, GFP_KERNEL); if (err) { @@ -278,40 +513,79 @@ int mt7996_npu_rx_queues_init(struct mt7996_dev *dev) &dev->mt76.q_rx[MT_RXQ_NPU1]); } -int mt7996_npu_hw_init(struct mt7996_dev *dev) +int __mt7996_npu_hw_init(struct mt7996_dev *dev) { struct airoha_npu *npu; - int i, err = 0; - - mutex_lock(&dev->mt76.mutex); + int i, err; npu = rcu_dereference_protected(dev->mt76.mmio.npu, &dev->mt76.mutex); if (!npu) - goto unlock; + return 0; err = mt7996_npu_offload_init(dev, npu); if (err) - goto unlock; + return err; + + if (is_mt7996(&dev->mt76)) + err = mt7996_npu_rxd_init(dev, npu); + else + err = mt7992_npu_rxd_init(dev, npu); - err = mt7996_npu_rxd_init(dev, npu); if (err) - goto unlock; + return err; err = mt7996_npu_txd_init(dev, npu); if (err) - goto unlock; + return err; err = mt7996_npu_rx_event_init(dev, npu); if (err) - goto unlock; + return err; + + err = mt7996_npu_set_pcie_addr(dev, npu); + if (err) + return err; err = mt7996_npu_tx_done_init(dev, npu); if (err) - goto unlock; + return err; for (i = MT_RXQ_NPU0; i <= MT_RXQ_NPU1; i++) airoha_npu_wlan_enable_irq(npu, i - MT_RXQ_NPU0); -unlock: + + return 0; +} + +int mt7996_npu_hw_init(struct mt7996_dev *dev) +{ + int i, err; + + BUILD_BUG_ON(ARRAY_SIZE(dev->npu_txd_addr) % 3); + + for (i = 0; i < ARRAY_SIZE(dev->npu_txd_addr); i += 3) { + int band = i && is_mt7996(&dev->mt76) ? MT_BAND2 : MT_BAND0; + u32 size = is_mt7996(&dev->mt76) ? band == MT_BAND2 + ? MT7996_NPU_TX_RING_SIZE + : MT7996_NPU_RX_RING_SIZE / 2 + : MT7996_TX_RING_SIZE; + + if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * size, + &dev->npu_txd_addr[i], GFP_KERNEL)) + return -ENOMEM; + + if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * size, + &dev->npu_txd_addr[i + 1], + GFP_KERNEL)) + return -ENOMEM; + + if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * 1024, + &dev->npu_txd_addr[i + 2], + GFP_KERNEL)) + return -ENOMEM; + } + + mutex_lock(&dev->mt76.mutex); + err = __mt7996_npu_hw_init(dev); mutex_unlock(&dev->mt76.mutex); return err; @@ -320,33 +594,38 @@ int mt7996_npu_hw_init(struct mt7996_dev *dev) int mt7996_npu_hw_stop(struct mt7996_dev *dev) { struct airoha_npu *npu; - int i, err; + int i, err = 0; u32 info; + mutex_lock(&dev->mt76.mutex); + npu = rcu_dereference_protected(dev->mt76.mmio.npu, &dev->mt76.mutex); if (!npu) - return 0; + goto unlock; err = mt76_npu_send_msg(npu, 4, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, 0, GFP_KERNEL); if (err) - return err; + goto unlock; for (i = 0; i < 10; i++) { err = mt76_npu_get_msg(npu, 3, WLAN_FUNC_GET_WAIT_NPU_INFO, &info, GFP_KERNEL); - if (err) - continue; + if (!err && !info) + break; - if (info) { - err = -ETIMEDOUT; - continue; - } + err = -ETIMEDOUT; + usleep_range(10000, 15000); } if (!err) err = mt76_npu_send_msg(npu, 6, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, 0, GFP_KERNEL); + else + dev_err(dev->mt76.dev, "npu stop failed\n"); +unlock: + mutex_unlock(&dev->mt76.mutex); + return err; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h index e48e0e575b64..c6379933b6c3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h @@ -159,6 +159,9 @@ enum offs_rev { #define MT_MDP_BASE 0x820cc000 #define MT_MDP(ofs) (MT_MDP_BASE + (ofs)) +#define MT_MDP_DCR0 MT_MDP(0x800) +#define MT_MDP_DCR0_RX_HDR_TRANS_EN BIT(19) + #define MT_MDP_DCR2 MT_MDP(0x8e8) #define MT_MDP_DCR2_RX_TRANS_SHORT BIT(2) @@ -733,7 +736,15 @@ enum offs_rev { #define MT_HW_REV 0x70010204 #define MT_HW_REV1 0x8a00 +#define MT_WF_L05_RST 0x70028550 +#define MT_WF_L05_RST_WF_RST_MASK GENMASK(4, 0) + #define MT_WF_SUBSYS_RST 0x70028600 +#define MT_WF_SUBSYS_RST_WHOLE_PATH_RST BIT(0) +#define MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT BIT(5) +#define MT_WF_SUBSYS_RST_BYPASS_WFDMA_SLP_PROT BIT(6) +#define MT_WF_SUBSYS_RST_BYPASS_WFDMA2_SLP_PROT BIT(16) +#define MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT_CYCLE GENMASK(15, 8) /* PCIE MAC */ #define MT_PCIE_MAC_BASE 0x74030000 diff --git a/drivers/net/wireless/mediatek/mt76/npu.c b/drivers/net/wireless/mediatek/mt76/npu.c index ec36975f6dc9..c4c7c0af6321 100644 --- a/drivers/net/wireless/mediatek/mt76/npu.c +++ b/drivers/net/wireless/mediatek/mt76/npu.c @@ -390,6 +390,36 @@ int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } EXPORT_SYMBOL_GPL(mt76_npu_net_setup_tc); +int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex, + u32 direction, u32 i_count_addr, + u32 o_status_addr, u32 o_count_addr) +{ + struct { + __le32 dir; + __le32 in_count_addr; + __le32 out_status_addr; + __le32 out_count_addr; + } info = { + .dir = cpu_to_le32(direction), + .in_count_addr = cpu_to_le32(i_count_addr), + .out_status_addr = cpu_to_le32(o_status_addr), + .out_count_addr = cpu_to_le32(o_count_addr), + }; + struct airoha_npu *npu; + int err = -ENODEV; + + rcu_read_lock(); + npu = rcu_dereference(dev->mmio.npu); + if (npu) + err = airoha_npu_wlan_send_msg(npu, ifindex, + WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, + &info, sizeof(info), GFP_ATOMIC); + rcu_read_unlock(); + + return err; +} +EXPORT_SYMBOL_GPL(mt76_npu_send_txrx_addr); + void mt76_npu_disable_irqs(struct mt76_dev *dev) { struct airoha_npu *npu; @@ -420,10 +450,6 @@ int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr, int type) struct airoha_npu *npu; int err = 0; - /* NPU offloading is only supported by MT7992 */ - if (!is_mt7992(dev)) - return 0; - mutex_lock(&dev->mutex); npu = airoha_npu_get(dev->dev); @@ -456,7 +482,8 @@ int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr, int type) dev->mmio.phy_addr = phy_addr; dev->mmio.npu_type = type; /* NPU offloading requires HW-RRO for RX packet reordering. */ - dev->hwrro_mode = MT76_HWRRO_V3_1; + dev->hwrro_mode = is_mt7996(dev) ? MT76_HWRRO_V3 : MT76_HWRRO_V3_1; + dev->rx_token_size = 32768; rcu_assign_pointer(dev->mmio.npu, npu); rcu_assign_pointer(dev->mmio.ppe_dev, ppe_dev); diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c index 63b0447e55c1..7fe1b1fbb699 100644 --- a/drivers/net/wireless/mediatek/mt76/scan.c +++ b/drivers/net/wireless/mediatek/mt76/scan.c @@ -16,9 +16,11 @@ static void mt76_scan_complete(struct mt76_dev *dev, bool abort) clear_bit(MT76_SCANNING, &phy->state); - if (dev->scan.chan && phy->main_chandef.chan && - !test_bit(MT76_MCU_RESET, &dev->phy.state)) + if (dev->scan.chan && phy->main_chandef.chan && phy->offchannel && + !test_bit(MT76_MCU_RESET, &dev->phy.state)) { mt76_set_channel(phy, &phy->main_chandef, false); + mt76_offchannel_notify(phy, false); + } mt76_put_vif_phy_link(phy, dev->scan.vif, dev->scan.mlink); memset(&dev->scan, 0, sizeof(dev->scan)); if (!test_bit(MT76_MCU_RESET, &dev->phy.state)) @@ -27,6 +29,10 @@ static void mt76_scan_complete(struct mt76_dev *dev, bool abort) void mt76_abort_scan(struct mt76_dev *dev) { + spin_lock_bh(&dev->scan_lock); + dev->scan.beacon_wait = false; + spin_unlock_bh(&dev->scan_lock); + cancel_delayed_work_sync(&dev->scan_work); mt76_scan_complete(dev, true); } @@ -77,6 +83,27 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid) rcu_read_unlock(); } +void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan) +{ + struct mt76_phy *phy; + + spin_lock(&dev->scan_lock); + + if (!dev->scan.beacon_wait || dev->scan.beacon_received || + dev->scan.chan != chan) + goto out; + + phy = dev->scan.phy; + if (!phy) + goto out; + + dev->scan.beacon_received = true; + ieee80211_queue_delayed_work(phy->hw, &dev->scan_work, 0); + +out: + spin_unlock(&dev->scan_lock); +} + void mt76_scan_work(struct work_struct *work) { struct mt76_dev *dev = container_of(work, struct mt76_dev, @@ -85,35 +112,60 @@ void mt76_scan_work(struct work_struct *work) struct cfg80211_chan_def chandef = {}; struct mt76_phy *phy = dev->scan.phy; int duration = HZ / 9; /* ~110 ms */ + bool beacon_rx, offchannel = true; int i; + if (!phy || !req) + return; + + spin_lock_bh(&dev->scan_lock); + beacon_rx = dev->scan.beacon_wait && dev->scan.beacon_received; + dev->scan.beacon_wait = false; + spin_unlock_bh(&dev->scan_lock); + + if (beacon_rx) + goto probe; + if (dev->scan.chan_idx >= req->n_channels) { mt76_scan_complete(dev, false); return; } - if (dev->scan.chan && phy->num_sta) { + if (dev->scan.chan && phy->num_sta && phy->offchannel) { dev->scan.chan = NULL; mt76_set_channel(phy, &phy->main_chandef, false); + mt76_offchannel_notify(phy, false); goto out; } dev->scan.chan = req->channels[dev->scan.chan_idx++]; - cfg80211_chandef_create(&chandef, dev->scan.chan, NL80211_CHAN_HT20); - mt76_set_channel(phy, &chandef, true); + offchannel = mt76_offchannel_chandef(phy, dev->scan.chan, &chandef); - if (!req->n_ssids || - chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) + if (offchannel) + mt76_offchannel_notify(phy, true); + mt76_set_channel(phy, &chandef, offchannel); + + if (!req->n_ssids) goto out; - duration = HZ / 16; /* ~60 ms */ + if (chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) { + spin_lock_bh(&dev->scan_lock); + dev->scan.beacon_received = false; + dev->scan.beacon_wait = true; + spin_unlock_bh(&dev->scan_lock); + goto out; + } + +probe: + if (phy->offchannel) + duration = HZ / 16; /* ~60 ms */ local_bh_disable(); for (i = 0; i < req->n_ssids; i++) mt76_scan_send_probe(dev, &req->ssids[i]); local_bh_enable(); out: - if (dev->scan.chan) + if (dev->scan.chan && phy->offchannel) duration = max_t(int, duration, msecs_to_jiffies(req->duration + (req->duration >> 5))); diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index 9ec6d0b53a84..22f9690634c9 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -227,7 +227,9 @@ mt76_tx_check_non_aql(struct mt76_dev *dev, struct mt76_wcid *wcid, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta; int pending; + int i; if (!wcid || info->tx_time_est) return; @@ -235,6 +237,17 @@ mt76_tx_check_non_aql(struct mt76_dev *dev, struct mt76_wcid *wcid, pending = atomic_dec_return(&wcid->non_aql_packets); if (pending < 0) atomic_cmpxchg(&wcid->non_aql_packets, pending, 0); + + sta = wcid_to_sta(wcid); + if (!sta || pending != MT_MAX_NON_AQL_PKT - 1) + return; + + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { + if (!sta->txq[i]) + continue; + + ieee80211_schedule_txq(dev->hw, sta->txq[i]); + } } void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *skb, @@ -542,6 +555,9 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) if (!wcid || test_bit(MT_WCID_FLAG_PS, &wcid->flags)) continue; + if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT) + continue; + phy = mt76_dev_phy(dev, wcid->phy_idx); if (test_bit(MT76_RESET, &phy->state) || phy->offchannel) continue; @@ -616,7 +632,7 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid, if ((dev->drv->drv_flags & MT_DRV_HW_MGMT_TXQ) && !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && - !ieee80211_is_data(hdr->frame_control) && + !ieee80211_is_data_present(hdr->frame_control) && (!ieee80211_is_bufferable_mmpdu(skb) || ieee80211_is_deauth(hdr->frame_control) || head == &wcid->tx_offchannel)) @@ -644,7 +660,7 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid, return ret; } -static void mt76_txq_schedule_pending(struct mt76_phy *phy) +void mt76_txq_schedule_pending(struct mt76_phy *phy) { LIST_HEAD(tx_list); int ret = 0; @@ -850,9 +866,15 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi) token = idr_alloc(&dev->token, *ptxwi, dev->token_start, dev->token_start + dev->token_size, GFP_ATOMIC); - if (token >= dev->token_start) + if (token >= dev->token_start) { dev->token_count++; + if ((*ptxwi)->qid == MT_TXQ_PSD) { + struct mt76_phy *mphy = mt76_dev_phy(dev, (*ptxwi)->phy_idx); + atomic_inc(&mphy->mgmt_tx_pending); + } + } + #ifdef CONFIG_NET_MEDIATEK_SOC_WED if (mtk_wed_device_active(&dev->mmio.wed) && token >= dev->mmio.wed.wlan.token_start) @@ -897,6 +919,12 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake) if (txwi) { dev->token_count--; + if (txwi->qid == MT_TXQ_PSD) { + struct mt76_phy *mphy = mt76_dev_phy(dev, txwi->phy_idx); + if (atomic_dec_and_test(&mphy->mgmt_tx_pending)) + wake_up(&dev->tx_wait); + } + #ifdef CONFIG_NET_MEDIATEK_SOC_WED if (mtk_wed_device_active(&dev->mmio.wed) && token >= dev->mmio.wed.wlan.token_start && diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c index 1b5cc271a9e1..bad6ca821400 100644 --- a/drivers/net/wireless/mediatek/mt7601u/mcu.c +++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c @@ -403,12 +403,18 @@ mt7601u_upload_firmware(struct mt7601u_dev *dev, const struct mt76_fw *fw) return ret; } +static const char * const mt7601u_fw_paths[] = { + "mediatek/" MT7601U_FIRMWARE, + MT7601U_FIRMWARE, +}; + static int mt7601u_load_firmware(struct mt7601u_dev *dev) { const struct firmware *fw; const struct mt76_fw_header *hdr; int len, ret; u32 val; + int i; mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | MT_USB_DMA_CFG_TX_BULK_EN)); @@ -416,7 +422,14 @@ static int mt7601u_load_firmware(struct mt7601u_dev *dev) if (firmware_running(dev)) return firmware_request_cache(dev->dev, MT7601U_FIRMWARE); - ret = request_firmware(&fw, MT7601U_FIRMWARE, dev->dev); + /* Try loading firmware from multiple locations */ + fw = NULL; + for (i = 0; i < MT7601U_FIRMWARE_PATHS; i++) { + ret = request_firmware(&fw, mt7601u_fw_paths[i], dev->dev); + if (ret == 0) + break; + } + if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.h b/drivers/net/wireless/mediatek/mt7601u/usb.h index 9fdf35970339..723025f84483 100644 --- a/drivers/net/wireless/mediatek/mt7601u/usb.h +++ b/drivers/net/wireless/mediatek/mt7601u/usb.h @@ -9,6 +9,7 @@ #include "mt7601u.h" #define MT7601U_FIRMWARE "mt7601u.bin" +#define MT7601U_FIRMWARE_PATHS ARRAY_SIZE(mt7601u_fw_paths) #define MT_VEND_REQ_MAX_RETRY 10 #define MT_VEND_REQ_TOUT_MS 300 diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 4a1eee1671b9..1fcf5d0d2e13 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -4640,6 +4640,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { }, /* PPE threshold information is not supported */ }, + .uhr_cap = { + .has_uhr = true, + .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX, + }, }, { .types_mask = BIT(NL80211_IFTYPE_AP) | @@ -4748,6 +4753,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { }, /* PPE threshold information is not supported */ }, + .uhr_cap = { + .has_uhr = true, + .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX, + }, }, #ifdef CONFIG_MAC80211_MESH { @@ -4917,6 +4927,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { }, /* PPE threshold information is not supported */ }, + .uhr_cap = { + .has_uhr = true, + .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX, + }, }, { .types_mask = BIT(NL80211_IFTYPE_AP) | @@ -5042,6 +5057,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { }, /* PPE threshold information is not supported */ }, + .uhr_cap = { + .has_uhr = true, + .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX, + }, }, #ifdef CONFIG_MAC80211_MESH { @@ -5235,6 +5255,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { }, /* PPE threshold information is not supported */ }, + .uhr_cap = { + .has_uhr = true, + .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX, + }, }, { .types_mask = BIT(NL80211_IFTYPE_AP) | @@ -5381,6 +5406,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { }, /* PPE threshold information is not supported */ }, + .uhr_cap = { + .has_uhr = true, + .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX, + }, }, #ifdef CONFIG_MAC80211_MESH { @@ -5472,6 +5502,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { }, /* PPE threshold information is not supported */ }, + .uhr_cap = { + .has_uhr = true, + .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX, + }, }, #endif }; diff --git a/include/linux/ieee80211-nan.h b/include/linux/ieee80211-nan.h index d07959bf8a90..ebf28ea651f9 100644 --- a/include/linux/ieee80211-nan.h +++ b/include/linux/ieee80211-nan.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 - 2025 Intel Corporation + * Copyright (c) 2018 - 2026 Intel Corporation */ #ifndef LINUX_IEEE80211_NAN_H @@ -23,6 +23,11 @@ #define NAN_OP_MODE_160MHZ 0x04 #define NAN_OP_MODE_PNDL_SUPPRTED 0x08 +#define NAN_DEV_CAPA_NUM_TX_ANT_POS 0 +#define NAN_DEV_CAPA_NUM_TX_ANT_MASK 0x0f +#define NAN_DEV_CAPA_NUM_RX_ANT_POS 4 +#define NAN_DEV_CAPA_NUM_RX_ANT_MASK 0xf0 + /* NAN Device capabilities, as defined in Wi-Fi Aware (TM) specification * Table 79 */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 52db36120314..b5d649db123f 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1194,6 +1194,13 @@ struct ieee80211_mgmt { #define IEEE80211_MIN_ACTION_SIZE(type) offsetofend(struct ieee80211_mgmt, u.action.type) +/* Link Reconfiguration Status Duple field */ +struct ieee80211_ml_reconf_status { + u8 info; + __le16 status; +} __packed; + +#define IEEE80211_ML_RECONF_LINK_ID_MASK 0xf /* Management MIC information element (IEEE 802.11w) for CMAC */ struct ieee80211_mmie { diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8cd870ece351..9d3639ff9c28 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1831,6 +1831,7 @@ struct cfg80211_ttlm_params { * @eml_cap: EML capabilities of this station * @link_sta_params: link related params. * @epp_peer: EPP peer indication + * @nmi_mac: MAC address of the NMI station of the NAN peer */ struct station_parameters { struct net_device *vlan; @@ -1858,6 +1859,7 @@ struct station_parameters { u16 eml_cap; struct link_station_parameters link_sta_params; bool epp_peer; + const u8 *nmi_mac; }; /** @@ -1897,6 +1899,8 @@ struct station_del_parameters { * entry that is operating, has been marked authorized by userspace) * @CFG80211_STA_MESH_PEER_KERNEL: peer on mesh interface (kernel managed) * @CFG80211_STA_MESH_PEER_USER: peer on mesh interface (user managed) + * @CFG80211_STA_NAN_MGMT: NAN management interface station + * @CFG80211_STA_NAN_DATA: NAN data path station */ enum cfg80211_station_type { CFG80211_STA_AP_CLIENT, @@ -1908,6 +1912,8 @@ enum cfg80211_station_type { CFG80211_STA_TDLS_PEER_ACTIVE, CFG80211_STA_MESH_PEER_KERNEL, CFG80211_STA_MESH_PEER_USER, + CFG80211_STA_NAN_MGMT, + CFG80211_STA_NAN_DATA, }; /** @@ -3980,6 +3986,77 @@ struct cfg80211_qos_map { struct cfg80211_dscp_range up[8]; }; +/** + * DOC: Neighbor Awareness Networking (NAN) + * + * NAN uses two interface types: + * + * - %NL80211_IFTYPE_NAN: a non-netdev interface. This has two roles: (1) holds + * the configuration of all NAN activities (DE parameters, synchronisation + * parameters, local schedule, etc.), and (2) uses as the NAN Management + * Interface (NMI), which is used for NAN management communication. + * + * - %NL80211_IFTYPE_NAN_DATA: The NAN Data Interface (NDI), used for data + * communication with NAN peers. + * + * An NDI interface can only be started (IFF_UP) if the NMI one is running and + * NAN is started. Before NAN is stopped, all associated NDI interfaces + * must be stopped first. + * + * The local schedule specifies which channels the device is available on and + * when. Must be cancelled before NAN is stopped. + * + * NAN Stations + * ~~~~~~~~~~~~ + * + * There are two types of stations corresponding to the two interface types: + * + * - NMI station: Represents the NAN peer. Peer-specific data such as the peer's + * schedule and the HT, VHT and HE capabilities belongs to the NMI station. + * Also used for Tx/Rx of NAN management frames to/from the peer. + * Added on the %NL80211_IFTYPE_NAN interface. + * + * - NDI station: Used for Tx/Rx of data frames (and non-NAN management frames) + * for a specific NDP established with the NAN peer. Added on the + * %NL80211_IFTYPE_NAN_DATA interface. + * + * A peer may reuse its NMI address as the NDI address. In that case, two + * separate stations should be added even though they share the same MAC + * address. + * + * HT, VHT and HE capabilities should not changes after it was set. It is the + * driver's responsibility to check that. + * + * An NDI station can only be added if the corresponding NMI station has already + * been configured with HT (and possibly VHT and HE) capabilities. It is the + * driver's responsibility to check that. + * + * All NDI stations must be removed before corresponding NMI station is removed. + * Therefore, removing a NMI station implies that the associated NDI station(s) + * (if any) will be removed first. + * + * NAN Dependencies + * ~~~~~~~~~~~~~~~~ + * + * The following diagram shows the dependencies between NAN components. + * An arrow from A to B means A must be started/added before B, and B must be + * stopped/removed before A: + * + * +-------------+ + * | NMI iface |---(local schedule) + * +------+------+ + * / \ + * v v + * +-----------+ +-------------+ + * | NDI iface | | NMI sta |---(peer schedule) + * +-----+-----+ +------+------+ + * \ / + * v v + * +----------+ + * | NDI sta | + * +----------+ + */ + /** * struct cfg80211_nan_band_config - NAN band specific configuration * @@ -4050,6 +4127,102 @@ struct cfg80211_nan_conf { u16 vendor_elems_len; }; +#define CFG80211_NAN_SCHED_NUM_TIME_SLOTS 32 + +/** + * struct cfg80211_nan_channel - NAN channel configuration + * + * This struct defines a NAN channel configuration + * + * @chandef: the channel definition + * @channel_entry: pointer to the Channel Entry blob as defined in Wi-Fi Aware + * (TM) 4.0 specification Table 100 (Channel Entry format for the NAN + * Availability attribute). + * @rx_nss: number of spatial streams supported on this channel + */ +struct cfg80211_nan_channel { + struct cfg80211_chan_def chandef; + const u8 *channel_entry; + u8 rx_nss; +}; + +/** + * struct cfg80211_nan_local_sched - NAN local schedule + * + * This struct defines NAN local schedule parameters + * + * @schedule: a mapping of time slots to chandef indexes in %nan_channels. + * An unscheduled slot will be set to %NL80211_NAN_SCHED_NOT_AVAIL_SLOT. + * @n_channels: number of channel definitions in %nan_channels. + * @nan_avail_blob: pointer to NAN Availability attribute blob. + * See %NL80211_ATTR_NAN_AVAIL_BLOB for more details. + * @nan_avail_blob_len: length of the @nan_avail_blob in bytes. + * @deferred: if true, the command containing this schedule configuration is a + * request from the device to perform an announced schedule update. This + * means that it needs to send the updated NAN availability to the peers, + * and do the actual switch on the right time (i.e. at the end of the slot + * after the slot in which the updated NAN Availability was sent). + * See %NL80211_ATTR_NAN_SCHED_DEFERRED for more details. + * If false, the schedule is applied immediately. + * @nan_channels: array of NAN channel definitions that can be scheduled. + */ +struct cfg80211_nan_local_sched { + u8 schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; + u8 n_channels; + const u8 *nan_avail_blob; + u16 nan_avail_blob_len; + bool deferred; + struct cfg80211_nan_channel nan_channels[] __counted_by(n_channels); +}; + +/** + * struct cfg80211_nan_peer_map - NAN peer schedule map + * + * This struct defines a single NAN peer schedule map + * + * @map_id: map ID of this schedule map + * @schedule: a mapping of time slots to chandef indexes in the schedule's + * @nan_channels. Each slot lasts 16TUs. An unscheduled slot will be + * set to %NL80211_NAN_SCHED_NOT_AVAIL_SLOT. + */ +struct cfg80211_nan_peer_map { + u8 map_id; + u8 schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; +}; + +#define CFG80211_NAN_MAX_PEER_MAPS 2 +#define CFG80211_NAN_INVALID_MAP_ID 0xff + +/** + * struct cfg80211_nan_peer_sched - NAN peer schedule + * + * This struct defines NAN peer schedule parameters for a peer. + * + * @peer_addr: MAC address of the peer (NMI address) + * @seq_id: sequence ID of the peer schedule. + * @committed_dw: committed DW as published by the peer. + * See %NL80211_ATTR_NAN_COMMITTED_DW + * @max_chan_switch: maximum channel switch time in microseconds as published + * by the peer. See %NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME. + * @init_ulw: initial ULWs as published by the peer. + * @ulw_size: number of bytes in @init_ulw. + * @n_channels: number of channel definitions in @nan_channels. + * @nan_channels: array of NAN channel definitions for this schedule. + * @maps: array of peer schedule maps. Unused entries have + * map_id = %CFG80211_NAN_INVALID_MAP_ID. + */ +struct cfg80211_nan_peer_sched { + const u8 *peer_addr; + u8 seq_id; + u16 committed_dw; + u16 max_chan_switch; + const u8 *init_ulw; + u16 ulw_size; + u8 n_channels; + struct cfg80211_nan_channel *nan_channels; + struct cfg80211_nan_peer_map maps[CFG80211_NAN_MAX_PEER_MAPS]; +}; + /** * enum cfg80211_nan_conf_changes - indicates changed fields in NAN * configuration @@ -4830,6 +5003,19 @@ struct mgmt_frame_regs { * @nan_change_conf: changes NAN configuration. The changed parameters must * be specified in @changes (using &enum cfg80211_nan_conf_changes); * All other parameters must be ignored. + * @nan_set_local_sched: configure the local schedule for NAN. The schedule + * consists of an array of %cfg80211_nan_channel and the schedule itself, + * in which each entry maps each time slot to the channel on which the + * radio should operate on. If the chandef of a NAN channel is not + * changed, the channel entry must also remain unchanged. It is the + * driver's responsibility to verify this. + * @nan_set_peer_sched: configure the peer schedule for NAN. The schedule + * consists of an array of %cfg80211_nan_channel and the schedule itself, + * in which each entry maps each time slot to a channel on which the + * radio should operate on. In addition, it contains more peer's schedule + * information such as committed DW, etc. When updating an existing peer + * schedule, the full new schedule is provided - partial updates are not + * supported, and the new schedule completely replaces the previous one. * * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS * @@ -5207,7 +5393,12 @@ struct cfg80211_ops { struct wireless_dev *wdev, struct cfg80211_nan_conf *conf, u32 changes); - + int (*nan_set_local_sched)(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched); + int (*nan_set_peer_sched)(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_peer_sched *sched); int (*set_multicast_to_unicast)(struct wiphy *wiphy, struct net_device *dev, const bool enabled); @@ -5836,6 +6027,12 @@ enum wiphy_nan_flags { * @max_channel_switch_time: maximum channel switch time in milliseconds. * @dev_capabilities: NAN device capabilities as defined in Wi-Fi Aware (TM) * specification Table 79 (Capabilities field). + * @phy: Band-agnostic capabilities for NAN data interfaces. Since NAN + * operates on multiple channels simultaneously, these capabilities apply + * across all bands. Valid only if NL80211_IFTYPE_NAN_DATA is supported. + * @phy.ht: HT capabilities (mandatory for NAN data) + * @phy.vht: VHT capabilities (optional) + * @phy.he: HE capabilities (optional) */ struct wiphy_nan_capa { u32 flags; @@ -5843,6 +6040,11 @@ struct wiphy_nan_capa { u8 n_antennas; u16 max_channel_switch_time; u8 dev_capabilities; + struct { + struct ieee80211_sta_ht_cap ht; + struct ieee80211_sta_vht_cap vht; + struct ieee80211_sta_he_cap he; + } phy; }; #define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff @@ -6736,8 +6938,8 @@ enum ieee80211_ap_reg_power { * the P2P Device. * @ps: powersave mode is enabled * @ps_timeout: dynamic powersave timeout - * @ap_unexpected_nlportid: (private) netlink port ID of application - * registered for unexpected class 3 frames (AP mode) + * @unexpected_nlportid: (private) netlink port ID of application + * registered for unexpected frames (AP mode or NAN_DATA mode) * @conn: (private) cfg80211 software SME connection state machine data * @connect_keys: (private) keys to set after connection is established * @conn_bss_type: connecting/connected BSS type @@ -6799,7 +7001,7 @@ struct wireless_dev { bool ps; int ps_timeout; - u32 ap_unexpected_nlportid; + u32 unexpected_nlportid; u32 owner_nlportid; bool nl_owner_dead; @@ -6859,6 +7061,9 @@ struct wireless_dev { } ocb; struct { u8 cluster_id[ETH_ALEN] __aligned(2); + u8 n_channels; + struct cfg80211_chan_def *chandefs; + bool sched_update_pending; } nan; } u; @@ -9367,9 +9572,10 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, * @addr: the transmitter address * @gfp: context flags * - * This function is used in AP mode (only!) to inform userspace that - * a spurious class 3 frame was received, to be able to deauth the - * sender. + * This function is used in AP mode to inform userspace that a spurious + * class 3 frame was received, to be able to deauth the sender. + * It is also used in NAN_DATA mode to report frames from unknown peers + * (A2 not assigned to any active NDP), per Wi-Fi Aware (TM) 4.0 specification 6.2.5. * Return: %true if the frame was passed to userspace (or this failed * for a reason other than not having a subscription.) */ @@ -10016,6 +10222,18 @@ void cfg80211_nan_func_terminated(struct wireless_dev *wdev, enum nl80211_nan_func_term_reason reason, u64 cookie, gfp_t gfp); +/** + * cfg80211_nan_sched_update_done - notify deferred schedule update completion + * @wdev: the wireless device reporting the event + * @success: whether or not the schedule update was successful + * @gfp: allocation flags + * + * This function notifies user space that a deferred local NAN schedule update + * (requested with %NL80211_ATTR_NAN_SCHED_DEFERRED) has been completed. + */ +void cfg80211_nan_sched_update_done(struct wireless_dev *wdev, bool success, + gfp_t gfp); + /* ethtool helper */ void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); @@ -10356,6 +10574,39 @@ void cfg80211_nan_cluster_joined(struct wireless_dev *wdev, const u8 *cluster_id, bool new_cluster, gfp_t gfp); +/** + * cfg80211_nan_ulw_update - Notify user space about ULW update + * @wdev: Pointer to the wireless device structure + * @ulw: Pointer to the ULW blob data + * @ulw_len: Length of the ULW blob in bytes + * @gfp: Memory allocation flags + * + * This function is used by drivers to notify user space when the device's + * ULW (Unaligned Schedule) blob has been updated. User space can use this + * blob to attach to frames sent to peers. + */ +void cfg80211_nan_ulw_update(struct wireless_dev *wdev, + const u8 *ulw, size_t ulw_len, gfp_t gfp); + +/** + * cfg80211_nan_channel_evac - Notify user space about NAN channel evacuation + * @wdev: Pointer to the wireless device structure + * @chandef: Pointer to the channel definition of the NAN channel that was + * evacuated + * @gfp: Memory allocation flags + * + * This function is used by drivers to notify user space when a NAN + * channel has been evacuated (i.e. ULWed) due to channel resource conflicts + * with other interfaces. + * This can happen when another interface sharing the channel resource with NAN + * needs to move to a different channel (e.g. due to channel switch or link + * switch). User space may reconfigure the local schedule to exclude the + * evacuated channel. + */ +void cfg80211_nan_channel_evac(struct wireless_dev *wdev, + const struct cfg80211_chan_def *chandef, + gfp_t gfp); + #ifdef CONFIG_CFG80211_DEBUGFS /** * wiphy_locked_debugfs_read - do a locked read in debugfs diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 67d764023988..3d55bf4be36f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -906,8 +906,9 @@ * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP * (or GO) interface (i.e. hostapd) to ask for unexpected frames to * implement sending deauth to stations that send unexpected class 3 - * frames. Also used as the event sent by the kernel when such a frame - * is received. + * frames. For NAN_DATA interfaces, this is used to report frames from + * unknown peers (A2 not assigned to any active NDP). + * Also used as the event sent by the kernel when such a frame is received. * For the event, the %NL80211_ATTR_MAC attribute carries the TA and * other attributes like the interface index are present. * If used as the command it must have an interface index and you can @@ -1367,6 +1368,53 @@ * %NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP. The current channel * definition is also sent. * + * @NL80211_CMD_NAN_SET_LOCAL_SCHED: Set the local NAN schedule. NAN must be + * operational (%NL80211_CMD_START_NAN was executed). Must contain + * %NL80211_ATTR_NAN_TIME_SLOTS and %NL80211_ATTR_NAN_AVAIL_BLOB, but + * %NL80211_ATTR_NAN_CHANNEL is optional (for example in case of a channel + * removal, that channel won't be provided). + * If %NL80211_ATTR_NAN_SCHED_DEFERRED is set, the command is a request + * from the device to perform an announced schedule update. See + * %NL80211_ATTR_NAN_SCHED_DEFERRED for more details. + * If not set, the schedule should be applied immediately. + * @NL80211_CMD_NAN_SCHED_UPDATE_DONE: Event sent to user space to notify that + * a deferred local NAN schedule update (requested with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED and %NL80211_ATTR_NAN_SCHED_DEFERRED) + * has been completed. The presence of %NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS + * indicates that the update was successful. + * @NL80211_CMD_NAN_SET_PEER_SCHED: Set the peer NAN schedule. NAN + * must be operational (%NL80211_CMD_START_NAN was executed). + * Required attributes: %NL80211_ATTR_MAC (peer NMI address) and + * %NL80211_ATTR_NAN_COMMITTED_DW. + * Optionally, the full schedule can be provided by including all of: + * %NL80211_ATTR_NAN_SEQ_ID, %NL80211_ATTR_NAN_CHANNEL (one or more), and + * %NL80211_ATTR_NAN_PEER_MAPS (see &enum nl80211_nan_peer_map_attrs). + * If any of these three optional attributes is provided, all three must + * be provided. + * Each peer channel must be compatible with at least one local channel + * set by %NL80211_CMD_SET_LOCAL_NAN_SCHED. Different maps must not + * contain compatible channels. + * For single-radio devices (n_radio <= 1), different maps must not + * schedule the same time slot, as the device cannot operate on multiple + * channels simultaneously. + * When updating an existing peer schedule, the full new schedule must be + * provided - partial updates are not supported. The new schedule will + * completely replace the previous one. + * The peer schedule is automatically removed when the NMI station is + * removed. + * @NL80211_CMD_NAN_ULW_UPDATE: Notification from the driver to user space + * with the updated ULW blob of the device. User space can use this blob + * to attach to frames sent to peers. This notification contains + * %NL80211_ATTR_NAN_ULW with the ULW blob. + * @NL80211_CMD_NAN_CHANNEL_EVAC: Notification to indicate that a NAN + * channel has been evacuated due to resource conflicts with other + * interfaces. This can happen when another interface sharing the channel + * resource with NAN needs to move to a different channel (e.g., channel + * switch or link switch on a BSS interface). + * The notification contains %NL80211_ATTR_NAN_CHANNEL attribute + * identifying the evacuated channel. + * User space may reconfigure the local schedule in response to this + * notification. * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1632,6 +1680,16 @@ enum nl80211_commands { NL80211_CMD_INCUMBENT_SIGNAL_DETECT, + NL80211_CMD_NAN_SET_LOCAL_SCHED, + + NL80211_CMD_NAN_SCHED_UPDATE_DONE, + + NL80211_CMD_NAN_SET_PEER_SCHED, + + NL80211_CMD_NAN_ULW_UPDATE, + + NL80211_CMD_NAN_CHANNEL_EVAC, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2659,7 +2717,8 @@ enum nl80211_commands { * a flow is assigned on each round of the DRR scheduler. * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from * association request when used with NL80211_CMD_NEW_STATION). Can be set - * only if %NL80211_STA_FLAG_WME is set. + * only if %NL80211_STA_FLAG_WME is set (except for NAN, which uses WME + * anyway). * * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include * in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing @@ -2991,6 +3050,82 @@ enum nl80211_commands { * @NL80211_ATTR_DISABLE_UHR: Force UHR capable interfaces to disable * this feature during association. This is a flag attribute. * Currently only supported in mac80211 drivers. + * @NL80211_ATTR_NAN_CHANNEL: This is a nested attribute. There can be multiple + * attributes of this type, each one represents a channel definition and + * consists of top-level attributes like %NL80211_ATTR_WIPHY_FREQ. + * When used with %NL80211_CMD_NAN_SET_LOCAL_SCHED, it specifies + * the channel definitions on which the radio needs to operate during + * specific time slots. All of the channel definitions should be mutually + * incompatible. With this command, %NL80211_ATTR_NAN_CHANNEL_ENTRY and + * %NL80211_ATTR_NAN_RX_NSS are mandatory. + * When used with %NL80211_CMD_NAN_SET_PEER_SCHED, it configures the + * peer NAN channels. In that case, the channel definitions can be + * compatible to each other, or even identical just with different RX NSS. + * With this command, %NL80211_ATTR_NAN_CHANNEL_ENTRY and + * %NL80211_ATTR_NAN_RX_NSS are mandatory. + * The number of channels should fit the current configuration of channels + * and the possible interface combinations. + * If an existing NAN channel is changed but the chandef isn't, the + * channel entry must also remain unchanged. + * When used with %NL80211_CMD_NAN_CHANNEL_EVAC, this identifies the + * channels that were evacuated. + * @NL80211_ATTR_NAN_CHANNEL_ENTRY: a byte array of 6 bytes. contains the + * Channel Entry as defined in Wi-Fi Aware (TM) 4.0 specification Table + * 100 (Channel Entry format for the NAN Availability attribute). + * @NL80211_ATTR_NAN_RX_NSS: (u8) RX NSS used for a NAN channel. This is + * used with %NL80211_ATTR_NAN_CHANNEL when configuring NAN channels with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED or %NL80211_CMD_NAN_SET_PEER_SCHED. + * @NL80211_ATTR_NAN_TIME_SLOTS: an array of u8 values and 32 cells. each value + * maps a time slot to the chandef on which the radio should operate on in + * that time. %NL80211_NAN_SCHED_NOT_AVAIL_SLOT indicates unscheduled. + * The chandef is represented using its index, where the index is the + * sequential number of the %NL80211_ATTR_NAN_CHANNEL attribute within all + * the attributes of this type. + * Each slots spans over 16TUs, hence the entire schedule spans over + * 512TUs. Other slot durations and periods are currently not supported. + * @NL80211_ATTR_NAN_AVAIL_BLOB: (Binary) The NAN Availability attribute blob, + * including the attribute header, as defined in Wi-Fi Aware (TM) 4.0 + * specification Table 93 (NAN Availability attribute format). Required with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED to provide the raw NAN Availability + * attribute. Used by the device to publish Schedule Update NAFs. + * @NL80211_ATTR_NAN_SCHED_DEFERRED: Flag attribute used with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED. When present, the command is a + * request from the device to perform an announced schedule update. This + * means that it needs to send the updated NAN availability to the peers, + * and do the actual switch on the right time (i.e. at the end of the slot + * after the slot in which the updated NAN Availability was sent). Since + * the slots management is done in the device, the update to the peers + * needs to be sent by the device, so it knows the actual switch time. + * If the flag is not set, the schedule should be applied immediately. + * When this flag is set, the total number of NAN channels from both the + * old and new schedules must not exceed the allowed number of local NAN + * channels, because with deferred scheduling the old channels cannot be + * removed before adding the new ones to free up space. + * @NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS: flag attribute used with + * %NL80211_CMD_NAN_SCHED_UPDATE_DONE to indicate that the deferred + * schedule update completed successfully. If this flag is not present, + * the update failed. + * @NL80211_ATTR_NAN_NMI_MAC: The address of the NMI station to which this NDI + * station belongs. Used with %NL80211_CMD_NEW_STATION when adding an NDI + * station. + * @NL80211_ATTR_NAN_ULW: (Binary) The initial ULW(s) as published by the + * peer, as defined in the Wi-Fi Aware (TM) 4.0 specification Table 109 + * (Unaligned Schedule attribute format). Used to configure the device + * with the initial ULW(s) of a peer, before the device starts tracking it. + * @NL80211_ATTR_NAN_COMMITTED_DW: (u16) The committed DW as published by the + * peer, as defined in the Wi-Fi Aware (TM) 4.0 specification Table 80 + * (Committed DW Information field format). + * @NL80211_ATTR_NAN_SEQ_ID: (u8) The sequence ID of the peer schedule that + * %NL80211_CMD_NAN_SET_PEER_SCHED defines. The device follows the + * sequence ID in the frames to identify newer schedules. Once a schedule + * with a higher sequence ID is received, the device may stop communicating + * with that peer until a new peer schedule with a matching sequence ID is + * received. + * @NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME: (u16) The maximum channel switch + * time, in microseconds. + * @NL80211_ATTR_NAN_PEER_MAPS: Nested array of peer schedule maps. + * Used with %NL80211_CMD_NAN_SET_PEER_SCHED. Contains up to 2 entries, + * each containing nested attributes from &enum nl80211_nan_peer_map_attrs. * * @NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP: u32 attribute specifying * the signal interference bitmap detected on the operating bandwidth for @@ -3582,6 +3717,22 @@ enum nl80211_attrs { NL80211_ATTR_UHR_OPERATION, + NL80211_ATTR_NAN_CHANNEL, + NL80211_ATTR_NAN_CHANNEL_ENTRY, + NL80211_ATTR_NAN_TIME_SLOTS, + NL80211_ATTR_NAN_RX_NSS, + NL80211_ATTR_NAN_AVAIL_BLOB, + NL80211_ATTR_NAN_SCHED_DEFERRED, + NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS, + + NL80211_ATTR_NAN_NMI_MAC, + + NL80211_ATTR_NAN_ULW, + NL80211_ATTR_NAN_COMMITTED_DW, + NL80211_ATTR_NAN_SEQ_ID, + NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME, + NL80211_ATTR_NAN_PEER_MAPS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3675,6 +3826,9 @@ enum nl80211_attrs { * @NL80211_IFTYPE_OCB: Outside Context of a BSS * This mode corresponds to the MIB variable dot11OCBActivated=true * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev) + * @NL80211_IFTYPE_NAN_DATA: NAN data interface type (netdev); NAN data + * interfaces can only be brought up (IFF_UP) when a NAN interface + * already exists and NAN has been started (using %NL80211_CMD_START_NAN). * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -3696,6 +3850,7 @@ enum nl80211_iftype { NL80211_IFTYPE_P2P_DEVICE, NL80211_IFTYPE_OCB, NL80211_IFTYPE_NAN, + NL80211_IFTYPE_NAN_DATA, /* keep last */ NUM_NL80211_IFTYPES, @@ -4384,6 +4539,46 @@ enum nl80211_band_attr { #define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA +/** + * enum nl80211_nan_phy_cap_attr - NAN PHY capabilities attributes + * @__NL80211_NAN_PHY_CAP_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET: 16-byte attribute containing HT MCS set + * @NL80211_NAN_PHY_CAP_ATTR_HT_CAPA: HT capabilities (u16) + * @NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR: HT A-MPDU factor (u8) + * @NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY: HT A-MPDU density (u8) + * @NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET: 8-byte attribute containing VHT MCS set + * @NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA: VHT capabilities (u32) + * @NL80211_NAN_PHY_CAP_ATTR_HE_MAC: HE MAC capabilities + * @NL80211_NAN_PHY_CAP_ATTR_HE_PHY: HE PHY capabilities + * @NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET: HE supported NSS/MCS combinations + * @NL80211_NAN_PHY_CAP_ATTR_HE_PPE: HE PPE thresholds + * @NL80211_NAN_PHY_CAP_ATTR_MAX: highest NAN PHY cap attribute number + * @__NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST: internal use + */ +enum nl80211_nan_phy_cap_attr { + __NL80211_NAN_PHY_CAP_ATTR_INVALID, + + /* HT capabilities */ + NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET, + NL80211_NAN_PHY_CAP_ATTR_HT_CAPA, + NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR, + NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY, + + /* VHT capabilities */ + NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET, + NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA, + + /* HE capabilities */ + NL80211_NAN_PHY_CAP_ATTR_HE_MAC, + NL80211_NAN_PHY_CAP_ATTR_HE_PHY, + NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET, + NL80211_NAN_PHY_CAP_ATTR_HE_PPE, + + /* keep last */ + __NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST, + NL80211_NAN_PHY_CAP_ATTR_MAX = __NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST - 1 +}; + /** * enum nl80211_wmm_rule - regulatory wmm rule * @@ -8557,6 +8752,8 @@ enum nl80211_s1g_short_beacon_attrs { * @NL80211_NAN_CAPA_CAPABILITIES: u8 attribute containing the * capabilities of the device as defined in Wi-Fi Aware (TM) * specification Table 79 (Capabilities field). + * @NL80211_NAN_CAPA_PHY: nested attribute containing band-agnostic + * capabilities for NAN data path. See &enum nl80211_nan_phy_cap_attr. * @__NL80211_NAN_CAPABILITIES_LAST: Internal * @NL80211_NAN_CAPABILITIES_MAX: Highest NAN capability attribute. */ @@ -8569,9 +8766,38 @@ enum nl80211_nan_capabilities { NL80211_NAN_CAPA_NUM_ANTENNAS, NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME, NL80211_NAN_CAPA_CAPABILITIES, + NL80211_NAN_CAPA_PHY, /* keep last */ __NL80211_NAN_CAPABILITIES_LAST, NL80211_NAN_CAPABILITIES_MAX = __NL80211_NAN_CAPABILITIES_LAST - 1, }; +/** + * enum nl80211_nan_peer_map_attrs - NAN peer schedule map attributes + * + * Nested attributes used within %NL80211_ATTR_NAN_PEER_MAPS to define + * individual peer schedule maps. + * + * @__NL80211_NAN_PEER_MAP_ATTR_INVALID: Invalid + * @NL80211_NAN_PEER_MAP_ATTR_MAP_ID: (u8) The map ID for this schedule map. + * @NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS: An array of u8 values with 32 cells. + * Each value maps a time slot to a channel index within the schedule's + * channel list (%NL80211_ATTR_NAN_CHANNEL attributes). + * %NL80211_NAN_SCHED_NOT_AVAIL_SLOT indicates unscheduled. + * @__NL80211_NAN_PEER_MAP_ATTR_LAST: Internal + * @NL80211_NAN_PEER_MAP_ATTR_MAX: Highest peer map attribute + */ +enum nl80211_nan_peer_map_attrs { + __NL80211_NAN_PEER_MAP_ATTR_INVALID, + + NL80211_NAN_PEER_MAP_ATTR_MAP_ID, + NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS, + + /* keep last */ + __NL80211_NAN_PEER_MAP_ATTR_LAST, + NL80211_NAN_PEER_MAP_ATTR_MAX = __NL80211_NAN_PEER_MAP_ATTR_LAST - 1, +}; + +#define NL80211_NAN_SCHED_NOT_AVAIL_SLOT 0xff + #endif /* __LINUX_NL80211_H */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c25d7c579952..b6163dcc7e92 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -718,6 +718,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev, case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_NAN_DATA: /* shouldn't happen */ WARN_ON_ONCE(1); break; @@ -2140,12 +2141,13 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, return -EINVAL; if (params->ht_capa) - ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, + ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap, params->ht_capa, link_sta); /* VHT can override some HT caps such as the A-MSDU max length */ if (params->vht_capa) ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, + &sband->vht_cap, params->vht_capa, NULL, link_sta); diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index fab9c1acd8eb..dd99fdc1ea9d 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -166,6 +166,13 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local, for_each_chanctx_user_all(local, ctx, &iter) num++; + /* + * This ctx is in the process of getting used, + * take it into consideration + */ + if (ctx->will_be_used) + num++; + return num; } @@ -447,74 +454,94 @@ ieee80211_get_max_required_bw(struct ieee80211_link_data *link) return max_bw; } +static enum nl80211_chan_width +ieee80211_get_width_of_link(struct ieee80211_link_data *link) +{ + struct ieee80211_local *local = link->sdata->local; + + switch (link->sdata->vif.type) { + case NL80211_IFTYPE_STATION: + if (!link->sdata->vif.cfg.assoc) { + /* + * The AP's sta->bandwidth may not yet be set + * at this point (pre-association), so simply + * take the width from the chandef. We cannot + * have TDLS peers yet (only after association). + */ + return link->conf->chanreq.oper.width; + } + /* + * otherwise just use min_def like in AP, depending on what + * we currently think the AP STA (and possibly TDLS peers) + * require(s) + */ + fallthrough; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + return ieee80211_get_max_required_bw(link); + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_NAN: + break; + case NL80211_IFTYPE_MONITOR: + WARN_ON_ONCE(!ieee80211_hw_check(&local->hw, + NO_VIRTUAL_MONITOR)); + fallthrough; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: + return link->conf->chanreq.oper.width; + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_NAN_DATA: + WARN_ON_ONCE(1); + break; + } + + /* Take the lowest possible, so it won't change the max width */ + return NL80211_CHAN_WIDTH_20_NOHT; +} + 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, bool check_reserved) { - struct ieee80211_sub_if_data *sdata; - struct ieee80211_link_data *link; enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; + struct ieee80211_chanctx_user_iter iter; + struct ieee80211_sub_if_data *sdata; + enum nl80211_chan_width width; if (WARN_ON(check_reserved && rsvd_for)) return ctx->conf.def.width; - for_each_sdata_link(local, link) { - enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; - - if (check_reserved) { - if (link->reserved_chanctx != ctx) - continue; - } else if (link != rsvd_for && - rcu_access_pointer(link->conf->chanctx_conf) != &ctx->conf) - continue; - - switch (link->sdata->vif.type) { - case NL80211_IFTYPE_STATION: - if (!link->sdata->vif.cfg.assoc) { - /* - * The AP's sta->bandwidth may not yet be set - * at this point (pre-association), so simply - * take the width from the chandef. We cannot - * have TDLS peers yet (only after association). - */ - width = link->conf->chanreq.oper.width; - break; - } - /* - * otherwise just use min_def like in AP, depending on what - * we currently think the AP STA (and possibly TDLS peers) - * require(s) - */ - fallthrough; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - width = ieee80211_get_max_required_bw(link); - break; - case NL80211_IFTYPE_P2P_DEVICE: - case NL80211_IFTYPE_NAN: - continue; - case NL80211_IFTYPE_MONITOR: - WARN_ON_ONCE(!ieee80211_hw_check(&local->hw, - NO_VIRTUAL_MONITOR)); - fallthrough; - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_OCB: - width = link->conf->chanreq.oper.width; - break; - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_UNSPECIFIED: - case NUM_NL80211_IFTYPES: - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - WARN_ON_ONCE(1); + /* When this is true we only care about the reserving links */ + if (check_reserved) { + for_each_chanctx_user_reserved(local, ctx, &iter) { + width = ieee80211_get_width_of_link(iter.link); + max_bw = max(max_bw, width); } + goto check_monitor; + } + /* Consider all assigned links */ + for_each_chanctx_user_assigned(local, ctx, &iter) { + width = ieee80211_get_width_of_link(iter.link); max_bw = max(max_bw, width); } + if (!rsvd_for || + rsvd_for->sdata == rcu_access_pointer(local->monitor_sdata)) + goto check_monitor; + + /* Consider the link for which this chanctx is reserved/going to be assigned */ + width = ieee80211_get_width_of_link(rsvd_for); + max_bw = max(max_bw, width); + +check_monitor: /* use the configured bandwidth in case of monitor interface */ sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata); if (sdata && @@ -752,10 +779,9 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, _ieee80211_change_chanctx(local, ctx, old_ctx, chanreq, NULL); } -/* Note: if successful, the returned chanctx is reserved for the link */ +/* Note: if successful, the returned chanctx will_be_used flag is set */ static struct ieee80211_chanctx * ieee80211_find_chanctx(struct ieee80211_local *local, - struct ieee80211_link_data *link, const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode) { @@ -767,9 +793,6 @@ ieee80211_find_chanctx(struct ieee80211_local *local, if (mode == IEEE80211_CHANCTX_EXCLUSIVE) return NULL; - if (WARN_ON(link->reserved_chanctx)) - return NULL; - list_for_each_entry(ctx, &local->chanctx_list, list) { const struct ieee80211_chan_req *compat; @@ -790,12 +813,12 @@ ieee80211_find_chanctx(struct ieee80211_local *local, continue; /* - * Reserve the chanctx temporarily, as the driver might change + * Mark the chanctx as will be used, as the driver might change * active links during callbacks we make into it below and/or * later during assignment, which could (otherwise) cause the * context to actually be removed. */ - link->reserved_chanctx = ctx; + ctx->will_be_used = true; ieee80211_change_chanctx(local, ctx, ctx, compat); @@ -1438,6 +1461,7 @@ ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link) case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: case NUM_NL80211_IFTYPES: WARN_ON(1); break; @@ -2011,6 +2035,36 @@ void __ieee80211_link_release_channel(struct ieee80211_link_data *link, ieee80211_vif_use_reserved_switch(local); } +static struct ieee80211_chanctx * +ieee80211_find_or_create_chanctx(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_chan_req *chanreq, + enum ieee80211_chanctx_mode mode, + bool assign_on_failure, + bool *reused_ctx) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx *ctx; + int radio_idx; + + lockdep_assert_wiphy(local->hw.wiphy); + + ctx = ieee80211_find_chanctx(local, chanreq, mode); + if (ctx) { + *reused_ctx = true; + return ctx; + } + + *reused_ctx = false; + + if (!ieee80211_find_available_radio(local, chanreq, + sdata->wdev.radio_mask, + &radio_idx)) + return ERR_PTR(-EBUSY); + + return ieee80211_new_chanctx(local, chanreq, mode, + assign_on_failure, radio_idx); +} + int _ieee80211_link_use_channel(struct ieee80211_link_data *link, const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode, @@ -2020,8 +2074,7 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx *ctx; u8 radar_detect_width = 0; - bool reserved = false; - int radio_idx; + bool reused_ctx = false; int ret; lockdep_assert_wiphy(local->hw.wiphy); @@ -2049,17 +2102,8 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link, if (!local->in_reconfig) __ieee80211_link_release_channel(link, false); - ctx = ieee80211_find_chanctx(local, link, chanreq, mode); - /* Note: context is now reserved */ - if (ctx) - reserved = true; - else if (!ieee80211_find_available_radio(local, chanreq, - sdata->wdev.radio_mask, - &radio_idx)) - ctx = ERR_PTR(-EBUSY); - else - ctx = ieee80211_new_chanctx(local, chanreq, mode, - assign_on_failure, radio_idx); + ctx = ieee80211_find_or_create_chanctx(sdata, chanreq, mode, + assign_on_failure, &reused_ctx); if (IS_ERR(ctx)) { ret = PTR_ERR(ctx); goto out; @@ -2069,10 +2113,13 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link, ret = ieee80211_assign_link_chanctx(link, ctx, assign_on_failure); - if (reserved) { - /* remove reservation */ - WARN_ON(link->reserved_chanctx != ctx); - link->reserved_chanctx = NULL; + /* + * In case an existing channel context is being used, we marked it as + * will_be_used, now that it is assigned - clear this indication + */ + if (reused_ctx) { + WARN_ON(!ctx->will_be_used); + ctx->will_be_used = false; } if (ret) { diff --git a/net/mac80211/he.c b/net/mac80211/he.c index f7b05e59374c..93e0342cff4f 100644 --- a/net/mac80211/he.c +++ b/net/mac80211/he.c @@ -108,14 +108,13 @@ static void ieee80211_he_mcs_intersection(__le16 *he_own_rx, __le16 *he_peer_rx, } void -ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, - struct ieee80211_supported_band *sband, - const u8 *he_cap_ie, u8 he_cap_len, - const struct ieee80211_he_6ghz_capa *he_6ghz_capa, - struct link_sta_info *link_sta) +_ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_sta_he_cap *own_he_cap_ptr, + const u8 *he_cap_ie, u8 he_cap_len, + const struct ieee80211_he_6ghz_capa *he_6ghz_capa, + struct link_sta_info *link_sta) { struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; - const struct ieee80211_sta_he_cap *own_he_cap_ptr; struct ieee80211_sta_he_cap own_he_cap; struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie; u8 he_ppe_size; @@ -125,12 +124,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, memset(he_cap, 0, sizeof(*he_cap)); - if (!he_cap_ie) - return; - - own_he_cap_ptr = - ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); - if (!own_he_cap_ptr) + if (!he_cap_ie || !own_he_cap_ptr || !own_he_cap_ptr->has_he) return; own_he_cap = *own_he_cap_ptr; @@ -164,7 +158,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta); link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); - if (sband->band == NL80211_BAND_6GHZ && he_6ghz_capa) + if (he_6ghz_capa) ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, link_sta); ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80, @@ -207,6 +201,23 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, } } +void +ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + const u8 *he_cap_ie, u8 he_cap_len, + const struct ieee80211_he_6ghz_capa *he_6ghz_capa, + struct link_sta_info *link_sta) +{ + const struct ieee80211_sta_he_cap *own_he_cap = + ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); + + _ieee80211_he_cap_ie_to_sta_he_cap(sdata, own_he_cap, he_cap_ie, + he_cap_len, + (sband->band == NL80211_BAND_6GHZ) ? + he_6ghz_capa : NULL, + link_sta); +} + void ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif *vif, const struct ieee80211_he_operation *he_op_ie) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 33f1e1b235e9..410e2354f33a 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -136,7 +136,7 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, - struct ieee80211_supported_band *sband, + const struct ieee80211_sta_ht_cap *own_cap_ptr, const struct ieee80211_ht_cap *ht_cap_ie, struct link_sta_info *link_sta) { @@ -151,12 +151,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, memset(&ht_cap, 0, sizeof(ht_cap)); - if (!ht_cap_ie || !sband->ht_cap.ht_supported) + if (!ht_cap_ie || !own_cap_ptr->ht_supported) goto apply; ht_cap.ht_supported = true; - own_cap = sband->ht_cap; + own_cap = *own_cap_ptr; /* * If user has specified capability over-rides, take care diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 0298272c37ec..1e1ab25d9d8d 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1014,7 +1014,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, ieee80211_chandef_ht_oper(elems->ht_operation, &chandef); memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie)); - rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, + rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, + &sband->ht_cap, &htcap_ie, &sta->deflink); @@ -1033,6 +1034,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, &chandef); memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie)); ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, + &sband->vht_cap, &cap_ie, NULL, &sta->deflink); if (memcmp(&cap, &sta->sta.deflink.vht_cap, sizeof(cap))) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d71e0c6d2165..bacb49ad2817 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -928,6 +928,9 @@ struct ieee80211_chanctx { bool radar_detected; + /* This chanctx is in process of getting used */ + bool will_be_used; + /* MUST be last - ends in a flexible-array member. */ struct ieee80211_chanctx_conf conf; }; @@ -2185,7 +2188,7 @@ void ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata, void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_ht_cap *ht_cap); bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, - struct ieee80211_supported_band *sband, + const struct ieee80211_sta_ht_cap *own_cap, const struct ieee80211_ht_cap *ht_cap_ie, struct link_sta_info *link_sta); void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, @@ -2270,6 +2273,7 @@ void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local, void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, + const struct ieee80211_sta_vht_cap *own_vht_cap, const struct ieee80211_vht_cap *vht_cap_ie, const struct ieee80211_vht_cap *vht_cap_ie2, struct link_sta_info *link_sta); @@ -2310,6 +2314,12 @@ ieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *sta); /* HE */ void +_ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_sta_he_cap *own_he_cap, + const u8 *he_cap_ie, u8 he_cap_len, + const struct ieee80211_he_6ghz_capa *he_6ghz_capa, + struct link_sta_info *link_sta); +void ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, const u8 *he_cap_ie, u8 he_cap_len, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 40ce0bb72726..125897717a4c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1222,14 +1222,14 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local, } } - set_bit(SDATA_STATE_RUNNING, &sdata->state); - ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR); if (ret) { kfree(sdata); return ret; } + set_bit(SDATA_STATE_RUNNING, &sdata->state); + mutex_lock(&local->iflist_mtx); rcu_assign_pointer(local->monitor_sdata, sdata); mutex_unlock(&local->iflist_mtx); @@ -1242,6 +1242,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local, mutex_unlock(&local->iflist_mtx); synchronize_net(); drv_remove_interface(local, sdata); + clear_bit(SDATA_STATE_RUNNING, &sdata->state); kfree(sdata); return ret; } @@ -1360,8 +1361,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) break; } case NL80211_IFTYPE_AP: - sdata->bss = &sdata->u.ap; - break; case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_MONITOR: @@ -1369,6 +1368,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: /* no special treatment */ break; case NL80211_IFTYPE_UNSPECIFIED: @@ -1386,8 +1386,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) local->reconfig_failure = false; res = drv_start(local); - if (res) - goto err_del_bss; + if (res) { + /* + * no need to worry about AP_VLAN cleanup since in that + * case we can't have open_count == 0 + */ + return res; + } ieee80211_led_radio(local, true); ieee80211_mod_tpt_led_trig(local, IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); @@ -1458,6 +1463,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) netif_carrier_on(dev); list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list); break; + case NL80211_IFTYPE_AP: + sdata->bss = &sdata->u.ap; + fallthrough; default: if (coming_up) { ieee80211_del_virtual_monitor(local); @@ -1546,12 +1554,10 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) err_stop: if (!local->open_count) drv_stop(local, false); - err_del_bss: - sdata->bss = NULL; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) list_del(&sdata->u.vlan.list); - /* might already be clear but that doesn't matter */ - clear_bit(SDATA_STATE_RUNNING, &sdata->state); + /* Might not be initialized yet, but it is harmless */ + sdata->bss = NULL; return res; } @@ -1940,6 +1946,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_P2P_DEVICE: sdata->vif.bss_conf.bssid = sdata->vif.addr; break; + case NL80211_IFTYPE_NAN_DATA: + break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_WDS: case NUM_NL80211_IFTYPES: diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 7d823a55636f..803106fc3134 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -450,12 +450,13 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, changed |= IEEE80211_RC_SUPP_RATES_CHANGED; sta->sta.deflink.supp_rates[sband->band] = rates; - if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, + if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap, elems->ht_cap_elem, &sta->deflink)) changed |= IEEE80211_RC_BW_CHANGED; ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, + &sband->vht_cap, elems->vht_cap_elem, NULL, &sta->deflink); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0cd8d07bf668..7fc5616cb244 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -5586,7 +5586,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, /* Set up internal HT/VHT capabilities */ if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT) - ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, + ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap, elems->ht_cap_elem, link_sta); @@ -5622,6 +5622,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, } ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, + &sband->vht_cap, elems->vht_cap_elem, bss_vht_cap, link_sta); rcu_read_unlock(); @@ -10458,8 +10459,8 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, pos = mgmt->u.action.ml_reconf_resp.variable; len -= offsetofend(typeof(*mgmt), u.action.ml_reconf_resp); - /* each status duple is 3 octets */ - if (len < mgmt->u.action.ml_reconf_resp.count * 3) { + if (len < mgmt->u.action.ml_reconf_resp.count * + sizeof(struct ieee80211_ml_reconf_status)) { sdata_info(sdata, "mlo: reconf: unexpected len=%zu, count=%u\n", len, mgmt->u.action.ml_reconf_resp.count); @@ -10468,9 +10469,11 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, link_mask = sta_changed_links; for (i = 0; i < mgmt->u.action.ml_reconf_resp.count; i++) { - u16 status = get_unaligned_le16(pos + 1); + struct ieee80211_ml_reconf_status *reconf_status = (void *)pos; + u16 status = le16_to_cpu(reconf_status->status); - link_id = *pos; + link_id = u8_get_bits(reconf_status->info, + IEEE80211_ML_RECONF_LINK_ID_MASK); if (!(link_mask & BIT(link_id))) { sdata_info(sdata, @@ -10505,8 +10508,8 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.reconf.added_links &= ~BIT(link_id); } - pos += 3; - len -= 3; + pos += sizeof(*reconf_status); + len -= sizeof(*reconf_status); } if (link_mask) { diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 19c33f7a8193..d9a654ef082d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -4607,6 +4607,8 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) (ieee80211_is_public_action(hdr, skb->len) || (ieee80211_is_auth(hdr->frame_control) && ether_addr_equal(sdata->vif.addr, hdr->addr1))); + case NL80211_IFTYPE_NAN_DATA: + return false; default: break; } diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 1f0c07eaad1b..e5968d754f8b 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1778,9 +1778,8 @@ TRACE_EVENT(drv_switch_vif_chanctx, SWITCH_ENTRY_ASSIGN(vif.vif_type, vif->type); SWITCH_ENTRY_ASSIGN(vif.p2p, vif->p2p); SWITCH_ENTRY_ASSIGN(link_id, link_conf->link_id); - strncpy(local_vifs[i].vif.vif_name, - sdata->name, - sizeof(local_vifs[i].vif.vif_name)); + strscpy_pad(local_vifs[i].vif.vif_name, + sdata->name); SWITCH_ENTRY_ASSIGN(old_chandef.control_freq, old_ctx->def.chan->center_freq); SWITCH_ENTRY_ASSIGN(old_chandef.freq_offset, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 55054de62508..8987a4504520 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2118,6 +2118,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) return res; } break; + case NL80211_IFTYPE_NAN_DATA: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_P2P_DEVICE: diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 80120f9f17b6..a6570781740a 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -4,7 +4,7 @@ * * Portions of this file * Copyright(c) 2015 - 2016 Intel Deutschland GmbH - * Copyright (C) 2018-2026 Intel Corporation + * Copyright (C) 2018 - 2026 Intel Corporation */ #include @@ -115,6 +115,7 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, + const struct ieee80211_sta_vht_cap *own_vht_cap, const struct ieee80211_vht_cap *vht_cap_ie, const struct ieee80211_vht_cap *vht_cap_ie2, struct link_sta_info *link_sta) @@ -122,7 +123,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; struct ieee80211_sta_vht_cap own_cap; u32 cap_info, i; - bool have_80mhz; u32 mpdu_len; memset(vht_cap, 0, sizeof(*vht_cap)); @@ -130,23 +130,26 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, if (!link_sta->pub->ht_cap.ht_supported) return; - if (!vht_cap_ie || !sband->vht_cap.vht_supported) + if (!vht_cap_ie || !own_vht_cap->vht_supported) return; - /* 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; + if (sband) { + /* Allow VHT if at least one channel on the sband supports 80 MHz */ + bool have_80mhz = false; - have_80mhz = true; - break; + 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) + return; } - if (!have_80mhz) - return; - /* * A VHT STA must support 40 MHz, but if we verify that here * then we break a few things - some APs (e.g. Netgear R6300v2 @@ -156,7 +159,7 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, vht_cap->vht_supported = true; - own_cap = sband->vht_cap; + own_cap = *own_vht_cap; /* * If user has specified capability overrides, take care * of that if the station we're setting up is the AP that diff --git a/net/wireless/chan.c b/net/wireless/chan.c index fa0764ede9c5..8b94c0de80ad 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -317,7 +317,7 @@ static bool cfg80211_valid_center_freq(u32 center, int step; /* We only do strict verification on 6 GHz */ - if (center < 5955 || center > 7115) + if (center < 5955 || center > 7215) return true; bw = nl80211_chan_width_to_mhz(width); @@ -325,7 +325,7 @@ static bool cfg80211_valid_center_freq(u32 center, return false; /* Validate that the channels bw is entirely within the 6 GHz band */ - if (center - bw / 2 < 5945 || center + bw / 2 > 7125) + if (center - bw / 2 < 5945 || center + bw / 2 > 7225) return false; /* With 320 MHz the permitted channels overlap */ @@ -816,6 +816,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_NAN_DATA: break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_UNSPECIFIED: @@ -939,6 +940,7 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) case NL80211_IFTYPE_P2P_DEVICE: /* Can NAN type be considered as beaconing interface? */ case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_WDS: diff --git a/net/wireless/core.c b/net/wireless/core.c index 23afc250bc10..6783e0672dcb 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.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-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -254,6 +254,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { + struct cfg80211_nan_local_sched empty_sched = {}; + lockdep_assert_held(&rdev->wiphy.mtx); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN)) @@ -262,6 +264,15 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, if (!wdev_running(wdev)) return; + /* + * If there is a scheduled update pending, mark it as canceled, so the + * empty schedule will be accepted + */ + wdev->u.nan.sched_update_pending = false; + + /* Unschedule all */ + cfg80211_nan_set_local_schedule(rdev, wdev, &empty_sched); + rdev_stop_nan(rdev, wdev); wdev->is_running = false; @@ -270,6 +281,47 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, rdev->opencount--; } +int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched) +{ + int ret; + + lockdep_assert_held(&rdev->wiphy.mtx); + + if (wdev->iftype != NL80211_IFTYPE_NAN || !wdev_running(wdev)) + return -EINVAL; + + if (wdev->u.nan.sched_update_pending) + return -EBUSY; + + ret = rdev_nan_set_local_sched(rdev, wdev, sched); + if (ret) + return ret; + + wdev->u.nan.sched_update_pending = sched->deferred; + + kfree(wdev->u.nan.chandefs); + wdev->u.nan.chandefs = NULL; + wdev->u.nan.n_channels = 0; + + if (!sched->n_channels) + return 0; + + wdev->u.nan.chandefs = kcalloc(sched->n_channels, + sizeof(*wdev->u.nan.chandefs), + GFP_KERNEL); + if (!wdev->u.nan.chandefs) + return -ENOMEM; + + for (int i = 0; i < sched->n_channels; i++) + wdev->u.nan.chandefs[i] = sched->nan_channels[i].chandef; + + wdev->u.nan.n_channels = sched->n_channels; + + return 0; +} + void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); @@ -277,16 +329,21 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) ASSERT_RTNL(); + /* + * Some netdev interfaces need to be closed before some non-netdev + * ones, i.e. NAN_DATA interfaces need to be closed before the NAN + * interface + */ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (wdev->netdev) { dev_close(wdev->netdev); continue; } + } - /* otherwise, check iftype */ - - guard(wiphy)(wiphy); + guard(wiphy)(wiphy); + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { switch (wdev->iftype) { case NL80211_IFTYPE_P2P_DEVICE: cfg80211_stop_p2p_device(rdev, wdev); @@ -344,6 +401,8 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) { if (wdev->nl_owner_dead) { + cfg80211_close_dependents(rdev, wdev); + if (wdev->netdev) dev_close(wdev->netdev); @@ -354,6 +413,21 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) } } +void cfg80211_close_dependents(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + ASSERT_RTNL(); + + if (wdev->iftype != NL80211_IFTYPE_NAN) + return; + + /* Close all NAN DATA interfaces */ + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) + dev_close(wdev->netdev); + } +} + static void cfg80211_destroy_iface_wk(struct work_struct *work) { struct cfg80211_registered_device *rdev; @@ -761,6 +835,10 @@ int wiphy_register(struct wiphy *wiphy) !(wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ))))) return -EINVAL; + if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN_DATA)) && + !wiphy->nan_capa.phy.ht.ht_supported)) + return -EINVAL; + if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))) return -EINVAL; @@ -1367,9 +1445,8 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, rdev->num_running_monitor_ifaces += num; } -void cfg80211_leave(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - int link_id) +void cfg80211_leave_locked(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int link_id) { struct net_device *dev = wdev->netdev; struct cfg80211_sched_scan_request *pos, *tmp; @@ -1420,6 +1497,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, break; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_NAN_DATA: /* nothing to do */ break; case NL80211_IFTYPE_UNSPECIFIED: @@ -1430,6 +1508,19 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, } } +void cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int link_id) +{ + ASSERT_RTNL(); + + /* NAN_DATA interfaces must be closed before stopping NAN */ + cfg80211_close_dependents(rdev, wdev); + + guard(wiphy)(&rdev->wiphy); + + cfg80211_leave_locked(rdev, wdev, link_id); +} + void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id, gfp_t gfp) { @@ -1445,6 +1536,9 @@ void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev, trace_cfg80211_stop_link(wiphy, wdev, link_id); + if (wdev->iftype == NL80211_IFTYPE_NAN) + return; + ev = kzalloc_obj(*ev, gfp); if (!ev) return; @@ -1595,10 +1689,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, } break; case NETDEV_GOING_DOWN: - scoped_guard(wiphy, &rdev->wiphy) { - cfg80211_leave(rdev, wdev, -1); + cfg80211_leave(rdev, wdev, -1); + scoped_guard(wiphy, &rdev->wiphy) cfg80211_remove_links(wdev); - } /* since we just did cfg80211_leave() nothing to do there */ cancel_work_sync(&wdev->disconnect_wk); cancel_work_sync(&wdev->pmsr_free_wk); @@ -1679,6 +1772,23 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, if (rfkill_blocked(rdev->wiphy.rfkill)) return notifier_from_errno(-ERFKILL); + + /* NAN_DATA interfaces require a running NAN interface */ + if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) { + struct wireless_dev *iter; + bool nan_started = false; + + list_for_each_entry(iter, &rdev->wiphy.wdev_list, list) { + if (iter->iftype == NL80211_IFTYPE_NAN && + wdev_running(iter)) { + nan_started = true; + break; + } + } + + if (!nan_started) + return notifier_from_errno(-ENOLINK); + } break; default: return NOTIFY_DONE; diff --git a/net/wireless/core.h b/net/wireless/core.h index 6cace846d7a3..ae2d56d3ad90 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -318,6 +318,9 @@ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev); +void cfg80211_close_dependents(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); + /* free object */ void cfg80211_dev_free(struct cfg80211_registered_device *rdev); @@ -541,6 +544,9 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); +void cfg80211_leave_locked(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int link_id); + void cfg80211_leave(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, int link_id); @@ -551,6 +557,10 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); +int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched); + struct cfg80211_internal_bss * cfg80211_bss_update(struct cfg80211_registered_device *rdev, struct cfg80211_internal_bss *tmp, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 5cd86253a62e..bd72317c4964 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-2025 Intel Corporation + * Copyright (C) 2019-2020, 2022-2026 Intel Corporation */ #include @@ -782,8 +782,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) rdev_crit_proto_stop(rdev, wdev); } - if (nlportid == wdev->ap_unexpected_nlportid) - wdev->ap_unexpected_nlportid = 0; + if (nlportid == wdev->unexpected_nlportid) + wdev->unexpected_nlportid = 0; } void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) @@ -933,12 +933,17 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, * cfg80211 doesn't track the stations */ break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + if (mgmt->u.action.category != + WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION) + err = -EOPNOTSUPP; + break; case NL80211_IFTYPE_P2P_DEVICE: /* * fall through, P2P device only supports * public action frames */ - case NL80211_IFTYPE_NAN: default: err = -EOPNOTSUPP; break; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e15cd26f3a79..f334cdef8958 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -333,6 +333,97 @@ static int validate_nan_cluster_id(const struct nlattr *attr, return 0; } +static int validate_nan_avail_blob(const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + const u8 *data = nla_data(attr); + unsigned int len = nla_len(attr); + u16 attr_len; + + /* Need at least: Attr ID (1) + Length (2) */ + if (len < 3) { + NL_SET_ERR_MSG_FMT(extack, + "NAN Availability: Too short (need at least 3 bytes, have %u)", + len); + return -EINVAL; + } + + if (data[0] != 0x12) { + NL_SET_ERR_MSG_FMT(extack, + "NAN Availability: Invalid Attribute ID 0x%02x (expected 0x12)", + data[0]); + return -EINVAL; + } + + attr_len = get_unaligned_le16(&data[1]); + + if (attr_len != len - 3) { + NL_SET_ERR_MSG_FMT(extack, + "NAN Availability: Length field (%u) doesn't match data length (%u)", + attr_len, len - 3); + return -EINVAL; + } + + return 0; +} + +static int validate_nan_ulw(const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + const u8 *data = nla_data(attr); + unsigned int len = nla_len(attr); + unsigned int pos = 0; + + while (pos < len) { + u16 attr_len; + + /* Need at least: Attr ID (1) + Length (2) */ + if (pos + 3 > len) { + NL_SET_ERR_MSG_FMT(extack, + "ULW: Incomplete header (need 3 bytes, have %u)", + len - pos); + return -EINVAL; + } + + if (data[pos] != 0x17) { + NL_SET_ERR_MSG_FMT(extack, + "ULW: Invalid Attribute ID 0x%02x (expected 0x17)", + data[pos]); + return -EINVAL; + } + pos++; + + /* Length is in little-endian format */ + attr_len = get_unaligned_le16(&data[pos]); + pos += 2; + + /* + * Check if length is one of the valid values: 16 (no + * channel/band entry included), 18 (band entry included), + * 21 (channel entry included without Auxiliary channel bitmap), + * or 23 (channel entry included with Auxiliary channel bitmap). + */ + if (attr_len != 16 && attr_len != 18 && attr_len != 21 && + attr_len != 23) { + NL_SET_ERR_MSG_FMT(extack, + "ULW: Invalid length %u (must be 16, 18, 21, or 23)", + attr_len); + return -EINVAL; + } + + if (pos + attr_len > len) { + NL_SET_ERR_MSG_FMT(extack, + "ULW: Length field (%u) exceeds remaining data (%u)", + attr_len, len - pos); + return -EINVAL; + } + + pos += attr_len; + } + + return 0; +} + static int validate_uhr_capa(const struct nlattr *attr, struct netlink_ext_ack *extack) { @@ -555,6 +646,13 @@ nl80211_nan_band_conf_policy[NL80211_NAN_BAND_CONF_ATTR_MAX + 1] = { [NL80211_NAN_BAND_CONF_DISABLE_SCAN] = { .type = NLA_FLAG }, }; +static const struct nla_policy +nl80211_nan_peer_map_policy[NL80211_NAN_PEER_MAP_ATTR_MAX + 1] = { + [NL80211_NAN_PEER_MAP_ATTR_MAP_ID] = NLA_POLICY_MAX(NLA_U8, 15), + [NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS] = + NLA_POLICY_EXACT_LEN(CFG80211_NAN_SCHED_NUM_TIME_SLOTS), +}; + static const struct nla_policy nl80211_nan_conf_policy[NL80211_NAN_CONF_ATTR_MAX + 1] = { [NL80211_NAN_CONF_CLUSTER_ID] = @@ -962,6 +1060,22 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_DISABLE_UHR] = { .type = NLA_FLAG }, [NL80211_ATTR_UHR_OPERATION] = NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_operation), + [NL80211_ATTR_NAN_CHANNEL] = NLA_POLICY_NESTED(nl80211_policy), + [NL80211_ATTR_NAN_CHANNEL_ENTRY] = NLA_POLICY_EXACT_LEN(6), + [NL80211_ATTR_NAN_RX_NSS] = { .type = NLA_U8 }, + [NL80211_ATTR_NAN_TIME_SLOTS] = + NLA_POLICY_EXACT_LEN(CFG80211_NAN_SCHED_NUM_TIME_SLOTS), + [NL80211_ATTR_NAN_AVAIL_BLOB] = + NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_avail_blob), + [NL80211_ATTR_NAN_SCHED_DEFERRED] = { .type = NLA_FLAG }, + [NL80211_ATTR_NAN_NMI_MAC] = NLA_POLICY_ETH_ADDR, + [NL80211_ATTR_NAN_ULW] = + NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_ulw), + [NL80211_ATTR_NAN_COMMITTED_DW] = { .type = NLA_U16 }, + [NL80211_ATTR_NAN_SEQ_ID] = { .type = NLA_U8 }, + [NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME] = { .type = NLA_U16 }, + [NL80211_ATTR_NAN_PEER_MAPS] = + NLA_POLICY_NESTED_ARRAY(nl80211_nan_peer_map_policy), }; /* policy for the key attributes */ @@ -1722,6 +1836,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) return 0; return -ENOLINK; case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: if (wiphy_ext_feature_isset(wdev->wiphy, NL80211_EXT_FEATURE_SECURE_NAN)) return 0; @@ -2678,6 +2793,68 @@ static int nl80211_put_radios(struct wiphy *wiphy, struct sk_buff *msg) return -ENOBUFS; } +static int nl80211_put_nan_phy_cap(struct wiphy *wiphy, struct sk_buff *msg) +{ + struct nlattr *nl_phy_cap; + const struct ieee80211_sta_ht_cap *ht_cap; + const struct ieee80211_sta_vht_cap *vht_cap; + const struct ieee80211_sta_he_cap *he_cap; + + if (!cfg80211_iftype_allowed(wiphy, NL80211_IFTYPE_NAN_DATA, false, 0)) + return 0; + + ht_cap = &wiphy->nan_capa.phy.ht; + vht_cap = &wiphy->nan_capa.phy.vht; + he_cap = &wiphy->nan_capa.phy.he; + + /* HT is mandatory */ + if (WARN_ON(!ht_cap->ht_supported)) + return 0; + + nl_phy_cap = nla_nest_start_noflag(msg, NL80211_NAN_CAPA_PHY); + if (!nl_phy_cap) + return -ENOBUFS; + + if (nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET, + sizeof(ht_cap->mcs), &ht_cap->mcs) || + nla_put_u16(msg, NL80211_NAN_PHY_CAP_ATTR_HT_CAPA, ht_cap->cap) || + nla_put_u8(msg, NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR, + ht_cap->ampdu_factor) || + nla_put_u8(msg, NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY, + ht_cap->ampdu_density)) + goto fail; + + if (vht_cap->vht_supported) { + if (nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET, + sizeof(vht_cap->vht_mcs), &vht_cap->vht_mcs) || + nla_put_u32(msg, NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA, + vht_cap->cap)) + goto fail; + } + + if (he_cap->has_he) { + if (nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_MAC, + sizeof(he_cap->he_cap_elem.mac_cap_info), + he_cap->he_cap_elem.mac_cap_info) || + nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_PHY, + sizeof(he_cap->he_cap_elem.phy_cap_info), + he_cap->he_cap_elem.phy_cap_info) || + nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET, + sizeof(he_cap->he_mcs_nss_supp), + &he_cap->he_mcs_nss_supp) || + nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_PPE, + sizeof(he_cap->ppe_thres), he_cap->ppe_thres)) + goto fail; + } + + nla_nest_end(msg, nl_phy_cap); + return 0; + +fail: + nla_nest_cancel(msg, nl_phy_cap); + return -ENOBUFS; +} + static int nl80211_put_nan_capa(struct wiphy *wiphy, struct sk_buff *msg) { struct nlattr *nan_caps; @@ -2704,6 +2881,9 @@ static int nl80211_put_nan_capa(struct wiphy *wiphy, struct sk_buff *msg) wiphy->nan_capa.dev_capabilities)) goto fail; + if (nl80211_put_nan_phy_cap(wiphy, msg)) + goto fail; + nla_nest_end(msg, nan_caps); return 0; @@ -4879,6 +5059,8 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) else dev_close(wdev->netdev); + cfg80211_close_dependents(rdev, wdev); + mutex_lock(&rdev->wiphy.mtx); return cfg80211_remove_virtual_intf(rdev, wdev); @@ -7123,6 +7305,26 @@ static int parse_station_flags(struct genl_info *info, if ((params->sta_flags_mask | params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) return -EINVAL; + + if ((iftype == NL80211_IFTYPE_NAN || + iftype == NL80211_IFTYPE_NAN_DATA) && + params->sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_MFP))) + return -EINVAL; + + /* WME is always used in NAN */ + if (iftype == NL80211_IFTYPE_NAN_DATA) { + /* but don't let userspace control it */ + if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME)) + return -EINVAL; + + params->sta_flags_mask |= BIT(NL80211_STA_FLAG_WME); + params->sta_flags_set |= BIT(NL80211_STA_FLAG_WME); + } + return 0; } @@ -8005,7 +8207,7 @@ static int nl80211_dump_station(struct sk_buff *skb, /* nl80211_prepare_wdev_dump acquired it in the successful case */ __acquire(&rdev->wiphy.mtx); - if (!wdev->netdev) { + if (!wdev->netdev && wdev->iftype != NL80211_IFTYPE_NAN) { err = -EINVAL; goto out_err; } @@ -8192,10 +8394,12 @@ int cfg80211_check_station_change(struct wiphy *wiphy, return -EINVAL; if (params->link_sta_params.supported_rates) return -EINVAL; - if (params->ext_capab || params->link_sta_params.ht_capa || - params->link_sta_params.vht_capa || - params->link_sta_params.he_capa || - params->link_sta_params.eht_capa || + if (statype != CFG80211_STA_NAN_MGMT && + (params->link_sta_params.ht_capa || + params->link_sta_params.vht_capa || + params->link_sta_params.he_capa)) + return -EINVAL; + if (params->ext_capab || params->link_sta_params.eht_capa || params->link_sta_params.uhr_capa) return -EINVAL; if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) @@ -8267,6 +8471,19 @@ int cfg80211_check_station_change(struct wiphy *wiphy, params->plink_action != NL80211_PLINK_ACTION_BLOCK) return -EINVAL; break; + case CFG80211_STA_NAN_MGMT: + if (params->sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_MFP))) + return -EINVAL; + break; + case CFG80211_STA_NAN_DATA: + if (params->sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_MFP) | + BIT(NL80211_STA_FLAG_WME))) + return -EINVAL; + break; } /* @@ -8481,7 +8698,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); - if (!dev) + if (!dev && wdev->iftype != NL80211_IFTYPE_NAN && + wdev->iftype != NL80211_IFTYPE_NAN_DATA) return -EINVAL; if (!rdev->ops->change_station) @@ -8624,6 +8842,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: break; default: err = -EOPNOTSUPP; @@ -8652,7 +8872,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); - if (!dev) + if (!dev && wdev->iftype != NL80211_IFTYPE_NAN) return -EINVAL; if (!rdev->ops->add_station) @@ -8661,15 +8881,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; - if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) - return -EINVAL; + if (wdev->iftype == NL80211_IFTYPE_NAN || + wdev->iftype == NL80211_IFTYPE_NAN_DATA) { + if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) + return -EINVAL; + if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) { + if (!info->attrs[NL80211_ATTR_NAN_NMI_MAC]) + return -EINVAL; - if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) - return -EINVAL; + /* Only NMI stations receive the HT/VHT/HE capabilities */ + if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || + info->attrs[NL80211_ATTR_VHT_CAPABILITY] || + info->attrs[NL80211_ATTR_HE_CAPABILITY]) + return -EINVAL; + } + } else { + if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) + return -EINVAL; - if (!info->attrs[NL80211_ATTR_STA_AID] && - !info->attrs[NL80211_ATTR_PEER_AID]) - return -EINVAL; + if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_STA_AID] && + !info->attrs[NL80211_ATTR_PEER_AID]) + return -EINVAL; + } params.link_sta_params.link_id = nl80211_link_id_or_invalid(info->attrs); @@ -8685,12 +8921,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); } - params.link_sta_params.supported_rates = - nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.link_sta_params.supported_rates_len = - nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.listen_interval = - nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); + if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { + params.link_sta_params.supported_rates = + nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); + params.link_sta_params.supported_rates_len = + nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); + } + + if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) + params.listen_interval = + nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); if (info->attrs[NL80211_ATTR_VLAN_ID]) params.vlan_id = nla_get_u16(info->attrs[NL80211_ATTR_VLAN_ID]); @@ -8709,7 +8949,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_PEER_AID]) params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); - else + else if (info->attrs[NL80211_ATTR_STA_AID]) params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { @@ -8830,6 +9070,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + if (wdev->iftype == NL80211_IFTYPE_NAN || + wdev->iftype == NL80211_IFTYPE_NAN_DATA) { + if (params.sta_modify_mask & STATION_PARAM_APPLY_UAPSD) + return -EINVAL; + /* NAN NMI station must be added in associated or authorized state */ + if (!(params.sta_flags_set & (BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_AUTHENTICATED)))) + return -EINVAL; + } + /* Ensure that HT/VHT capabilities are not set for 6 GHz HE STA */ if (params.link_sta_params.he_6ghz_capa && (params.link_sta_params.ht_capa || params.link_sta_params.vht_capa)) @@ -8922,6 +9172,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED); break; + case NL80211_IFTYPE_NAN: + break; + case NL80211_IFTYPE_NAN_DATA: + params.nmi_mac = nla_data(info->attrs[NL80211_ATTR_NAN_NMI_MAC]); + break; default: return -EOPNOTSUPP; } @@ -8963,7 +9218,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); - if (!dev) + if (!dev && wdev->iftype != NL80211_IFTYPE_NAN) return -EINVAL; if (info->attrs[NL80211_ATTR_MAC]) @@ -8974,6 +9229,8 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: /* always accept these */ break; case NL80211_IFTYPE_ADHOC: @@ -13950,6 +14207,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_P2P_DEVICE: break; case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: if (!wiphy_ext_feature_isset(wdev->wiphy, NL80211_EXT_FEATURE_SECURE_NAN) && !(wdev->wiphy->nan_capa.flags & @@ -14013,6 +14271,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_P2P_GO: break; case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: if (!wiphy_ext_feature_isset(wdev->wiphy, NL80211_EXT_FEATURE_SECURE_NAN) && !(wdev->wiphy->nan_capa.flags & @@ -15520,13 +15779,14 @@ static int nl80211_register_unexpected_frame(struct sk_buff *skb, struct wireless_dev *wdev = dev->ieee80211_ptr; if (wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO) + wdev->iftype != NL80211_IFTYPE_P2P_GO && + wdev->iftype != NL80211_IFTYPE_NAN_DATA) return -EINVAL; - if (wdev->ap_unexpected_nlportid) + if (wdev->unexpected_nlportid) return -EBUSY; - wdev->ap_unexpected_nlportid = info->snd_portid; + wdev->unexpected_nlportid = info->snd_portid; return 0; } @@ -15922,6 +16182,10 @@ static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info) if (wdev->iftype != NL80211_IFTYPE_NAN) return -EOPNOTSUPP; + cfg80211_close_dependents(rdev, wdev); + + guard(wiphy)(&rdev->wiphy); + cfg80211_stop_nan(rdev, wdev); return 0; @@ -16421,6 +16685,482 @@ void cfg80211_nan_func_terminated(struct wireless_dev *wdev, } EXPORT_SYMBOL(cfg80211_nan_func_terminated); +void cfg80211_nan_sched_update_done(struct wireless_dev *wdev, bool success, + gfp_t gfp) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct sk_buff *msg; + void *hdr; + + trace_cfg80211_nan_sched_update_done(wiphy, wdev, success); + + /* Can happen if we stopped NAN */ + if (!wdev->u.nan.sched_update_pending) + return; + + wdev->u.nan.sched_update_pending = false; + + if (!wdev->owner_nlportid) + return; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_SCHED_UPDATE_DONE); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), + NL80211_ATTR_PAD) || + (success && + nla_put_flag(msg, NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS))) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid); + + return; + +nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_nan_sched_update_done); + +static int nl80211_parse_nan_channel(struct cfg80211_registered_device *rdev, + struct nlattr *channel, + struct genl_info *info, + struct cfg80211_nan_channel *nan_channels, + u8 index, bool local) +{ + struct nlattr **channel_parsed __free(kfree) = NULL; + struct cfg80211_chan_def chandef; + u8 n_rx_nss; + int ret; + + channel_parsed = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*channel_parsed), + GFP_KERNEL); + if (!channel_parsed) + return -ENOMEM; + + ret = nla_parse_nested(channel_parsed, NL80211_ATTR_MAX, channel, NULL, + info->extack); + if (ret) + return ret; + + ret = nl80211_parse_chandef(rdev, info->extack, channel_parsed, + &chandef); + if (ret) + return ret; + + if (chandef.chan->band == NL80211_BAND_6GHZ) { + NL_SET_ERR_MSG(info->extack, + "6 GHz band is not supported"); + return -EOPNOTSUPP; + } + + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, + NL80211_IFTYPE_NAN)) { + NL_SET_ERR_MSG_ATTR(info->extack, channel, + "Channel in NAN schedule is not allowed for NAN operation"); + return -EINVAL; + } + + if (local) { + for (int i = 0; i < index; i++) { + if (cfg80211_chandef_compatible(&nan_channels[i].chandef, + &chandef)) { + NL_SET_ERR_MSG_ATTR(info->extack, channel, + "Channels in NAN schedule must be mutually incompatible"); + return -EINVAL; + } + } + } + + if (!channel_parsed[NL80211_ATTR_NAN_CHANNEL_ENTRY]) { + NL_SET_ERR_MSG(info->extack, + "Missing NAN channel entry attribute"); + return -EINVAL; + } + + nan_channels[index].channel_entry = + nla_data(channel_parsed[NL80211_ATTR_NAN_CHANNEL_ENTRY]); + + if (!channel_parsed[NL80211_ATTR_NAN_RX_NSS]) { + NL_SET_ERR_MSG(info->extack, + "Missing NAN RX NSS attribute"); + return -EINVAL; + } + + nan_channels[index].rx_nss = + nla_get_u8(channel_parsed[NL80211_ATTR_NAN_RX_NSS]); + + n_rx_nss = u8_get_bits(rdev->wiphy.nan_capa.n_antennas, 0x03); + if ((local && nan_channels[index].rx_nss > n_rx_nss) || + !nan_channels[index].rx_nss) { + NL_SET_ERR_MSG_ATTR(info->extack, channel, + "Invalid RX NSS in NAN channel definition"); + return -EINVAL; + } + + nan_channels[index].chandef = chandef; + + return 0; +} + +static int +nl80211_parse_nan_schedule(struct genl_info *info, struct nlattr *slots_attr, + u8 schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS], + u8 n_channels) +{ + if (WARN_ON(nla_len(slots_attr) != CFG80211_NAN_SCHED_NUM_TIME_SLOTS)) + return -EINVAL; + + memcpy(schedule, nla_data(slots_attr), nla_len(slots_attr)); + + for (int slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; slot++) { + if (schedule[slot] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT && + schedule[slot] >= n_channels) { + NL_SET_ERR_MSG_FMT(info->extack, + "Invalid time slot: slot %d refers to channel index %d, n_channels=%d", + slot, schedule[slot], n_channels); + return -EINVAL; + } + } + + return 0; +} + +static int +nl80211_parse_nan_peer_map(struct genl_info *info, struct nlattr *map_attr, + struct cfg80211_nan_peer_map *map, u8 n_channels) +{ + struct nlattr *tb[NL80211_NAN_PEER_MAP_ATTR_MAX + 1]; + int ret; + + ret = nla_parse_nested(tb, NL80211_NAN_PEER_MAP_ATTR_MAX, map_attr, + nl80211_nan_peer_map_policy, info->extack); + if (ret) + return ret; + + if (!tb[NL80211_NAN_PEER_MAP_ATTR_MAP_ID] || + !tb[NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS]) { + NL_SET_ERR_MSG(info->extack, + "Missing required peer map attributes"); + return -EINVAL; + } + + map->map_id = nla_get_u8(tb[NL80211_NAN_PEER_MAP_ATTR_MAP_ID]); + + /* Parse schedule */ + return nl80211_parse_nan_schedule(info, + tb[NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS], + map->schedule, n_channels); +} + +static int nl80211_nan_validate_map_pair(struct wiphy *wiphy, + struct genl_info *info, + const struct cfg80211_nan_peer_map *map1, + const struct cfg80211_nan_peer_map *map2, + struct cfg80211_nan_channel *nan_channels) +{ + /* Check for duplicate map_id */ + if (map1->map_id == map2->map_id) { + NL_SET_ERR_MSG_FMT(info->extack, "Duplicate map_id %u", + map1->map_id); + return -EINVAL; + } + + /* Check for compatible channels between maps */ + for (int i = 0; i < ARRAY_SIZE(map1->schedule); i++) { + if (map1->schedule[i] == NL80211_NAN_SCHED_NOT_AVAIL_SLOT) + continue; + + for (int j = 0; j < ARRAY_SIZE(map2->schedule); j++) { + u8 ch1 = map1->schedule[i]; + u8 ch2 = map2->schedule[j]; + + if (ch2 == NL80211_NAN_SCHED_NOT_AVAIL_SLOT) + continue; + + if (cfg80211_chandef_compatible(&nan_channels[ch1].chandef, + &nan_channels[ch2].chandef)) { + NL_SET_ERR_MSG_FMT(info->extack, + "Maps %u and %u have compatible channels %d and %d", + map1->map_id, map2->map_id, + ch1, ch2); + return -EINVAL; + } + } + } + + /* + * Check for conflicting time slots between maps. + * Only check for single-radio devices (n_radio <= 1) which cannot + * operate on multiple channels simultaneously. + */ + if (wiphy->n_radio > 1) + return 0; + + for (int i = 0; i < ARRAY_SIZE(map1->schedule); i++) { + if (map1->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT && + map2->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT) { + NL_SET_ERR_MSG_FMT(info->extack, + "Maps %u and %u both schedule slot %d", + map1->map_id, map2->map_id, i); + return -EINVAL; + } + } + + return 0; +} + +static int nl80211_nan_set_peer_sched(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_nan_channel *nan_channels __free(kfree) = NULL; + struct cfg80211_nan_peer_sched sched = {}; + struct wireless_dev *wdev = info->user_ptr[1]; + struct nlattr *map_attr, *channel; + int ret, n_maps = 0, n_channels = 0, i = 0, rem; + + if (wdev->iftype != NL80211_IFTYPE_NAN) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_MAC] || + !info->attrs[NL80211_ATTR_NAN_COMMITTED_DW]) { + NL_SET_ERR_MSG(info->extack, + "Required NAN peer schedule attributes are missing"); + return -EINVAL; + } + + /* First count how many channel attributes we got */ + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) + n_channels++; + + if (!((info->attrs[NL80211_ATTR_NAN_SEQ_ID] && + info->attrs[NL80211_ATTR_NAN_PEER_MAPS] && n_channels) || + ((!info->attrs[NL80211_ATTR_NAN_SEQ_ID] && + !info->attrs[NL80211_ATTR_NAN_PEER_MAPS] && !n_channels)))) { + NL_SET_ERR_MSG(info->extack, + "Either provide all of: seq id, channels and maps, or none"); + return -EINVAL; + } + + /* + * Limit the number of peer channels to: + * local_channels * 4 (possible BWs) * 2 (possible NSS values) + */ + if (n_channels && n_channels > wdev->u.nan.n_channels * 4 * 2) { + NL_SET_ERR_MSG_FMT(info->extack, + "Too many peer channels: %d (max %d)", + n_channels, + wdev->u.nan.n_channels * 4 * 2); + return -EINVAL; + } + + if (n_channels) { + nan_channels = kcalloc(n_channels, sizeof(*nan_channels), + GFP_KERNEL); + if (!nan_channels) + return -ENOMEM; + } + + /* Parse peer channels */ + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) { + bool compatible = false; + + ret = nl80211_parse_nan_channel(rdev, channel, info, + nan_channels, i, false); + if (ret) + return ret; + + /* Verify channel is compatible with at least one local channel */ + for (int j = 0; j < wdev->u.nan.n_channels; j++) { + if (cfg80211_chandef_compatible(&nan_channels[i].chandef, + &wdev->u.nan.chandefs[j])) { + compatible = true; + break; + } + } + if (!compatible) { + NL_SET_ERR_MSG_FMT(info->extack, + "Channel %d not compatible with any local channel", + i); + return -EINVAL; + } + i++; + } + + sched.n_channels = n_channels; + sched.nan_channels = nan_channels; + sched.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + sched.seq_id = nla_get_u8_default(info->attrs[NL80211_ATTR_NAN_SEQ_ID], 0); + sched.committed_dw = nla_get_u16(info->attrs[NL80211_ATTR_NAN_COMMITTED_DW]); + sched.max_chan_switch = + nla_get_u16_default(info->attrs[NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME], 0); + + if (info->attrs[NL80211_ATTR_NAN_ULW]) { + sched.ulw_size = nla_len(info->attrs[NL80211_ATTR_NAN_ULW]); + sched.init_ulw = nla_data(info->attrs[NL80211_ATTR_NAN_ULW]); + } + + /* Initialize all maps as invalid */ + for (int j = 0; j < ARRAY_SIZE(sched.maps); j++) + sched.maps[j].map_id = CFG80211_NAN_INVALID_MAP_ID; + + if (info->attrs[NL80211_ATTR_NAN_PEER_MAPS]) { + /* Parse each map */ + nla_for_each_nested(map_attr, info->attrs[NL80211_ATTR_NAN_PEER_MAPS], + rem) { + if (n_maps >= ARRAY_SIZE(sched.maps)) { + NL_SET_ERR_MSG(info->extack, "Too many peer maps"); + return -EINVAL; + } + + ret = nl80211_parse_nan_peer_map(info, map_attr, + &sched.maps[n_maps], + n_channels); + if (ret) + return ret; + + /* Validate against previous maps */ + for (int j = 0; j < n_maps; j++) { + ret = nl80211_nan_validate_map_pair(&rdev->wiphy, info, + &sched.maps[j], + &sched.maps[n_maps], + nan_channels); + if (ret) + return ret; + } + + n_maps++; + } + } + + /* Verify each channel is scheduled at least once */ + for (int ch = 0; ch < n_channels; ch++) { + bool scheduled = false; + + for (int m = 0; m < n_maps && !scheduled; m++) { + for (int s = 0; s < ARRAY_SIZE(sched.maps[m].schedule); s++) { + if (sched.maps[m].schedule[s] == ch) { + scheduled = true; + break; + } + } + } + if (!scheduled) { + NL_SET_ERR_MSG_FMT(info->extack, + "Channel %d is not scheduled in any map", + ch); + return -EINVAL; + } + } + + return rdev_nan_set_peer_sched(rdev, wdev, &sched); +} + +static bool nl80211_nan_is_sched_empty(struct cfg80211_nan_local_sched *sched) +{ + if (!sched->n_channels) + return true; + + for (int i = 0; i < ARRAY_SIZE(sched->schedule); i++) { + if (sched->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT) + return false; + } + + return true; +} + +static int nl80211_nan_set_local_sched(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_nan_local_sched *sched __free(kfree) = NULL; + struct wireless_dev *wdev = info->user_ptr[1]; + int rem, i = 0, n_channels = 0, ret; + struct nlattr *channel; + bool sched_empty; + + if (wdev->iftype != NL80211_IFTYPE_NAN) + return -EOPNOTSUPP; + + if (!wdev_running(wdev)) + return -ENOTCONN; + + if (!info->attrs[NL80211_ATTR_NAN_TIME_SLOTS]) + return -EINVAL; + + /* First count how many channel attributes we got */ + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) + n_channels++; + + sched = kzalloc(struct_size(sched, nan_channels, n_channels), + GFP_KERNEL); + if (!sched) + return -ENOMEM; + + sched->n_channels = n_channels; + + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) { + ret = nl80211_parse_nan_channel(rdev, channel, info, + sched->nan_channels, i, true); + + if (ret) + return ret; + i++; + } + + /* Parse and validate schedule */ + ret = nl80211_parse_nan_schedule(info, + info->attrs[NL80211_ATTR_NAN_TIME_SLOTS], + sched->schedule, sched->n_channels); + if (ret) + return ret; + + sched_empty = nl80211_nan_is_sched_empty(sched); + + sched->deferred = + nla_get_flag(info->attrs[NL80211_ATTR_NAN_SCHED_DEFERRED]); + + if (sched_empty) { + if (sched->deferred) { + NL_SET_ERR_MSG(info->extack, + "Schedule cannot be deferred if all time slots are unavailable"); + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]) { + NL_SET_ERR_MSG(info->extack, + "NAN Availability blob must be empty if all time slots are unavailable"); + return -EINVAL; + } + } else { + if (!info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]) { + NL_SET_ERR_MSG(info->extack, + "NAN Availability blob attribute is required"); + return -EINVAL; + } + + sched->nan_avail_blob = + nla_data(info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]); + sched->nan_avail_blob_len = + nla_len(info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]); + } + + return cfg80211_nan_set_local_schedule(rdev, wdev, sched); +} + static int nl80211_get_protocol_features(struct sk_buff *skb, struct genl_info *info) { @@ -18096,7 +18836,11 @@ nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *info) NL80211_FLAG_NEED_RTNL) \ SELECTOR(__sel, WIPHY_CLEAR, \ NL80211_FLAG_NEED_WIPHY | \ - NL80211_FLAG_CLEAR_SKB) + NL80211_FLAG_CLEAR_SKB) \ + SELECTOR(__sel, WDEV_UP_RTNL_NOMTX, \ + NL80211_FLAG_NEED_WDEV_UP | \ + NL80211_FLAG_NO_WIPHY_MTX | \ + NL80211_FLAG_NEED_RTNL) enum nl80211_internal_flags_selector { #define SELECTOR(_, name, value) NL80211_IFL_SEL_##name, @@ -18933,6 +19677,7 @@ static const struct genl_small_ops nl80211_small_ops[] = { .doit = nl80211_stop_nan, .flags = GENL_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | + NL80211_FLAG_NO_WIPHY_MTX | NL80211_FLAG_NEED_RTNL), }, { @@ -19227,6 +19972,18 @@ static const struct genl_small_ops nl80211_small_ops[] = { .flags = GENL_UNS_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), }, + { + .cmd = NL80211_CMD_NAN_SET_LOCAL_SCHED, + .doit = nl80211_nan_set_local_sched, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), + }, + { + .cmd = NL80211_CMD_NAN_SET_PEER_SCHED, + .doit = nl80211_nan_set_peer_sched, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), + }, }; static struct genl_family nl80211_fam __ro_after_init = { @@ -20527,7 +21284,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct sk_buff *msg; void *hdr; - u32 nlportid = READ_ONCE(wdev->ap_unexpected_nlportid); + u32 nlportid = READ_ONCE(wdev->unexpected_nlportid); if (!nlportid) return false; @@ -20567,7 +21324,8 @@ bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr, trace_cfg80211_rx_spurious_frame(dev, addr, link_id); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) { + wdev->iftype != NL80211_IFTYPE_P2P_GO && + wdev->iftype != NL80211_IFTYPE_NAN_DATA)) { trace_cfg80211_return_bool(false); return false; } @@ -22137,6 +22895,97 @@ void cfg80211_nan_cluster_joined(struct wireless_dev *wdev, } EXPORT_SYMBOL(cfg80211_nan_cluster_joined); +void cfg80211_nan_ulw_update(struct wireless_dev *wdev, + const u8 *ulw, size_t ulw_len, gfp_t gfp) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct sk_buff *msg; + void *hdr; + + trace_cfg80211_nan_ulw_update(wiphy, wdev, ulw, ulw_len); + + if (!wdev->owner_nlportid) + return; + + /* 32 for the wiphy idx, 64 for the wdev id, 100 for padding */ + msg = nlmsg_new(nla_total_size(sizeof(u32)) + + nla_total_size(ulw_len) + + nla_total_size(sizeof(u64)) + 100, + gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_ULW_UPDATE); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), + NL80211_ATTR_PAD) || + (ulw && ulw_len && + nla_put(msg, NL80211_ATTR_NAN_ULW, ulw_len, ulw))) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid); + + return; + + nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_nan_ulw_update); + +void cfg80211_nan_channel_evac(struct wireless_dev *wdev, + const struct cfg80211_chan_def *chandef, + gfp_t gfp) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct sk_buff *msg; + struct nlattr *chan_attr; + void *hdr; + + trace_cfg80211_nan_channel_evac(wiphy, wdev, chandef); + + if (!wdev->owner_nlportid) + return; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_CHANNEL_EVAC); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), + NL80211_ATTR_PAD)) + goto nla_put_failure; + + chan_attr = nla_nest_start(msg, NL80211_ATTR_NAN_CHANNEL); + if (!chan_attr) + goto nla_put_failure; + + if (nl80211_send_chandef(msg, chandef)) + goto nla_put_failure; + + nla_nest_end(msg, chan_attr); + + genlmsg_end(msg, hdr); + + genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid); + + return; + + nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_nan_channel_evac); + /* initialisation/exit functions */ int __init nl80211_init(void) diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 2bad8b60b7c9..bba239a068f6 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1060,6 +1060,38 @@ rdev_nan_change_conf(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_nan_set_local_sched(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched) +{ + int ret; + + trace_rdev_nan_set_local_sched(&rdev->wiphy, wdev, sched); + if (rdev->ops->nan_set_local_sched) + ret = rdev->ops->nan_set_local_sched(&rdev->wiphy, wdev, sched); + else + ret = -EOPNOTSUPP; + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_nan_set_peer_sched(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_peer_sched *sched) +{ + int ret; + + trace_rdev_nan_set_peer_sched(&rdev->wiphy, wdev, sched); + if (rdev->ops->nan_set_peer_sched) + ret = rdev->ops->nan_set_peer_sched(&rdev->wiphy, wdev, sched); + else + ret = -EOPNOTSUPP; + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_acl_data *params) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 20bba7e491c5..5db2121c0b57 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2348,6 +2348,18 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) if (!wdev->netdev || !netif_running(wdev->netdev)) return true; + /* NAN doesn't have links, handle it separately */ + if (iftype == NL80211_IFTYPE_NAN) { + for (int i = 0; i < wdev->u.nan.n_channels; i++) { + ret = cfg80211_reg_can_beacon(wiphy, + &wdev->u.nan.chandefs[i], + NL80211_IFTYPE_NAN); + if (!ret) + return false; + } + return true; + } + for (link = 0; link < ARRAY_SIZE(wdev->links); link++) { struct ieee80211_channel *chan; @@ -2397,9 +2409,9 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) continue; chandef = wdev->u.ocb.chandef; break; - case NL80211_IFTYPE_NAN: - /* we have no info, but NAN is also pretty universal */ - continue; + case NL80211_IFTYPE_NAN_DATA: + /* NAN channels are checked in NL80211_IFTYPE_NAN interface */ + break; default: /* others not implemented for now */ WARN_ON_ONCE(1); @@ -2436,11 +2448,14 @@ static void reg_leave_invalid_chans(struct wiphy *wiphy) struct wireless_dev *wdev; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - guard(wiphy)(wiphy); + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + bool valid; - list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) - if (!reg_wdev_chan_valid(wiphy, wdev)) + scoped_guard(wiphy, wiphy) + valid = reg_wdev_chan_valid(wiphy, wdev); + if (!valid) cfg80211_leave(rdev, wdev, -1); + } } static void reg_check_chans_work(struct work_struct *work) diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 3385a27468f7..d45ddc457c30 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -102,25 +102,26 @@ static int wiphy_suspend(struct device *dev) if (!rdev->wiphy.registered) goto out_unlock_rtnl; - wiphy_lock(&rdev->wiphy); if (rdev->wiphy.wowlan_config) { - cfg80211_process_wiphy_works(rdev, NULL); - if (rdev->ops->suspend) - ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config); - if (ret <= 0) - goto out_unlock_wiphy; + scoped_guard(wiphy, &rdev->wiphy) { + cfg80211_process_wiphy_works(rdev, NULL); + if (rdev->ops->suspend) + ret = rdev_suspend(rdev, + rdev->wiphy.wowlan_config); + if (ret <= 0) + goto out_unlock_rtnl; + } } /* Driver refused to configure wowlan (ret = 1) or no wowlan */ cfg80211_leave_all(rdev); - cfg80211_process_rdev_events(rdev); - cfg80211_process_wiphy_works(rdev, NULL); - if (rdev->ops->suspend) - ret = rdev_suspend(rdev, NULL); - -out_unlock_wiphy: - wiphy_unlock(&rdev->wiphy); + scoped_guard(wiphy, &rdev->wiphy) { + cfg80211_process_rdev_events(rdev); + cfg80211_process_wiphy_works(rdev, NULL); + if (rdev->ops->suspend) + ret = rdev_suspend(rdev, NULL); + } out_unlock_rtnl: if (ret == 0) rdev->suspended = true; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index af23f4fca90a..eb5bedf9c92a 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2410,6 +2410,55 @@ TRACE_EVENT(rdev_del_nan_func, WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie) ); +TRACE_EVENT(rdev_nan_set_local_sched, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched), + TP_ARGS(wiphy, wdev, sched), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __array(u8, schedule, CFG80211_NAN_SCHED_NUM_TIME_SLOTS) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + memcpy(__entry->schedule, sched->schedule, + CFG80211_NAN_SCHED_NUM_TIME_SLOTS); + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", schedule: %s", + WIPHY_PR_ARG, WDEV_PR_ARG, + __print_array(__entry->schedule, + CFG80211_NAN_SCHED_NUM_TIME_SLOTS, 1)) +); + +TRACE_EVENT(rdev_nan_set_peer_sched, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_nan_peer_sched *sched), + TP_ARGS(wiphy, wdev, sched), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __array(u8, peer_addr, ETH_ALEN) + __field(u8, seq_id) + __field(u16, committed_dw) + __field(u16, max_chan_switch) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + memcpy(__entry->peer_addr, sched->peer_addr, ETH_ALEN); + __entry->seq_id = sched->seq_id; + __entry->committed_dw = sched->committed_dw; + __entry->max_chan_switch = sched->max_chan_switch; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT + ", peer: %pM, seq_id: %u, committed_dw: 0x%x, max_chan_switch: %u", + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->peer_addr, + __entry->seq_id, __entry->committed_dw, + __entry->max_chan_switch + ) +); + TRACE_EVENT(rdev_set_mac_acl, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_acl_data *params), @@ -4276,6 +4325,62 @@ TRACE_EVENT(cfg80211_incumbent_signal_notify, TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", signal_interference_bitmap=0x%x", WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->signal_interference_bitmap) ); + +TRACE_EVENT(cfg80211_nan_sched_update_done, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, bool success), + TP_ARGS(wiphy, wdev, success), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(bool, success) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->success = success; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT " success=%d", + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->success) +); + +TRACE_EVENT(cfg80211_nan_ulw_update, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + const u8 *ulw, size_t ulw_len), + TP_ARGS(wiphy, wdev, ulw, ulw_len), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __dynamic_array(u8, ulw, ulw_len) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + if (ulw && ulw_len) + memcpy(__get_dynamic_array(ulw), ulw, ulw_len); + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT " ulw: %s", + WIPHY_PR_ARG, WDEV_PR_ARG, + __print_array(__get_dynamic_array(ulw), + __get_dynamic_array_len(ulw), 1)) +); + +TRACE_EVENT(cfg80211_nan_channel_evac, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + const struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, wdev, chandef), + TP_STRUCT__entry( + WDEV_ENTRY + WIPHY_ENTRY + CHAN_DEF_ENTRY + ), + TP_fast_assign( + WDEV_ASSIGN; + WIPHY_ASSIGN; + CHAN_DEF_ASSIGN(chandef); + ), + TP_printk(WDEV_PR_FMT ", " WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, + WDEV_PR_ARG, WIPHY_PR_ARG, CHAN_DEF_PR_ARG) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH diff --git a/net/wireless/util.c b/net/wireless/util.c index 0a0cea018fc5..cff5a1bd95cc 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -90,7 +90,7 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band) /* see 802.11ax D6.1 27.3.23.2 */ if (chan == 2) return MHZ_TO_KHZ(5935); - if (chan <= 233) + if (chan <= 253) return MHZ_TO_KHZ(5950 + chan * 5); break; case NL80211_BAND_60GHZ: @@ -625,8 +625,9 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, case cpu_to_le16(0): if (iftype != NL80211_IFTYPE_ADHOC && iftype != NL80211_IFTYPE_STATION && - iftype != NL80211_IFTYPE_OCB) - return -1; + iftype != NL80211_IFTYPE_OCB && + iftype != NL80211_IFTYPE_NAN_DATA) + return -1; break; } @@ -1144,8 +1145,15 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) ev->ij.channel); break; case EVENT_STOPPED: - cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev, - ev->link_id); + /* + * for NAN interfaces cfg80211_leave must be called but + * locking here doesn't allow this. + */ + if (WARN_ON(wdev->iftype == NL80211_IFTYPE_NAN)) + break; + + cfg80211_leave_locked(wiphy_to_rdev(wdev->wiphy), wdev, + ev->link_id); break; case EVENT_PORT_AUTHORIZED: __cfg80211_port_authorized(wdev, ev->pa.peer_addr, @@ -1184,6 +1192,13 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, if (otype == NL80211_IFTYPE_AP_VLAN) return -EOPNOTSUPP; + /* + * for NAN interfaces cfg80211_leave must be called for leaving, + * but locking here doesn't allow this. + */ + if (otype == NL80211_IFTYPE_NAN) + return -EOPNOTSUPP; + /* cannot change into P2P device or NAN */ if (ntype == NL80211_IFTYPE_P2P_DEVICE || ntype == NL80211_IFTYPE_NAN) @@ -1204,7 +1219,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, dev->ieee80211_ptr->use_4addr = false; rdev_set_qos_map(rdev, dev, NULL); - cfg80211_leave(rdev, dev->ieee80211_ptr, -1); + cfg80211_leave_locked(rdev, dev->ieee80211_ptr, -1); cfg80211_process_rdev_events(rdev); cfg80211_mlme_purge_registrations(dev->ieee80211_ptr); @@ -1232,6 +1247,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_NAN_DATA: dev->priv_flags |= IFF_DONT_BRIDGE; break; case NL80211_IFTYPE_P2P_GO: