diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath11k')
30 files changed, 2212 insertions, 506 deletions
diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig index c88e16d4022b..738f99090d83 100644 --- a/drivers/net/wireless/ath/ath11k/Kconfig +++ b/drivers/net/wireless/ath/ath11k/Kconfig @@ -3,6 +3,7 @@ config ATH11K tristate "Qualcomm Technologies 802.11ax chipset support" depends on MAC80211 && HAS_DMA depends on REMOTEPROC + depends on CRYPTO_MICHAEL_MIC depends on ARCH_QCOM || COMPILE_TEST select ATH_COMMON select QCOM_QMI_HELPERS diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile index 2761d07d938e..fe7736e53583 100644 --- a/drivers/net/wireless/ath/ath11k/Makefile +++ b/drivers/net/wireless/ath/ath11k/Makefile @@ -20,6 +20,7 @@ ath11k-y += core.o \ ath11k-$(CONFIG_ATH11K_DEBUGFS) += debug_htt_stats.o debugfs_sta.o ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o ath11k-$(CONFIG_ATH11K_TRACING) += trace.o +ath11k-$(CONFIG_THERMAL) += thermal.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index e7e3e64c07aa..59342d2797ca 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -458,7 +458,6 @@ static void ath11k_ahb_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) static void __ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) { - struct sk_buff *skb; int i; for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { @@ -468,9 +467,6 @@ static void __ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) napi_synchronize(&irq_grp->napi); napi_disable(&irq_grp->napi); - - while ((skb = __skb_dequeue(&irq_grp->pending_q))) - dev_kfree_skb_any(skb); } } @@ -681,6 +677,9 @@ static irqreturn_t ath11k_ahb_ce_interrupt_handler(int irq, void *arg) { struct ath11k_ce_pipe *ce_pipe = arg; + /* last interrupt received for this CE */ + ce_pipe->timestamp = jiffies; + ath11k_ahb_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num); tasklet_schedule(&ce_pipe->intr_tq); @@ -712,6 +711,9 @@ static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg) { struct ath11k_ext_irq_grp *irq_grp = arg; + /* last interrupt received for this group */ + irq_grp->timestamp = jiffies; + ath11k_ahb_ext_grp_disable(irq_grp); napi_schedule(&irq_grp->napi); @@ -734,7 +736,6 @@ static int ath11k_ahb_ext_irq_config(struct ath11k_base *ab) init_dummy_netdev(&irq_grp->napi_ndev); netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, ath11k_ahb_ext_grp_napi_poll, NAPI_POLL_WEIGHT); - __skb_queue_head_init(&irq_grp->pending_q); for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) { if (ath11k_tx_ring_mask[i] & BIT(j)) { diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h index e355dfda48bf..688f357e6eaf 100644 --- a/drivers/net/wireless/ath/ath11k/ce.h +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -161,6 +161,7 @@ struct ath11k_ce_pipe { struct ath11k_ce_ring *src_ring; struct ath11k_ce_ring *dest_ring; struct ath11k_ce_ring *status_ring; + u64 timestamp; }; struct ath11k_ce { diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 9e823056e673..bf5657d2ae18 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -392,11 +392,19 @@ static int ath11k_core_pdev_create(struct ath11k_base *ab) goto err_mac_unregister; } + ret = ath11k_thermal_register(ab); + if (ret) { + ath11k_err(ab, "could not register thermal device: %d\n", + ret); + goto err_dp_pdev_free; + } + return 0; +err_dp_pdev_free: + ath11k_dp_pdev_free(ab); err_mac_unregister: ath11k_mac_unregister(ab); - err_pdev_debug: ath11k_debug_pdev_destroy(ab); @@ -405,6 +413,7 @@ err_pdev_debug: static void ath11k_core_pdev_destroy(struct ath11k_base *ab) { + ath11k_thermal_unregister(ab); ath11k_mac_unregister(ab); ath11k_ahb_ext_irq_disable(ab); ath11k_dp_pdev_free(ab); @@ -569,6 +578,7 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab) int ret; mutex_lock(&ab->core_lock); + ath11k_thermal_unregister(ab); ath11k_ahb_ext_irq_disable(ab); ath11k_dp_pdev_free(ab); ath11k_ahb_stop(ab); @@ -607,6 +617,7 @@ void ath11k_core_halt(struct ath11k *ar) lockdep_assert_held(&ar->conf_mutex); ar->num_created_vdevs = 0; + ar->allocated_vdev_map = 0; ath11k_mac_scan_finish(ar); ath11k_mac_peer_cleanup_all(ar); @@ -644,6 +655,7 @@ static void ath11k_core_restart(struct work_struct *work) complete(&ar->install_key_done); complete(&ar->vdev_setup_done); complete(&ar->bss_survey_done); + complete(&ar->thermal.wmi_sync); wake_up(&ar->dp.tx_empty_waitq); idr_for_each(&ar->txmgmt_idr, diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 25cdcf71d0c4..6e7b8ecd09a6 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -20,6 +20,7 @@ #include "hw.h" #include "hal_rx.h" #include "reg.h" +#include "thermal.h" #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -76,6 +77,8 @@ struct ath11k_skb_rxcb { u8 err_code; u8 mac_id; u8 unmapped; + u8 is_frag; + u8 tid; }; enum ath11k_hw_rev { @@ -108,12 +111,9 @@ struct ath11k_ext_irq_grp { u32 irqs[ATH11K_EXT_IRQ_NUM_MAX]; u32 num_irq; u32 grp_id; + u64 timestamp; struct napi_struct napi; struct net_device napi_ndev; - /* Queue of pending packets, not expected to be accessed concurrently - * to avoid locking overhead. - */ - struct sk_buff_head pending_q; }; #define HEHANDLE_CAP_PHYINFO_SIZE 3 @@ -243,6 +243,8 @@ struct ath11k_rx_peer_stats { u64 pream_cnt[HAL_RX_PREAMBLE_MAX]; u64 reception_type[HAL_RX_RECEPTION_TYPE_MAX]; u64 rx_duration; + u64 dcm_count; + u64 ru_alloc_cnt[HAL_RX_RU_ALLOC_TYPE_MAX]; }; #define ATH11K_HE_MCS_NUM 12 @@ -329,9 +331,9 @@ struct ath11k_sta { u32 bw; u32 nss; u32 smps; + enum hal_pn_type pn_type; struct work_struct update_wk; - struct ieee80211_tx_info tx_info; struct rate_info txrate; struct rate_info last_txrate; u64 rx_duration; @@ -486,6 +488,7 @@ struct ath11k { int max_num_peers; u32 num_started_vdevs; u32 num_created_vdevs; + unsigned long long allocated_vdev_map; struct idr txmgmt_idr; /* protects txmgmt_idr data */ @@ -524,6 +527,7 @@ struct ath11k { struct ath11k_debug debug; #endif bool dfs_block_radar_events; + struct ath11k_thermal thermal; }; struct ath11k_band_cap { diff --git a/drivers/net/wireless/ath/ath11k/debug.h b/drivers/net/wireless/ath/ath11k/debug.h index 8e8d5588b541..97e7306c506d 100644 --- a/drivers/net/wireless/ath/ath11k/debug.h +++ b/drivers/net/wireless/ath/ath11k/debug.h @@ -53,6 +53,8 @@ enum ath11k_dbg_htt_ext_stats_type { ATH11K_DBG_HTT_EXT_STATS_TWT_SESSIONS = 20, ATH11K_DBG_HTT_EXT_STATS_REO_RESOURCE_STATS = 21, ATH11K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, + ATH11K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, + ATH11K_DBG_HTT_EXT_STATS_RING_BACKPRESSURE_STATS = 24, /* keep this last */ ATH11K_DBG_HTT_NUM_EXT_STATS, @@ -68,12 +70,19 @@ struct debug_htt_stats_req { u8 buf[0]; }; -#define ATH11K_HTT_STATS_BUF_SIZE (1024 * 512) +struct ath_pktlog_hdr { + u16 flags; + u16 missed_cnt; + u16 log_type; + u16 size; + u32 timestamp; + u32 type_specific_data; + u8 payload[0]; +}; +#define ATH11K_HTT_STATS_BUF_SIZE (1024 * 512) #define ATH11K_FW_STATS_BUF_SIZE (1024 * 1024) -#define ATH11K_HTT_PKTLOG_MAX_SIZE 2048 - enum ath11k_pktlog_filter { ATH11K_PKTLOG_RX = 0x000000001, ATH11K_PKTLOG_TX = 0x000000002, diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.c b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c index 9939e909628f..5db0c27de475 100644 --- a/drivers/net/wireless/ath/ath11k/debug_htt_stats.c +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c @@ -22,7 +22,7 @@ do { \ int index = 0; u8 i; \ for (i = 0; i < len; i++) { \ - index += snprintf(out + index, HTT_MAX_STRING_LEN - index, \ + index += scnprintf(out + index, HTT_MAX_STRING_LEN - index, \ " %u:%u,", i, arr[i]); \ if (index < 0 || index >= HTT_MAX_STRING_LEN) \ break; \ @@ -46,7 +46,7 @@ static inline void htt_print_stats_string_tlv(const void *tag_buf, len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_STATS_STRING_TLV:"); for (i = 0; i < tag_len; i++) { - index += snprintf(&data[index], + index += scnprintf(&data[index], HTT_MAX_STRING_LEN - index, "%.*s", 4, (char *)&(htt_stats_buf->data[i])); if (index >= HTT_MAX_STRING_LEN) @@ -3097,7 +3097,7 @@ static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, index = 0; for (i = 0; i < HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_PER_NSS; i++) - index += snprintf(&rx_pilot_evm_db[j][index], + index += scnprintf(&rx_pilot_evm_db[j][index], HTT_MAX_STRING_LEN - index, " %u:%d,", i, @@ -3109,7 +3109,7 @@ static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, index = 0; memset(str_buf, 0x0, HTT_MAX_STRING_LEN); for (i = 0; i < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++) - index += snprintf(&str_buf[index], + index += scnprintf(&str_buf[index], HTT_MAX_STRING_LEN - index, " %u:%d,", i, htt_stats_buf->rx_pilot_evm_db_mean[i]); len += HTT_DBG_OUT(buf + len, buf_len - len, "pilot_evm_dB_mean = %s ", str_buf); @@ -3217,7 +3217,7 @@ static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, index = 0; memset(str_buf, 0x0, HTT_MAX_STRING_LEN); for (i = 0; i < HTT_RX_PDEV_MAX_OFDMA_NUM_USER; i++) - index += snprintf(&str_buf[index], + index += scnprintf(&str_buf[index], HTT_MAX_STRING_LEN - index, " %u:%d,", i, htt_stats_buf->rx_ul_fd_rssi[j][i]); @@ -3232,7 +3232,7 @@ static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, index = 0; memset(str_buf, 0x0, HTT_MAX_STRING_LEN); for (i = 0; i < HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) - index += snprintf(&str_buf[index], + index += scnprintf(&str_buf[index], HTT_MAX_STRING_LEN - index, " %u:%d,", i, @@ -3854,6 +3854,47 @@ htt_print_pdev_obss_pd_stats_tlv_v(const void *tag_buf, stats_req->buf_len = len; } +static inline void htt_print_backpressure_stats_tlv_v(const u32 *tag_buf, + u8 *data) +{ + struct debug_htt_stats_req *stats_req = + (struct debug_htt_stats_req *)data; + struct htt_ring_backpressure_stats_tlv *htt_stats_buf = + (struct htt_ring_backpressure_stats_tlv *)tag_buf; + int i; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_id = %u", + htt_stats_buf->pdev_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "current_head_idx = %u", + htt_stats_buf->current_head_idx); + len += HTT_DBG_OUT(buf + len, buf_len - len, "current_tail_idx = %u", + htt_stats_buf->current_tail_idx); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_htt_msgs_sent = %u", + htt_stats_buf->num_htt_msgs_sent); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "backpressure_time_ms = %u", + htt_stats_buf->backpressure_time_ms); + + for (i = 0; i < 5; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "backpressure_hist_%u = %u", + i + 1, htt_stats_buf->backpressure_hist[i]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "============================"); + + if (len >= buf_len) { + buf[buf_len - 1] = 0; + stats_req->buf_len = buf_len - 1; + } else { + buf[len] = 0; + stats_req->buf_len = len; + } +} + static inline void htt_htt_stats_debug_dump(const u32 *tag_buf, struct debug_htt_stats_req *stats_req) { @@ -4246,6 +4287,9 @@ static int ath11k_dbg_htt_ext_stats_parse(struct ath11k_base *ab, case HTT_STATS_PDEV_OBSS_PD_TAG: htt_print_pdev_obss_pd_stats_tlv_v(tag_buf, stats_req); break; + case HTT_STATS_RING_BACKPRESSURE_STATS_TAG: + htt_print_backpressure_stats_tlv_v(tag_buf, user_data); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.h b/drivers/net/wireless/ath/ath11k/debug_htt_stats.h index 4bdb62dd7b8d..23a6baa9e95a 100644 --- a/drivers/net/wireless/ath/ath11k/debug_htt_stats.h +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.h @@ -100,6 +100,8 @@ enum htt_tlv_tag_t { HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG = 86, HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87, HTT_STATS_PDEV_OBSS_PD_TAG = 88, + HTT_STATS_HW_WAR_TAG = 89, + HTT_STATS_RING_BACKPRESSURE_STATS_TAG = 90, HTT_STATS_MAX_TAG, }; @@ -1659,4 +1661,30 @@ struct htt_pdev_obss_pd_stats_tlv { }; void ath11k_debug_htt_stats_init(struct ath11k *ar); + +struct htt_ring_backpressure_stats_tlv { + u32 pdev_id; + u32 current_head_idx; + u32 current_tail_idx; + u32 num_htt_msgs_sent; + /* Time in milliseconds for which the ring has been in + * its current backpressure condition + */ + u32 backpressure_time_ms; + /* backpressure_hist - histogram showing how many times + * different degrees of backpressure duration occurred: + * Index 0 indicates the number of times ring was + * continuously in backpressure state for 100 - 200ms. + * Index 1 indicates the number of times ring was + * continuously in backpressure state for 200 - 300ms. + * Index 2 indicates the number of times ring was + * continuously in backpressure state for 300 - 400ms. + * Index 3 indicates the number of times ring was + * continuously in backpressure state for 400 - 500ms. + * Index 4 indicates the number of times ring was + * continuously in backpressure state beyond 500ms. + */ + u32 backpressure_hist[5]; +}; + #endif diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.c b/drivers/net/wireless/ath/ath11k/debugfs_sta.c index 743760c9bcae..389dac219238 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c @@ -24,7 +24,7 @@ ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, tx_stats = arsta->tx_stats; gi = FIELD_GET(RATE_INFO_FLAGS_SHORT_GI, arsta->txrate.flags); mcs = txrate->mcs; - bw = txrate->bw; + bw = ath11k_mac_mac80211_bw_to_ath11k_bw(txrate->bw); nss = txrate->nss - 1; #define STATS_OP_FMT(name) tx_stats->stats[ATH11K_STATS_TYPE_##name] @@ -136,7 +136,7 @@ void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, struct ath11k_sta *arsta; struct ieee80211_sta *sta; u16 rate; - u8 rate_idx; + u8 rate_idx = 0; int ret; u8 mcs; @@ -219,6 +219,9 @@ static ssize_t ath11k_dbg_sta_dump_tx_stats(struct file *file, const int size = 2 * 4096; char *buf; + if (!arsta->tx_stats) + return -ENOENT; + buf = kzalloc(size, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -379,6 +382,13 @@ static ssize_t ath11k_dbg_sta_dump_rx_stats(struct file *file, len += scnprintf(buf + len, size - len, "%llu ", rx_stats->nss_count[i]); len += scnprintf(buf + len, size - len, "\nRX Duration:%llu ", rx_stats->rx_duration); + len += scnprintf(buf + len, size - len, + "\nDCM: %llu\nRU: 26 %llu 52: %llu 106: %llu 242: %llu 484: %llu 996: %llu\n", + rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0], + rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2], + rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4], + rx_stats->ru_alloc_cnt[5]); + len += scnprintf(buf + len, size - len, "\n"); spin_unlock_bh(&ar->ab->base_lock); diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index b112825a52ed..50350f77b309 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -3,6 +3,7 @@ * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ +#include <crypto/hash.h> #include "core.h" #include "dp_tx.h" #include "hal_tx.h" @@ -33,14 +34,16 @@ void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr) } ath11k_peer_rx_tid_cleanup(ar, peer); + crypto_free_shash(peer->tfm_mmic); spin_unlock_bh(&ab->base_lock); } int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr) { struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; u32 reo_dest; - int ret; + int ret = 0, tid; /* NOTE: reo_dest ring id starts from 1 unlike mac_id which starts from 0 */ reo_dest = ar->dp.mac_id + 1; @@ -54,24 +57,42 @@ int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr) return ret; } - ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, - HAL_DESC_REO_NON_QOS_TID, 1, 0); - if (ret) { - ath11k_warn(ab, "failed to setup rxd tid queue for non-qos tid %d\n", - ret); - return ret; + for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) { + ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, tid, 1, 0, + HAL_PN_TYPE_NONE); + if (ret) { + ath11k_warn(ab, "failed to setup rxd tid queue for tid %d: %d\n", + tid, ret); + goto peer_clean; + } } - ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, 0, 1, 0); + ret = ath11k_peer_rx_frag_setup(ar, addr, vdev_id); if (ret) { - ath11k_warn(ab, "failed to setup rxd tid queue for tid 0 %d\n", - ret); + ath11k_warn(ab, "failed to setup rx defrag context\n"); return ret; } /* TODO: Setup other peer specific resource used in data path */ return 0; + +peer_clean: + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, vdev_id, addr); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to del rx tid\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; + } + + for (; tid >= 0; tid--) + ath11k_peer_rx_tid_delete(ar, peer, tid); + + spin_unlock_bh(&ab->base_lock); + + return ret; } void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) @@ -197,6 +218,7 @@ static int ath11k_dp_srng_common_setup(struct ath11k_base *ab) struct ath11k_dp *dp = &ab->dp; struct hal_srng *srng; int i, ret; + u32 ring_hash_map; ret = ath11k_dp_srng_setup(ab, &dp->wbm_desc_rel_ring, HAL_SW2WBM_RELEASE, 0, 0, @@ -284,7 +306,21 @@ static int ath11k_dp_srng_common_setup(struct ath11k_base *ab) goto err; } - ath11k_hal_reo_hw_setup(ab); + /* When hash based routing of rx packet is enabled, 32 entries to map + * the hash values to the ring will be configured. Each hash entry uses + * three bits to map to a particular ring. The ring mapping will be + * 0:TCL, 1:SW1, 2:SW2, 3:SW3, 4:SW4, 5:Release, 6:FW and 7:Not used. + */ + ring_hash_map = HAL_HASH_ROUTING_RING_SW1 << 0 | + HAL_HASH_ROUTING_RING_SW2 << 3 | + HAL_HASH_ROUTING_RING_SW3 << 6 | + HAL_HASH_ROUTING_RING_SW4 << 9 | + HAL_HASH_ROUTING_RING_SW1 << 12 | + HAL_HASH_ROUTING_RING_SW2 << 15 | + HAL_HASH_ROUTING_RING_SW3 << 18 | + HAL_HASH_ROUTING_RING_SW4 << 21; + + ath11k_hal_reo_hw_setup(ab, ring_hash_map); return 0; @@ -614,17 +650,13 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, } if (ath11k_rx_ring_mask[grp_id]) { - for (i = 0; i < ab->num_radios; i++) { - if (ath11k_rx_ring_mask[grp_id] & BIT(i)) { - work_done = ath11k_dp_process_rx(ab, i, napi, - &irq_grp->pending_q, - budget); - budget -= work_done; - tot_work_done += work_done; - } - if (budget <= 0) - goto done; - } + i = fls(ath11k_rx_ring_mask[grp_id]) - 1; + work_done = ath11k_dp_process_rx(ab, i, napi, + budget); + budget -= work_done; + tot_work_done += work_done; + if (budget <= 0) + goto done; } if (rx_mon_status_ring_mask[grp_id]) { diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index 6ef5be4201b2..551f9c9fb847 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -22,6 +22,18 @@ struct dp_rx_tid { u32 size; u32 ba_win_sz; bool active; + + /* Info related to rx fragments */ + u32 cur_sn; + u16 last_frag_no; + u16 rx_frag_bitmap; + + struct sk_buff_head rx_frags; + struct hal_reo_dest_ring *dst_ring_desc; + + /* Timer info related to fragments */ + struct timer_list frag_timer; + struct ath11k_base *ab; }; #define DP_REO_DESC_FREE_TIMEOUT_MS 1000 @@ -128,7 +140,6 @@ struct ath11k_pdev_dp { u32 mac_id; atomic_t num_tx_pending; wait_queue_head_t tx_empty_waitq; - struct dp_srng reo_dst_ring; struct dp_rxdma_ring rx_refill_buf_ring; struct dp_srng rxdma_err_dst_ring; struct dp_srng rxdma_mon_dst_ring; @@ -148,7 +159,7 @@ struct ath11k_pdev_dp { #define DP_AVG_MPDUS_PER_TID_MAX 128 #define DP_AVG_MSDUS_PER_MPDU 4 -#define DP_RX_HASH_ENABLE 0 /* Disable hash based Rx steering */ +#define DP_RX_HASH_ENABLE 1 /* Enable hash based Rx steering */ #define DP_BA_WIN_SZ_MAX 256 @@ -168,7 +179,7 @@ struct ath11k_pdev_dp { #define DP_RX_RELEASE_RING_SIZE 1024 #define DP_REO_EXCEPTION_RING_SIZE 128 #define DP_REO_CMD_RING_SIZE 128 -#define DP_REO_STATUS_RING_SIZE 256 +#define DP_REO_STATUS_RING_SIZE 2048 #define DP_RXDMA_BUF_RING_SIZE 4096 #define DP_RXDMA_REFILL_RING_SIZE 2048 #define DP_RXDMA_ERR_DST_RING_SIZE 1024 @@ -206,6 +217,7 @@ struct ath11k_dp { struct dp_srng reo_except_ring; struct dp_srng reo_cmd_ring; struct dp_srng reo_status_ring; + struct dp_srng reo_dst_ring[DP_REO_DST_RING_MAX]; struct dp_tx_ring tx_ring[DP_TCL_NUM_RING_MAX]; struct hal_wbm_idle_scatter_list scatter_list[DP_IDLE_SCATTER_BUFS_MAX]; struct list_head reo_cmd_list; @@ -924,6 +936,7 @@ enum htt_t2h_msg_type { HTT_T2H_MSG_TYPE_PEER_UNMAP = 0x1f, HTT_T2H_MSG_TYPE_PPDU_STATS_IND = 0x1d, HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c, + HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND = 0x24, }; #define HTT_TARGET_VERSION_MAJOR 3 @@ -972,6 +985,13 @@ struct htt_resp_msg { }; } __packed; +#define HTT_BACKPRESSURE_EVENT_PDEV_ID_M GENMASK(15, 8) +#define HTT_BACKPRESSURE_EVENT_RING_TYPE_M GENMASK(23, 16) +#define HTT_BACKPRESSURE_EVENT_RING_ID_M GENMASK(31, 24) + +#define HTT_BACKPRESSURE_EVENT_HP_M GENMASK(15, 0) +#define HTT_BACKPRESSURE_EVENT_TP_M GENMASK(31, 16) + /* ppdu stats * * @details @@ -1066,6 +1086,13 @@ struct htt_ppdu_stats_common { u16 bw_mhz; } __packed; +enum htt_ppdu_stats_gi { + HTT_PPDU_STATS_SGI_0_8_US, + HTT_PPDU_STATS_SGI_0_4_US, + HTT_PPDU_STATS_SGI_1_6_US, + HTT_PPDU_STATS_SGI_3_2_US, +}; + #define HTT_PPDU_STATS_USER_RATE_INFO0_USER_POS_M GENMASK(3, 0) #define HTT_PPDU_STATS_USER_RATE_INFO0_MU_GROUP_ID_M GENMASK(11, 4) @@ -1094,6 +1121,8 @@ struct htt_ppdu_stats_common { FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_MCS_M, _val) #define HTT_USR_RATE_GI(_val) \ FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M, _val) +#define HTT_USR_RATE_DCM(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_DCM_M, _val) #define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LTF_SIZE_M GENMASK(1, 0) #define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_STBC_M BIT(2) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index b08da839b7d9..f74a0e74bf3e 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4,6 +4,9 @@ */ #include <linux/ieee80211.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <crypto/hash.h> #include "core.h" #include "debug.h" #include "hal_desc.h" @@ -13,6 +16,8 @@ #include "dp_tx.h" #include "peer.h" +#define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) + static u8 *ath11k_dp_rx_h_80211_hdr(struct hal_rx_desc *desc) { return desc->hdr_status; @@ -28,22 +33,56 @@ static enum hal_encrypt_type ath11k_dp_rx_h_mpdu_start_enctype(struct hal_rx_des __le32_to_cpu(desc->mpdu_start.info2)); } -static u8 ath11k_dp_rx_h_mpdu_start_decap_type(struct hal_rx_desc *desc) +static u8 ath11k_dp_rx_h_msdu_start_decap_type(struct hal_rx_desc *desc) { - return FIELD_GET(RX_MPDU_START_INFO5_DECAP_TYPE, - __le32_to_cpu(desc->mpdu_start.info5)); + return FIELD_GET(RX_MSDU_START_INFO2_DECAP_FORMAT, + __le32_to_cpu(desc->msdu_start.info2)); } -static bool ath11k_dp_rx_h_attn_msdu_done(struct hal_rx_desc *desc) +static u8 ath11k_dp_rx_h_msdu_start_mesh_ctl_present(struct hal_rx_desc *desc) { - return !!FIELD_GET(RX_ATTENTION_INFO2_MSDU_DONE, - __le32_to_cpu(desc->attention.info2)); + return FIELD_GET(RX_MSDU_START_INFO2_MESH_CTRL_PRESENT, + __le32_to_cpu(desc->msdu_start.info2)); } -static bool ath11k_dp_rx_h_attn_first_mpdu(struct hal_rx_desc *desc) +static bool ath11k_dp_rx_h_mpdu_start_seq_ctrl_valid(struct hal_rx_desc *desc) { - return !!FIELD_GET(RX_ATTENTION_INFO1_FIRST_MPDU, - __le32_to_cpu(desc->attention.info1)); + return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID, + __le32_to_cpu(desc->mpdu_start.info1)); +} + +static bool ath11k_dp_rx_h_mpdu_start_fc_valid(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_FCTRL_VALID, + __le32_to_cpu(desc->mpdu_start.info1)); +} + +static bool ath11k_dp_rx_h_mpdu_start_more_frags(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *)(skb->data + HAL_RX_DESC_SIZE); + return ieee80211_has_morefrags(hdr->frame_control); +} + +static u16 ath11k_dp_rx_h_mpdu_start_frag_no(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *)(skb->data + HAL_RX_DESC_SIZE); + return le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; +} + +static u16 ath11k_dp_rx_h_mpdu_start_seq_no(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_NUM, + __le32_to_cpu(desc->mpdu_start.info1)); +} + +static bool ath11k_dp_rx_h_attn_msdu_done(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO2_MSDU_DONE, + __le32_to_cpu(desc->attention.info2)); } static bool ath11k_dp_rx_h_attn_l4_cksum_fail(struct hal_rx_desc *desc) @@ -137,6 +176,17 @@ static u8 ath11k_dp_rx_h_msdu_start_nss(struct hal_rx_desc *desc) return hweight8(mimo_ss_bitmap); } +static u8 ath11k_dp_rx_h_mpdu_start_tid(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO2_TID, + __le32_to_cpu(desc->mpdu_start.info2)); +} + +static u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct hal_rx_desc *desc) +{ + return __le16_to_cpu(desc->mpdu_start.sw_peer_id); +} + static u8 ath11k_dp_rx_h_msdu_end_l3pad(struct hal_rx_desc *desc) { return FIELD_GET(RX_MSDU_END_INFO2_L3_HDR_PADDING, @@ -402,32 +452,25 @@ static void ath11k_dp_rx_pdev_srng_free(struct ath11k *ar) void ath11k_dp_pdev_reo_cleanup(struct ath11k_base *ab) { - struct ath11k_pdev_dp *dp; - struct ath11k *ar; + struct ath11k_dp *dp = &ab->dp; int i; - for (i = 0; i < ab->num_radios; i++) { - ar = ab->pdevs[i].ar; - dp = &ar->dp; - ath11k_dp_srng_cleanup(ab, &dp->reo_dst_ring); - } + for (i = 0; i < DP_REO_DST_RING_MAX; i++) + ath11k_dp_srng_cleanup(ab, &dp->reo_dst_ring[i]); } int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab) { - struct ath11k *ar; - struct ath11k_pdev_dp *dp; + struct ath11k_dp *dp = &ab->dp; int ret; int i; - for (i = 0; i < ab->num_radios; i++) { - ar = ab->pdevs[i].ar; - dp = &ar->dp; - ret = ath11k_dp_srng_setup(ab, &dp->reo_dst_ring, HAL_REO_DST, - dp->mac_id, dp->mac_id, + for (i = 0; i < DP_REO_DST_RING_MAX; i++) { + ret = ath11k_dp_srng_setup(ab, &dp->reo_dst_ring[i], + HAL_REO_DST, i, 0, DP_REO_DST_RING_SIZE); if (ret) { - ath11k_warn(ar->ab, "failed to setup reo_dst_ring\n"); + ath11k_warn(ab, "failed to setup reo_dst_ring\n"); goto err_reo_cleanup; } } @@ -633,8 +676,8 @@ free_desc: kfree(rx_tid->vaddr); } -static void ath11k_peer_rx_tid_delete(struct ath11k *ar, - struct ath11k_peer *peer, u8 tid) +void ath11k_peer_rx_tid_delete(struct ath11k *ar, + struct ath11k_peer *peer, u8 tid) { struct ath11k_hal_reo_cmd cmd = {0}; struct dp_rx_tid *rx_tid = &peer->rx_tid[tid]; @@ -661,12 +704,75 @@ static void ath11k_peer_rx_tid_delete(struct ath11k *ar, rx_tid->active = false; } +static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab, + u32 *link_desc, + enum hal_wbm_rel_bm_act action) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng; + u32 *desc; + int ret = 0; + + srng = &ab->hal.srng_list[dp->wbm_desc_rel_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) { + ret = -ENOBUFS; + goto exit; + } + + ath11k_hal_rx_msdu_link_desc_set(ab, (void *)desc, (void *)link_desc, + action); + +exit: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return ret; +} + +static void ath11k_dp_rx_frags_cleanup(struct dp_rx_tid *rx_tid, bool rel_link_desc) +{ + struct ath11k_base *ab = rx_tid->ab; + + lockdep_assert_held(&ab->base_lock); + + if (rx_tid->dst_ring_desc) { + if (rel_link_desc) + ath11k_dp_rx_link_desc_return(ab, (u32 *)rx_tid->dst_ring_desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + kfree(rx_tid->dst_ring_desc); + rx_tid->dst_ring_desc = NULL; + } + + rx_tid->cur_sn = 0; + rx_tid->last_frag_no = 0; + rx_tid->rx_frag_bitmap = 0; + __skb_queue_purge(&rx_tid->rx_frags); +} + void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer) { + struct dp_rx_tid *rx_tid; int i; - for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + lockdep_assert_held(&ar->ab->base_lock); + + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) { + rx_tid = &peer->rx_tid[i]; + ath11k_peer_rx_tid_delete(ar, peer, i); + ath11k_dp_rx_frags_cleanup(rx_tid, true); + + spin_unlock_bh(&ar->ab->base_lock); + del_timer_sync(&rx_tid->frag_timer); + spin_lock_bh(&ar->ab->base_lock); + } } static int ath11k_peer_rx_tid_reo_update(struct ath11k *ar, @@ -732,7 +838,8 @@ unlock_exit: } int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, - u8 tid, u32 ba_win_sz, u16 ssn) + u8 tid, u32 ba_win_sz, u16 ssn, + enum hal_pn_type pn_type) { struct ath11k_base *ab = ar->ab; struct ath11k_peer *peer; @@ -793,7 +900,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); - ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, ssn); + ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, + ssn, pn_type); paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, DMA_BIDIRECTIONAL); @@ -837,7 +945,7 @@ int ath11k_dp_rx_ampdu_start(struct ath11k *ar, ret = ath11k_peer_rx_tid_setup(ar, params->sta->addr, vdev_id, params->tid, params->buf_size, - params->ssn); + params->ssn, arsta->pn_type); if (ret) ath11k_warn(ab, "failed to setup rx tid %d\n", ret); @@ -890,8 +998,80 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, return ret; } -static int ath11k_get_ppdu_user_index(struct htt_ppdu_stats *ppdu_stats, - u16 peer_id) +int ath11k_dp_peer_rx_pn_replay_config(struct ath11k_vif *arvif, + const u8 *peer_addr, + enum set_key_cmd key_cmd, + struct ieee80211_key_conf *key) +{ + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct ath11k_hal_reo_cmd cmd = {0}; + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + u8 tid; + int ret = 0; + + /* NOTE: Enable PN/TSC replay check offload only for unicast frames. + * We use mac80211 PN/TSC replay check functionality for bcast/mcast + * for now. + */ + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return 0; + + cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS; + cmd.upd0 |= HAL_REO_CMD_UPD0_PN | + HAL_REO_CMD_UPD0_PN_SIZE | + HAL_REO_CMD_UPD0_PN_VALID | + HAL_REO_CMD_UPD0_PN_CHECK | + HAL_REO_CMD_UPD0_SVLD; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + if (key_cmd == SET_KEY) { + cmd.upd1 |= HAL_REO_CMD_UPD1_PN_CHECK; + cmd.pn_size = 48; + } + break; + default: + break; + } + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to configure pn replay detection\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; + } + + for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) { + rx_tid = &peer->rx_tid[tid]; + if (!rx_tid->active) + continue; + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid, + HAL_REO_CMD_UPDATE_RX_QUEUE, + &cmd, NULL); + if (ret) { + ath11k_warn(ab, "failed to configure rx tid %d queue for pn replay detection %d\n", + tid, ret); + break; + } + } + + spin_unlock_bh(&ar->ab->base_lock); + + return ret; +} + +static inline int ath11k_get_ppdu_user_index(struct htt_ppdu_stats *ppdu_stats, + u16 peer_id) { int i; @@ -1028,23 +1208,23 @@ int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, return 0; } -static u32 ath11k_bw_to_mac80211_bwflags(u8 bw) +static inline u32 ath11k_he_gi_to_nl80211_he_gi(u8 sgi) { - u32 bwflags = 0; + u32 ret = 0; - switch (bw) { - case ATH11K_BW_40: - bwflags = IEEE80211_TX_RC_40_MHZ_WIDTH; + switch (sgi) { + case RX_MSDU_START_SGI_0_8_US: + ret = NL80211_RATE_INFO_HE_GI_0_8; break; - case ATH11K_BW_80: - bwflags = IEEE80211_TX_RC_80_MHZ_WIDTH; + case RX_MSDU_START_SGI_1_6_US: + ret = NL80211_RATE_INFO_HE_GI_1_6; break; - case ATH11K_BW_160: - bwflags = IEEE80211_TX_RC_160_MHZ_WIDTH; + case RX_MSDU_START_SGI_3_2_US: + ret = NL80211_RATE_INFO_HE_GI_3_2; break; } - return bwflags; + return ret; } static void @@ -1056,12 +1236,11 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar, struct ieee80211_sta *sta; struct ath11k_sta *arsta; struct htt_ppdu_stats_user_rate *user_rate; - struct ieee80211_chanctx_conf *conf = NULL; struct ath11k_per_peer_tx_stats *peer_stats = &ar->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, rate_idx = 0; + u8 flags, mcs, nss, bw, sgi, dcm, rate_idx = 0; u32 succ_bytes = 0; u16 rate = 0, succ_pkts = 0; u32 tx_duration = 0; @@ -1096,18 +1275,29 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar, 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); /* 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_VHT && mcs > 9) { + if (flags == WMI_RATE_PREAMBLE_HE && mcs > 11) { + ath11k_warn(ab, "Invalid HE mcs %hhd peer stats", mcs); + return; + } + + if (flags == WMI_RATE_PREAMBLE_HE && mcs > ATH11K_HE_MCS_MAX) { + ath11k_warn(ab, "Invalid HE mcs %hhd peer stats", mcs); + return; + } + + if (flags == WMI_RATE_PREAMBLE_VHT && mcs > ATH11K_VHT_MCS_MAX) { ath11k_warn(ab, "Invalid VHT mcs %hhd peer stats", mcs); return; } - if (flags == WMI_RATE_PREAMBLE_HT && (mcs > 7 || nss < 1)) { + if (flags == WMI_RATE_PREAMBLE_HT && (mcs > ATH11K_HT_MCS_MAX || nss < 1)) { ath11k_warn(ab, "Invalid HT mcs %hhd nss %hhd peer stats", mcs, nss); return; @@ -1136,60 +1326,42 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar, arsta = (struct ath11k_sta *)sta->drv_priv; memset(&arsta->txrate, 0, sizeof(arsta->txrate)); - memset(&arsta->tx_info.status, 0, sizeof(arsta->tx_info.status)); switch (flags) { case WMI_RATE_PREAMBLE_OFDM: arsta->txrate.legacy = rate; - if (arsta->arvif && arsta->arvif->vif) - conf = rcu_dereference(arsta->arvif->vif->chanctx_conf); - if (conf && conf->def.chan->band == NL80211_BAND_5GHZ) - arsta->tx_info.status.rates[0].idx = rate_idx - 4; break; case WMI_RATE_PREAMBLE_CCK: arsta->txrate.legacy = rate; - arsta->tx_info.status.rates[0].idx = rate_idx; - if (mcs > ATH11K_HW_RATE_CCK_LP_1M && - mcs <= ATH11K_HW_RATE_CCK_SP_2M) - arsta->tx_info.status.rates[0].flags |= - IEEE80211_TX_RC_USE_SHORT_PREAMBLE; break; case WMI_RATE_PREAMBLE_HT: arsta->txrate.mcs = mcs + 8 * (nss - 1); - arsta->tx_info.status.rates[0].idx = arsta->txrate.mcs; arsta->txrate.flags = RATE_INFO_FLAGS_MCS; - arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_MCS; - if (sgi) { + if (sgi) arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - arsta->tx_info.status.rates[0].flags |= - IEEE80211_TX_RC_SHORT_GI; - } break; case WMI_RATE_PREAMBLE_VHT: arsta->txrate.mcs = mcs; - ieee80211_rate_set_vht(&arsta->tx_info.status.rates[0], mcs, nss); arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; - arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_VHT_MCS; - if (sgi) { + if (sgi) arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - arsta->tx_info.status.rates[0].flags |= - IEEE80211_TX_RC_SHORT_GI; - } + break; + case WMI_RATE_PREAMBLE_HE: + arsta->txrate.mcs = mcs; + arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS; + arsta->txrate.he_dcm = dcm; + arsta->txrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi); + arsta->txrate.he_ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc( + (user_rate->ru_end - + user_rate->ru_start) + 1); break; } arsta->txrate.nss = nss; arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); - arsta->tx_info.status.rates[0].flags |= ath11k_bw_to_mac80211_bwflags(bw); arsta->tx_duration += tx_duration; memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info)); - if (succ_pkts) { - arsta->tx_info.flags = IEEE80211_TX_STAT_ACK; - arsta->tx_info.status.rates[0].count = 1; - ieee80211_tx_rate_update(ar->hw, sta, &arsta->tx_info); - } - /* PPDU stats reported for mgmt packet doesn't have valid tx bytes. * So skip peer stats update for mgmt packets. */ @@ -1308,18 +1480,10 @@ exit: static void ath11k_htt_pktlog(struct ath11k_base *ab, struct sk_buff *skb) { struct htt_pktlog_msg *data = (struct htt_pktlog_msg *)skb->data; + struct ath_pktlog_hdr *hdr = (struct ath_pktlog_hdr *)data; struct ath11k *ar; - u32 len; u8 pdev_id; - len = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE, data->hdr); - if (len > ATH11K_HTT_PKTLOG_MAX_SIZE) { - ath11k_warn(ab, "htt pktlog buffer size %d, expected < %d\n", - len, - ATH11K_HTT_PKTLOG_MAX_SIZE); - return; - } - pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, data->hdr); ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); if (!ar) { @@ -1327,7 +1491,30 @@ static void ath11k_htt_pktlog(struct ath11k_base *ab, struct sk_buff *skb) return; } - trace_ath11k_htt_pktlog(ar, data->payload, len); + trace_ath11k_htt_pktlog(ar, data->payload, hdr->size); +} + +static void ath11k_htt_backpressure_event_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + u32 *data = (u32 *)skb->data; + u8 pdev_id, ring_type, ring_id; + u16 hp, tp; + u32 backpressure_time; + + pdev_id = FIELD_GET(HTT_BACKPRESSURE_EVENT_PDEV_ID_M, *data); + ring_type = FIELD_GET(HTT_BACKPRESSURE_EVENT_RING_TYPE_M, *data); + ring_id = FIELD_GET(HTT_BACKPRESSURE_EVENT_RING_ID_M, *data); + ++data; + + hp = FIELD_GET(HTT_BACKPRESSURE_EVENT_HP_M, *data); + tp = FIELD_GET(HTT_BACKPRESSURE_EVENT_TP_M, *data); + ++data; + + backpressure_time = *data; + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt backpressure event, pdev %d, ring type %d,ring id %d, hp %d tp %d, backpressure time %d\n", + pdev_id, ring_type, ring_id, hp, tp, backpressure_time); } void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, @@ -1379,6 +1566,9 @@ void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, case HTT_T2H_MSG_TYPE_PKTLOG: ath11k_htt_pktlog(ab, skb); break; + case HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND: + ath11k_htt_backpressure_event_handler(ab, skb); + break; default: ath11k_warn(ab, "htt event %d not handled\n", type); break; @@ -1490,88 +1680,6 @@ static struct sk_buff *ath11k_dp_rx_get_msdu_last_buf(struct sk_buff_head *msdu_ return NULL; } -static int ath11k_dp_rx_retrieve_amsdu(struct ath11k *ar, - struct sk_buff_head *msdu_list, - struct sk_buff_head *amsdu_list) -{ - struct sk_buff *msdu = skb_peek(msdu_list); - struct sk_buff *last_buf; - struct ath11k_skb_rxcb *rxcb; - struct ieee80211_hdr *hdr; - struct hal_rx_desc *rx_desc, *lrx_desc; - u16 msdu_len; - u8 l3_pad_bytes; - u8 *hdr_status; - int ret; - - if (!msdu) - return -ENOENT; - - rx_desc = (struct hal_rx_desc *)msdu->data; - hdr_status = ath11k_dp_rx_h_80211_hdr(rx_desc); - hdr = (struct ieee80211_hdr *)hdr_status; - /* Process only data frames */ - if (!ieee80211_is_data(hdr->frame_control)) { - __skb_unlink(msdu, msdu_list); - dev_kfree_skb_any(msdu); - return -EINVAL; - } - - do { - __skb_unlink(msdu, msdu_list); - last_buf = ath11k_dp_rx_get_msdu_last_buf(msdu_list, msdu); - if (!last_buf) { - ath11k_warn(ar->ab, - "No valid Rx buffer to access Atten/MSDU_END/MPDU_END tlvs\n"); - ret = -EIO; - goto free_out; - } - - rx_desc = (struct hal_rx_desc *)msdu->data; - lrx_desc = (struct hal_rx_desc *)last_buf->data; - - if (!ath11k_dp_rx_h_attn_msdu_done(lrx_desc)) { - ath11k_warn(ar->ab, "msdu_done bit in attention is not set\n"); - ret = -EIO; - goto free_out; - } - - rxcb = ATH11K_SKB_RXCB(msdu); - rxcb->rx_desc = rx_desc; - msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(rx_desc); - l3_pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(lrx_desc); - - if (!rxcb->is_continuation) { - skb_put(msdu, HAL_RX_DESC_SIZE + l3_pad_bytes + msdu_len); - skb_pull(msdu, HAL_RX_DESC_SIZE + l3_pad_bytes); - } else { - ret = ath11k_dp_rx_msdu_coalesce(ar, msdu_list, - msdu, last_buf, - l3_pad_bytes, msdu_len); - if (ret) { - ath11k_warn(ar->ab, - "failed to coalesce msdu rx buffer%d\n", ret); - goto free_out; - } - } - __skb_queue_tail(amsdu_list, msdu); - - /* Should we also consider msdu_cnt from mpdu_meta while - * preparing amsdu list? - */ - if (rxcb->is_last_msdu) - break; - } while ((msdu = skb_peek(msdu_list)) != NULL); - - return 0; - -free_out: - dev_kfree_skb_any(msdu); - __skb_queue_purge(amsdu_list); - - return ret; -} - static void ath11k_dp_rx_h_csum_offload(struct sk_buff *msdu) { struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); @@ -1670,20 +1778,53 @@ static void ath11k_dp_rx_h_undecap_nwifi(struct ath11k *ar, enum hal_encrypt_type enctype, struct ieee80211_rx_status *status) { + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + u8 decap_hdr[DP_MAX_NWIFI_HDR_LEN]; struct ieee80211_hdr *hdr; size_t hdr_len; u8 da[ETH_ALEN]; u8 sa[ETH_ALEN]; + u16 qos_ctl = 0; + u8 *qos; - /* pull decapped header and copy SA & DA */ + /* copy SA & DA and pull decapped header */ hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); ether_addr_copy(da, ieee80211_get_DA(hdr)); ether_addr_copy(sa, ieee80211_get_SA(hdr)); skb_pull(msdu, ieee80211_hdrlen(hdr->frame_control)); - /* push original 802.11 header */ - hdr = (struct ieee80211_hdr *)first_hdr; - hdr_len = ieee80211_hdrlen(hdr->frame_control); + if (rxcb->is_first_msdu) { + /* original 802.11 header is valid for the first msdu + * hence we can reuse the same header + */ + hdr = (struct ieee80211_hdr *)first_hdr; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + /* Each A-MSDU subframe will be reported as a separate MSDU, + * so strip the A-MSDU bit from QoS Ctl. + */ + if (ieee80211_is_data_qos(hdr->frame_control)) { + qos = ieee80211_get_qos_ctl(hdr); + qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; + } + } else { + /* Rebuild qos header if this is a middle/last msdu */ + hdr->frame_control |= __cpu_to_le16(IEEE80211_STYPE_QOS_DATA); + + /* Reset the order bit as the HT_Control header is stripped */ + hdr->frame_control &= ~(__cpu_to_le16(IEEE80211_FCTL_ORDER)); + + qos_ctl = rxcb->tid; + + if (ath11k_dp_rx_h_msdu_start_mesh_ctl_present(rxcb->rx_desc)) + qos_ctl |= IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT; + + /* TODO Add other QoS ctl fields when required */ + + /* copy decap header before overwriting for reuse below */ + memcpy(decap_hdr, (uint8_t *)hdr, hdr_len); + } if (!(status->flag & RX_FLAG_IV_STRIPPED)) { memcpy(skb_push(msdu, @@ -1692,6 +1833,14 @@ static void ath11k_dp_rx_h_undecap_nwifi(struct ath11k *ar, ath11k_dp_rx_crypto_param_len(ar, enctype)); } + if (!rxcb->is_first_msdu) { + memcpy(skb_push(msdu, + IEEE80211_QOS_CTL_LEN), &qos_ctl, + IEEE80211_QOS_CTL_LEN); + memcpy(skb_push(msdu, hdr_len), decap_hdr, hdr_len); + return; + } + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); /* original 802.11 header has a different DA and in @@ -1846,7 +1995,7 @@ static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu, u8 decap; first_hdr = ath11k_dp_rx_h_80211_hdr(rx_desc); - decap = ath11k_dp_rx_h_mpdu_start_decap_type(rx_desc); + decap = ath11k_dp_rx_h_msdu_start_decap_type(rx_desc); switch (decap) { case DP_RX_DECAP_TYPE_NATIVE_WIFI: @@ -1858,6 +2007,7 @@ static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu, decrypted); break; case DP_RX_DECAP_TYPE_ETHERNET2_DIX: + /* TODO undecap support for middle/last msdu's of amsdu */ ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr, enctype, status); break; @@ -1868,42 +2018,41 @@ static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu, } static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, - struct sk_buff_head *amsdu_list, + struct sk_buff *msdu, struct hal_rx_desc *rx_desc, struct ieee80211_rx_status *rx_status) { - struct ieee80211_hdr *hdr; + bool fill_crypto_hdr, mcast; enum hal_encrypt_type enctype; - struct sk_buff *last_msdu; - struct sk_buff *msdu; - struct ath11k_skb_rxcb *last_rxcb; - bool is_decrypted; + bool is_decrypted = false; + struct ieee80211_hdr *hdr; + struct ath11k_peer *peer; u32 err_bitmap; - u8 *qos; - if (skb_queue_empty(amsdu_list)) - return; + hdr = (struct ieee80211_hdr *)msdu->data; - hdr = (struct ieee80211_hdr *)ath11k_dp_rx_h_80211_hdr(rx_desc); + /* PN for multicast packets will be checked in mac80211 */ - /* Each A-MSDU subframe will use the original header as the base and be - * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl. - */ - if (ieee80211_is_data_qos(hdr->frame_control)) { - qos = ieee80211_get_qos_ctl(hdr); - qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; - } + mcast = is_multicast_ether_addr(hdr->addr1); + fill_crypto_hdr = mcast; is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc); - enctype = ath11k_dp_rx_h_mpdu_start_enctype(rx_desc); - /* Some attention flags are valid only in the last MSDU. */ - last_msdu = skb_peek_tail(amsdu_list); - last_rxcb = ATH11K_SKB_RXCB(last_msdu); + spin_lock_bh(&ar->ab->base_lock); + peer = ath11k_peer_find_by_addr(ar->ab, hdr->addr2); + if (peer) { + if (mcast) + enctype = peer->sec_type_grp; + else + enctype = peer->sec_type; + } else { + enctype = HAL_ENCRYPT_TYPE_OPEN; + } + spin_unlock_bh(&ar->ab->base_lock); - err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(last_rxcb->rx_desc); + err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(rx_desc); - /* Clear per-MPDU flags while leaving per-PPDU flags intact. */ + /* Clear per-MPDU flags while leaving per-PPDU flags intact */ rx_status->flag &= ~(RX_FLAG_FAILED_FCS_CRC | RX_FLAG_MMIC_ERROR | RX_FLAG_DECRYPTED | @@ -1912,19 +2061,29 @@ static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, if (err_bitmap & DP_RX_MPDU_ERR_FCS) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC) rx_status->flag |= RX_FLAG_MMIC_ERROR; - if (is_decrypted) - rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED | - RX_FLAG_MIC_STRIPPED | RX_FLAG_ICV_STRIPPED; + if (is_decrypted) { + rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED; - skb_queue_walk(amsdu_list, msdu) { - ath11k_dp_rx_h_csum_offload(msdu); - ath11k_dp_rx_h_undecap(ar, msdu, rx_desc, - enctype, rx_status, is_decrypted); + if (fill_crypto_hdr) + rx_status->flag |= RX_FLAG_MIC_STRIPPED | + RX_FLAG_ICV_STRIPPED; + else + rx_status->flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_PN_VALIDATED; } + + ath11k_dp_rx_h_csum_offload(msdu); + ath11k_dp_rx_h_undecap(ar, msdu, rx_desc, + enctype, rx_status, is_decrypted); + + if (!is_decrypted || fill_crypto_hdr) + return; + + hdr = (void *)msdu->data; + hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); } static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, @@ -1988,6 +2147,7 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, } rx_status->encoding = RX_ENC_HE; rx_status->nss = nss; + rx_status->he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi); rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); break; } @@ -2013,9 +2173,13 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc, } else if (channel_num >= 36 && channel_num <= 173) { rx_status->band = NL80211_BAND_5GHZ; } else { - ath11k_warn(ar->ab, "Unsupported Channel info received %d\n", - channel_num); - return; + spin_lock_bh(&ar->data_lock); + rx_status->band = ar->rx_channel->band; + channel_num = + ieee80211_frequency_to_channel(ar->rx_channel->center_freq); + spin_unlock_bh(&ar->data_lock); + ath11k_dbg_dump(ar->ab, ATH11K_DBG_DATA, NULL, "rx_desc: ", + rx_desc, sizeof(struct hal_rx_desc)); } rx_status->freq = ieee80211_channel_to_frequency(channel_num, @@ -2024,29 +2188,6 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc, ath11k_dp_rx_h_rate(ar, rx_desc, rx_status); } -static void ath11k_dp_rx_process_amsdu(struct ath11k *ar, - struct sk_buff_head *amsdu_list, - struct ieee80211_rx_status *rx_status) -{ - struct sk_buff *first; - struct ath11k_skb_rxcb *rxcb; - struct hal_rx_desc *rx_desc; - bool first_mpdu; - - if (skb_queue_empty(amsdu_list)) - return; - - first = skb_peek(amsdu_list); - rxcb = ATH11K_SKB_RXCB(first); - rx_desc = rxcb->rx_desc; - - first_mpdu = ath11k_dp_rx_h_attn_first_mpdu(rx_desc); - if (first_mpdu) - ath11k_dp_rx_h_ppdu(ar, rx_desc, rx_status); - - ath11k_dp_rx_h_mpdu(ar, amsdu_list, rx_desc, rx_status); -} - static char *ath11k_print_get_tid(struct ieee80211_hdr *hdr, char *out, size_t size) { @@ -2113,55 +2254,115 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap ieee80211_rx_napi(ar->hw, NULL, msdu, napi); } -static void ath11k_dp_rx_pre_deliver_amsdu(struct ath11k *ar, - struct sk_buff_head *amsdu_list, - struct ieee80211_rx_status *rxs) +static int ath11k_dp_rx_process_msdu(struct ath11k *ar, + struct sk_buff *msdu, + struct sk_buff_head *msdu_list) { - struct sk_buff *msdu; - struct sk_buff *first_subframe; + struct hal_rx_desc *rx_desc, *lrx_desc; + struct ieee80211_rx_status rx_status = {0}; struct ieee80211_rx_status *status; + struct ath11k_skb_rxcb *rxcb; + struct ieee80211_hdr *hdr; + struct sk_buff *last_buf; + u8 l3_pad_bytes; + u16 msdu_len; + int ret; - first_subframe = skb_peek(amsdu_list); + last_buf = ath11k_dp_rx_get_msdu_last_buf(msdu_list, msdu); + if (!last_buf) { + ath11k_warn(ar->ab, + "No valid Rx buffer to access Atten/MSDU_END/MPDU_END tlvs\n"); + ret = -EIO; + goto free_out; + } - skb_queue_walk(amsdu_list, msdu) { - /* Setup per-MSDU flags */ - if (skb_queue_empty(amsdu_list)) - rxs->flag &= ~RX_FLAG_AMSDU_MORE; - else - rxs->flag |= RX_FLAG_AMSDU_MORE; + rx_desc = (struct hal_rx_desc *)msdu->data; + lrx_desc = (struct hal_rx_desc *)last_buf->data; + if (!ath11k_dp_rx_h_attn_msdu_done(lrx_desc)) { + ath11k_warn(ar->ab, "msdu_done bit in attention is not set\n"); + ret = -EIO; + goto free_out; + } - if (msdu == first_subframe) { - first_subframe = NULL; - rxs->flag &= ~RX_FLAG_ALLOW_SAME_PN; - } else { - rxs->flag |= RX_FLAG_ALLOW_SAME_PN; + rxcb = ATH11K_SKB_RXCB(msdu); + rxcb->rx_desc = rx_desc; + msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(rx_desc); + l3_pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(lrx_desc); + + if (rxcb->is_frag) { + skb_pull(msdu, HAL_RX_DESC_SIZE); + } else if (!rxcb->is_continuation) { + if ((msdu_len + HAL_RX_DESC_SIZE) > DP_RX_BUFFER_SIZE) { + ret = -EINVAL; + ath11k_warn(ar->ab, "invalid msdu len %u\n", msdu_len); + goto free_out; + } + skb_put(msdu, HAL_RX_DESC_SIZE + l3_pad_bytes + msdu_len); + skb_pull(msdu, HAL_RX_DESC_SIZE + l3_pad_bytes); + } else { + ret = ath11k_dp_rx_msdu_coalesce(ar, msdu_list, + msdu, last_buf, + l3_pad_bytes, msdu_len); + if (ret) { + ath11k_warn(ar->ab, + "failed to coalesce msdu rx buffer%d\n", ret); + goto free_out; } - rxs->flag |= RX_FLAG_SKIP_MONITOR; - - status = IEEE80211_SKB_RXCB(msdu); - *status = *rxs; } + + hdr = (struct ieee80211_hdr *)msdu->data; + + /* Process only data frames */ + if (!ieee80211_is_data(hdr->frame_control)) + return -EINVAL; + + ath11k_dp_rx_h_ppdu(ar, rx_desc, &rx_status); + ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, &rx_status); + + rx_status.flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; + + status = IEEE80211_SKB_RXCB(msdu); + *status = rx_status; + return 0; + +free_out: + return ret; } -static void ath11k_dp_rx_process_pending_packets(struct ath11k_base *ab, - struct napi_struct *napi, - struct sk_buff_head *pending_q, - int *quota, u8 mac_id) +static void ath11k_dp_rx_process_received_packets(struct ath11k_base *ab, + struct napi_struct *napi, + struct sk_buff_head *msdu_list, + int *quota, int ring_id) { - struct ath11k *ar; + struct ath11k_skb_rxcb *rxcb; struct sk_buff *msdu; - struct ath11k_pdev *pdev; + struct ath11k *ar; + u8 mac_id; + int ret; - if (skb_queue_empty(pending_q)) + if (skb_queue_empty(msdu_list)) return; - ar = ab->pdevs[mac_id].ar; - rcu_read_lock(); - pdev = rcu_dereference(ab->pdevs_active[mac_id]); - while (*quota && (msdu = __skb_dequeue(pending_q))) { - if (!pdev) { + while (*quota && (msdu = __skb_dequeue(msdu_list))) { + rxcb = ATH11K_SKB_RXCB(msdu); + mac_id = rxcb->mac_id; + ar = ab->pdevs[mac_id].ar; + if (!rcu_dereference(ab->pdevs_active[mac_id])) { + dev_kfree_skb_any(msdu); + continue; + } + + if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { + dev_kfree_skb_any(msdu); + continue; + } + + ret = ath11k_dp_rx_process_msdu(ar, msdu, msdu_list); + if (ret) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "Unable to process msdu %d", ret); dev_kfree_skb_any(msdu); continue; } @@ -2169,46 +2370,31 @@ static void ath11k_dp_rx_process_pending_packets(struct ath11k_base *ab, ath11k_dp_rx_deliver_msdu(ar, napi, msdu); (*quota)--; } + rcu_read_unlock(); } -int ath11k_dp_process_rx(struct ath11k_base *ab, int mac_id, - struct napi_struct *napi, struct sk_buff_head *pending_q, - int budget) +int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id, + struct napi_struct *napi, int budget) { - struct ath11k *ar = ab->pdevs[mac_id].ar; - struct ath11k_pdev_dp *dp = &ar->dp; - struct ieee80211_rx_status *rx_status = &dp->rx_status; - struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; - struct hal_srng *srng; - struct sk_buff *msdu; + struct ath11k_dp *dp = &ab->dp; + struct dp_rxdma_ring *rx_ring; + int num_buffs_reaped[MAX_RADIOS] = {0}; struct sk_buff_head msdu_list; - struct sk_buff_head amsdu_list; struct ath11k_skb_rxcb *rxcb; - u32 *rx_desc; - int buf_id; - int num_buffs_reaped = 0; + int total_msdu_reaped = 0; + struct hal_srng *srng; + struct sk_buff *msdu; int quota = budget; - int ret; bool done = false; - - /* Process any pending packets from the previous napi poll. - * Note: All msdu's in this pending_q corresponds to the same mac id - * due to pdev based reo dest mapping and also since each irq group id - * maps to specific reo dest ring. - */ - ath11k_dp_rx_process_pending_packets(ab, napi, pending_q, "a, - mac_id); - - /* If all quota is exhausted by processing the pending_q, - * Wait for the next napi poll to reap the new info - */ - if (!quota) - goto exit; + int buf_id, mac_id; + struct ath11k *ar; + u32 *rx_desc; + int i; __skb_queue_head_init(&msdu_list); - srng = &ab->hal.srng_list[dp->reo_dst_ring.ring_id]; + srng = &ab->hal.srng_list[dp->reo_dst_ring[ring_id].ring_id]; spin_lock_bh(&srng->lock); @@ -2224,6 +2410,10 @@ try_again: desc->buf_addr_info.info1); buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie); + mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, cookie); + + ar = ab->pdevs[mac_id].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; spin_lock_bh(&rx_ring->idr_lock); msdu = idr_find(&rx_ring->bufs_idr, buf_id); if (!msdu) { @@ -2241,15 +2431,15 @@ try_again: msdu->len + skb_tailroom(msdu), DMA_FROM_DEVICE); - num_buffs_reaped++; + num_buffs_reaped[mac_id]++; + total_msdu_reaped++; push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, desc->info0); if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { - /* TODO: Check if the msdu can be sent up for processing */ dev_kfree_skb_any(msdu); - ab->soc_stats.hal_reo_error[dp->reo_dst_ring.ring_id]++; + ab->soc_stats.hal_reo_error[dp->reo_dst_ring[ring_id].ring_id]++; continue; } @@ -2260,19 +2450,12 @@ try_again: rxcb->is_continuation = !!(desc->rx_msdu_info.info0 & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); rxcb->mac_id = mac_id; + rxcb->tid = FIELD_GET(HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM, + desc->info0); + __skb_queue_tail(&msdu_list, msdu); - /* Stop reaping from the ring once quota is exhausted - * and we've received all msdu's in the the AMSDU. The - * additional msdu's reaped in excess of quota here would - * be pushed into the pending queue to be processed during - * the next napi poll. - * Note: More profiling can be done to see the impact on - * pending_q and throughput during various traffic & density - * and how use of budget instead of remaining quota affects it. - */ - if (num_buffs_reaped >= quota && rxcb->is_last_msdu && - !rxcb->is_continuation) { + if (total_msdu_reaped >= quota && !rxcb->is_continuation) { done = true; break; } @@ -2293,58 +2476,23 @@ try_again: spin_unlock_bh(&srng->lock); - if (!num_buffs_reaped) + if (!total_msdu_reaped) goto exit; - /* Should we reschedule it later if we are not able to replenish all - * the buffers? - */ - ath11k_dp_rxbufs_replenish(ab, mac_id, rx_ring, num_buffs_reaped, - HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); - - rcu_read_lock(); - if (!rcu_dereference(ab->pdevs_active[mac_id])) { - __skb_queue_purge(&msdu_list); - goto rcu_unlock; - } - - if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { - __skb_queue_purge(&msdu_list); - goto rcu_unlock; - } - - while (!skb_queue_empty(&msdu_list)) { - __skb_queue_head_init(&amsdu_list); - ret = ath11k_dp_rx_retrieve_amsdu(ar, &msdu_list, &amsdu_list); - if (ret) { - if (ret == -EIO) { - ath11k_err(ab, "rx ring got corrupted %d\n", ret); - __skb_queue_purge(&msdu_list); - /* Should stop processing any more rx in - * future from this ring? - */ - goto rcu_unlock; - } - - /* A-MSDU retrieval got failed due to non-fatal condition, - * continue processing with the next msdu. - */ + for (i = 0; i < ab->num_radios; i++) { + if (!num_buffs_reaped[i]) continue; - } - ath11k_dp_rx_process_amsdu(ar, &amsdu_list, rx_status); + ar = ab->pdevs[i].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; - ath11k_dp_rx_pre_deliver_amsdu(ar, &amsdu_list, rx_status); - skb_queue_splice_tail(&amsdu_list, pending_q); + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, num_buffs_reaped[i], + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); } - while (quota && (msdu = __skb_dequeue(pending_q))) { - ath11k_dp_rx_deliver_msdu(ar, napi, msdu); - quota--; - } + ath11k_dp_rx_process_received_packets(ab, napi, &msdu_list, + "a, ring_id); -rcu_unlock: - rcu_read_unlock(); exit: return budget - quota; } @@ -2411,6 +2559,8 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, rx_stats->num_mpdu_fcs_ok += ppdu_info->num_mpdu_fcs_ok; rx_stats->num_mpdu_fcs_err += ppdu_info->num_mpdu_fcs_err; + rx_stats->dcm_count += ppdu_info->dcm; + rx_stats->ru_alloc_cnt[ppdu_info->ru_alloc] += num_msdu; arsta->rssi_comb = ppdu_info->rssi_comb; rx_stats->rx_duration += ppdu_info->rx_duration; @@ -2681,99 +2831,563 @@ exit: return num_buffs_reaped; } -static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab, - u32 *link_desc, - enum hal_wbm_rel_bm_act action) +static void ath11k_dp_rx_frag_timer(struct timer_list *timer) { - struct ath11k_dp *dp = &ab->dp; - struct hal_srng *srng; - u32 *desc; - int ret = 0; + struct dp_rx_tid *rx_tid = from_timer(rx_tid, timer, frag_timer); - srng = &ab->hal.srng_list[dp->wbm_desc_rel_ring.ring_id]; + spin_lock_bh(&rx_tid->ab->base_lock); + if (rx_tid->last_frag_no && + rx_tid->rx_frag_bitmap == GENMASK(rx_tid->last_frag_no, 0)) { + spin_unlock_bh(&rx_tid->ab->base_lock); + return; + } + ath11k_dp_rx_frags_cleanup(rx_tid, true); + spin_unlock_bh(&rx_tid->ab->base_lock); +} - spin_lock_bh(&srng->lock); +int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id) +{ + struct ath11k_base *ab = ar->ab; + struct crypto_shash *tfm; + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + int i; - ath11k_hal_srng_access_begin(ab, srng); + tfm = crypto_alloc_shash("michael_mic", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); - desc = ath11k_hal_srng_src_get_next_entry(ab, srng); - if (!desc) { - ret = -ENOBUFS; - goto exit; + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, vdev_id, peer_mac); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to set up fragment info\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; } - ath11k_hal_rx_msdu_link_desc_set(ab, (void *)desc, (void *)link_desc, - action); + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) { + rx_tid = &peer->rx_tid[i]; + rx_tid->ab = ab; + timer_setup(&rx_tid->frag_timer, ath11k_dp_rx_frag_timer, 0); + skb_queue_head_init(&rx_tid->rx_frags); + } -exit: - ath11k_hal_srng_access_end(ab, srng); + peer->tfm_mmic = tfm; + spin_unlock_bh(&ab->base_lock); - spin_unlock_bh(&srng->lock); + return 0; +} + +static int ath11k_dp_rx_h_michael_mic(struct crypto_shash *tfm, u8 *key, + struct ieee80211_hdr *hdr, u8 *data, + size_t data_len, u8 *mic) +{ + SHASH_DESC_ON_STACK(desc, tfm); + u8 mic_hdr[16] = {0}; + u8 tid = 0; + int ret; + + if (!tfm) + return -EINVAL; + + desc->tfm = tfm; + + ret = crypto_shash_setkey(tfm, key, 8); + if (ret) + goto out; + ret = crypto_shash_init(desc); + if (ret) + goto out; + + /* TKIP MIC header */ + memcpy(mic_hdr, ieee80211_get_DA(hdr), ETH_ALEN); + memcpy(mic_hdr + ETH_ALEN, ieee80211_get_SA(hdr), ETH_ALEN); + if (ieee80211_is_data_qos(hdr->frame_control)) + tid = ieee80211_get_tid(hdr); + mic_hdr[12] = tid; + + ret = crypto_shash_update(desc, mic_hdr, 16); + if (ret) + goto out; + ret = crypto_shash_update(desc, data, data_len); + if (ret) + goto out; + ret = crypto_shash_final(desc, mic); +out: + shash_desc_zero(desc); return ret; } -static void ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar, - struct sk_buff *msdu, - struct hal_rx_desc *rx_desc, - struct ieee80211_rx_status *rx_status) +static int ath11k_dp_rx_h_verify_tkip_mic(struct ath11k *ar, struct ath11k_peer *peer, + struct sk_buff *msdu) +{ + struct hal_rx_desc *rx_desc = (struct hal_rx_desc *)msdu->data; + struct ieee80211_rx_status *rxs = IEEE80211_SKB_RXCB(msdu); + struct ieee80211_key_conf *key_conf; + struct ieee80211_hdr *hdr; + u8 mic[IEEE80211_CCMP_MIC_LEN]; + int head_len, tail_len, ret; + size_t data_len; + u32 hdr_len; + u8 *key, *data; + u8 key_idx; + + if (ath11k_dp_rx_h_mpdu_start_enctype(rx_desc) != HAL_ENCRYPT_TYPE_TKIP_MIC) + return 0; + + hdr = (struct ieee80211_hdr *)(msdu->data + HAL_RX_DESC_SIZE); + hdr_len = ieee80211_hdrlen(hdr->frame_control); + head_len = hdr_len + HAL_RX_DESC_SIZE + IEEE80211_TKIP_IV_LEN; + tail_len = IEEE80211_CCMP_MIC_LEN + IEEE80211_TKIP_ICV_LEN + FCS_LEN; + + if (!is_multicast_ether_addr(hdr->addr1)) + key_idx = peer->ucast_keyidx; + else + key_idx = peer->mcast_keyidx; + + key_conf = peer->keys[key_idx]; + + data = msdu->data + head_len; + data_len = msdu->len - head_len - tail_len; + key = &key_conf->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]; + + ret = ath11k_dp_rx_h_michael_mic(peer->tfm_mmic, key, hdr, data, data_len, mic); + if (ret || memcmp(mic, data + data_len, IEEE80211_CCMP_MIC_LEN)) + goto mic_fail; + + return 0; + +mic_fail: + (ATH11K_SKB_RXCB(msdu))->is_first_msdu = 1; + (ATH11K_SKB_RXCB(msdu))->is_last_msdu = 1; + + rxs->flag |= RX_FLAG_MMIC_ERROR | RX_FLAG_MMIC_STRIPPED | + RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED; + skb_pull(msdu, HAL_RX_DESC_SIZE); + + ath11k_dp_rx_h_ppdu(ar, rx_desc, rxs); + ath11k_dp_rx_h_undecap(ar, msdu, rx_desc, + HAL_ENCRYPT_TYPE_TKIP_MIC, rxs, true); + ieee80211_rx(ar->hw, msdu); + return -EINVAL; +} + +static void ath11k_dp_rx_h_undecap_frag(struct ath11k *ar, struct sk_buff *msdu, + enum hal_encrypt_type enctype, u32 flags) +{ + struct ieee80211_hdr *hdr; + size_t hdr_len; + size_t crypto_len; + + if (!flags) + return; + + hdr = (struct ieee80211_hdr *)(msdu->data + HAL_RX_DESC_SIZE); + + if (flags & RX_FLAG_MIC_STRIPPED) + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_mic_len(ar, enctype)); + + if (flags & RX_FLAG_ICV_STRIPPED) + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_icv_len(ar, enctype)); + + if (flags & RX_FLAG_IV_STRIPPED) { + hdr_len = ieee80211_hdrlen(hdr->frame_control); + crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype); + + memmove((void *)msdu->data + HAL_RX_DESC_SIZE + crypto_len, + (void *)msdu->data + HAL_RX_DESC_SIZE, hdr_len); + skb_pull(msdu, crypto_len); + } +} + +static int ath11k_dp_rx_h_defrag(struct ath11k *ar, + struct ath11k_peer *peer, + struct dp_rx_tid *rx_tid, + struct sk_buff **defrag_skb) { - u8 rx_channel; + struct hal_rx_desc *rx_desc; + struct sk_buff *skb, *first_frag, *last_frag; + struct ieee80211_hdr *hdr; enum hal_encrypt_type enctype; - bool is_decrypted; - u32 err_bitmap; + bool is_decrypted = false; + int msdu_len = 0; + int extra_space; + u32 flags; + + first_frag = skb_peek(&rx_tid->rx_frags); + last_frag = skb_peek_tail(&rx_tid->rx_frags); + + skb_queue_walk(&rx_tid->rx_frags, skb) { + flags = 0; + rx_desc = (struct hal_rx_desc *)skb->data; + hdr = (struct ieee80211_hdr *)(skb->data + HAL_RX_DESC_SIZE); + + enctype = ath11k_dp_rx_h_mpdu_start_enctype(rx_desc); + if (enctype != HAL_ENCRYPT_TYPE_OPEN) + is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc); + + if (is_decrypted) { + if (skb != first_frag) + flags |= RX_FLAG_IV_STRIPPED; + if (skb != last_frag) + flags |= RX_FLAG_ICV_STRIPPED | + RX_FLAG_MIC_STRIPPED; + } - is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc); - enctype = ath11k_dp_rx_h_mpdu_start_enctype(rx_desc); - err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(rx_desc); + /* RX fragments are always raw packets */ + if (skb != last_frag) + skb_trim(skb, skb->len - FCS_LEN); + ath11k_dp_rx_h_undecap_frag(ar, skb, enctype, flags); - if (err_bitmap & DP_RX_MPDU_ERR_FCS) - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (skb != first_frag) + skb_pull(skb, HAL_RX_DESC_SIZE + + ieee80211_hdrlen(hdr->frame_control)); + msdu_len += skb->len; + } - if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC) - rx_status->flag |= RX_FLAG_MMIC_ERROR; + extra_space = msdu_len - (DP_RX_BUFFER_SIZE + skb_tailroom(first_frag)); + if (extra_space > 0 && + (pskb_expand_head(first_frag, 0, extra_space, GFP_ATOMIC) < 0)) + return -ENOMEM; - rx_status->encoding = RX_ENC_LEGACY; - rx_status->bw = RATE_INFO_BW_20; + __skb_unlink(first_frag, &rx_tid->rx_frags); + while ((skb = __skb_dequeue(&rx_tid->rx_frags))) { + skb_put_data(first_frag, skb->data, skb->len); + dev_kfree_skb_any(skb); + } - rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + hdr = (struct ieee80211_hdr *)(first_frag->data + HAL_RX_DESC_SIZE); + hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); + ATH11K_SKB_RXCB(first_frag)->is_frag = 1; - rx_channel = ath11k_dp_rx_h_msdu_start_freq(rx_desc); + if (ath11k_dp_rx_h_verify_tkip_mic(ar, peer, first_frag)) + first_frag = NULL; - if (rx_channel >= 1 && rx_channel <= 14) { - rx_status->band = NL80211_BAND_2GHZ; - } else if (rx_channel >= 36 && rx_channel <= 173) { - rx_status->band = NL80211_BAND_5GHZ; - } else { - ath11k_warn(ar->ab, "Unsupported Channel info received %d\n", - rx_channel); + *defrag_skb = first_frag; + return 0; +} + +static int ath11k_dp_rx_h_defrag_reo_reinject(struct ath11k *ar, struct dp_rx_tid *rx_tid, + struct sk_buff *defrag_skb) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_refill_ring = &dp->rx_refill_buf_ring; + struct hal_rx_desc *rx_desc = (struct hal_rx_desc *)defrag_skb->data; + struct hal_reo_entrance_ring *reo_ent_ring; + struct hal_reo_dest_ring *reo_dest_ring; + struct dp_link_desc_bank *link_desc_banks; + struct hal_rx_msdu_link *msdu_link; + struct hal_rx_msdu_details *msdu0; + struct hal_srng *srng; + dma_addr_t paddr; + u32 desc_bank, msdu_info, mpdu_info; + u32 dst_idx, cookie; + u32 *msdu_len_offset; + int ret, buf_id; + + link_desc_banks = ab->dp.link_desc_banks; + reo_dest_ring = rx_tid->dst_ring_desc; + + ath11k_hal_rx_reo_ent_paddr_get(ab, reo_dest_ring, &paddr, &desc_bank); + msdu_link = (struct hal_rx_msdu_link *)(link_desc_banks[desc_bank].vaddr + + (paddr - link_desc_banks[desc_bank].paddr)); + msdu0 = &msdu_link->msdu_link[0]; + dst_idx = FIELD_GET(RX_MSDU_DESC_INFO0_REO_DEST_IND, msdu0->rx_msdu_info.info0); + memset(msdu0, 0, sizeof(*msdu0)); + + msdu_info = FIELD_PREP(RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU, 1) | + FIELD_PREP(RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU, 1) | + FIELD_PREP(RX_MSDU_DESC_INFO0_MSDU_CONTINUATION, 0) | + FIELD_PREP(RX_MSDU_DESC_INFO0_MSDU_LENGTH, + defrag_skb->len - HAL_RX_DESC_SIZE) | + FIELD_PREP(RX_MSDU_DESC_INFO0_REO_DEST_IND, dst_idx) | + FIELD_PREP(RX_MSDU_DESC_INFO0_VALID_SA, 1) | + FIELD_PREP(RX_MSDU_DESC_INFO0_VALID_DA, 1); + msdu0->rx_msdu_info.info0 = msdu_info; + + /* change msdu len in hal rx desc */ + msdu_len_offset = (u32 *)&rx_desc->msdu_start; + *msdu_len_offset &= ~(RX_MSDU_START_INFO1_MSDU_LENGTH); + *msdu_len_offset |= defrag_skb->len - HAL_RX_DESC_SIZE; + + paddr = dma_map_single(ab->dev, defrag_skb->data, + defrag_skb->len + skb_tailroom(defrag_skb), + DMA_FROM_DEVICE); + if (dma_mapping_error(ab->dev, paddr)) + return -ENOMEM; + + spin_lock_bh(&rx_refill_ring->idr_lock); + buf_id = idr_alloc(&rx_refill_ring->bufs_idr, defrag_skb, 0, + rx_refill_ring->bufs_max * 3, GFP_ATOMIC); + spin_unlock_bh(&rx_refill_ring->idr_lock); + if (buf_id < 0) { + ret = -ENOMEM; + goto err_unmap_dma; + } + + ATH11K_SKB_RXCB(defrag_skb)->paddr = paddr; + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, dp->mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + + ath11k_hal_rx_buf_addr_info_set(msdu0, paddr, cookie, HAL_RX_BUF_RBM_SW3_BM); + + /* Fill mpdu details into reo entrace ring */ + srng = &ab->hal.srng_list[ab->dp.reo_reinject_ring.ring_id]; + + spin_lock_bh(&srng->lock); + ath11k_hal_srng_access_begin(ab, srng); + + reo_ent_ring = (struct hal_reo_entrance_ring *) + ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!reo_ent_ring) { + ath11k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + ret = -ENOSPC; + goto err_free_idr; + } + memset(reo_ent_ring, 0, sizeof(*reo_ent_ring)); + + ath11k_hal_rx_reo_ent_paddr_get(ab, reo_dest_ring, &paddr, &desc_bank); + ath11k_hal_rx_buf_addr_info_set(reo_ent_ring, paddr, desc_bank, + HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST); + + mpdu_info = FIELD_PREP(RX_MPDU_DESC_INFO0_MSDU_COUNT, 1) | + FIELD_PREP(RX_MPDU_DESC_INFO0_SEQ_NUM, rx_tid->cur_sn) | + FIELD_PREP(RX_MPDU_DESC_INFO0_FRAG_FLAG, 0) | + FIELD_PREP(RX_MPDU_DESC_INFO0_VALID_SA, 1) | + FIELD_PREP(RX_MPDU_DESC_INFO0_VALID_DA, 1) | + FIELD_PREP(RX_MPDU_DESC_INFO0_RAW_MPDU, 1) | + FIELD_PREP(RX_MPDU_DESC_INFO0_VALID_PN, 1); + + reo_ent_ring->rx_mpdu_info.info0 = mpdu_info; + reo_ent_ring->rx_mpdu_info.meta_data = reo_dest_ring->rx_mpdu_info.meta_data; + reo_ent_ring->queue_addr_lo = reo_dest_ring->queue_addr_lo; + reo_ent_ring->info0 = FIELD_PREP(HAL_REO_ENTR_RING_INFO0_QUEUE_ADDR_HI, + FIELD_GET(HAL_REO_DEST_RING_INFO0_QUEUE_ADDR_HI, + reo_dest_ring->info0)) | + FIELD_PREP(HAL_REO_ENTR_RING_INFO0_DEST_IND, dst_idx); + ath11k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + return 0; + +err_free_idr: + spin_lock_bh(&rx_refill_ring->idr_lock); + idr_remove(&rx_refill_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_refill_ring->idr_lock); +err_unmap_dma: + dma_unmap_single(ab->dev, paddr, defrag_skb->len + skb_tailroom(defrag_skb), + DMA_FROM_DEVICE); + return ret; +} + +static int ath11k_dp_rx_h_cmp_frags(struct sk_buff *a, struct sk_buff *b) +{ + int frag1, frag2; + + frag1 = ath11k_dp_rx_h_mpdu_start_frag_no(a); + frag2 = ath11k_dp_rx_h_mpdu_start_frag_no(b); + + return frag1 - frag2; +} + +static void ath11k_dp_rx_h_sort_frags(struct sk_buff_head *frag_list, + struct sk_buff *cur_frag) +{ + struct sk_buff *skb; + int cmp; + + skb_queue_walk(frag_list, skb) { + cmp = ath11k_dp_rx_h_cmp_frags(skb, cur_frag); + if (cmp < 0) + continue; + __skb_queue_before(frag_list, skb, cur_frag); return; } + __skb_queue_tail(frag_list, cur_frag); +} - rx_status->freq = ieee80211_channel_to_frequency(rx_channel, - rx_status->band); - ath11k_dp_rx_h_rate(ar, rx_desc, rx_status); +static u64 ath11k_dp_rx_h_get_pn(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + u64 pn = 0; + u8 *ehdr; - /* Rx fragments are received in raw mode */ - skb_trim(msdu, msdu->len - FCS_LEN); + hdr = (struct ieee80211_hdr *)(skb->data + HAL_RX_DESC_SIZE); + ehdr = skb->data + HAL_RX_DESC_SIZE + ieee80211_hdrlen(hdr->frame_control); - if (is_decrypted) { - rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED; - skb_trim(msdu, msdu->len - - ath11k_dp_rx_crypto_mic_len(ar, enctype)); + pn = ehdr[0]; + pn |= (u64)ehdr[1] << 8; + pn |= (u64)ehdr[4] << 16; + pn |= (u64)ehdr[5] << 24; + pn |= (u64)ehdr[6] << 32; + pn |= (u64)ehdr[7] << 40; + + return pn; +} + +static bool +ath11k_dp_rx_h_defrag_validate_incr_pn(struct ath11k *ar, struct dp_rx_tid *rx_tid) +{ + enum hal_encrypt_type encrypt_type; + struct sk_buff *first_frag, *skb; + struct hal_rx_desc *desc; + u64 last_pn; + u64 cur_pn; + + first_frag = skb_peek(&rx_tid->rx_frags); + desc = (struct hal_rx_desc *)first_frag->data; + + encrypt_type = ath11k_dp_rx_h_mpdu_start_enctype(desc); + if (encrypt_type != HAL_ENCRYPT_TYPE_CCMP_128 && + encrypt_type != HAL_ENCRYPT_TYPE_CCMP_256 && + encrypt_type != HAL_ENCRYPT_TYPE_GCMP_128 && + encrypt_type != HAL_ENCRYPT_TYPE_AES_GCMP_256) + return true; + + last_pn = ath11k_dp_rx_h_get_pn(first_frag); + skb_queue_walk(&rx_tid->rx_frags, skb) { + if (skb == first_frag) + continue; + + cur_pn = ath11k_dp_rx_h_get_pn(skb); + if (cur_pn != last_pn + 1) + return false; + last_pn = cur_pn; + } + return true; +} + +static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar, + struct sk_buff *msdu, + u32 *ring_desc) +{ + struct ath11k_base *ab = ar->ab; + struct hal_rx_desc *rx_desc; + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + struct sk_buff *defrag_skb = NULL; + u32 peer_id; + u16 seqno, frag_no; + u8 tid; + int ret = 0; + bool more_frags; + + rx_desc = (struct hal_rx_desc *)msdu->data; + peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(rx_desc); + tid = ath11k_dp_rx_h_mpdu_start_tid(rx_desc); + seqno = ath11k_dp_rx_h_mpdu_start_seq_no(rx_desc); + frag_no = ath11k_dp_rx_h_mpdu_start_frag_no(msdu); + more_frags = ath11k_dp_rx_h_mpdu_start_more_frags(msdu); + + if (!ath11k_dp_rx_h_mpdu_start_seq_ctrl_valid(rx_desc) || + !ath11k_dp_rx_h_mpdu_start_fc_valid(rx_desc) || + tid > IEEE80211_NUM_TIDS) + return -EINVAL; + + /* received unfragmented packet in reo + * exception ring, this shouldn't happen + * as these packets typically come from + * reo2sw srngs. + */ + if (WARN_ON_ONCE(!frag_no && !more_frags)) + return -EINVAL; + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, peer_id); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to de-fragment received fragment peer_id %d\n", + peer_id); + ret = -ENOENT; + goto out_unlock; + } + rx_tid = &peer->rx_tid[tid]; + + if ((!skb_queue_empty(&rx_tid->rx_frags) && seqno != rx_tid->cur_sn) || + skb_queue_empty(&rx_tid->rx_frags)) { + /* Flush stored fragments and start a new sequence */ + ath11k_dp_rx_frags_cleanup(rx_tid, true); + rx_tid->cur_sn = seqno; + } + + if (rx_tid->rx_frag_bitmap & BIT(frag_no)) { + /* Fragment already present */ + ret = -EINVAL; + goto out_unlock; + } + + if (frag_no > __fls(rx_tid->rx_frag_bitmap)) + __skb_queue_tail(&rx_tid->rx_frags, msdu); + else + ath11k_dp_rx_h_sort_frags(&rx_tid->rx_frags, msdu); + + rx_tid->rx_frag_bitmap |= BIT(frag_no); + if (!more_frags) + rx_tid->last_frag_no = frag_no; + + if (frag_no == 0) { + rx_tid->dst_ring_desc = kmemdup(ring_desc, + sizeof(*rx_tid->dst_ring_desc), + GFP_ATOMIC); + if (!rx_tid->dst_ring_desc) { + ret = -ENOMEM; + goto out_unlock; + } + } else { + ath11k_dp_rx_link_desc_return(ab, ring_desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); } + + if (!rx_tid->last_frag_no || + rx_tid->rx_frag_bitmap != GENMASK(rx_tid->last_frag_no, 0)) { + mod_timer(&rx_tid->frag_timer, jiffies + + ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS); + goto out_unlock; + } + + spin_unlock_bh(&ab->base_lock); + del_timer_sync(&rx_tid->frag_timer); + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find_by_id(ab, peer_id); + if (!peer) + goto err_frags_cleanup; + + if (!ath11k_dp_rx_h_defrag_validate_incr_pn(ar, rx_tid)) + goto err_frags_cleanup; + + if (ath11k_dp_rx_h_defrag(ar, peer, rx_tid, &defrag_skb)) + goto err_frags_cleanup; + + if (!defrag_skb) + goto err_frags_cleanup; + + if (ath11k_dp_rx_h_defrag_reo_reinject(ar, rx_tid, defrag_skb)) + goto err_frags_cleanup; + + ath11k_dp_rx_frags_cleanup(rx_tid, false); + goto out_unlock; + +err_frags_cleanup: + dev_kfree_skb_any(defrag_skb); + ath11k_dp_rx_frags_cleanup(rx_tid, true); +out_unlock: + spin_unlock_bh(&ab->base_lock); + return ret; } static int -ath11k_dp_process_rx_err_buf(struct ath11k *ar, struct napi_struct *napi, - int buf_id, bool frag) +ath11k_dp_process_rx_err_buf(struct ath11k *ar, u32 *ring_desc, int buf_id, bool drop) { struct ath11k_pdev_dp *dp = &ar->dp; struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; - struct ieee80211_rx_status rx_status = {0}; struct sk_buff *msdu; struct ath11k_skb_rxcb *rxcb; - struct ieee80211_rx_status *status; struct hal_rx_desc *rx_desc; u16 msdu_len; @@ -2794,10 +3408,7 @@ ath11k_dp_process_rx_err_buf(struct ath11k *ar, struct napi_struct *napi, msdu->len + skb_tailroom(msdu), DMA_FROM_DEVICE); - if (!frag) { - /* Process only rx fragments below, and drop - * msdu's indicated due to error reasons. - */ + if (drop) { dev_kfree_skb_any(msdu); return 0; } @@ -2816,16 +3427,12 @@ ath11k_dp_process_rx_err_buf(struct ath11k *ar, struct napi_struct *napi, rx_desc = (struct hal_rx_desc *)msdu->data; msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(rx_desc); skb_put(msdu, HAL_RX_DESC_SIZE + msdu_len); - skb_pull(msdu, HAL_RX_DESC_SIZE); - - ath11k_dp_rx_frag_h_mpdu(ar, msdu, rx_desc, &rx_status); - - status = IEEE80211_SKB_RXCB(msdu); - - *status = rx_status; - - ath11k_dp_rx_deliver_msdu(ar, napi, msdu); + if (ath11k_dp_rx_frag_h_mpdu(ar, msdu, ring_desc)) { + dev_kfree_skb_any(msdu); + ath11k_dp_rx_link_desc_return(ar->ab, ring_desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + } exit: rcu_read_unlock(); return 0; @@ -2850,6 +3457,7 @@ int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, dma_addr_t paddr; u32 *desc; bool is_frag; + u8 drop = 0; tot_n_bufs_reaped = 0; quota = budget; @@ -2891,9 +3499,15 @@ int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, is_frag = !!(reo_desc->rx_mpdu_info.info0 & RX_MPDU_DESC_INFO0_FRAG_FLAG); - /* Return the link desc back to wbm idle list */ - ath11k_dp_rx_link_desc_return(ab, desc, - HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + /* Process only rx fragments with one msdu per link desc below, and drop + * msdu's indicated due to error reasons. + */ + if (!is_frag || num_msdus > 1) { + drop = 1; + /* Return the link desc back to wbm idle list */ + ath11k_dp_rx_link_desc_return(ab, desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + } for (i = 0; i < num_msdus; i++) { buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, @@ -2904,8 +3518,7 @@ int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, ar = ab->pdevs[mac_id].ar; - if (!ath11k_dp_process_rx_err_buf(ar, napi, buf_id, - is_frag)) { + if (!ath11k_dp_process_rx_err_buf(ar, desc, buf_id, drop)) { n_bufs_reaped[mac_id]++; tot_n_bufs_reaped++; } @@ -2966,7 +3579,6 @@ static int ath11k_dp_rx_h_null_q_desc(struct ath11k *ar, struct sk_buff *msdu, struct ieee80211_rx_status *status, struct sk_buff_head *msdu_list) { - struct sk_buff_head amsdu_list; u16 msdu_len; struct hal_rx_desc *desc = (struct hal_rx_desc *)msdu->data; u8 l3pad_bytes; @@ -2974,7 +3586,7 @@ static int ath11k_dp_rx_h_null_q_desc(struct ath11k *ar, struct sk_buff *msdu, msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(desc); - if ((msdu_len + HAL_RX_DESC_SIZE) > DP_RX_BUFFER_SIZE) { + if (!rxcb->is_frag && ((msdu_len + HAL_RX_DESC_SIZE) > DP_RX_BUFFER_SIZE)) { /* First buffer will be freed by the caller, so deduct it's length */ msdu_len = msdu_len - (DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE); ath11k_dp_rx_null_q_desc_sg_drop(ar, msdu_len, msdu_list); @@ -2997,24 +3609,25 @@ static int ath11k_dp_rx_h_null_q_desc(struct ath11k *ar, struct sk_buff *msdu, * This error can show up both in a REO destination or WBM release ring. */ - __skb_queue_head_init(&amsdu_list); - rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(desc); rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(desc); - l3pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(desc); - - if ((HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) - return -EINVAL; + if (rxcb->is_frag) { + skb_pull(msdu, HAL_RX_DESC_SIZE); + } else { + l3pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(desc); - skb_put(msdu, HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len); - skb_pull(msdu, HAL_RX_DESC_SIZE + l3pad_bytes); + if ((HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) + return -EINVAL; + skb_put(msdu, HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len); + skb_pull(msdu, HAL_RX_DESC_SIZE + l3pad_bytes); + } ath11k_dp_rx_h_ppdu(ar, desc, status); - __skb_queue_tail(&amsdu_list, msdu); + ath11k_dp_rx_h_mpdu(ar, msdu, desc, status); - ath11k_dp_rx_h_mpdu(ar, &amsdu_list, desc, status); + rxcb->tid = ath11k_dp_rx_h_mpdu_start_tid(desc); /* Please note that caller will having the access to msdu and completing * rx with mac80211. Need not worry about cleaning up amsdu_list. @@ -3037,6 +3650,13 @@ static bool ath11k_dp_rx_h_reo_err(struct ath11k *ar, struct sk_buff *msdu, if (ath11k_dp_rx_h_null_q_desc(ar, msdu, status, msdu_list)) drop = true; break; + case HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED: + /* TODO: Do not drop PN failed packets in the driver; + * instead, it is good to drop such packets in mac80211 + * after incrementing the replay counters. + */ + + /* fall through */ default: /* TODO: Review other errors and process them to mac80211 * as appropriate. diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.h b/drivers/net/wireless/ath/ath11k/dp_rx.h index eec5deaa59ad..88bbcae14e34 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.h +++ b/drivers/net/wireless/ath/ath11k/dp_rx.h @@ -9,6 +9,8 @@ #include "rx_desc.h" #include "debug.h" +#define DP_MAX_NWIFI_HDR_LEN 30 + #define DP_RX_MPDU_ERR_FCS BIT(0) #define DP_RX_MPDU_ERR_DECRYPT BIT(1) #define DP_RX_MPDU_ERR_TKIP_MIC BIT(2) @@ -43,9 +45,16 @@ int ath11k_dp_rx_ampdu_start(struct ath11k *ar, struct ieee80211_ampdu_params *params); int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, struct ieee80211_ampdu_params *params); +int ath11k_dp_peer_rx_pn_replay_config(struct ath11k_vif *arvif, + const u8 *peer_addr, + enum set_key_cmd key_cmd, + struct ieee80211_key_conf *key); void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer); +void ath11k_peer_rx_tid_delete(struct ath11k *ar, + struct ath11k_peer *peer, u8 tid); int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, - u8 tid, u32 ba_win_sz, u16 ssn); + u8 tid, u32 ba_win_sz, u16 ssn, + enum hal_pn_type pn_type); void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, struct sk_buff *skb); int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab); @@ -60,7 +69,7 @@ int ath11k_dp_rx_process_wbm_err(struct ath11k_base *ab, int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, int budget); int ath11k_dp_process_rx(struct ath11k_base *ab, int mac_id, - struct napi_struct *napi, struct sk_buff_head *pending_q, + struct napi_struct *napi, int budget); int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, struct dp_rxdma_ring *rx_ring, @@ -82,5 +91,6 @@ int ath11k_dp_rx_mon_status_bufs_replenish(struct ath11k_base *ab, int mac_id, gfp_t gfp); int ath11k_dp_rx_pdev_mon_detach(struct ath11k *ar); int ath11k_dp_rx_pdev_mon_attach(struct ath11k *ar); +int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id); #endif /* ATH11K_DP_RX_H */ diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 6d7d33761caf..7aac4b0eea0c 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -7,6 +7,7 @@ #include "dp_tx.h" #include "debug.h" #include "hw.h" +#include "peer.h" /* NOTE: Any of the mapped ring id value must not exceed DP_TCL_NUM_RING_MAX */ static const u8 @@ -46,7 +47,7 @@ static u8 ath11k_dp_tx_get_tid(struct sk_buff *skb) return skb->priority & IEEE80211_QOS_CTL_TID_MASK; } -static enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher) +enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher) { switch (cipher) { case WLAN_CIPHER_SUITE_WEP40: diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c index b58ac11c2747..9e40c4bdd674 100644 --- a/drivers/net/wireless/ath/ath11k/hal.c +++ b/drivers/net/wireless/ath/ath11k/hal.c @@ -877,23 +877,32 @@ void ath11k_hal_srng_access_end(struct ath11k_base *ab, struct hal_srng *srng) /* For LMAC rings, ring pointer updates are done through FW and * hence written to a shared memory location that is read by FW */ - if (srng->ring_dir == HAL_SRNG_DIR_SRC) + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + srng->u.src_ring.last_tp = + *(volatile u32 *)srng->u.src_ring.tp_addr; *srng->u.src_ring.hp_addr = srng->u.src_ring.hp; - else + } else { + srng->u.dst_ring.last_hp = *srng->u.dst_ring.hp_addr; *srng->u.dst_ring.tp_addr = srng->u.dst_ring.tp; + } } else { if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + srng->u.src_ring.last_tp = + *(volatile u32 *)srng->u.src_ring.tp_addr; ath11k_ahb_write32(ab, (unsigned long)srng->u.src_ring.hp_addr - (unsigned long)ab->mem, srng->u.src_ring.hp); } else { + srng->u.dst_ring.last_hp = *srng->u.dst_ring.hp_addr; ath11k_ahb_write32(ab, (unsigned long)srng->u.dst_ring.tp_addr - (unsigned long)ab->mem, srng->u.dst_ring.tp); } } + + srng->timestamp = jiffies; } void ath11k_hal_setup_link_idle_list(struct ath11k_base *ab, @@ -1017,6 +1026,7 @@ int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, params->intr_batch_cntr_thres_entries; srng->intr_timer_thres_us = params->intr_timer_thres_us; srng->flags = params->flags; + srng->initialized = 1; spin_lock_init(&srng->lock); for (i = 0; i < HAL_SRNG_NUM_REG_GRP; i++) { @@ -1122,3 +1132,55 @@ void ath11k_hal_srng_deinit(struct ath11k_base *ab) ath11k_hal_free_cont_rdp(ab); ath11k_hal_free_cont_wrp(ab); } + +void ath11k_hal_dump_srng_stats(struct ath11k_base *ab) +{ + struct hal_srng *srng; + struct ath11k_ext_irq_grp *irq_grp; + struct ath11k_ce_pipe *ce_pipe; + int i; + + ath11k_err(ab, "Last interrupt received for each CE:\n"); + for (i = 0; i < CE_COUNT; i++) { + ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + ath11k_err(ab, "CE_id %d pipe_num %d %ums before\n", + i, ce_pipe->pipe_num, + jiffies_to_msecs(jiffies - ce_pipe->timestamp)); + } + + ath11k_err(ab, "\nLast interrupt received for each group:\n"); + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + irq_grp = &ab->ext_irq_grp[i]; + ath11k_err(ab, "group_id %d %ums before\n", + irq_grp->grp_id, + jiffies_to_msecs(jiffies - irq_grp->timestamp)); + } + + for (i = 0; i < HAL_SRNG_RING_ID_MAX; i++) { + srng = &ab->hal.srng_list[i]; + + if (!srng->initialized) + continue; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + ath11k_err(ab, + "src srng id %u hp %u, reap_hp %u, cur tp %u, cached tp %u last tp %u napi processed before %ums\n", + srng->ring_id, srng->u.src_ring.hp, + srng->u.src_ring.reap_hp, + *srng->u.src_ring.tp_addr, srng->u.src_ring.cached_tp, + srng->u.src_ring.last_tp, + jiffies_to_msecs(jiffies - srng->timestamp)); + else if (srng->ring_dir == HAL_SRNG_DIR_DST) + ath11k_err(ab, + "dst srng id %u tp %u, cur hp %u, cached hp %u last hp %u napi processed before %ums\n", + srng->ring_id, srng->u.dst_ring.tp, + *srng->u.dst_ring.hp_addr, + srng->u.dst_ring.cached_hp, + srng->u.dst_ring.last_hp, + jiffies_to_msecs(jiffies - srng->timestamp)); + } +} diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h index 5b13ccdf39e4..7722822a0456 100644 --- a/drivers/net/wireless/ath/ath11k/hal.h +++ b/drivers/net/wireless/ath/ath11k/hal.h @@ -96,6 +96,8 @@ struct ath11k_base; /* REO2SW(x) R0 ring configuration address */ #define HAL_REO1_GEN_ENABLE 0x00000000 +#define HAL_REO1_DEST_RING_CTRL_IX_0 0x00000004 +#define HAL_REO1_DEST_RING_CTRL_IX_1 0x00000008 #define HAL_REO1_DEST_RING_CTRL_IX_2 0x0000000c #define HAL_REO1_DEST_RING_CTRL_IX_3 0x00000010 #define HAL_REO1_RING_BASE_LSB 0x0000029c @@ -529,6 +531,8 @@ struct hal_srng { */ u32 hwreg_base[HAL_SRNG_NUM_REG_GRP]; + u64 timestamp; + /* Source or Destination ring */ enum hal_srng_dir ring_dir; @@ -554,6 +558,9 @@ struct hal_srng { /* max transfer size */ u16 max_buffer_length; + + /* head pointer at access end */ + u32 last_hp; } dst_ring; struct { @@ -577,6 +584,9 @@ struct hal_srng { /* Low threshold - in number of ring entries */ u32 low_threshold; + + /* tail pointer at access end */ + u32 last_tp; } src_ring; } u; }; @@ -717,6 +727,14 @@ enum hal_ce_desc { HAL_CE_DESC_DST_STATUS, }; +#define HAL_HASH_ROUTING_RING_TCL 0 +#define HAL_HASH_ROUTING_RING_SW1 1 +#define HAL_HASH_ROUTING_RING_SW2 2 +#define HAL_HASH_ROUTING_RING_SW3 3 +#define HAL_HASH_ROUTING_RING_SW4 4 +#define HAL_HASH_ROUTING_RING_REL 5 +#define HAL_HASH_ROUTING_RING_FW 6 + struct hal_reo_status_header { u16 cmd_num; enum hal_reo_cmd_status cmd_status; @@ -847,10 +865,10 @@ struct ath11k_hal { u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid); void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, - u32 start_seqtype); + u32 start_seq, enum hal_pn_type type); void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab, struct hal_srng *srng); -void ath11k_hal_reo_hw_setup(struct ath11k_base *ab); +void ath11k_hal_reo_hw_setup(struct ath11k_base *ab, u32 ring_hash_map); void ath11k_hal_setup_link_idle_list(struct ath11k_base *ab, struct hal_wbm_idle_scatter_list *sbuf, u32 nsbufs, u32 tot_link_desc, @@ -893,5 +911,6 @@ int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, struct hal_srng_params *params); int ath11k_hal_srng_init(struct ath11k_base *ath11k); void ath11k_hal_srng_deinit(struct ath11k_base *ath11k); +void ath11k_hal_dump_srng_stats(struct ath11k_base *ab); #endif diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index 9e0f8064e427..f277c9434a25 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -694,7 +694,7 @@ u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid) } void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, - u32 start_seq) + u32 start_seq, enum hal_pn_type type) { struct hal_rx_reo_queue *qdesc = (struct hal_rx_reo_queue *)vaddr; struct hal_rx_reo_queue_ext *ext_desc; @@ -723,6 +723,18 @@ void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE, ba_window_size - 1); + switch (type) { + case HAL_PN_TYPE_NONE: + case HAL_PN_TYPE_WAPI_EVEN: + case HAL_PN_TYPE_WAPI_UNEVEN: + break; + case HAL_PN_TYPE_WPA: + qdesc->info0 |= + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_PN_CHECK, 1) | + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_PN_SIZE, + HAL_RX_REO_QUEUE_PN_SIZE_48); + break; + } /* TODO: Set Ignore ampdu flags based on BA window size and/or * AMPDU capabilities @@ -787,7 +799,7 @@ void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab, } } -void ath11k_hal_reo_hw_setup(struct ath11k_base *ab) +void ath11k_hal_reo_hw_setup(struct ath11k_base *ab, u32 ring_hash_map) { u32 reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; u32 val; @@ -809,6 +821,19 @@ void ath11k_hal_reo_hw_setup(struct ath11k_base *ab) HAL_DEFAULT_REO_TIMEOUT_USEC); ath11k_ahb_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3, HAL_DEFAULT_REO_TIMEOUT_USEC); + + ath11k_ahb_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_0, + FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP, + ring_hash_map)); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_1, + FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP, + ring_hash_map)); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_2, + FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP, + ring_hash_map)); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3, + FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP, + ring_hash_map)); } static enum hal_rx_mon_status @@ -1001,6 +1026,7 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, } ppdu_info->nss = nsts + 1; + ppdu_info->dcm = dcm; ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; break; } @@ -1038,9 +1064,15 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, break; } case HAL_PHYRX_HE_SIG_B1_MU: { - /* TODO: Check if resource unit(RU) allocation stats - * are required - */ + struct hal_rx_he_sig_b1_mu_info *he_sig_b1_mu = + (struct hal_rx_he_sig_b1_mu_info *)tlv_data; + u16 ru_tones; + + info0 = __le32_to_cpu(he_sig_b1_mu->info0); + + ru_tones = FIELD_GET(HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION, + info0); + ppdu_info->ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; break; } diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h index bb022c781c48..e863e4abfcc1 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.h +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -99,6 +99,8 @@ struct hal_rx_mon_ppdu_info { u8 beamformed; u8 rssi_comb; u8 tid; + u8 dcm; + u8 ru_alloc; u8 reception_type; u64 rx_duration; }; @@ -325,6 +327,34 @@ enum hal_rx_mon_status ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab, struct hal_rx_mon_ppdu_info *ppdu_info, struct sk_buff *skb); + +static inline u32 ath11k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) +{ + u32 ret = 0; + + switch (ru_tones) { + case RU_26: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; + break; + case RU_52: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; + break; + case RU_106: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; + break; + case RU_242: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; + break; + case RU_484: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; + break; + case RU_996: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + } + return ret; +} + #define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0 0xDDBEEF #define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1 0xADBEEF #define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2 0xBDBEEF diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index dd39333ec0ea..9973477ae373 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -62,6 +62,7 @@ #define TARGET_RX_BATCHMODE 1 #define ATH11K_HW_MAX_QUEUES 4 +#define ATH11K_QUEUE_LEN 4096 #define ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK 0x4 diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 6640662f5ede..9f8bc19cc5ae 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -178,6 +178,22 @@ u8 ath11k_mac_bw_to_mac80211_bw(u8 bw) return ret; } +enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw) +{ + switch (bw) { + case RATE_INFO_BW_20: + return ATH11K_BW_20; + case RATE_INFO_BW_40: + return ATH11K_BW_40; + case RATE_INFO_BW_80: + return ATH11K_BW_80; + case RATE_INFO_BW_160: + return ATH11K_BW_160; + default: + return ATH11K_BW_20; + } +} + int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx, u16 *rate) { @@ -369,8 +385,10 @@ struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id) flags, ath11k_get_arvif_iter, &arvif_iter); - if (!arvif_iter.arvif) + if (!arvif_iter.arvif) { + ath11k_warn(ar->ab, "No VIF found for vdev %d\n", vdev_id); return NULL; + } return arvif_iter.arvif; } @@ -398,14 +416,12 @@ struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id) { int i; struct ath11k_pdev *pdev; - struct ath11k_vif *arvif; for (i = 0; i < ab->num_radios; i++) { pdev = rcu_dereference(ab->pdevs_active[i]); if (pdev && pdev->ar) { - arvif = ath11k_mac_get_arvif(pdev->ar, vdev_id); - if (arvif) - return arvif->ar; + if (pdev->ar->allocated_vdev_map & (1LL << vdev_id)) + return pdev->ar; } } @@ -1940,6 +1956,31 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, ath11k_wmi_send_obss_spr_cmd(ar, arvif->vdev_id, &info->he_obss_pd); + if (changed & BSS_CHANGED_HE_BSS_COLOR) { + if (vif->type == NL80211_IFTYPE_AP) { + ret = ath11k_wmi_send_obss_color_collision_cfg_cmd( + ar, arvif->vdev_id, info->he_bss_color.color, + ATH11K_BSS_COLOR_COLLISION_DETECTION_AP_PERIOD_MS, + !info->he_bss_color.disabled); + if (ret) + ath11k_warn(ar->ab, "failed to set bss color collision on vdev %i: %d\n", + arvif->vdev_id, ret); + } else if (vif->type == NL80211_IFTYPE_STATION) { + ret = ath11k_wmi_send_bss_color_change_enable_cmd(ar, + arvif->vdev_id, + 1); + if (ret) + ath11k_warn(ar->ab, "failed to enable bss color change on vdev %i: %d\n", + arvif->vdev_id, ret); + ret = ath11k_wmi_send_obss_color_collision_cfg_cmd( + ar, arvif->vdev_id, 0, + ATH11K_BSS_COLOR_COLLISION_DETECTION_STA_PERIOD_MS, 1); + if (ret) + ath11k_warn(ar->ab, "failed to set bss color collision on vdev %i: %d\n", + arvif->vdev_id, ret); + } + } + mutex_unlock(&ar->conf_mutex); } @@ -2309,6 +2350,7 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ath11k_base *ab = ar->ab; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_peer *peer; + struct ath11k_sta *arsta; const u8 *peer_addr; int ret = 0; u32 flags = 0; @@ -2366,15 +2408,53 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, goto exit; } + ret = ath11k_dp_peer_rx_pn_replay_config(arvif, peer_addr, cmd, key); + if (ret) { + ath11k_warn(ab, "failed to offload PN replay detection %d\n", ret); + goto exit; + } + spin_lock_bh(&ab->base_lock); peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); - if (peer && cmd == SET_KEY) + if (peer && cmd == SET_KEY) { peer->keys[key->keyidx] = key; - else if (peer && cmd == DISABLE_KEY) + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + peer->ucast_keyidx = key->keyidx; + peer->sec_type = ath11k_dp_tx_get_encrypt_type(key->cipher); + } else { + peer->mcast_keyidx = key->keyidx; + peer->sec_type_grp = ath11k_dp_tx_get_encrypt_type(key->cipher); + } + } else if (peer && cmd == DISABLE_KEY) { peer->keys[key->keyidx] = NULL; - else if (!peer) + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + peer->ucast_keyidx = 0; + else + peer->mcast_keyidx = 0; + } else if (!peer) /* impossible unless FW goes crazy */ ath11k_warn(ab, "peer %pM disappeared!\n", peer_addr); + + if (sta) { + arsta = (struct ath11k_sta *)sta->drv_priv; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + if (cmd == SET_KEY) + arsta->pn_type = HAL_PN_TYPE_WPA; + else + arsta->pn_type = HAL_PN_TYPE_NONE; + break; + default: + arsta->pn_type = HAL_PN_TYPE_NONE; + break; + } + } + spin_unlock_bh(&ab->base_lock); exit: @@ -2786,6 +2866,7 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k_peer *peer; int ret = 0; /* cancel must be done outside the mutex to avoid deadlock */ @@ -2818,6 +2899,17 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, sta->addr, arvif->vdev_id); ath11k_mac_dec_num_stations(arvif, sta); + spin_lock_bh(&ar->ab->base_lock); + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); + if (peer && peer->sta == sta) { + ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", + vif->addr, arvif->vdev_id); + peer->sta = NULL; + list_del(&peer->list); + kfree(peer); + ar->num_peers--; + } + spin_unlock_bh(&ar->ab->base_lock); kfree(arsta->tx_stats); arsta->tx_stats = NULL; @@ -3874,6 +3966,7 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw) ar->num_started_vdevs = 0; ar->num_created_vdevs = 0; ar->num_peers = 0; + ar->allocated_vdev_map = 0; /* Configure monitor status ring with default rx_filter to get rx status * such as rssi, rx_duration. @@ -3885,6 +3978,9 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw) goto err; } + /* Configure the hash seed for hash based reo dest ring selection */ + ath11k_wmi_pdev_lro_cfg(ar, ar->pdev->pdev_id); + mutex_unlock(&ar->conf_mutex); rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], @@ -4112,8 +4208,9 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, } ar->num_created_vdevs++; - + ar->allocated_vdev_map |= 1LL << arvif->vdev_id; ab->free_vdev_map &= ~(1LL << arvif->vdev_id); + spin_lock_bh(&ar->data_lock); list_add(&arvif->list, &ar->arvifs); spin_unlock_bh(&ar->data_lock); @@ -4227,6 +4324,7 @@ err_peer_del: err_vdev_del: ath11k_wmi_vdev_delete(ar, arvif->vdev_id); ar->num_created_vdevs--; + ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); ab->free_vdev_map |= 1LL << arvif->vdev_id; spin_lock_bh(&ar->data_lock); list_del(&arvif->list); @@ -4263,7 +4361,6 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, ath11k_dbg(ab, ATH11K_DBG_MAC, "mac remove interface (vdev %d)\n", arvif->vdev_id); - ab->free_vdev_map |= 1LL << (arvif->vdev_id); spin_lock_bh(&ar->data_lock); list_del(&arvif->list); spin_unlock_bh(&ar->data_lock); @@ -4281,6 +4378,8 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, arvif->vdev_id, ret); ar->num_created_vdevs--; + ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); + ab->free_vdev_map |= 1LL << (arvif->vdev_id); ath11k_peer_cleanup(ar, arvif->vdev_id); @@ -5699,6 +5798,7 @@ static int __ath11k_mac_register(struct ath11k *ar) ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW); ieee80211_hw_set(ar->hw, SUPPORTS_REORDERING_BUFFER); ieee80211_hw_set(ar->hw, SUPPORTS_AMSDU_IN_AMPDU); + ieee80211_hw_set(ar->hw, USES_RSS); } ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS; @@ -5730,6 +5830,7 @@ static int __ath11k_mac_register(struct ath11k *ar) ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations; ar->hw->queues = ATH11K_HW_MAX_QUEUES; + ar->hw->wiphy->tx_queue_len = ATH11K_QUEUE_LEN; ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; ar->hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; @@ -5873,6 +5974,8 @@ int ath11k_mac_allocate(struct ath11k_base *ab) init_completion(&ar->bss_survey_done); init_completion(&ar->scan.started); init_completion(&ar->scan.completed); + init_completion(&ar->thermal.wmi_sync); + INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work); INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work); diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index f286531cdd78..0607479774a9 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -144,4 +144,6 @@ void ath11k_mac_drain_tx(struct ath11k *ar); void ath11k_mac_peer_cleanup_all(struct ath11k *ar); int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); u8 ath11k_mac_bw_to_mac80211_bw(u8 bw); +enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw); +enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher); #endif diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c index 4bf1dfa498b6..f43deacc01bd 100644 --- a/drivers/net/wireless/ath/ath11k/peer.c +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -228,6 +228,9 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, peer->sta = sta; arvif->ast_hash = peer->ast_hash; + peer->sec_type = HAL_ENCRYPT_TYPE_OPEN; + peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN; + ar->num_peers++; spin_unlock_bh(&ar->ab->base_lock); diff --git a/drivers/net/wireless/ath/ath11k/peer.h b/drivers/net/wireless/ath/ath11k/peer.h index 9a40d1f6ccd9..ccca1523a6ea 100644 --- a/drivers/net/wireless/ath/ath11k/peer.h +++ b/drivers/net/wireless/ath/ath11k/peer.h @@ -17,6 +17,15 @@ struct ath11k_peer { /* protected by ab->data_lock */ struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; struct dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; + + /* Info used in MMIC verification of + * RX fragments + */ + struct crypto_shash *tfm_mmic; + u8 mcast_keyidx; + u8 ucast_keyidx; + u16 sec_type; + u16 sec_type_grp; }; void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 2377895a58ec..c00a99ad8dbc 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -2365,6 +2365,7 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work) break; case ATH11K_QMI_EVENT_FW_READY: if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { + ath11k_hal_dump_srng_stats(ab); queue_work(ab->workqueue, &ab->restart_work); break; } diff --git a/drivers/net/wireless/ath/ath11k/rx_desc.h b/drivers/net/wireless/ath/ath11k/rx_desc.h index a5aff801f17f..1c4264637a41 100644 --- a/drivers/net/wireless/ath/ath11k/rx_desc.h +++ b/drivers/net/wireless/ath/ath11k/rx_desc.h @@ -342,7 +342,7 @@ struct rx_attention { #define RX_MPDU_START_INFO0_PROTO_VER_ERR BIT(12) #define RX_MPDU_START_INFO0_AST_LOOKUP_VALID BIT(13) -#define RX_MPDU_START_INFO1_MPDU_CTRL_VALID BIT(0) +#define RX_MPDU_START_INFO1_MPDU_FCTRL_VALID BIT(0) #define RX_MPDU_START_INFO1_MPDU_DUR_VALID BIT(1) #define RX_MPDU_START_INFO1_MAC_ADDR1_VALID BIT(2) #define RX_MPDU_START_INFO1_MAC_ADDR2_VALID BIT(3) @@ -1209,4 +1209,12 @@ struct hal_rx_desc { u8 msdu_payload[0]; } __packed; +#define HAL_RX_RU_ALLOC_TYPE_MAX 6 +#define RU_26 1 +#define RU_52 2 +#define RU_106 4 +#define RU_242 9 +#define RU_484 18 +#define RU_996 37 + #endif /* ATH11K_RX_DESC_H */ diff --git a/drivers/net/wireless/ath/ath11k/thermal.c b/drivers/net/wireless/ath/ath11k/thermal.c new file mode 100644 index 000000000000..259dddbda2c7 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/thermal.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/thermal.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include "core.h" +#include "debug.h" + +static int +ath11k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = ATH11K_THERMAL_THROTTLE_MAX; + + return 0; +} + +static int +ath11k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct ath11k *ar = cdev->devdata; + + mutex_lock(&ar->conf_mutex); + *state = ar->thermal.throttle_state; + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int +ath11k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long throttle_state) +{ + struct ath11k *ar = cdev->devdata; + int ret; + + if (throttle_state > ATH11K_THERMAL_THROTTLE_MAX) { + ath11k_warn(ar->ab, "throttle state %ld is exceeding the limit %d\n", + throttle_state, ATH11K_THERMAL_THROTTLE_MAX); + return -EINVAL; + } + mutex_lock(&ar->conf_mutex); + ret = ath11k_thermal_set_throttling(ar, throttle_state); + if (ret == 0) + ar->thermal.throttle_state = throttle_state; + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static struct thermal_cooling_device_ops ath11k_thermal_ops = { + .get_max_state = ath11k_thermal_get_max_throttle_state, + .get_cur_state = ath11k_thermal_get_cur_throttle_state, + .set_cur_state = ath11k_thermal_set_cur_throttle_state, +}; + +static ssize_t ath11k_thermal_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ath11k *ar = dev_get_drvdata(dev); + int ret, temperature; + unsigned long time_left; + + mutex_lock(&ar->conf_mutex); + + /* Can't get temperature when the card is off */ + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + reinit_completion(&ar->thermal.wmi_sync); + ret = ath11k_wmi_send_pdev_temperature_cmd(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to read temperature %d\n", ret); + goto out; + } + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) { + ret = -ESHUTDOWN; + goto out; + } + + time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync, + ATH11K_THERMAL_SYNC_TIMEOUT_HZ); + if (!time_left) { + ath11k_warn(ar->ab, "failed to synchronize thermal read\n"); + ret = -ETIMEDOUT; + goto out; + } + + spin_lock_bh(&ar->data_lock); + temperature = ar->thermal.temperature; + spin_unlock_bh(&ar->data_lock); + + /* display in millidegree celcius */ + ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000); +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +void ath11k_thermal_event_temperature(struct ath11k *ar, int temperature) +{ + spin_lock_bh(&ar->data_lock); + ar->thermal.temperature = temperature; + spin_unlock_bh(&ar->data_lock); + complete(&ar->thermal.wmi_sync); +} + +static SENSOR_DEVICE_ATTR(temp1_input, 0444, ath11k_thermal_show_temp, + NULL, 0); + +static struct attribute *ath11k_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ath11k_hwmon); + +int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state) +{ + struct ath11k_base *sc = ar->ab; + struct thermal_mitigation_params param; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) + return 0; + + memset(¶m, 0, sizeof(param)); + param.pdev_id = ar->pdev->pdev_id; + param.enable = throttle_state ? 1 : 0; + param.dc = ATH11K_THERMAL_DEFAULT_DUTY_CYCLE; + param.dc_per_event = 0xFFFFFFFF; + + param.levelconf[0].tmplwm = ATH11K_THERMAL_TEMP_LOW_MARK; + param.levelconf[0].tmphwm = ATH11K_THERMAL_TEMP_HIGH_MARK; + param.levelconf[0].dcoffpercent = throttle_state; + param.levelconf[0].priority = 0; /* disable all data tx queues */ + + ret = ath11k_wmi_send_thermal_mitigation_param_cmd(ar, ¶m); + if (ret) { + ath11k_warn(sc, "failed to send thermal mitigation duty cycle %u ret %d\n", + throttle_state, ret); + } + + return ret; +} + +int ath11k_thermal_register(struct ath11k_base *sc) +{ + struct thermal_cooling_device *cdev; + struct device *hwmon_dev; + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i, ret; + + for (i = 0; i < sc->num_radios; i++) { + pdev = &sc->pdevs[i]; + ar = pdev->ar; + if (!ar) + continue; + + cdev = thermal_cooling_device_register("ath11k_thermal", ar, + &ath11k_thermal_ops); + + if (IS_ERR(cdev)) { + ath11k_err(sc, "failed to setup thermal device result: %ld\n", + PTR_ERR(cdev)); + return -EINVAL; + } + + ret = sysfs_create_link(&ar->hw->wiphy->dev.kobj, &cdev->device.kobj, + "cooling_device"); + if (ret) { + ath11k_err(sc, "failed to create cooling device symlink\n"); + goto err_thermal_destroy; + } + + ar->thermal.cdev = cdev; + if (!IS_REACHABLE(CONFIG_HWMON)) + return 0; + + hwmon_dev = devm_hwmon_device_register_with_groups(&ar->hw->wiphy->dev, + "ath11k_hwmon", ar, + ath11k_hwmon_groups); + if (IS_ERR(hwmon_dev)) { + ath11k_err(ar->ab, "failed to register hwmon device: %ld\n", + PTR_ERR(hwmon_dev)); + ret = -EINVAL; + goto err_thermal_destroy; + } + } + + return 0; + +err_thermal_destroy: + ath11k_thermal_unregister(sc); + return ret; +} + +void ath11k_thermal_unregister(struct ath11k_base *sc) +{ + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i; + + for (i = 0; i < sc->num_radios; i++) { + pdev = &sc->pdevs[i]; + ar = pdev->ar; + if (!ar) + continue; + + sysfs_remove_link(&ar->hw->wiphy->dev.kobj, "cooling_device"); + thermal_cooling_device_unregister(ar->thermal.cdev); + } +} diff --git a/drivers/net/wireless/ath/ath11k/thermal.h b/drivers/net/wireless/ath/ath11k/thermal.h new file mode 100644 index 000000000000..459b8d49c184 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/thermal.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef _ATH11K_THERMAL_ +#define _ATH11K_THERMAL_ + +#define ATH11K_THERMAL_TEMP_LOW_MARK -100 +#define ATH11K_THERMAL_TEMP_HIGH_MARK 150 +#define ATH11K_THERMAL_THROTTLE_MAX 100 +#define ATH11K_THERMAL_DEFAULT_DUTY_CYCLE 100 +#define ATH11K_HWMON_NAME_LEN 15 +#define ATH11K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ) + +struct ath11k_thermal { + struct thermal_cooling_device *cdev; + struct completion wmi_sync; + + /* protected by conf_mutex */ + u32 throttle_state; + /* temperature value in Celcius degree + * protected by data_lock + */ + int temperature; +}; + +#if IS_REACHABLE(CONFIG_THERMAL) +int ath11k_thermal_register(struct ath11k_base *sc); +void ath11k_thermal_unregister(struct ath11k_base *sc); +int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state); +void ath11k_thermal_event_temperature(struct ath11k *ar, int temperature); +#else +static inline int ath11k_thermal_register(struct ath11k_base *sc) +{ + return 0; +} + +static inline void ath11k_thermal_unregister(struct ath11k *ar) +{ +} + +static inline int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state) +{ +} + +static inline void ath11k_thermal_event_temperature(struct ath11k *ar, + int temperature) +{ +} + +#endif +#endif /* _ATH11K_THERMAL_ */ diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index a9b301ceb24b..e7ce36966d6a 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -1471,6 +1471,34 @@ int ath11k_wmi_send_stats_request_cmd(struct ath11k *ar, return ret; } +int ath11k_wmi_send_pdev_temperature_cmd(struct ath11k *ar) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_get_pdev_temperature_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_get_pdev_temperature_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_GET_TEMPERATURE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->pdev_id = ar->pdev->pdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_GET_TEMPERATURE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_GET_TEMPERATURE cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI pdev get temperature for pdev_id %d\n", ar->pdev->pdev_id); + + return ret; +} + int ath11k_wmi_send_bcn_offload_control_cmd(struct ath11k *ar, u32 vdev_id, u32 bcn_ctrl_op) { @@ -2442,6 +2470,70 @@ out: return ret; } +int +ath11k_wmi_send_thermal_mitigation_param_cmd(struct ath11k *ar, + struct thermal_mitigation_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_therm_throt_config_request_cmd *cmd; + struct wmi_therm_throt_level_config_info *lvl_conf; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int i, ret, len; + + len = sizeof(*cmd) + TLV_HDR_SIZE + + THERMAL_LEVELS * sizeof(struct wmi_therm_throt_level_config_info); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_therm_throt_config_request_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_THERM_THROT_CONFIG_REQUEST) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = ar->pdev->pdev_id; + cmd->enable = param->enable; + cmd->dc = param->dc; + cmd->dc_per_event = param->dc_per_event; + cmd->therm_throt_levels = THERMAL_LEVELS; + + tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, + (THERMAL_LEVELS * + sizeof(struct wmi_therm_throt_level_config_info))); + + lvl_conf = (struct wmi_therm_throt_level_config_info *)(skb->data + + sizeof(*cmd) + + TLV_HDR_SIZE); + for (i = 0; i < THERMAL_LEVELS; i++) { + lvl_conf->tlv_header = + FIELD_PREP(WMI_TLV_TAG, WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*lvl_conf) - TLV_HDR_SIZE); + + lvl_conf->temp_lwm = param->levelconf[i].tmplwm; + lvl_conf->temp_hwm = param->levelconf[i].tmphwm; + lvl_conf->dc_off_percent = param->levelconf[i].dcoffpercent; + lvl_conf->prio = param->levelconf[i].priority; + lvl_conf++; + } + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_THERM_THROT_SET_CONF_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send THERM_THROT_SET_CONF cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev set thermal throt pdev_id %d enable %d dc %d dc_per_event %x levels %d\n", + ar->pdev->pdev_id, param->enable, param->dc, + param->dc_per_event, THERMAL_LEVELS); + + return ret; +} + int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter) { struct ath11k_pdev_wmi *wmi = ar->wmi; @@ -2615,6 +2707,84 @@ ath11k_wmi_send_obss_spr_cmd(struct ath11k *ar, u32 vdev_id, return ret; } +int +ath11k_wmi_send_obss_color_collision_cfg_cmd(struct ath11k *ar, u32 vdev_id, + u8 bss_color, u32 period, + bool enable) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_obss_color_collision_cfg_params_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_obss_color_collision_cfg_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_OBSS_COLOR_COLLISION_DET_CONFIG) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->evt_type = enable ? ATH11K_OBSS_COLOR_COLLISION_DETECTION : + ATH11K_OBSS_COLOR_COLLISION_DETECTION_DISABLE; + cmd->current_bss_color = bss_color; + cmd->detection_period_ms = period; + cmd->scan_period_ms = ATH11K_BSS_COLOR_COLLISION_SCAN_PERIOD_MS; + cmd->free_slot_expiry_time_ms = 0; + cmd->flags = 0; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi_send_obss_color_collision_cfg id %d type %d bss_color %d detect_period %d scan_period %d\n", + cmd->vdev_id, cmd->evt_type, cmd->current_bss_color, + cmd->detection_period_ms, cmd->scan_period_ms); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_OBSS_COLOR_COLLISION_DET_CONFIG_CMDID); + if (ret) { + ath11k_warn(ab, "Failed to send WMI_OBSS_COLOR_COLLISION_DET_CONFIG_CMDID"); + dev_kfree_skb(skb); + } + return ret; +} + +int ath11k_wmi_send_bss_color_change_enable_cmd(struct ath11k *ar, u32 vdev_id, + bool enable) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_bss_color_change_enable_params_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_bss_color_change_enable_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_BSS_COLOR_CHANGE_ENABLE) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->enable = enable ? 1 : 0; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi_send_bss_color_change_enable id %d enable %d\n", + cmd->vdev_id, cmd->enable); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_BSS_COLOR_CHANGE_ENABLE_CMDID); + if (ret) { + ath11k_warn(ab, "Failed to send WMI_TWT_DIeABLE_CMDID"); + dev_kfree_skb(skb); + } + return ret; +} + static void ath11k_fill_band_to_mac_param(struct ath11k_base *soc, struct wmi_host_pdev_band_to_mac *band_to_mac) @@ -2825,6 +2995,41 @@ static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, return ret; } +int ath11k_wmi_pdev_lro_cfg(struct ath11k *ar, + int pdev_id) +{ + struct ath11k_wmi_pdev_lro_config_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct ath11k_wmi_pdev_lro_config_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_LRO_INFO_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + get_random_bytes(cmd->th_4, sizeof(uint32_t) * ATH11K_IPV4_TH_SEED_SIZE); + get_random_bytes(cmd->th_6, sizeof(uint32_t) * ATH11K_IPV6_TH_SEED_SIZE); + + cmd->pdev_id = pdev_id; + + ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_LRO_CONFIG_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send lro cfg req wmi cmd\n"); + goto err; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI lro cfg cmd pdev_id 0x%x\n", pdev_id); + return 0; +err: + dev_kfree_skb(skb); + return ret; +} + int ath11k_wmi_wait_for_service_ready(struct ath11k_base *ab) { unsigned long time_left; @@ -4168,6 +4373,31 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, return 0; } +static int +ath11k_pull_pdev_temp_ev(struct ath11k_base *ab, u8 *evt_buf, + u32 len, const struct wmi_pdev_temperature_event *ev) +{ + const void **tb; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch pdev temp ev"); + kfree(tb); + return -EPROTO; + } + + kfree(tb); + return 0; +} + size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head) { struct ath11k_fw_stats_vdev *i; @@ -5345,15 +5575,18 @@ static void ath11k_peer_assoc_conf_event(struct ath11k_base *ab, struct sk_buff "peer assoc conf ev vdev id %d macaddr %pM\n", peer_assoc_conf.vdev_id, peer_assoc_conf.macaddr); + rcu_read_lock(); ar = ath11k_mac_get_ar_by_vdev_id(ab, peer_assoc_conf.vdev_id); if (!ar) { ath11k_warn(ab, "invalid vdev id in peer assoc conf ev %d", peer_assoc_conf.vdev_id); + rcu_read_unlock(); return; } complete(&ar->peer_assoc_done); + rcu_read_unlock(); } static void ath11k_update_stats_event(struct ath11k_base *ab, struct sk_buff *skb) @@ -5511,6 +5744,30 @@ exit: kfree(tb); } +static void +ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k *ar; + struct wmi_pdev_temperature_event ev = {0}; + + if (ath11k_pull_pdev_temp_ev(ab, skb->data, skb->len, &ev) != 0) { + ath11k_warn(ab, "failed to extract pdev temperature event"); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "pdev temperature ev temp %d pdev_id %d\n", ev.temp, ev.pdev_id); + + ar = ath11k_mac_get_ar_by_pdev_id(ab, ev.pdev_id); + if (!ar) { + ath11k_warn(ab, "invalid pdev id in pdev temperature ev %d", ev.pdev_id); + return; + } + + ath11k_thermal_event_temperature(ar, ev.temp); +} + static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -5588,6 +5845,9 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) case WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID: ath11k_wmi_pdev_csa_switch_count_status_event(ab, skb); break; + case WMI_PDEV_TEMPERATURE_EVENTID: + ath11k_wmi_pdev_temperature_event(ab, skb); + break; /* add Unsupported events here */ case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: case WMI_VDEV_DELETE_RESP_EVENTID: diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 1fde15c762ad..510f9c6bc1d7 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -442,6 +442,10 @@ enum wmi_tlv_cmd_id { WMI_DBGLOG_TIME_STAMP_SYNC_CMDID, WMI_SET_MULTIPLE_MCAST_FILTER_CMDID, WMI_READ_DATA_FROM_FLASH_CMDID, + WMI_THERM_THROT_SET_CONF_CMDID, + WMI_RUNTIME_DPD_RECAL_CMDID, + WMI_GET_TPC_POWER_CMDID, + WMI_IDLE_TRIGGER_MONITOR_CMDID, WMI_GPIO_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_GPIO), WMI_GPIO_OUTPUT_CMDID, WMI_TXBF_CMDID, @@ -484,6 +488,7 @@ enum wmi_tlv_cmd_id { WMI_SAR_LIMITS_CMDID, WMI_OBSS_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_OBSS_OFL), WMI_OBSS_SCAN_DISABLE_CMDID, + WMI_OBSS_COLOR_COLLISION_DET_CONFIG_CMDID, WMI_LPI_MGMT_SNOOPING_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_LPI), WMI_LPI_START_SCAN_CMDID, WMI_LPI_STOP_SCAN_CMDID, @@ -3300,6 +3305,12 @@ struct wmi_request_stats_cmd { u32 pdev_id; } __packed; +struct wmi_get_pdev_temperature_cmd { + u32 tlv_header; + u32 param; + u32 pdev_id; +} __packed; + #define WMI_BEACON_TX_BUFFER_SIZE 512 struct wmi_bcn_tmpl_cmd { @@ -3605,6 +3616,39 @@ struct wmi_init_country_cmd { } cc_info; } __packed; +#define THERMAL_LEVELS 1 +struct tt_level_config { + u32 tmplwm; + u32 tmphwm; + u32 dcoffpercent; + u32 priority; +}; + +struct thermal_mitigation_params { + u32 pdev_id; + u32 enable; + u32 dc; + u32 dc_per_event; + struct tt_level_config levelconf[THERMAL_LEVELS]; +}; + +struct wmi_therm_throt_config_request_cmd { + u32 tlv_header; + u32 pdev_id; + u32 enable; + u32 dc; + u32 dc_per_event; + u32 therm_throt_levels; +} __packed; + +struct wmi_therm_throt_level_config_info { + u32 tlv_header; + u32 temp_lwm; + u32 temp_hwm; + u32 dc_off_percent; + u32 prio; +} __packed; + struct wmi_pdev_pktlog_filter_info { u32 tlv_header; struct wmi_mac_addr peer_macaddr; @@ -4095,6 +4139,12 @@ struct wmi_pdev_radar_ev { s32 sidx; } __packed; +struct wmi_pdev_temperature_event { + /* temperature value in Celcius degree */ + s32 temp; + u32 pdev_id; +} __packed; + #define WMI_RX_STATUS_OK 0x00 #define WMI_RX_STATUS_ERR_CRC 0x01 #define WMI_RX_STATUS_ERR_DECRYPT 0x08 @@ -4571,6 +4621,42 @@ struct wmi_obss_spatial_reuse_params_cmd { u32 vdev_id; } __packed; +#define ATH11K_BSS_COLOR_COLLISION_SCAN_PERIOD_MS 200 +#define ATH11K_OBSS_COLOR_COLLISION_DETECTION_DISABLE 0 +#define ATH11K_OBSS_COLOR_COLLISION_DETECTION 1 + +#define ATH11K_BSS_COLOR_COLLISION_DETECTION_STA_PERIOD_MS 10000 +#define ATH11K_BSS_COLOR_COLLISION_DETECTION_AP_PERIOD_MS 5000 + +struct wmi_obss_color_collision_cfg_params_cmd { + u32 tlv_header; + u32 vdev_id; + u32 flags; + u32 evt_type; + u32 current_bss_color; + u32 detection_period_ms; + u32 scan_period_ms; + u32 free_slot_expiry_time_ms; +} __packed; + +struct wmi_bss_color_change_enable_params_cmd { + u32 tlv_header; + u32 vdev_id; + u32 enable; +} __packed; + +#define ATH11K_IPV4_TH_SEED_SIZE 5 +#define ATH11K_IPV6_TH_SEED_SIZE 11 + +struct ath11k_wmi_pdev_lro_config_cmd { + u32 tlv_header; + u32 lro_enable; + u32 res; + u32 th_4[ATH11K_IPV4_TH_SEED_SIZE]; + u32 th_6[ATH11K_IPV6_TH_SEED_SIZE]; + u32 pdev_id; +} __packed; + struct target_resource_config { u32 num_vdevs; u32 num_peers; @@ -4726,6 +4812,7 @@ int ath11k_wmi_pdev_bss_chan_info_request(struct ath11k *ar, enum wmi_bss_chan_info_req_type type); int ath11k_wmi_send_stats_request_cmd(struct ath11k *ar, struct stats_request_params *param); +int ath11k_wmi_send_pdev_temperature_cmd(struct ath11k *ar); int ath11k_wmi_send_peer_flush_tids_cmd(struct ath11k *ar, u8 peer_addr[ETH_ALEN], struct peer_flush_params *param); @@ -4740,6 +4827,9 @@ int ath11k_wmi_send_bcn_offload_control_cmd(struct ath11k *ar, int ath11k_wmi_send_init_country_cmd(struct ath11k *ar, struct wmi_init_country_params init_cc_param); +int +ath11k_wmi_send_thermal_mitigation_param_cmd(struct ath11k *ar, + struct thermal_mitigation_params *param); int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter); int ath11k_wmi_pdev_pktlog_disable(struct ath11k *ar); int ath11k_wmi_pdev_peer_pktlog_filter(struct ath11k *ar, u8 *addr, u8 enable); @@ -4761,4 +4851,10 @@ int ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id); int ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id); int ath11k_wmi_send_obss_spr_cmd(struct ath11k *ar, u32 vdev_id, struct ieee80211_he_obss_pd *he_obss_pd); +int ath11k_wmi_send_obss_color_collision_cfg_cmd(struct ath11k *ar, u32 vdev_id, + u8 bss_color, u32 period, + bool enable); +int ath11k_wmi_send_bss_color_change_enable_cmd(struct ath11k *ar, u32 vdev_id, + bool enable); +int ath11k_wmi_pdev_lro_cfg(struct ath11k *ar, int pdev_id); #endif |