// SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" #include "peer.h" #include "htc.h" #include "dp_htt.h" #include "debugfs_htt_stats.h" #include "debugfs.h" static void ath12k_dp_htt_htc_tx_complete(struct ath12k_base *ab, struct sk_buff *skb) { dev_kfree_skb_any(skb); } int ath12k_dp_htt_connect(struct ath12k_dp *dp) { struct ath12k_htc_svc_conn_req conn_req = {}; struct ath12k_htc_svc_conn_resp conn_resp = {}; int status; conn_req.ep_ops.ep_tx_complete = ath12k_dp_htt_htc_tx_complete; conn_req.ep_ops.ep_rx_complete = ath12k_dp_htt_htc_t2h_msg_handler; /* connect to control service */ conn_req.service_id = ATH12K_HTC_SVC_ID_HTT_DATA_MSG; status = ath12k_htc_connect_service(&dp->ab->htc, &conn_req, &conn_resp); if (status) return status; dp->eid = conn_resp.eid; return 0; } static int ath12k_get_ppdu_user_index(struct htt_ppdu_stats *ppdu_stats, u16 peer_id) { int i; for (i = 0; i < HTT_PPDU_STATS_MAX_USERS - 1; i++) { if (ppdu_stats->user_stats[i].is_valid_peer_id) { if (peer_id == ppdu_stats->user_stats[i].peer_id) return i; } else { return i; } } return -EINVAL; } static int ath12k_htt_tlv_ppdu_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *ptr, void *data) { const struct htt_ppdu_stats_usr_cmpltn_ack_ba_status *ba_status; const struct htt_ppdu_stats_usr_cmpltn_cmn *cmplt_cmn; const struct htt_ppdu_stats_user_rate *user_rate; struct htt_ppdu_stats_info *ppdu_info; struct htt_ppdu_user_stats *user_stats; int cur_user; u16 peer_id; ppdu_info = data; switch (tag) { case HTT_PPDU_STATS_TAG_COMMON: if (len < sizeof(struct htt_ppdu_stats_common)) { ath12k_warn(ab, "Invalid len %d for the tag 0x%x\n", len, tag); return -EINVAL; } memcpy(&ppdu_info->ppdu_stats.common, ptr, sizeof(struct htt_ppdu_stats_common)); break; case HTT_PPDU_STATS_TAG_USR_RATE: if (len < sizeof(struct htt_ppdu_stats_user_rate)) { ath12k_warn(ab, "Invalid len %d for the tag 0x%x\n", len, tag); return -EINVAL; } user_rate = ptr; peer_id = le16_to_cpu(user_rate->sw_peer_id); cur_user = ath12k_get_ppdu_user_index(&ppdu_info->ppdu_stats, peer_id); if (cur_user < 0) return -EINVAL; user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; user_stats->peer_id = peer_id; user_stats->is_valid_peer_id = true; memcpy(&user_stats->rate, ptr, sizeof(struct htt_ppdu_stats_user_rate)); user_stats->tlv_flags |= BIT(tag); break; case HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON: if (len < sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn)) { ath12k_warn(ab, "Invalid len %d for the tag 0x%x\n", len, tag); return -EINVAL; } cmplt_cmn = ptr; peer_id = le16_to_cpu(cmplt_cmn->sw_peer_id); cur_user = ath12k_get_ppdu_user_index(&ppdu_info->ppdu_stats, peer_id); if (cur_user < 0) return -EINVAL; user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; user_stats->peer_id = peer_id; user_stats->is_valid_peer_id = true; memcpy(&user_stats->cmpltn_cmn, ptr, sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn)); user_stats->tlv_flags |= BIT(tag); break; case HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS: if (len < sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)) { ath12k_warn(ab, "Invalid len %d for the tag 0x%x\n", len, tag); return -EINVAL; } ba_status = ptr; peer_id = le16_to_cpu(ba_status->sw_peer_id); cur_user = ath12k_get_ppdu_user_index(&ppdu_info->ppdu_stats, peer_id); if (cur_user < 0) return -EINVAL; user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; user_stats->peer_id = peer_id; user_stats->is_valid_peer_id = true; memcpy(&user_stats->ack_ba, ptr, sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)); user_stats->tlv_flags |= BIT(tag); break; } return 0; } int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, const void *ptr, void *data), void *data) { const struct htt_tlv *tlv; const void *begin = ptr; u16 tlv_tag, tlv_len; int ret = -EINVAL; while (len > 0) { if (len < sizeof(*tlv)) { ath12k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", ptr - begin, len, sizeof(*tlv)); return -EINVAL; } tlv = (struct htt_tlv *)ptr; tlv_tag = le32_get_bits(tlv->header, HTT_TLV_TAG); tlv_len = le32_get_bits(tlv->header, HTT_TLV_LEN); ptr += sizeof(*tlv); len -= sizeof(*tlv); if (tlv_len > len) { ath12k_err(ab, "htt tlv parse failure of tag %u at byte %zd (%zu bytes left, %u expected)\n", tlv_tag, ptr - begin, len, tlv_len); return -EINVAL; } ret = iter(ab, tlv_tag, tlv_len, ptr, data); if (ret == -ENOMEM) return ret; ptr += tlv_len; len -= tlv_len; } return 0; } static void ath12k_update_per_peer_tx_stats(struct ath12k_pdev_dp *dp_pdev, struct htt_ppdu_stats *ppdu_stats, u8 user) { struct ath12k_dp *dp = dp_pdev->dp; struct ath12k_base *ab = dp->ab; struct ath12k_dp_link_peer *peer; struct htt_ppdu_stats_user_rate *user_rate; struct ath12k_per_peer_tx_stats *peer_stats = &dp_pdev->peer_tx_stats; struct htt_ppdu_user_stats *usr_stats = &ppdu_stats->user_stats[user]; struct htt_ppdu_stats_common *common = &ppdu_stats->common; int ret; u8 flags, mcs, nss, bw, sgi, dcm, ppdu_type, rate_idx = 0; u32 v, succ_bytes = 0; u16 tones, rate = 0, succ_pkts = 0; u32 tx_duration = 0; u8 tid = HTT_PPDU_STATS_NON_QOS_TID; u16 tx_retry_failed = 0, tx_retry_count = 0; bool is_ampdu = false, is_ofdma; 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)) { 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)) { succ_bytes = le32_to_cpu(usr_stats->ack_ba.success_bytes); succ_pkts = le32_get_bits(usr_stats->ack_ba.info, 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); user_rate = &usr_stats->rate; flags = HTT_USR_RATE_PREAMBLE(user_rate->rate_flags); bw = HTT_USR_RATE_BW(user_rate->rate_flags) - 2; nss = HTT_USR_RATE_NSS(user_rate->rate_flags) + 1; mcs = HTT_USR_RATE_MCS(user_rate->rate_flags); sgi = HTT_USR_RATE_GI(user_rate->rate_flags); dcm = HTT_USR_RATE_DCM(user_rate->rate_flags); ppdu_type = HTT_USR_RATE_PPDU_TYPE(user_rate->info1); is_ofdma = (ppdu_type == HTT_PPDU_STATS_PPDU_TYPE_MU_OFDMA) || (ppdu_type == HTT_PPDU_STATS_PPDU_TYPE_MU_MIMO_OFDMA); /* Note: If host configured fixed rates and in some other special * cases, the broadcast/management frames are sent in different rates. * Firmware rate's control to be skipped for this? */ if (flags == WMI_RATE_PREAMBLE_HE && mcs > ATH12K_HE_MCS_MAX) { ath12k_warn(ab, "Invalid HE mcs %d peer stats", mcs); return; } if (flags == WMI_RATE_PREAMBLE_VHT && mcs > ATH12K_VHT_MCS_MAX) { ath12k_warn(ab, "Invalid VHT mcs %d peer stats", mcs); return; } if (flags == WMI_RATE_PREAMBLE_HT && (mcs > ATH12K_HT_MCS_MAX || nss < 1)) { ath12k_warn(ab, "Invalid HT mcs %d nss %d peer stats", mcs, nss); return; } if (flags == WMI_RATE_PREAMBLE_CCK || flags == WMI_RATE_PREAMBLE_OFDM) { ret = ath12k_mac_hw_ratecode_to_legacy_rate(mcs, flags, &rate_idx, &rate); if (ret < 0) return; } rcu_read_lock(); peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, usr_stats->peer_id); if (!peer || !peer->sta) { rcu_read_unlock(); return; } spin_lock_bh(&dp->dp_lock); memset(&peer->txrate, 0, sizeof(peer->txrate)); peer->txrate.bw = ath12k_mac_bw_to_mac80211_bw(bw); switch (flags) { case WMI_RATE_PREAMBLE_OFDM: peer->txrate.legacy = rate; break; case WMI_RATE_PREAMBLE_CCK: peer->txrate.legacy = rate; break; case WMI_RATE_PREAMBLE_HT: peer->txrate.mcs = mcs + 8 * (nss - 1); peer->txrate.flags = RATE_INFO_FLAGS_MCS; if (sgi) peer->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; break; case WMI_RATE_PREAMBLE_VHT: peer->txrate.mcs = mcs; peer->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; if (sgi) peer->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; break; case WMI_RATE_PREAMBLE_HE: peer->txrate.mcs = mcs; peer->txrate.flags = RATE_INFO_FLAGS_HE_MCS; peer->txrate.he_dcm = dcm; peer->txrate.he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi); tones = le16_to_cpu(user_rate->ru_end) - le16_to_cpu(user_rate->ru_start) + 1; v = ath12k_he_ru_tones_to_nl80211_he_ru_alloc(tones); peer->txrate.he_ru_alloc = v; if (is_ofdma) peer->txrate.bw = RATE_INFO_BW_HE_RU; break; case WMI_RATE_PREAMBLE_EHT: peer->txrate.mcs = mcs; peer->txrate.flags = RATE_INFO_FLAGS_EHT_MCS; peer->txrate.he_dcm = dcm; peer->txrate.eht_gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(sgi); tones = le16_to_cpu(user_rate->ru_end) - le16_to_cpu(user_rate->ru_start) + 1; v = ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(tones); peer->txrate.eht_ru_alloc = v; if (is_ofdma) peer->txrate.bw = RATE_INFO_BW_EHT_RU; break; } peer->tx_retry_failed += tx_retry_failed; peer->tx_retry_count += tx_retry_count; peer->txrate.nss = nss; peer->tx_duration += tx_duration; memcpy(&peer->last_txrate, &peer->txrate, sizeof(struct rate_info)); spin_unlock_bh(&dp->dp_lock); /* PPDU stats reported for mgmt packet doesn't have valid tx bytes. * So skip peer stats update for mgmt packets. */ if (tid < HTT_PPDU_STATS_NON_QOS_TID) { memset(peer_stats, 0, sizeof(*peer_stats)); peer_stats->succ_pkts = succ_pkts; peer_stats->succ_bytes = succ_bytes; peer_stats->is_ampdu = is_ampdu; peer_stats->duration = tx_duration; peer_stats->ba_fails = HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); } rcu_read_unlock(); } static void ath12k_htt_update_ppdu_stats(struct ath12k_pdev_dp *dp_pdev, struct htt_ppdu_stats *ppdu_stats) { u8 user; for (user = 0; user < HTT_PPDU_STATS_MAX_USERS - 1; user++) ath12k_update_per_peer_tx_stats(dp_pdev, ppdu_stats, user); } static struct htt_ppdu_stats_info *ath12k_dp_htt_get_ppdu_desc(struct ath12k_pdev_dp *dp_pdev, u32 ppdu_id) { struct htt_ppdu_stats_info *ppdu_info; lockdep_assert_held(&dp_pdev->ppdu_list_lock); if (!list_empty(&dp_pdev->ppdu_stats_info)) { list_for_each_entry(ppdu_info, &dp_pdev->ppdu_stats_info, list) { if (ppdu_info->ppdu_id == ppdu_id) return ppdu_info; } if (dp_pdev->ppdu_stat_list_depth > HTT_PPDU_DESC_MAX_DEPTH) { ppdu_info = list_first_entry(&dp_pdev->ppdu_stats_info, typeof(*ppdu_info), list); list_del(&ppdu_info->list); dp_pdev->ppdu_stat_list_depth--; ath12k_htt_update_ppdu_stats(dp_pdev, &ppdu_info->ppdu_stats); kfree(ppdu_info); } } ppdu_info = kzalloc(sizeof(*ppdu_info), GFP_ATOMIC); if (!ppdu_info) return NULL; list_add_tail(&ppdu_info->list, &dp_pdev->ppdu_stats_info); dp_pdev->ppdu_stat_list_depth++; return ppdu_info; } static void ath12k_copy_to_delay_stats(struct ath12k_dp_link_peer *peer, struct htt_ppdu_user_stats *usr_stats) { peer->ppdu_stats_delayba.sw_peer_id = le16_to_cpu(usr_stats->rate.sw_peer_id); peer->ppdu_stats_delayba.info0 = le32_to_cpu(usr_stats->rate.info0); peer->ppdu_stats_delayba.ru_end = le16_to_cpu(usr_stats->rate.ru_end); peer->ppdu_stats_delayba.ru_start = le16_to_cpu(usr_stats->rate.ru_start); peer->ppdu_stats_delayba.info1 = le32_to_cpu(usr_stats->rate.info1); peer->ppdu_stats_delayba.rate_flags = le32_to_cpu(usr_stats->rate.rate_flags); peer->ppdu_stats_delayba.resp_rate_flags = le32_to_cpu(usr_stats->rate.resp_rate_flags); peer->delayba_flag = true; } static void ath12k_copy_to_bar(struct ath12k_dp_link_peer *peer, struct htt_ppdu_user_stats *usr_stats) { usr_stats->rate.sw_peer_id = cpu_to_le16(peer->ppdu_stats_delayba.sw_peer_id); usr_stats->rate.info0 = cpu_to_le32(peer->ppdu_stats_delayba.info0); usr_stats->rate.ru_end = cpu_to_le16(peer->ppdu_stats_delayba.ru_end); usr_stats->rate.ru_start = cpu_to_le16(peer->ppdu_stats_delayba.ru_start); usr_stats->rate.info1 = cpu_to_le32(peer->ppdu_stats_delayba.info1); usr_stats->rate.rate_flags = cpu_to_le32(peer->ppdu_stats_delayba.rate_flags); usr_stats->rate.resp_rate_flags = cpu_to_le32(peer->ppdu_stats_delayba.resp_rate_flags); peer->delayba_flag = false; } static int ath12k_htt_pull_ppdu_stats(struct ath12k_base *ab, struct sk_buff *skb) { struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct ath12k_htt_ppdu_stats_msg *msg; struct htt_ppdu_stats_info *ppdu_info; struct ath12k_dp_link_peer *peer = NULL; struct htt_ppdu_user_stats *usr_stats = NULL; u32 peer_id = 0; struct ath12k_pdev_dp *dp_pdev; int ret, i; u8 pdev_id, pdev_idx; u32 ppdu_id, len; msg = (struct ath12k_htt_ppdu_stats_msg *)skb->data; len = le32_get_bits(msg->info, HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE); if (len > (skb->len - struct_size(msg, data, 0))) { ath12k_warn(ab, "HTT PPDU STATS event has unexpected payload size %u, should be smaller than %u\n", len, skb->len); return -EINVAL; } pdev_id = le32_get_bits(msg->info, HTT_T2H_PPDU_STATS_INFO_PDEV_ID); ppdu_id = le32_to_cpu(msg->ppdu_id); pdev_idx = DP_HW2SW_MACID(pdev_id); if (pdev_idx >= MAX_RADIOS) { ath12k_warn(ab, "HTT PPDU STATS invalid pdev id %u", pdev_id); return -EINVAL; } rcu_read_lock(); dp_pdev = ath12k_dp_to_pdev_dp(dp, pdev_idx); if (!dp_pdev) { ret = -EINVAL; goto exit; } spin_lock_bh(&dp_pdev->ppdu_list_lock); ppdu_info = ath12k_dp_htt_get_ppdu_desc(dp_pdev, ppdu_id); if (!ppdu_info) { spin_unlock_bh(&dp_pdev->ppdu_list_lock); ret = -EINVAL; goto exit; } ppdu_info->ppdu_id = ppdu_id; ret = ath12k_dp_htt_tlv_iter(ab, msg->data, len, ath12k_htt_tlv_ppdu_stats_parse, (void *)ppdu_info); if (ret) { spin_unlock_bh(&dp_pdev->ppdu_list_lock); ath12k_warn(ab, "Failed to parse tlv %d\n", ret); goto exit; } if (ppdu_info->ppdu_stats.common.num_users >= HTT_PPDU_STATS_MAX_USERS) { spin_unlock_bh(&dp_pdev->ppdu_list_lock); ath12k_warn(ab, "HTT PPDU STATS event has unexpected num_users %u, should be smaller than %u\n", ppdu_info->ppdu_stats.common.num_users, HTT_PPDU_STATS_MAX_USERS); ret = -EINVAL; goto exit; } /* back up data rate tlv for all peers */ if (ppdu_info->frame_type == HTT_STATS_PPDU_FTYPE_DATA && (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_COMMON)) && ppdu_info->delay_ba) { for (i = 0; i < ppdu_info->ppdu_stats.common.num_users; i++) { peer_id = ppdu_info->ppdu_stats.user_stats[i].peer_id; peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, peer_id); if (!peer) continue; usr_stats = &ppdu_info->ppdu_stats.user_stats[i]; if (usr_stats->delay_ba) ath12k_copy_to_delay_stats(peer, usr_stats); } } /* restore all peers' data rate tlv to mu-bar tlv */ if (ppdu_info->frame_type == HTT_STATS_PPDU_FTYPE_BAR && (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_COMMON))) { for (i = 0; i < ppdu_info->bar_num_users; i++) { peer_id = ppdu_info->ppdu_stats.user_stats[i].peer_id; peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, peer_id); if (!peer) continue; usr_stats = &ppdu_info->ppdu_stats.user_stats[i]; if (peer->delayba_flag) ath12k_copy_to_bar(peer, usr_stats); } } spin_unlock_bh(&dp_pdev->ppdu_list_lock); exit: rcu_read_unlock(); return ret; } static void ath12k_htt_mlo_offset_event_handler(struct ath12k_base *ab, struct sk_buff *skb) { struct ath12k_htt_mlo_offset_msg *msg; struct ath12k_pdev *pdev; struct ath12k *ar; u8 pdev_id; msg = (struct ath12k_htt_mlo_offset_msg *)skb->data; pdev_id = u32_get_bits(__le32_to_cpu(msg->info), HTT_T2H_MLO_OFFSET_INFO_PDEV_ID); rcu_read_lock(); ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id); if (!ar) { /* It is possible that the ar is not yet active (started). * The above function will only look for the active pdev * and hence %NULL return is possible. Just silently * discard this message */ goto exit; } spin_lock_bh(&ar->data_lock); pdev = ar->pdev; pdev->timestamp.info = __le32_to_cpu(msg->info); pdev->timestamp.sync_timestamp_lo_us = __le32_to_cpu(msg->sync_timestamp_lo_us); pdev->timestamp.sync_timestamp_hi_us = __le32_to_cpu(msg->sync_timestamp_hi_us); pdev->timestamp.mlo_offset_lo = __le32_to_cpu(msg->mlo_offset_lo); pdev->timestamp.mlo_offset_hi = __le32_to_cpu(msg->mlo_offset_hi); pdev->timestamp.mlo_offset_clks = __le32_to_cpu(msg->mlo_offset_clks); pdev->timestamp.mlo_comp_clks = __le32_to_cpu(msg->mlo_comp_clks); pdev->timestamp.mlo_comp_timer = __le32_to_cpu(msg->mlo_comp_timer); spin_unlock_bh(&ar->data_lock); exit: rcu_read_unlock(); } void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, struct sk_buff *skb) { struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data; enum htt_t2h_msg_type type; u16 peer_id; u8 vdev_id; u8 mac_addr[ETH_ALEN]; u16 peer_mac_h16; u16 ast_hash = 0; u16 hw_peer_id; type = le32_get_bits(resp->version_msg.version, HTT_T2H_MSG_TYPE); ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "dp_htt rx msg type :0x%0x\n", type); switch (type) { case HTT_T2H_MSG_TYPE_VERSION_CONF: dp->htt_tgt_ver_major = le32_get_bits(resp->version_msg.version, HTT_T2H_VERSION_CONF_MAJOR); dp->htt_tgt_ver_minor = le32_get_bits(resp->version_msg.version, HTT_T2H_VERSION_CONF_MINOR); complete(&dp->htt_tgt_version_received); break; /* TODO: remove unused peer map versions after testing */ case HTT_T2H_MSG_TYPE_PEER_MAP: vdev_id = le32_get_bits(resp->peer_map_ev.info, HTT_T2H_PEER_MAP_INFO_VDEV_ID); peer_id = le32_get_bits(resp->peer_map_ev.info, HTT_T2H_PEER_MAP_INFO_PEER_ID); peer_mac_h16 = le32_get_bits(resp->peer_map_ev.info1, HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16); ath12k_dp_get_mac_addr(le32_to_cpu(resp->peer_map_ev.mac_addr_l32), peer_mac_h16, mac_addr); ath12k_dp_link_peer_map_event(ab, vdev_id, peer_id, mac_addr, 0, 0); break; case HTT_T2H_MSG_TYPE_PEER_MAP2: vdev_id = le32_get_bits(resp->peer_map_ev.info, HTT_T2H_PEER_MAP_INFO_VDEV_ID); peer_id = le32_get_bits(resp->peer_map_ev.info, HTT_T2H_PEER_MAP_INFO_PEER_ID); peer_mac_h16 = le32_get_bits(resp->peer_map_ev.info1, HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16); ath12k_dp_get_mac_addr(le32_to_cpu(resp->peer_map_ev.mac_addr_l32), peer_mac_h16, mac_addr); ast_hash = le32_get_bits(resp->peer_map_ev.info2, HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL); hw_peer_id = le32_get_bits(resp->peer_map_ev.info1, HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID); ath12k_dp_link_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash, hw_peer_id); break; case HTT_T2H_MSG_TYPE_PEER_MAP3: vdev_id = le32_get_bits(resp->peer_map_ev.info, HTT_T2H_PEER_MAP_INFO_VDEV_ID); peer_id = le32_get_bits(resp->peer_map_ev.info, HTT_T2H_PEER_MAP_INFO_PEER_ID); peer_mac_h16 = le32_get_bits(resp->peer_map_ev.info1, HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16); ath12k_dp_get_mac_addr(le32_to_cpu(resp->peer_map_ev.mac_addr_l32), peer_mac_h16, mac_addr); ast_hash = le32_get_bits(resp->peer_map_ev.info2, HTT_T2H_PEER_MAP3_INFO2_AST_HASH_VAL); hw_peer_id = le32_get_bits(resp->peer_map_ev.info2, HTT_T2H_PEER_MAP3_INFO2_HW_PEER_ID); ath12k_dp_link_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash, hw_peer_id); break; case HTT_T2H_MSG_TYPE_PEER_UNMAP: case HTT_T2H_MSG_TYPE_PEER_UNMAP2: peer_id = le32_get_bits(resp->peer_unmap_ev.info, HTT_T2H_PEER_UNMAP_INFO_PEER_ID); ath12k_dp_link_peer_unmap_event(ab, peer_id); break; case HTT_T2H_MSG_TYPE_PPDU_STATS_IND: ath12k_htt_pull_ppdu_stats(ab, skb); break; case HTT_T2H_MSG_TYPE_EXT_STATS_CONF: ath12k_debugfs_htt_ext_stats_handler(ab, skb); break; case HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND: ath12k_htt_mlo_offset_event_handler(ab, skb); break; default: ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "dp_htt event %d not handled\n", type); break; } dev_kfree_skb_any(skb); } EXPORT_SYMBOL(ath12k_dp_htt_htc_t2h_msg_handler); static int ath12k_dp_tx_get_ring_id_type(struct ath12k_base *ab, int mac_id, u32 ring_id, enum hal_ring_type ring_type, enum htt_srng_ring_type *htt_ring_type, enum htt_srng_ring_id *htt_ring_id) { int ret = 0; switch (ring_type) { case HAL_RXDMA_BUF: /* for some targets, host fills rx buffer to fw and fw fills to * rxbuf ring for each rxdma */ if (!ab->hw_params->rx_mac_buf_ring) { if (!(ring_id == HAL_SRNG_SW2RXDMA_BUF0 || ring_id == HAL_SRNG_SW2RXDMA_BUF1)) { ret = -EINVAL; } *htt_ring_id = HTT_RXDMA_HOST_BUF_RING; *htt_ring_type = HTT_SW_TO_HW_RING; } else { if (ring_id == HAL_SRNG_SW2RXDMA_BUF0) { *htt_ring_id = HTT_HOST1_TO_FW_RXBUF_RING; *htt_ring_type = HTT_SW_TO_SW_RING; } else { *htt_ring_id = HTT_RXDMA_HOST_BUF_RING; *htt_ring_type = HTT_SW_TO_HW_RING; } } break; case HAL_RXDMA_DST: *htt_ring_id = HTT_RXDMA_NON_MONITOR_DEST_RING; *htt_ring_type = HTT_HW_TO_SW_RING; break; case HAL_RXDMA_MONITOR_BUF: *htt_ring_id = HTT_RX_MON_HOST2MON_BUF_RING; *htt_ring_type = HTT_SW_TO_HW_RING; break; case HAL_RXDMA_MONITOR_STATUS: *htt_ring_id = HTT_RXDMA_MONITOR_STATUS_RING; *htt_ring_type = HTT_SW_TO_HW_RING; break; case HAL_RXDMA_MONITOR_DST: *htt_ring_id = HTT_RX_MON_MON2HOST_DEST_RING; *htt_ring_type = HTT_HW_TO_SW_RING; break; case HAL_RXDMA_MONITOR_DESC: *htt_ring_id = HTT_RXDMA_MONITOR_DESC_RING; *htt_ring_type = HTT_SW_TO_HW_RING; break; default: ath12k_warn(ab, "Unsupported ring type in DP :%d\n", ring_type); ret = -EINVAL; } return ret; } int ath12k_dp_tx_htt_srng_setup(struct ath12k_base *ab, u32 ring_id, int mac_id, enum hal_ring_type ring_type) { struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct htt_srng_setup_cmd *cmd; struct hal_srng *srng = &ab->hal.srng_list[ring_id]; struct hal_srng_params params; struct sk_buff *skb; u32 ring_entry_sz; int len = sizeof(*cmd); dma_addr_t hp_addr, tp_addr; enum htt_srng_ring_type htt_ring_type; enum htt_srng_ring_id htt_ring_id; int ret; skb = ath12k_htc_alloc_skb(ab, len); if (!skb) return -ENOMEM; memset(¶ms, 0, sizeof(params)); ath12k_hal_srng_get_params(ab, srng, ¶ms); hp_addr = ath12k_hal_srng_get_hp_addr(ab, srng); tp_addr = ath12k_hal_srng_get_tp_addr(ab, srng); ret = ath12k_dp_tx_get_ring_id_type(ab, mac_id, ring_id, ring_type, &htt_ring_type, &htt_ring_id); if (ret) goto err_free; skb_put(skb, len); cmd = (struct htt_srng_setup_cmd *)skb->data; cmd->info0 = le32_encode_bits(HTT_H2T_MSG_TYPE_SRING_SETUP, HTT_SRNG_SETUP_CMD_INFO0_MSG_TYPE); if (htt_ring_type == HTT_SW_TO_HW_RING || htt_ring_type == HTT_HW_TO_SW_RING) cmd->info0 |= le32_encode_bits(DP_SW2HW_MACID(mac_id), HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID); else cmd->info0 |= le32_encode_bits(mac_id, HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID); cmd->info0 |= le32_encode_bits(htt_ring_type, HTT_SRNG_SETUP_CMD_INFO0_RING_TYPE); cmd->info0 |= le32_encode_bits(htt_ring_id, HTT_SRNG_SETUP_CMD_INFO0_RING_ID); cmd->ring_base_addr_lo = cpu_to_le32(params.ring_base_paddr & HAL_ADDR_LSB_REG_MASK); cmd->ring_base_addr_hi = cpu_to_le32((u64)params.ring_base_paddr >> HAL_ADDR_MSB_REG_SHIFT); ret = ath12k_hal_srng_get_entrysize(ab, ring_type); if (ret < 0) goto err_free; ring_entry_sz = ret; ring_entry_sz >>= 2; cmd->info1 = le32_encode_bits(ring_entry_sz, HTT_SRNG_SETUP_CMD_INFO1_RING_ENTRY_SIZE); cmd->info1 |= le32_encode_bits(params.num_entries * ring_entry_sz, HTT_SRNG_SETUP_CMD_INFO1_RING_SIZE); cmd->info1 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP), HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_MSI_SWAP); cmd->info1 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP), HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_TLV_SWAP); cmd->info1 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_RING_PTR_SWAP), HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_HOST_FW_SWAP); if (htt_ring_type == HTT_SW_TO_HW_RING) cmd->info1 |= cpu_to_le32(HTT_SRNG_SETUP_CMD_INFO1_RING_LOOP_CNT_DIS); cmd->ring_head_off32_remote_addr_lo = cpu_to_le32(lower_32_bits(hp_addr)); cmd->ring_head_off32_remote_addr_hi = cpu_to_le32(upper_32_bits(hp_addr)); cmd->ring_tail_off32_remote_addr_lo = cpu_to_le32(lower_32_bits(tp_addr)); cmd->ring_tail_off32_remote_addr_hi = cpu_to_le32(upper_32_bits(tp_addr)); cmd->ring_msi_addr_lo = cpu_to_le32(lower_32_bits(params.msi_addr)); cmd->ring_msi_addr_hi = cpu_to_le32(upper_32_bits(params.msi_addr)); cmd->msi_data = cpu_to_le32(params.msi_data); cmd->intr_info = le32_encode_bits(params.intr_batch_cntr_thres_entries * ring_entry_sz, HTT_SRNG_SETUP_CMD_INTR_INFO_BATCH_COUNTER_THRESH); cmd->intr_info |= le32_encode_bits(params.intr_timer_thres_us >> 3, HTT_SRNG_SETUP_CMD_INTR_INFO_INTR_TIMER_THRESH); cmd->info2 = 0; if (params.flags & HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN) { cmd->info2 = le32_encode_bits(params.low_threshold, HTT_SRNG_SETUP_CMD_INFO2_INTR_LOW_THRESH); } ath12k_dbg(ab, ATH12K_DBG_HAL, "%s msi_addr_lo:0x%x, msi_addr_hi:0x%x, msi_data:0x%x\n", __func__, cmd->ring_msi_addr_lo, cmd->ring_msi_addr_hi, cmd->msi_data); ath12k_dbg(ab, ATH12K_DBG_HAL, "ring_id:%d, ring_type:%d, intr_info:0x%x, flags:0x%x\n", ring_id, ring_type, cmd->intr_info, cmd->info2); ret = ath12k_htc_send(&ab->htc, dp->eid, skb); if (ret) goto err_free; return 0; err_free: dev_kfree_skb_any(skb); return ret; } int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab) { struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct sk_buff *skb; struct htt_ver_req_cmd *cmd; int len = sizeof(*cmd); u32 metadata_version; int ret; init_completion(&dp->htt_tgt_version_received); skb = ath12k_htc_alloc_skb(ab, len); if (!skb) return -ENOMEM; skb_put(skb, len); cmd = (struct htt_ver_req_cmd *)skb->data; cmd->ver_reg_info = le32_encode_bits(HTT_H2T_MSG_TYPE_VERSION_REQ, HTT_OPTION_TAG); metadata_version = ath12k_ftm_mode ? HTT_OPTION_TCL_METADATA_VER_V1 : HTT_OPTION_TCL_METADATA_VER_V2; cmd->tcl_metadata_version = le32_encode_bits(HTT_TAG_TCL_METADATA_VERSION, HTT_OPTION_TAG) | le32_encode_bits(HTT_TCL_METADATA_VER_SZ, HTT_OPTION_LEN) | le32_encode_bits(metadata_version, HTT_OPTION_VALUE); ret = ath12k_htc_send(&ab->htc, dp->eid, skb); if (ret) { dev_kfree_skb_any(skb); return ret; } ret = wait_for_completion_timeout(&dp->htt_tgt_version_received, HTT_TARGET_VERSION_TIMEOUT_HZ); if (ret == 0) { ath12k_warn(ab, "htt target version request timed out\n"); return -ETIMEDOUT; } if (dp->htt_tgt_ver_major != HTT_TARGET_VERSION_MAJOR) { ath12k_err(ab, "unsupported htt major version %d supported version is %d\n", dp->htt_tgt_ver_major, HTT_TARGET_VERSION_MAJOR); return -EOPNOTSUPP; } return 0; } int ath12k_dp_tx_htt_h2t_ppdu_stats_req(struct ath12k *ar, u32 mask) { struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct sk_buff *skb; struct htt_ppdu_stats_cfg_cmd *cmd; int len = sizeof(*cmd); u8 pdev_mask; int ret; int i; for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { skb = ath12k_htc_alloc_skb(ab, len); if (!skb) return -ENOMEM; skb_put(skb, len); cmd = (struct htt_ppdu_stats_cfg_cmd *)skb->data; cmd->msg = le32_encode_bits(HTT_H2T_MSG_TYPE_PPDU_STATS_CFG, HTT_PPDU_STATS_CFG_MSG_TYPE); pdev_mask = 1 << (i + ar->pdev_idx); cmd->msg |= le32_encode_bits(pdev_mask, HTT_PPDU_STATS_CFG_PDEV_ID); cmd->msg |= le32_encode_bits(mask, HTT_PPDU_STATS_CFG_TLV_TYPE_BITMASK); ret = ath12k_htc_send(&ab->htc, dp->eid, skb); if (ret) { dev_kfree_skb_any(skb); return ret; } } return 0; } int ath12k_dp_tx_htt_rx_filter_setup(struct ath12k_base *ab, u32 ring_id, int mac_id, enum hal_ring_type ring_type, int rx_buf_size, struct htt_rx_ring_tlv_filter *tlv_filter) { struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct htt_rx_ring_selection_cfg_cmd *cmd; struct hal_srng *srng = &ab->hal.srng_list[ring_id]; struct hal_srng_params params; struct sk_buff *skb; int len = sizeof(*cmd); enum htt_srng_ring_type htt_ring_type; enum htt_srng_ring_id htt_ring_id; int ret; skb = ath12k_htc_alloc_skb(ab, len); if (!skb) return -ENOMEM; memset(¶ms, 0, sizeof(params)); ath12k_hal_srng_get_params(ab, srng, ¶ms); ret = ath12k_dp_tx_get_ring_id_type(ab, mac_id, ring_id, ring_type, &htt_ring_type, &htt_ring_id); if (ret) goto err_free; skb_put(skb, len); cmd = (struct htt_rx_ring_selection_cfg_cmd *)skb->data; cmd->info0 = le32_encode_bits(HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG, HTT_RX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE); if (htt_ring_type == HTT_SW_TO_HW_RING || htt_ring_type == HTT_HW_TO_SW_RING) cmd->info0 |= le32_encode_bits(DP_SW2HW_MACID(mac_id), HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID); else cmd->info0 |= le32_encode_bits(mac_id, HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID); cmd->info0 |= le32_encode_bits(htt_ring_id, HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID); cmd->info0 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP), HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS); cmd->info0 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP), HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS); cmd->info0 |= le32_encode_bits(tlv_filter->offset_valid, HTT_RX_RING_SELECTION_CFG_CMD_INFO0_OFFSET_VALID); cmd->info0 |= le32_encode_bits(tlv_filter->drop_threshold_valid, HTT_RX_RING_SELECTION_CFG_CMD_INFO0_DROP_THRES_VAL); cmd->info0 |= le32_encode_bits(!tlv_filter->rxmon_disable, HTT_RX_RING_SELECTION_CFG_CMD_INFO0_EN_RXMON); cmd->info1 = le32_encode_bits(rx_buf_size, HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE); cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_mgmt, HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT); cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_ctrl, HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL); cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_data, HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA); cmd->pkt_type_en_flags0 = cpu_to_le32(tlv_filter->pkt_filter_flags0); cmd->pkt_type_en_flags1 = cpu_to_le32(tlv_filter->pkt_filter_flags1); cmd->pkt_type_en_flags2 = cpu_to_le32(tlv_filter->pkt_filter_flags2); cmd->pkt_type_en_flags3 = cpu_to_le32(tlv_filter->pkt_filter_flags3); cmd->rx_filter_tlv = cpu_to_le32(tlv_filter->rx_filter); cmd->info2 = le32_encode_bits(tlv_filter->rx_drop_threshold, HTT_RX_RING_SELECTION_CFG_CMD_INFO2_DROP_THRESHOLD); cmd->info2 |= le32_encode_bits(tlv_filter->enable_log_mgmt_type, HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_MGMT_TYPE); cmd->info2 |= le32_encode_bits(tlv_filter->enable_log_ctrl_type, HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_CTRL_TYPE); cmd->info2 |= le32_encode_bits(tlv_filter->enable_log_data_type, HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_DATA_TYPE); cmd->info3 = le32_encode_bits(tlv_filter->enable_rx_tlv_offset, HTT_RX_RING_SELECTION_CFG_CMD_INFO3_EN_TLV_PKT_OFFSET); cmd->info3 |= le32_encode_bits(tlv_filter->rx_tlv_offset, HTT_RX_RING_SELECTION_CFG_CMD_INFO3_PKT_TLV_OFFSET); if (tlv_filter->offset_valid) { cmd->rx_packet_offset = le32_encode_bits(tlv_filter->rx_packet_offset, HTT_RX_RING_SELECTION_CFG_RX_PACKET_OFFSET); cmd->rx_packet_offset |= le32_encode_bits(tlv_filter->rx_header_offset, HTT_RX_RING_SELECTION_CFG_RX_HEADER_OFFSET); cmd->rx_mpdu_offset = le32_encode_bits(tlv_filter->rx_mpdu_end_offset, HTT_RX_RING_SELECTION_CFG_RX_MPDU_END_OFFSET); cmd->rx_mpdu_offset |= le32_encode_bits(tlv_filter->rx_mpdu_start_offset, HTT_RX_RING_SELECTION_CFG_RX_MPDU_START_OFFSET); cmd->rx_msdu_offset = le32_encode_bits(tlv_filter->rx_msdu_end_offset, HTT_RX_RING_SELECTION_CFG_RX_MSDU_END_OFFSET); cmd->rx_msdu_offset |= le32_encode_bits(tlv_filter->rx_msdu_start_offset, HTT_RX_RING_SELECTION_CFG_RX_MSDU_START_OFFSET); cmd->rx_attn_offset = le32_encode_bits(tlv_filter->rx_attn_offset, HTT_RX_RING_SELECTION_CFG_RX_ATTENTION_OFFSET); } if (tlv_filter->rx_mpdu_start_wmask > 0 && tlv_filter->rx_msdu_end_wmask > 0) { cmd->info2 |= le32_encode_bits(true, HTT_RX_RING_SELECTION_CFG_WORD_MASK_COMPACT_SET); cmd->rx_mpdu_start_end_mask = le32_encode_bits(tlv_filter->rx_mpdu_start_wmask, HTT_RX_RING_SELECTION_CFG_RX_MPDU_START_MASK); /* mpdu_end is not used for any hardwares so far * please assign it in future if any chip is * using through hal ops */ cmd->rx_mpdu_start_end_mask |= le32_encode_bits(tlv_filter->rx_mpdu_end_wmask, HTT_RX_RING_SELECTION_CFG_RX_MPDU_END_MASK); cmd->rx_msdu_end_word_mask = le32_encode_bits(tlv_filter->rx_msdu_end_wmask, HTT_RX_RING_SELECTION_CFG_RX_MSDU_END_MASK); } ret = ath12k_htc_send(&ab->htc, dp->eid, skb); if (ret) goto err_free; return 0; err_free: dev_kfree_skb_any(skb); return ret; } EXPORT_SYMBOL(ath12k_dp_tx_htt_rx_filter_setup); int ath12k_dp_tx_htt_h2t_ext_stats_req(struct ath12k *ar, u8 type, struct htt_ext_stats_cfg_params *cfg_params, u64 cookie) { struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct sk_buff *skb; struct htt_ext_stats_cfg_cmd *cmd; int len = sizeof(*cmd); int ret; u32 pdev_id; skb = ath12k_htc_alloc_skb(ab, len); if (!skb) return -ENOMEM; skb_put(skb, len); cmd = (struct htt_ext_stats_cfg_cmd *)skb->data; memset(cmd, 0, sizeof(*cmd)); cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_EXT_STATS_CFG; pdev_id = ath12k_mac_get_target_pdev_id(ar); cmd->hdr.pdev_mask = 1 << pdev_id; cmd->hdr.stats_type = type; cmd->cfg_param0 = cpu_to_le32(cfg_params->cfg0); cmd->cfg_param1 = cpu_to_le32(cfg_params->cfg1); cmd->cfg_param2 = cpu_to_le32(cfg_params->cfg2); cmd->cfg_param3 = cpu_to_le32(cfg_params->cfg3); cmd->cookie_lsb = cpu_to_le32(lower_32_bits(cookie)); cmd->cookie_msb = cpu_to_le32(upper_32_bits(cookie)); ret = ath12k_htc_send(&ab->htc, dp->eid, skb); if (ret) { ath12k_warn(ab, "failed to send htt type stats request: %d", ret); dev_kfree_skb_any(skb); return ret; } return 0; } int ath12k_dp_tx_htt_monitor_mode_ring_config(struct ath12k *ar, bool reset) { struct ath12k_base *ab = ar->ab; int ret; ret = ath12k_dp_tx_htt_rx_monitor_mode_ring_config(ar, reset); if (ret) { ath12k_err(ab, "failed to setup rx monitor filter %d\n", ret); return ret; } return 0; } int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset) { struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct htt_rx_ring_tlv_filter tlv_filter = {}; int ret, ring_id, i; tlv_filter.offset_valid = false; if (!reset) { tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_DEST_RING; tlv_filter.drop_threshold_valid = true; tlv_filter.rx_drop_threshold = HTT_RX_RING_TLV_DROP_THRESHOLD_VALUE; tlv_filter.enable_log_mgmt_type = true; tlv_filter.enable_log_ctrl_type = true; tlv_filter.enable_log_data_type = true; tlv_filter.conf_len_ctrl = HTT_RX_RING_DEFAULT_DMA_LENGTH; tlv_filter.conf_len_mgmt = HTT_RX_RING_DEFAULT_DMA_LENGTH; tlv_filter.conf_len_data = HTT_RX_RING_DEFAULT_DMA_LENGTH; tlv_filter.enable_rx_tlv_offset = true; tlv_filter.rx_tlv_offset = HTT_RX_RING_PKT_TLV_OFFSET; tlv_filter.pkt_filter_flags0 = HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 | HTT_RX_MON_MO_MGMT_FILTER_FLAGS0; tlv_filter.pkt_filter_flags1 = HTT_RX_MON_FP_MGMT_FILTER_FLAGS1 | HTT_RX_MON_MO_MGMT_FILTER_FLAGS1; tlv_filter.pkt_filter_flags2 = HTT_RX_MON_FP_CTRL_FILTER_FLASG2 | HTT_RX_MON_MO_CTRL_FILTER_FLASG2; tlv_filter.pkt_filter_flags3 = HTT_RX_MON_FP_CTRL_FILTER_FLASG3 | HTT_RX_MON_MO_CTRL_FILTER_FLASG3 | HTT_RX_MON_FP_DATA_FILTER_FLASG3 | HTT_RX_MON_MO_DATA_FILTER_FLASG3; } else { tlv_filter = ath12k_mac_mon_status_filter_default; if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) tlv_filter.rx_filter = ath12k_debugfs_rx_filter(ar); } if (ab->hw_params->rxdma1_enable) { for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id + i, HAL_RXDMA_MONITOR_DST, DP_RXDMA_REFILL_RING_SIZE, &tlv_filter); if (ret) { ath12k_err(ab, "failed to setup filter for monitor buf %d\n", ret); return ret; } } return 0; } if (!reset) { for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { ring_id = dp->rx_mac_buf_ring[i].ring_id; ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, i, HAL_RXDMA_BUF, DP_RXDMA_REFILL_RING_SIZE, &tlv_filter); if (ret) { ath12k_err(ab, "failed to setup filter for mon rx buf %d\n", ret); return ret; } } } for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { ring_id = dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; if (!reset) { tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING; } ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id, i, HAL_RXDMA_MONITOR_STATUS, RX_MON_STATUS_BUF_SIZE, &tlv_filter); if (ret) { ath12k_err(ab, "failed to setup filter for mon status buf %d\n", ret); return ret; } } return 0; } int ath12k_dp_tx_htt_tx_filter_setup(struct ath12k_base *ab, u32 ring_id, int mac_id, enum hal_ring_type ring_type, int tx_buf_size, struct htt_tx_ring_tlv_filter *htt_tlv_filter) { struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct htt_tx_ring_selection_cfg_cmd *cmd; struct hal_srng *srng = &ab->hal.srng_list[ring_id]; struct hal_srng_params params; struct sk_buff *skb; int len = sizeof(*cmd); enum htt_srng_ring_type htt_ring_type; enum htt_srng_ring_id htt_ring_id; int ret; skb = ath12k_htc_alloc_skb(ab, len); if (!skb) return -ENOMEM; memset(¶ms, 0, sizeof(params)); ath12k_hal_srng_get_params(ab, srng, ¶ms); ret = ath12k_dp_tx_get_ring_id_type(ab, mac_id, ring_id, ring_type, &htt_ring_type, &htt_ring_id); if (ret) goto err_free; skb_put(skb, len); cmd = (struct htt_tx_ring_selection_cfg_cmd *)skb->data; cmd->info0 = le32_encode_bits(HTT_H2T_MSG_TYPE_TX_MONITOR_CFG, HTT_TX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE); if (htt_ring_type == HTT_SW_TO_HW_RING || htt_ring_type == HTT_HW_TO_SW_RING) cmd->info0 |= le32_encode_bits(DP_SW2HW_MACID(mac_id), HTT_TX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID); else cmd->info0 |= le32_encode_bits(mac_id, HTT_TX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID); cmd->info0 |= le32_encode_bits(htt_ring_id, HTT_TX_RING_SELECTION_CFG_CMD_INFO0_RING_ID); cmd->info0 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP), HTT_TX_RING_SELECTION_CFG_CMD_INFO0_SS); cmd->info0 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP), HTT_TX_RING_SELECTION_CFG_CMD_INFO0_PS); cmd->info1 |= le32_encode_bits(tx_buf_size, HTT_TX_RING_SELECTION_CFG_CMD_INFO1_RING_BUFF_SIZE); if (htt_tlv_filter->tx_mon_mgmt_filter) { cmd->info1 |= le32_encode_bits(HTT_STATS_FRAME_CTRL_TYPE_MGMT, HTT_TX_RING_SELECTION_CFG_CMD_INFO1_PKT_TYPE); cmd->info1 |= le32_encode_bits(htt_tlv_filter->tx_mon_pkt_dma_len, HTT_TX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT); cmd->info2 |= le32_encode_bits(HTT_STATS_FRAME_CTRL_TYPE_MGMT, HTT_TX_RING_SELECTION_CFG_CMD_INFO2_PKT_TYPE_EN_FLAG); } if (htt_tlv_filter->tx_mon_data_filter) { cmd->info1 |= le32_encode_bits(HTT_STATS_FRAME_CTRL_TYPE_CTRL, HTT_TX_RING_SELECTION_CFG_CMD_INFO1_PKT_TYPE); cmd->info1 |= le32_encode_bits(htt_tlv_filter->tx_mon_pkt_dma_len, HTT_TX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL); cmd->info2 |= le32_encode_bits(HTT_STATS_FRAME_CTRL_TYPE_CTRL, HTT_TX_RING_SELECTION_CFG_CMD_INFO2_PKT_TYPE_EN_FLAG); } if (htt_tlv_filter->tx_mon_ctrl_filter) { cmd->info1 |= le32_encode_bits(HTT_STATS_FRAME_CTRL_TYPE_DATA, HTT_TX_RING_SELECTION_CFG_CMD_INFO1_PKT_TYPE); cmd->info1 |= le32_encode_bits(htt_tlv_filter->tx_mon_pkt_dma_len, HTT_TX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA); cmd->info2 |= le32_encode_bits(HTT_STATS_FRAME_CTRL_TYPE_DATA, HTT_TX_RING_SELECTION_CFG_CMD_INFO2_PKT_TYPE_EN_FLAG); } cmd->tlv_filter_mask_in0 = cpu_to_le32(htt_tlv_filter->tx_mon_downstream_tlv_flags); cmd->tlv_filter_mask_in1 = cpu_to_le32(htt_tlv_filter->tx_mon_upstream_tlv_flags0); cmd->tlv_filter_mask_in2 = cpu_to_le32(htt_tlv_filter->tx_mon_upstream_tlv_flags1); cmd->tlv_filter_mask_in3 = cpu_to_le32(htt_tlv_filter->tx_mon_upstream_tlv_flags2); ret = ath12k_htc_send(&ab->htc, dp->eid, skb); if (ret) goto err_free; return 0; err_free: dev_kfree_skb_any(skb); return ret; }