diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-28 16:02:33 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-28 16:02:33 -0800 |
commit | bd2463ac7d7ec51d432f23bf0e893fb371a908cd (patch) | |
tree | 3da32c23be83adb9d9bda7e51b51fa39f69f2447 /drivers/net/wireless/ath | |
parent | a78208e2436963d0b2c7d186277d6e1a9755029a (diff) | |
parent | f76e4c167ea2212e23c15ee7e601a865e822c291 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from David Miller:
1) Add WireGuard
2) Add HE and TWT support to ath11k driver, from John Crispin.
3) Add ESP in TCP encapsulation support, from Sabrina Dubroca.
4) Add variable window congestion control to TIPC, from Jon Maloy.
5) Add BCM84881 PHY driver, from Russell King.
6) Start adding netlink support for ethtool operations, from Michal
Kubecek.
7) Add XDP drop and TX action support to ena driver, from Sameeh
Jubran.
8) Add new ipv4 route notifications so that mlxsw driver does not have
to handle identical routes itself. From Ido Schimmel.
9) Add BPF dynamic program extensions, from Alexei Starovoitov.
10) Support RX and TX timestamping in igc, from Vinicius Costa Gomes.
11) Add support for macsec HW offloading, from Antoine Tenart.
12) Add initial support for MPTCP protocol, from Christoph Paasch,
Matthieu Baerts, Florian Westphal, Peter Krystad, and many others.
13) Add Octeontx2 PF support, from Sunil Goutham, Geetha sowjanya, Linu
Cherian, and others.
* git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1469 commits)
net: phy: add default ARCH_BCM_IPROC for MDIO_BCM_IPROC
udp: segment looped gso packets correctly
netem: change mailing list
qed: FW 8.42.2.0 debug features
qed: rt init valid initialization changed
qed: Debug feature: ilt and mdump
qed: FW 8.42.2.0 Add fw overlay feature
qed: FW 8.42.2.0 HSI changes
qed: FW 8.42.2.0 iscsi/fcoe changes
qed: Add abstraction for different hsi values per chip
qed: FW 8.42.2.0 Additional ll2 type
qed: Use dmae to write to widebus registers in fw_funcs
qed: FW 8.42.2.0 Parser offsets modified
qed: FW 8.42.2.0 Queue Manager changes
qed: FW 8.42.2.0 Expose new registers and change windows
qed: FW 8.42.2.0 Internal ram offsets modifications
MAINTAINERS: Add entry for Marvell OcteonTX2 Physical Function driver
Documentation: net: octeontx2: Add RVU HW and drivers overview
octeontx2-pf: ethtool RSS config support
octeontx2-pf: Add basic ethtool support
...
Diffstat (limited to 'drivers/net/wireless/ath')
84 files changed, 49811 insertions, 180 deletions
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 7b90b8546162..b10972b6cba4 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -62,5 +62,6 @@ source "drivers/net/wireless/ath/ar5523/Kconfig" source "drivers/net/wireless/ath/wil6210/Kconfig" source "drivers/net/wireless/ath/ath10k/Kconfig" source "drivers/net/wireless/ath/wcn36xx/Kconfig" +source "drivers/net/wireless/ath/ath11k/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index ee2b2431e5a3..8e4ae9de5ae4 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_AR5523) += ar5523/ obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH10K) += ath10k/ obj-$(CONFIG_WCN36XX) += wcn36xx/ +obj-$(CONFIG_ATH11K) += ath11k/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index da2d179430ca..49cc4b7ed516 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -74,7 +74,7 @@ static void ar5523_read_reply(struct ar5523 *ar, struct ar5523_cmd_hdr *hdr, if (cmd->odata) { if (cmd->olen < olen) { - ar5523_err(ar, "olen to small %d < %d\n", + ar5523_err(ar, "olen too small %d < %d\n", cmd->olen, olen); cmd->olen = 0; cmd->res = -EOVERFLOW; @@ -1770,6 +1770,8 @@ static const struct usb_device_id ar5523_id_table[] = { AR5523_DEVICE_UX(0x0846, 0x4300), /* Netgear / WG111U */ AR5523_DEVICE_UG(0x0846, 0x4250), /* Netgear / WG111T */ AR5523_DEVICE_UG(0x0846, 0x5f00), /* Netgear / WPN111 */ + AR5523_DEVICE_UG(0x083a, 0x4506), /* SMC / EZ Connect + SMCWUSBT-G2 */ AR5523_DEVICE_UG(0x157e, 0x3006), /* Umedia / AR5523_1 */ AR5523_DEVICE_UX(0x157e, 0x3205), /* Umedia / AR5523_2 */ AR5523_DEVICE_UG(0x157e, 0x3006), /* Umedia / TEW444UBEU */ diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 95dc4be82e5c..ea908107581d 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -346,6 +346,52 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result) return 0; } +static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 length) +{ + struct bmi_cmd *cmd; + u32 hdrlen = sizeof(cmd->id) + sizeof(cmd->lz_data); + u32 txlen; + int ret; + size_t buf_len; + + ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n", + buffer, length); + + if (ar->bmi.done_sent) { + ath10k_warn(ar, "command disallowed\n"); + return -EBUSY; + } + + buf_len = sizeof(*cmd) + BMI_MAX_LARGE_DATA_SIZE - BMI_MAX_DATA_SIZE; + cmd = kzalloc(buf_len, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + while (length) { + txlen = min(length, BMI_MAX_LARGE_DATA_SIZE - hdrlen); + + WARN_ON_ONCE(txlen & 3); + + cmd->id = __cpu_to_le32(BMI_LZ_DATA); + cmd->lz_data.len = __cpu_to_le32(txlen); + memcpy(cmd->lz_data.payload, buffer, txlen); + + ret = ath10k_hif_exchange_bmi_msg(ar, cmd, hdrlen + txlen, + NULL, NULL); + if (ret) { + ath10k_warn(ar, "unable to write to the device\n"); + return ret; + } + + buffer += txlen; + length -= txlen; + } + + kfree(cmd); + + return 0; +} + int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) { struct bmi_cmd cmd; @@ -430,7 +476,11 @@ int ath10k_bmi_fast_download(struct ath10k *ar, if (trailer_len > 0) memcpy(trailer, buffer + head_len, trailer_len); - ret = ath10k_bmi_lz_data(ar, buffer, head_len); + if (ar->hw_params.bmi_large_size_download) + ret = ath10k_bmi_lz_data_large(ar, buffer, head_len); + else + ret = ath10k_bmi_lz_data(ar, buffer, head_len); + if (ret) return ret; diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index ef3bdba43bed..f6fadcbdd86e 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -45,6 +45,15 @@ sizeof(u32) + \ sizeof(u32)) +/* Maximum data size used for large BMI transfers */ +#define BMI_MAX_LARGE_DATA_SIZE 2048 + +/* len = cmd + addr + length */ +#define BMI_MAX_LARGE_CMDBUF_SIZE (BMI_MAX_LARGE_DATA_SIZE + \ + sizeof(u32) + \ + sizeof(u32) + \ + sizeof(u32)) + /* BMI Commands */ enum bmi_cmd_id { @@ -258,6 +267,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result); int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address); int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length); + int ath10k_bmi_fast_download(struct ath10k *ar, u32 address, const void *buffer, u32 length); int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val); diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 4f76ba5d78a9..5ec16ce19b69 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -189,6 +189,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .num_wds_entries = 0x20, .uart_pin_workaround = true, .tx_stats_over_pktlog = false, + .bmi_large_size_download = true, }, { .id = QCA6174_HW_2_1_VERSION, @@ -714,18 +715,6 @@ static int ath10k_init_sdio(struct ath10k *ar, enum ath10k_firmware_mode mode) if (ret) return ret; - /* Explicitly set fwlog prints to zero as target may turn it on - * based on scratch registers. - */ - ret = ath10k_bmi_read32(ar, hi_option_flag, ¶m); - if (ret) - return ret; - - param |= HI_OPTION_DISABLE_DBGLOG; - ret = ath10k_bmi_write32(ar, hi_option_flag, param); - if (ret) - return ret; - return 0; } @@ -3231,6 +3220,8 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, init_waitqueue_head(&ar->htt.empty_tx_wq); init_waitqueue_head(&ar->wmi.tx_credits_wq); + skb_queue_head_init(&ar->htt.rx_indication_head); + init_completion(&ar->offchan_tx_completed); INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); skb_queue_head_init(&ar->offchan_tx_queue); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index af68eb5d0776..5101bf2b5b15 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -124,6 +124,7 @@ struct ath10k_skb_cb { struct ath10k_skb_rxcb { dma_addr_t paddr; struct hlist_node hlist; + u8 eid; }; static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) @@ -1180,6 +1181,7 @@ struct ath10k { struct { /* protected by data_lock */ + u32 rx_crc_err_drop; u32 fw_crash_counter; u32 fw_warm_reset_counter; u32 fw_cold_reset_counter; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 04c50a26a4f4..e000677ac516 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1094,6 +1094,7 @@ static const char ath10k_gstrings_stats[][ETH_GSTRING_LEN] = { "d_rts_good", "d_tx_power", /* in .5 dbM I think */ "d_rx_crc_err", /* fcs_bad */ + "d_rx_crc_err_drop", /* frame with FCS error, dropped late in kernel */ "d_no_beacon", "d_tx_mpdus_queued", "d_tx_msdu_queued", @@ -1193,6 +1194,7 @@ void ath10k_debug_get_et_stats(struct ieee80211_hw *hw, data[i++] = pdev_stats->rts_good; data[i++] = pdev_stats->chan_tx_power; data[i++] = pdev_stats->fcs_bad; + data[i++] = ar->stats.rx_crc_err_drop; data[i++] = pdev_stats->no_beacons; data[i++] = pdev_stats->mpdu_enqued; data[i++] = pdev_stats->msdu_enqued; diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 1d4d1a1992fe..2248d6c022f4 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -270,7 +270,7 @@ ath10k_htc_process_lookahead_bundle(struct ath10k_htc *htc, struct ath10k *ar = htc->ar; int bundle_cnt = len / sizeof(*report); - if (!bundle_cnt || (bundle_cnt > HTC_HOST_MAX_MSG_PER_RX_BUNDLE)) { + if (!bundle_cnt || (bundle_cnt > htc->max_msgs_per_htc_bundle)) { ath10k_warn(ar, "Invalid lookahead bundle count: %d\n", bundle_cnt); return -EINVAL; @@ -800,8 +800,8 @@ setup: &ep->ul_pipe_id, &ep->dl_pipe_id); if (status) { - ath10k_warn(ar, "unsupported HTC service id: %d\n", - ep->service_id); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC service id: %d\n", + ep->service_id); return status; } @@ -878,8 +878,8 @@ static bool ath10k_htc_pktlog_svc_supported(struct ath10k *ar) &ul_pipe_id, &dl_pipe_id); if (status) { - ath10k_warn(ar, "unsupported HTC service id: %d\n", - ATH10K_HTC_SVC_ID_HTT_LOG_MSG); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC pktlog service id: %d\n", + ATH10K_HTC_SVC_ID_HTT_LOG_MSG); return false; } diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index f55d3caec61f..065c82d9d689 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -12,6 +12,7 @@ #include <linux/bug.h> #include <linux/skbuff.h> #include <linux/timer.h> +#include <linux/bitfield.h> struct ath10k; @@ -39,7 +40,7 @@ struct ath10k; * 4-byte aligned. */ -#define HTC_HOST_MAX_MSG_PER_RX_BUNDLE 8 +#define HTC_HOST_MAX_MSG_PER_RX_BUNDLE 32 enum ath10k_htc_tx_flags { ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01, @@ -49,9 +50,27 @@ enum ath10k_htc_tx_flags { enum ath10k_htc_rx_flags { ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK = 0x01, ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02, - ATH10K_HTC_FLAG_BUNDLE_MASK = 0xF0 }; +#define ATH10K_HTC_FLAG_BUNDLE_MASK GENMASK(7, 4) + +/* bits 2-3 are for extra bundle count bits 4-5 */ +#define ATH10K_HTC_BUNDLE_EXTRA_MASK GENMASK(3, 2) +#define ATH10K_HTC_BUNDLE_EXTRA_SHIFT 4 + +static inline unsigned int ath10k_htc_get_bundle_count(u8 max_msgs, u8 flags) +{ + unsigned int count, extra_count = 0; + + count = FIELD_GET(ATH10K_HTC_FLAG_BUNDLE_MASK, flags); + + if (max_msgs > 16) + extra_count = FIELD_GET(ATH10K_HTC_BUNDLE_EXTRA_MASK, flags) << + ATH10K_HTC_BUNDLE_EXTRA_SHIFT; + + return count + extra_count; +} + struct ath10k_htc_hdr { u8 eid; /* @enum ath10k_htc_ep_id */ u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */ diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 30c080094af1..4a12564fc30e 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1869,6 +1869,8 @@ struct ath10k_htt { struct ath10k *ar; enum ath10k_htc_ep_id eid; + struct sk_buff_head rx_indication_head; + u8 target_version_major; u8 target_version_minor; struct completion target_version_received; @@ -2283,6 +2285,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu); void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget); +int ath10k_htt_rx_hl_indication(struct ath10k *ar, int budget); void ath10k_htt_set_tx_ops(struct ath10k_htt *htt); void ath10k_htt_set_rx_ops(struct ath10k_htt *htt); #endif diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index d95b63f133ab..38a5814cf345 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1285,6 +1285,13 @@ static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb) status = IEEE80211_SKB_RXCB(skb); + if (!(ar->filter_flags & FIF_FCSFAIL) && + status->flag & RX_FLAG_FAILED_FCS_CRC) { + ar->stats.rx_crc_err_drop++; + dev_kfree_skb_any(skb); + return; + } + ath10k_dbg(ar, ATH10K_DBG_DATA, "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", skb, @@ -2133,7 +2140,7 @@ static bool ath10k_htt_rx_pn_check_replay_hl(struct ath10k *ar, if (last_pn_valid) pn_invalid = ath10k_htt_rx_pn_cmp48(&new_pn, last_pn); else - peer->tids_last_pn_valid[tid] = 1; + peer->tids_last_pn_valid[tid] = true; if (!pn_invalid) last_pn->pn48 = new_pn.pn48; @@ -2196,8 +2203,8 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, HTT_RX_IND_MPDU_STATUS_OK && mpdu_ranges->mpdu_range_status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) { - ath10k_warn(ar, "MPDU range status: %d\n", - mpdu_ranges->mpdu_range_status); + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt mpdu_range_status %d\n", + mpdu_ranges->mpdu_range_status); goto err; } @@ -2235,8 +2242,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, hdr = (struct ieee80211_hdr *)skb->data; qos = ieee80211_is_data_qos(hdr->frame_control); + rx_status = IEEE80211_SKB_RXCB(skb); - rx_status->chains |= BIT(0); + memset(rx_status, 0, sizeof(*rx_status)); + if (rx->ppdu.combined_rssi == 0) { /* SDIO firmware does not provide signal */ rx_status->signal = 0; @@ -2350,7 +2359,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, memcpy(skb->data + offset, &qos_ctrl, IEEE80211_QOS_CTL_LEN); } - ieee80211_rx_ni(ar->hw, skb); + if (ar->napi.dev) + ieee80211_rx_napi(ar->hw, NULL, skb, &ar->napi); + else + ieee80211_rx_ni(ar->hw, skb); /* We have delivered the skb to the upper layers (mac80211) so we * must not free it. @@ -3751,14 +3763,12 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } case HTT_T2H_MSG_TYPE_RX_IND: - if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) - return ath10k_htt_rx_proc_rx_ind_hl(htt, - &resp->rx_ind_hl, - skb, - HTT_RX_PN_CHECK, - HTT_RX_NON_TKIP_MIC); - else + if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL) { ath10k_htt_rx_proc_rx_ind_ll(htt, &resp->rx_ind); + } else { + skb_queue_tail(&htt->rx_indication_head, skb); + return false; + } break; case HTT_T2H_MSG_TYPE_PEER_MAP: { struct htt_peer_map_event ev = { @@ -3948,6 +3958,37 @@ static int ath10k_htt_rx_deliver_msdu(struct ath10k *ar, int quota, int budget) return quota; } +int ath10k_htt_rx_hl_indication(struct ath10k *ar, int budget) +{ + struct htt_resp *resp; + struct ath10k_htt *htt = &ar->htt; + struct sk_buff *skb; + bool release; + int quota; + + for (quota = 0; quota < budget; quota++) { + skb = skb_dequeue(&htt->rx_indication_head); + if (!skb) + break; + + resp = (struct htt_resp *)skb->data; + + release = ath10k_htt_rx_proc_rx_ind_hl(htt, + &resp->rx_ind_hl, + skb, + HTT_RX_PN_CHECK, + HTT_RX_NON_TKIP_MIC); + + if (release) + dev_kfree_skb_any(skb); + + ath10k_dbg(ar, ATH10K_DBG_HTT, "rx indication poll pending count:%d\n", + skb_queue_len(&htt->rx_indication_head)); + } + return quota; +} +EXPORT_SYMBOL(ath10k_htt_rx_hl_indication); + int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget) { struct ath10k_htt *htt = &ar->htt; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 35a362329a4f..775fd62fb92d 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -613,6 +613,9 @@ struct ath10k_hw_params { /* target supporting fw download via diag ce */ bool fw_diag_ce_download; + /* target supporting fw download via large size BMI */ + bool bmi_large_size_download; + /* need to set uart pin if disable uart print, workaround for a * firmware bug */ @@ -813,7 +816,7 @@ ath10k_is_rssi_enable(struct ath10k_hw_params *hw, #define TARGET_10_4_TX_DBG_LOG_SIZE 1024 #define TARGET_10_4_NUM_WDS_ENTRIES 32 -#define TARGET_10_4_DMA_BURST_SIZE 0 +#define TARGET_10_4_DMA_BURST_SIZE 1 #define TARGET_10_4_MAC_AGGR_DELIM 0 #define TARGET_10_4_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 #define TARGET_10_4_VOW_CONFIG 0 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 978f0037ed52..7fee35ff966b 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1098,7 +1098,7 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar) ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn(ar, "failed to to request monitor vdev %i stop: %d\n", + ath10k_warn(ar, "failed to request monitor vdev %i stop: %d\n", ar->monitor_vdev_id, ret); ret = ath10k_vdev_setup_sync(ar); @@ -6329,6 +6329,9 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (sta && sta->tdls) ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, ar->wmi.peer_param->authorize, 1); + else if (sta && cmd == SET_KEY && (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + ath10k_wmi_peer_set_param(ar, arvif->vdev_id, peer_addr, + ar->wmi.peer_param->authorize, 1); exit: mutex_unlock(&ar->conf_mutex); @@ -8908,6 +8911,7 @@ int ath10k_mac_register(struct ath10k *ar) WMI_PNO_MAX_SCHED_SCAN_PLAN_INT; ar->hw->wiphy->max_sched_scan_plan_iterations = WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS; + ar->hw->wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; } ar->hw->vif_data_size = sizeof(struct ath10k_vif); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index bb44f5a0941b..ded7a220a4aa 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1578,7 +1578,7 @@ static int ath10k_pci_set_ram_config(struct ath10k *ar, u32 config) return 0; } -/* if an error happened returns < 0, otherwise the length */ +/* Always returns the length */ static int ath10k_pci_dump_memory_sram(struct ath10k *ar, const struct ath10k_mem_region *region, u8 *buf) @@ -1604,11 +1604,22 @@ static int ath10k_pci_dump_memory_reg(struct ath10k *ar, { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); u32 i; + int ret; + + mutex_lock(&ar->conf_mutex); + if (ar->state != ATH10K_STATE_ON) { + ath10k_warn(ar, "Skipping pci_dump_memory_reg invalid state\n"); + ret = -EIO; + goto done; + } for (i = 0; i < region->len; i += 4) *(u32 *)(buf + i) = ioread32(ar_pci->mem + region->start + i); - return region->len; + ret = region->len; +done: + mutex_unlock(&ar->conf_mutex); + return ret; } /* if an error happened returns < 0, otherwise the length */ @@ -1704,7 +1715,11 @@ static void ath10k_pci_dump_memory(struct ath10k *ar, count = ath10k_pci_dump_memory_sram(ar, current_region, buf); break; case ATH10K_MEM_REGION_TYPE_IOREG: - count = ath10k_pci_dump_memory_reg(ar, current_region, buf); + ret = ath10k_pci_dump_memory_reg(ar, current_region, buf); + if (ret < 0) + break; + + count = ret; break; default: ret = ath10k_pci_dump_memory_generic(ar, current_region, buf); diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index a0ba07b85362..85dce43c5439 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -84,6 +84,9 @@ static int ath10k_qmi_setup_msa_permissions(struct ath10k_qmi *qmi) int ret; int i; + if (qmi->msa_fixed_perm) + return 0; + for (i = 0; i < qmi->nr_mem_region; i++) { ret = ath10k_qmi_map_msa_permission(qmi, &qmi->mem_region[i]); if (ret) @@ -102,6 +105,9 @@ static void ath10k_qmi_remove_msa_permission(struct ath10k_qmi *qmi) { int i; + if (qmi->msa_fixed_perm) + return; + for (i = 0; i < qmi->nr_mem_region; i++) ath10k_qmi_unmap_msa_permission(qmi, &qmi->mem_region[i]); } @@ -279,7 +285,15 @@ static int ath10k_qmi_bdf_dnld_send_sync(struct ath10k_qmi *qmi) if (ret < 0) goto out; - if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + /* end = 1 triggers a CRC check on the BDF. If this fails, we + * get a QMI_ERR_MALFORMED_MSG_V01 error, but the FW is still + * willing to use the BDF. For some platforms, all the valid + * released BDFs fail this CRC check, so attempt to detect this + * scenario and treat it as non-fatal. + */ + if (resp.resp.result != QMI_RESULT_SUCCESS_V01 && + !(req->end == 1 && + resp.resp.result == QMI_ERR_MALFORMED_MSG_V01)) { ath10k_err(ar, "failed to download board data file: %d\n", resp.resp.error); ret = -EINVAL; @@ -635,7 +649,9 @@ static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi) if (ret < 0) goto out; - if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + /* older FW didn't support this request, which is not fatal */ + if (resp.resp.result != QMI_RESULT_SUCCESS_V01 && + resp.resp.error != QMI_ERR_NOT_SUPPORTED_V01) { ath10k_err(ar, "host capability request rejected: %d\n", resp.resp.error); ret = -EINVAL; goto out; @@ -1025,6 +1041,9 @@ static int ath10k_qmi_setup_msa_resources(struct ath10k_qmi *qmi, u32 msa_size) qmi->msa_mem_size = msa_size; } + if (of_property_read_bool(dev->of_node, "qcom,msa-fixed-perm")) + qmi->msa_fixed_perm = true; + ath10k_dbg(ar, ATH10K_DBG_QMI, "msa pa: %pad , msa va: 0x%p\n", &qmi->msa_pa, qmi->msa_va); diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h index 40aafb875ed0..dc257375f161 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.h +++ b/drivers/net/wireless/ath/ath10k/qmi.h @@ -104,6 +104,7 @@ struct ath10k_qmi { bool fw_ready; char fw_build_timestamp[MAX_TIMESTAMP_LEN + 1]; struct ath10k_qmi_cal_data cal_data[MAX_NUM_CAL_V01]; + bool msa_fixed_perm; }; int ath10k_qmi_wlan_enable(struct ath10k *ar, diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 120200a93bcc..e5316b911e1d 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -24,6 +24,8 @@ #include "trace.h" #include "sdio.h" +#define ATH10K_SDIO_VSG_BUF_SIZE (64 * 1024) + /* inlined helper functions */ static inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio, @@ -417,6 +419,7 @@ static int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar, struct ath10k_htc *htc = &ar->htc; struct ath10k_sdio_rx_data *pkt; struct ath10k_htc_ep *ep; + struct ath10k_skb_rxcb *cb; enum ath10k_htc_ep_id id; int ret, i, *n_lookahead_local; u32 *lookaheads_local; @@ -462,10 +465,16 @@ static int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar, if (ret) goto out; - if (!pkt->trailer_only) - ep->ep_ops.ep_rx_complete(ar_sdio->ar, pkt->skb); - else + if (!pkt->trailer_only) { + cb = ATH10K_SKB_RXCB(pkt->skb); + cb->eid = id; + + skb_queue_tail(&ar_sdio->rx_head, pkt->skb); + queue_work(ar->workqueue_aux, + &ar_sdio->async_work_rx); + } else { kfree_skb(pkt->skb); + } /* The RX complete handler now owns the skb...*/ pkt->skb = NULL; @@ -484,21 +493,22 @@ out: return ret; } -static int ath10k_sdio_mbox_alloc_pkt_bundle(struct ath10k *ar, - struct ath10k_sdio_rx_data *rx_pkts, - struct ath10k_htc_hdr *htc_hdr, - size_t full_len, size_t act_len, - size_t *bndl_cnt) +static int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar, + struct ath10k_sdio_rx_data *rx_pkts, + struct ath10k_htc_hdr *htc_hdr, + size_t full_len, size_t act_len, + size_t *bndl_cnt) { int ret, i; + u8 max_msgs = ar->htc.max_msgs_per_htc_bundle; - *bndl_cnt = FIELD_GET(ATH10K_HTC_FLAG_BUNDLE_MASK, htc_hdr->flags); + *bndl_cnt = ath10k_htc_get_bundle_count(max_msgs, htc_hdr->flags); - if (*bndl_cnt > HTC_HOST_MAX_MSG_PER_RX_BUNDLE) { + if (*bndl_cnt > max_msgs) { ath10k_warn(ar, "HTC bundle length %u exceeds maximum %u\n", le16_to_cpu(htc_hdr->len), - HTC_HOST_MAX_MSG_PER_RX_BUNDLE); + max_msgs); return -ENOMEM; } @@ -529,12 +539,11 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, size_t full_len, act_len; bool last_in_bundle; int ret, i; + int pkt_cnt = 0; if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) { - ath10k_warn(ar, - "the total number of pkgs to be fetched (%u) exceeds maximum %u\n", - n_lookaheads, - ATH10K_SDIO_MAX_RX_MSGS); + ath10k_warn(ar, "the total number of pkgs to be fetched (%u) exceeds maximum %u\n", + n_lookaheads, ATH10K_SDIO_MAX_RX_MSGS); ret = -ENOMEM; goto err; } @@ -543,10 +552,8 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, htc_hdr = (struct ath10k_htc_hdr *)&lookaheads[i]; last_in_bundle = false; - if (le16_to_cpu(htc_hdr->len) > - ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) { - ath10k_warn(ar, - "payload length %d exceeds max htc length: %zu\n", + if (le16_to_cpu(htc_hdr->len) > ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) { + ath10k_warn(ar, "payload length %d exceeds max htc length: %zu\n", le16_to_cpu(htc_hdr->len), ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH); ret = -ENOMEM; @@ -557,36 +564,37 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, full_len = ath10k_sdio_calc_txrx_padded_len(ar_sdio, act_len); if (full_len > ATH10K_SDIO_MAX_BUFFER_SIZE) { - ath10k_warn(ar, - "rx buffer requested with invalid htc_hdr length (%d, 0x%x): %d\n", + ath10k_warn(ar, "rx buffer requested with invalid htc_hdr length (%d, 0x%x): %d\n", htc_hdr->eid, htc_hdr->flags, le16_to_cpu(htc_hdr->len)); ret = -EINVAL; goto err; } - if (htc_hdr->flags & ATH10K_HTC_FLAG_BUNDLE_MASK) { + if (ath10k_htc_get_bundle_count( + ar->htc.max_msgs_per_htc_bundle, htc_hdr->flags)) { /* HTC header indicates that every packet to follow * has the same padded length so that it can be * optimally fetched as a full bundle. */ size_t bndl_cnt; - ret = ath10k_sdio_mbox_alloc_pkt_bundle(ar, - &ar_sdio->rx_pkts[i], - htc_hdr, - full_len, - act_len, - &bndl_cnt); + ret = ath10k_sdio_mbox_alloc_bundle(ar, + &ar_sdio->rx_pkts[pkt_cnt], + htc_hdr, + full_len, + act_len, + &bndl_cnt); if (ret) { - ath10k_warn(ar, "alloc_bundle error %d\n", ret); + ath10k_warn(ar, "failed to allocate a bundle: %d\n", + ret); goto err; } - n_lookaheads += bndl_cnt; - i += bndl_cnt; - /*Next buffer will be the last in the bundle */ + pkt_cnt += bndl_cnt; + + /* next buffer will be the last in the bundle */ last_in_bundle = true; } @@ -597,7 +605,7 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, if (htc_hdr->flags & ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK) full_len += ATH10K_HIF_MBOX_BLOCK_SIZE; - ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[i], + ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[pkt_cnt], act_len, full_len, last_in_bundle, @@ -606,9 +614,11 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret); goto err; } + + pkt_cnt++; } - ar_sdio->n_rx_pkts = i; + ar_sdio->n_rx_pkts = pkt_cnt; return 0; @@ -622,10 +632,10 @@ err: return ret; } -static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar, - struct ath10k_sdio_rx_data *pkt) +static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar) { struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); + struct ath10k_sdio_rx_data *pkt = &ar_sdio->rx_pkts[0]; struct sk_buff *skb = pkt->skb; struct ath10k_htc_hdr *htc_hdr; int ret; @@ -633,48 +643,75 @@ static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar, ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr, skb->data, pkt->alloc_len); if (ret) - goto out; + goto err; - /* Update actual length. The original length may be incorrect, - * as the FW will bundle multiple packets as long as their sizes - * fit within the same aligned length (pkt->alloc_len). - */ htc_hdr = (struct ath10k_htc_hdr *)skb->data; pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); + if (pkt->act_len > pkt->alloc_len) { - ath10k_warn(ar, "rx packet too large (%zu > %zu)\n", - pkt->act_len, pkt->alloc_len); - ret = -EMSGSIZE; - goto out; + ret = -EINVAL; + goto err; } skb_put(skb, pkt->act_len); + return 0; -out: - pkt->status = ret; +err: + ar_sdio->n_rx_pkts = 0; + ath10k_sdio_mbox_free_rx_pkt(pkt); return ret; } -static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar) +static int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar) { struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); + struct ath10k_sdio_rx_data *pkt; + struct ath10k_htc_hdr *htc_hdr; int ret, i; + u32 pkt_offset, virt_pkt_len; + + virt_pkt_len = 0; + for (i = 0; i < ar_sdio->n_rx_pkts; i++) + virt_pkt_len += ar_sdio->rx_pkts[i].alloc_len; + + if (virt_pkt_len > ATH10K_SDIO_VSG_BUF_SIZE) { + ath10k_warn(ar, "sdio vsg buffer size limit: %d\n", virt_pkt_len); + ret = -E2BIG; + goto err; + } + ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr, + ar_sdio->vsg_buffer, virt_pkt_len); + if (ret) { + ath10k_warn(ar, "failed to read bundle packets: %d", ret); + goto err; + } + + pkt_offset = 0; for (i = 0; i < ar_sdio->n_rx_pkts; i++) { - ret = ath10k_sdio_mbox_rx_packet(ar, - &ar_sdio->rx_pkts[i]); - if (ret) + pkt = &ar_sdio->rx_pkts[i]; + htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset); + pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); + + if (pkt->act_len > pkt->alloc_len ) { + ret = -EINVAL; goto err; + } + + skb_put_data(pkt->skb, htc_hdr, pkt->act_len); + pkt_offset += pkt->alloc_len; } return 0; err: /* Free all packets that was not successfully fetched. */ - for (; i < ar_sdio->n_rx_pkts; i++) + for (i = 0; i < ar_sdio->n_rx_pkts; i++) ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]); + ar_sdio->n_rx_pkts = 0; + return ret; } @@ -717,7 +754,10 @@ static int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar, */ *done = false; - ret = ath10k_sdio_mbox_rx_fetch(ar); + if (ar_sdio->n_rx_pkts > 1) + ret = ath10k_sdio_mbox_rx_fetch_bundle(ar); + else + ret = ath10k_sdio_mbox_rx_fetch(ar); /* Process fetched packets. This will potentially update * n_lookaheads depending on if the packets contain lookahead @@ -1293,6 +1333,31 @@ static void __ath10k_sdio_write_async(struct ath10k *ar, ath10k_sdio_free_bus_req(ar, req); } +/* To improve throughput use workqueue to deliver packets to HTC layer, + * this way SDIO bus is utilised much better. + */ +static void ath10k_rx_indication_async_work(struct work_struct *work) +{ + struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio, + async_work_rx); + struct ath10k *ar = ar_sdio->ar; + struct ath10k_htc_ep *ep; + struct ath10k_skb_rxcb *cb; + struct sk_buff *skb; + + while (true) { + skb = skb_dequeue(&ar_sdio->rx_head); + if (!skb) + break; + cb = ATH10K_SKB_RXCB(skb); + ep = &ar->htc.endpoint[cb->eid]; + ep->ep_ops.ep_rx_complete(ar, skb); + } + + if (test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) + napi_schedule(&ar->napi); +} + static void ath10k_sdio_write_async_work(struct work_struct *work) { struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio, @@ -1681,6 +1746,8 @@ static int ath10k_sdio_hif_start(struct ath10k *ar) struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); int ret; + napi_enable(&ar->napi); + /* Sleep 20 ms before HIF interrupts are disabled. * This will give target plenty of time to process the BMI done * request before interrupts are disabled. @@ -1805,13 +1872,16 @@ static void ath10k_sdio_hif_stop(struct ath10k *ar) } spin_unlock_bh(&ar_sdio->wr_async_lock); + + napi_synchronize(&ar->napi); + napi_disable(&ar->napi); } #ifdef CONFIG_PM static int ath10k_sdio_hif_suspend(struct ath10k *ar) { - return -EOPNOTSUPP; + return 0; } static int ath10k_sdio_hif_resume(struct ath10k *ar) @@ -1961,7 +2031,26 @@ static const struct ath10k_hif_ops ath10k_sdio_hif_ops = { */ static int ath10k_sdio_pm_suspend(struct device *device) { - return 0; + struct sdio_func *func = dev_to_sdio_func(device); + struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func); + struct ath10k *ar = ar_sdio->ar; + mmc_pm_flag_t pm_flag, pm_caps; + int ret; + + if (!device_may_wakeup(ar->dev)) + return 0; + + pm_flag = MMC_PM_KEEP_POWER; + + ret = sdio_set_host_pm_flags(func, pm_flag); + if (ret) { + pm_caps = sdio_get_host_pm_caps(func); + ath10k_warn(ar, "failed to set sdio host pm flags (0x%x, 0x%x): %d\n", + pm_flag, pm_caps, ret); + return ret; + } + + return ret; } static int ath10k_sdio_pm_resume(struct device *device) @@ -1980,6 +2069,20 @@ static SIMPLE_DEV_PM_OPS(ath10k_sdio_pm_ops, ath10k_sdio_pm_suspend, #endif /* CONFIG_PM_SLEEP */ +static int ath10k_sdio_napi_poll(struct napi_struct *ctx, int budget) +{ + struct ath10k *ar = container_of(ctx, struct ath10k, napi); + int done; + + done = ath10k_htt_rx_hl_indication(ar, budget); + ath10k_dbg(ar, ATH10K_DBG_SDIO, "napi poll: done: %d, budget:%d\n", done, budget); + + if (done < budget) + napi_complete_done(ctx, done); + + return done; +} + static int ath10k_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { @@ -2005,6 +2108,9 @@ static int ath10k_sdio_probe(struct sdio_func *func, return -ENOMEM; } + netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll, + ATH10K_NAPI_BUDGET); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n", func->num, func->vendor, func->device, @@ -2020,6 +2126,12 @@ static int ath10k_sdio_probe(struct sdio_func *func, goto err_core_destroy; } + ar_sdio->vsg_buffer = devm_kmalloc(ar->dev, ATH10K_SDIO_VSG_BUF_SIZE, GFP_KERNEL); + if (!ar_sdio->vsg_buffer) { + ret = -ENOMEM; + goto err_core_destroy; + } + ar_sdio->irq_data.irq_en_reg = devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs), GFP_KERNEL); @@ -2028,7 +2140,7 @@ static int ath10k_sdio_probe(struct sdio_func *func, goto err_core_destroy; } - ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_CMDBUF_SIZE, GFP_KERNEL); + ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_LARGE_CMDBUF_SIZE, GFP_KERNEL); if (!ar_sdio->bmi_buf) { ret = -ENOMEM; goto err_core_destroy; @@ -2057,6 +2169,9 @@ static int ath10k_sdio_probe(struct sdio_func *func, for (i = 0; i < ATH10K_SDIO_BUS_REQUEST_MAX_NUM; i++) ath10k_sdio_free_bus_req(ar, &ar_sdio->bus_req[i]); + skb_queue_head_init(&ar_sdio->rx_head); + INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work); + dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, id->device); switch (dev_id_base) { case QCA_MANUFACTURER_ID_AR6005_BASE: @@ -2080,6 +2195,8 @@ static int ath10k_sdio_probe(struct sdio_func *func, bus_params.chip_id = 0; bus_params.hl_msdu_ids = true; + ar->hw->max_mtu = ETH_DATA_LEN; + ret = ath10k_core_register(ar, &bus_params); if (ret) { ath10k_err(ar, "failed to register driver core: %d\n", ret); @@ -2106,6 +2223,9 @@ static void ath10k_sdio_remove(struct sdio_func *func) func->num, func->vendor, func->device); ath10k_core_unregister(ar); + + netif_napi_del(&ar->napi); + ath10k_core_destroy(ar); flush_workqueue(ar_sdio->workqueue); diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h index b8c7ac0330bd..33195f49acab 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.h +++ b/drivers/net/wireless/ath/ath10k/sdio.h @@ -89,10 +89,10 @@ * to the maximum value (HTC_HOST_MAX_MSG_PER_RX_BUNDLE). * * in this case the driver must allocate - * (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * HTC_HOST_MAX_MSG_PER_RX_BUNDLE) skb's. + * (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * 2) skb's. */ #define ATH10K_SDIO_MAX_RX_MSGS \ - (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * HTC_HOST_MAX_MSG_PER_RX_BUNDLE) + (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * 2) #define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL 0x00000868u #define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_OFF 0xFFFEFFFF @@ -126,7 +126,6 @@ struct ath10k_sdio_rx_data { bool part_of_bundle; bool last_in_bundle; bool trailer_only; - int status; }; struct ath10k_sdio_irq_proc_regs { @@ -138,8 +137,8 @@ struct ath10k_sdio_irq_proc_regs { u8 rx_lookahead_valid; u8 host_int_status2; u8 gmbox_rx_avail; - __le32 rx_lookahead[2]; - __le32 rx_gmbox_lookahead_alias[2]; + __le32 rx_lookahead[2 * ATH10K_HIF_MBOX_NUM_MAX]; + __le32 int_status_enable; }; struct ath10k_sdio_irq_enable_regs { @@ -187,6 +186,9 @@ struct ath10k_sdio { struct ath10k_sdio_bus_request bus_req[ATH10K_SDIO_BUS_REQUEST_MAX_NUM]; /* free list of bus requests */ struct list_head bus_req_freeq; + + struct sk_buff_head rx_head; + /* protects access to bus_req_freeq */ spinlock_t lock; @@ -196,6 +198,13 @@ struct ath10k_sdio { struct ath10k *ar; struct ath10k_sdio_irq_data irq_data; + /* temporary buffer for sdio read. + * It is allocated when probe, and used for receive bundled packets, + * the read for bundled packets is not parallel, so it does not need + * protected. + */ + u8 *vsg_buffer; + /* temporary buffer for BMI requests */ u8 *bmi_buf; @@ -206,6 +215,8 @@ struct ath10k_sdio { struct list_head wr_asyncq; /* protects access to wr_asyncq */ spinlock_t wr_async_lock; + + struct work_struct async_work_rx; }; static inline struct ath10k_sdio *ath10k_sdio_priv(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 16177497bba7..21081b4a27d7 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -46,7 +46,7 @@ static const char * const ath10k_regulators[] = { }; static const char * const ath10k_clocks[] = { - "cxo_ref_clk_pin", + "cxo_ref_clk_pin", "qdss", }; static void ath10k_snoc_htc_tx_cb(struct ath10k_ce_pipe *ce_state); @@ -582,7 +582,7 @@ static void ath10k_snoc_process_rx_cb(struct ath10k_ce_pipe *ce_state, max_nbytes, DMA_FROM_DEVICE); if (unlikely(max_nbytes < nbytes)) { - ath10k_warn(ar, "rxed more than expected (nbytes %d, max %d)", + ath10k_warn(ar, "rxed more than expected (nbytes %d, max %d)\n", nbytes, max_nbytes); dev_kfree_skb_any(skb); continue; @@ -1201,7 +1201,7 @@ static int ath10k_snoc_request_irq(struct ath10k *ar) irqflags, ce_name[id], ar); if (ret) { ath10k_err(ar, - "failed to register IRQ handler for CE %d: %d", + "failed to register IRQ handler for CE %d: %d\n", id, ret); goto err_irq; } @@ -1466,7 +1466,6 @@ MODULE_DEVICE_TABLE(of, ath10k_snoc_dt_match); static int ath10k_snoc_probe(struct platform_device *pdev) { const struct ath10k_snoc_drv_priv *drv_data; - const struct of_device_id *of_id; struct ath10k_snoc *ar_snoc; struct device *dev; struct ath10k *ar; @@ -1474,18 +1473,16 @@ static int ath10k_snoc_probe(struct platform_device *pdev) int ret; u32 i; - of_id = of_match_device(ath10k_snoc_dt_match, &pdev->dev); - if (!of_id) { - dev_err(&pdev->dev, "failed to find matching device tree id\n"); + dev = &pdev->dev; + drv_data = device_get_match_data(dev); + if (!drv_data) { + dev_err(dev, "failed to find matching device tree id\n"); return -EINVAL; } - drv_data = of_id->data; - dev = &pdev->dev; - ret = dma_set_mask_and_coherent(dev, drv_data->dma_mask); if (ret) { - dev_err(dev, "failed to set dma mask: %d", ret); + dev_err(dev, "failed to set dma mask: %d\n", ret); return ret; } @@ -1563,13 +1560,16 @@ static int ath10k_snoc_probe(struct platform_device *pdev) ret = ath10k_qmi_init(ar, msa_size); if (ret) { ath10k_warn(ar, "failed to register wlfw qmi client: %d\n", ret); - goto err_core_destroy; + goto err_power_off; } ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc probe\n"); return 0; +err_power_off: + ath10k_hw_power_off(ar); + err_free_irq: ath10k_snoc_free_irq(ar); diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index 1bffe3fbea3f..7a9b9bbcdbfc 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -65,7 +65,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI); if (ret) { ath10k_warn(ar, - "failed to to put testmode wmi event cmd attribute: %d\n", + "failed to put testmode wmi event cmd attribute: %d\n", ret); kfree_skb(nl_skb); goto out; @@ -74,7 +74,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id); if (ret) { ath10k_warn(ar, - "failed to to put testmode wmi even cmd_id: %d\n", + "failed to put testmode wmi event cmd_id: %d\n", ret); kfree_skb(nl_skb); goto out; diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 69a1ec53df29..4e68debda9bf 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -841,7 +841,7 @@ static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, const struct wmi_tlv_mgmt_rx_ev *ev; const u8 *frame; u32 msdu_len; - int ret; + int ret, i; tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); if (IS_ERR(tb)) { @@ -865,6 +865,9 @@ static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, arg->phy_mode = ev->phy_mode; arg->rate = ev->rate; + for (i = 0; i < ARRAY_SIZE(ev->rssi); i++) + arg->rssi[i] = ev->rssi[i]; + msdu_len = __le32_to_cpu(arg->buf_len); if (skb->len < (frame - skb->data) + msdu_len) { @@ -3707,6 +3710,7 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar, struct wmi_tlv *tlv; struct sk_buff *skb; __le32 *channel_list; + u16 tlv_len; size_t len; void *ptr; u32 i; @@ -3764,10 +3768,12 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar, /* nlo_configured_parameters(nlo_list) */ cmd->no_of_ssids = __cpu_to_le32(min_t(u8, pno->uc_networks_count, WMI_NLO_MAX_SSIDS)); + tlv_len = __le32_to_cpu(cmd->no_of_ssids) * + sizeof(struct nlo_configured_parameters); tlv = ptr; tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); - tlv->len = __cpu_to_le16(len); + tlv->len = __cpu_to_le16(tlv_len); ptr += sizeof(*tlv); nlo_list = ptr; diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 9f564e2b7a14..61885d4d662c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2463,10 +2463,10 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) u32 rx_status; u32 channel; u32 phy_mode; - u32 snr; + u32 snr, rssi; u32 rate; u16 fc; - int ret; + int ret, i; ret = ath10k_wmi_pull_mgmt_rx(ar, skb, &arg); if (ret) { @@ -2525,6 +2525,20 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->freq = ieee80211_channel_to_frequency(channel, status->band); status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; + + BUILD_BUG_ON(ARRAY_SIZE(status->chain_signal) != ARRAY_SIZE(arg.rssi)); + + for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { + status->chains &= ~BIT(i); + rssi = __le32_to_cpu(arg.rssi[i]); + ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt rssi[%d]:%d\n", i, arg.rssi[i]); + + if (rssi != ATH10K_INVALID_RSSI && rssi != 0) { + status->chain_signal[i] = ATH10K_DEFAULT_NOISE_FLOOR + rssi; + status->chains |= BIT(i); + } + } + status->rate_idx = ath10k_mac_bitrate_to_idx(sband, rate / 100); hdr = (struct ieee80211_hdr *)skb->data; @@ -9476,7 +9490,7 @@ static int ath10k_wmi_mgmt_tx_clean_up_pending(int msdu_id, void *ptr, msdu = pkt_addr->vaddr; dma_unmap_single(ar->dev, pkt_addr->paddr, - msdu->len, DMA_FROM_DEVICE); + msdu->len, DMA_TO_DEVICE); ieee80211_free_txskb(ar->hw, msdu); return 0; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 74adce1dd3a9..972d53d77654 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6786,6 +6786,7 @@ struct wmi_peer_delete_resp_ev_arg { struct wmi_mac_addr peer_addr; }; +#define WMI_MGMT_RX_NUM_RSSI 4 struct wmi_mgmt_rx_ev_arg { __le32 channel; __le32 snr; @@ -6794,6 +6795,7 @@ struct wmi_mgmt_rx_ev_arg { __le32 buf_len; __le32 status; /* %WMI_RX_STATUS_ */ struct wmi_mgmt_rx_ext_info ext_info; + __le32 rssi[WMI_MGMT_RX_NUM_RSSI]; }; struct wmi_ch_info_ev_arg { diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig new file mode 100644 index 000000000000..c88e16d4022b --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/Kconfig @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config ATH11K + tristate "Qualcomm Technologies 802.11ax chipset support" + depends on MAC80211 && HAS_DMA + depends on REMOTEPROC + depends on ARCH_QCOM || COMPILE_TEST + select ATH_COMMON + select QCOM_QMI_HELPERS + ---help--- + This module adds support for Qualcomm Technologies 802.11ax family of + chipsets. + + If you choose to build a module, it'll be called ath11k. + +config ATH11K_DEBUG + bool "QCA ath11k debugging" + depends on ATH11K + ---help--- + Enables debug support + + If unsure, say Y to make it easier to debug problems. + +config ATH11K_DEBUGFS + bool "QCA ath11k debugfs support" + depends on ATH11K && DEBUG_FS && MAC80211_DEBUGFS + ---help--- + Enable ath11k debugfs support + + If unsure, say Y to make it easier to debug problems. + +config ATH11K_TRACING + bool "ath11k tracing support" + depends on ATH11K && EVENT_TRACING + ---help--- + Select this to use ath11k tracing infrastructure. diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile new file mode 100644 index 000000000000..2761d07d938e --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/Makefile @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +obj-$(CONFIG_ATH11K) += ath11k.o +ath11k-y += core.o \ + hal.o \ + hal_tx.o \ + hal_rx.o \ + ahb.o \ + wmi.o \ + mac.o \ + reg.o \ + htc.o \ + qmi.o \ + dp.o \ + dp_tx.o \ + dp_rx.o \ + debug.o \ + ce.o \ + peer.o + +ath11k-$(CONFIG_ATH11K_DEBUGFS) += debug_htt_stats.o debugfs_sta.o +ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o +ath11k-$(CONFIG_ATH11K_TRACING) += trace.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 new file mode 100644 index 000000000000..e7e3e64c07aa --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -0,0 +1,1003 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/dma-mapping.h> +#include "ahb.h" +#include "debug.h" +#include <linux/remoteproc.h> + +static const struct of_device_id ath11k_ahb_of_match[] = { + /* TODO: Should we change the compatible string to something similar + * to one that ath10k uses? + */ + { .compatible = "qcom,ipq8074-wifi", + .data = (void *)ATH11K_HW_IPQ8074, + }, + { } +}; + +MODULE_DEVICE_TABLE(of, ath11k_ahb_of_match); + +/* Target firmware's Copy Engine configuration. */ +static const struct ce_pipe_config target_ce_config_wlan[] = { + /* CE0: host->target HTC control and raw streams */ + { + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE1: target->host HTT + HTC control */ + { + .pipenum = __cpu_to_le32(1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE2: target->host WMI */ + { + .pipenum = __cpu_to_le32(2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE3: host->target WMI */ + { + .pipenum = __cpu_to_le32(3), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE4: host->target HTT */ + { + .pipenum = __cpu_to_le32(4), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(256), + .nbytes_max = __cpu_to_le32(256), + .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = __cpu_to_le32(0), + }, + + /* CE5: target->host Pktlog */ + { + .pipenum = __cpu_to_le32(5), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(0), + .reserved = __cpu_to_le32(0), + }, + + /* CE6: Reserved for target autonomous hif_memcpy */ + { + .pipenum = __cpu_to_le32(6), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(65535), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE7 used only by Host */ + { + .pipenum = __cpu_to_le32(7), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE8 target->host used only by IPA */ + { + .pipenum = __cpu_to_le32(8), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(65535), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE9 host->target HTT */ + { + .pipenum = __cpu_to_le32(9), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE10 target->host HTT */ + { + .pipenum = __cpu_to_le32(10), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT_H2H), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE11 Not used */ + { + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(0), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, +}; + +/* Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +static const struct service_to_pipe target_service_to_ce_map_wlan[] = { + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(7), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(9), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(0), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(1), + }, + { /* not used */ + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(0), + }, + { /* not used */ + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(1), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(4), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(1), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_PKT_LOG), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(5), + }, + + /* (Additions here) */ + + { /* terminator entry */ } +}; + +#define ATH11K_IRQ_CE0_OFFSET 4 + +static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { + "misc-pulse1", + "misc-latch", + "sw-exception", + "watchdog", + "ce0", + "ce1", + "ce2", + "ce3", + "ce4", + "ce5", + "ce6", + "ce7", + "ce8", + "ce9", + "ce10", + "ce11", + "host2wbm-desc-feed", + "host2reo-re-injection", + "host2reo-command", + "host2rxdma-monitor-ring3", + "host2rxdma-monitor-ring2", + "host2rxdma-monitor-ring1", + "reo2ost-exception", + "wbm2host-rx-release", + "reo2host-status", + "reo2host-destination-ring4", + "reo2host-destination-ring3", + "reo2host-destination-ring2", + "reo2host-destination-ring1", + "rxdma2host-monitor-destination-mac3", + "rxdma2host-monitor-destination-mac2", + "rxdma2host-monitor-destination-mac1", + "ppdu-end-interrupts-mac3", + "ppdu-end-interrupts-mac2", + "ppdu-end-interrupts-mac1", + "rxdma2host-monitor-status-ring-mac3", + "rxdma2host-monitor-status-ring-mac2", + "rxdma2host-monitor-status-ring-mac1", + "host2rxdma-host-buf-ring-mac3", + "host2rxdma-host-buf-ring-mac2", + "host2rxdma-host-buf-ring-mac1", + "rxdma2host-destination-ring-mac3", + "rxdma2host-destination-ring-mac2", + "rxdma2host-destination-ring-mac1", + "host2tcl-input-ring4", + "host2tcl-input-ring3", + "host2tcl-input-ring2", + "host2tcl-input-ring1", + "wbm2host-tx-completions-ring3", + "wbm2host-tx-completions-ring2", + "wbm2host-tx-completions-ring1", + "tcl2host-status-ring", +}; + +#define ATH11K_TX_RING_MASK_0 0x1 +#define ATH11K_TX_RING_MASK_1 0x2 +#define ATH11K_TX_RING_MASK_2 0x4 + +#define ATH11K_RX_RING_MASK_0 0x1 +#define ATH11K_RX_RING_MASK_1 0x2 +#define ATH11K_RX_RING_MASK_2 0x4 +#define ATH11K_RX_RING_MASK_3 0x8 + +#define ATH11K_RX_ERR_RING_MASK_0 0x1 + +#define ATH11K_RX_WBM_REL_RING_MASK_0 0x1 + +#define ATH11K_REO_STATUS_RING_MASK_0 0x1 + +#define ATH11K_RXDMA2HOST_RING_MASK_0 0x1 +#define ATH11K_RXDMA2HOST_RING_MASK_1 0x2 +#define ATH11K_RXDMA2HOST_RING_MASK_2 0x4 + +#define ATH11K_HOST2RXDMA_RING_MASK_0 0x1 +#define ATH11K_HOST2RXDMA_RING_MASK_1 0x2 +#define ATH11K_HOST2RXDMA_RING_MASK_2 0x4 + +#define ATH11K_RX_MON_STATUS_RING_MASK_0 0x1 +#define ATH11K_RX_MON_STATUS_RING_MASK_1 0x2 +#define ATH11K_RX_MON_STATUS_RING_MASK_2 0x4 + +const u8 ath11k_tx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_TX_RING_MASK_0, + ATH11K_TX_RING_MASK_1, + ATH11K_TX_RING_MASK_2, +}; + +const u8 rx_mon_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + 0, 0, 0, 0, + ATH11K_RX_MON_STATUS_RING_MASK_0, + ATH11K_RX_MON_STATUS_RING_MASK_1, + ATH11K_RX_MON_STATUS_RING_MASK_2, +}; + +const u8 ath11k_rx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + 0, 0, 0, 0, 0, 0, 0, + ATH11K_RX_RING_MASK_0, + ATH11K_RX_RING_MASK_1, + ATH11K_RX_RING_MASK_2, + ATH11K_RX_RING_MASK_3, +}; + +const u8 ath11k_rx_err_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_RX_ERR_RING_MASK_0, +}; + +const u8 ath11k_rx_wbm_rel_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_RX_WBM_REL_RING_MASK_0, +}; + +const u8 ath11k_reo_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_REO_STATUS_RING_MASK_0, +}; + +const u8 ath11k_rxdma2host_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_RXDMA2HOST_RING_MASK_0, + ATH11K_RXDMA2HOST_RING_MASK_1, + ATH11K_RXDMA2HOST_RING_MASK_2, +}; + +const u8 ath11k_host2rxdma_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_HOST2RXDMA_RING_MASK_0, + ATH11K_HOST2RXDMA_RING_MASK_1, + ATH11K_HOST2RXDMA_RING_MASK_2, +}; + +/* enum ext_irq_num - irq numbers that can be used by external modules + * like datapath + */ +enum ext_irq_num { + host2wbm_desc_feed = 16, + host2reo_re_injection, + host2reo_command, + host2rxdma_monitor_ring3, + host2rxdma_monitor_ring2, + host2rxdma_monitor_ring1, + reo2host_exception, + wbm2host_rx_release, + reo2host_status, + reo2host_destination_ring4, + reo2host_destination_ring3, + reo2host_destination_ring2, + reo2host_destination_ring1, + rxdma2host_monitor_destination_mac3, + rxdma2host_monitor_destination_mac2, + rxdma2host_monitor_destination_mac1, + ppdu_end_interrupts_mac3, + ppdu_end_interrupts_mac2, + ppdu_end_interrupts_mac1, + rxdma2host_monitor_status_ring_mac3, + rxdma2host_monitor_status_ring_mac2, + rxdma2host_monitor_status_ring_mac1, + host2rxdma_host_buf_ring_mac3, + host2rxdma_host_buf_ring_mac2, + host2rxdma_host_buf_ring_mac1, + rxdma2host_destination_ring_mac3, + rxdma2host_destination_ring_mac2, + rxdma2host_destination_ring_mac1, + host2tcl_input_ring4, + host2tcl_input_ring3, + host2tcl_input_ring2, + host2tcl_input_ring1, + wbm2host_tx_completions_ring3, + wbm2host_tx_completions_ring2, + wbm2host_tx_completions_ring1, + tcl2host_status_ring, +}; + +static void ath11k_ahb_kill_tasklets(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) { + struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + tasklet_kill(&ce_pipe->intr_tq); + } +} + +static void ath11k_ahb_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +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++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + ath11k_ahb_ext_grp_disable(irq_grp); + + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + + while ((skb = __skb_dequeue(&irq_grp->pending_q))) + dev_kfree_skb_any(skb); + } +} + +static void ath11k_ahb_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void ath11k_ahb_setbit32(struct ath11k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath11k_ahb_read32(ab, offset); + ath11k_ahb_write32(ab, offset, val | BIT(bit)); +} + +static void ath11k_ahb_clearbit32(struct ath11k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath11k_ahb_read32(ab, offset); + ath11k_ahb_write32(ab, offset, val & ~BIT(bit)); +} + +static void ath11k_ahb_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) +{ + const struct ce_pipe_config *ce_config; + + ce_config = &target_ce_config_wlan[ce_id]; + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_OUT) + ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_ADDRESS); + + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_IN) { + ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); + ath11k_ahb_setbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + CE_HOST_IE_3_ADDRESS); + } +} + +static void ath11k_ahb_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) +{ + const struct ce_pipe_config *ce_config; + + ce_config = &target_ce_config_wlan[ce_id]; + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_OUT) + ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_ADDRESS); + + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_IN) { + ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); + ath11k_ahb_clearbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + CE_HOST_IE_3_ADDRESS); + } +} + +static void ath11k_ahb_sync_ce_irqs(struct ath11k_base *ab) +{ + int i; + int irq_idx; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH11K_IRQ_CE0_OFFSET + i; + synchronize_irq(ab->irq_num[irq_idx]); + } +} + +static void ath11k_ahb_sync_ext_irqs(struct ath11k_base *ab) +{ + int i, j; + int irq_idx; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) { + irq_idx = irq_grp->irqs[j]; + synchronize_irq(ab->irq_num[irq_idx]); + } + } +} + +static void ath11k_ahb_ce_irqs_enable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + ath11k_ahb_ce_irq_enable(ab, i); + } +} + +static void ath11k_ahb_ce_irqs_disable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + ath11k_ahb_ce_irq_disable(ab, i); + } +} + +int ath11k_ahb_start(struct ath11k_base *ab) +{ + ath11k_ahb_ce_irqs_enable(ab); + ath11k_ce_rx_post_buf(ab); + + return 0; +} + +void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + napi_enable(&irq_grp->napi); + ath11k_ahb_ext_grp_enable(irq_grp); + } +} + +void ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) +{ + __ath11k_ahb_ext_irq_disable(ab); + ath11k_ahb_sync_ext_irqs(ab); +} + +void ath11k_ahb_stop(struct ath11k_base *ab) +{ + if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + ath11k_ahb_ce_irqs_disable(ab); + ath11k_ahb_sync_ce_irqs(ab); + ath11k_ahb_kill_tasklets(ab); + del_timer_sync(&ab->rx_replenish_retry); + ath11k_ce_cleanup_pipes(ab); +} + +int ath11k_ahb_power_up(struct ath11k_base *ab) +{ + int ret; + + ret = rproc_boot(ab->tgt_rproc); + if (ret) + ath11k_err(ab, "failed to boot the remote processor Q6\n"); + + return ret; +} + +void ath11k_ahb_power_down(struct ath11k_base *ab) +{ + rproc_shutdown(ab->tgt_rproc); +} + +static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab) +{ + struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; + + cfg->tgt_ce_len = ARRAY_SIZE(target_ce_config_wlan) - 1; + cfg->tgt_ce = target_ce_config_wlan; + cfg->svc_to_ce_map_len = ARRAY_SIZE(target_service_to_ce_map_wlan); + cfg->svc_to_ce_map = target_service_to_ce_map_wlan; +} + +static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) +{ + int i, j; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) + free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); + } +} + +static void ath11k_ahb_free_irq(struct ath11k_base *ab) +{ + int irq_idx; + int i; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + irq_idx = ATH11K_IRQ_CE0_OFFSET + i; + free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); + } + + ath11k_ahb_free_ext_irq(ab); +} + +static void ath11k_ahb_ce_tasklet(unsigned long data) +{ + struct ath11k_ce_pipe *ce_pipe = (struct ath11k_ce_pipe *)data; + + ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); + + ath11k_ahb_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num); +} + +static irqreturn_t ath11k_ahb_ce_interrupt_handler(int irq, void *arg) +{ + struct ath11k_ce_pipe *ce_pipe = arg; + + ath11k_ahb_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num); + + tasklet_schedule(&ce_pipe->intr_tq); + + return IRQ_HANDLED; +} + +static int ath11k_ahb_ext_grp_napi_poll(struct napi_struct *napi, int budget) +{ + struct ath11k_ext_irq_grp *irq_grp = container_of(napi, + struct ath11k_ext_irq_grp, + napi); + struct ath11k_base *ab = irq_grp->ab; + int work_done; + + work_done = ath11k_dp_service_srng(ab, irq_grp, budget); + if (work_done < budget) { + napi_complete_done(napi, work_done); + ath11k_ahb_ext_grp_enable(irq_grp); + } + + if (work_done > budget) + work_done = budget; + + return work_done; +} + +static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg) +{ + struct ath11k_ext_irq_grp *irq_grp = arg; + + ath11k_ahb_ext_grp_disable(irq_grp); + + napi_schedule(&irq_grp->napi); + + return IRQ_HANDLED; +} + +static int ath11k_ahb_ext_irq_config(struct ath11k_base *ab) +{ + int i, j; + int irq; + int ret; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + u32 num_irq = 0; + + irq_grp->ab = ab; + irq_grp->grp_id = i; + 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)) { + irq_grp->irqs[num_irq++] = + wbm2host_tx_completions_ring1 - j; + } + + if (ath11k_rx_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + reo2host_destination_ring1 - j; + } + + if (ath11k_rx_err_ring_mask[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_exception; + + if (ath11k_rx_wbm_rel_ring_mask[i] & BIT(j)) + irq_grp->irqs[num_irq++] = wbm2host_rx_release; + + if (ath11k_reo_status_ring_mask[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_status; + + if (j < MAX_RADIOS) { + if (ath11k_rxdma2host_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + rxdma2host_destination_ring_mac1 + - ath11k_core_get_hw_mac_id(ab, j); + } + + if (ath11k_host2rxdma_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + host2rxdma_host_buf_ring_mac1 + - ath11k_core_get_hw_mac_id(ab, j); + } + + if (rx_mon_status_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + ppdu_end_interrupts_mac1 - + ath11k_core_get_hw_mac_id(ab, j); + irq_grp->irqs[num_irq++] = + rxdma2host_monitor_status_ring_mac1 - + ath11k_core_get_hw_mac_id(ab, j); + } + } + } + irq_grp->num_irq = num_irq; + + for (j = 0; j < irq_grp->num_irq; j++) { + int irq_idx = irq_grp->irqs[j]; + + irq = platform_get_irq_byname(ab->pdev, + irq_name[irq_idx]); + ab->irq_num[irq_idx] = irq; + irq_set_status_flags(irq, IRQ_NOAUTOEN); + ret = request_irq(irq, ath11k_ahb_ext_interrupt_handler, + IRQF_TRIGGER_RISING, + irq_name[irq_idx], irq_grp); + if (ret) { + ath11k_err(ab, "failed request_irq for %d\n", + irq); + } + } + } + + return 0; +} + +static int ath11k_ahb_config_irq(struct ath11k_base *ab) +{ + int irq, irq_idx, i; + int ret; + + /* Configure CE irqs */ + for (i = 0; i < CE_COUNT; i++) { + struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH11K_IRQ_CE0_OFFSET + i; + + tasklet_init(&ce_pipe->intr_tq, ath11k_ahb_ce_tasklet, + (unsigned long)ce_pipe); + irq = platform_get_irq_byname(ab->pdev, irq_name[irq_idx]); + ret = request_irq(irq, ath11k_ahb_ce_interrupt_handler, + IRQF_TRIGGER_RISING, irq_name[irq_idx], + ce_pipe); + if (ret) + return ret; + + ab->irq_num[irq_idx] = irq; + } + + /* Configure external interrupts */ + ret = ath11k_ahb_ext_irq_config(ab); + + return ret; +} + +int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe) +{ + const struct service_to_pipe *entry; + bool ul_set = false, dl_set = false; + int i; + + for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) { + entry = &target_service_to_ce_map_wlan[i]; + + if (__le32_to_cpu(entry->service_id) != service_id) + continue; + + switch (__le32_to_cpu(entry->pipedir)) { + case PIPEDIR_NONE: + break; + case PIPEDIR_IN: + WARN_ON(dl_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + break; + case PIPEDIR_OUT: + WARN_ON(ul_set); + *ul_pipe = __le32_to_cpu(entry->pipenum); + ul_set = true; + break; + case PIPEDIR_INOUT: + WARN_ON(dl_set); + WARN_ON(ul_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + *ul_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + ul_set = true; + break; + } + } + + if (WARN_ON(!ul_set || !dl_set)) + return -ENOENT; + + return 0; +} + +static int ath11k_ahb_probe(struct platform_device *pdev) +{ + struct ath11k_base *ab; + const struct of_device_id *of_id; + struct resource *mem_res; + void __iomem *mem; + int ret; + + of_id = of_match_device(ath11k_ahb_of_match, &pdev->dev); + if (!of_id) { + dev_err(&pdev->dev, "failed to find matching device tree id\n"); + return -EINVAL; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + dev_err(&pdev->dev, "failed to get IO memory resource\n"); + return -ENXIO; + } + + mem = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(mem)) { + dev_err(&pdev->dev, "ioremap error\n"); + return PTR_ERR(mem); + } + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "failed to set 32-bit consistent dma\n"); + return ret; + } + + ab = ath11k_core_alloc(&pdev->dev); + if (!ab) { + dev_err(&pdev->dev, "failed to allocate ath11k base\n"); + return -ENOMEM; + } + + ab->pdev = pdev; + ab->hw_rev = (enum ath11k_hw_rev)of_id->data; + ab->mem = mem; + ab->mem_len = resource_size(mem_res); + platform_set_drvdata(pdev, ab); + + ret = ath11k_hal_srng_init(ab); + if (ret) + goto err_core_free; + + ret = ath11k_ce_alloc_pipes(ab); + if (ret) { + ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret); + goto err_hal_srng_deinit; + } + + ath11k_ahb_init_qmi_ce_config(ab); + + ret = ath11k_ahb_config_irq(ab); + if (ret) { + ath11k_err(ab, "failed to configure irq: %d\n", ret); + goto err_ce_free; + } + + ret = ath11k_core_init(ab); + if (ret) { + ath11k_err(ab, "failed to init core: %d\n", ret); + goto err_ce_free; + } + + return 0; + +err_ce_free: + ath11k_ce_free_pipes(ab); + +err_hal_srng_deinit: + ath11k_hal_srng_deinit(ab); + +err_core_free: + ath11k_core_free(ab); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int ath11k_ahb_remove(struct platform_device *pdev) +{ + struct ath11k_base *ab = platform_get_drvdata(pdev); + + reinit_completion(&ab->driver_recovery); + + if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags)) + wait_for_completion_timeout(&ab->driver_recovery, + ATH11K_AHB_RECOVERY_TIMEOUT); + + set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + cancel_work_sync(&ab->restart_work); + + ath11k_core_deinit(ab); + ath11k_ahb_free_irq(ab); + + ath11k_hal_srng_deinit(ab); + ath11k_ce_free_pipes(ab); + ath11k_core_free(ab); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ath11k_ahb_driver = { + .driver = { + .name = "ath11k", + .of_match_table = ath11k_ahb_of_match, + }, + .probe = ath11k_ahb_probe, + .remove = ath11k_ahb_remove, +}; + +int ath11k_ahb_init(void) +{ + return platform_driver_register(&ath11k_ahb_driver); +} + +void ath11k_ahb_exit(void) +{ + platform_driver_unregister(&ath11k_ahb_driver); +} diff --git a/drivers/net/wireless/ath/ath11k/ahb.h b/drivers/net/wireless/ath/ath11k/ahb.h new file mode 100644 index 000000000000..93f46dfe22df --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ahb.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#ifndef ATH11K_AHB_H +#define ATH11K_AHB_H + +#include "core.h" + +#define ATH11K_AHB_RECOVERY_TIMEOUT (3 * HZ) +struct ath11k_base; + +static inline u32 ath11k_ahb_read32(struct ath11k_base *ab, u32 offset) +{ + return ioread32(ab->mem + offset); +} + +static inline void ath11k_ahb_write32(struct ath11k_base *ab, u32 offset, u32 value) +{ + iowrite32(value, ab->mem + offset); +} + +void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab); +void ath11k_ahb_ext_irq_disable(struct ath11k_base *ab); +int ath11k_ahb_start(struct ath11k_base *ab); +void ath11k_ahb_stop(struct ath11k_base *ab); +int ath11k_ahb_power_up(struct ath11k_base *ab); +void ath11k_ahb_power_down(struct ath11k_base *ab); +int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe); + +int ath11k_ahb_init(void); +void ath11k_ahb_exit(void); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c new file mode 100644 index 000000000000..cdd40c8fc867 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -0,0 +1,808 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "dp_rx.h" +#include "debug.h" + +static const struct ce_attr host_ce_config_wlan[] = { + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE3: host->target WMI (mac0) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 2048, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE5: target->host pktlog */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_dp_htt_htc_t2h_msg_handler, + }, + + /* CE6: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE7: host->target WMI (mac1) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE8: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE9: host->target WMI (mac2) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE10: target->host HTT */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE11: Not used */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, +}; + +static int ath11k_ce_rx_buf_enqueue_pipe(struct ath11k_ce_pipe *pipe, + struct sk_buff *skb, dma_addr_t paddr) +{ + struct ath11k_base *ab = pipe->ab; + struct ath11k_ce_ring *ring = pipe->dest_ring; + struct hal_srng *srng; + unsigned int write_index; + unsigned int nentries_mask = ring->nentries_mask; + u32 *desc; + int ret; + + lockdep_assert_held(&ab->ce.ce_lock); + + write_index = ring->write_index; + + srng = &ab->hal.srng_list[ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + if (unlikely(ath11k_hal_srng_src_num_free(ab, srng, false) < 1)) { + ret = -ENOSPC; + goto exit; + } + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) { + ret = -ENOSPC; + goto exit; + } + + ath11k_hal_ce_dst_set_desc(desc, paddr); + + ring->skb[write_index] = skb; + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + ring->write_index = write_index; + + pipe->rx_buf_needed--; + + ret = 0; +exit: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return ret; +} + +static int ath11k_ce_rx_post_pipe(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; + dma_addr_t paddr; + int ret = 0; + + if (!(pipe->dest_ring || pipe->status_ring)) + return 0; + + spin_lock_bh(&ab->ce.ce_lock); + while (pipe->rx_buf_needed) { + skb = dev_alloc_skb(pipe->buf_sz); + if (!skb) { + ret = -ENOMEM; + goto exit; + } + + WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4)); + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(ab->dev, paddr))) { + ath11k_warn(ab, "failed to dma map ce rx buf\n"); + dev_kfree_skb_any(skb); + ret = -EIO; + goto exit; + } + + ATH11K_SKB_RXCB(skb)->paddr = paddr; + + ret = ath11k_ce_rx_buf_enqueue_pipe(pipe, skb, paddr); + + if (ret) { + ath11k_warn(ab, "failed to enqueue rx buf: %d\n", ret); + dma_unmap_single(ab->dev, paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + goto exit; + } + } + +exit: + spin_unlock_bh(&ab->ce.ce_lock); + return ret; +} + +static int ath11k_ce_completed_recv_next(struct ath11k_ce_pipe *pipe, + struct sk_buff **skb, int *nbytes) +{ + struct ath11k_base *ab = pipe->ab; + struct hal_srng *srng; + unsigned int sw_index; + unsigned int nentries_mask; + u32 *desc; + int ret = 0; + + spin_lock_bh(&ab->ce.ce_lock); + + sw_index = pipe->dest_ring->sw_index; + nentries_mask = pipe->dest_ring->nentries_mask; + + srng = &ab->hal.srng_list[pipe->status_ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + desc = ath11k_hal_srng_dst_get_next_entry(ab, srng); + if (!desc) { + ret = -EIO; + goto err; + } + + *nbytes = ath11k_hal_ce_dst_status_get_length(desc); + if (*nbytes == 0) { + ret = -EIO; + goto err; + } + + *skb = pipe->dest_ring->skb[sw_index]; + pipe->dest_ring->skb[sw_index] = NULL; + + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + pipe->dest_ring->sw_index = sw_index; + + pipe->rx_buf_needed++; +err: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return ret; +} + +static void ath11k_ce_recv_process_cb(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; + struct sk_buff_head list; + unsigned int nbytes, max_nbytes; + int ret; + + __skb_queue_head_init(&list); + while (ath11k_ce_completed_recv_next(pipe, &skb, &nbytes) == 0) { + max_nbytes = skb->len + skb_tailroom(skb); + dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + max_nbytes, DMA_FROM_DEVICE); + + if (unlikely(max_nbytes < nbytes)) { + ath11k_warn(ab, "rxed more than expected (nbytes %d, max %d)", + nbytes, max_nbytes); + dev_kfree_skb_any(skb); + continue; + } + + skb_put(skb, nbytes); + __skb_queue_tail(&list, skb); + } + + while ((skb = __skb_dequeue(&list))) { + ath11k_dbg(ab, ATH11K_DBG_AHB, "rx ce pipe %d len %d\n", + pipe->pipe_num, skb->len); + pipe->recv_cb(ab, skb); + } + + ret = ath11k_ce_rx_post_pipe(pipe); + if (ret && ret != -ENOSPC) { + ath11k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n", + pipe->pipe_num, ret); + mod_timer(&ab->rx_replenish_retry, + jiffies + ATH11K_CE_RX_POST_RETRY_JIFFIES); + } +} + +static struct sk_buff *ath11k_ce_completed_send_next(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct hal_srng *srng; + unsigned int sw_index; + unsigned int nentries_mask; + struct sk_buff *skb; + u32 *desc; + + spin_lock_bh(&ab->ce.ce_lock); + + sw_index = pipe->src_ring->sw_index; + nentries_mask = pipe->src_ring->nentries_mask; + + srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + desc = ath11k_hal_srng_src_reap_next(ab, srng); + if (!desc) { + skb = ERR_PTR(-EIO); + goto err_unlock; + } + + skb = pipe->src_ring->skb[sw_index]; + + pipe->src_ring->skb[sw_index] = NULL; + + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + pipe->src_ring->sw_index = sw_index; + +err_unlock: + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return skb; +} + +static void ath11k_ce_send_done_cb(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; + + while (!IS_ERR(skb = ath11k_ce_completed_send_next(pipe))) { + if (!skb) + continue; + + dma_unmap_single(ab->dev, ATH11K_SKB_CB(skb)->paddr, skb->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + } +} + +static int ath11k_ce_init_ring(struct ath11k_base *ab, + struct ath11k_ce_ring *ce_ring, + int ce_id, enum hal_ring_type type) +{ + struct hal_srng_params params = { 0 }; + int ret; + + params.ring_base_paddr = ce_ring->base_addr_ce_space; + params.ring_base_vaddr = ce_ring->base_addr_owner_space; + params.num_entries = ce_ring->nentries; + + switch (type) { + case HAL_CE_SRC: + if (!(CE_ATTR_DIS_INTR & host_ce_config_wlan[ce_id].flags)) + params.intr_batch_cntr_thres_entries = 1; + break; + case HAL_CE_DST: + params.max_buffer_len = host_ce_config_wlan[ce_id].src_sz_max; + if (!(host_ce_config_wlan[ce_id].flags & CE_ATTR_DIS_INTR)) { + params.intr_timer_thres_us = 1024; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.low_threshold = ce_ring->nentries - 3; + } + break; + case HAL_CE_DST_STATUS: + if (!(host_ce_config_wlan[ce_id].flags & CE_ATTR_DIS_INTR)) { + params.intr_batch_cntr_thres_entries = 1; + params.intr_timer_thres_us = 0x1000; + } + break; + default: + ath11k_warn(ab, "Invalid CE ring type %d\n", type); + return -EINVAL; + } + + /* TODO: Init other params needed by HAL to init the ring */ + + ret = ath11k_hal_srng_setup(ab, type, ce_id, 0, ¶ms); + if (ret < 0) { + ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n", + ret, ce_id); + return ret; + } + ce_ring->hal_ring_id = ret; + + return 0; +} + +static struct ath11k_ce_ring * +ath11k_ce_alloc_ring(struct ath11k_base *ab, int nentries, int desc_sz) +{ + struct ath11k_ce_ring *ce_ring; + dma_addr_t base_addr; + + ce_ring = kzalloc(struct_size(ce_ring, skb, nentries), GFP_KERNEL); + if (ce_ring == NULL) + return ERR_PTR(-ENOMEM); + + ce_ring->nentries = nentries; + ce_ring->nentries_mask = nentries - 1; + + /* Legacy platforms that do not support cache + * coherent DMA are unsupported + */ + ce_ring->base_addr_owner_space_unaligned = + dma_alloc_coherent(ab->dev, + nentries * desc_sz + CE_DESC_RING_ALIGN, + &base_addr, GFP_KERNEL); + if (!ce_ring->base_addr_owner_space_unaligned) { + kfree(ce_ring); + return ERR_PTR(-ENOMEM); + } + + ce_ring->base_addr_ce_space_unaligned = base_addr; + + ce_ring->base_addr_owner_space = PTR_ALIGN( + ce_ring->base_addr_owner_space_unaligned, + CE_DESC_RING_ALIGN); + ce_ring->base_addr_ce_space = ALIGN( + ce_ring->base_addr_ce_space_unaligned, + CE_DESC_RING_ALIGN); + + return ce_ring; +} + +static int ath11k_ce_alloc_pipe(struct ath11k_base *ab, int ce_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id]; + const struct ce_attr *attr = &host_ce_config_wlan[ce_id]; + struct ath11k_ce_ring *ring; + int nentries; + int desc_sz; + + pipe->attr_flags = attr->flags; + + if (attr->src_nentries) { + pipe->send_cb = ath11k_ce_send_done_cb; + nentries = roundup_pow_of_two(attr->src_nentries); + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_SRC); + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (IS_ERR(ring)) + return PTR_ERR(ring); + pipe->src_ring = ring; + } + + if (attr->dest_nentries) { + pipe->recv_cb = attr->recv_cb; + nentries = roundup_pow_of_two(attr->dest_nentries); + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST); + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (IS_ERR(ring)) + return PTR_ERR(ring); + pipe->dest_ring = ring; + + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS); + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (IS_ERR(ring)) + return PTR_ERR(ring); + pipe->status_ring = ring; + } + + return 0; +} + +void ath11k_ce_per_engine_service(struct ath11k_base *ab, u16 ce_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id]; + + if (pipe->send_cb) + pipe->send_cb(pipe); + + if (pipe->recv_cb) + ath11k_ce_recv_process_cb(pipe); +} + +void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id]; + + if ((pipe->attr_flags & CE_ATTR_DIS_INTR) && pipe->send_cb) + pipe->send_cb(pipe); +} + +int ath11k_ce_send(struct ath11k_base *ab, struct sk_buff *skb, u8 pipe_id, + u16 transfer_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id]; + struct hal_srng *srng; + u32 *desc; + unsigned int write_index, sw_index; + unsigned int nentries_mask; + int ret = 0; + u8 byte_swap_data = 0; + int num_used; + + /* Check if some entries could be regained by handling tx completion if + * the CE has interrupts disabled and the used entries is more than the + * defined usage threshold. + */ + if (pipe->attr_flags & CE_ATTR_DIS_INTR) { + spin_lock_bh(&ab->ce.ce_lock); + write_index = pipe->src_ring->write_index; + + sw_index = pipe->src_ring->sw_index; + + if (write_index >= sw_index) + num_used = write_index - sw_index; + else + num_used = pipe->src_ring->nentries - sw_index + + write_index; + + spin_unlock_bh(&ab->ce.ce_lock); + + if (num_used > ATH11K_CE_USAGE_THRESHOLD) + ath11k_ce_poll_send_completed(ab, pipe->pipe_num); + } + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + return -ESHUTDOWN; + + spin_lock_bh(&ab->ce.ce_lock); + + write_index = pipe->src_ring->write_index; + nentries_mask = pipe->src_ring->nentries_mask; + + srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + if (unlikely(ath11k_hal_srng_src_num_free(ab, srng, false) < 1)) { + ath11k_hal_srng_access_end(ab, srng); + ret = -ENOBUFS; + goto err_unlock; + } + + desc = ath11k_hal_srng_src_get_next_reaped(ab, srng); + if (!desc) { + ath11k_hal_srng_access_end(ab, srng); + ret = -ENOBUFS; + goto err_unlock; + } + + if (pipe->attr_flags & CE_ATTR_BYTE_SWAP_DATA) + byte_swap_data = 1; + + ath11k_hal_ce_src_set_desc(desc, ATH11K_SKB_CB(skb)->paddr, + skb->len, transfer_id, byte_swap_data); + + pipe->src_ring->skb[write_index] = skb; + pipe->src_ring->write_index = CE_RING_IDX_INCR(nentries_mask, + write_index); + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return 0; + +err_unlock: + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return ret; +} + +static void ath11k_ce_rx_pipe_cleanup(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct ath11k_ce_ring *ring = pipe->dest_ring; + struct sk_buff *skb; + int i; + + if (!(ring && pipe->buf_sz)) + return; + + for (i = 0; i < ring->nentries; i++) { + skb = ring->skb[i]; + if (!skb) + continue; + + ring->skb[i] = NULL; + dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } +} + +void ath11k_ce_cleanup_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int pipe_num; + + for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { + pipe = &ab->ce.ce_pipe[pipe_num]; + ath11k_ce_rx_pipe_cleanup(pipe); + + /* Cleanup any src CE's which have interrupts disabled */ + ath11k_ce_poll_send_completed(ab, pipe_num); + + /* NOTE: Should we also clean up tx buffer in all pipes? */ + } +} + +void ath11k_ce_rx_post_buf(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int i; + int ret; + + for (i = 0; i < CE_COUNT; i++) { + pipe = &ab->ce.ce_pipe[i]; + ret = ath11k_ce_rx_post_pipe(pipe); + if (ret) { + if (ret == -ENOSPC) + continue; + + ath11k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n", + i, ret); + mod_timer(&ab->rx_replenish_retry, + jiffies + ATH11K_CE_RX_POST_RETRY_JIFFIES); + + return; + } + } +} + +void ath11k_ce_rx_replenish_retry(struct timer_list *t) +{ + struct ath11k_base *ab = from_timer(ab, t, rx_replenish_retry); + + ath11k_ce_rx_post_buf(ab); +} + +int ath11k_ce_init_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int i; + int ret; + + for (i = 0; i < CE_COUNT; i++) { + pipe = &ab->ce.ce_pipe[i]; + + if (pipe->src_ring) { + ret = ath11k_ce_init_ring(ab, pipe->src_ring, i, + HAL_CE_SRC); + if (ret) { + ath11k_warn(ab, "failed to init src ring: %d\n", + ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->src_ring->write_index = 0; + pipe->src_ring->sw_index = 0; + } + + if (pipe->dest_ring) { + ret = ath11k_ce_init_ring(ab, pipe->dest_ring, i, + HAL_CE_DST); + if (ret) { + ath11k_warn(ab, "failed to init dest ring: %d\n", + ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->rx_buf_needed = pipe->dest_ring->nentries ? + pipe->dest_ring->nentries - 2 : 0; + + pipe->dest_ring->write_index = 0; + pipe->dest_ring->sw_index = 0; + } + + if (pipe->status_ring) { + ret = ath11k_ce_init_ring(ab, pipe->status_ring, i, + HAL_CE_DST_STATUS); + if (ret) { + ath11k_warn(ab, "failed to init dest status ing: %d\n", + ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->status_ring->write_index = 0; + pipe->status_ring->sw_index = 0; + } + } + + return 0; +} + +void ath11k_ce_free_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int desc_sz; + int i; + + for (i = 0; i < CE_COUNT; i++) { + pipe = &ab->ce.ce_pipe[i]; + + if (pipe->src_ring) { + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_SRC); + dma_free_coherent(ab->dev, + pipe->src_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, + pipe->src_ring->base_addr_owner_space, + pipe->src_ring->base_addr_ce_space); + kfree(pipe->src_ring); + pipe->src_ring = NULL; + } + + if (pipe->dest_ring) { + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST); + dma_free_coherent(ab->dev, + pipe->dest_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, + pipe->dest_ring->base_addr_owner_space, + pipe->dest_ring->base_addr_ce_space); + kfree(pipe->dest_ring); + pipe->dest_ring = NULL; + } + + if (pipe->status_ring) { + desc_sz = + ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS); + dma_free_coherent(ab->dev, + pipe->status_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, + pipe->status_ring->base_addr_owner_space, + pipe->status_ring->base_addr_ce_space); + kfree(pipe->status_ring); + pipe->status_ring = NULL; + } + } +} + +int ath11k_ce_alloc_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int i; + int ret; + const struct ce_attr *attr; + + spin_lock_init(&ab->ce.ce_lock); + + for (i = 0; i < CE_COUNT; i++) { + attr = &host_ce_config_wlan[i]; + pipe = &ab->ce.ce_pipe[i]; + pipe->pipe_num = i; + pipe->ab = ab; + pipe->buf_sz = attr->src_sz_max; + + ret = ath11k_ce_alloc_pipe(ab, i); + if (ret) { + /* Free any parial successful allocation */ + ath11k_ce_free_pipes(ab); + return ret; + } + } + + return 0; +} + +/* For Big Endian Host, Copy Engine byte_swap is enabled + * When Copy Engine does byte_swap, need to byte swap again for the + * Host to get/put buffer content in the correct byte order + */ +void ath11k_ce_byte_swap(void *mem, u32 len) +{ + int i; + + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { + if (!mem) + return; + + for (i = 0; i < (len / 4); i++) { + *(u32 *)mem = swab32(*(u32 *)mem); + mem += 4; + } + } +} + +int ath11k_ce_get_attr_flags(int ce_id) +{ + if (ce_id >= CE_COUNT) + return -EINVAL; + + return host_ce_config_wlan[ce_id].flags; +} diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h new file mode 100644 index 000000000000..e355dfda48bf --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_CE_H +#define ATH11K_CE_H + +#define CE_COUNT 12 + +/* Byte swap data words */ +#define CE_ATTR_BYTE_SWAP_DATA 2 + +/* no interrupt on copy completion */ +#define CE_ATTR_DIS_INTR 8 + +/* Host software's Copy Engine configuration. */ +#ifdef __BIG_ENDIAN +#define CE_ATTR_FLAGS CE_ATTR_BYTE_SWAP_DATA +#else +#define CE_ATTR_FLAGS 0 +#endif + +/* Threshold to poll for tx completion in case of Interrupt disabled CE's */ +#define ATH11K_CE_USAGE_THRESHOLD 32 + +void ath11k_ce_byte_swap(void *mem, u32 len); + +/* + * Directions for interconnect pipe configuration. + * These definitions may be used during configuration and are shared + * between Host and Target. + * + * Pipe Directions are relative to the Host, so PIPEDIR_IN means + * "coming IN over air through Target to Host" as with a WiFi Rx operation. + * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air" + * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man" + * Target since things that are "PIPEDIR_OUT" are coming IN to the Target + * over the interconnect. + */ +#define PIPEDIR_NONE 0 +#define PIPEDIR_IN 1 /* Target-->Host, WiFi Rx direction */ +#define PIPEDIR_OUT 2 /* Host->Target, WiFi Tx direction */ +#define PIPEDIR_INOUT 3 /* bidirectional */ +#define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */ + +/* CE address/mask */ +#define CE_HOST_IE_ADDRESS 0x00A1803C +#define CE_HOST_IE_2_ADDRESS 0x00A18040 +#define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS + +#define CE_HOST_IE_3_SHIFT 0xC + +#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask)) + +#define ATH11K_CE_RX_POST_RETRY_JIFFIES 50 + +struct ath11k_base; + +/* + * Establish a mapping between a service/direction and a pipe. + * Configuration information for a Copy Engine pipe and services. + * Passed from Host to Target through QMI message and must be in + * little endian format. + */ +struct service_to_pipe { + __le32 service_id; + __le32 pipedir; + __le32 pipenum; +}; + +/* + * Configuration information for a Copy Engine pipe. + * Passed from Host to Target through QMI message during startup (one per CE). + * + * NOTE: Structure is shared between Host software and Target firmware! + */ +struct ce_pipe_config { + __le32 pipenum; + __le32 pipedir; + __le32 nentries; + __le32 nbytes_max; + __le32 flags; + __le32 reserved; +}; + +struct ce_attr { + /* CE_ATTR_* values */ + unsigned int flags; + + /* #entries in source ring - Must be a power of 2 */ + unsigned int src_nentries; + + /* + * Max source send size for this CE. + * This is also the minimum size of a destination buffer. + */ + unsigned int src_sz_max; + + /* #entries in destination ring - Must be a power of 2 */ + unsigned int dest_nentries; + + void (*recv_cb)(struct ath11k_base *, struct sk_buff *); +}; + +#define CE_DESC_RING_ALIGN 8 + +struct ath11k_ce_ring { + /* Number of entries in this ring; must be power of 2 */ + unsigned int nentries; + unsigned int nentries_mask; + + /* For dest ring, this is the next index to be processed + * by software after it was/is received into. + * + * For src ring, this is the last descriptor that was sent + * and completion processed by software. + * + * Regardless of src or dest ring, this is an invariant + * (modulo ring size): + * write index >= read index >= sw_index + */ + unsigned int sw_index; + /* cached copy */ + unsigned int write_index; + + /* Start of DMA-coherent area reserved for descriptors */ + /* Host address space */ + void *base_addr_owner_space_unaligned; + /* CE address space */ + u32 base_addr_ce_space_unaligned; + + /* Actual start of descriptors. + * Aligned to descriptor-size boundary. + * Points into reserved DMA-coherent area, above. + */ + /* Host address space */ + void *base_addr_owner_space; + + /* CE address space */ + u32 base_addr_ce_space; + + /* HAL ring id */ + u32 hal_ring_id; + + /* keep last */ + struct sk_buff *skb[0]; +}; + +struct ath11k_ce_pipe { + struct ath11k_base *ab; + u16 pipe_num; + unsigned int attr_flags; + unsigned int buf_sz; + unsigned int rx_buf_needed; + + void (*send_cb)(struct ath11k_ce_pipe *); + void (*recv_cb)(struct ath11k_base *, struct sk_buff *); + + struct tasklet_struct intr_tq; + struct ath11k_ce_ring *src_ring; + struct ath11k_ce_ring *dest_ring; + struct ath11k_ce_ring *status_ring; +}; + +struct ath11k_ce { + struct ath11k_ce_pipe ce_pipe[CE_COUNT]; + /* Protects rings of all ce pipes */ + spinlock_t ce_lock; +}; + +void ath11k_ce_cleanup_pipes(struct ath11k_base *ab); +void ath11k_ce_rx_replenish_retry(struct timer_list *t); +void ath11k_ce_per_engine_service(struct ath11k_base *ab, u16 ce_id); +int ath11k_ce_send(struct ath11k_base *ab, struct sk_buff *skb, u8 pipe_id, + u16 transfer_id); +void ath11k_ce_rx_post_buf(struct ath11k_base *ab); +int ath11k_ce_init_pipes(struct ath11k_base *ab); +int ath11k_ce_alloc_pipes(struct ath11k_base *ab); +void ath11k_ce_free_pipes(struct ath11k_base *ab); +int ath11k_ce_get_attr_flags(int ce_id); +void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id); +#endif diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c new file mode 100644 index 000000000000..9e823056e673 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -0,0 +1,795 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/remoteproc.h> +#include <linux/firmware.h> +#include "ahb.h" +#include "core.h" +#include "dp_tx.h" +#include "dp_rx.h" +#include "debug.h" + +unsigned int ath11k_debug_mask; +module_param_named(debug_mask, ath11k_debug_mask, uint, 0644); +MODULE_PARM_DESC(debug_mask, "Debugging mask"); + +static const struct ath11k_hw_params ath11k_hw_params = { + .name = "ipq8074", + .fw = { + .dir = IPQ8074_FW_DIR, + .board_size = IPQ8074_MAX_BOARD_DATA_SZ, + .cal_size = IPQ8074_MAX_CAL_DATA_SZ, + }, +}; + +/* Map from pdev index to hw mac index */ +u8 ath11k_core_get_hw_mac_id(struct ath11k_base *ab, int pdev_idx) +{ + switch (pdev_idx) { + case 0: + return 0; + case 1: + return 2; + case 2: + return 1; + default: + ath11k_warn(ab, "Invalid pdev idx %d\n", pdev_idx); + return ATH11K_INVALID_HW_MAC_ID; + } +} + +static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name, + size_t name_len) +{ + /* Note: bus is fixed to ahb. When other bus type supported, + * make it to dynamic. + */ + scnprintf(name, name_len, + "bus=ahb,qmi-chip-id=%d,qmi-board-id=%d", + ab->qmi.target.chip_id, + ab->qmi.target.board_id); + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot using board name '%s'\n", name); + + return 0; +} + +static const struct firmware *ath11k_fetch_fw_file(struct ath11k_base *ab, + const char *dir, + const char *file) +{ + char filename[100]; + const struct firmware *fw; + int ret; + + if (file == NULL) + return ERR_PTR(-ENOENT); + + if (dir == NULL) + dir = "."; + + snprintf(filename, sizeof(filename), "%s/%s", dir, file); + ret = firmware_request_nowarn(&fw, filename, ab->dev); + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot fw request '%s': %d\n", + filename, ret); + + if (ret) + return ERR_PTR(ret); + ath11k_warn(ab, "Downloading BDF: %s, size: %zu\n", + filename, fw->size); + + return fw; +} + +void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) +{ + if (!IS_ERR(bd->fw)) + release_firmware(bd->fw); + + memset(bd, 0, sizeof(*bd)); +} + +static int ath11k_core_parse_bd_ie_board(struct ath11k_base *ab, + struct ath11k_board_data *bd, + const void *buf, size_t buf_len, + const char *boardname, + int bd_ie_type) +{ + const struct ath11k_fw_ie *hdr; + bool name_match_found; + int ret, board_ie_id; + size_t board_ie_len; + const void *board_ie_data; + + name_match_found = false; + + /* go through ATH11K_BD_IE_BOARD_ elements */ + while (buf_len > sizeof(struct ath11k_fw_ie)) { + hdr = buf; + board_ie_id = le32_to_cpu(hdr->id); + board_ie_len = le32_to_cpu(hdr->len); + board_ie_data = hdr->data; + + buf_len -= sizeof(*hdr); + buf += sizeof(*hdr); + + if (buf_len < ALIGN(board_ie_len, 4)) { + ath11k_err(ab, "invalid ATH11K_BD_IE_BOARD length: %zu < %zu\n", + buf_len, ALIGN(board_ie_len, 4)); + ret = -EINVAL; + goto out; + } + + switch (board_ie_id) { + case ATH11K_BD_IE_BOARD_NAME: + ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "board name", "", + board_ie_data, board_ie_len); + + if (board_ie_len != strlen(boardname)) + break; + + ret = memcmp(board_ie_data, boardname, strlen(boardname)); + if (ret) + break; + + name_match_found = true; + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot found match for name '%s'", + boardname); + break; + case ATH11K_BD_IE_BOARD_DATA: + if (!name_match_found) + /* no match found */ + break; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot found board data for '%s'", boardname); + + bd->data = board_ie_data; + bd->len = board_ie_len; + + ret = 0; + goto out; + default: + ath11k_warn(ab, "unknown ATH11K_BD_IE_BOARD found: %d\n", + board_ie_id); + break; + } + + /* jump over the padding */ + board_ie_len = ALIGN(board_ie_len, 4); + + buf_len -= board_ie_len; + buf += board_ie_len; + } + + /* no match found */ + ret = -ENOENT; + +out: + return ret; +} + +static int ath11k_core_fetch_board_data_api_n(struct ath11k_base *ab, + struct ath11k_board_data *bd, + const char *boardname) +{ + size_t len, magic_len; + const u8 *data; + char *filename = ATH11K_BOARD_API2_FILE; + size_t ie_len; + struct ath11k_fw_ie *hdr; + int ret, ie_id; + + if (!bd->fw) + bd->fw = ath11k_fetch_fw_file(ab, + ab->hw_params.fw.dir, + filename); + if (IS_ERR(bd->fw)) + return PTR_ERR(bd->fw); + + data = bd->fw->data; + len = bd->fw->size; + + /* magic has extra null byte padded */ + magic_len = strlen(ATH11K_BOARD_MAGIC) + 1; + if (len < magic_len) { + ath11k_err(ab, "failed to find magic value in %s/%s, file too short: %zu\n", + ab->hw_params.fw.dir, filename, len); + ret = -EINVAL; + goto err; + } + + if (memcmp(data, ATH11K_BOARD_MAGIC, magic_len)) { + ath11k_err(ab, "found invalid board magic\n"); + ret = -EINVAL; + goto err; + } + + /* magic is padded to 4 bytes */ + magic_len = ALIGN(magic_len, 4); + if (len < magic_len) { + ath11k_err(ab, "failed: %s/%s too small to contain board data, len: %zu\n", + ab->hw_params.fw.dir, filename, len); + ret = -EINVAL; + goto err; + } + + data += magic_len; + len -= magic_len; + + while (len > sizeof(struct ath11k_fw_ie)) { + hdr = (struct ath11k_fw_ie *)data; + ie_id = le32_to_cpu(hdr->id); + ie_len = le32_to_cpu(hdr->len); + + len -= sizeof(*hdr); + data = hdr->data; + + if (len < ALIGN(ie_len, 4)) { + ath11k_err(ab, "invalid length for board ie_id %d ie_len %zu len %zu\n", + ie_id, ie_len, len); + return -EINVAL; + } + + switch (ie_id) { + case ATH11K_BD_IE_BOARD: + ret = ath11k_core_parse_bd_ie_board(ab, bd, data, + ie_len, + boardname, + ATH11K_BD_IE_BOARD); + if (ret == -ENOENT) + /* no match found, continue */ + break; + else if (ret) + /* there was an error, bail out */ + goto err; + /* either found or error, so stop searching */ + goto out; + } + + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + + len -= ie_len; + data += ie_len; + } + +out: + if (!bd->data || !bd->len) { + ath11k_err(ab, + "failed to fetch board data for %s from %s/%s\n", + boardname, ab->hw_params.fw.dir, filename); + ret = -ENODATA; + goto err; + } + + return 0; + +err: + ath11k_core_free_bdf(ab, bd); + return ret; +} + +static int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, + struct ath11k_board_data *bd) +{ + bd->fw = ath11k_fetch_fw_file(ab, + ab->hw_params.fw.dir, + ATH11K_DEFAULT_BOARD_FILE); + if (IS_ERR(bd->fw)) + return PTR_ERR(bd->fw); + + bd->data = bd->fw->data; + bd->len = bd->fw->size; + + return 0; +} + +#define BOARD_NAME_SIZE 100 +int ath11k_core_fetch_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) +{ + char boardname[BOARD_NAME_SIZE]; + int ret; + + ret = ath11k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); + if (ret) { + ath11k_err(ab, "failed to create board name: %d", ret); + return ret; + } + + ab->bd_api = 2; + ret = ath11k_core_fetch_board_data_api_n(ab, bd, boardname); + if (!ret) + goto success; + + ab->bd_api = 1; + ret = ath11k_core_fetch_board_data_api_1(ab, bd); + if (ret) { + ath11k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n", + ab->hw_params.fw.dir); + return ret; + } + +success: + ath11k_dbg(ab, ATH11K_DBG_BOOT, "using board api %d\n", ab->bd_api); + return 0; +} + +static void ath11k_core_stop(struct ath11k_base *ab) +{ + if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + ath11k_qmi_firmware_stop(ab); + ath11k_ahb_stop(ab); + ath11k_wmi_detach(ab); + ath11k_dp_pdev_reo_cleanup(ab); + + /* De-Init of components as needed */ +} + +static int ath11k_core_soc_create(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_qmi_init_service(ab); + if (ret) { + ath11k_err(ab, "failed to initialize qmi :%d\n", ret); + return ret; + } + + ret = ath11k_debug_soc_create(ab); + if (ret) { + ath11k_err(ab, "failed to create ath11k debugfs\n"); + goto err_qmi_deinit; + } + + ret = ath11k_ahb_power_up(ab); + if (ret) { + ath11k_err(ab, "failed to power up :%d\n", ret); + goto err_debugfs_reg; + } + + return 0; + +err_debugfs_reg: + ath11k_debug_soc_destroy(ab); +err_qmi_deinit: + ath11k_qmi_deinit_service(ab); + return ret; +} + +static void ath11k_core_soc_destroy(struct ath11k_base *ab) +{ + ath11k_debug_soc_destroy(ab); + ath11k_dp_free(ab); + ath11k_reg_free(ab); + ath11k_qmi_deinit_service(ab); +} + +static int ath11k_core_pdev_create(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_debug_pdev_create(ab); + if (ret) { + ath11k_err(ab, "failed to create core pdev debugfs: %d\n", ret); + return ret; + } + + ret = ath11k_mac_register(ab); + if (ret) { + ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret); + goto err_pdev_debug; + } + + ret = ath11k_dp_pdev_alloc(ab); + if (ret) { + ath11k_err(ab, "failed to attach DP pdev: %d\n", ret); + goto err_mac_unregister; + } + + return 0; + +err_mac_unregister: + ath11k_mac_unregister(ab); + +err_pdev_debug: + ath11k_debug_pdev_destroy(ab); + + return ret; +} + +static void ath11k_core_pdev_destroy(struct ath11k_base *ab) +{ + ath11k_mac_unregister(ab); + ath11k_ahb_ext_irq_disable(ab); + ath11k_dp_pdev_free(ab); + ath11k_debug_pdev_destroy(ab); +} + +static int ath11k_core_start(struct ath11k_base *ab, + enum ath11k_firmware_mode mode) +{ + int ret; + + ret = ath11k_qmi_firmware_start(ab, mode); + if (ret) { + ath11k_err(ab, "failed to attach wmi: %d\n", ret); + return ret; + } + + ret = ath11k_wmi_attach(ab); + if (ret) { + ath11k_err(ab, "failed to attach wmi: %d\n", ret); + goto err_firmware_stop; + } + + ret = ath11k_htc_init(ab); + if (ret) { + ath11k_err(ab, "failed to init htc: %d\n", ret); + goto err_wmi_detach; + } + + ret = ath11k_ahb_start(ab); + if (ret) { + ath11k_err(ab, "failed to start HIF: %d\n", ret); + goto err_wmi_detach; + } + + ret = ath11k_htc_wait_target(&ab->htc); + if (ret) { + ath11k_err(ab, "failed to connect to HTC: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_dp_htt_connect(&ab->dp); + if (ret) { + ath11k_err(ab, "failed to connect to HTT: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_wmi_connect(ab); + if (ret) { + ath11k_err(ab, "failed to connect wmi: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_htc_start(&ab->htc); + if (ret) { + ath11k_err(ab, "failed to start HTC: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_wmi_wait_for_service_ready(ab); + if (ret) { + ath11k_err(ab, "failed to receive wmi service ready event: %d\n", + ret); + goto err_hif_stop; + } + + ret = ath11k_mac_allocate(ab); + if (ret) { + ath11k_err(ab, "failed to create new hw device with mac80211 :%d\n", + ret); + goto err_hif_stop; + } + + ath11k_dp_pdev_pre_alloc(ab); + + ret = ath11k_dp_pdev_reo_setup(ab); + if (ret) { + ath11k_err(ab, "failed to initialize reo destination rings: %d\n", ret); + goto err_mac_destroy; + } + + ret = ath11k_wmi_cmd_init(ab); + if (ret) { + ath11k_err(ab, "failed to send wmi init cmd: %d\n", ret); + goto err_reo_cleanup; + } + + ret = ath11k_wmi_wait_for_unified_ready(ab); + if (ret) { + ath11k_err(ab, "failed to receive wmi unified ready event: %d\n", + ret); + goto err_reo_cleanup; + } + + ret = ath11k_dp_tx_htt_h2t_ver_req_msg(ab); + if (ret) { + ath11k_err(ab, "failed to send htt version request message: %d\n", + ret); + goto err_reo_cleanup; + } + + return 0; + +err_reo_cleanup: + ath11k_dp_pdev_reo_cleanup(ab); +err_mac_destroy: + ath11k_mac_destroy(ab); +err_hif_stop: + ath11k_ahb_stop(ab); +err_wmi_detach: + ath11k_wmi_detach(ab); +err_firmware_stop: + ath11k_qmi_firmware_stop(ab); + + return ret; +} + +int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_ce_init_pipes(ab); + if (ret) { + ath11k_err(ab, "failed to initialize CE: %d\n", ret); + return ret; + } + + ret = ath11k_dp_alloc(ab); + if (ret) { + ath11k_err(ab, "failed to init DP: %d\n", ret); + return ret; + } + + mutex_lock(&ab->core_lock); + ret = ath11k_core_start(ab, ATH11K_FIRMWARE_MODE_NORMAL); + if (ret) { + ath11k_err(ab, "failed to start core: %d\n", ret); + goto err_dp_free; + } + + ret = ath11k_core_pdev_create(ab); + if (ret) { + ath11k_err(ab, "failed to create pdev core: %d\n", ret); + goto err_core_stop; + } + ath11k_ahb_ext_irq_enable(ab); + mutex_unlock(&ab->core_lock); + + return 0; + +err_core_stop: + ath11k_core_stop(ab); + ath11k_mac_destroy(ab); +err_dp_free: + ath11k_dp_free(ab); + mutex_unlock(&ab->core_lock); + return ret; +} + +static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab) +{ + int ret; + + mutex_lock(&ab->core_lock); + ath11k_ahb_ext_irq_disable(ab); + ath11k_dp_pdev_free(ab); + ath11k_ahb_stop(ab); + ath11k_wmi_detach(ab); + ath11k_dp_pdev_reo_cleanup(ab); + mutex_unlock(&ab->core_lock); + + ath11k_dp_free(ab); + ath11k_hal_srng_deinit(ab); + + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; + + ret = ath11k_hal_srng_init(ab); + if (ret) + return ret; + + clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); + + ret = ath11k_core_qmi_firmware_ready(ab); + if (ret) + goto err_hal_srng_deinit; + + clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); + + return 0; + +err_hal_srng_deinit: + ath11k_hal_srng_deinit(ab); + return ret; +} + +void ath11k_core_halt(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + + lockdep_assert_held(&ar->conf_mutex); + + ar->num_created_vdevs = 0; + + ath11k_mac_scan_finish(ar); + ath11k_mac_peer_cleanup_all(ar); + cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->regd_update_work); + + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); + synchronize_rcu(); + INIT_LIST_HEAD(&ar->arvifs); + idr_init(&ar->txmgmt_idr); +} + +static void ath11k_core_restart(struct work_struct *work) +{ + struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i, ret = 0; + + spin_lock_bh(&ab->base_lock); + ab->stats.fw_crash_counter++; + spin_unlock_bh(&ab->base_lock); + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar || ar->state == ATH11K_STATE_OFF) + continue; + + ieee80211_stop_queues(ar->hw); + ath11k_mac_drain_tx(ar); + complete(&ar->scan.started); + complete(&ar->scan.completed); + complete(&ar->peer_assoc_done); + complete(&ar->install_key_done); + complete(&ar->vdev_setup_done); + complete(&ar->bss_survey_done); + + wake_up(&ar->dp.tx_empty_waitq); + idr_for_each(&ar->txmgmt_idr, + ath11k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + } + + wake_up(&ab->wmi_ab.tx_credits_wq); + wake_up(&ab->peer_mapping_wq); + + ret = ath11k_core_reconfigure_on_crash(ab); + if (ret) { + ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); + return; + } + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar || ar->state == ATH11K_STATE_OFF) + continue; + + mutex_lock(&ar->conf_mutex); + + switch (ar->state) { + case ATH11K_STATE_ON: + ar->state = ATH11K_STATE_RESTARTING; + ath11k_core_halt(ar); + ieee80211_restart_hw(ar->hw); + break; + case ATH11K_STATE_OFF: + ath11k_warn(ab, + "cannot restart radio %d that hasn't been started\n", + i); + break; + case ATH11K_STATE_RESTARTING: + break; + case ATH11K_STATE_RESTARTED: + ar->state = ATH11K_STATE_WEDGED; + /* fall through */ + case ATH11K_STATE_WEDGED: + ath11k_warn(ab, + "device is wedged, will not restart radio %d\n", i); + break; + } + mutex_unlock(&ar->conf_mutex); + } + complete(&ab->driver_recovery); +} + +int ath11k_core_init(struct ath11k_base *ab) +{ + struct device *dev = ab->dev; + struct rproc *prproc; + phandle rproc_phandle; + int ret; + + if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle)) { + ath11k_err(ab, "failed to get q6_rproc handle\n"); + return -ENOENT; + } + + prproc = rproc_get_by_phandle(rproc_phandle); + if (!prproc) { + ath11k_err(ab, "failed to get rproc\n"); + return -EINVAL; + } + ab->tgt_rproc = prproc; + ab->hw_params = ath11k_hw_params; + + ret = ath11k_core_soc_create(ab); + if (ret) { + ath11k_err(ab, "failed to create soc core: %d\n", ret); + return ret; + } + + return 0; +} + +void ath11k_core_deinit(struct ath11k_base *ab) +{ + mutex_lock(&ab->core_lock); + + ath11k_core_pdev_destroy(ab); + ath11k_core_stop(ab); + + mutex_unlock(&ab->core_lock); + + ath11k_ahb_power_down(ab); + ath11k_mac_destroy(ab); + ath11k_core_soc_destroy(ab); +} + +void ath11k_core_free(struct ath11k_base *ab) +{ + kfree(ab); +} + +struct ath11k_base *ath11k_core_alloc(struct device *dev) +{ + struct ath11k_base *ab; + + ab = kzalloc(sizeof(*ab), GFP_KERNEL); + if (!ab) + return NULL; + + init_completion(&ab->driver_recovery); + + ab->workqueue = create_singlethread_workqueue("ath11k_wq"); + if (!ab->workqueue) + goto err_sc_free; + + mutex_init(&ab->core_lock); + spin_lock_init(&ab->base_lock); + + INIT_LIST_HEAD(&ab->peers); + init_waitqueue_head(&ab->peer_mapping_wq); + init_waitqueue_head(&ab->wmi_ab.tx_credits_wq); + INIT_WORK(&ab->restart_work, ath11k_core_restart); + timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); + ab->dev = dev; + + return ab; + +err_sc_free: + kfree(ab); + return NULL; +} + +static int __init ath11k_init(void) +{ + int ret; + + ret = ath11k_ahb_init(); + if (ret) + printk(KERN_ERR "failed to register ath11k ahb driver: %d\n", + ret); + return ret; +} +module_init(ath11k_init); + +static void __exit ath11k_exit(void) +{ + ath11k_ahb_exit(); +} +module_exit(ath11k_exit); + +MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax wireless chip"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h new file mode 100644 index 000000000000..25cdcf71d0c4 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -0,0 +1,826 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_CORE_H +#define ATH11K_CORE_H + +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/bitfield.h> +#include "qmi.h" +#include "htc.h" +#include "wmi.h" +#include "hal.h" +#include "dp.h" +#include "ce.h" +#include "mac.h" +#include "hw.h" +#include "hal_rx.h" +#include "reg.h" + +#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +#define ATH11K_TX_MGMT_NUM_PENDING_MAX 512 + +#define ATH11K_TX_MGMT_TARGET_MAX_SUPPORT_WMI 64 + +/* Pending management packets threshold for dropping probe responses */ +#define ATH11K_PRB_RSP_DROP_THRESHOLD ((ATH11K_TX_MGMT_TARGET_MAX_SUPPORT_WMI * 3) / 4) + +#define ATH11K_INVALID_HW_MAC_ID 0xFF + +enum ath11k_supported_bw { + ATH11K_BW_20 = 0, + ATH11K_BW_40 = 1, + ATH11K_BW_80 = 2, + ATH11K_BW_160 = 3, +}; + +enum wme_ac { + WME_AC_BE, + WME_AC_BK, + WME_AC_VI, + WME_AC_VO, + WME_NUM_AC +}; + +#define ATH11K_HT_MCS_MAX 7 +#define ATH11K_VHT_MCS_MAX 9 +#define ATH11K_HE_MCS_MAX 11 + +static inline enum wme_ac ath11k_tid_to_ac(u32 tid) +{ + return (((tid == 0) || (tid == 3)) ? WME_AC_BE : + ((tid == 1) || (tid == 2)) ? WME_AC_BK : + ((tid == 4) || (tid == 5)) ? WME_AC_VI : + WME_AC_VO); +} + +struct ath11k_skb_cb { + dma_addr_t paddr; + u8 eid; + struct ath11k *ar; + struct ieee80211_vif *vif; +} __packed; + +struct ath11k_skb_rxcb { + dma_addr_t paddr; + bool is_first_msdu; + bool is_last_msdu; + bool is_continuation; + struct hal_rx_desc *rx_desc; + u8 err_rel_src; + u8 err_code; + u8 mac_id; + u8 unmapped; +}; + +enum ath11k_hw_rev { + ATH11K_HW_IPQ8074, +}; + +enum ath11k_firmware_mode { + /* the default mode, standard 802.11 functionality */ + ATH11K_FIRMWARE_MODE_NORMAL, + + /* factory tests etc */ + ATH11K_FIRMWARE_MODE_FTM, +}; + +#define ATH11K_IRQ_NUM_MAX 52 +#define ATH11K_EXT_IRQ_GRP_NUM_MAX 11 +#define ATH11K_EXT_IRQ_NUM_MAX 16 + +extern const u8 ath11k_reo_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_tx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rx_err_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rx_wbm_rel_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rxdma2host_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_host2rxdma_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 rx_mon_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + +struct ath11k_ext_irq_grp { + struct ath11k_base *ab; + u32 irqs[ATH11K_EXT_IRQ_NUM_MAX]; + u32 num_irq; + u32 grp_id; + 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 +#define HECAP_PHYINFO_SIZE 9 +#define HECAP_MACINFO_SIZE 5 +#define HECAP_TXRX_MCS_NSS_SIZE 2 +#define HECAP_PPET16_PPET8_MAX_SIZE 25 + +#define HE_PPET16_PPET8_SIZE 8 + +/* 802.11ax PPE (PPDU packet Extension) threshold */ +struct he_ppe_threshold { + u32 numss_m1; + u32 ru_mask; + u32 ppet16_ppet8_ru3_ru0[HE_PPET16_PPET8_SIZE]; +}; + +struct ath11k_he { + u8 hecap_macinfo[HECAP_MACINFO_SIZE]; + u32 hecap_rxmcsnssmap; + u32 hecap_txmcsnssmap; + u32 hecap_phyinfo[HEHANDLE_CAP_PHYINFO_SIZE]; + struct he_ppe_threshold hecap_ppet; + u32 heop_param; +}; + +#define MAX_RADIOS 3 + +enum { + WMI_HOST_TP_SCALE_MAX = 0, + WMI_HOST_TP_SCALE_50 = 1, + WMI_HOST_TP_SCALE_25 = 2, + WMI_HOST_TP_SCALE_12 = 3, + WMI_HOST_TP_SCALE_MIN = 4, + WMI_HOST_TP_SCALE_SIZE = 5, +}; + +enum ath11k_scan_state { + ATH11K_SCAN_IDLE, + ATH11K_SCAN_STARTING, + ATH11K_SCAN_RUNNING, + ATH11K_SCAN_ABORTING, +}; + +enum ath11k_dev_flags { + ATH11K_CAC_RUNNING, + ATH11K_FLAG_CORE_REGISTERED, + ATH11K_FLAG_CRASH_FLUSH, + ATH11K_FLAG_RAW_MODE, + ATH11K_FLAG_HW_CRYPTO_DISABLED, + ATH11K_FLAG_BTCOEX, + ATH11K_FLAG_RECOVERY, + ATH11K_FLAG_UNREGISTERING, + ATH11K_FLAG_REGISTERED, +}; + +enum ath11k_monitor_flags { + ATH11K_FLAG_MONITOR_ENABLED, +}; + +struct ath11k_vif { + u32 vdev_id; + enum wmi_vdev_type vdev_type; + enum wmi_vdev_subtype vdev_subtype; + u32 beacon_interval; + u32 dtim_period; + u16 ast_hash; + u16 tcl_metadata; + u8 hal_addr_search_flags; + u8 search_type; + + struct ath11k *ar; + struct ieee80211_vif *vif; + + u16 tx_seq_no; + struct wmi_wmm_params_all_arg wmm_params; + struct list_head list; + union { + struct { + u32 uapsd; + } sta; + struct { + /* 127 stations; wmi limit */ + u8 tim_bitmap[16]; + u8 tim_len; + u32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + bool hidden_ssid; + /* P2P_IE with NoA attribute for P2P_GO case */ + u32 noa_len; + u8 *noa_data; + } ap; + } u; + + bool is_started; + bool is_up; + u32 aid; + u8 bssid[ETH_ALEN]; + struct cfg80211_bitrate_mask bitrate_mask; + int num_legacy_stations; + int rtscts_prot_mode; + int txpower; +}; + +struct ath11k_vif_iter { + u32 vdev_id; + struct ath11k_vif *arvif; +}; + +struct ath11k_rx_peer_stats { + u64 num_msdu; + u64 num_mpdu_fcs_ok; + u64 num_mpdu_fcs_err; + u64 tcp_msdu_count; + u64 udp_msdu_count; + u64 other_msdu_count; + u64 ampdu_msdu_count; + u64 non_ampdu_msdu_count; + u64 stbc_count; + u64 beamformed_count; + u64 mcs_count[HAL_RX_MAX_MCS + 1]; + u64 nss_count[HAL_RX_MAX_NSS]; + u64 bw_count[HAL_RX_BW_MAX]; + u64 gi_count[HAL_RX_GI_MAX]; + u64 coding_count[HAL_RX_SU_MU_CODING_MAX]; + u64 tid_count[IEEE80211_NUM_TIDS + 1]; + u64 pream_cnt[HAL_RX_PREAMBLE_MAX]; + u64 reception_type[HAL_RX_RECEPTION_TYPE_MAX]; + u64 rx_duration; +}; + +#define ATH11K_HE_MCS_NUM 12 +#define ATH11K_VHT_MCS_NUM 10 +#define ATH11K_BW_NUM 4 +#define ATH11K_NSS_NUM 4 +#define ATH11K_LEGACY_NUM 12 +#define ATH11K_GI_NUM 4 +#define ATH11K_HT_MCS_NUM 32 + +enum ath11k_pkt_rx_err { + ATH11K_PKT_RX_ERR_FCS, + ATH11K_PKT_RX_ERR_TKIP, + ATH11K_PKT_RX_ERR_CRYPT, + ATH11K_PKT_RX_ERR_PEER_IDX_INVAL, + ATH11K_PKT_RX_ERR_MAX, +}; + +enum ath11k_ampdu_subfrm_num { + ATH11K_AMPDU_SUBFRM_NUM_10, + ATH11K_AMPDU_SUBFRM_NUM_20, + ATH11K_AMPDU_SUBFRM_NUM_30, + ATH11K_AMPDU_SUBFRM_NUM_40, + ATH11K_AMPDU_SUBFRM_NUM_50, + ATH11K_AMPDU_SUBFRM_NUM_60, + ATH11K_AMPDU_SUBFRM_NUM_MORE, + ATH11K_AMPDU_SUBFRM_NUM_MAX, +}; + +enum ath11k_amsdu_subfrm_num { + ATH11K_AMSDU_SUBFRM_NUM_1, + ATH11K_AMSDU_SUBFRM_NUM_2, + ATH11K_AMSDU_SUBFRM_NUM_3, + ATH11K_AMSDU_SUBFRM_NUM_4, + ATH11K_AMSDU_SUBFRM_NUM_MORE, + ATH11K_AMSDU_SUBFRM_NUM_MAX, +}; + +enum ath11k_counter_type { + ATH11K_COUNTER_TYPE_BYTES, + ATH11K_COUNTER_TYPE_PKTS, + ATH11K_COUNTER_TYPE_MAX, +}; + +enum ath11k_stats_type { + ATH11K_STATS_TYPE_SUCC, + ATH11K_STATS_TYPE_FAIL, + ATH11K_STATS_TYPE_RETRY, + ATH11K_STATS_TYPE_AMPDU, + ATH11K_STATS_TYPE_MAX, +}; + +struct ath11k_htt_data_stats { + u64 legacy[ATH11K_COUNTER_TYPE_MAX][ATH11K_LEGACY_NUM]; + u64 ht[ATH11K_COUNTER_TYPE_MAX][ATH11K_HT_MCS_NUM]; + u64 vht[ATH11K_COUNTER_TYPE_MAX][ATH11K_VHT_MCS_NUM]; + u64 he[ATH11K_COUNTER_TYPE_MAX][ATH11K_HE_MCS_NUM]; + u64 bw[ATH11K_COUNTER_TYPE_MAX][ATH11K_BW_NUM]; + u64 nss[ATH11K_COUNTER_TYPE_MAX][ATH11K_NSS_NUM]; + u64 gi[ATH11K_COUNTER_TYPE_MAX][ATH11K_GI_NUM]; +}; + +struct ath11k_htt_tx_stats { + struct ath11k_htt_data_stats stats[ATH11K_STATS_TYPE_MAX]; + u64 tx_duration; + u64 ba_fails; + u64 ack_fails; +}; + +struct ath11k_per_ppdu_tx_stats { + u16 succ_pkts; + u16 failed_pkts; + u16 retry_pkts; + u32 succ_bytes; + u32 failed_bytes; + u32 retry_bytes; +}; + +struct ath11k_sta { + struct ath11k_vif *arvif; + + /* the following are protected by ar->data_lock */ + u32 changed; /* IEEE80211_RC_* */ + u32 bw; + u32 nss; + u32 smps; + + struct work_struct update_wk; + struct ieee80211_tx_info tx_info; + struct rate_info txrate; + struct rate_info last_txrate; + u64 rx_duration; + u64 tx_duration; + u8 rssi_comb; + struct ath11k_htt_tx_stats *tx_stats; + struct ath11k_rx_peer_stats *rx_stats; +}; + +#define ATH11K_NUM_CHANS 41 +#define ATH11K_MAX_5G_CHAN 173 + +enum ath11k_state { + ATH11K_STATE_OFF, + ATH11K_STATE_ON, + ATH11K_STATE_RESTARTING, + ATH11K_STATE_RESTARTED, + ATH11K_STATE_WEDGED, + /* Add other states as required */ +}; + +/* Antenna noise floor */ +#define ATH11K_DEFAULT_NOISE_FLOOR -95 + +struct ath11k_fw_stats { + struct dentry *debugfs_fwstats; + u32 pdev_id; + u32 stats_id; + struct list_head pdevs; + struct list_head vdevs; + struct list_head bcn; +}; + +struct ath11k_dbg_htt_stats { + u8 type; + u8 reset; + struct debug_htt_stats_req *stats_req; + /* protects shared stats req buffer */ + spinlock_t lock; +}; + +struct ath11k_debug { + struct dentry *debugfs_pdev; + struct ath11k_dbg_htt_stats htt_stats; + u32 extd_tx_stats; + struct ath11k_fw_stats fw_stats; + struct completion fw_stats_complete; + bool fw_stats_done; + u32 extd_rx_stats; + u32 pktlog_filter; + u32 pktlog_mode; + u32 pktlog_peer_valid; + u8 pktlog_peer_addr[ETH_ALEN]; +}; + +struct ath11k_per_peer_tx_stats { + u32 succ_bytes; + u32 retry_bytes; + u32 failed_bytes; + u16 succ_pkts; + u16 retry_pkts; + u16 failed_pkts; + u32 duration; + u8 ba_fails; + bool is_ampdu; +}; + +#define ATH11K_FLUSH_TIMEOUT (5 * HZ) + +struct ath11k_vdev_stop_status { + bool stop_in_progress; + u32 vdev_id; +}; + +struct ath11k { + struct ath11k_base *ab; + struct ath11k_pdev *pdev; + struct ieee80211_hw *hw; + struct ieee80211_ops *ops; + struct ath11k_pdev_wmi *wmi; + struct ath11k_pdev_dp dp; + u8 mac_addr[ETH_ALEN]; + u32 ht_cap_info; + u32 vht_cap_info; + struct ath11k_he ar_he; + enum ath11k_state state; + struct { + struct completion started; + struct completion completed; + struct completion on_channel; + struct delayed_work timeout; + enum ath11k_scan_state state; + bool is_roc; + int vdev_id; + int roc_freq; + bool roc_notify; + } scan; + + struct { + struct ieee80211_supported_band sbands[NUM_NL80211_BANDS]; + struct ieee80211_sband_iftype_data + iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; + } mac; + unsigned long dev_flags; + unsigned int filter_flags; + unsigned long monitor_flags; + u32 min_tx_power; + u32 max_tx_power; + u32 txpower_limit_2g; + u32 txpower_limit_5g; + u32 txpower_scale; + u32 power_scale; + u32 chan_tx_pwr; + u32 num_stations; + u32 max_num_stations; + bool monitor_present; + /* To synchronize concurrent synchronous mac80211 callback operations, + * concurrent debugfs configuration and concurrent FW statistics events. + */ + struct mutex conf_mutex; + /* protects the radio specific data like debug stats, ppdu_stats_info stats, + * vdev_stop_status info, scan data, ath11k_sta info, ath11k_vif info, + * channel context data, survey info, test mode data. + */ + spinlock_t data_lock; + + struct list_head arvifs; + /* should never be NULL; needed for regular htt rx */ + struct ieee80211_channel *rx_channel; + + /* valid during scan; needed for mgmt rx during scan */ + struct ieee80211_channel *scan_channel; + + u8 cfg_tx_chainmask; + u8 cfg_rx_chainmask; + u8 num_rx_chains; + u8 num_tx_chains; + /* pdev_idx starts from 0 whereas pdev->pdev_id starts with 1 */ + u8 pdev_idx; + u8 lmac_id; + + struct completion peer_assoc_done; + + int install_key_status; + struct completion install_key_done; + + int last_wmi_vdev_start_status; + struct ath11k_vdev_stop_status vdev_stop_status; + struct completion vdev_setup_done; + + int num_peers; + int max_num_peers; + u32 num_started_vdevs; + u32 num_created_vdevs; + + struct idr txmgmt_idr; + /* protects txmgmt_idr data */ + spinlock_t txmgmt_idr_lock; + atomic_t num_pending_mgmt_tx; + + /* cycle count is reported twice for each visited channel during scan. + * access protected by data_lock + */ + u32 survey_last_rx_clear_count; + u32 survey_last_cycle_count; + + /* Channel info events are expected to come in pairs without and with + * COMPLETE flag set respectively for each channel visit during scan. + * + * However there are deviations from this rule. This flag is used to + * avoid reporting garbage data. + */ + bool ch_info_can_report_survey; + struct survey_info survey[ATH11K_NUM_CHANS]; + struct completion bss_survey_done; + + struct work_struct regd_update_work; + + struct work_struct wmi_mgmt_tx_work; + struct sk_buff_head wmi_mgmt_tx_queue; + + struct ath11k_per_peer_tx_stats peer_tx_stats; + struct list_head ppdu_stats_info; + u32 ppdu_stat_list_depth; + + struct ath11k_per_peer_tx_stats cached_stats; + u32 last_ppdu_id; + u32 cached_ppdu_id; +#ifdef CONFIG_ATH11K_DEBUGFS + struct ath11k_debug debug; +#endif + bool dfs_block_radar_events; +}; + +struct ath11k_band_cap { + u32 max_bw_supported; + u32 ht_cap_info; + u32 he_cap_info[2]; + u32 he_mcs; + u32 he_cap_phy_info[PSOC_HOST_MAX_PHY_SIZE]; + struct ath11k_ppe_threshold he_ppet; +}; + +struct ath11k_pdev_cap { + u32 supported_bands; + u32 ampdu_density; + u32 vht_cap; + u32 vht_mcs; + u32 he_mcs; + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 tx_chain_mask_shift; + u32 rx_chain_mask_shift; + struct ath11k_band_cap band[NUM_NL80211_BANDS]; +}; + +struct ath11k_pdev { + struct ath11k *ar; + u32 pdev_id; + struct ath11k_pdev_cap cap; + u8 mac_addr[ETH_ALEN]; +}; + +struct ath11k_board_data { + const struct firmware *fw; + const void *data; + size_t len; +}; + +/* IPQ8074 HW channel counters frequency value in hertz */ +#define IPQ8074_CC_FREQ_HERTZ 320000 + +struct ath11k_soc_dp_rx_stats { + u32 err_ring_pkts; + u32 invalid_rbm; + u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX]; + u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX]; + u32 hal_reo_error[DP_REO_DST_RING_MAX]; +}; + +/* Master structure to hold the hw data which may be used in core module */ +struct ath11k_base { + enum ath11k_hw_rev hw_rev; + struct platform_device *pdev; + struct device *dev; + struct ath11k_qmi qmi; + struct ath11k_wmi_base wmi_ab; + struct completion fw_ready; + struct rproc *tgt_rproc; + int num_radios; + /* HW channel counters frequency value in hertz common to all MACs */ + u32 cc_freq_hz; + + struct ath11k_htc htc; + + struct ath11k_dp dp; + + void __iomem *mem; + unsigned long mem_len; + + const struct ath11k_hif_ops *hif_ops; + + struct ath11k_ce ce; + struct timer_list rx_replenish_retry; + struct ath11k_hal hal; + /* To synchronize core_start/core_stop */ + struct mutex core_lock; + /* Protects data like peers */ + spinlock_t base_lock; + struct ath11k_pdev pdevs[MAX_RADIOS]; + struct ath11k_pdev __rcu *pdevs_active[MAX_RADIOS]; + struct ath11k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS]; + unsigned long long free_vdev_map; + struct list_head peers; + wait_queue_head_t peer_mapping_wq; + u8 mac_addr[ETH_ALEN]; + bool wmi_ready; + u32 wlan_init_status; + int irq_num[ATH11K_IRQ_NUM_MAX]; + struct ath11k_ext_irq_grp ext_irq_grp[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + struct napi_struct *napi; + struct ath11k_targ_cap target_caps; + u32 ext_service_bitmap[WMI_SERVICE_EXT_BM_SIZE]; + bool pdevs_macaddr_valid; + int bd_api; + struct ath11k_hw_params hw_params; + const struct firmware *cal_file; + + /* Below regd's are protected by ab->data_lock */ + /* This is the regd set for every radio + * by the firmware during initializatin + */ + struct ieee80211_regdomain *default_regd[MAX_RADIOS]; + /* This regd is set during dynamic country setting + * This may or may not be used during the runtime + */ + struct ieee80211_regdomain *new_regd[MAX_RADIOS]; + + /* Current DFS Regulatory */ + enum ath11k_dfs_region dfs_region; +#ifdef CONFIG_ATH11K_DEBUGFS + struct dentry *debugfs_soc; + struct dentry *debugfs_ath11k; +#endif + struct ath11k_soc_dp_rx_stats soc_stats; + + unsigned long dev_flags; + struct completion driver_recovery; + struct workqueue_struct *workqueue; + struct work_struct restart_work; + struct { + /* protected by data_lock */ + u32 fw_crash_counter; + } stats; +}; + +struct ath11k_fw_stats_pdev { + struct list_head list; + + /* PDEV stats */ + s32 ch_noise_floor; + /* Cycles spent transmitting frames */ + u32 tx_frame_count; + /* Cycles spent receiving frames */ + u32 rx_frame_count; + /* Total channel busy time, evidently */ + u32 rx_clear_count; + /* Total on-channel time */ + u32 cycle_count; + u32 phy_err_count; + u32 chan_tx_power; + u32 ack_rx_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 no_beacons; + u32 mib_int_count; + + /* PDEV TX stats */ + /* Num HTT cookies queued to dispatch list */ + s32 comp_queued; + /* Num HTT cookies dispatched */ + s32 comp_delivered; + /* Num MSDU queued to WAL */ + s32 msdu_enqued; + /* Num MPDU queue to WAL */ + s32 mpdu_enqued; + /* Num MSDUs dropped by WMM limit */ + s32 wmm_drop; + /* Num Local frames queued */ + s32 local_enqued; + /* Num Local frames done */ + s32 local_freed; + /* Num queued to HW */ + s32 hw_queued; + /* Num PPDU reaped from HW */ + s32 hw_reaped; + /* Num underruns */ + s32 underrun; + /* Num PPDUs cleaned up in TX abort */ + s32 tx_abort; + /* Num MPDUs requed by SW */ + s32 mpdus_requed; + /* excessive retries */ + u32 tx_ko; + /* data hw rate code */ + u32 data_rc; + /* Scheduler self triggers */ + u32 self_triggers; + /* frames dropped due to excessive sw retries */ + u32 sw_retry_failure; + /* illegal rate phy errors */ + u32 illgl_rate_phy_err; + /* wal pdev continuous xretry */ + u32 pdev_cont_xretry; + /* wal pdev tx timeouts */ + u32 pdev_tx_timeout; + /* wal pdev resets */ + u32 pdev_resets; + /* frames dropped due to non-availability of stateless TIDs */ + u32 stateless_tid_alloc_failure; + /* PhY/BB underrun */ + u32 phy_underrun; + /* MPDU is more than txop limit */ + u32 txop_ovf; + + /* PDEV RX stats */ + /* Cnts any change in ring routing mid-ppdu */ + s32 mid_ppdu_route_change; + /* Total number of statuses processed */ + s32 status_rcvd; + /* Extra frags on rings 0-3 */ + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + /* MSDUs / MPDUs delivered to HTT */ + s32 htt_msdus; + s32 htt_mpdus; + /* MSDUs / MPDUs delivered to local stack */ + s32 loc_msdus; + s32 loc_mpdus; + /* AMSDUs that have more MSDUs than the status ring size */ + s32 oversize_amsdu; + /* Number of PHY errors */ + s32 phy_errs; + /* Number of PHY errors drops */ + s32 phy_err_drop; + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + s32 mpdu_errs; +}; + +struct ath11k_fw_stats_vdev { + struct list_head list; + + u32 vdev_id; + u32 beacon_snr; + u32 data_snr; + u32 num_tx_frames[WLAN_MAX_AC]; + u32 num_rx_frames; + u32 num_tx_frames_retries[WLAN_MAX_AC]; + u32 num_tx_frames_failures[WLAN_MAX_AC]; + u32 num_rts_fail; + u32 num_rts_success; + u32 num_rx_err; + u32 num_rx_discard; + u32 num_tx_not_acked; + u32 tx_rate_history[MAX_TX_RATE_VALUES]; + u32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +}; + +struct ath11k_fw_stats_bcn { + struct list_head list; + + u32 vdev_id; + u32 tx_bcn_succ_cnt; + u32 tx_bcn_outage_cnt; +}; + +void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); +void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash); +struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, int peer_id); +int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab); +int ath11k_core_init(struct ath11k_base *ath11k); +void ath11k_core_deinit(struct ath11k_base *ath11k); +struct ath11k_base *ath11k_core_alloc(struct device *dev); +void ath11k_core_free(struct ath11k_base *ath11k); +int ath11k_core_fetch_bdf(struct ath11k_base *ath11k, + struct ath11k_board_data *bd); +void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); + +void ath11k_core_halt(struct ath11k *ar); +u8 ath11k_core_get_hw_mac_id(struct ath11k_base *ab, int pdev_idx); + +static inline const char *ath11k_scan_state_str(enum ath11k_scan_state state) +{ + switch (state) { + case ATH11K_SCAN_IDLE: + return "idle"; + case ATH11K_SCAN_STARTING: + return "starting"; + case ATH11K_SCAN_RUNNING: + return "running"; + case ATH11K_SCAN_ABORTING: + return "aborting"; + } + + return "unknown"; +} + +static inline struct ath11k_skb_cb *ATH11K_SKB_CB(struct sk_buff *skb) +{ + return (struct ath11k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data; +} + +static inline struct ath11k_skb_rxcb *ATH11K_SKB_RXCB(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct ath11k_skb_rxcb) > sizeof(skb->cb)); + return (struct ath11k_skb_rxcb *)skb->cb; +} + +static inline struct ath11k_vif *ath11k_vif_to_arvif(struct ieee80211_vif *vif) +{ + return (struct ath11k_vif *)vif->drv_priv; +} + +#endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/debug.c b/drivers/net/wireless/ath/ath11k/debug.c new file mode 100644 index 000000000000..8d485171b0b3 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -0,0 +1,1075 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/vmalloc.h> +#include "core.h" +#include "debug.h" +#include "wmi.h" +#include "hal_rx.h" +#include "dp_tx.h" +#include "debug_htt_stats.h" +#include "peer.h" + +void ath11k_info(struct ath11k_base *ab, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + dev_info(ab->dev, "%pV", &vaf); + /* TODO: Trace the log */ + va_end(args); +} + +void ath11k_err(struct ath11k_base *ab, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + dev_err(ab->dev, "%pV", &vaf); + /* TODO: Trace the log */ + va_end(args); +} + +void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + dev_warn_ratelimited(ab->dev, "%pV", &vaf); + /* TODO: Trace the log */ + va_end(args); +} + +#ifdef CONFIG_ATH11K_DEBUG +void __ath11k_dbg(struct ath11k_base *ab, enum ath11k_debug_mask mask, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (ath11k_debug_mask & mask) + dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); + + /* TODO: trace log */ + + va_end(args); +} + +void ath11k_dbg_dump(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ + char linebuf[256]; + size_t linebuflen; + const void *ptr; + + if (ath11k_debug_mask & mask) { + if (msg) + __ath11k_dbg(ab, mask, "%s\n", msg); + + for (ptr = buf; (ptr - buf) < len; ptr += 16) { + linebuflen = 0; + linebuflen += scnprintf(linebuf + linebuflen, + sizeof(linebuf) - linebuflen, + "%s%08x: ", + (prefix ? prefix : ""), + (unsigned int)(ptr - buf)); + hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1, + linebuf + linebuflen, + sizeof(linebuf) - linebuflen, true); + dev_printk(KERN_DEBUG, ab->dev, "%s\n", linebuf); + } + } +} + +#endif + +#ifdef CONFIG_ATH11K_DEBUGFS +static void ath11k_fw_stats_pdevs_free(struct list_head *head) +{ + struct ath11k_fw_stats_pdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath11k_fw_stats_vdevs_free(struct list_head *head) +{ + struct ath11k_fw_stats_vdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath11k_fw_stats_bcn_free(struct list_head *head) +{ + struct ath11k_fw_stats_bcn *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath11k_debug_fw_stats_reset(struct ath11k *ar) +{ + spin_lock_bh(&ar->data_lock); + ar->debug.fw_stats_done = false; + ath11k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs); + ath11k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs); + spin_unlock_bh(&ar->data_lock); +} + +void ath11k_debug_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct ath11k_fw_stats stats = {}; + struct ath11k *ar; + struct ath11k_pdev *pdev; + bool is_end; + static unsigned int num_vdev, num_bcn; + size_t total_vdevs_started = 0; + int i, ret; + + INIT_LIST_HEAD(&stats.pdevs); + INIT_LIST_HEAD(&stats.vdevs); + INIT_LIST_HEAD(&stats.bcn); + + ret = ath11k_wmi_pull_fw_stats(ab, skb, &stats); + if (ret) { + ath11k_warn(ab, "failed to pull fw stats: %d\n", ret); + goto free; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, stats.pdev_id); + if (!ar) { + rcu_read_unlock(); + ath11k_warn(ab, "failed to get ar for pdev_id %d: %d\n", + stats.pdev_id, ret); + goto free; + } + + spin_lock_bh(&ar->data_lock); + + if (stats.stats_id == WMI_REQUEST_PDEV_STAT) { + list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs); + ar->debug.fw_stats_done = true; + goto complete; + } + + if (stats.stats_id == WMI_REQUEST_VDEV_STAT) { + if (list_empty(&stats.vdevs)) { + ath11k_warn(ab, "empty vdev stats"); + goto complete; + } + /* FW sends all the active VDEV stats irrespective of PDEV, + * hence limit until the count of all VDEVs started + */ + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) + total_vdevs_started += ar->num_started_vdevs; + } + + is_end = ((++num_vdev) == total_vdevs_started ? true : false); + + list_splice_tail_init(&stats.vdevs, + &ar->debug.fw_stats.vdevs); + + if (is_end) { + ar->debug.fw_stats_done = true; + num_vdev = 0; + } + goto complete; + } + + if (stats.stats_id == WMI_REQUEST_BCN_STAT) { + if (list_empty(&stats.bcn)) { + ath11k_warn(ab, "empty bcn stats"); + goto complete; + } + /* Mark end until we reached the count of all started VDEVs + * within the PDEV + */ + is_end = ((++num_bcn) == ar->num_started_vdevs ? true : false); + + list_splice_tail_init(&stats.bcn, + &ar->debug.fw_stats.bcn); + + if (is_end) { + ar->debug.fw_stats_done = true; + num_bcn = 0; + } + } +complete: + complete(&ar->debug.fw_stats_complete); + rcu_read_unlock(); + spin_unlock_bh(&ar->data_lock); + +free: + ath11k_fw_stats_pdevs_free(&stats.pdevs); + ath11k_fw_stats_vdevs_free(&stats.vdevs); + ath11k_fw_stats_bcn_free(&stats.bcn); +} + +static int ath11k_debug_fw_stats_request(struct ath11k *ar, + struct stats_request_params *req_param) +{ + struct ath11k_base *ab = ar->ab; + unsigned long timeout, time_left; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + /* FW stats can get split when exceeding the stats data buffer limit. + * In that case, since there is no end marking for the back-to-back + * received 'update stats' event, we keep a 3 seconds timeout in case, + * fw_stats_done is not marked yet + */ + timeout = jiffies + msecs_to_jiffies(3 * HZ); + + ath11k_debug_fw_stats_reset(ar); + + reinit_completion(&ar->debug.fw_stats_complete); + + ret = ath11k_wmi_send_stats_request_cmd(ar, req_param); + + if (ret) { + ath11k_warn(ab, "could not request fw stats (%d)\n", + ret); + return ret; + } + + time_left = + wait_for_completion_timeout(&ar->debug.fw_stats_complete, + 1 * HZ); + if (!time_left) + return -ETIMEDOUT; + + for (;;) { + if (time_after(jiffies, timeout)) + break; + + spin_lock_bh(&ar->data_lock); + if (ar->debug.fw_stats_done) { + spin_unlock_bh(&ar->data_lock); + break; + } + spin_unlock_bh(&ar->data_lock); + } + return 0; +} + +static int ath11k_open_pdev_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct ath11k_base *ab = ar->ab; + struct stats_request_params req_param; + void *buf = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + req_param.pdev_id = ar->pdev->pdev_id; + req_param.vdev_id = 0; + req_param.stats_id = WMI_REQUEST_PDEV_STAT; + + ret = ath11k_debug_fw_stats_request(ar, &req_param); + if (ret) { + ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret); + goto err_free; + } + + ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, + buf); + + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_release_pdev_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath11k_read_pdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_pdev_stats = { + .open = ath11k_open_pdev_stats, + .release = ath11k_release_pdev_stats, + .read = ath11k_read_pdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_open_vdev_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct stats_request_params req_param; + void *buf = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + req_param.pdev_id = ar->pdev->pdev_id; + /* VDEV stats is always sent for all active VDEVs from FW */ + req_param.vdev_id = 0; + req_param.stats_id = WMI_REQUEST_VDEV_STAT; + + ret = ath11k_debug_fw_stats_request(ar, &req_param); + if (ret) { + ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret); + goto err_free; + } + + ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, + buf); + + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_release_vdev_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath11k_read_vdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_vdev_stats = { + .open = ath11k_open_vdev_stats, + .release = ath11k_release_vdev_stats, + .read = ath11k_read_vdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_open_bcn_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct ath11k_vif *arvif; + struct stats_request_params req_param; + void *buf = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + req_param.stats_id = WMI_REQUEST_BCN_STAT; + req_param.pdev_id = ar->pdev->pdev_id; + + /* loop all active VDEVs for bcn stats */ + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_up) + continue; + + req_param.vdev_id = arvif->vdev_id; + ret = ath11k_debug_fw_stats_request(ar, &req_param); + if (ret) { + ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret); + goto err_free; + } + } + + ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, + buf); + + /* since beacon stats request is looped for all active VDEVs, saved fw + * stats is not freed for each request until done for all active VDEVs + */ + spin_lock_bh(&ar->data_lock); + ath11k_fw_stats_bcn_free(&ar->debug.fw_stats.bcn); + spin_unlock_bh(&ar->data_lock); + + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_release_bcn_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath11k_read_bcn_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_bcn_stats = { + .open = ath11k_open_bcn_stats, + .release = ath11k_release_bcn_stats, + .read = ath11k_read_bcn_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_read_simulate_fw_crash(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char buf[] = + "To simulate firmware crash write one of the keywords to this file:\n" + "`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n" + "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n"; + + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +/* Simulate firmware crash: + * 'soft': Call wmi command causing firmware hang. This firmware hang is + * recoverable by warm firmware reset. + * 'hard': Force firmware crash by setting any vdev parameter for not allowed + * vdev id. This is hard firmware crash because it is recoverable only by cold + * firmware reset. + */ +static ssize_t ath11k_write_simulate_fw_crash(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k_base *ab = file->private_data; + struct ath11k_pdev *pdev; + struct ath11k *ar = ab->pdevs[0].ar; + char buf[32] = {0}; + ssize_t rc; + int i, ret, radioup = 0; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (ar && ar->state == ATH11K_STATE_ON) { + radioup = 1; + break; + } + } + /* filter partial writes and invalid commands */ + if (*ppos != 0 || count >= sizeof(buf) || count == 0) + return -EINVAL; + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (rc < 0) + return rc; + + /* drop the possible '\n' from the end */ + if (buf[*ppos - 1] == '\n') + buf[*ppos - 1] = '\0'; + + if (radioup == 0) { + ret = -ENETDOWN; + goto exit; + } + + if (!strcmp(buf, "assert")) { + ath11k_info(ab, "simulating firmware assert crash\n"); + ret = ath11k_wmi_force_fw_hang_cmd(ar, + ATH11K_WMI_FW_HANG_ASSERT_TYPE, + ATH11K_WMI_FW_HANG_DELAY); + } else { + ret = -EINVAL; + goto exit; + } + + if (ret) { + ath11k_warn(ab, "failed to simulate firmware crash: %d\n", ret); + goto exit; + } + + ret = count; + +exit: + return ret; +} + +static const struct file_operations fops_simulate_fw_crash = { + .read = ath11k_read_simulate_fw_crash, + .write = ath11k_write_simulate_fw_crash, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_write_enable_extd_tx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u32 filter; + int ret; + + if (kstrtouint_from_user(ubuf, count, 0, &filter)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + if (filter == ar->debug.extd_tx_stats) { + ret = count; + goto out; + } + + ar->debug.extd_tx_stats = filter; + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_enable_extd_tx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) + +{ + char buf[32] = {0}; + struct ath11k *ar = file->private_data; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%08x\n", + ar->debug.extd_tx_stats); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_extd_tx_stats = { + .read = ath11k_read_enable_extd_tx_stats, + .write = ath11k_write_enable_extd_tx_stats, + .open = simple_open +}; + +static ssize_t ath11k_write_extd_rx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 enable, rx_filter = 0, ring_id; + int ret; + + if (kstrtouint_from_user(ubuf, count, 0, &enable)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto exit; + } + + if (enable > 1) { + ret = -EINVAL; + goto exit; + } + + if (enable == ar->debug.extd_rx_stats) { + ret = count; + goto exit; + } + + if (enable) { + rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE; + + tlv_filter.rx_filter = rx_filter; + tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | + HTT_RX_FP_DATA_FILTER_FLASG3; + } else { + tlv_filter = ath11k_mac_mon_status_filter_default; + } + + ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); + + if (ret) { + ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); + goto exit; + } + + ar->debug.extd_rx_stats = enable; + ret = count; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_extd_rx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + ar->debug.extd_rx_stats); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_extd_rx_stats = { + .read = ath11k_read_extd_rx_stats, + .write = ath11k_write_extd_rx_stats, + .open = simple_open, +}; + +static ssize_t ath11k_debug_dump_soc_rx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k_base *ab = file->private_data; + struct ath11k_soc_dp_rx_stats *soc_stats = &ab->soc_stats; + int len = 0, i, retval; + const int size = 4096; + static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = { + "Overflow", "MPDU len", "FCS", "Decrypt", "TKIP MIC", + "Unencrypt", "MSDU len", "MSDU limit", "WiFi parse", + "AMSDU parse", "SA timeout", "DA timeout", + "Flow timeout", "Flush req"}; + static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = { + "Desc addr zero", "Desc inval", "AMPDU in non BA", + "Non BA dup", "BA dup", "Frame 2k jump", "BAR 2k jump", + "Frame OOR", "BAR OOR", "No BA session", + "Frame SN equal SSN", "PN check fail", "2k err", + "PN err", "Desc blocked"}; + + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n"); + len += scnprintf(buf + len, size - len, "err ring pkts: %u\n", + soc_stats->err_ring_pkts); + len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n", + soc_stats->invalid_rbm); + len += scnprintf(buf + len, size - len, "RXDMA errors:\n"); + for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + rxdma_err[i], soc_stats->rxdma_error[i]); + + len += scnprintf(buf + len, size - len, "\nREO errors:\n"); + for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + reo_err[i], soc_stats->reo_error[i]); + + len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n"); + len += scnprintf(buf + len, size - len, + "ring0: %u\nring1: %u\nring2: %u\nring3: %u\n", + soc_stats->hal_reo_error[0], + soc_stats->hal_reo_error[1], + soc_stats->hal_reo_error[2], + soc_stats->hal_reo_error[3]); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_soc_rx_stats = { + .read = ath11k_debug_dump_soc_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath11k_debug_pdev_create(struct ath11k_base *ab) +{ + ab->debugfs_soc = debugfs_create_dir(ab->hw_params.name, ab->debugfs_ath11k); + + if (IS_ERR_OR_NULL(ab->debugfs_soc)) { + if (IS_ERR(ab->debugfs_soc)) + return PTR_ERR(ab->debugfs_soc); + return -ENOMEM; + } + + debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab, + &fops_simulate_fw_crash); + + debugfs_create_file("soc_rx_stats", 0600, ab->debugfs_soc, ab, + &fops_soc_rx_stats); + + return 0; +} + +void ath11k_debug_pdev_destroy(struct ath11k_base *ab) +{ + debugfs_remove_recursive(ab->debugfs_ath11k); + ab->debugfs_ath11k = NULL; +} + +int ath11k_debug_soc_create(struct ath11k_base *ab) +{ + ab->debugfs_ath11k = debugfs_create_dir("ath11k", NULL); + + if (IS_ERR_OR_NULL(ab->debugfs_ath11k)) { + if (IS_ERR(ab->debugfs_ath11k)) + return PTR_ERR(ab->debugfs_ath11k); + return -ENOMEM; + } + + return 0; +} + +void ath11k_debug_soc_destroy(struct ath11k_base *ab) +{ + debugfs_remove_recursive(ab->debugfs_soc); + ab->debugfs_soc = NULL; +} + +void ath11k_debug_fw_stats_init(struct ath11k *ar) +{ + struct dentry *fwstats_dir = debugfs_create_dir("fw_stats", + ar->debug.debugfs_pdev); + + ar->debug.fw_stats.debugfs_fwstats = fwstats_dir; + + /* all stats debugfs files created are under "fw_stats" directory + * created per PDEV + */ + debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar, + &fops_pdev_stats); + debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar, + &fops_vdev_stats); + debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar, + &fops_bcn_stats); + + INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs); + INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs); + INIT_LIST_HEAD(&ar->debug.fw_stats.bcn); + + init_completion(&ar->debug.fw_stats_complete); +} + +static ssize_t ath11k_write_pktlog_filter(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 rx_filter = 0, ring_id, filter, mode; + u8 buf[128] = {0}; + int ret; + ssize_t rc; + + mutex_lock(&ar->conf_mutex); + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); + if (rc < 0) { + ret = rc; + goto out; + } + buf[rc] = '\0'; + + ret = sscanf(buf, "0x%x %u", &filter, &mode); + if (ret != 2) { + ret = -EINVAL; + goto out; + } + + if (filter) { + ret = ath11k_wmi_pdev_pktlog_enable(ar, filter); + if (ret) { + ath11k_warn(ar->ab, + "failed to enable pktlog filter %x: %d\n", + ar->debug.pktlog_filter, ret); + goto out; + } + } else { + ret = ath11k_wmi_pdev_pktlog_disable(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to disable pktlog: %d\n", ret); + goto out; + } + } + +#define HTT_RX_FILTER_TLV_LITE_MODE \ + (HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_START) + + if (mode == ATH11K_PKTLOG_MODE_FULL) { + rx_filter = HTT_RX_FILTER_TLV_LITE_MODE | + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | + HTT_RX_FILTER_TLV_FLAGS_ATTENTION; + } else if (mode == ATH11K_PKTLOG_MODE_LITE) { + ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar, + HTT_PPDU_STATS_TAG_PKTLOG); + if (ret) { + ath11k_err(ar->ab, "failed to enable pktlog lite: %d\n", ret); + goto out; + } + + rx_filter = HTT_RX_FILTER_TLV_LITE_MODE; + } else { + ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar, + HTT_PPDU_STATS_TAG_DEFAULT); + if (ret) { + ath11k_err(ar->ab, "failed to send htt ppdu stats req: %d\n", + ret); + goto out; + } + } + + tlv_filter.rx_filter = rx_filter; + if (rx_filter) { + tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | + HTT_RX_FP_DATA_FILTER_FLASG3; + } + + ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); + if (ret) { + ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); + goto out; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "pktlog filter %d mode %s\n", + filter, ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite")); + + ar->debug.pktlog_filter = filter; + ar->debug.pktlog_mode = mode; + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_pktlog_filter(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) + +{ + char buf[32] = {0}; + struct ath11k *ar = file->private_data; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%08x %08x\n", + ar->debug.pktlog_filter, + ar->debug.pktlog_mode); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_pktlog_filter = { + .read = ath11k_read_pktlog_filter, + .write = ath11k_write_pktlog_filter, + .open = simple_open +}; + +static ssize_t ath11k_write_simulate_radar(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + int ret; + + ret = ath11k_wmi_simulate_radar(ar); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_simulate_radar = { + .write = ath11k_write_simulate_radar, + .open = simple_open +}; + +int ath11k_debug_register(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + char pdev_name[5]; + char buf[100] = {0}; + + snprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx); + + ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc); + + if (IS_ERR_OR_NULL(ar->debug.debugfs_pdev)) { + if (IS_ERR(ar->debug.debugfs_pdev)) + return PTR_ERR(ar->debug.debugfs_pdev); + + return -ENOMEM; + } + + /* Create a symlink under ieee80211/phy* */ + snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev); + debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf); + + ath11k_debug_htt_stats_init(ar); + + ath11k_debug_fw_stats_init(ar); + + debugfs_create_file("ext_tx_stats", 0644, + ar->debug.debugfs_pdev, ar, + &fops_extd_tx_stats); + debugfs_create_file("ext_rx_stats", 0644, + ar->debug.debugfs_pdev, ar, + &fops_extd_rx_stats); + debugfs_create_file("pktlog_filter", 0644, + ar->debug.debugfs_pdev, ar, + &fops_pktlog_filter); + + if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) { + debugfs_create_file("dfs_simulate_radar", 0200, + ar->debug.debugfs_pdev, ar, + &fops_simulate_radar); + debugfs_create_bool("dfs_block_radar_events", 0200, + ar->debug.debugfs_pdev, + &ar->dfs_block_radar_events); + } + + return 0; +} + +void ath11k_debug_unregister(struct ath11k *ar) +{ +} +#endif /* CONFIG_ATH11K_DEBUGFS */ diff --git a/drivers/net/wireless/ath/ath11k/debug.h b/drivers/net/wireless/ath/ath11k/debug.h new file mode 100644 index 000000000000..8e8d5588b541 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef _ATH11K_DEBUG_H_ +#define _ATH11K_DEBUG_H_ + +#include "hal_tx.h" +#include "trace.h" + +#define ATH11K_TX_POWER_MAX_VAL 70 +#define ATH11K_TX_POWER_MIN_VAL 0 + +enum ath11k_debug_mask { + ATH11K_DBG_AHB = 0x00000001, + ATH11K_DBG_WMI = 0x00000002, + ATH11K_DBG_HTC = 0x00000004, + ATH11K_DBG_DP_HTT = 0x00000008, + ATH11K_DBG_MAC = 0x00000010, + ATH11K_DBG_BOOT = 0x00000020, + ATH11K_DBG_QMI = 0x00000040, + ATH11K_DBG_DATA = 0x00000080, + ATH11K_DBG_MGMT = 0x00000100, + ATH11K_DBG_REG = 0x00000200, + ATH11K_DBG_TESTMODE = 0x00000400, + ATH11k_DBG_HAL = 0x00000800, + ATH11K_DBG_ANY = 0xffffffff, +}; + +/* htt_dbg_ext_stats_type */ +enum ath11k_dbg_htt_ext_stats_type { + ATH11K_DBG_HTT_EXT_STATS_RESET = 0, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX = 1, + ATH11K_DBG_HTT_EXT_STATS_PDEV_RX = 2, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_HWQ = 3, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, + ATH11K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, + ATH11K_DBG_HTT_EXT_STATS_TQM_CMDQ = 7, + ATH11K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9, + ATH11K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10, + ATH11K_DBG_HTT_EXT_STATS_PEER_INFO = 11, + ATH11K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, + ATH11K_DBG_HTT_EXT_STATS_TX_MU_HWQ = 13, + ATH11K_DBG_HTT_EXT_STATS_RING_IF_INFO = 14, + ATH11K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, + ATH11K_DBG_HTT_EXT_STATS_SFM_INFO = 16, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, + ATH11K_DBG_HTT_EXT_STATS_ACTIVE_PEERS_LIST = 18, + ATH11K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, + 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, + + /* keep this last */ + ATH11K_DBG_HTT_NUM_EXT_STATS, +}; + +struct debug_htt_stats_req { + bool done; + u8 pdev_id; + u8 type; + u8 peer_addr[ETH_ALEN]; + struct completion cmpln; + u32 buf_len; + u8 buf[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, + ATH11K_PKTLOG_RCFIND = 0x000000004, + ATH11K_PKTLOG_RCUPDATE = 0x000000008, + ATH11K_PKTLOG_EVENT_SMART_ANT = 0x000000020, + ATH11K_PKTLOG_EVENT_SW = 0x000000040, + ATH11K_PKTLOG_ANY = 0x00000006f, +}; + +enum ath11k_pktlog_mode { + ATH11K_PKTLOG_MODE_LITE = 1, + ATH11K_PKTLOG_MODE_FULL = 2, +}; + +enum ath11k_pktlog_enum { + ATH11K_PKTLOG_TYPE_TX_CTRL = 1, + ATH11K_PKTLOG_TYPE_TX_STAT = 2, + ATH11K_PKTLOG_TYPE_TX_MSDU_ID = 3, + ATH11K_PKTLOG_TYPE_RX_STAT = 5, + ATH11K_PKTLOG_TYPE_RC_FIND = 6, + ATH11K_PKTLOG_TYPE_RC_UPDATE = 7, + ATH11K_PKTLOG_TYPE_TX_VIRT_ADDR = 8, + ATH11K_PKTLOG_TYPE_RX_CBF = 10, + ATH11K_PKTLOG_TYPE_RX_STATBUF = 22, + ATH11K_PKTLOG_TYPE_PPDU_STATS = 23, + ATH11K_PKTLOG_TYPE_LITE_RX = 24, +}; + +__printf(2, 3) void ath11k_info(struct ath11k_base *ab, const char *fmt, ...); +__printf(2, 3) void ath11k_err(struct ath11k_base *ab, const char *fmt, ...); +__printf(2, 3) void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...); + +extern unsigned int ath11k_debug_mask; + +#ifdef CONFIG_ATH11K_DEBUG +__printf(3, 4) void __ath11k_dbg(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *fmt, ...); +void ath11k_dbg_dump(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len); +#else /* CONFIG_ATH11K_DEBUG */ +static inline int __ath11k_dbg(struct ath11k_base *ab, + enum ath11k_debug_mask dbg_mask, + const char *fmt, ...) +{ + return 0; +} + +static inline void ath11k_dbg_dump(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ +} +#endif /* CONFIG_ATH11K_DEBUG */ + +#ifdef CONFIG_ATH11K_DEBUGFS +int ath11k_debug_soc_create(struct ath11k_base *ab); +void ath11k_debug_soc_destroy(struct ath11k_base *ab); +int ath11k_debug_pdev_create(struct ath11k_base *ab); +void ath11k_debug_pdev_destroy(struct ath11k_base *ab); +int ath11k_debug_register(struct ath11k *ar); +void ath11k_debug_unregister(struct ath11k *ar); +void ath11k_dbg_htt_ext_stats_handler(struct ath11k_base *ab, + struct sk_buff *skb); +void ath11k_debug_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb); + +void ath11k_debug_fw_stats_init(struct ath11k *ar); +int ath11k_dbg_htt_stats_req(struct ath11k *ar); + +static inline bool ath11k_debug_is_pktlog_lite_mode_enabled(struct ath11k *ar) +{ + return (ar->debug.pktlog_mode == ATH11K_PKTLOG_MODE_LITE); +} + +static inline bool ath11k_debug_is_pktlog_rx_stats_enabled(struct ath11k *ar) +{ + return (!ar->debug.pktlog_peer_valid && ar->debug.pktlog_mode); +} + +static inline bool ath11k_debug_is_pktlog_peer_valid(struct ath11k *ar, u8 *addr) +{ + return (ar->debug.pktlog_peer_valid && ar->debug.pktlog_mode && + ether_addr_equal(addr, ar->debug.pktlog_peer_addr)); +} + +static inline int ath11k_debug_is_extd_tx_stats_enabled(struct ath11k *ar) +{ + return ar->debug.extd_tx_stats; +} + +static inline int ath11k_debug_is_extd_rx_stats_enabled(struct ath11k *ar) +{ + return ar->debug.extd_rx_stats; +} + +void ath11k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir); +void +ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx); +void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts); +#else +static inline int ath11k_debug_soc_create(struct ath11k_base *ab) +{ + return 0; +} + +static inline void ath11k_debug_soc_destroy(struct ath11k_base *ab) +{ +} + +static inline int ath11k_debug_pdev_create(struct ath11k_base *ab) +{ + return 0; +} + +static inline void ath11k_debug_pdev_destroy(struct ath11k_base *ab) +{ +} + +static inline int ath11k_debug_register(struct ath11k *ar) +{ + return 0; +} + +static inline void ath11k_debug_unregister(struct ath11k *ar) +{ +} + +static inline void ath11k_dbg_htt_ext_stats_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ +} + +static inline void ath11k_debug_fw_stats_process(struct ath11k_base *ab, + struct sk_buff *skb) +{ +} + +static inline void ath11k_debug_fw_stats_init(struct ath11k *ar) +{ +} + +static inline int ath11k_debug_is_extd_tx_stats_enabled(struct ath11k *ar) +{ + return 0; +} + +static inline int ath11k_debug_is_extd_rx_stats_enabled(struct ath11k *ar) +{ + return 0; +} + +static inline int ath11k_dbg_htt_stats_req(struct ath11k *ar) +{ + return 0; +} + +static inline bool ath11k_debug_is_pktlog_lite_mode_enabled(struct ath11k *ar) +{ + return false; +} + +static inline bool ath11k_debug_is_pktlog_rx_stats_enabled(struct ath11k *ar) +{ + return false; +} + +static inline bool ath11k_debug_is_pktlog_peer_valid(struct ath11k *ar, u8 *addr) +{ + return false; +} + +static inline void +ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx) +{ +} + +static inline void +ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ +} + +#endif /* CONFIG_MAC80211_DEBUGFS*/ + +#define ath11k_dbg(ar, dbg_mask, fmt, ...) \ +do { \ + if (ath11k_debug_mask & dbg_mask) \ + __ath11k_dbg(ar, dbg_mask, fmt, ##__VA_ARGS__); \ +} while (0) + +#endif /* _ATH11K_DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.c b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c new file mode 100644 index 000000000000..9939e909628f --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c @@ -0,0 +1,4570 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/vmalloc.h> +#include "core.h" +#include "dp_tx.h" +#include "dp_rx.h" +#include "debug.h" +#include "debug_htt_stats.h" + +#define HTT_DBG_OUT(buf, len, fmt, ...) \ + scnprintf(buf, len, fmt "\n", ##__VA_ARGS__) + +#define HTT_MAX_STRING_LEN 256 +#define HTT_MAX_PRINT_CHAR_PER_ELEM 15 + +#define HTT_TLV_HDR_LEN 4 + +#define ARRAY_TO_STRING(out, arr, len) \ + do { \ + int index = 0; u8 i; \ + for (i = 0; i < len; i++) { \ + index += snprintf(out + index, HTT_MAX_STRING_LEN - index, \ + " %u:%u,", i, arr[i]); \ + if (index < 0 || index >= HTT_MAX_STRING_LEN) \ + break; \ + } \ + } while (0) + +static inline void htt_print_stats_string_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_stats_string_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i; + u16 index = 0; + char data[HTT_MAX_STRING_LEN] = {0}; + + tag_len = tag_len >> 2; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_STATS_STRING_TLV:"); + + for (i = 0; i < tag_len; i++) { + index += snprintf(&data[index], + HTT_MAX_STRING_LEN - index, + "%.*s", 4, (char *)&(htt_stats_buf->data[i])); + if (index >= HTT_MAX_STRING_LEN) + break; + } + + len += HTT_DBG_OUT(buf + len, buf_len - len, "data = %s\n", data); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_pdev_stats_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_cmn_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_PDEV_STATS_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_queued = %u", + htt_stats_buf->hw_queued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_reaped = %u", + htt_stats_buf->hw_reaped); + len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun = %u", + htt_stats_buf->underrun); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_paused = %u", + htt_stats_buf->hw_paused); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_flush = %u", + htt_stats_buf->hw_flush); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_filt = %u", + htt_stats_buf->hw_filt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort = %u", + htt_stats_buf->tx_abort); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_requeued = %u", + htt_stats_buf->mpdu_requed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_xretry = %u", + htt_stats_buf->tx_xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "data_rc = %u", + htt_stats_buf->data_rc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_dropped_xretry = %u", + htt_stats_buf->mpdu_dropped_xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "illegal_rate_phy_err = %u", + htt_stats_buf->illgl_rate_phy_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cont_xretry = %u", + htt_stats_buf->cont_xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_timeout = %u", + htt_stats_buf->tx_timeout); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_resets = %u", + htt_stats_buf->pdev_resets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_underrun = %u", + htt_stats_buf->phy_underrun); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txop_ovf = %u", + htt_stats_buf->txop_ovf); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_posted = %u", + htt_stats_buf->seq_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_failed_queueing = %u", + htt_stats_buf->seq_failed_queueing); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_completed = %u", + htt_stats_buf->seq_completed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_restarted = %u", + htt_stats_buf->seq_restarted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_seq_posted = %u", + htt_stats_buf->mu_seq_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_switch_hw_paused = %u", + htt_stats_buf->seq_switch_hw_paused); + len += HTT_DBG_OUT(buf + len, buf_len - len, "next_seq_posted_dsr = %u", + htt_stats_buf->next_seq_posted_dsr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_posted_isr = %u", + htt_stats_buf->seq_posted_isr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_ctrl_cached = %u", + htt_stats_buf->seq_ctrl_cached); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_count_tqm = %u", + htt_stats_buf->mpdu_count_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_count_tqm = %u", + htt_stats_buf->msdu_count_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_removed_tqm = %u", + htt_stats_buf->mpdu_removed_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_removed_tqm = %u", + htt_stats_buf->msdu_removed_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_sw_flush = %u", + htt_stats_buf->mpdus_sw_flush); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_hw_filter = %u", + htt_stats_buf->mpdus_hw_filter); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_truncated = %u", + htt_stats_buf->mpdus_truncated); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_ack_failed = %u", + htt_stats_buf->mpdus_ack_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_expired = %u", + htt_stats_buf->mpdus_expired); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_seq_hw_retry = %u", + htt_stats_buf->mpdus_seq_hw_retry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", + htt_stats_buf->ack_tlv_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "coex_abort_mpdu_cnt_valid = %u", + htt_stats_buf->coex_abort_mpdu_cnt_valid); + len += HTT_DBG_OUT(buf + len, buf_len - len, "coex_abort_mpdu_cnt = %u", + htt_stats_buf->coex_abort_mpdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_total_ppdus_tried_ota = %u", + htt_stats_buf->num_total_ppdus_tried_ota); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_tried_ota = %u", + htt_stats_buf->num_data_ppdus_tried_ota); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_ctrl_mgmt_enqued = %u", + htt_stats_buf->local_ctrl_mgmt_enqued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_ctrl_mgmt_freed = %u", + htt_stats_buf->local_ctrl_mgmt_freed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_data_enqued = %u", + htt_stats_buf->local_data_enqued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_data_freed = %u", + htt_stats_buf->local_data_freed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_tried = %u", + htt_stats_buf->mpdu_tried); + len += HTT_DBG_OUT(buf + len, buf_len - len, "isr_wait_seq_posted = %u", + htt_stats_buf->isr_wait_seq_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_active_dur_us_low = %u", + htt_stats_buf->tx_active_dur_us_low); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_active_dur_us_high = %u\n", + htt_stats_buf->tx_active_dur_us_high); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_urrn_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_urrn_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char urrn_stats[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_URRN_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_URRN_TLV_V:"); + + ARRAY_TO_STRING(urrn_stats, htt_stats_buf->urrn_stats, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "urrn_stats = %s\n", urrn_stats); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_flush_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_flush_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char flush_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_FLUSH_REASON_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_FLUSH_TLV_V:"); + + ARRAY_TO_STRING(flush_errs, htt_stats_buf->flush_errs, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_errs = %s\n", flush_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_sifs_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_sifs_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sifs_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_SIFS_TLV_V:"); + + ARRAY_TO_STRING(sifs_status, htt_stats_buf->sifs_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_status = %s\n", + sifs_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_phy_err_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_phy_err_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char phy_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_PHY_ERR_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_PHY_ERR_TLV_V:"); + + ARRAY_TO_STRING(phy_errs, htt_stats_buf->phy_errs, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_sifs_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_sifs_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sifs_hist_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SIFS_HIST_TLV_V:"); + + ARRAY_TO_STRING(sifs_hist_status, htt_stats_buf->sifs_hist_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_hist_status = %s\n", + sifs_hist_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_tx_ppdu_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_tx_ppdu_stats_tlv_v *htt_stats_buf = tag_buf; + 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, + "HTT_TX_PDEV_STATS_TX_PPDU_STATS_TLV_V:"); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_legacy_su = %u", + htt_stats_buf->num_data_ppdus_legacy_su); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ac_su = %u", + htt_stats_buf->num_data_ppdus_ac_su); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ax_su = %u", + htt_stats_buf->num_data_ppdus_ax_su); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ac_su_txbf = %u", + htt_stats_buf->num_data_ppdus_ac_su_txbf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ax_su_txbf = %u\n", + htt_stats_buf->num_data_ppdus_ax_su_txbf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tried_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = ((tag_len - sizeof(htt_stats_buf->hist_bin_size)) >> 2); + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_TRIED_MPDU_CNT_HIST_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u", + htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(tried_mpdu_cnt_hist, + htt_stats_buf->tried_mpdu_cnt_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tried_mpdu_cnt_hist = %s\n", + tried_mpdu_cnt_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER\n"); + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_hw_stats_intr_misc_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_intr_misc_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char hw_intr_name[HTT_STATS_MAX_HW_INTR_NAME_LEN + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_INTR_MISC_TLV:"); + memcpy(hw_intr_name, &(htt_stats_buf->hw_intr_name[0]), + HTT_STATS_MAX_HW_INTR_NAME_LEN); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_intr_name = %s ", hw_intr_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mask = %u", + htt_stats_buf->mask); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u\n", + htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_hw_stats_wd_timeout_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_wd_timeout_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char hw_module_name[HTT_STATS_MAX_HW_MODULE_NAME_LEN + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_WD_TIMEOUT_TLV:"); + memcpy(hw_module_name, &(htt_stats_buf->hw_module_name[0]), + HTT_STATS_MAX_HW_MODULE_NAME_LEN); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_module_name = %s ", + hw_module_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u", + htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_hw_stats_pdev_errs_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_pdev_errs_tlv *htt_stats_buf = tag_buf; + 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, "HTT_HW_STATS_PDEV_ERRS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort = %u", + htt_stats_buf->tx_abort); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort_fail_count = %u", + htt_stats_buf->tx_abort_fail_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_abort = %u", + htt_stats_buf->rx_abort); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_abort_fail_count = %u", + htt_stats_buf->rx_abort_fail_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "warm_reset = %u", + htt_stats_buf->warm_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cold_reset = %u", + htt_stats_buf->cold_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_flush = %u", + htt_stats_buf->tx_flush); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_glb_reset = %u", + htt_stats_buf->tx_glb_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_txq_reset = %u", + htt_stats_buf->tx_txq_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_timeout_reset = %u\n", + htt_stats_buf->rx_timeout_reset); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_msdu_flow_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_msdu_flow_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_MSDU_FLOW_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_update_timestamp = %u", + htt_stats_buf->last_update_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_add_timestamp = %u", + htt_stats_buf->last_add_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_remove_timestamp = %u", + htt_stats_buf->last_remove_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_processed_msdu_count = %u", + htt_stats_buf->total_processed_msdu_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cur_msdu_count_in_flowq = %u", + htt_stats_buf->cur_msdu_count_in_flowq); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_flow_no = %u", + htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xF0000) >> + 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "drop_rule = %u", + (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0x100000) >> + 20); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_enqueue_count = %u", + htt_stats_buf->last_cycle_enqueue_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_dequeue_count = %u", + htt_stats_buf->last_cycle_dequeue_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_drop_count = %u", + htt_stats_buf->last_cycle_drop_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "current_drop_th = %u\n", + htt_stats_buf->current_drop_th); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tid_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tid_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TID_STATS_TLV:"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sched_pending = %u", + htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ppdu_in_hwq = %u", + (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & + 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_flags = 0x%x", + htt_stats_buf->tid_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_queued = %u", + htt_stats_buf->hw_queued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_reaped = %u", + htt_stats_buf->hw_reaped); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_hw_filter = %u", + htt_stats_buf->mpdus_hw_filter); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_bytes = %u", + htt_stats_buf->qdepth_bytes); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_msdu = %u", + htt_stats_buf->qdepth_num_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_mpdu = %u", + htt_stats_buf->qdepth_num_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_scheduled_tsmp = %u", + htt_stats_buf->last_scheduled_tsmp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_module_id = %u", + htt_stats_buf->pause_module_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "block_module_id = %u\n", + htt_stats_buf->block_module_id); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tid_stats_v1_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tid_stats_v1_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TID_STATS_V1_TLV:"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sched_pending = %u", + htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ppdu_in_hwq = %u", + (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & + 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_flags = 0x%x", + htt_stats_buf->tid_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "max_qdepth_bytes = %u", + htt_stats_buf->max_qdepth_bytes); + len += HTT_DBG_OUT(buf + len, buf_len - len, "max_qdepth_n_msdus = %u", + htt_stats_buf->max_qdepth_n_msdus); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rsvd = %u", + htt_stats_buf->rsvd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_bytes = %u", + htt_stats_buf->qdepth_bytes); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_msdu = %u", + htt_stats_buf->qdepth_num_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_mpdu = %u", + htt_stats_buf->qdepth_num_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_scheduled_tsmp = %u", + htt_stats_buf->last_scheduled_tsmp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_module_id = %u", + htt_stats_buf->pause_module_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "block_module_id = %u", + htt_stats_buf->block_module_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "allow_n_flags = 0x%x", + htt_stats_buf->allow_n_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sendn_frms_allowed = %u\n", + htt_stats_buf->sendn_frms_allowed); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_tid_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_tid_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_TID_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_in_reorder = %u", + htt_stats_buf->dup_in_reorder); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_past_outside_window = %u", + htt_stats_buf->dup_past_outside_window); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_past_within_window = %u", + htt_stats_buf->dup_past_within_window); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rxdesc_err_decrypt = %u\n", + htt_stats_buf->rxdesc_err_decrypt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_counter_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_counter_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char counter_name[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_COUNTER_TLV:"); + + ARRAY_TO_STRING(counter_name, + htt_stats_buf->counter_name, + HTT_MAX_COUNTER_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "counter_name = %s ", counter_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u\n", + htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_peer_stats_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_peer_stats_cmn_tlv *htt_stats_buf = tag_buf; + 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, "HTT_PEER_STATS_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ppdu_cnt = %u", + htt_stats_buf->ppdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt = %u", + htt_stats_buf->mpdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_cnt = %u", + htt_stats_buf->msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_bitmap = %u", + htt_stats_buf->pause_bitmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "block_bitmap = %u", + htt_stats_buf->block_bitmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_rssi = %d", + htt_stats_buf->rssi); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueued_count = %llu", + htt_stats_buf->peer_enqueued_count_low | + ((u64)htt_stats_buf->peer_enqueued_count_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dequeued_count = %llu", + htt_stats_buf->peer_dequeued_count_low | + ((u64)htt_stats_buf->peer_dequeued_count_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dropped_count = %llu", + htt_stats_buf->peer_dropped_count_low | + ((u64)htt_stats_buf->peer_dropped_count_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "transmitted_ppdu_bytes = %llu", + htt_stats_buf->ppdu_transmitted_bytes_low | + ((u64)htt_stats_buf->ppdu_transmitted_bytes_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ttl_removed_count = %u", + htt_stats_buf->peer_ttl_removed_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "inactive_time = %u\n", + htt_stats_buf->inactive_time); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_peer_details_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_peer_details_tlv *htt_stats_buf = tag_buf; + 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, "HTT_PEER_DETAILS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_type = %u", + htt_stats_buf->peer_type); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "vdev_id = %u", + htt_stats_buf->vdev_pdev_ast_idx & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_id = %u", + (htt_stats_buf->vdev_pdev_ast_idx & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ast_idx = %u", + (htt_stats_buf->vdev_pdev_ast_idx & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "mac_addr = %02x:%02x:%02x:%02x:%02x:%02x", + htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF, + (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF00) >> 8, + (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF0000) >> 16, + (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF000000) >> 24, + (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF), + (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_flags = 0x%x", + htt_stats_buf->peer_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qpeer_flags = 0x%x\n", + htt_stats_buf->qpeer_flags); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_peer_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_peer_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + char *tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS] = {NULL}; + u8 j; + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { + tx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + if (!tx_gi[j]) + goto fail; + } + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PEER_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_ldpc = %u", + htt_stats_buf->tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_rssi = %u", + htt_stats_buf->ack_rssi); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_su_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_su_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mu_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mu_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, + htt_stats_buf->tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, + htt_stats_buf->tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_stbc, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_pream, + HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], + htt_stats_buf->tx_gi[j], + HTT_TX_PEER_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, + htt_stats_buf->tx_dcm, + HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; + +fail: + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + kfree(tx_gi[j]); +} + +static inline void htt_print_rx_peer_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_peer_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; + char *rssi_chain[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS] = {NULL}; + char *rx_gi[HTT_RX_PEER_STATS_NUM_GI_COUNTERS] = {NULL}; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) { + rssi_chain[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + if (!rssi_chain[j]) + goto fail; + } + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) { + rx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + if (!rx_gi[j]) + goto fail; + } + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PEER_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "nsts = %u", + htt_stats_buf->nsts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ldpc = %u", + htt_stats_buf->rx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_mgmt = %u", + htt_stats_buf->rssi_mgmt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_data = %u", + htt_stats_buf->rssi_data); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_comb = %u", + htt_stats_buf->rssi_comb); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_dcm, + HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_stbc, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) { + ARRAY_TO_STRING(rssi_chain[j], htt_stats_buf->rssi_chain[j], + HTT_RX_PEER_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", + j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", + j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_pream, + HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s\n", str_buf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; + +fail: + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) + kfree(rssi_chain[j]); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) + kfree(rx_gi[j]); +} + +static inline void +htt_print_tx_hwq_mu_mimo_sch_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_mu_mimo_sch_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_HWQ_MU_MIMO_SCH_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_posted = %u", + htt_stats_buf->mu_mimo_sch_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_failed = %u", + htt_stats_buf->mu_mimo_sch_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n", + htt_stats_buf->mu_mimo_ppdu_posted); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_mu_mimo_mpdu_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_mu_mimo_mpdu_stats_tlv *htt_stats_buf = tag_buf; + 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, + "HTT_TX_HWQ_MU_MIMO_MPDU_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_queued_usr = %u", + htt_stats_buf->mu_mimo_mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_tried_usr = %u", + htt_stats_buf->mu_mimo_mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_failed_usr = %u", + htt_stats_buf->mu_mimo_mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_requeued_usr = %u", + htt_stats_buf->mu_mimo_mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_err_no_ba_usr = %u", + htt_stats_buf->mu_mimo_err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdu_underrun_usr = %u", + htt_stats_buf->mu_mimo_mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ampdu_underrun_usr = %u\n", + htt_stats_buf->mu_mimo_ampdu_underrun_usr); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_mu_mimo_cmn_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_mu_mimo_cmn_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_HWQ_MU_MIMO_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__hwq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwq_id = %u\n", + (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_stats_cmn_tlv(const void *tag_buf, struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_stats_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + /* TODO: HKDBG */ + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_STATS_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__hwq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwq_id = %u", + (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "xretry = %u", + htt_stats_buf->xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun_cnt = %u", + htt_stats_buf->underrun_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cnt = %u", + htt_stats_buf->flush_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "filt_cnt = %u", + htt_stats_buf->filt_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "null_mpdu_bmap = %u", + htt_stats_buf->null_mpdu_bmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "user_ack_failure = %u", + htt_stats_buf->user_ack_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", + htt_stats_buf->ack_tlv_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_id_proc = %u", + htt_stats_buf->sched_id_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "null_mpdu_tx_count = %u", + htt_stats_buf->null_mpdu_tx_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_bmap_not_recvd = %u", + htt_stats_buf->mpdu_bmap_not_recvd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_bar = %u", + htt_stats_buf->num_bar); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts = %u", + htt_stats_buf->rts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cts2self = %u", + htt_stats_buf->cts2self); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_null = %u", + htt_stats_buf->qos_null); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_tried_cnt = %u", + htt_stats_buf->mpdu_tried_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queued_cnt = %u", + htt_stats_buf->mpdu_queued_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_ack_fail_cnt = %u", + htt_stats_buf->mpdu_ack_fail_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_filt_cnt = %u", + htt_stats_buf->mpdu_filt_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "false_mpdu_ack_count = %u", + htt_stats_buf->false_mpdu_ack_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txq_timeout = %u\n", + htt_stats_buf->txq_timeout); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_difs_latency_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_difs_latency_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS); + char difs_latency_hist[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_DIFS_LATENCY_STATS_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hist_intvl = %u", + htt_stats_buf->hist_intvl); + + ARRAY_TO_STRING(difs_latency_hist, htt_stats_buf->difs_latency_hist, + data_len); + len += HTT_DBG_OUT(buf + len, buf_len - len, "difs_latency_hist = %s\n", + difs_latency_hist); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_cmd_result_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_cmd_result_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 data_len; + char cmd_result[HTT_MAX_STRING_LEN] = {0}; + + data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_RESULT_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_CMD_RESULT_STATS_TLV_V:"); + + ARRAY_TO_STRING(cmd_result, htt_stats_buf->cmd_result, data_len); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_result = %s\n", cmd_result); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_cmd_stall_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_cmd_stall_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 num_elems; + char cmd_stall_status[HTT_MAX_STRING_LEN] = {0}; + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_STALL_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_CMD_STALL_STATS_TLV_V:"); + + ARRAY_TO_STRING(cmd_stall_status, htt_stats_buf->cmd_stall_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_stall_status = %s\n", + cmd_stall_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_fes_result_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_fes_result_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 num_elems; + char fes_result[HTT_MAX_STRING_LEN] = {0}; + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_FES_RESULT_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_FES_RESULT_STATS_TLV_V:"); + + ARRAY_TO_STRING(fes_result, htt_stats_buf->fes_result, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fes_result = %s\n", fes_result); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_tried_mpdu_cnt_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_tried_mpdu_cnt_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tried_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = ((tag_len - + sizeof(htt_stats_buf->hist_bin_size)) >> 2); + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_TRIED_MPDU_CNT_HIST_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u", + htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(tried_mpdu_cnt_hist, + htt_stats_buf->tried_mpdu_cnt_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "tried_mpdu_cnt_hist = %s\n", + tried_mpdu_cnt_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER "); + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_txop_used_cnt_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_txop_used_cnt_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char txop_used_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = tag_len >> 2; + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_TXOP_USED_CNT_HIST_TLV_V:"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(txop_used_cnt_hist, + htt_stats_buf->txop_used_cnt_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txop_used_cnt_hist = %s\n", + txop_used_cnt_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER "); + } + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_sounding_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + s32 i; + const struct htt_tx_sounding_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + const u32 *cbf_20 = htt_stats_buf->cbf_20; + const u32 *cbf_40 = htt_stats_buf->cbf_40; + const u32 *cbf_80 = htt_stats_buf->cbf_80; + const u32 *cbf_160 = htt_stats_buf->cbf_160; + + if (htt_stats_buf->tx_sounding_mode == HTT_TX_AC_SOUNDING_MODE) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "\nHTT_TX_AC_SOUNDING_STATS_TLV:\n"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u ", + cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u ", + i, + htt_stats_buf->sounding[0], + htt_stats_buf->sounding[1], + htt_stats_buf->sounding[2], + htt_stats_buf->sounding[3]); + } + } else if (htt_stats_buf->tx_sounding_mode == HTT_TX_AX_SOUNDING_MODE) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "\nHTT_TX_AX_SOUNDING_STATS_TLV:\n"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u ", + cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS; i++) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u ", + i, + htt_stats_buf->sounding[0], + htt_stats_buf->sounding[1], + htt_stats_buf->sounding[2], + htt_stats_buf->sounding[3]); + } + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_cmn_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_cmn_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_SELFGEN_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "su_bar = %u", + htt_stats_buf->su_bar); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts = %u", + htt_stats_buf->rts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cts2self = %u", + htt_stats_buf->cts2self); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_null = %u", + htt_stats_buf->qos_null); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_1 = %u", + htt_stats_buf->delayed_bar_1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_2 = %u", + htt_stats_buf->delayed_bar_2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_3 = %u", + htt_stats_buf->delayed_bar_3); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_4 = %u", + htt_stats_buf->delayed_bar_4); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_5 = %u", + htt_stats_buf->delayed_bar_5); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_6 = %u", + htt_stats_buf->delayed_bar_6); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_7 = %u\n", + htt_stats_buf->delayed_bar_7); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ac_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ac_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_SELFGEN_AC_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndpa = %u", + htt_stats_buf->ac_su_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndp = %u", + htt_stats_buf->ac_su_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndpa = %u", + htt_stats_buf->ac_mu_mimo_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndp = %u", + htt_stats_buf->ac_mu_mimo_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_1 = %u", + htt_stats_buf->ac_mu_mimo_brpoll_1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_2 = %u", + htt_stats_buf->ac_mu_mimo_brpoll_2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_3 = %u\n", + htt_stats_buf->ac_mu_mimo_brpoll_3); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ax_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ax_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_SELFGEN_AX_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndpa = %u", + htt_stats_buf->ax_su_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndp = %u", + htt_stats_buf->ax_su_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndpa = %u", + htt_stats_buf->ax_mu_mimo_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndp = %u", + htt_stats_buf->ax_mu_mimo_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_1 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_2 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_3 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_3); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_4 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_4); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_5 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_5); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_6 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_6); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_7 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_7); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_basic_trigger = %u", + htt_stats_buf->ax_basic_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_bsr_trigger = %u", + htt_stats_buf->ax_bsr_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_bar_trigger = %u", + htt_stats_buf->ax_mu_bar_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_rts_trigger = %u\n", + htt_stats_buf->ax_mu_rts_trigger); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ac_err_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ac_err_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_SELFGEN_AC_ERR_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndp_err = %u", + htt_stats_buf->ac_su_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndpa_err = %u", + htt_stats_buf->ac_su_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndpa_err = %u", + htt_stats_buf->ac_mu_mimo_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndp_err = %u", + htt_stats_buf->ac_mu_mimo_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp1_err = %u", + htt_stats_buf->ac_mu_mimo_brp1_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp2_err = %u", + htt_stats_buf->ac_mu_mimo_brp2_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp3_err = %u\n", + htt_stats_buf->ac_mu_mimo_brp3_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ax_err_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ax_err_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_SELFGEN_AX_ERR_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndp_err = %u", + htt_stats_buf->ax_su_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndpa_err = %u", + htt_stats_buf->ax_su_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndpa_err = %u", + htt_stats_buf->ax_mu_mimo_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndp_err = %u", + htt_stats_buf->ax_mu_mimo_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp1_err = %u", + htt_stats_buf->ax_mu_mimo_brp1_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp2_err = %u", + htt_stats_buf->ax_mu_mimo_brp2_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp3_err = %u", + htt_stats_buf->ax_mu_mimo_brp3_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp4_err = %u", + htt_stats_buf->ax_mu_mimo_brp4_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp5_err = %u", + htt_stats_buf->ax_mu_mimo_brp5_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp6_err = %u", + htt_stats_buf->ax_mu_mimo_brp6_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp7_err = %u", + htt_stats_buf->ax_mu_mimo_brp7_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_basic_trigger_err = %u", + htt_stats_buf->ax_basic_trigger_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_bsr_trigger_err = %u", + htt_stats_buf->ax_bsr_trigger_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_bar_trigger_err = %u", + htt_stats_buf->ax_mu_bar_trigger_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_rts_trigger_err = %u\n", + htt_stats_buf->ax_mu_rts_trigger_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_mu_mimo_sch_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_mu_mimo_sch_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_SCH_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_posted = %u", + htt_stats_buf->mu_mimo_sch_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_failed = %u", + htt_stats_buf->mu_mimo_sch_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n", + htt_stats_buf->mu_mimo_ppdu_posted); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "11ac MU_MIMO SCH STATS:"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_sch_nusers_%u = %u", + i, htt_stats_buf->ac_mu_mimo_sch_nusers[i]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "\n11ax MU_MIMO SCH STATS:"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_sch_nusers_%u = %u", + i, htt_stats_buf->ax_mu_mimo_sch_nusers[i]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "\n11ax OFDMA SCH STATS:"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_ofdma_sch_nusers_%u = %u", + i, htt_stats_buf->ax_ofdma_sch_nusers[i]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_mu_mimo_mpdu_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_MIMO_AC) { + if (!htt_stats_buf->user_index) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_AC_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < + HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_queued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_tried_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_failed_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_requeued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_err_no_ba_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdu_underrun_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_ampdu_underrun_usr_%u = %u\n", + htt_stats_buf->user_index, + htt_stats_buf->ampdu_underrun_usr); + } + } + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_MIMO_AX) { + if (!htt_stats_buf->user_index) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_AX_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < + HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_queued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_tried_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_failed_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_requeued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_err_no_ba_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdu_underrun_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_ampdu_underrun_usr_%u = %u\n", + htt_stats_buf->user_index, + htt_stats_buf->ampdu_underrun_usr); + } + } + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_OFDMA_AX) { + if (!htt_stats_buf->user_index) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_AX_MU_OFDMA_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_queued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_tried_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_failed_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_requeued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_err_no_ba_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdu_underrun_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_ampdu_underrun_usr_%u = %u\n", + htt_stats_buf->user_index, + htt_stats_buf->ampdu_underrun_usr); + } + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_cmd_posted_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_cmd_posted_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_cmd_posted[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_POSTED_TLV_V:"); + + ARRAY_TO_STRING(sched_cmd_posted, htt_stats_buf->sched_cmd_posted, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_posted = %s\n", + sched_cmd_posted); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_cmd_reaped_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_cmd_reaped_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_cmd_reaped[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_REAPED_TLV_V:"); + + ARRAY_TO_STRING(sched_cmd_reaped, htt_stats_buf->sched_cmd_reaped, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_reaped = %s\n", + sched_cmd_reaped); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_sched_order_su_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_sched_order_su_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_order_su[HTT_MAX_STRING_LEN] = {0}; + /* each entry is u32, i.e. 4 bytes */ + u32 sched_order_su_num_entries = + min_t(u32, (tag_len >> 2), HTT_TX_PDEV_NUM_SCHED_ORDER_LOG); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV_V:"); + + ARRAY_TO_STRING(sched_order_su, htt_stats_buf->sched_order_su, + sched_order_su_num_entries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_order_su = %s\n", + sched_order_su); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_sched_ineligibility_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_sched_ineligibility_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_ineligibility[HTT_MAX_STRING_LEN] = {0}; + /* each entry is u32, i.e. 4 bytes */ + u32 sched_ineligibility_num_entries = tag_len >> 2; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_INELIGIBILITY_V:"); + + ARRAY_TO_STRING(sched_ineligibility, htt_stats_buf->sched_ineligibility, + sched_ineligibility_num_entries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_ineligibility = %s\n", + sched_ineligibility); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_sched_per_txq_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_sched_per_txq_tlv *htt_stats_buf = tag_buf; + 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, + "HTT_TX_PDEV_STATS_SCHED_PER_TXQ_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__txq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txq_id = %u", + (htt_stats_buf->mac_id__txq_id__word & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_policy = %u", + htt_stats_buf->sched_policy); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "last_sched_cmd_posted_timestamp = %u", + htt_stats_buf->last_sched_cmd_posted_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "last_sched_cmd_compl_timestamp = %u", + htt_stats_buf->last_sched_cmd_compl_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_2_tac_lwm_count = %u", + htt_stats_buf->sched_2_tac_lwm_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_2_tac_ring_full = %u", + htt_stats_buf->sched_2_tac_ring_full); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_post_failure = %u", + htt_stats_buf->sched_cmd_post_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_active_tids = %u", + htt_stats_buf->num_active_tids); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ps_schedules = %u", + htt_stats_buf->num_ps_schedules); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmds_pending = %u", + htt_stats_buf->sched_cmds_pending); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tid_register = %u", + htt_stats_buf->num_tid_register); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tid_unregister = %u", + htt_stats_buf->num_tid_unregister); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_qstats_queried = %u", + htt_stats_buf->num_qstats_queried); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qstats_update_pending = %u", + htt_stats_buf->qstats_update_pending); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_qstats_query_timestamp = %u", + htt_stats_buf->last_qstats_query_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tqm_cmdq_full = %u", + htt_stats_buf->num_tqm_cmdq_full); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_de_sched_algo_trigger = %u", + htt_stats_buf->num_de_sched_algo_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_rt_sched_algo_trigger = %u", + htt_stats_buf->num_rt_sched_algo_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tqm_sched_algo_trigger = %u", + htt_stats_buf->num_tqm_sched_algo_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_sched = %u\n", + htt_stats_buf->notify_sched); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dur_based_sendn_term = %u\n", + htt_stats_buf->dur_based_sendn_term); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_stats_tx_sched_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_stats_tx_sched_cmn_tlv *htt_stats_buf = tag_buf; + 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, "HTT_STATS_TX_SCHED_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "current_timestamp = %u\n", + htt_stats_buf->current_timestamp); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_gen_mpdu_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_gen_mpdu_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char gen_mpdu_end_reason[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_GEN_MPDU_STATS_TLV_V:"); + + ARRAY_TO_STRING(gen_mpdu_end_reason, htt_stats_buf->gen_mpdu_end_reason, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_end_reason = %s\n", + gen_mpdu_end_reason); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_list_mpdu_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_list_mpdu_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char list_mpdu_end_reason[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_TQM_LIST_MPDU_STATS_TLV_V:"); + + ARRAY_TO_STRING(list_mpdu_end_reason, htt_stats_buf->list_mpdu_end_reason, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_end_reason = %s\n", + list_mpdu_end_reason); + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_list_mpdu_cnt_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_list_mpdu_cnt_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char list_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_CNT_TLV_V:"); + + ARRAY_TO_STRING(list_mpdu_cnt_hist, htt_stats_buf->list_mpdu_cnt_hist, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_cnt_hist = %s\n", + list_mpdu_cnt_hist); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_pdev_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_pdev_stats_tlv_v *htt_stats_buf = tag_buf; + 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, "HTT_TX_TQM_PDEV_STATS_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_count = %u", + htt_stats_buf->msdu_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_count = %u", + htt_stats_buf->mpdu_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu = %u", + htt_stats_buf->remove_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu = %u", + htt_stats_buf->remove_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_ttl = %u", + htt_stats_buf->remove_msdu_ttl); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_bar = %u", + htt_stats_buf->send_bar); + len += HTT_DBG_OUT(buf + len, buf_len - len, "bar_sync = %u", + htt_stats_buf->bar_sync); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu = %u", + htt_stats_buf->notify_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sync_cmd = %u", + htt_stats_buf->sync_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "write_cmd = %u", + htt_stats_buf->write_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_trigger = %u", + htt_stats_buf->hwsch_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", + htt_stats_buf->ack_tlv_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_cmd = %u", + htt_stats_buf->gen_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_list_cmd = %u", + htt_stats_buf->gen_list_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_cmd = %u", + htt_stats_buf->remove_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_tried_cmd = %u", + htt_stats_buf->remove_mpdu_tried_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u", + htt_stats_buf->mpdu_queue_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_head_info_cmd = %u", + htt_stats_buf->mpdu_head_info_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u", + htt_stats_buf->msdu_flow_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_cmd = %u", + htt_stats_buf->remove_msdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_ttl_cmd = %u", + htt_stats_buf->remove_msdu_ttl_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cache_cmd = %u", + htt_stats_buf->flush_cache_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "update_mpduq_cmd = %u", + htt_stats_buf->update_mpduq_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueue = %u", + htt_stats_buf->enqueue); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueue_notify = %u", + htt_stats_buf->enqueue_notify); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu_at_head = %u", + htt_stats_buf->notify_mpdu_at_head); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu_state_valid = %u", + htt_stats_buf->notify_mpdu_state_valid); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_udp_notify1 = %u", + htt_stats_buf->sched_udp_notify1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_udp_notify2 = %u", + htt_stats_buf->sched_udp_notify2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_nonudp_notify1 = %u", + htt_stats_buf->sched_nonudp_notify1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_nonudp_notify2 = %u\n", + htt_stats_buf->sched_nonudp_notify2); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tqm_cmn_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_cmn_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_TQM_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "max_cmdq_id = %u", + htt_stats_buf->max_cmdq_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_cnt_hist_intvl = %u", + htt_stats_buf->list_mpdu_cnt_hist_intvl); + len += HTT_DBG_OUT(buf + len, buf_len - len, "add_msdu = %u", + htt_stats_buf->add_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_empty = %u", + htt_stats_buf->q_empty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_not_empty = %u", + htt_stats_buf->q_not_empty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "drop_notification = %u", + htt_stats_buf->drop_notification); + len += HTT_DBG_OUT(buf + len, buf_len - len, "desc_threshold = %u\n", + htt_stats_buf->desc_threshold); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tqm_error_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_error_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_TQM_ERROR_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_empty_failure = %u", + htt_stats_buf->q_empty_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_not_empty_failure = %u", + htt_stats_buf->q_not_empty_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "add_msdu_failure = %u\n", + htt_stats_buf->add_msdu_failure); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tqm_cmdq_status_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_cmdq_status_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_TQM_CMDQ_STATUS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__cmdq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmdq_id = %u\n", + (htt_stats_buf->mac_id__cmdq_id__word & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sync_cmd = %u", + htt_stats_buf->sync_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "write_cmd = %u", + htt_stats_buf->write_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_cmd = %u", + htt_stats_buf->gen_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u", + htt_stats_buf->mpdu_queue_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_head_info_cmd = %u", + htt_stats_buf->mpdu_head_info_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u", + htt_stats_buf->msdu_flow_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_cmd = %u", + htt_stats_buf->remove_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_cmd = %u", + htt_stats_buf->remove_msdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cache_cmd = %u", + htt_stats_buf->flush_cache_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "update_mpduq_cmd = %u", + htt_stats_buf->update_mpduq_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "update_msduq_cmd = %u\n", + htt_stats_buf->update_msduq_cmd); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_eapol_packets_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_eapol_packets_stats_tlv *htt_stats_buf = tag_buf; + 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, + "HTT_TX_DE_EAPOL_PACKETS_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m1_packets = %u", + htt_stats_buf->m1_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m2_packets = %u", + htt_stats_buf->m2_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m3_packets = %u", + htt_stats_buf->m3_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m4_packets = %u", + htt_stats_buf->m4_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "g1_packets = %u", + htt_stats_buf->g1_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "g2_packets = %u\n", + htt_stats_buf->g2_packets); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_classify_failed_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_classify_failed_stats_tlv *htt_stats_buf = tag_buf; + 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, + "HTT_TX_DE_CLASSIFY_FAILED_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ap_bss_peer_not_found = %u", + htt_stats_buf->ap_bss_peer_not_found); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ap_bcast_mcast_no_peer = %u", + htt_stats_buf->ap_bcast_mcast_no_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sta_delete_in_progress = %u", + htt_stats_buf->sta_delete_in_progress); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ibss_no_bss_peer = %u", + htt_stats_buf->ibss_no_bss_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_vdev_type = %u", + htt_stats_buf->invalid_vdev_type); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_ast_peer_entry = %u", + htt_stats_buf->invalid_ast_peer_entry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_entry_invalid = %u", + htt_stats_buf->peer_entry_invalid); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ethertype_not_ip = %u", + htt_stats_buf->ethertype_not_ip); + len += HTT_DBG_OUT(buf + len, buf_len - len, "eapol_lookup_failed = %u", + htt_stats_buf->eapol_lookup_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qpeer_not_allow_data = %u", + htt_stats_buf->qpeer_not_allow_data); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_tid_override = %u", + htt_stats_buf->fse_tid_override); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ipv6_jumbogram_zero_length = %u", + htt_stats_buf->ipv6_jumbogram_zero_length); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_to_non_qos_in_prog = %u\n", + htt_stats_buf->qos_to_non_qos_in_prog); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_classify_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_classify_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_DE_CLASSIFY_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "arp_packets = %u", + htt_stats_buf->arp_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "igmp_packets = %u", + htt_stats_buf->igmp_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dhcp_packets = %u", + htt_stats_buf->dhcp_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "host_inspected = %u", + htt_stats_buf->host_inspected); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_included = %u", + htt_stats_buf->htt_included); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_mcs = %u", + htt_stats_buf->htt_valid_mcs); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_nss = %u", + htt_stats_buf->htt_valid_nss); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_preamble_type = %u", + htt_stats_buf->htt_valid_preamble_type); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_chainmask = %u", + htt_stats_buf->htt_valid_chainmask); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_guard_interval = %u", + htt_stats_buf->htt_valid_guard_interval); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_retries = %u", + htt_stats_buf->htt_valid_retries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_bw_info = %u", + htt_stats_buf->htt_valid_bw_info); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_power = %u", + htt_stats_buf->htt_valid_power); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_key_flags = 0x%x", + htt_stats_buf->htt_valid_key_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_no_encryption = %u", + htt_stats_buf->htt_valid_no_encryption); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_entry_count = %u", + htt_stats_buf->fse_entry_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_be = %u", + htt_stats_buf->fse_priority_be); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_high = %u", + htt_stats_buf->fse_priority_high); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_low = %u", + htt_stats_buf->fse_priority_low); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_be = %u", + htt_stats_buf->fse_traffic_ptrn_be); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_over_sub = %u", + htt_stats_buf->fse_traffic_ptrn_over_sub); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_bursty = %u", + htt_stats_buf->fse_traffic_ptrn_bursty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_interactive = %u", + htt_stats_buf->fse_traffic_ptrn_interactive); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_periodic = %u", + htt_stats_buf->fse_traffic_ptrn_periodic); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_alloc = %u", + htt_stats_buf->fse_hwqueue_alloc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_created = %u", + htt_stats_buf->fse_hwqueue_created); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_send_to_host = %u", + htt_stats_buf->fse_hwqueue_send_to_host); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mcast_entry = %u", + htt_stats_buf->mcast_entry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "bcast_entry = %u", + htt_stats_buf->bcast_entry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_update_peer_cache = %u", + htt_stats_buf->htt_update_peer_cache); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_learning_frame = %u", + htt_stats_buf->htt_learning_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_invalid_peer = %u", + htt_stats_buf->fse_invalid_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mec_notify = %u\n", + htt_stats_buf->mec_notify); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_classify_status_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_classify_status_stats_tlv *htt_stats_buf = tag_buf; + 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, + "HTT_TX_DE_CLASSIFY_STATUS_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "eok = %u", + htt_stats_buf->eok); + len += HTT_DBG_OUT(buf + len, buf_len - len, "classify_done = %u", + htt_stats_buf->classify_done); + len += HTT_DBG_OUT(buf + len, buf_len - len, "lookup_failed = %u", + htt_stats_buf->lookup_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_dhcp = %u", + htt_stats_buf->send_host_dhcp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_mcast = %u", + htt_stats_buf->send_host_mcast); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_unknown_dest = %u", + htt_stats_buf->send_host_unknown_dest); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host = %u", + htt_stats_buf->send_host); + len += HTT_DBG_OUT(buf + len, buf_len - len, "status_invalid = %u\n", + htt_stats_buf->status_invalid); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_enqueue_packets_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_enqueue_packets_stats_tlv *htt_stats_buf = tag_buf; + 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, + "HTT_TX_DE_ENQUEUE_PACKETS_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueued_pkts = %u", + htt_stats_buf->enqueued_pkts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "to_tqm = %u", + htt_stats_buf->to_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "to_tqm_bypass = %u\n", + htt_stats_buf->to_tqm_bypass); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_enqueue_discard_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_enqueue_discard_stats_tlv *htt_stats_buf = tag_buf; + 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, + "HTT_TX_DE_ENQUEUE_DISCARD_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "discarded_pkts = %u", + htt_stats_buf->discarded_pkts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_frames = %u", + htt_stats_buf->local_frames); + len += HTT_DBG_OUT(buf + len, buf_len - len, "is_ext_msdu = %u\n", + htt_stats_buf->is_ext_msdu); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_de_compl_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_compl_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_DE_COMPL_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl_dummy_frame = %u", + htt_stats_buf->tcl_dummy_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_dummy_frame = %u", + htt_stats_buf->tqm_dummy_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_notify_frame = %u", + htt_stats_buf->tqm_notify_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw2wbm_enq = %u", + htt_stats_buf->fw2wbm_enq); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_bypass_frame = %u\n", + htt_stats_buf->tqm_bypass_frame); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_fw2wbm_ring_full_hist_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_fw2wbm_ring_full_hist_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw2wbm_ring_full_hist[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = tag_len >> 2; + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_FW2WBM_RING_FULL_HIST_TLV"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(fw2wbm_ring_full_hist, + htt_stats_buf->fw2wbm_ring_full_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "fw2wbm_ring_full_hist = %s\n", + fw2wbm_ring_full_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER "); + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_cmn_stats_tlv(const void *tag_buf, struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_cmn_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_TX_DE_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl2fw_entry_count = %u", + htt_stats_buf->tcl2fw_entry_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "not_to_fw = %u", + htt_stats_buf->not_to_fw); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_pdev_vdev_peer = %u", + htt_stats_buf->invalid_pdev_vdev_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl_res_invalid_addrx = %u", + htt_stats_buf->tcl_res_invalid_addrx); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm2fw_entry_count = %u", + htt_stats_buf->wbm2fw_entry_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_pdev = %u\n", + htt_stats_buf->invalid_pdev); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_ring_if_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_ring_if_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char low_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; + char high_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RING_IF_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr = %u", + htt_stats_buf->base_addr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "elem_size = %u", + htt_stats_buf->elem_size); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_elems = %u", + htt_stats_buf->num_elems__prefetch_tail_idx & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "prefetch_tail_idx = %u", + (htt_stats_buf->num_elems__prefetch_tail_idx & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "head_idx = %u", + htt_stats_buf->head_idx__tail_idx & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tail_idx = %u", + (htt_stats_buf->head_idx__tail_idx & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "shadow_head_idx = %u", + htt_stats_buf->shadow_head_idx__shadow_tail_idx & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "shadow_tail_idx = %u", + (htt_stats_buf->shadow_head_idx__shadow_tail_idx & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tail_incr = %u", + htt_stats_buf->num_tail_incr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "lwm_thresh = %u", + htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwm_thresh = %u", + (htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "overrun_hit_count = %u", + htt_stats_buf->overrun_hit_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun_hit_count = %u", + htt_stats_buf->underrun_hit_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "prod_blockwait_count = %u", + htt_stats_buf->prod_blockwait_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cons_blockwait_count = %u", + htt_stats_buf->cons_blockwait_count); + + ARRAY_TO_STRING(low_wm_hit_count, htt_stats_buf->low_wm_hit_count, + HTT_STATS_LOW_WM_BINS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "low_wm_hit_count = %s ", + low_wm_hit_count); + + ARRAY_TO_STRING(high_wm_hit_count, htt_stats_buf->high_wm_hit_count, + HTT_STATS_HIGH_WM_BINS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "high_wm_hit_count = %s\n", + high_wm_hit_count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_ring_if_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_ring_if_cmn_tlv *htt_stats_buf = tag_buf; + 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, "HTT_RING_IF_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", + htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sfm_client_user_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sfm_client_user_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char dwords_used_by_user_n[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = tag_len >> 2; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CLIENT_USER_TLV_V:"); + + ARRAY_TO_STRING(dwords_used_by_user_n, + htt_stats_buf->dwords_used_by_user_n, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dwords_used_by_user_n = %s\n", + dwords_used_by_user_n); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sfm_client_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sfm_client_tlv *htt_stats_buf = tag_buf; + 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, "HTT_SFM_CLIENT_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "client_id = %u", + htt_stats_buf->client_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_min = %u", + htt_stats_buf->buf_min); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_max = %u", + htt_stats_buf->buf_max); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_busy = %u", + htt_stats_buf->buf_busy); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_alloc = %u", + htt_stats_buf->buf_alloc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_avail = %u", + htt_stats_buf->buf_avail); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_users = %u\n", + htt_stats_buf->num_users); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sfm_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sfm_cmn_tlv *htt_stats_buf = tag_buf; + 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, "HTT_SFM_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_total = %u", + htt_stats_buf->buf_total); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mem_empty = %u", + htt_stats_buf->mem_empty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "deallocate_bufs = %u", + htt_stats_buf->deallocate_bufs); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", + htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sring_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sring_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_SRING_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ring_id = %u", + (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "arena = %u", + (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ep = %u", + (htt_stats_buf->mac_id__ring_id__arena__ep & 0x1000000) >> 24); + len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr_lsb = 0x%x", + htt_stats_buf->base_addr_lsb); + len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr_msb = 0x%x", + htt_stats_buf->base_addr_msb); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ring_size = %u", + htt_stats_buf->ring_size); + len += HTT_DBG_OUT(buf + len, buf_len - len, "elem_size = %u", + htt_stats_buf->elem_size); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_avail_words = %u", + htt_stats_buf->num_avail_words__num_valid_words & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_valid_words = %u", + (htt_stats_buf->num_avail_words__num_valid_words & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "head_ptr = %u", + htt_stats_buf->head_ptr__tail_ptr & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tail_ptr = %u", + (htt_stats_buf->head_ptr__tail_ptr & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "consumer_empty = %u", + htt_stats_buf->consumer_empty__producer_full & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "producer_full = %u", + (htt_stats_buf->consumer_empty__producer_full & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "prefetch_count = %u", + htt_stats_buf->prefetch_count__internal_tail_ptr & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "internal_tail_ptr = %u\n", + (htt_stats_buf->prefetch_count__internal_tail_ptr & + 0xFFFF0000) >> 16); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sring_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sring_cmn_tlv *htt_stats_buf = tag_buf; + 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, "HTT_SRING_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", + htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_pdev_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + char *tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS] = {NULL}; + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { + tx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + if (!tx_gi[j]) + goto fail; + } + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_ldpc = %u", + htt_stats_buf->tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u", + htt_stats_buf->ac_mu_mimo_tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_ldpc = %u", + htt_stats_buf->ax_mu_mimo_tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_ldpc = %u", + htt_stats_buf->ofdma_tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_success = %u", + htt_stats_buf->rts_success); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_rssi = %u", + htt_stats_buf->ack_rssi); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Legacy CCK Rates: 1 Mbps: %u, 2 Mbps: %u, 5.5 Mbps: %u, 11 Mbps: %u", + htt_stats_buf->tx_legacy_cck_rate[0], + htt_stats_buf->tx_legacy_cck_rate[1], + htt_stats_buf->tx_legacy_cck_rate[2], + htt_stats_buf->tx_legacy_cck_rate[3]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Legacy OFDM Rates: 6 Mbps: %u, 9 Mbps: %u, 12 Mbps: %u, 18 Mbps: %u\n" + " 24 Mbps: %u, 36 Mbps: %u, 48 Mbps: %u, 54 Mbps: %u", + htt_stats_buf->tx_legacy_ofdm_rate[0], + htt_stats_buf->tx_legacy_ofdm_rate[1], + htt_stats_buf->tx_legacy_ofdm_rate[2], + htt_stats_buf->tx_legacy_ofdm_rate[3], + htt_stats_buf->tx_legacy_ofdm_rate[4], + htt_stats_buf->tx_legacy_ofdm_rate[5], + htt_stats_buf->tx_legacy_ofdm_rate[6], + htt_stats_buf->tx_legacy_ofdm_rate[7]); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_stbc, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_pream, + HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u", + htt_stats_buf->tx_he_ltf[1], + htt_stats_buf->tx_he_ltf[2], + htt_stats_buf->tx_he_ltf[3]); + + /* SU GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* AC MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ac_mu_mimo_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* AX MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ax_mu_mimo_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* DL OFDMA GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ofdma_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_dcm, + HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +fail: + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + kfree(tx_gi[j]); +} + +static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i, j; + u16 index = 0; + char *rssi_chain[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] = {NULL}; + char *rx_gi[HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] = {NULL}; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + char *rx_pilot_evm_db[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] = {NULL}; + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + rssi_chain[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + if (!rssi_chain[j]) + goto fail; + } + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + rx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + if (!rx_gi[j]) + goto fail; + } + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + rx_pilot_evm_db[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + if (!rx_pilot_evm_db[j]) + goto fail; + } + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "nsts = %u", + htt_stats_buf->nsts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ldpc = %u", + htt_stats_buf->rx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_mgmt = %u", + htt_stats_buf->rssi_mgmt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_data = %u", + htt_stats_buf->rssi_data); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_comb = %u", + htt_stats_buf->rssi_comb); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_in_dbm = %d", + htt_stats_buf->rssi_in_dbm); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_dcm, + HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_stbc, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_evm_nss_count = %u", + htt_stats_buf->nss_count); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_evm_pilot_count = %u", + htt_stats_buf->pilot_count); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + index = 0; + + for (i = 0; i < HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_PER_NSS; i++) + index += snprintf(&rx_pilot_evm_db[j][index], + HTT_MAX_STRING_LEN - index, + " %u:%d,", + i, + htt_stats_buf->rx_pilot_evm_db[j][i]); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pilot_evm_dB[%u] = %s ", + j, rx_pilot_evm_db[j]); + } + + 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], + 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); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + ARRAY_TO_STRING(rssi_chain[j], htt_stats_buf->rssi_chain[j], + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", + j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", + j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_pream, + HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s", str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_su_ext = %u", + htt_stats_buf->rx_11ax_su_ext); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ac_mumimo = %u", + htt_stats_buf->rx_11ac_mumimo); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_mumimo = %u", + htt_stats_buf->rx_11ax_mumimo); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_ofdma = %u", + htt_stats_buf->rx_11ax_ofdma); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txbf = %u", + htt_stats_buf->txbf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_legacy_cck_rate, + HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_cck_rate = %s ", + str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_legacy_ofdm_rate, + HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_ofdm_rate = %s ", + str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_active_dur_us_low = %u", + htt_stats_buf->rx_active_dur_us_low); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_active_dur_us_high = %u", + htt_stats_buf->rx_active_dur_us_high); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u", + htt_stats_buf->rx_11ax_ul_ofdma); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ul_ofdma_rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_mcs = %s ", str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->ul_ofdma_rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_gi[%u] = %s ", + j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ul_ofdma_rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ul_ofdma_rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_bw = %s ", str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u", + htt_stats_buf->ul_ofdma_rx_stbc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u", + htt_stats_buf->ul_ofdma_rx_ldpc); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_ulofdma_non_data_ppdu, + HTT_RX_PDEV_MAX_OFDMA_NUM_USER); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_non_data_ppdu = %s ", + str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_ulofdma_data_ppdu, + HTT_RX_PDEV_MAX_OFDMA_NUM_USER); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_data_ppdu = %s ", + str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_ulofdma_mpdu_ok, + HTT_RX_PDEV_MAX_OFDMA_NUM_USER); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_mpdu_ok = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_ulofdma_mpdu_fail, + HTT_RX_PDEV_MAX_OFDMA_NUM_USER); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_mpdu_fail = %s", + str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + 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], + HTT_MAX_STRING_LEN - index, + " %u:%d,", + i, htt_stats_buf->rx_ul_fd_rssi[j][i]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "rx_ul_fd_rssi: nss[%u] = %s", j, str_buf); + } + + len += HTT_DBG_OUT(buf + len, buf_len - len, "per_chain_rssi_pkt_type = %#x", + htt_stats_buf->per_chain_rssi_pkt_type); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + 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], + HTT_MAX_STRING_LEN - index, + " %u:%d,", + i, + htt_stats_buf->rx_per_chain_rssi_in_dbm[j][i]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "rx_per_chain_rssi_in_dbm[%u] = %s ", j, str_buf); + } + len += HTT_DBG_OUT(buf + len, buf_len - len, "\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; + +fail: + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + kfree(rssi_chain[j]); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + kfree(rx_pilot_evm_db[j]); + + for (i = 0; i < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; i++) + kfree(rx_gi[i]); +} + +static inline void htt_print_rx_soc_fw_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_stats_tlv *htt_stats_buf = tag_buf; + 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, "HTT_RX_SOC_FW_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_reo_ring_data_msdu = %u", + htt_stats_buf->fw_reo_ring_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_to_host_data_msdu_bcmc = %u", + htt_stats_buf->fw_to_host_data_msdu_bcmc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_to_host_data_msdu_uc = %u", + htt_stats_buf->fw_to_host_data_msdu_uc); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_remote_data_buf_recycle_cnt = %u", + htt_stats_buf->ofld_remote_data_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_remote_free_buf_indication_cnt = %u", + htt_stats_buf->ofld_remote_free_buf_indication_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_buf_to_host_data_msdu_uc = %u", + htt_stats_buf->ofld_buf_to_host_data_msdu_uc); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "reo_fw_ring_to_host_data_msdu_uc = %u", + htt_stats_buf->reo_fw_ring_to_host_data_msdu_uc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_sw_ring_reap = %u", + htt_stats_buf->wbm_sw_ring_reap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_forward_to_host_cnt = %u", + htt_stats_buf->wbm_forward_to_host_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_target_recycle_cnt = %u", + htt_stats_buf->wbm_target_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "target_refill_ring_recycle_cnt = %u", + htt_stats_buf->target_refill_ring_recycle_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_empty_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_empty_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char refill_ring_empty_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_EMPTY_TLV_V:"); + + ARRAY_TO_STRING(refill_ring_empty_cnt, + htt_stats_buf->refill_ring_empty_cnt, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_empty_cnt = %s\n", + refill_ring_empty_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v *htt_stats_buf = + tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char rxdma_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_RXDMA_MAX_ERR_CODE); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_RXDMA_ERR_TLV_V:"); + + ARRAY_TO_STRING(rxdma_err_cnt, + htt_stats_buf->rxdma_err, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rxdma_err = %s\n", + rxdma_err_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_num_reo_err_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_num_reo_err_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char reo_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_REO_MAX_ERR_CODE); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REO_ERR_TLV_V:"); + + ARRAY_TO_STRING(reo_err_cnt, + htt_stats_buf->reo_err, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "reo_err = %s\n", + reo_err_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_reo_debug_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_reo_resource_stats_tlv_v *htt_stats_buf = tag_buf; + 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, "HTT_RX_REO_RESOURCE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sample_id = %u", + htt_stats_buf->sample_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_max = %u", + htt_stats_buf->total_max); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_avg = %u", + htt_stats_buf->total_avg); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_sample = %u", + htt_stats_buf->total_sample); + len += HTT_DBG_OUT(buf + len, buf_len - len, "non_zeros_avg = %u", + htt_stats_buf->non_zeros_avg); + len += HTT_DBG_OUT(buf + len, buf_len - len, "non_zeros_sample = %u", + htt_stats_buf->non_zeros_sample); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_max = %u", + htt_stats_buf->last_non_zeros_max); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_min %u", + htt_stats_buf->last_non_zeros_min); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_avg %u", + htt_stats_buf->last_non_zeros_avg); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_sample %u\n", + htt_stats_buf->last_non_zeros_sample); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_num_refill_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_num_refill_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char refill_ring_num_refill[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REFILL_TLV_V:"); + + ARRAY_TO_STRING(refill_ring_num_refill, + htt_stats_buf->refill_ring_num_refill, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_num_refill = %s\n", + refill_ring_num_refill); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_pdev_fw_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_ring_mgmt_subtype[HTT_MAX_STRING_LEN] = {0}; + char fw_ring_ctrl_subtype[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ppdu_recvd = %u", + htt_stats_buf->ppdu_recvd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt_fcs_ok = %u", + htt_stats_buf->mpdu_cnt_fcs_ok); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt_fcs_err = %u", + htt_stats_buf->mpdu_cnt_fcs_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcp_msdu_cnt = %u", + htt_stats_buf->tcp_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcp_ack_msdu_cnt = %u", + htt_stats_buf->tcp_ack_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "udp_msdu_cnt = %u", + htt_stats_buf->udp_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "other_msdu_cnt = %u", + htt_stats_buf->other_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_ind = %u", + htt_stats_buf->fw_ring_mpdu_ind); + + ARRAY_TO_STRING(fw_ring_mgmt_subtype, + htt_stats_buf->fw_ring_mgmt_subtype, + HTT_STATS_SUBTYPE_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mgmt_subtype = %s ", + fw_ring_mgmt_subtype); + + ARRAY_TO_STRING(fw_ring_ctrl_subtype, + htt_stats_buf->fw_ring_ctrl_subtype, + HTT_STATS_SUBTYPE_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_ctrl_subtype = %s ", + fw_ring_ctrl_subtype); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mcast_data_msdu = %u", + htt_stats_buf->fw_ring_mcast_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_bcast_data_msdu = %u", + htt_stats_buf->fw_ring_bcast_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_ucast_data_msdu = %u", + htt_stats_buf->fw_ring_ucast_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_null_data_msdu = %u", + htt_stats_buf->fw_ring_null_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_drop = %u", + htt_stats_buf->fw_ring_mpdu_drop); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofld_local_data_ind_cnt = %u", + htt_stats_buf->ofld_local_data_ind_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_local_data_buf_recycle_cnt = %u", + htt_stats_buf->ofld_local_data_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "drx_local_data_ind_cnt = %u", + htt_stats_buf->drx_local_data_ind_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "drx_local_data_buf_recycle_cnt = %u", + htt_stats_buf->drx_local_data_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_nondata_ind_cnt = %u", + htt_stats_buf->local_nondata_ind_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_nondata_buf_recycle_cnt = %u", + htt_stats_buf->local_nondata_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_status_buf_ring_refill_cnt = %u", + htt_stats_buf->fw_status_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_status_buf_ring_empty_cnt = %u", + htt_stats_buf->fw_status_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_pkt_buf_ring_refill_cnt = %u", + htt_stats_buf->fw_pkt_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_pkt_buf_ring_empty_cnt = %u", + htt_stats_buf->fw_pkt_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_link_buf_ring_refill_cnt = %u", + htt_stats_buf->fw_link_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_link_buf_ring_empty_cnt = %u", + htt_stats_buf->fw_link_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "host_pkt_buf_ring_refill_cnt = %u", + htt_stats_buf->host_pkt_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "host_pkt_buf_ring_empty_cnt = %u", + htt_stats_buf->host_pkt_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_pkt_buf_ring_refill_cnt = %u", + htt_stats_buf->mon_pkt_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_pkt_buf_ring_empty_cnt = %u", + htt_stats_buf->mon_pkt_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "mon_status_buf_ring_refill_cnt = %u", + htt_stats_buf->mon_status_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_status_buf_ring_empty_cnt = %u", + htt_stats_buf->mon_status_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_desc_buf_ring_refill_cnt = %u", + htt_stats_buf->mon_desc_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_desc_buf_ring_empty_cnt = %u", + htt_stats_buf->mon_desc_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_dest_ring_update_cnt = %u", + htt_stats_buf->mon_dest_ring_update_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_dest_ring_full_cnt = %u", + htt_stats_buf->mon_dest_ring_full_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_suspend_cnt = %u", + htt_stats_buf->rx_suspend_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_suspend_fail_cnt = %u", + htt_stats_buf->rx_suspend_fail_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_resume_cnt = %u", + htt_stats_buf->rx_resume_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_resume_fail_cnt = %u", + htt_stats_buf->rx_resume_fail_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ring_switch_cnt = %u", + htt_stats_buf->rx_ring_switch_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ring_restore_cnt = %u", + htt_stats_buf->rx_ring_restore_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_flush_cnt = %u", + htt_stats_buf->rx_flush_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_recovery_reset_cnt = %u\n", + htt_stats_buf->rx_recovery_reset_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_pdev_fw_ring_mpdu_err_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_ring_mpdu_err_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_ring_mpdu_err[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_PDEV_FW_RING_MPDU_ERR_TLV_V:"); + + ARRAY_TO_STRING(fw_ring_mpdu_err, + htt_stats_buf->fw_ring_mpdu_err, + HTT_RX_STATS_RXDMA_MAX_ERR); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_err = %s\n", + fw_ring_mpdu_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_pdev_fw_mpdu_drop_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_mpdu_drop_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_mpdu_drop[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_FW_DROP_REASON_MAX); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_MPDU_DROP_TLV_V:"); + + ARRAY_TO_STRING(fw_mpdu_drop, + htt_stats_buf->fw_mpdu_drop, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_mpdu_drop = %s\n", fw_mpdu_drop); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_pdev_fw_stats_phy_err_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_stats_phy_err_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char phy_errs[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_PHY_ERR_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id__word = %u", + htt_stats_buf->mac_id__word); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_phy_err_nct = %u", + htt_stats_buf->total_phy_err_cnt); + + ARRAY_TO_STRING(phy_errs, + htt_stats_buf->phy_err, + HTT_STATS_PHY_ERR_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_cca_stats_hist_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_cca_stats_hist_v1_tlv *htt_stats_buf = tag_buf; + 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, "\nHTT_PDEV_CCA_STATS_HIST_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "chan_num = %u", + htt_stats_buf->chan_num); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u", + htt_stats_buf->num_records); + len += HTT_DBG_OUT(buf + len, buf_len - len, "valid_cca_counters_bitmap = 0x%x", + htt_stats_buf->valid_cca_counters_bitmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "collection_interval = %u\n", + htt_stats_buf->collection_interval); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_PDEV_STATS_CCA_COUNTERS_TLV:(in usec)"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "| tx_frame| rx_frame| rx_clear| my_rx_frame| cnt| med_rx_idle| med_tx_idle_global| cca_obss|"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_stats_cca_counters_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_stats_cca_counters_tlv *htt_stats_buf = tag_buf; + 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, + "|%10u| %10u| %10u| %11u| %10u| %11u| %18u| %10u|", + htt_stats_buf->tx_frame_usec, + htt_stats_buf->rx_frame_usec, + htt_stats_buf->rx_clear_usec, + htt_stats_buf->my_rx_frame_usec, + htt_stats_buf->usec_cnt, + htt_stats_buf->med_rx_idle_usec, + htt_stats_buf->med_tx_idle_global_usec, + htt_stats_buf->cca_obss_usec); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_hw_stats_whal_tx_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_whal_tx_tlv *htt_stats_buf = tag_buf; + 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, "HTT_HW_STATS_WHAL_TX_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_unpause_ppdu_id = %u", + htt_stats_buf->last_unpause_ppdu_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_unpause_wait_tqm_write = %u", + htt_stats_buf->hwsch_unpause_wait_tqm_write); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_dummy_tlv_skipped = %u", + htt_stats_buf->hwsch_dummy_tlv_skipped); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "hwsch_misaligned_offset_received = %u", + htt_stats_buf->hwsch_misaligned_offset_received); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_reset_count = %u", + htt_stats_buf->hwsch_reset_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_dev_reset_war = %u", + htt_stats_buf->hwsch_dev_reset_war); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_delayed_pause = %u", + htt_stats_buf->hwsch_delayed_pause); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_long_delayed_pause = %u", + htt_stats_buf->hwsch_long_delayed_pause); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_rx_ppdu_no_response = %u", + htt_stats_buf->sch_rx_ppdu_no_response); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_selfgen_response = %u", + htt_stats_buf->sch_selfgen_response); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_rx_sifs_resp_trigger= %u\n", + htt_stats_buf->sch_rx_sifs_resp_trigger); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_stats_twt_sessions_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_stats_twt_sessions_tlv *htt_stats_buf = tag_buf; + 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, "HTT_PDEV_STATS_TWT_SESSIONS_TLV:"); + 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, "num_sessions = %u\n", + htt_stats_buf->num_sessions); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_stats_twt_session_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_stats_twt_session_tlv *htt_stats_buf = tag_buf; + 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, "HTT_PDEV_STATS_TWT_SESSION_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "vdev_id = %u", + htt_stats_buf->vdev_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "peer_mac = %02x:%02x:%02x:%02x:%02x:%02x", + htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF, + (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF00) >> 8, + (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF0000) >> 16, + (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF000000) >> 24, + (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF), + (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flow_id_flags = %u", + htt_stats_buf->flow_id_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dialog_id = %u", + htt_stats_buf->dialog_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wake_dura_us = %u", + htt_stats_buf->wake_dura_us); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wake_intvl_us = %u", + htt_stats_buf->wake_intvl_us); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sp_offset_us = %u\n", + htt_stats_buf->sp_offset_us); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_obss_pd_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_obss_pd_stats_tlv *htt_stats_buf = tag_buf; + 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, "OBSS Tx success PPDU = %u", + htt_stats_buf->num_obss_tx_ppdu_success); + len += HTT_DBG_OUT(buf + len, buf_len - len, "OBSS Tx failures PPDU = %u\n", + htt_stats_buf->num_obss_tx_ppdu_failure); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + 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) +{ + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u32 tlv_len = 0, i = 0, word_len = 0; + + tlv_len = FIELD_GET(HTT_TLV_LEN, *tag_buf) + HTT_TLV_HDR_LEN; + word_len = (tlv_len % 4) == 0 ? (tlv_len / 4) : ((tlv_len / 4) + 1); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "============================================"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HKDBG TLV DUMP: (tag_len=%u bytes, words=%u)", + tlv_len, word_len); + + for (i = 0; i + 3 < word_len; i += 4) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "0x%08x 0x%08x 0x%08x 0x%08x", + tag_buf[i], tag_buf[i + 1], + tag_buf[i + 2], tag_buf[i + 3]); + } + + if (i + 3 == word_len) { + len += HTT_DBG_OUT(buf + len, buf_len - len, "0x%08x 0x%08x 0x%08x ", + tag_buf[i], tag_buf[i + 1], tag_buf[i + 2]); + } else if (i + 2 == word_len) { + len += HTT_DBG_OUT(buf + len, buf_len - len, "0x%08x 0x%08x ", + tag_buf[i], tag_buf[i + 1]); + } else if (i + 1 == word_len) { + len += HTT_DBG_OUT(buf + len, buf_len - len, "0x%08x ", + tag_buf[i]); + } + len += HTT_DBG_OUT(buf + len, buf_len - len, + "============================================"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static int ath11k_dbg_htt_ext_stats_parse(struct ath11k_base *ab, + u16 tag, u16 len, const void *tag_buf, + void *user_data) +{ + struct debug_htt_stats_req *stats_req = user_data; + + switch (tag) { + case HTT_STATS_TX_PDEV_CMN_TAG: + htt_print_tx_pdev_stats_cmn_tlv(tag_buf, stats_req); + break; + case HTT_STATS_TX_PDEV_UNDERRUN_TAG: + htt_print_tx_pdev_stats_urrn_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SIFS_TAG: + htt_print_tx_pdev_stats_sifs_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_FLUSH_TAG: + htt_print_tx_pdev_stats_flush_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_PHY_ERR_TAG: + htt_print_tx_pdev_stats_phy_err_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SIFS_HIST_TAG: + htt_print_tx_pdev_stats_sifs_hist_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_PDEV_TX_PPDU_STATS_TAG: + htt_print_tx_pdev_stats_tx_ppdu_stats_tlv_v(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_TRIED_MPDU_CNT_HIST_TAG: + htt_print_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v(tag_buf, len, + stats_req); + break; + + case HTT_STATS_STRING_TAG: + htt_print_stats_string_tlv(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_CMN_TAG: + htt_print_tx_hwq_stats_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_DIFS_LATENCY_TAG: + htt_print_tx_hwq_difs_latency_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_CMD_RESULT_TAG: + htt_print_tx_hwq_cmd_result_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_CMD_STALL_TAG: + htt_print_tx_hwq_cmd_stall_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_FES_STATUS_TAG: + htt_print_tx_hwq_fes_result_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_TRIED_MPDU_CNT_HIST_TAG: + htt_print_tx_hwq_tried_mpdu_cnt_hist_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_TXOP_USED_CNT_HIST_TAG: + htt_print_tx_hwq_txop_used_cnt_hist_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_GEN_MPDU_TAG: + htt_print_tx_tqm_gen_mpdu_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_TQM_LIST_MPDU_TAG: + htt_print_tx_tqm_list_mpdu_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG: + htt_print_tx_tqm_list_mpdu_cnt_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_TQM_CMN_TAG: + htt_print_tx_tqm_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TQM_PDEV_TAG: + htt_print_tx_tqm_pdev_stats_tlv_v(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TQM_CMDQ_STATUS_TAG: + htt_print_tx_tqm_cmdq_status_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_EAPOL_PACKETS_TAG: + htt_print_tx_de_eapol_packets_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_CLASSIFY_FAILED_TAG: + htt_print_tx_de_classify_failed_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_CLASSIFY_STATS_TAG: + htt_print_tx_de_classify_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_CLASSIFY_STATUS_TAG: + htt_print_tx_de_classify_status_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_ENQUEUE_PACKETS_TAG: + htt_print_tx_de_enqueue_packets_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_ENQUEUE_DISCARD_TAG: + htt_print_tx_de_enqueue_discard_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_FW2WBM_RING_FULL_HIST_TAG: + htt_print_tx_de_fw2wbm_ring_full_hist_tlv(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_DE_CMN_TAG: + htt_print_tx_de_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RING_IF_TAG: + htt_print_ring_if_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG: + htt_print_tx_pdev_mu_mimo_sch_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SFM_CMN_TAG: + htt_print_sfm_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SRING_STATS_TAG: + htt_print_sring_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_FW_STATS_TAG: + htt_print_rx_pdev_fw_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_FW_RING_MPDU_ERR_TAG: + htt_print_rx_pdev_fw_ring_mpdu_err_tlv_v(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_FW_MPDU_DROP_TAG: + htt_print_rx_pdev_fw_mpdu_drop_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_SOC_FW_STATS_TAG: + htt_print_rx_soc_fw_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_SOC_FW_REFILL_RING_EMPTY_TAG: + htt_print_rx_soc_fw_refill_ring_empty_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_SOC_FW_REFILL_RING_NUM_REFILL_TAG: + htt_print_rx_soc_fw_refill_ring_num_refill_tlv_v( + tag_buf, len, stats_req); + break; + case HTT_STATS_RX_REFILL_RXDMA_ERR_TAG: + htt_print_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v( + tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_REFILL_REO_ERR_TAG: + htt_print_rx_soc_fw_refill_ring_num_reo_err_tlv_v( + tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_REO_RESOURCE_STATS_TAG: + htt_print_rx_reo_debug_stats_tlv_v( + tag_buf, stats_req); + break; + case HTT_STATS_RX_PDEV_FW_STATS_PHY_ERR_TAG: + htt_print_rx_pdev_fw_stats_phy_err_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_RATE_STATS_TAG: + htt_print_tx_pdev_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_RATE_STATS_TAG: + htt_print_rx_pdev_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG: + htt_print_tx_pdev_stats_sched_per_txq_tlv(tag_buf, stats_req); + break; + case HTT_STATS_TX_SCHED_CMN_TAG: + htt_print_stats_tx_sched_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_MPDU_STATS_TAG: + htt_print_tx_pdev_mu_mimo_mpdu_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG: + htt_print_sched_txq_cmd_posted_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_RING_IF_CMN_TAG: + htt_print_ring_if_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SFM_CLIENT_USER_TAG: + htt_print_sfm_client_user_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_SFM_CLIENT_TAG: + htt_print_sfm_client_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TQM_ERROR_STATS_TAG: + htt_print_tx_tqm_error_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG: + htt_print_sched_txq_cmd_reaped_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_SRING_CMN_TAG: + htt_print_sring_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SOUNDING_STATS_TAG: + htt_print_tx_sounding_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AC_ERR_STATS_TAG: + htt_print_tx_selfgen_ac_err_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_CMN_STATS_TAG: + htt_print_tx_selfgen_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AC_STATS_TAG: + htt_print_tx_selfgen_ac_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AX_STATS_TAG: + htt_print_tx_selfgen_ax_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AX_ERR_STATS_TAG: + htt_print_tx_selfgen_ax_err_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_MUMIMO_SCH_STATS_TAG: + htt_print_tx_hwq_mu_mimo_sch_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_MUMIMO_MPDU_STATS_TAG: + htt_print_tx_hwq_mu_mimo_mpdu_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_MUMIMO_CMN_STATS_TAG: + htt_print_tx_hwq_mu_mimo_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_HW_INTR_MISC_TAG: + htt_print_hw_stats_intr_misc_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_HW_WD_TIMEOUT_TAG: + htt_print_hw_stats_wd_timeout_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_HW_PDEV_ERRS_TAG: + htt_print_hw_stats_pdev_errs_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_COUNTER_NAME_TAG: + htt_print_counter_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TID_DETAILS_TAG: + htt_print_tx_tid_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TID_DETAILS_V1_TAG: + htt_print_tx_tid_stats_v1_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_TID_DETAILS_TAG: + htt_print_rx_tid_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_STATS_CMN_TAG: + htt_print_peer_stats_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_DETAILS_TAG: + htt_print_peer_details_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_MSDU_FLOWQ_TAG: + htt_print_msdu_flow_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_TX_RATE_STATS_TAG: + htt_print_tx_peer_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_RX_RATE_STATS_TAG: + htt_print_rx_peer_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_COMPL_STATS_TAG: + htt_print_tx_de_compl_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_CCA_1SEC_HIST_TAG: + case HTT_STATS_PDEV_CCA_100MSEC_HIST_TAG: + case HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG: + htt_print_pdev_cca_stats_hist_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_CCA_COUNTERS_TAG: + htt_print_pdev_stats_cca_counters_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_WHAL_TX_TAG: + htt_print_hw_stats_whal_tx_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_TWT_SESSIONS_TAG: + htt_print_pdev_stats_twt_sessions_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_TWT_SESSION_TAG: + htt_print_pdev_stats_twt_session_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG: + htt_print_sched_txq_sched_order_su_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG: + htt_print_sched_txq_sched_ineligibility_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_PDEV_OBSS_PD_TAG: + htt_print_pdev_obss_pd_stats_tlv_v(tag_buf, stats_req); + break; + default: + break; + } + + return 0; +} + +void ath11k_dbg_htt_ext_stats_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_htt_extd_stats_msg *msg; + struct debug_htt_stats_req *stats_req; + struct ath11k *ar; + u32 len; + u64 cookie; + int ret; + u8 pdev_id; + + msg = (struct ath11k_htt_extd_stats_msg *)skb->data; + cookie = msg->cookie; + + if (FIELD_GET(HTT_STATS_COOKIE_MSB, cookie) != HTT_STATS_MAGIC_VALUE) { + ath11k_warn(ab, "received invalid htt ext stats event\n"); + return; + } + + pdev_id = FIELD_GET(HTT_STATS_COOKIE_LSB, cookie); + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + rcu_read_unlock(); + if (!ar) { + ath11k_warn(ab, "failed to get ar for pdev_id %d\n", pdev_id); + return; + } + + stats_req = ar->debug.htt_stats.stats_req; + if (!stats_req) + return; + + spin_lock_bh(&ar->debug.htt_stats.lock); + if (stats_req->done) { + spin_unlock_bh(&ar->debug.htt_stats.lock); + return; + } + stats_req->done = true; + spin_unlock_bh(&ar->debug.htt_stats.lock); + + len = FIELD_GET(HTT_T2H_EXT_STATS_INFO1_LENGTH, msg->info1); + ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, + ath11k_dbg_htt_ext_stats_parse, + stats_req); + if (ret) + ath11k_warn(ab, "Failed to parse tlv %d\n", ret); + + complete(&stats_req->cmpln); +} + +static ssize_t ath11k_read_htt_stats_type(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + size_t len; + + len = scnprintf(buf, sizeof(buf), "%u\n", ar->debug.htt_stats.type); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_htt_stats_type(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u8 type; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= ATH11K_DBG_HTT_NUM_EXT_STATS) + return -E2BIG; + + if (type == ATH11K_DBG_HTT_EXT_STATS_RESET || + type == ATH11K_DBG_HTT_EXT_STATS_PEER_INFO) + return -EPERM; + + ar->debug.htt_stats.type = type; + + ret = count; + + return ret; +} + +static const struct file_operations fops_htt_stats_type = { + .read = ath11k_read_htt_stats_type, + .write = ath11k_write_htt_stats_type, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_prep_htt_stats_cfg_params(struct ath11k *ar, u8 type, + const u8 *mac_addr, + struct htt_ext_stats_cfg_params *cfg_params) +{ + if (!cfg_params) + return -EINVAL; + + switch (type) { + case ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_HWQ: + case ATH11K_DBG_HTT_EXT_STATS_TX_MU_HWQ: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_HWQS; + break; + case ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_TXQS; + break; + case ATH11K_DBG_HTT_EXT_STATS_TQM_CMDQ: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_CMDQS; + break; + case ATH11K_DBG_HTT_EXT_STATS_PEER_INFO: + cfg_params->cfg0 = HTT_STAT_PEER_INFO_MAC_ADDR; + cfg_params->cfg0 |= FIELD_PREP(GENMASK(15, 1), + HTT_PEER_STATS_REQ_MODE_FLUSH_TQM); + cfg_params->cfg1 = HTT_STAT_DEFAULT_PEER_REQ_TYPE; + cfg_params->cfg2 |= FIELD_PREP(GENMASK(7, 0), mac_addr[0]); + cfg_params->cfg2 |= FIELD_PREP(GENMASK(15, 8), mac_addr[1]); + cfg_params->cfg2 |= FIELD_PREP(GENMASK(23, 16), mac_addr[2]); + cfg_params->cfg2 |= FIELD_PREP(GENMASK(31, 24), mac_addr[3]); + cfg_params->cfg3 |= FIELD_PREP(GENMASK(7, 0), mac_addr[4]); + cfg_params->cfg3 |= FIELD_PREP(GENMASK(15, 8), mac_addr[5]); + break; + case ATH11K_DBG_HTT_EXT_STATS_RING_IF_INFO: + case ATH11K_DBG_HTT_EXT_STATS_SRNG_INFO: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_RINGS; + break; + case ATH11K_DBG_HTT_EXT_STATS_ACTIVE_PEERS_LIST: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ACTIVE_PEERS; + break; + case ATH11K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_CCA_CUMULATIVE; + break; + case ATH11K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS; + break; + default: + break; + } + + return 0; +} + +int ath11k_dbg_htt_stats_req(struct ath11k *ar) +{ + struct debug_htt_stats_req *stats_req = ar->debug.htt_stats.stats_req; + u8 type = stats_req->type; + u64 cookie = 0; + int ret, pdev_id = ar->pdev->pdev_id; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + + init_completion(&stats_req->cmpln); + + stats_req->done = false; + stats_req->pdev_id = pdev_id; + + cookie = FIELD_PREP(HTT_STATS_COOKIE_MSB, HTT_STATS_MAGIC_VALUE) | + FIELD_PREP(HTT_STATS_COOKIE_LSB, pdev_id); + + ret = ath11k_prep_htt_stats_cfg_params(ar, type, stats_req->peer_addr, + &cfg_params); + if (ret) { + ath11k_warn(ar->ab, "failed to set htt stats cfg params: %d\n", ret); + return ret; + } + + ret = ath11k_dp_tx_htt_h2t_ext_stats_req(ar, type, &cfg_params, cookie); + if (ret) { + ath11k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + return ret; + } + + while (!wait_for_completion_timeout(&stats_req->cmpln, 3 * HZ)) { + spin_lock_bh(&ar->debug.htt_stats.lock); + if (!stats_req->done) { + stats_req->done = true; + spin_unlock_bh(&ar->debug.htt_stats.lock); + ath11k_warn(ar->ab, "stats request timed out\n"); + return -ETIMEDOUT; + } + spin_unlock_bh(&ar->debug.htt_stats.lock); + } + + return 0; +} + +static int ath11k_open_htt_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct debug_htt_stats_req *stats_req; + u8 type = ar->debug.htt_stats.type; + int ret; + + if (type == ATH11K_DBG_HTT_EXT_STATS_RESET) + return -EPERM; + + stats_req = vzalloc(sizeof(*stats_req) + ATH11K_HTT_STATS_BUF_SIZE); + if (!stats_req) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + ar->debug.htt_stats.stats_req = stats_req; + stats_req->type = type; + ret = ath11k_dbg_htt_stats_req(ar); + mutex_unlock(&ar->conf_mutex); + if (ret < 0) + goto out; + + file->private_data = stats_req; + return 0; +out: + vfree(stats_req); + return ret; +} + +static int ath11k_release_htt_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + return 0; +} + +static ssize_t ath11k_read_htt_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct debug_htt_stats_req *stats_req = file->private_data; + char *buf; + u32 length = 0; + + buf = stats_req->buf; + length = min_t(u32, stats_req->buf_len, ATH11K_HTT_STATS_BUF_SIZE); + return simple_read_from_buffer(user_buf, count, ppos, buf, length); +} + +static const struct file_operations fops_dump_htt_stats = { + .open = ath11k_open_htt_stats, + .release = ath11k_release_htt_stats, + .read = ath11k_read_htt_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_read_htt_stats_reset(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + size_t len; + + len = scnprintf(buf, sizeof(buf), "%u\n", ar->debug.htt_stats.reset); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_htt_stats_reset(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u8 type; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= ATH11K_DBG_HTT_NUM_EXT_STATS || + type == ATH11K_DBG_HTT_EXT_STATS_RESET) + return -E2BIG; + + mutex_lock(&ar->conf_mutex); + cfg_params.cfg0 = HTT_STAT_DEFAULT_RESET_START_OFFSET; + cfg_params.cfg1 = 1 << (cfg_params.cfg0 + type); + ret = ath11k_dp_tx_htt_h2t_ext_stats_req(ar, + ATH11K_DBG_HTT_EXT_STATS_RESET, + &cfg_params, + 0ULL); + if (ret) { + ath11k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + mutex_unlock(&ar->conf_mutex); + return ret; + } + + ar->debug.htt_stats.reset = type; + mutex_unlock(&ar->conf_mutex); + + ret = count; + + return ret; +} + +static const struct file_operations fops_htt_stats_reset = { + .read = ath11k_read_htt_stats_reset, + .write = ath11k_write_htt_stats_reset, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath11k_debug_htt_stats_init(struct ath11k *ar) +{ + spin_lock_init(&ar->debug.htt_stats.lock); + debugfs_create_file("htt_stats_type", 0600, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_type); + debugfs_create_file("htt_stats", 0400, ar->debug.debugfs_pdev, + ar, &fops_dump_htt_stats); + debugfs_create_file("htt_stats_reset", 0600, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_reset); +} diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.h b/drivers/net/wireless/ath/ath11k/debug_htt_stats.h new file mode 100644 index 000000000000..4bdb62dd7b8d --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.h @@ -0,0 +1,1662 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef DEBUG_HTT_STATS_H +#define DEBUG_HTT_STATS_H + +#define HTT_STATS_COOKIE_LSB GENMASK_ULL(31, 0) +#define HTT_STATS_COOKIE_MSB GENMASK_ULL(63, 32) +#define HTT_STATS_MAGIC_VALUE 0xF0F0F0F0 + +enum htt_tlv_tag_t { + HTT_STATS_TX_PDEV_CMN_TAG = 0, + HTT_STATS_TX_PDEV_UNDERRUN_TAG = 1, + HTT_STATS_TX_PDEV_SIFS_TAG = 2, + HTT_STATS_TX_PDEV_FLUSH_TAG = 3, + HTT_STATS_TX_PDEV_PHY_ERR_TAG = 4, + HTT_STATS_STRING_TAG = 5, + HTT_STATS_TX_HWQ_CMN_TAG = 6, + HTT_STATS_TX_HWQ_DIFS_LATENCY_TAG = 7, + HTT_STATS_TX_HWQ_CMD_RESULT_TAG = 8, + HTT_STATS_TX_HWQ_CMD_STALL_TAG = 9, + HTT_STATS_TX_HWQ_FES_STATUS_TAG = 10, + HTT_STATS_TX_TQM_GEN_MPDU_TAG = 11, + HTT_STATS_TX_TQM_LIST_MPDU_TAG = 12, + HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG = 13, + HTT_STATS_TX_TQM_CMN_TAG = 14, + HTT_STATS_TX_TQM_PDEV_TAG = 15, + HTT_STATS_TX_TQM_CMDQ_STATUS_TAG = 16, + HTT_STATS_TX_DE_EAPOL_PACKETS_TAG = 17, + HTT_STATS_TX_DE_CLASSIFY_FAILED_TAG = 18, + HTT_STATS_TX_DE_CLASSIFY_STATS_TAG = 19, + HTT_STATS_TX_DE_CLASSIFY_STATUS_TAG = 20, + HTT_STATS_TX_DE_ENQUEUE_PACKETS_TAG = 21, + HTT_STATS_TX_DE_ENQUEUE_DISCARD_TAG = 22, + HTT_STATS_TX_DE_CMN_TAG = 23, + HTT_STATS_RING_IF_TAG = 24, + HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG = 25, + HTT_STATS_SFM_CMN_TAG = 26, + HTT_STATS_SRING_STATS_TAG = 27, + HTT_STATS_RX_PDEV_FW_STATS_TAG = 28, + HTT_STATS_RX_PDEV_FW_RING_MPDU_ERR_TAG = 29, + HTT_STATS_RX_PDEV_FW_MPDU_DROP_TAG = 30, + HTT_STATS_RX_SOC_FW_STATS_TAG = 31, + HTT_STATS_RX_SOC_FW_REFILL_RING_EMPTY_TAG = 32, + HTT_STATS_RX_SOC_FW_REFILL_RING_NUM_REFILL_TAG = 33, + HTT_STATS_TX_PDEV_RATE_STATS_TAG = 34, + HTT_STATS_RX_PDEV_RATE_STATS_TAG = 35, + HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36, + HTT_STATS_TX_SCHED_CMN_TAG = 37, + HTT_STATS_TX_PDEV_MUMIMO_MPDU_STATS_TAG = 38, + HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, + HTT_STATS_RING_IF_CMN_TAG = 40, + HTT_STATS_SFM_CLIENT_USER_TAG = 41, + HTT_STATS_SFM_CLIENT_TAG = 42, + HTT_STATS_TX_TQM_ERROR_STATS_TAG = 43, + HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG = 44, + HTT_STATS_SRING_CMN_TAG = 45, + HTT_STATS_TX_SELFGEN_AC_ERR_STATS_TAG = 46, + HTT_STATS_TX_SELFGEN_CMN_STATS_TAG = 47, + HTT_STATS_TX_SELFGEN_AC_STATS_TAG = 48, + HTT_STATS_TX_SELFGEN_AX_STATS_TAG = 49, + HTT_STATS_TX_SELFGEN_AX_ERR_STATS_TAG = 50, + HTT_STATS_TX_HWQ_MUMIMO_SCH_STATS_TAG = 51, + HTT_STATS_TX_HWQ_MUMIMO_MPDU_STATS_TAG = 52, + HTT_STATS_TX_HWQ_MUMIMO_CMN_STATS_TAG = 53, + HTT_STATS_HW_INTR_MISC_TAG = 54, + HTT_STATS_HW_WD_TIMEOUT_TAG = 55, + HTT_STATS_HW_PDEV_ERRS_TAG = 56, + HTT_STATS_COUNTER_NAME_TAG = 57, + HTT_STATS_TX_TID_DETAILS_TAG = 58, + HTT_STATS_RX_TID_DETAILS_TAG = 59, + HTT_STATS_PEER_STATS_CMN_TAG = 60, + HTT_STATS_PEER_DETAILS_TAG = 61, + HTT_STATS_PEER_TX_RATE_STATS_TAG = 62, + HTT_STATS_PEER_RX_RATE_STATS_TAG = 63, + HTT_STATS_PEER_MSDU_FLOWQ_TAG = 64, + HTT_STATS_TX_DE_COMPL_STATS_TAG = 65, + HTT_STATS_WHAL_TX_TAG = 66, + HTT_STATS_TX_PDEV_SIFS_HIST_TAG = 67, + HTT_STATS_RX_PDEV_FW_STATS_PHY_ERR_TAG = 68, + HTT_STATS_TX_TID_DETAILS_V1_TAG = 69, + HTT_STATS_PDEV_CCA_1SEC_HIST_TAG = 70, + HTT_STATS_PDEV_CCA_100MSEC_HIST_TAG = 71, + HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG = 72, + HTT_STATS_PDEV_CCA_COUNTERS_TAG = 73, + HTT_STATS_TX_PDEV_MPDU_STATS_TAG = 74, + HTT_STATS_PDEV_TWT_SESSIONS_TAG = 75, + HTT_STATS_PDEV_TWT_SESSION_TAG = 76, + HTT_STATS_RX_REFILL_RXDMA_ERR_TAG = 77, + HTT_STATS_RX_REFILL_REO_ERR_TAG = 78, + HTT_STATS_RX_REO_RESOURCE_STATS_TAG = 79, + HTT_STATS_TX_SOUNDING_STATS_TAG = 80, + HTT_STATS_TX_PDEV_TX_PPDU_STATS_TAG = 81, + HTT_STATS_TX_PDEV_TRIED_MPDU_CNT_HIST_TAG = 82, + HTT_STATS_TX_HWQ_TRIED_MPDU_CNT_HIST_TAG = 83, + HTT_STATS_TX_HWQ_TXOP_USED_CNT_HIST_TAG = 84, + HTT_STATS_TX_DE_FW2WBM_RING_FULL_HIST_TAG = 85, + 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_MAX_TAG, +}; + +#define HTT_STATS_MAX_STRING_SZ32 4 +#define HTT_STATS_MACID_INVALID 0xff +#define HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS 10 +#define HTT_TX_HWQ_MAX_CMD_RESULT_STATS 13 +#define HTT_TX_HWQ_MAX_CMD_STALL_STATS 5 +#define HTT_TX_HWQ_MAX_FES_RESULT_STATS 10 + +enum htt_tx_pdev_underrun_enum { + HTT_STATS_TX_PDEV_NO_DATA_UNDERRUN = 0, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_BETWEEN_MPDU = 1, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_WITHIN_MPDU = 2, + HTT_TX_PDEV_MAX_URRN_STATS = 3, +}; + +#define HTT_TX_PDEV_MAX_FLUSH_REASON_STATS 71 +#define HTT_TX_PDEV_MAX_SIFS_BURST_STATS 9 +#define HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS 10 +#define HTT_TX_PDEV_MAX_PHY_ERR_STATS 18 +#define HTT_TX_PDEV_SCHED_TX_MODE_MAX 4 +#define HTT_TX_PDEV_NUM_SCHED_ORDER_LOG 20 + +#define HTT_RX_STATS_REFILL_MAX_RING 4 +#define HTT_RX_STATS_RXDMA_MAX_ERR 16 +#define HTT_RX_STATS_FW_DROP_REASON_MAX 16 + +/* Bytes stored in little endian order */ +/* Length should be multiple of DWORD */ +struct htt_stats_string_tlv { + u32 data[0]; /* Can be variable length */ +} __packed; + +/* == TX PDEV STATS == */ +struct htt_tx_pdev_stats_cmn_tlv { + u32 mac_id__word; + u32 hw_queued; + u32 hw_reaped; + u32 underrun; + u32 hw_paused; + u32 hw_flush; + u32 hw_filt; + u32 tx_abort; + u32 mpdu_requed; + u32 tx_xretry; + u32 data_rc; + u32 mpdu_dropped_xretry; + u32 illgl_rate_phy_err; + u32 cont_xretry; + u32 tx_timeout; + u32 pdev_resets; + u32 phy_underrun; + u32 txop_ovf; + u32 seq_posted; + u32 seq_failed_queueing; + u32 seq_completed; + u32 seq_restarted; + u32 mu_seq_posted; + u32 seq_switch_hw_paused; + u32 next_seq_posted_dsr; + u32 seq_posted_isr; + u32 seq_ctrl_cached; + u32 mpdu_count_tqm; + u32 msdu_count_tqm; + u32 mpdu_removed_tqm; + u32 msdu_removed_tqm; + u32 mpdus_sw_flush; + u32 mpdus_hw_filter; + u32 mpdus_truncated; + u32 mpdus_ack_failed; + u32 mpdus_expired; + u32 mpdus_seq_hw_retry; + u32 ack_tlv_proc; + u32 coex_abort_mpdu_cnt_valid; + u32 coex_abort_mpdu_cnt; + u32 num_total_ppdus_tried_ota; + u32 num_data_ppdus_tried_ota; + u32 local_ctrl_mgmt_enqued; + u32 local_ctrl_mgmt_freed; + u32 local_data_enqued; + u32 local_data_freed; + u32 mpdu_tried; + u32 isr_wait_seq_posted; + + u32 tx_active_dur_us_low; + u32 tx_active_dur_us_high; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_urrn_tlv_v { + u32 urrn_stats[0]; /* HTT_TX_PDEV_MAX_URRN_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_flush_tlv_v { + u32 flush_errs[0]; /* HTT_TX_PDEV_MAX_FLUSH_REASON_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_sifs_tlv_v { + u32 sifs_status[0]; /* HTT_TX_PDEV_MAX_SIFS_BURST_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_phy_err_tlv_v { + u32 phy_errs[0]; /* HTT_TX_PDEV_MAX_PHY_ERR_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_sifs_hist_tlv_v { + u32 sifs_hist_status[0]; /* HTT_TX_PDEV_SIFS_BURST_HIST_STATS */ +}; + +struct htt_tx_pdev_stats_tx_ppdu_stats_tlv_v { + u32 num_data_ppdus_legacy_su; + u32 num_data_ppdus_ac_su; + u32 num_data_ppdus_ax_su; + u32 num_data_ppdus_ac_su_txbf; + u32 num_data_ppdus_ax_su_txbf; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size . + * + * Tried_mpdu_cnt_hist is the histogram of MPDUs tries per HWQ. + * The tries here is the count of the MPDUS within a PPDU that the + * HW had attempted to transmit on air, for the HWSCH Schedule + * command submitted by FW.It is not the retry attempts. + * The histogram bins are 0-29, 30-59, 60-89 and so on. The are + * 10 bins in this histogram. They are defined in FW using the + * following macros + * #define WAL_MAX_TRIED_MPDU_CNT_HISTOGRAM 9 + * #define WAL_TRIED_MPDU_CNT_HISTOGRAM_INTERVAL 30 + */ +struct htt_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v { + u32 hist_bin_size; + u32 tried_mpdu_cnt_hist[0]; /* HTT_TX_PDEV_TRIED_MPDU_CNT_HIST */ +}; + +/* == SOC ERROR STATS == */ + +/* =============== PDEV ERROR STATS ============== */ +#define HTT_STATS_MAX_HW_INTR_NAME_LEN 8 +struct htt_hw_stats_intr_misc_tlv { + /* Stored as little endian */ + u8 hw_intr_name[HTT_STATS_MAX_HW_INTR_NAME_LEN]; + u32 mask; + u32 count; +}; + +#define HTT_STATS_MAX_HW_MODULE_NAME_LEN 8 +struct htt_hw_stats_wd_timeout_tlv { + /* Stored as little endian */ + u8 hw_module_name[HTT_STATS_MAX_HW_MODULE_NAME_LEN]; + u32 count; +}; + +struct htt_hw_stats_pdev_errs_tlv { + u32 mac_id__word; /* BIT [ 7 : 0] : mac_id */ + u32 tx_abort; + u32 tx_abort_fail_count; + u32 rx_abort; + u32 rx_abort_fail_count; + u32 warm_reset; + u32 cold_reset; + u32 tx_flush; + u32 tx_glb_reset; + u32 tx_txq_reset; + u32 rx_timeout_reset; +}; + +struct htt_hw_stats_whal_tx_tlv { + u32 mac_id__word; + u32 last_unpause_ppdu_id; + u32 hwsch_unpause_wait_tqm_write; + u32 hwsch_dummy_tlv_skipped; + u32 hwsch_misaligned_offset_received; + u32 hwsch_reset_count; + u32 hwsch_dev_reset_war; + u32 hwsch_delayed_pause; + u32 hwsch_long_delayed_pause; + u32 sch_rx_ppdu_no_response; + u32 sch_selfgen_response; + u32 sch_rx_sifs_resp_trigger; +}; + +/* ============ PEER STATS ============ */ +struct htt_msdu_flow_stats_tlv { + u32 last_update_timestamp; + u32 last_add_timestamp; + u32 last_remove_timestamp; + u32 total_processed_msdu_count; + u32 cur_msdu_count_in_flowq; + u32 sw_peer_id; + u32 tx_flow_no__tid_num__drop_rule; + u32 last_cycle_enqueue_count; + u32 last_cycle_dequeue_count; + u32 last_cycle_drop_count; + u32 current_drop_th; +}; + +#define MAX_HTT_TID_NAME 8 + +/* Tidq stats */ +struct htt_tx_tid_stats_tlv { + /* Stored as little endian */ + u8 tid_name[MAX_HTT_TID_NAME]; + u32 sw_peer_id__tid_num; + u32 num_sched_pending__num_ppdu_in_hwq; + u32 tid_flags; + u32 hw_queued; + u32 hw_reaped; + u32 mpdus_hw_filter; + + u32 qdepth_bytes; + u32 qdepth_num_msdu; + u32 qdepth_num_mpdu; + u32 last_scheduled_tsmp; + u32 pause_module_id; + u32 block_module_id; + u32 tid_tx_airtime; +}; + +/* Tidq stats */ +struct htt_tx_tid_stats_v1_tlv { + /* Stored as little endian */ + u8 tid_name[MAX_HTT_TID_NAME]; + u32 sw_peer_id__tid_num; + u32 num_sched_pending__num_ppdu_in_hwq; + u32 tid_flags; + u32 max_qdepth_bytes; + u32 max_qdepth_n_msdus; + u32 rsvd; + + u32 qdepth_bytes; + u32 qdepth_num_msdu; + u32 qdepth_num_mpdu; + u32 last_scheduled_tsmp; + u32 pause_module_id; + u32 block_module_id; + u32 tid_tx_airtime; + u32 allow_n_flags; + u32 sendn_frms_allowed; +}; + +struct htt_rx_tid_stats_tlv { + u32 sw_peer_id__tid_num; + u8 tid_name[MAX_HTT_TID_NAME]; + u32 dup_in_reorder; + u32 dup_past_outside_window; + u32 dup_past_within_window; + u32 rxdesc_err_decrypt; + u32 tid_rx_airtime; +}; + +#define HTT_MAX_COUNTER_NAME 8 +struct htt_counter_tlv { + u8 counter_name[HTT_MAX_COUNTER_NAME]; + u32 count; +}; + +struct htt_peer_stats_cmn_tlv { + u32 ppdu_cnt; + u32 mpdu_cnt; + u32 msdu_cnt; + u32 pause_bitmap; + u32 block_bitmap; + u32 current_timestamp; + u32 peer_tx_airtime; + u32 peer_rx_airtime; + s32 rssi; + u32 peer_enqueued_count_low; + u32 peer_enqueued_count_high; + u32 peer_dequeued_count_low; + u32 peer_dequeued_count_high; + u32 peer_dropped_count_low; + u32 peer_dropped_count_high; + u32 ppdu_transmitted_bytes_low; + u32 ppdu_transmitted_bytes_high; + u32 peer_ttl_removed_count; + u32 inactive_time; +}; + +struct htt_peer_details_tlv { + u32 peer_type; + u32 sw_peer_id; + u32 vdev_pdev_ast_idx; + struct htt_mac_addr mac_addr; + u32 peer_flags; + u32 qpeer_flags; +}; + +enum htt_stats_param_type { + HTT_STATS_PREAM_OFDM, + HTT_STATS_PREAM_CCK, + HTT_STATS_PREAM_HT, + HTT_STATS_PREAM_VHT, + HTT_STATS_PREAM_HE, + HTT_STATS_PREAM_RSVD, + HTT_STATS_PREAM_RSVD1, + + HTT_STATS_PREAM_COUNT, +}; + +#define HTT_TX_PEER_STATS_NUM_MCS_COUNTERS 12 +#define HTT_TX_PEER_STATS_NUM_GI_COUNTERS 4 +#define HTT_TX_PEER_STATS_NUM_DCM_COUNTERS 5 +#define HTT_TX_PEER_STATS_NUM_BW_COUNTERS 4 +#define HTT_TX_PEER_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_TX_PEER_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT + +struct htt_tx_peer_rate_stats_tlv { + u32 tx_ldpc; + u32 rts_cnt; + u32 ack_rssi; + + u32 tx_mcs[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + u32 tx_su_mcs[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + u32 tx_mu_mcs[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 tx_nss[HTT_TX_PEER_STATS_NUM_SPATIAL_STREAMS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 tx_bw[HTT_TX_PEER_STATS_NUM_BW_COUNTERS]; + u32 tx_stbc[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + u32 tx_pream[HTT_TX_PEER_STATS_NUM_PREAMBLE_TYPES]; + + /* Counters to track number of tx packets in each GI + * (400us, 800us, 1600us & 3200us) in each mcs (0-11) + */ + u32 tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS][HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + + /* Counters to track packets in dcm mcs (MCS 0, 1, 3, 4) */ + u32 tx_dcm[HTT_TX_PEER_STATS_NUM_DCM_COUNTERS]; + +}; + +#define HTT_RX_PEER_STATS_NUM_MCS_COUNTERS 12 +#define HTT_RX_PEER_STATS_NUM_GI_COUNTERS 4 +#define HTT_RX_PEER_STATS_NUM_DCM_COUNTERS 5 +#define HTT_RX_PEER_STATS_NUM_BW_COUNTERS 4 +#define HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_RX_PEER_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT + +struct htt_rx_peer_rate_stats_tlv { + u32 nsts; + + /* Number of rx ldpc packets */ + u32 rx_ldpc; + /* Number of rx rts packets */ + u32 rts_cnt; + + u32 rssi_mgmt; /* units = dB above noise floor */ + u32 rssi_data; /* units = dB above noise floor */ + u32 rssi_comb; /* units = dB above noise floor */ + u32 rx_mcs[HTT_RX_PEER_STATS_NUM_MCS_COUNTERS]; + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 rx_nss[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS]; + u32 rx_dcm[HTT_RX_PEER_STATS_NUM_DCM_COUNTERS]; + u32 rx_stbc[HTT_RX_PEER_STATS_NUM_MCS_COUNTERS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 rx_bw[HTT_RX_PEER_STATS_NUM_BW_COUNTERS]; + u32 rx_pream[HTT_RX_PEER_STATS_NUM_PREAMBLE_TYPES]; + /* units = dB above noise floor */ + u8 rssi_chain[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PEER_STATS_NUM_BW_COUNTERS]; + + /* Counters to track number of rx packets in each GI in each mcs (0-11) */ + u32 rx_gi[HTT_RX_PEER_STATS_NUM_GI_COUNTERS] + [HTT_RX_PEER_STATS_NUM_MCS_COUNTERS]; +}; + +enum htt_peer_stats_req_mode { + HTT_PEER_STATS_REQ_MODE_NO_QUERY, + HTT_PEER_STATS_REQ_MODE_QUERY_TQM, + HTT_PEER_STATS_REQ_MODE_FLUSH_TQM, +}; + +enum htt_peer_stats_tlv_enum { + HTT_PEER_STATS_CMN_TLV = 0, + HTT_PEER_DETAILS_TLV = 1, + HTT_TX_PEER_RATE_STATS_TLV = 2, + HTT_RX_PEER_RATE_STATS_TLV = 3, + HTT_TX_TID_STATS_TLV = 4, + HTT_RX_TID_STATS_TLV = 5, + HTT_MSDU_FLOW_STATS_TLV = 6, + + HTT_PEER_STATS_MAX_TLV = 31, +}; + +/* =========== MUMIMO HWQ stats =========== */ +/* MU MIMO stats per hwQ */ +struct htt_tx_hwq_mu_mimo_sch_stats_tlv { + u32 mu_mimo_sch_posted; + u32 mu_mimo_sch_failed; + u32 mu_mimo_ppdu_posted; +}; + +struct htt_tx_hwq_mu_mimo_mpdu_stats_tlv { + u32 mu_mimo_mpdus_queued_usr; + u32 mu_mimo_mpdus_tried_usr; + u32 mu_mimo_mpdus_failed_usr; + u32 mu_mimo_mpdus_requeued_usr; + u32 mu_mimo_err_no_ba_usr; + u32 mu_mimo_mpdu_underrun_usr; + u32 mu_mimo_ampdu_underrun_usr; +}; + +struct htt_tx_hwq_mu_mimo_cmn_stats_tlv { + u32 mac_id__hwq_id__word; +}; + +/* == TX HWQ STATS == */ +struct htt_tx_hwq_stats_cmn_tlv { + u32 mac_id__hwq_id__word; + + /* PPDU level stats */ + u32 xretry; + u32 underrun_cnt; + u32 flush_cnt; + u32 filt_cnt; + u32 null_mpdu_bmap; + u32 user_ack_failure; + u32 ack_tlv_proc; + u32 sched_id_proc; + u32 null_mpdu_tx_count; + u32 mpdu_bmap_not_recvd; + + /* Selfgen stats per hwQ */ + u32 num_bar; + u32 rts; + u32 cts2self; + u32 qos_null; + + /* MPDU level stats */ + u32 mpdu_tried_cnt; + u32 mpdu_queued_cnt; + u32 mpdu_ack_fail_cnt; + u32 mpdu_filt_cnt; + u32 false_mpdu_ack_count; + + u32 txq_timeout; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_difs_latency_stats_tlv_v { + u32 hist_intvl; + /* histogram of ppdu post to hwsch - > cmd status received */ + u32 difs_latency_hist[0]; /* HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_cmd_result_stats_tlv_v { + /* Histogram of sched cmd result */ + u32 cmd_result[0]; /* HTT_TX_HWQ_MAX_CMD_RESULT_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_cmd_stall_stats_tlv_v { + /* Histogram of various pause conitions */ + u32 cmd_stall_status[0]; /* HTT_TX_HWQ_MAX_CMD_STALL_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_fes_result_stats_tlv_v { + /* Histogram of number of user fes result */ + u32 fes_result[0]; /* HTT_TX_HWQ_MAX_FES_RESULT_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size + * + * The hwq_tried_mpdu_cnt_hist is a histogram of MPDUs tries per HWQ. + * The tries here is the count of the MPDUS within a PPDU that the HW + * had attempted to transmit on air, for the HWSCH Schedule command + * submitted by FW in this HWQ .It is not the retry attempts. The + * histogram bins are 0-29, 30-59, 60-89 and so on. The are 10 bins + * in this histogram. + * they are defined in FW using the following macros + * #define WAL_MAX_TRIED_MPDU_CNT_HISTOGRAM 9 + * #define WAL_TRIED_MPDU_CNT_HISTOGRAM_INTERVAL 30 + */ +struct htt_tx_hwq_tried_mpdu_cnt_hist_tlv_v { + u32 hist_bin_size; + /* Histogram of number of mpdus on tried mpdu */ + u32 tried_mpdu_cnt_hist[0]; /* HTT_TX_HWQ_TRIED_MPDU_CNT_HIST */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size + * + * The txop_used_cnt_hist is the histogram of txop per burst. After + * completing the burst, we identify the txop used in the burst and + * incr the corresponding bin. + * Each bin represents 1ms & we have 10 bins in this histogram. + * they are deined in FW using the following macros + * #define WAL_MAX_TXOP_USED_CNT_HISTOGRAM 10 + * #define WAL_TXOP_USED_HISTOGRAM_INTERVAL 1000 ( 1 ms ) + */ +struct htt_tx_hwq_txop_used_cnt_hist_tlv_v { + /* Histogram of txop used cnt */ + u32 txop_used_cnt_hist[0]; /* HTT_TX_HWQ_TXOP_USED_CNT_HIST */ +}; + +/* == TX SELFGEN STATS == */ +struct htt_tx_selfgen_cmn_stats_tlv { + u32 mac_id__word; + u32 su_bar; + u32 rts; + u32 cts2self; + u32 qos_null; + u32 delayed_bar_1; /* MU user 1 */ + u32 delayed_bar_2; /* MU user 2 */ + u32 delayed_bar_3; /* MU user 3 */ + u32 delayed_bar_4; /* MU user 4 */ + u32 delayed_bar_5; /* MU user 5 */ + u32 delayed_bar_6; /* MU user 6 */ + u32 delayed_bar_7; /* MU user 7 */ +}; + +struct htt_tx_selfgen_ac_stats_tlv { + /* 11AC */ + u32 ac_su_ndpa; + u32 ac_su_ndp; + u32 ac_mu_mimo_ndpa; + u32 ac_mu_mimo_ndp; + u32 ac_mu_mimo_brpoll_1; /* MU user 1 */ + u32 ac_mu_mimo_brpoll_2; /* MU user 2 */ + u32 ac_mu_mimo_brpoll_3; /* MU user 3 */ +}; + +struct htt_tx_selfgen_ax_stats_tlv { + /* 11AX */ + u32 ax_su_ndpa; + u32 ax_su_ndp; + u32 ax_mu_mimo_ndpa; + u32 ax_mu_mimo_ndp; + u32 ax_mu_mimo_brpoll_1; /* MU user 1 */ + u32 ax_mu_mimo_brpoll_2; /* MU user 2 */ + u32 ax_mu_mimo_brpoll_3; /* MU user 3 */ + u32 ax_mu_mimo_brpoll_4; /* MU user 4 */ + u32 ax_mu_mimo_brpoll_5; /* MU user 5 */ + u32 ax_mu_mimo_brpoll_6; /* MU user 6 */ + u32 ax_mu_mimo_brpoll_7; /* MU user 7 */ + u32 ax_basic_trigger; + u32 ax_bsr_trigger; + u32 ax_mu_bar_trigger; + u32 ax_mu_rts_trigger; +}; + +struct htt_tx_selfgen_ac_err_stats_tlv { + /* 11AC error stats */ + u32 ac_su_ndp_err; + u32 ac_su_ndpa_err; + u32 ac_mu_mimo_ndpa_err; + u32 ac_mu_mimo_ndp_err; + u32 ac_mu_mimo_brp1_err; + u32 ac_mu_mimo_brp2_err; + u32 ac_mu_mimo_brp3_err; +}; + +struct htt_tx_selfgen_ax_err_stats_tlv { + /* 11AX error stats */ + u32 ax_su_ndp_err; + u32 ax_su_ndpa_err; + u32 ax_mu_mimo_ndpa_err; + u32 ax_mu_mimo_ndp_err; + u32 ax_mu_mimo_brp1_err; + u32 ax_mu_mimo_brp2_err; + u32 ax_mu_mimo_brp3_err; + u32 ax_mu_mimo_brp4_err; + u32 ax_mu_mimo_brp5_err; + u32 ax_mu_mimo_brp6_err; + u32 ax_mu_mimo_brp7_err; + u32 ax_basic_trigger_err; + u32 ax_bsr_trigger_err; + u32 ax_mu_bar_trigger_err; + u32 ax_mu_rts_trigger_err; +}; + +/* == TX MU STATS == */ +#define HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS 4 +#define HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS 8 +#define HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS 74 + +struct htt_tx_pdev_mu_mimo_sch_stats_tlv { + /* mu-mimo sw sched cmd stats */ + u32 mu_mimo_sch_posted; + u32 mu_mimo_sch_failed; + /* MU PPDU stats per hwQ */ + u32 mu_mimo_ppdu_posted; + /* + * Counts the number of users in each transmission of + * the given TX mode. + * + * Index is the number of users - 1. + */ + u32 ac_mu_mimo_sch_nusers[HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS]; + u32 ax_mu_mimo_sch_nusers[HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS]; + u32 ax_ofdma_sch_nusers[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; +}; + +struct htt_tx_pdev_mu_mimo_mpdu_stats_tlv { + u32 mu_mimo_mpdus_queued_usr; + u32 mu_mimo_mpdus_tried_usr; + u32 mu_mimo_mpdus_failed_usr; + u32 mu_mimo_mpdus_requeued_usr; + u32 mu_mimo_err_no_ba_usr; + u32 mu_mimo_mpdu_underrun_usr; + u32 mu_mimo_ampdu_underrun_usr; + + u32 ax_mu_mimo_mpdus_queued_usr; + u32 ax_mu_mimo_mpdus_tried_usr; + u32 ax_mu_mimo_mpdus_failed_usr; + u32 ax_mu_mimo_mpdus_requeued_usr; + u32 ax_mu_mimo_err_no_ba_usr; + u32 ax_mu_mimo_mpdu_underrun_usr; + u32 ax_mu_mimo_ampdu_underrun_usr; + + u32 ax_ofdma_mpdus_queued_usr; + u32 ax_ofdma_mpdus_tried_usr; + u32 ax_ofdma_mpdus_failed_usr; + u32 ax_ofdma_mpdus_requeued_usr; + u32 ax_ofdma_err_no_ba_usr; + u32 ax_ofdma_mpdu_underrun_usr; + u32 ax_ofdma_ampdu_underrun_usr; +}; + +#define HTT_STATS_TX_SCHED_MODE_MU_MIMO_AC 1 +#define HTT_STATS_TX_SCHED_MODE_MU_MIMO_AX 2 +#define HTT_STATS_TX_SCHED_MODE_MU_OFDMA_AX 3 + +struct htt_tx_pdev_mpdu_stats_tlv { + /* mpdu level stats */ + u32 mpdus_queued_usr; + u32 mpdus_tried_usr; + u32 mpdus_failed_usr; + u32 mpdus_requeued_usr; + u32 err_no_ba_usr; + u32 mpdu_underrun_usr; + u32 ampdu_underrun_usr; + u32 user_index; + u32 tx_sched_mode; /* HTT_STATS_TX_SCHED_MODE_xxx */ +}; + +/* == TX SCHED STATS == */ +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_cmd_posted_tlv_v { + u32 sched_cmd_posted[0]; /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_cmd_reaped_tlv_v { + u32 sched_cmd_reaped[0]; /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_sched_order_su_tlv_v { + u32 sched_order_su[0]; /* HTT_TX_PDEV_NUM_SCHED_ORDER_LOG */ +}; + +enum htt_sched_txq_sched_ineligibility_tlv_enum { + HTT_SCHED_TID_SKIP_SCHED_MASK_DISABLED = 0, + HTT_SCHED_TID_SKIP_NOTIFY_MPDU, + HTT_SCHED_TID_SKIP_MPDU_STATE_INVALID, + HTT_SCHED_TID_SKIP_SCHED_DISABLED, + HTT_SCHED_TID_SKIP_TQM_BYPASS_CMD_PENDING, + HTT_SCHED_TID_SKIP_SECOND_SU_SCHEDULE, + + HTT_SCHED_TID_SKIP_CMD_SLOT_NOT_AVAIL, + HTT_SCHED_TID_SKIP_NO_ENQ, + HTT_SCHED_TID_SKIP_LOW_ENQ, + HTT_SCHED_TID_SKIP_PAUSED, + HTT_SCHED_TID_SKIP_UL, + HTT_SCHED_TID_REMOVE_PAUSED, + HTT_SCHED_TID_REMOVE_NO_ENQ, + HTT_SCHED_TID_REMOVE_UL, + HTT_SCHED_TID_QUERY, + HTT_SCHED_TID_SU_ONLY, + HTT_SCHED_TID_ELIGIBLE, + HTT_SCHED_INELIGIBILITY_MAX, +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_sched_ineligibility_tlv_v { + /* indexed by htt_sched_txq_sched_ineligibility_tlv_enum */ + u32 sched_ineligibility[0]; +}; + +struct htt_tx_pdev_stats_sched_per_txq_tlv { + u32 mac_id__txq_id__word; + u32 sched_policy; + u32 last_sched_cmd_posted_timestamp; + u32 last_sched_cmd_compl_timestamp; + u32 sched_2_tac_lwm_count; + u32 sched_2_tac_ring_full; + u32 sched_cmd_post_failure; + u32 num_active_tids; + u32 num_ps_schedules; + u32 sched_cmds_pending; + u32 num_tid_register; + u32 num_tid_unregister; + u32 num_qstats_queried; + u32 qstats_update_pending; + u32 last_qstats_query_timestamp; + u32 num_tqm_cmdq_full; + u32 num_de_sched_algo_trigger; + u32 num_rt_sched_algo_trigger; + u32 num_tqm_sched_algo_trigger; + u32 notify_sched; + u32 dur_based_sendn_term; +}; + +struct htt_stats_tx_sched_cmn_tlv { + /* BIT [ 7 : 0] :- mac_id + * BIT [31 : 8] :- reserved + */ + u32 mac_id__word; + /* Current timestamp */ + u32 current_timestamp; +}; + +/* == TQM STATS == */ +#define HTT_TX_TQM_MAX_GEN_MPDU_END_REASON 16 +#define HTT_TX_TQM_MAX_LIST_MPDU_END_REASON 16 +#define HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS 16 + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_tqm_gen_mpdu_stats_tlv_v { + u32 gen_mpdu_end_reason[0]; /* HTT_TX_TQM_MAX_GEN_MPDU_END_REASON */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_tqm_list_mpdu_stats_tlv_v { + u32 list_mpdu_end_reason[0]; /* HTT_TX_TQM_MAX_LIST_MPDU_END_REASON */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_tqm_list_mpdu_cnt_tlv_v { + u32 list_mpdu_cnt_hist[0]; + /* HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS */ +}; + +struct htt_tx_tqm_pdev_stats_tlv_v { + u32 msdu_count; + u32 mpdu_count; + u32 remove_msdu; + u32 remove_mpdu; + u32 remove_msdu_ttl; + u32 send_bar; + u32 bar_sync; + u32 notify_mpdu; + u32 sync_cmd; + u32 write_cmd; + u32 hwsch_trigger; + u32 ack_tlv_proc; + u32 gen_mpdu_cmd; + u32 gen_list_cmd; + u32 remove_mpdu_cmd; + u32 remove_mpdu_tried_cmd; + u32 mpdu_queue_stats_cmd; + u32 mpdu_head_info_cmd; + u32 msdu_flow_stats_cmd; + u32 remove_msdu_cmd; + u32 remove_msdu_ttl_cmd; + u32 flush_cache_cmd; + u32 update_mpduq_cmd; + u32 enqueue; + u32 enqueue_notify; + u32 notify_mpdu_at_head; + u32 notify_mpdu_state_valid; + /* + * On receiving TQM_FLOW_NOT_EMPTY_STATUS from TQM, (on MSDUs being enqueued + * the flow is non empty), if the number of MSDUs is greater than the threshold, + * notify is incremented. UDP_THRESH counters are for UDP MSDUs, and NONUDP are + * for non-UDP MSDUs. + * MSDUQ_SWNOTIFY_UDP_THRESH1 threshold - sched_udp_notify1 is incremented + * MSDUQ_SWNOTIFY_UDP_THRESH2 threshold - sched_udp_notify2 is incremented + * MSDUQ_SWNOTIFY_NONUDP_THRESH1 threshold - sched_nonudp_notify1 is incremented + * MSDUQ_SWNOTIFY_NONUDP_THRESH2 threshold - sched_nonudp_notify2 is incremented + * + * Notify signifies that we trigger the scheduler. + */ + u32 sched_udp_notify1; + u32 sched_udp_notify2; + u32 sched_nonudp_notify1; + u32 sched_nonudp_notify2; +}; + +struct htt_tx_tqm_cmn_stats_tlv { + u32 mac_id__word; + u32 max_cmdq_id; + u32 list_mpdu_cnt_hist_intvl; + + /* Global stats */ + u32 add_msdu; + u32 q_empty; + u32 q_not_empty; + u32 drop_notification; + u32 desc_threshold; +}; + +struct htt_tx_tqm_error_stats_tlv { + /* Error stats */ + u32 q_empty_failure; + u32 q_not_empty_failure; + u32 add_msdu_failure; +}; + +/* == TQM CMDQ stats == */ +struct htt_tx_tqm_cmdq_status_tlv { + u32 mac_id__cmdq_id__word; + u32 sync_cmd; + u32 write_cmd; + u32 gen_mpdu_cmd; + u32 mpdu_queue_stats_cmd; + u32 mpdu_head_info_cmd; + u32 msdu_flow_stats_cmd; + u32 remove_mpdu_cmd; + u32 remove_msdu_cmd; + u32 flush_cache_cmd; + u32 update_mpduq_cmd; + u32 update_msduq_cmd; +}; + +/* == TX-DE STATS == */ +/* Structures for tx de stats */ +struct htt_tx_de_eapol_packets_stats_tlv { + u32 m1_packets; + u32 m2_packets; + u32 m3_packets; + u32 m4_packets; + u32 g1_packets; + u32 g2_packets; +}; + +struct htt_tx_de_classify_failed_stats_tlv { + u32 ap_bss_peer_not_found; + u32 ap_bcast_mcast_no_peer; + u32 sta_delete_in_progress; + u32 ibss_no_bss_peer; + u32 invalid_vdev_type; + u32 invalid_ast_peer_entry; + u32 peer_entry_invalid; + u32 ethertype_not_ip; + u32 eapol_lookup_failed; + u32 qpeer_not_allow_data; + u32 fse_tid_override; + u32 ipv6_jumbogram_zero_length; + u32 qos_to_non_qos_in_prog; +}; + +struct htt_tx_de_classify_stats_tlv { + u32 arp_packets; + u32 igmp_packets; + u32 dhcp_packets; + u32 host_inspected; + u32 htt_included; + u32 htt_valid_mcs; + u32 htt_valid_nss; + u32 htt_valid_preamble_type; + u32 htt_valid_chainmask; + u32 htt_valid_guard_interval; + u32 htt_valid_retries; + u32 htt_valid_bw_info; + u32 htt_valid_power; + u32 htt_valid_key_flags; + u32 htt_valid_no_encryption; + u32 fse_entry_count; + u32 fse_priority_be; + u32 fse_priority_high; + u32 fse_priority_low; + u32 fse_traffic_ptrn_be; + u32 fse_traffic_ptrn_over_sub; + u32 fse_traffic_ptrn_bursty; + u32 fse_traffic_ptrn_interactive; + u32 fse_traffic_ptrn_periodic; + u32 fse_hwqueue_alloc; + u32 fse_hwqueue_created; + u32 fse_hwqueue_send_to_host; + u32 mcast_entry; + u32 bcast_entry; + u32 htt_update_peer_cache; + u32 htt_learning_frame; + u32 fse_invalid_peer; + /* + * mec_notify is HTT TX WBM multicast echo check notification + * from firmware to host. FW sends SA addresses to host for all + * multicast/broadcast packets received on STA side. + */ + u32 mec_notify; +}; + +struct htt_tx_de_classify_status_stats_tlv { + u32 eok; + u32 classify_done; + u32 lookup_failed; + u32 send_host_dhcp; + u32 send_host_mcast; + u32 send_host_unknown_dest; + u32 send_host; + u32 status_invalid; +}; + +struct htt_tx_de_enqueue_packets_stats_tlv { + u32 enqueued_pkts; + u32 to_tqm; + u32 to_tqm_bypass; +}; + +struct htt_tx_de_enqueue_discard_stats_tlv { + u32 discarded_pkts; + u32 local_frames; + u32 is_ext_msdu; +}; + +struct htt_tx_de_compl_stats_tlv { + u32 tcl_dummy_frame; + u32 tqm_dummy_frame; + u32 tqm_notify_frame; + u32 fw2wbm_enq; + u32 tqm_bypass_frame; +}; + +/* + * The htt_tx_de_fw2wbm_ring_full_hist_tlv is a histogram of time we waited + * for the fw2wbm ring buffer. we are requesting a buffer in FW2WBM release + * ring,which may fail, due to non availability of buffer. Hence we sleep for + * 200us & again request for it. This is a histogram of time we wait, with + * bin of 200ms & there are 10 bin (2 seconds max) + * They are defined by the following macros in FW + * #define ENTRIES_PER_BIN_COUNT 1000 // per bin 1000 * 200us = 200ms + * #define RING_FULL_BIN_ENTRIES (WAL_TX_DE_FW2WBM_ALLOC_TIMEOUT_COUNT / + * ENTRIES_PER_BIN_COUNT) + */ +struct htt_tx_de_fw2wbm_ring_full_hist_tlv { + u32 fw2wbm_ring_full_hist[0]; +}; + +struct htt_tx_de_cmn_stats_tlv { + u32 mac_id__word; + + /* Global Stats */ + u32 tcl2fw_entry_count; + u32 not_to_fw; + u32 invalid_pdev_vdev_peer; + u32 tcl_res_invalid_addrx; + u32 wbm2fw_entry_count; + u32 invalid_pdev; +}; + +/* == RING-IF STATS == */ +#define HTT_STATS_LOW_WM_BINS 5 +#define HTT_STATS_HIGH_WM_BINS 5 + +struct htt_ring_if_stats_tlv { + u32 base_addr; /* DWORD aligned base memory address of the ring */ + u32 elem_size; + u32 num_elems__prefetch_tail_idx; + u32 head_idx__tail_idx; + u32 shadow_head_idx__shadow_tail_idx; + u32 num_tail_incr; + u32 lwm_thresh__hwm_thresh; + u32 overrun_hit_count; + u32 underrun_hit_count; + u32 prod_blockwait_count; + u32 cons_blockwait_count; + u32 low_wm_hit_count[HTT_STATS_LOW_WM_BINS]; + u32 high_wm_hit_count[HTT_STATS_HIGH_WM_BINS]; +}; + +struct htt_ring_if_cmn_tlv { + u32 mac_id__word; + u32 num_records; +}; + +/* == SFM STATS == */ +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sfm_client_user_tlv_v { + /* Number of DWORDS used per user and per client */ + u32 dwords_used_by_user_n[0]; +}; + +struct htt_sfm_client_tlv { + /* Client ID */ + u32 client_id; + /* Minimum number of buffers */ + u32 buf_min; + /* Maximum number of buffers */ + u32 buf_max; + /* Number of Busy buffers */ + u32 buf_busy; + /* Number of Allocated buffers */ + u32 buf_alloc; + /* Number of Available/Usable buffers */ + u32 buf_avail; + /* Number of users */ + u32 num_users; +}; + +struct htt_sfm_cmn_tlv { + u32 mac_id__word; + /* Indicates the total number of 128 byte buffers + * in the CMEM that are available for buffer sharing + */ + u32 buf_total; + /* Indicates for certain client or all the clients + * there is no dowrd saved in SFM, refer to SFM_R1_MEM_EMPTY + */ + u32 mem_empty; + /* DEALLOCATE_BUFFERS, refer to register SFM_R0_DEALLOCATE_BUFFERS */ + u32 deallocate_bufs; + /* Number of Records */ + u32 num_records; +}; + +/* == SRNG STATS == */ +struct htt_sring_stats_tlv { + u32 mac_id__ring_id__arena__ep; + u32 base_addr_lsb; /* DWORD aligned base memory address of the ring */ + u32 base_addr_msb; + u32 ring_size; + u32 elem_size; + + u32 num_avail_words__num_valid_words; + u32 head_ptr__tail_ptr; + u32 consumer_empty__producer_full; + u32 prefetch_count__internal_tail_ptr; +}; + +struct htt_sring_cmn_tlv { + u32 num_records; +}; + +/* == PDEV TX RATE CTRL STATS == */ +#define HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define HTT_TX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT +#define HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define HTT_TX_PDEV_STATS_NUM_LTF 4 + +#define HTT_TX_NUM_OF_SOUNDING_STATS_WORDS \ + (HTT_TX_PDEV_STATS_NUM_BW_COUNTERS * \ + HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS) + +struct htt_tx_pdev_rate_stats_tlv { + u32 mac_id__word; + u32 tx_ldpc; + u32 rts_cnt; + /* RSSI value of last ack packet (units = dB above noise floor) */ + u32 ack_rssi; + + u32 tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + u32 tx_su_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 tx_mu_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 tx_stbc[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 tx_pream[HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + + /* Counters to track number of tx packets + * in each GI (400us, 800us, 1600us & 3200us) in each mcs (0-11) + */ + u32 tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS][HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + /* Counters to track packets in dcm mcs (MCS 0, 1, 3, 4) */ + u32 tx_dcm[HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS]; + /* Number of CTS-acknowledged RTS packets */ + u32 rts_success; + + /* + * Counters for legacy 11a and 11b transmissions. + * + * The index corresponds to: + * + * CCK: 0: 1 Mbps, 1: 2 Mbps, 2: 5.5 Mbps, 3: 11 Mbps + * + * OFDM: 0: 6 Mbps, 1: 9 Mbps, 2: 12 Mbps, 3: 18 Mbps, + * 4: 24 Mbps, 5: 36 Mbps, 6: 48 Mbps, 7: 54 Mbps + */ + u32 tx_legacy_cck_rate[HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + u32 tx_legacy_ofdm_rate[HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + + u32 ac_mu_mimo_tx_ldpc; + u32 ax_mu_mimo_tx_ldpc; + u32 ofdma_tx_ldpc; + + /* + * Counters for 11ax HE LTF selection during TX. + * + * The index corresponds to: + * + * 0: unused, 1: 1x LTF, 2: 2x LTF, 3: 4x LTF + */ + u32 tx_he_ltf[HTT_TX_PDEV_STATS_NUM_LTF]; + + u32 ac_mu_mimo_tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ax_mu_mimo_tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ofdma_tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + u32 ac_mu_mimo_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 ax_mu_mimo_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 ofdma_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + + u32 ac_mu_mimo_tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 ax_mu_mimo_tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 ofdma_tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + + u32 ac_mu_mimo_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ax_mu_mimo_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ofdma_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; +}; + +/* == PDEV RX RATE CTRL STATS == */ +#define HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define HTT_RX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define HTT_RX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT +#define HTT_RX_PDEV_MAX_OFDMA_NUM_USER 8 +#define HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_PER_NSS 16 + +struct htt_rx_pdev_rate_stats_tlv { + u32 mac_id__word; + u32 nsts; + + u32 rx_ldpc; + u32 rts_cnt; + + u32 rssi_mgmt; /* units = dB above noise floor */ + u32 rssi_data; /* units = dB above noise floor */ + u32 rssi_comb; /* units = dB above noise floor */ + u32 rx_mcs[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 rx_nss[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 rx_dcm[HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS]; + u32 rx_stbc[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 rx_bw[HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 rx_pream[HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + u8 rssi_chain[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + /* units = dB above noise floor */ + + /* Counters to track number of rx packets + * in each GI in each mcs (0-11) + */ + u32 rx_gi[HTT_RX_PDEV_STATS_NUM_GI_COUNTERS][HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + s32 rssi_in_dbm; /* rx Signal Strength value in dBm unit */ + + u32 rx_11ax_su_ext; + u32 rx_11ac_mumimo; + u32 rx_11ax_mumimo; + u32 rx_11ax_ofdma; + u32 txbf; + u32 rx_legacy_cck_rate[HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + u32 rx_legacy_ofdm_rate[HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + u32 rx_active_dur_us_low; + u32 rx_active_dur_us_high; + + u32 rx_11ax_ul_ofdma; + + u32 ul_ofdma_rx_mcs[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ul_ofdma_rx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ul_ofdma_rx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 ul_ofdma_rx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 ul_ofdma_rx_stbc; + u32 ul_ofdma_rx_ldpc; + + /* record the stats for each user index */ + u32 rx_ulofdma_non_data_ppdu[HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; /* ppdu level */ + u32 rx_ulofdma_data_ppdu[HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; /* ppdu level */ + u32 rx_ulofdma_mpdu_ok[HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; /* mpdu level */ + u32 rx_ulofdma_mpdu_fail[HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; /* mpdu level */ + + u32 nss_count; + u32 pilot_count; + /* RxEVM stats in dB */ + s32 rx_pilot_evm_db[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_PER_NSS]; + /* rx_pilot_evm_db_mean: + * EVM mean across pilots, computed as + * mean(10*log10(rx_pilot_evm_linear)) = mean(rx_pilot_evm_db) + */ + s32 rx_pilot_evm_db_mean[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + s8 rx_ul_fd_rssi[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; /* dBm units */ + /* per_chain_rssi_pkt_type: + * This field shows what type of rx frame the per-chain RSSI was computed + * on, by recording the frame type and sub-type as bit-fields within this + * field: + * BIT [3 : 0] :- IEEE80211_FC0_TYPE + * BIT [7 : 4] :- IEEE80211_FC0_SUBTYPE + * BIT [31 : 8] :- Reserved + */ + u32 per_chain_rssi_pkt_type; + s8 rx_per_chain_rssi_in_dbm[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; +}; + +/* == RX PDEV/SOC STATS == */ +struct htt_rx_soc_fw_stats_tlv { + u32 fw_reo_ring_data_msdu; + u32 fw_to_host_data_msdu_bcmc; + u32 fw_to_host_data_msdu_uc; + u32 ofld_remote_data_buf_recycle_cnt; + u32 ofld_remote_free_buf_indication_cnt; + + u32 ofld_buf_to_host_data_msdu_uc; + u32 reo_fw_ring_to_host_data_msdu_uc; + + u32 wbm_sw_ring_reap; + u32 wbm_forward_to_host_cnt; + u32 wbm_target_recycle_cnt; + + u32 target_refill_ring_recycle_cnt; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_empty_tlv_v { + u32 refill_ring_empty_cnt[0]; /* HTT_RX_STATS_REFILL_MAX_RING */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_num_refill_tlv_v { + u32 refill_ring_num_refill[0]; /* HTT_RX_STATS_REFILL_MAX_RING */ +}; + +/* RXDMA error code from WBM released packets */ +enum htt_rx_rxdma_error_code_enum { + HTT_RX_RXDMA_OVERFLOW_ERR = 0, + HTT_RX_RXDMA_MPDU_LENGTH_ERR = 1, + HTT_RX_RXDMA_FCS_ERR = 2, + HTT_RX_RXDMA_DECRYPT_ERR = 3, + HTT_RX_RXDMA_TKIP_MIC_ERR = 4, + HTT_RX_RXDMA_UNECRYPTED_ERR = 5, + HTT_RX_RXDMA_MSDU_LEN_ERR = 6, + HTT_RX_RXDMA_MSDU_LIMIT_ERR = 7, + HTT_RX_RXDMA_WIFI_PARSE_ERR = 8, + HTT_RX_RXDMA_AMSDU_PARSE_ERR = 9, + HTT_RX_RXDMA_SA_TIMEOUT_ERR = 10, + HTT_RX_RXDMA_DA_TIMEOUT_ERR = 11, + HTT_RX_RXDMA_FLOW_TIMEOUT_ERR = 12, + HTT_RX_RXDMA_FLUSH_REQUEST = 13, + HTT_RX_RXDMA_ERR_CODE_RVSD0 = 14, + HTT_RX_RXDMA_ERR_CODE_RVSD1 = 15, + + /* This MAX_ERR_CODE should not be used in any host/target messages, + * so that even though it is defined within a host/target interface + * definition header file, it isn't actually part of the host/target + * interface, and thus can be modified. + */ + HTT_RX_RXDMA_MAX_ERR_CODE +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v { + u32 rxdma_err[0]; /* HTT_RX_RXDMA_MAX_ERR_CODE */ +}; + +/* REO error code from WBM released packets */ +enum htt_rx_reo_error_code_enum { + HTT_RX_REO_QUEUE_DESC_ADDR_ZERO = 0, + HTT_RX_REO_QUEUE_DESC_NOT_VALID = 1, + HTT_RX_AMPDU_IN_NON_BA = 2, + HTT_RX_NON_BA_DUPLICATE = 3, + HTT_RX_BA_DUPLICATE = 4, + HTT_RX_REGULAR_FRAME_2K_JUMP = 5, + HTT_RX_BAR_FRAME_2K_JUMP = 6, + HTT_RX_REGULAR_FRAME_OOR = 7, + HTT_RX_BAR_FRAME_OOR = 8, + HTT_RX_BAR_FRAME_NO_BA_SESSION = 9, + HTT_RX_BAR_FRAME_SN_EQUALS_SSN = 10, + HTT_RX_PN_CHECK_FAILED = 11, + HTT_RX_2K_ERROR_HANDLING_FLAG_SET = 12, + HTT_RX_PN_ERROR_HANDLING_FLAG_SET = 13, + HTT_RX_QUEUE_DESCRIPTOR_BLOCKED_SET = 14, + HTT_RX_REO_ERR_CODE_RVSD = 15, + + /* This MAX_ERR_CODE should not be used in any host/target messages, + * so that even though it is defined within a host/target interface + * definition header file, it isn't actually part of the host/target + * interface, and thus can be modified. + */ + HTT_RX_REO_MAX_ERR_CODE +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_num_reo_err_tlv_v { + u32 reo_err[0]; /* HTT_RX_REO_MAX_ERR_CODE */ +}; + +/* == RX PDEV STATS == */ +#define HTT_STATS_SUBTYPE_MAX 16 + +struct htt_rx_pdev_fw_stats_tlv { + u32 mac_id__word; + u32 ppdu_recvd; + u32 mpdu_cnt_fcs_ok; + u32 mpdu_cnt_fcs_err; + u32 tcp_msdu_cnt; + u32 tcp_ack_msdu_cnt; + u32 udp_msdu_cnt; + u32 other_msdu_cnt; + u32 fw_ring_mpdu_ind; + u32 fw_ring_mgmt_subtype[HTT_STATS_SUBTYPE_MAX]; + u32 fw_ring_ctrl_subtype[HTT_STATS_SUBTYPE_MAX]; + u32 fw_ring_mcast_data_msdu; + u32 fw_ring_bcast_data_msdu; + u32 fw_ring_ucast_data_msdu; + u32 fw_ring_null_data_msdu; + u32 fw_ring_mpdu_drop; + u32 ofld_local_data_ind_cnt; + u32 ofld_local_data_buf_recycle_cnt; + u32 drx_local_data_ind_cnt; + u32 drx_local_data_buf_recycle_cnt; + u32 local_nondata_ind_cnt; + u32 local_nondata_buf_recycle_cnt; + + u32 fw_status_buf_ring_refill_cnt; + u32 fw_status_buf_ring_empty_cnt; + u32 fw_pkt_buf_ring_refill_cnt; + u32 fw_pkt_buf_ring_empty_cnt; + u32 fw_link_buf_ring_refill_cnt; + u32 fw_link_buf_ring_empty_cnt; + + u32 host_pkt_buf_ring_refill_cnt; + u32 host_pkt_buf_ring_empty_cnt; + u32 mon_pkt_buf_ring_refill_cnt; + u32 mon_pkt_buf_ring_empty_cnt; + u32 mon_status_buf_ring_refill_cnt; + u32 mon_status_buf_ring_empty_cnt; + u32 mon_desc_buf_ring_refill_cnt; + u32 mon_desc_buf_ring_empty_cnt; + u32 mon_dest_ring_update_cnt; + u32 mon_dest_ring_full_cnt; + + u32 rx_suspend_cnt; + u32 rx_suspend_fail_cnt; + u32 rx_resume_cnt; + u32 rx_resume_fail_cnt; + u32 rx_ring_switch_cnt; + u32 rx_ring_restore_cnt; + u32 rx_flush_cnt; + u32 rx_recovery_reset_cnt; +}; + +#define HTT_STATS_PHY_ERR_MAX 43 + +struct htt_rx_pdev_fw_stats_phy_err_tlv { + u32 mac_id__word; + u32 total_phy_err_cnt; + /* Counts of different types of phy errs + * The mapping of PHY error types to phy_err array elements is HW dependent. + * The only currently-supported mapping is shown below: + * + * 0 phyrx_err_phy_off Reception aborted due to receiving a PHY_OFF TLV + * 1 phyrx_err_synth_off + * 2 phyrx_err_ofdma_timing + * 3 phyrx_err_ofdma_signal_parity + * 4 phyrx_err_ofdma_rate_illegal + * 5 phyrx_err_ofdma_length_illegal + * 6 phyrx_err_ofdma_restart + * 7 phyrx_err_ofdma_service + * 8 phyrx_err_ppdu_ofdma_power_drop + * 9 phyrx_err_cck_blokker + * 10 phyrx_err_cck_timing + * 11 phyrx_err_cck_header_crc + * 12 phyrx_err_cck_rate_illegal + * 13 phyrx_err_cck_length_illegal + * 14 phyrx_err_cck_restart + * 15 phyrx_err_cck_service + * 16 phyrx_err_cck_power_drop + * 17 phyrx_err_ht_crc_err + * 18 phyrx_err_ht_length_illegal + * 19 phyrx_err_ht_rate_illegal + * 20 phyrx_err_ht_zlf + * 21 phyrx_err_false_radar_ext + * 22 phyrx_err_green_field + * 23 phyrx_err_bw_gt_dyn_bw + * 24 phyrx_err_leg_ht_mismatch + * 25 phyrx_err_vht_crc_error + * 26 phyrx_err_vht_siga_unsupported + * 27 phyrx_err_vht_lsig_len_invalid + * 28 phyrx_err_vht_ndp_or_zlf + * 29 phyrx_err_vht_nsym_lt_zero + * 30 phyrx_err_vht_rx_extra_symbol_mismatch + * 31 phyrx_err_vht_rx_skip_group_id0 + * 32 phyrx_err_vht_rx_skip_group_id1to62 + * 33 phyrx_err_vht_rx_skip_group_id63 + * 34 phyrx_err_ofdm_ldpc_decoder_disabled + * 35 phyrx_err_defer_nap + * 36 phyrx_err_fdomain_timeout + * 37 phyrx_err_lsig_rel_check + * 38 phyrx_err_bt_collision + * 39 phyrx_err_unsupported_mu_feedback + * 40 phyrx_err_ppdu_tx_interrupt_rx + * 41 phyrx_err_unsupported_cbf + * 42 phyrx_err_other + */ + u32 phy_err[HTT_STATS_PHY_ERR_MAX]; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_pdev_fw_ring_mpdu_err_tlv_v { + /* Num error MPDU for each RxDMA error type */ + u32 fw_ring_mpdu_err[0]; /* HTT_RX_STATS_RXDMA_MAX_ERR */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_pdev_fw_mpdu_drop_tlv_v { + /* Num MPDU dropped */ + u32 fw_mpdu_drop[0]; /* HTT_RX_STATS_FW_DROP_REASON_MAX */ +}; + +#define HTT_PDEV_CCA_STATS_TX_FRAME_INFO_PRESENT (0x1) +#define HTT_PDEV_CCA_STATS_RX_FRAME_INFO_PRESENT (0x2) +#define HTT_PDEV_CCA_STATS_RX_CLEAR_INFO_PRESENT (0x4) +#define HTT_PDEV_CCA_STATS_MY_RX_FRAME_INFO_PRESENT (0x8) +#define HTT_PDEV_CCA_STATS_USEC_CNT_INFO_PRESENT (0x10) +#define HTT_PDEV_CCA_STATS_MED_RX_IDLE_INFO_PRESENT (0x20) +#define HTT_PDEV_CCA_STATS_MED_TX_IDLE_GLOBAL_INFO_PRESENT (0x40) +#define HTT_PDEV_CCA_STATS_CCA_OBBS_USEC_INFO_PRESENT (0x80) + +struct htt_pdev_stats_cca_counters_tlv { + /* Below values are obtained from the HW Cycles counter registers */ + u32 tx_frame_usec; + u32 rx_frame_usec; + u32 rx_clear_usec; + u32 my_rx_frame_usec; + u32 usec_cnt; + u32 med_rx_idle_usec; + u32 med_tx_idle_global_usec; + u32 cca_obss_usec; +}; + +struct htt_pdev_cca_stats_hist_v1_tlv { + u32 chan_num; + /* num of CCA records (Num of htt_pdev_stats_cca_counters_tlv)*/ + u32 num_records; + u32 valid_cca_counters_bitmap; + u32 collection_interval; + + /* This will be followed by an array which contains the CCA stats + * collected in the last N intervals, + * if the indication is for last N intervals CCA stats. + * Then the pdev_cca_stats[0] element contains the oldest CCA stats + * and pdev_cca_stats[N-1] will have the most recent CCA stats. + * htt_pdev_stats_cca_counters_tlv cca_hist_tlv[1]; + */ +}; + +struct htt_pdev_stats_twt_session_tlv { + u32 vdev_id; + struct htt_mac_addr peer_mac; + u32 flow_id_flags; + + /* TWT_DIALOG_ID_UNAVAILABLE is used + * when TWT session is not initiated by host + */ + u32 dialog_id; + u32 wake_dura_us; + u32 wake_intvl_us; + u32 sp_offset_us; +}; + +struct htt_pdev_stats_twt_sessions_tlv { + u32 pdev_id; + u32 num_sessions; + struct htt_pdev_stats_twt_session_tlv twt_session[0]; +}; + +enum htt_rx_reo_resource_sample_id_enum { + /* Global link descriptor queued in REO */ + HTT_RX_REO_RESOURCE_GLOBAL_LINK_DESC_COUNT_0 = 0, + HTT_RX_REO_RESOURCE_GLOBAL_LINK_DESC_COUNT_1 = 1, + HTT_RX_REO_RESOURCE_GLOBAL_LINK_DESC_COUNT_2 = 2, + /*Number of queue descriptors of this aging group */ + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC0 = 3, + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC1 = 4, + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC2 = 5, + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC3 = 6, + /* Total number of MSDUs buffered in AC */ + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC0 = 7, + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC1 = 8, + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC2 = 9, + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC3 = 10, + + HTT_RX_REO_RESOURCE_STATS_MAX = 16 +}; + +struct htt_rx_reo_resource_stats_tlv_v { + /* Variable based on the Number of records. HTT_RX_REO_RESOURCE_STATS_MAX */ + u32 sample_id; + u32 total_max; + u32 total_avg; + u32 total_sample; + u32 non_zeros_avg; + u32 non_zeros_sample; + u32 last_non_zeros_max; + u32 last_non_zeros_min; + u32 last_non_zeros_avg; + u32 last_non_zeros_sample; +}; + +/* == TX SOUNDING STATS == */ + +enum htt_txbf_sound_steer_modes { + HTT_IMPLICIT_TXBF_STEER_STATS = 0, + HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS = 1, + HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS = 2, + HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS = 3, + HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS = 4, + HTT_TXBF_MAX_NUM_OF_MODES = 5 +}; + +enum htt_stats_sounding_tx_mode { + HTT_TX_AC_SOUNDING_MODE = 0, + HTT_TX_AX_SOUNDING_MODE = 1, +}; + +struct htt_tx_sounding_stats_tlv { + u32 tx_sounding_mode; /* HTT_TX_XX_SOUNDING_MODE */ + /* Counts number of soundings for all steering modes in each bw */ + u32 cbf_20[HTT_TXBF_MAX_NUM_OF_MODES]; + u32 cbf_40[HTT_TXBF_MAX_NUM_OF_MODES]; + u32 cbf_80[HTT_TXBF_MAX_NUM_OF_MODES]; + u32 cbf_160[HTT_TXBF_MAX_NUM_OF_MODES]; + /* + * The sounding array is a 2-D array stored as an 1-D array of + * u32. The stats for a particular user/bw combination is + * referenced with the following: + * + * sounding[(user* max_bw) + bw] + * + * ... where max_bw == 4 for 160mhz + */ + u32 sounding[HTT_TX_NUM_OF_SOUNDING_STATS_WORDS]; +}; + +struct htt_pdev_obss_pd_stats_tlv { + u32 num_obss_tx_ppdu_success; + u32 num_obss_tx_ppdu_failure; +}; + +void ath11k_debug_htt_stats_init(struct ath11k *ar); +#endif diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.c b/drivers/net/wireless/ath/ath11k/debugfs_sta.c new file mode 100644 index 000000000000..743760c9bcae --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/vmalloc.h> + +#include "core.h" +#include "peer.h" +#include "debug.h" + +void +ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx) +{ + struct rate_info *txrate = &arsta->txrate; + struct ath11k_htt_tx_stats *tx_stats; + int gi, mcs, bw, nss; + + if (!arsta->tx_stats) + return; + + tx_stats = arsta->tx_stats; + gi = FIELD_GET(RATE_INFO_FLAGS_SHORT_GI, arsta->txrate.flags); + mcs = txrate->mcs; + bw = txrate->bw; + nss = txrate->nss - 1; + +#define STATS_OP_FMT(name) tx_stats->stats[ATH11K_STATS_TYPE_##name] + + if (txrate->flags & RATE_INFO_FLAGS_HE_MCS) { + STATS_OP_FMT(SUCC).he[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).he[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).he[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).he[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).he[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).he[1][mcs] += peer_stats->retry_pkts; + } else if (txrate->flags & RATE_INFO_FLAGS_VHT_MCS) { + STATS_OP_FMT(SUCC).vht[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).vht[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).vht[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).vht[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).vht[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).vht[1][mcs] += peer_stats->retry_pkts; + } else if (txrate->flags & RATE_INFO_FLAGS_MCS) { + STATS_OP_FMT(SUCC).ht[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).ht[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).ht[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).ht[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).ht[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).ht[1][mcs] += peer_stats->retry_pkts; + } else { + mcs = legacy_rate_idx; + + STATS_OP_FMT(SUCC).legacy[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).legacy[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).legacy[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).legacy[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).legacy[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).legacy[1][mcs] += peer_stats->retry_pkts; + } + + if (peer_stats->is_ampdu) { + tx_stats->ba_fails += peer_stats->ba_fails; + + if (txrate->flags & RATE_INFO_FLAGS_HE_MCS) { + STATS_OP_FMT(AMPDU).he[0][mcs] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).he[1][mcs] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } else if (txrate->flags & RATE_INFO_FLAGS_MCS) { + STATS_OP_FMT(AMPDU).ht[0][mcs] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).ht[1][mcs] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } else { + STATS_OP_FMT(AMPDU).vht[0][mcs] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).vht[1][mcs] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } + STATS_OP_FMT(AMPDU).bw[0][bw] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).nss[0][nss] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).gi[0][gi] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).bw[1][bw] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + STATS_OP_FMT(AMPDU).nss[1][nss] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + STATS_OP_FMT(AMPDU).gi[1][gi] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } else { + tx_stats->ack_fails += peer_stats->ba_fails; + } + + STATS_OP_FMT(SUCC).bw[0][bw] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).nss[0][nss] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).gi[0][gi] += peer_stats->succ_bytes; + + STATS_OP_FMT(SUCC).bw[1][bw] += peer_stats->succ_pkts; + STATS_OP_FMT(SUCC).nss[1][nss] += peer_stats->succ_pkts; + STATS_OP_FMT(SUCC).gi[1][gi] += peer_stats->succ_pkts; + + STATS_OP_FMT(FAIL).bw[0][bw] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).nss[0][nss] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).gi[0][gi] += peer_stats->failed_bytes; + + STATS_OP_FMT(FAIL).bw[1][bw] += peer_stats->failed_pkts; + STATS_OP_FMT(FAIL).nss[1][nss] += peer_stats->failed_pkts; + STATS_OP_FMT(FAIL).gi[1][gi] += peer_stats->failed_pkts; + + STATS_OP_FMT(RETRY).bw[0][bw] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).nss[0][nss] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).gi[0][gi] += peer_stats->retry_bytes; + + STATS_OP_FMT(RETRY).bw[1][bw] += peer_stats->retry_pkts; + STATS_OP_FMT(RETRY).nss[1][nss] += peer_stats->retry_pkts; + STATS_OP_FMT(RETRY).gi[1][gi] += peer_stats->retry_pkts; + + tx_stats->tx_duration += peer_stats->duration; +} + +void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; + enum hal_tx_rate_stats_pkt_type pkt_type; + enum hal_tx_rate_stats_sgi sgi; + enum hal_tx_rate_stats_bw bw; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + struct ieee80211_sta *sta; + u16 rate; + u8 rate_idx; + int ret; + u8 mcs; + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ts->peer_id); + if (!peer || !peer->sta) { + ath11k_warn(ab, "failed to find the peer\n"); + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + return; + } + + sta = peer->sta; + arsta = (struct ath11k_sta *)sta->drv_priv; + + memset(&arsta->txrate, 0, sizeof(arsta->txrate)); + pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, + ts->rate_stats); + mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, + ts->rate_stats); + sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI, + ts->rate_stats); + bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats); + + if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A || + pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) { + ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, + pkt_type, + &rate_idx, + &rate); + if (ret < 0) + goto err_out; + arsta->txrate.legacy = rate; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) { + if (mcs > 7) { + ath11k_warn(ab, "Invalid HT mcs index %d\n", mcs); + goto err_out; + } + + arsta->txrate.mcs = mcs + 8 * (arsta->last_txrate.nss - 1); + arsta->txrate.flags = RATE_INFO_FLAGS_MCS; + if (sgi) + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) { + if (mcs > 9) { + ath11k_warn(ab, "Invalid VHT mcs index %d\n", mcs); + goto err_out; + } + + arsta->txrate.mcs = mcs; + arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + if (sgi) + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { + /* TODO */ + } + + arsta->txrate.nss = arsta->last_txrate.nss; + arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); + + ath11k_accumulate_per_peer_tx_stats(arsta, peer_stats, rate_idx); +err_out: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +} + +static ssize_t ath11k_dbg_sta_dump_tx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct ath11k_htt_data_stats *stats; + static const char *str_name[ATH11K_STATS_TYPE_MAX] = {"succ", "fail", + "retry", "ampdu"}; + static const char *str[ATH11K_COUNTER_TYPE_MAX] = {"bytes", "packets"}; + int len = 0, i, j, k, retval = 0; + const int size = 2 * 4096; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + for (k = 0; k < ATH11K_STATS_TYPE_MAX; k++) { + for (j = 0; j < ATH11K_COUNTER_TYPE_MAX; j++) { + stats = &arsta->tx_stats->stats[k]; + len += scnprintf(buf + len, size - len, "%s_%s\n", + str_name[k], + str[j]); + len += scnprintf(buf + len, size - len, + " HE MCS %s\n", + str[j]); + for (i = 0; i < ATH11K_HE_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", + stats->he[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " VHT MCS %s\n", + str[j]); + for (i = 0; i < ATH11K_VHT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", + stats->vht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, " HT MCS %s\n", + str[j]); + for (i = 0; i < ATH11K_HT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", stats->ht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " BW %s (20,40,80,160 MHz)\n", str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->bw[j][0], stats->bw[j][1], + stats->bw[j][2], stats->bw[j][3]); + len += scnprintf(buf + len, size - len, + " NSS %s (1x1,2x2,3x3,4x4)\n", str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->nss[j][0], stats->nss[j][1], + stats->nss[j][2], stats->nss[j][3]); + len += scnprintf(buf + len, size - len, + " GI %s (0.4us,0.8us,1.6us,3.2us)\n", + str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->gi[j][0], stats->gi[j][1], + stats->gi[j][2], stats->gi[j][3]); + len += scnprintf(buf + len, size - len, + " legacy rate %s (1,2 ... Mbps)\n ", + str[j]); + for (i = 0; i < ATH11K_LEGACY_NUM; i++) + len += scnprintf(buf + len, size - len, "%llu ", + stats->legacy[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + } + } + + len += scnprintf(buf + len, size - len, + "\nTX duration\n %llu usecs\n", + arsta->tx_stats->tx_duration); + len += scnprintf(buf + len, size - len, + "BA fails\n %llu\n", arsta->tx_stats->ba_fails); + len += scnprintf(buf + len, size - len, + "ack fails\n %llu\n", arsta->tx_stats->ack_fails); + spin_unlock_bh(&ar->data_lock); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + mutex_unlock(&ar->conf_mutex); + return retval; +} + +static const struct file_operations fops_tx_stats = { + .read = ath11k_dbg_sta_dump_tx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_dbg_sta_dump_rx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; + int len = 0, i, retval = 0; + const int size = 4096; + char *buf; + + if (!rx_stats) + return -ENOENT; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + spin_lock_bh(&ar->ab->base_lock); + + len += scnprintf(buf + len, size - len, "RX peer stats:\n"); + len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n", + rx_stats->num_msdu); + len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n", + rx_stats->tcp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n", + rx_stats->udp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n", + rx_stats->ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n", + rx_stats->non_ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n", + rx_stats->stbc_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n", + rx_stats->beamformed_count); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n", + rx_stats->num_mpdu_fcs_ok); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n", + rx_stats->num_mpdu_fcs_err); + len += scnprintf(buf + len, size - len, + "GI: 0.8us %llu 0.4us %llu 1.6us %llu 3.2us %llu\n", + rx_stats->gi_count[0], rx_stats->gi_count[1], + rx_stats->gi_count[2], rx_stats->gi_count[3]); + len += scnprintf(buf + len, size - len, + "BW: 20Mhz %llu 40Mhz %llu 80Mhz %llu 160Mhz %llu\n", + rx_stats->bw_count[0], rx_stats->bw_count[1], + rx_stats->bw_count[2], rx_stats->bw_count[3]); + len += scnprintf(buf + len, size - len, "BCC %llu LDPC %llu\n", + rx_stats->coding_count[0], rx_stats->coding_count[1]); + len += scnprintf(buf + len, size - len, + "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu\n", + rx_stats->pream_cnt[0], rx_stats->pream_cnt[1], + rx_stats->pream_cnt[2], rx_stats->pream_cnt[3], + rx_stats->pream_cnt[4]); + len += scnprintf(buf + len, size - len, + "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n", + rx_stats->reception_type[0], rx_stats->reception_type[1], + rx_stats->reception_type[2], rx_stats->reception_type[3]); + len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):"); + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]); + len += scnprintf(buf + len, size - len, "\nMCS(0-11) Legacy MCS(12):"); + for (i = 0; i < HAL_RX_MAX_MCS + 1; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->mcs_count[i]); + len += scnprintf(buf + len, size - len, "\nNSS(1-8):"); + for (i = 0; i < HAL_RX_MAX_NSS; i++) + 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, "\n"); + + spin_unlock_bh(&ar->ab->base_lock); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + mutex_unlock(&ar->conf_mutex); + return retval; +} + +static const struct file_operations fops_rx_stats = { + .read = ath11k_dbg_sta_dump_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int +ath11k_dbg_sta_open_htt_peer_stats(struct inode *inode, struct file *file) +{ + struct ieee80211_sta *sta = inode->i_private; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct debug_htt_stats_req *stats_req; + int ret; + + stats_req = vzalloc(sizeof(*stats_req) + ATH11K_HTT_STATS_BUF_SIZE); + if (!stats_req) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + ar->debug.htt_stats.stats_req = stats_req; + stats_req->type = ATH11K_DBG_HTT_EXT_STATS_PEER_INFO; + memcpy(stats_req->peer_addr, sta->addr, ETH_ALEN); + ret = ath11k_dbg_htt_stats_req(ar); + mutex_unlock(&ar->conf_mutex); + if (ret < 0) + goto out; + + file->private_data = stats_req; + return 0; +out: + vfree(stats_req); + return ret; +} + +static int +ath11k_dbg_sta_release_htt_peer_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + return 0; +} + +static ssize_t ath11k_dbg_sta_read_htt_peer_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct debug_htt_stats_req *stats_req = file->private_data; + char *buf; + u32 length = 0; + + buf = stats_req->buf; + length = min_t(u32, stats_req->buf_len, ATH11K_HTT_STATS_BUF_SIZE); + return simple_read_from_buffer(user_buf, count, ppos, buf, length); +} + +static const struct file_operations fops_htt_peer_stats = { + .open = ath11k_dbg_sta_open_htt_peer_stats, + .release = ath11k_dbg_sta_release_htt_peer_stats, + .read = ath11k_dbg_sta_read_htt_peer_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_dbg_sta_write_peer_pktlog(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + int ret, enable; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + ret = kstrtoint_from_user(buf, count, 0, &enable); + if (ret) + goto out; + + ar->debug.pktlog_peer_valid = enable; + memcpy(ar->debug.pktlog_peer_addr, sta->addr, ETH_ALEN); + + /* Send peer based pktlog enable/disable */ + ret = ath11k_wmi_pdev_peer_pktlog_filter(ar, sta->addr, enable); + if (ret) { + ath11k_warn(ar->ab, "failed to set peer pktlog filter %pM: %d\n", + sta->addr, ret); + goto out; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "peer pktlog filter set to %d\n", + enable); + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_dbg_sta_read_peer_pktlog(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + char buf[32] = {0}; + int len; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf), "%08x %pM\n", + ar->debug.pktlog_peer_valid, + ar->debug.pktlog_peer_addr); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_peer_pktlog = { + .write = ath11k_dbg_sta_write_peer_pktlog, + .read = ath11k_dbg_sta_read_peer_pktlog, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath11k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir) +{ + struct ath11k *ar = hw->priv; + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) + debugfs_create_file("tx_stats", 0400, dir, sta, + &fops_tx_stats); + if (ath11k_debug_is_extd_rx_stats_enabled(ar)) + debugfs_create_file("rx_stats", 0400, dir, sta, + &fops_rx_stats); + + debugfs_create_file("htt_peer_stats", 0400, dir, sta, + &fops_htt_peer_stats); + + debugfs_create_file("peer_pktlog", 0644, dir, sta, + &fops_peer_pktlog); +} diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c new file mode 100644 index 000000000000..b112825a52ed --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -0,0 +1,899 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "core.h" +#include "dp_tx.h" +#include "hal_tx.h" +#include "debug.h" +#include "dp_rx.h" +#include "peer.h" + +static void ath11k_dp_htt_htc_tx_complete(struct ath11k_base *ab, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); +} + +void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + + /* TODO: Any other peer specific DP cleanup */ + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, vdev_id, addr); + if (!peer) { + ath11k_warn(ab, "failed to lookup peer %pM on vdev %d\n", + addr, vdev_id); + spin_unlock_bh(&ab->base_lock); + return; + } + + ath11k_peer_rx_tid_cleanup(ar, peer); + 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; + u32 reo_dest; + int ret; + + /* NOTE: reo_dest ring id starts from 1 unlike mac_id which starts from 0 */ + reo_dest = ar->dp.mac_id + 1; + ret = ath11k_wmi_set_peer_param(ar, addr, vdev_id, + WMI_PEER_SET_DEFAULT_ROUTING, + DP_RX_HASH_ENABLE | (reo_dest << 1)); + + if (ret) { + ath11k_warn(ab, "failed to set default routing %d peer :%pM vdev_id :%d\n", + ret, addr, vdev_id); + 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; + } + + ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, 0, 1, 0); + if (ret) { + ath11k_warn(ab, "failed to setup rxd tid queue for tid 0 %d\n", + ret); + return ret; + } + + /* TODO: Setup other peer specific resource used in data path */ + + return 0; +} + +void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) +{ + if (!ring->vaddr_unaligned) + return; + + dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned, + ring->paddr_unaligned); + + ring->vaddr_unaligned = NULL; +} + +int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, + enum hal_ring_type type, int ring_num, + int mac_id, int num_entries) +{ + struct hal_srng_params params = { 0 }; + int entry_sz = ath11k_hal_srng_get_entrysize(type); + int max_entries = ath11k_hal_srng_get_max_entries(type); + int ret; + + if (max_entries < 0 || entry_sz < 0) + return -EINVAL; + + if (num_entries > max_entries) + num_entries = max_entries; + + ring->size = (num_entries * entry_sz) + HAL_RING_BASE_ALIGN - 1; + ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size, + &ring->paddr_unaligned, + GFP_KERNEL); + if (!ring->vaddr_unaligned) + return -ENOMEM; + + ring->vaddr = PTR_ALIGN(ring->vaddr_unaligned, HAL_RING_BASE_ALIGN); + ring->paddr = ring->paddr_unaligned + ((unsigned long)ring->vaddr - + (unsigned long)ring->vaddr_unaligned); + + params.ring_base_vaddr = ring->vaddr; + params.ring_base_paddr = ring->paddr; + params.num_entries = num_entries; + + switch (type) { + case HAL_REO_DST: + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_RX; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; + case HAL_RXDMA_BUF: + case HAL_RXDMA_MONITOR_BUF: + case HAL_RXDMA_MONITOR_STATUS: + params.low_threshold = num_entries >> 3; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.intr_batch_cntr_thres_entries = 0; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; + case HAL_WBM2SW_RELEASE: + if (ring_num < 3) { + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_TX; + params.intr_timer_thres_us = + HAL_SRNG_INT_TIMER_THRESHOLD_TX; + break; + } + /* follow through when ring_num >= 3 */ + /* fall through */ + case HAL_REO_EXCEPTION: + case HAL_REO_REINJECT: + case HAL_REO_CMD: + case HAL_REO_STATUS: + case HAL_TCL_DATA: + case HAL_TCL_CMD: + case HAL_TCL_STATUS: + case HAL_WBM_IDLE_LINK: + case HAL_SW2WBM_RELEASE: + case HAL_RXDMA_DST: + case HAL_RXDMA_MONITOR_DST: + case HAL_RXDMA_MONITOR_DESC: + case HAL_RXDMA_DIR_BUF: + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_OTHER; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_OTHER; + break; + default: + ath11k_warn(ab, "Not a valid ring type in dp :%d\n", type); + return -EINVAL; + } + + ret = ath11k_hal_srng_setup(ab, type, ring_num, mac_id, ¶ms); + if (ret < 0) { + ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n", + ret, ring_num); + return ret; + } + + ring->ring_id = ret; + + return 0; +} + +static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + int i; + + ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring); + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_data_ring); + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_comp_ring); + } + ath11k_dp_srng_cleanup(ab, &dp->reo_reinject_ring); + ath11k_dp_srng_cleanup(ab, &dp->rx_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->reo_except_ring); + ath11k_dp_srng_cleanup(ab, &dp->reo_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->reo_status_ring); +} + +static int ath11k_dp_srng_common_setup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng; + int i, ret; + + ret = ath11k_dp_srng_setup(ab, &dp->wbm_desc_rel_ring, + HAL_SW2WBM_RELEASE, 0, 0, + DP_WBM_RELEASE_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up wbm2sw_release ring :%d\n", + ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->tcl_cmd_ring, HAL_TCL_CMD, 0, 0, + DP_TCL_CMD_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_cmd ring :%d\n", ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->tcl_status_ring, HAL_TCL_STATUS, + 0, 0, DP_TCL_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_status ring :%d\n", ret); + goto err; + } + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_data_ring, + HAL_TCL_DATA, i, 0, + DP_TCL_DATA_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_data ring (%d) :%d\n", + i, ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_comp_ring, + HAL_WBM2SW_RELEASE, i, 0, + DP_TX_COMP_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_comp ring ring (%d) :%d\n", + i, ret); + goto err; + } + + srng = &ab->hal.srng_list[dp->tx_ring[i].tcl_data_ring.ring_id]; + ath11k_hal_tx_init_data_ring(ab, srng); + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_reinject_ring, HAL_REO_REINJECT, + 0, 0, DP_REO_REINJECT_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_reinject ring :%d\n", + ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->rx_rel_ring, HAL_WBM2SW_RELEASE, + 3, 0, DP_RX_RELEASE_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up rx_rel ring :%d\n", ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_except_ring, HAL_REO_EXCEPTION, + 0, 0, DP_REO_EXCEPTION_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_exception ring :%d\n", + ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_cmd_ring, HAL_REO_CMD, + 0, 0, DP_REO_CMD_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_cmd ring :%d\n", ret); + goto err; + } + + srng = &ab->hal.srng_list[dp->reo_cmd_ring.ring_id]; + ath11k_hal_reo_init_cmd_ring(ab, srng); + + ret = ath11k_dp_srng_setup(ab, &dp->reo_status_ring, HAL_REO_STATUS, + 0, 0, DP_REO_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_status ring :%d\n", ret); + goto err; + } + + ath11k_hal_reo_hw_setup(ab); + + return 0; + +err: + ath11k_dp_srng_common_cleanup(ab); + + return ret; +} + +static void ath11k_dp_scatter_idle_link_desc_cleanup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_wbm_idle_scatter_list *slist = dp->scatter_list; + int i; + + for (i = 0; i < DP_IDLE_SCATTER_BUFS_MAX; i++) { + if (!slist[i].vaddr) + continue; + + dma_free_coherent(ab->dev, HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX, + slist[i].vaddr, slist[i].paddr); + slist[i].vaddr = NULL; + } +} + +static int ath11k_dp_scatter_idle_link_desc_setup(struct ath11k_base *ab, + int size, + u32 n_link_desc_bank, + u32 n_link_desc, + u32 last_bank_sz) +{ + struct ath11k_dp *dp = &ab->dp; + struct dp_link_desc_bank *link_desc_banks = dp->link_desc_banks; + struct hal_wbm_idle_scatter_list *slist = dp->scatter_list; + u32 n_entries_per_buf; + int num_scatter_buf, scatter_idx; + struct hal_wbm_link_desc *scatter_buf; + int align_bytes, n_entries; + dma_addr_t paddr; + int rem_entries; + int i; + int ret = 0; + u32 end_offset; + + n_entries_per_buf = HAL_WBM_IDLE_SCATTER_BUF_SIZE / + ath11k_hal_srng_get_entrysize(HAL_WBM_IDLE_LINK); + num_scatter_buf = DIV_ROUND_UP(size, HAL_WBM_IDLE_SCATTER_BUF_SIZE); + + if (num_scatter_buf > DP_IDLE_SCATTER_BUFS_MAX) + return -EINVAL; + + for (i = 0; i < num_scatter_buf; i++) { + slist[i].vaddr = dma_alloc_coherent(ab->dev, + HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX, + &slist[i].paddr, GFP_KERNEL); + if (!slist[i].vaddr) { + ret = -ENOMEM; + goto err; + } + } + + scatter_idx = 0; + scatter_buf = slist[scatter_idx].vaddr; + rem_entries = n_entries_per_buf; + + for (i = 0; i < n_link_desc_bank; i++) { + align_bytes = link_desc_banks[i].vaddr - + link_desc_banks[i].vaddr_unaligned; + n_entries = (DP_LINK_DESC_ALLOC_SIZE_THRESH - align_bytes) / + HAL_LINK_DESC_SIZE; + paddr = link_desc_banks[i].paddr; + while (n_entries) { + ath11k_hal_set_link_desc_addr(scatter_buf, i, paddr); + n_entries--; + paddr += HAL_LINK_DESC_SIZE; + if (rem_entries) { + rem_entries--; + scatter_buf++; + continue; + } + + rem_entries = n_entries_per_buf; + scatter_idx++; + scatter_buf = slist[scatter_idx].vaddr; + } + } + + end_offset = (scatter_buf - slist[scatter_idx].vaddr) * + sizeof(struct hal_wbm_link_desc); + ath11k_hal_setup_link_idle_list(ab, slist, num_scatter_buf, + n_link_desc, end_offset); + + return 0; + +err: + ath11k_dp_scatter_idle_link_desc_cleanup(ab); + + return ret; +} + +static void +ath11k_dp_link_desc_bank_free(struct ath11k_base *ab, + struct dp_link_desc_bank *link_desc_banks) +{ + int i; + + for (i = 0; i < DP_LINK_DESC_BANKS_MAX; i++) { + if (link_desc_banks[i].vaddr_unaligned) { + dma_free_coherent(ab->dev, + link_desc_banks[i].size, + link_desc_banks[i].vaddr_unaligned, + link_desc_banks[i].paddr_unaligned); + link_desc_banks[i].vaddr_unaligned = NULL; + } + } +} + +static int ath11k_dp_link_desc_bank_alloc(struct ath11k_base *ab, + struct dp_link_desc_bank *desc_bank, + int n_link_desc_bank, + int last_bank_sz) +{ + struct ath11k_dp *dp = &ab->dp; + int i; + int ret = 0; + int desc_sz = DP_LINK_DESC_ALLOC_SIZE_THRESH; + + for (i = 0; i < n_link_desc_bank; i++) { + if (i == (n_link_desc_bank - 1) && last_bank_sz) + desc_sz = last_bank_sz; + + desc_bank[i].vaddr_unaligned = + dma_alloc_coherent(ab->dev, desc_sz, + &desc_bank[i].paddr_unaligned, + GFP_KERNEL); + if (!desc_bank[i].vaddr_unaligned) { + ret = -ENOMEM; + goto err; + } + + desc_bank[i].vaddr = PTR_ALIGN(desc_bank[i].vaddr_unaligned, + HAL_LINK_DESC_ALIGN); + desc_bank[i].paddr = desc_bank[i].paddr_unaligned + + ((unsigned long)desc_bank[i].vaddr - + (unsigned long)desc_bank[i].vaddr_unaligned); + desc_bank[i].size = desc_sz; + } + + return 0; + +err: + ath11k_dp_link_desc_bank_free(ab, dp->link_desc_banks); + + return ret; +} + +void ath11k_dp_link_desc_cleanup(struct ath11k_base *ab, + struct dp_link_desc_bank *desc_bank, + u32 ring_type, struct dp_srng *ring) +{ + ath11k_dp_link_desc_bank_free(ab, desc_bank); + + if (ring_type != HAL_RXDMA_MONITOR_DESC) { + ath11k_dp_srng_cleanup(ab, ring); + ath11k_dp_scatter_idle_link_desc_cleanup(ab); + } +} + +static int ath11k_wbm_idle_ring_setup(struct ath11k_base *ab, u32 *n_link_desc) +{ + struct ath11k_dp *dp = &ab->dp; + u32 n_mpdu_link_desc, n_mpdu_queue_desc; + u32 n_tx_msdu_link_desc, n_rx_msdu_link_desc; + int ret = 0; + + n_mpdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX) / + HAL_NUM_MPDUS_PER_LINK_DESC; + + n_mpdu_queue_desc = n_mpdu_link_desc / + HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC; + + n_tx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_FLOWS_PER_TID * + DP_AVG_MSDUS_PER_FLOW) / + HAL_NUM_TX_MSDUS_PER_LINK_DESC; + + n_rx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX * + DP_AVG_MSDUS_PER_MPDU) / + HAL_NUM_RX_MSDUS_PER_LINK_DESC; + + *n_link_desc = n_mpdu_link_desc + n_mpdu_queue_desc + + n_tx_msdu_link_desc + n_rx_msdu_link_desc; + + if (*n_link_desc & (*n_link_desc - 1)) + *n_link_desc = 1 << fls(*n_link_desc); + + ret = ath11k_dp_srng_setup(ab, &dp->wbm_idle_ring, + HAL_WBM_IDLE_LINK, 0, 0, *n_link_desc); + if (ret) { + ath11k_warn(ab, "failed to setup wbm_idle_ring: %d\n", ret); + return ret; + } + return ret; +} + +int ath11k_dp_link_desc_setup(struct ath11k_base *ab, + struct dp_link_desc_bank *link_desc_banks, + u32 ring_type, struct hal_srng *srng, + u32 n_link_desc) +{ + u32 tot_mem_sz; + u32 n_link_desc_bank, last_bank_sz; + u32 entry_sz, align_bytes, n_entries; + u32 paddr; + u32 *desc; + int i, ret; + + tot_mem_sz = n_link_desc * HAL_LINK_DESC_SIZE; + tot_mem_sz += HAL_LINK_DESC_ALIGN; + + if (tot_mem_sz <= DP_LINK_DESC_ALLOC_SIZE_THRESH) { + n_link_desc_bank = 1; + last_bank_sz = tot_mem_sz; + } else { + n_link_desc_bank = tot_mem_sz / + (DP_LINK_DESC_ALLOC_SIZE_THRESH - + HAL_LINK_DESC_ALIGN); + last_bank_sz = tot_mem_sz % + (DP_LINK_DESC_ALLOC_SIZE_THRESH - + HAL_LINK_DESC_ALIGN); + + if (last_bank_sz) + n_link_desc_bank += 1; + } + + if (n_link_desc_bank > DP_LINK_DESC_BANKS_MAX) + return -EINVAL; + + ret = ath11k_dp_link_desc_bank_alloc(ab, link_desc_banks, + n_link_desc_bank, last_bank_sz); + if (ret) + return ret; + + /* Setup link desc idle list for HW internal usage */ + entry_sz = ath11k_hal_srng_get_entrysize(ring_type); + tot_mem_sz = entry_sz * n_link_desc; + + /* Setup scatter desc list when the total memory requirement is more */ + if (tot_mem_sz > DP_LINK_DESC_ALLOC_SIZE_THRESH && + ring_type != HAL_RXDMA_MONITOR_DESC) { + ret = ath11k_dp_scatter_idle_link_desc_setup(ab, tot_mem_sz, + n_link_desc_bank, + n_link_desc, + last_bank_sz); + if (ret) { + ath11k_warn(ab, "failed to setup scatting idle list descriptor :%d\n", + ret); + goto fail_desc_bank_free; + } + + return 0; + } + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + for (i = 0; i < n_link_desc_bank; i++) { + align_bytes = link_desc_banks[i].vaddr - + link_desc_banks[i].vaddr_unaligned; + n_entries = (link_desc_banks[i].size - align_bytes) / + HAL_LINK_DESC_SIZE; + paddr = link_desc_banks[i].paddr; + while (n_entries && + (desc = ath11k_hal_srng_src_get_next_entry(ab, srng))) { + ath11k_hal_set_link_desc_addr((struct hal_wbm_link_desc *)desc, + i, paddr); + n_entries--; + paddr += HAL_LINK_DESC_SIZE; + } + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return 0; + +fail_desc_bank_free: + ath11k_dp_link_desc_bank_free(ab, link_desc_banks); + + return ret; +} + +int ath11k_dp_service_srng(struct ath11k_base *ab, + struct ath11k_ext_irq_grp *irq_grp, + int budget) +{ + struct napi_struct *napi = &irq_grp->napi; + int grp_id = irq_grp->grp_id; + int work_done = 0; + int i = 0; + int tot_work_done = 0; + + while (ath11k_tx_ring_mask[grp_id] >> i) { + if (ath11k_tx_ring_mask[grp_id] & BIT(i)) + ath11k_dp_tx_completion_handler(ab, i); + i++; + } + + if (ath11k_rx_err_ring_mask[grp_id]) { + work_done = ath11k_dp_process_rx_err(ab, napi, budget); + budget -= work_done; + tot_work_done += work_done; + if (budget <= 0) + goto done; + } + + if (ath11k_rx_wbm_rel_ring_mask[grp_id]) { + work_done = ath11k_dp_rx_process_wbm_err(ab, + napi, + budget); + budget -= work_done; + tot_work_done += work_done; + + if (budget <= 0) + goto done; + } + + 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; + } + } + + if (rx_mon_status_ring_mask[grp_id]) { + for (i = 0; i < ab->num_radios; i++) { + if (rx_mon_status_ring_mask[grp_id] & BIT(i)) { + work_done = + ath11k_dp_rx_process_mon_rings(ab, + i, napi, + budget); + budget -= work_done; + tot_work_done += work_done; + } + if (budget <= 0) + goto done; + } + } + + if (ath11k_reo_status_ring_mask[grp_id]) + ath11k_dp_process_reo_status(ab); + + for (i = 0; i < ab->num_radios; i++) { + if (ath11k_rxdma2host_ring_mask[grp_id] & BIT(i)) { + work_done = ath11k_dp_process_rxdma_err(ab, i, budget); + budget -= work_done; + tot_work_done += work_done; + } + + if (budget <= 0) + goto done; + + if (ath11k_host2rxdma_ring_mask[grp_id] & BIT(i)) { + struct ath11k_pdev_dp *dp = &ab->pdevs[i].ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, 0, + HAL_RX_BUF_RBM_SW3_BM, + GFP_ATOMIC); + } + } + /* TODO: Implement handler for other interrupts */ + +done: + return tot_work_done; +} + +void ath11k_dp_pdev_free(struct ath11k_base *ab) +{ + struct ath11k *ar; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + ath11k_dp_rx_pdev_free(ab, i); + ath11k_debug_unregister(ar); + ath11k_dp_rx_pdev_mon_detach(ar); + } +} + +void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev_dp *dp; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + dp = &ar->dp; + dp->mac_id = i; + idr_init(&dp->rx_refill_buf_ring.bufs_idr); + spin_lock_init(&dp->rx_refill_buf_ring.idr_lock); + atomic_set(&dp->num_tx_pending, 0); + init_waitqueue_head(&dp->tx_empty_waitq); + idr_init(&dp->rx_mon_status_refill_ring.bufs_idr); + spin_lock_init(&dp->rx_mon_status_refill_ring.idr_lock); + idr_init(&dp->rxdma_mon_buf_ring.bufs_idr); + spin_lock_init(&dp->rxdma_mon_buf_ring.idr_lock); + } +} + +int ath11k_dp_pdev_alloc(struct ath11k_base *ab) +{ + struct ath11k *ar; + int ret; + int i; + + /* TODO:Per-pdev rx ring unlike tx ring which is mapped to different AC's */ + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + ret = ath11k_dp_rx_pdev_alloc(ab, i); + if (ret) { + ath11k_warn(ab, "failed to allocate pdev rx for pdev_id :%d\n", + i); + goto err; + } + ret = ath11k_dp_rx_pdev_mon_attach(ar); + if (ret) { + ath11k_warn(ab, "failed to initialize mon pdev %d\n", + i); + goto err; + } + } + + return 0; + +err: + ath11k_dp_pdev_free(ab); + + return ret; +} + +int ath11k_dp_htt_connect(struct ath11k_dp *dp) +{ + struct ath11k_htc_svc_conn_req conn_req; + struct ath11k_htc_svc_conn_resp conn_resp; + int status; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + conn_req.ep_ops.ep_tx_complete = ath11k_dp_htt_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath11k_dp_htt_htc_t2h_msg_handler; + + /* connect to control service */ + conn_req.service_id = ATH11K_HTC_SVC_ID_HTT_DATA_MSG; + + status = ath11k_htc_connect_service(&dp->ab->htc, &conn_req, + &conn_resp); + + if (status) + return status; + + dp->eid = conn_resp.eid; + + return 0; +} + +static void ath11k_dp_update_vdev_search(struct ath11k_vif *arvif) +{ + /* For STA mode, enable address search index, + * tcl uses ast_hash value in the descriptor. + */ + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_STA: + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; + break; + case WMI_VDEV_TYPE_AP: + case WMI_VDEV_TYPE_IBSS: + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT; + break; + case WMI_VDEV_TYPE_MONITOR: + default: + return; + } +} + +void ath11k_dp_vdev_tx_attach(struct ath11k *ar, struct ath11k_vif *arvif) +{ + arvif->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 1) | + FIELD_PREP(HTT_TCL_META_DATA_VDEV_ID, + arvif->vdev_id) | + FIELD_PREP(HTT_TCL_META_DATA_PDEV_ID, + ar->pdev->pdev_id); + + /* set HTT extension valid bit to 0 by default */ + arvif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; + + ath11k_dp_update_vdev_search(arvif); +} + +static int ath11k_dp_tx_pending_cleanup(int buf_id, void *skb, void *ctx) +{ + struct ath11k_base *ab = (struct ath11k_base *)ctx; + struct sk_buff *msdu = skb; + + dma_unmap_single(ab->dev, ATH11K_SKB_CB(msdu)->paddr, msdu->len, + DMA_TO_DEVICE); + + dev_kfree_skb_any(msdu); + + return 0; +} + +void ath11k_dp_free(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + int i; + + ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring); + + ath11k_dp_srng_common_cleanup(ab); + + ath11k_dp_reo_cmd_list_cleanup(ab); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + spin_lock_bh(&dp->tx_ring[i].tx_idr_lock); + idr_for_each(&dp->tx_ring[i].txbuf_idr, + ath11k_dp_tx_pending_cleanup, ab); + idr_destroy(&dp->tx_ring[i].txbuf_idr); + spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock); + kfree(dp->tx_ring[i].tx_status); + } + + /* Deinit any SOC level resource */ +} + +int ath11k_dp_alloc(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng = NULL; + size_t size = 0; + u32 n_link_desc = 0; + int ret; + int i; + + dp->ab = ab; + + INIT_LIST_HEAD(&dp->reo_cmd_list); + INIT_LIST_HEAD(&dp->reo_cmd_cache_flush_list); + spin_lock_init(&dp->reo_cmd_lock); + + ret = ath11k_wbm_idle_ring_setup(ab, &n_link_desc); + if (ret) { + ath11k_warn(ab, "failed to setup wbm_idle_ring: %d\n", ret); + return ret; + } + + srng = &ab->hal.srng_list[dp->wbm_idle_ring.ring_id]; + + ret = ath11k_dp_link_desc_setup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, srng, n_link_desc); + if (ret) { + ath11k_warn(ab, "failed to setup link desc: %d\n", ret); + return ret; + } + + ret = ath11k_dp_srng_common_setup(ab); + if (ret) + goto fail_link_desc_cleanup; + + size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE; + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + idr_init(&dp->tx_ring[i].txbuf_idr); + spin_lock_init(&dp->tx_ring[i].tx_idr_lock); + dp->tx_ring[i].tcl_data_ring_id = i; + + dp->tx_ring[i].tx_status_head = 0; + dp->tx_ring[i].tx_status_tail = DP_TX_COMP_RING_SIZE - 1; + dp->tx_ring[i].tx_status = kmalloc(size, GFP_KERNEL); + if (!dp->tx_ring[i].tx_status) + goto fail_cmn_srng_cleanup; + } + + for (i = 0; i < HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX; i++) + ath11k_hal_tx_set_dscp_tid_map(ab, i); + + /* Init any SOC level resource for DP */ + + return 0; + +fail_cmn_srng_cleanup: + ath11k_dp_srng_common_cleanup(ab); + +fail_link_desc_cleanup: + ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring); + + return ret; +} diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h new file mode 100644 index 000000000000..6ef5be4201b2 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -0,0 +1,1535 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_DP_H +#define ATH11K_DP_H + +#include "hal_rx.h" + +struct ath11k_base; +struct ath11k_peer; +struct ath11k_dp; +struct ath11k_vif; +struct hal_tcl_status_ring; +struct ath11k_ext_irq_grp; + +struct dp_rx_tid { + u8 tid; + u32 *vaddr; + dma_addr_t paddr; + u32 size; + u32 ba_win_sz; + bool active; +}; + +#define DP_REO_DESC_FREE_TIMEOUT_MS 1000 + +struct dp_reo_cache_flush_elem { + struct list_head list; + struct dp_rx_tid data; + unsigned long ts; +}; + +struct dp_reo_cmd { + struct list_head list; + struct dp_rx_tid data; + int cmd_num; + void (*handler)(struct ath11k_dp *, void *, + enum hal_reo_cmd_status status); +}; + +struct dp_srng { + u32 *vaddr_unaligned; + u32 *vaddr; + dma_addr_t paddr_unaligned; + dma_addr_t paddr; + int size; + u32 ring_id; +}; + +struct dp_rxdma_ring { + struct dp_srng refill_buf_ring; + struct idr bufs_idr; + /* Protects bufs_idr */ + spinlock_t idr_lock; + int bufs_max; +}; + +#define ATH11K_TX_COMPL_NEXT(x) (((x) + 1) % DP_TX_COMP_RING_SIZE) + +struct dp_tx_ring { + u8 tcl_data_ring_id; + struct dp_srng tcl_data_ring; + struct dp_srng tcl_comp_ring; + struct idr txbuf_idr; + /* Protects txbuf_idr and num_pending */ + spinlock_t tx_idr_lock; + struct hal_wbm_release_ring *tx_status; + int tx_status_head; + int tx_status_tail; +}; + +struct ath11k_pdev_mon_stats { + u32 status_ppdu_state; + u32 status_ppdu_start; + u32 status_ppdu_end; + u32 status_ppdu_compl; + u32 status_ppdu_start_mis; + u32 status_ppdu_end_mis; + u32 status_ppdu_done; + u32 dest_ppdu_done; + u32 dest_mpdu_done; + u32 dest_mpdu_drop; + u32 dup_mon_linkdesc_cnt; + u32 dup_mon_buf_cnt; +}; + +struct dp_link_desc_bank { + void *vaddr_unaligned; + void *vaddr; + dma_addr_t paddr_unaligned; + dma_addr_t paddr; + u32 size; +}; + +/* Size to enforce scatter idle list mode */ +#define DP_LINK_DESC_ALLOC_SIZE_THRESH 0x200000 +#define DP_LINK_DESC_BANKS_MAX 8 + +#define DP_RX_DESC_COOKIE_INDEX_MAX 0x3ffff +#define DP_RX_DESC_COOKIE_POOL_ID_MAX 0x1c0000 +#define DP_RX_DESC_COOKIE_MAX \ + (DP_RX_DESC_COOKIE_INDEX_MAX | DP_RX_DESC_COOKIE_POOL_ID_MAX) +#define DP_NOT_PPDU_ID_WRAP_AROUND 20000 + +enum ath11k_dp_ppdu_state { + DP_PPDU_STATUS_START, + DP_PPDU_STATUS_DONE, +}; + +struct ath11k_mon_data { + struct dp_link_desc_bank link_desc_banks[DP_LINK_DESC_BANKS_MAX]; + struct hal_rx_mon_ppdu_info mon_ppdu_info; + + u32 mon_ppdu_status; + u32 mon_last_buf_cookie; + u64 mon_last_linkdesc_paddr; + u16 chan_noise_floor; + + struct ath11k_pdev_mon_stats rx_mon_stats; + /* lock for monitor data */ + spinlock_t mon_lock; + struct sk_buff_head rx_status_q; +}; + +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; + struct dp_srng rxdma_mon_desc_ring; + + struct dp_rxdma_ring rxdma_mon_buf_ring; + struct dp_rxdma_ring rx_mon_status_refill_ring; + struct ieee80211_rx_status rx_status; + struct ath11k_mon_data mon_data; +}; + +#define DP_NUM_CLIENTS_MAX 64 +#define DP_AVG_TIDS_PER_CLIENT 2 +#define DP_NUM_TIDS_MAX (DP_NUM_CLIENTS_MAX * DP_AVG_TIDS_PER_CLIENT) +#define DP_AVG_MSDUS_PER_FLOW 128 +#define DP_AVG_FLOWS_PER_TID 2 +#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_BA_WIN_SZ_MAX 256 + +#define DP_TCL_NUM_RING_MAX 3 + +#define DP_IDLE_SCATTER_BUFS_MAX 16 + +#define DP_WBM_RELEASE_RING_SIZE 64 +#define DP_TCL_DATA_RING_SIZE 512 +#define DP_TX_COMP_RING_SIZE 8192 +#define DP_TX_IDR_SIZE (DP_TX_COMP_RING_SIZE << 1) +#define DP_TCL_CMD_RING_SIZE 32 +#define DP_TCL_STATUS_RING_SIZE 32 +#define DP_REO_DST_RING_MAX 4 +#define DP_REO_DST_RING_SIZE 2048 +#define DP_REO_REINJECT_RING_SIZE 32 +#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_RXDMA_BUF_RING_SIZE 4096 +#define DP_RXDMA_REFILL_RING_SIZE 2048 +#define DP_RXDMA_ERR_DST_RING_SIZE 1024 +#define DP_RXDMA_MON_STATUS_RING_SIZE 1024 +#define DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 +#define DP_RXDMA_MONITOR_DST_RING_SIZE 2048 +#define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 + +#define DP_RX_BUFFER_SIZE 2048 +#define DP_RX_BUFFER_ALIGN_SIZE 128 + +#define DP_RXDMA_BUF_COOKIE_BUF_ID GENMASK(17, 0) +#define DP_RXDMA_BUF_COOKIE_PDEV_ID GENMASK(20, 18) + +#define DP_HW2SW_MACID(mac_id) ((mac_id) ? ((mac_id) - 1) : 0) +#define DP_SW2HW_MACID(mac_id) ((mac_id) + 1) + +#define DP_TX_DESC_ID_MAC_ID GENMASK(1, 0) +#define DP_TX_DESC_ID_MSDU_ID GENMASK(18, 2) +#define DP_TX_DESC_ID_POOL_ID GENMASK(20, 19) + +struct ath11k_dp { + struct ath11k_base *ab; + enum ath11k_htc_ep_id eid; + struct completion htt_tgt_version_received; + u8 htt_tgt_ver_major; + u8 htt_tgt_ver_minor; + struct dp_link_desc_bank link_desc_banks[DP_LINK_DESC_BANKS_MAX]; + struct dp_srng wbm_idle_ring; + struct dp_srng wbm_desc_rel_ring; + struct dp_srng tcl_cmd_ring; + struct dp_srng tcl_status_ring; + struct dp_srng reo_reinject_ring; + struct dp_srng rx_rel_ring; + struct dp_srng reo_except_ring; + struct dp_srng reo_cmd_ring; + struct dp_srng reo_status_ring; + 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; + struct list_head reo_cmd_cache_flush_list; + /* protects access to reo_cmd_list and reo_cmd_cache_flush_list */ + spinlock_t reo_cmd_lock; +}; + +/* HTT definitions */ + +#define HTT_TCL_META_DATA_TYPE BIT(0) +#define HTT_TCL_META_DATA_VALID_HTT BIT(1) + +/* vdev meta data */ +#define HTT_TCL_META_DATA_VDEV_ID GENMASK(9, 2) +#define HTT_TCL_META_DATA_PDEV_ID GENMASK(11, 10) +#define HTT_TCL_META_DATA_HOST_INSPECTED BIT(12) + +/* peer meta data */ +#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 2) + +#define HTT_TX_WBM_COMP_STATUS_OFFSET 8 + +/* HTT tx completion is overlayed in wbm_release_ring */ +#define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(12, 9) +#define HTT_TX_WBM_COMP_INFO0_REINJECT_REASON GENMASK(16, 13) +#define HTT_TX_WBM_COMP_INFO0_REINJECT_REASON GENMASK(16, 13) + +#define HTT_TX_WBM_COMP_INFO1_ACK_RSSI GENMASK(31, 24) + +struct htt_tx_wbm_completion { + u32 info0; + u32 info1; + u32 info2; + u32 info3; +} __packed; + +enum htt_h2t_msg_type { + HTT_H2T_MSG_TYPE_VERSION_REQ = 0, + HTT_H2T_MSG_TYPE_SRING_SETUP = 0xb, + HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG = 0xc, + HTT_H2T_MSG_TYPE_EXT_STATS_CFG = 0x10, + HTT_H2T_MSG_TYPE_PPDU_STATS_CFG = 0x11, +}; + +#define HTT_VER_REQ_INFO_MSG_ID GENMASK(7, 0) + +struct htt_ver_req_cmd { + u32 ver_reg_info; +} __packed; + +enum htt_srng_ring_type { + HTT_HW_TO_SW_RING, + HTT_SW_TO_HW_RING, + HTT_SW_TO_SW_RING, +}; + +enum htt_srng_ring_id { + HTT_RXDMA_HOST_BUF_RING, + HTT_RXDMA_MONITOR_STATUS_RING, + HTT_RXDMA_MONITOR_BUF_RING, + HTT_RXDMA_MONITOR_DESC_RING, + HTT_RXDMA_MONITOR_DEST_RING, + HTT_HOST1_TO_FW_RXBUF_RING, + HTT_HOST2_TO_FW_RXBUF_RING, + HTT_RXDMA_NON_MONITOR_DEST_RING, +}; + +/* host -> target HTT_SRING_SETUP message + * + * After target is booted up, Host can send SRING setup message for + * each host facing LMAC SRING. Target setups up HW registers based + * on setup message and confirms back to Host if response_required is set. + * Host should wait for confirmation message before sending new SRING + * setup message + * + * The message would appear as follows: + * + * |31 24|23 20|19|18 16|15|14 8|7 0| + * |--------------- +-----------------+----------------+------------------| + * | ring_type | ring_id | pdev_id | msg_type | + * |----------------------------------------------------------------------| + * | ring_base_addr_lo | + * |----------------------------------------------------------------------| + * | ring_base_addr_hi | + * |----------------------------------------------------------------------| + * |ring_misc_cfg_flag|ring_entry_size| ring_size | + * |----------------------------------------------------------------------| + * | ring_head_offset32_remote_addr_lo | + * |----------------------------------------------------------------------| + * | ring_head_offset32_remote_addr_hi | + * |----------------------------------------------------------------------| + * | ring_tail_offset32_remote_addr_lo | + * |----------------------------------------------------------------------| + * | ring_tail_offset32_remote_addr_hi | + * |----------------------------------------------------------------------| + * | ring_msi_addr_lo | + * |----------------------------------------------------------------------| + * | ring_msi_addr_hi | + * |----------------------------------------------------------------------| + * | ring_msi_data | + * |----------------------------------------------------------------------| + * | intr_timer_th |IM| intr_batch_counter_th | + * |----------------------------------------------------------------------| + * | reserved |RR|PTCF| intr_low_threshold | + * |----------------------------------------------------------------------| + * Where + * IM = sw_intr_mode + * RR = response_required + * PTCF = prefetch_timer_cfg + * + * The message is interpreted as follows: + * dword0 - b'0:7 - msg_type: This will be set to + * HTT_H2T_MSG_TYPE_SRING_SETUP + * b'8:15 - pdev_id: + * 0 (for rings at SOC/UMAC level), + * 1/2/3 mac id (for rings at LMAC level) + * b'16:23 - ring_id: identify which ring is to setup, + * more details can be got from enum htt_srng_ring_id + * b'24:31 - ring_type: identify type of host rings, + * more details can be got from enum htt_srng_ring_type + * dword1 - b'0:31 - ring_base_addr_lo: Lower 32bits of ring base address + * dword2 - b'0:31 - ring_base_addr_hi: Upper 32bits of ring base address + * dword3 - b'0:15 - ring_size: size of the ring in unit of 4-bytes words + * b'16:23 - ring_entry_size: Size of each entry in 4-byte word units + * b'24:31 - ring_misc_cfg_flag: Valid only for HW_TO_SW_RING and + * SW_TO_HW_RING. + * Refer to HTT_SRING_SETUP_RING_MISC_CFG_RING defs. + * dword4 - b'0:31 - ring_head_off32_remote_addr_lo: + * Lower 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the head + * element within the ring. + * (The head offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword5 - b'0:31 - ring_head_off32_remote_addr_hi: + * Upper 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the head + * element within the ring. + * (The head offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword6 - b'0:31 - ring_tail_off32_remote_addr_lo: + * Lower 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the tail + * element within the ring. + * (The tail offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword7 - b'0:31 - ring_tail_off32_remote_addr_hi: + * Upper 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the tail + * element within the ring. + * (The tail offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword8 - b'0:31 - ring_msi_addr_lo: Lower 32bits of MSI cfg address + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword9 - b'0:31 - ring_msi_addr_hi: Upper 32bits of MSI cfg address + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword10 - b'0:31 - ring_msi_data: MSI data + * Refer to HTT_SRING_SETUP_RING_MSC_CFG_xxx defs + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword11 - b'0:14 - intr_batch_counter_th: + * batch counter threshold is in units of 4-byte words. + * HW internally maintains and increments batch count. + * (see SRING spec for detail description). + * When batch count reaches threshold value, an interrupt + * is generated by HW. + * b'15 - sw_intr_mode: + * This configuration shall be static. + * Only programmed at power up. + * 0: generate pulse style sw interrupts + * 1: generate level style sw interrupts + * b'16:31 - intr_timer_th: + * The timer init value when timer is idle or is + * initialized to start downcounting. + * In 8us units (to cover a range of 0 to 524 ms) + * dword12 - b'0:15 - intr_low_threshold: + * Used only by Consumer ring to generate ring_sw_int_p. + * Ring entries low threshold water mark, that is used + * in combination with the interrupt timer as well as + * the the clearing of the level interrupt. + * b'16:18 - prefetch_timer_cfg: + * Used only by Consumer ring to set timer mode to + * support Application prefetch handling. + * The external tail offset/pointer will be updated + * at following intervals: + * 3'b000: (Prefetch feature disabled; used only for debug) + * 3'b001: 1 usec + * 3'b010: 4 usec + * 3'b011: 8 usec (default) + * 3'b100: 16 usec + * Others: Reserverd + * b'19 - response_required: + * Host needs HTT_T2H_MSG_TYPE_SRING_SETUP_DONE as response + * b'20:31 - reserved: reserved for future use + */ + +#define HTT_SRNG_SETUP_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +#define HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID GENMASK(15, 8) +#define HTT_SRNG_SETUP_CMD_INFO0_RING_ID GENMASK(23, 16) +#define HTT_SRNG_SETUP_CMD_INFO0_RING_TYPE GENMASK(31, 24) + +#define HTT_SRNG_SETUP_CMD_INFO1_RING_SIZE GENMASK(15, 0) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_ENTRY_SIZE GENMASK(23, 16) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_LOOP_CNT_DIS BIT(25) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_MSI_SWAP BIT(27) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_HOST_FW_SWAP BIT(28) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_TLV_SWAP BIT(29) + +#define HTT_SRNG_SETUP_CMD_INTR_INFO_BATCH_COUNTER_THRESH GENMASK(14, 0) +#define HTT_SRNG_SETUP_CMD_INTR_INFO_SW_INTR_MODE BIT(15) +#define HTT_SRNG_SETUP_CMD_INTR_INFO_INTR_TIMER_THRESH GENMASK(31, 16) + +#define HTT_SRNG_SETUP_CMD_INFO2_INTR_LOW_THRESH GENMASK(15, 0) +#define HTT_SRNG_SETUP_CMD_INFO2_PRE_FETCH_TIMER_CFG BIT(16) +#define HTT_SRNG_SETUP_CMD_INFO2_RESPONSE_REQUIRED BIT(19) + +struct htt_srng_setup_cmd { + u32 info0; + u32 ring_base_addr_lo; + u32 ring_base_addr_hi; + u32 info1; + u32 ring_head_off32_remote_addr_lo; + u32 ring_head_off32_remote_addr_hi; + u32 ring_tail_off32_remote_addr_lo; + u32 ring_tail_off32_remote_addr_hi; + u32 ring_msi_addr_lo; + u32 ring_msi_addr_hi; + u32 msi_data; + u32 intr_info; + u32 info2; +} __packed; + +/* host -> target FW PPDU_STATS config message + * + * @details + * The following field definitions describe the format of the HTT host + * to target FW for PPDU_STATS_CFG msg. + * The message allows the host to configure the PPDU_STATS_IND messages + * produced by the target. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | REQ bit mask | pdev_mask | msg type | + * |-----------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a req to configure ppdu_stats_ind from target + * Value: 0x11 + * - PDEV_MASK + * Bits 8:15 + * Purpose: identifies which pdevs this PPDU stats configuration applies to + * Value: This is a overloaded field, refer to usage and interpretation of + * PDEV in interface document. + * Bit 8 : Reserved for SOC stats + * Bit 9 - 15 : Indicates PDEV_MASK in DBDC + * Indicates MACID_MASK in DBS + * - REQ_TLV_BIT_MASK + * Bits 16:31 + * Purpose: each set bit indicates the corresponding PPDU stats TLV type + * needs to be included in the target's PPDU_STATS_IND messages. + * Value: refer htt_ppdu_stats_tlv_tag_t <<<??? + * + */ + +struct htt_ppdu_stats_cfg_cmd { + u32 msg; +} __packed; + +#define HTT_PPDU_STATS_CFG_MSG_TYPE GENMASK(7, 0) +#define HTT_PPDU_STATS_CFG_PDEV_ID GENMASK(16, 9) +#define HTT_PPDU_STATS_CFG_TLV_TYPE_BITMASK GENMASK(31, 16) + +enum htt_ppdu_stats_tag_type { + HTT_PPDU_STATS_TAG_COMMON, + HTT_PPDU_STATS_TAG_USR_COMMON, + HTT_PPDU_STATS_TAG_USR_RATE, + HTT_PPDU_STATS_TAG_USR_MPDU_ENQ_BITMAP_64, + HTT_PPDU_STATS_TAG_USR_MPDU_ENQ_BITMAP_256, + HTT_PPDU_STATS_TAG_SCH_CMD_STATUS, + HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON, + HTT_PPDU_STATS_TAG_USR_COMPLTN_BA_BITMAP_64, + HTT_PPDU_STATS_TAG_USR_COMPLTN_BA_BITMAP_256, + HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS, + HTT_PPDU_STATS_TAG_USR_COMPLTN_FLUSH, + HTT_PPDU_STATS_TAG_USR_COMMON_ARRAY, + HTT_PPDU_STATS_TAG_INFO, + HTT_PPDU_STATS_TAG_TX_MGMTCTRL_PAYLOAD, + + /* New TLV's are added above to this line */ + HTT_PPDU_STATS_TAG_MAX, +}; + +#define HTT_PPDU_STATS_TAG_DEFAULT (BIT(HTT_PPDU_STATS_TAG_COMMON) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMMON) \ + | BIT(HTT_PPDU_STATS_TAG_USR_RATE) \ + | BIT(HTT_PPDU_STATS_TAG_SCH_CMD_STATUS) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_FLUSH) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMMON_ARRAY)) + +#define HTT_PPDU_STATS_TAG_PKTLOG (BIT(HTT_PPDU_STATS_TAG_USR_MPDU_ENQ_BITMAP_64) | \ + BIT(HTT_PPDU_STATS_TAG_USR_MPDU_ENQ_BITMAP_256) | \ + BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_BA_BITMAP_64) | \ + BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_BA_BITMAP_256) | \ + BIT(HTT_PPDU_STATS_TAG_INFO) | \ + BIT(HTT_PPDU_STATS_TAG_TX_MGMTCTRL_PAYLOAD) | \ + HTT_PPDU_STATS_TAG_DEFAULT) + +/* HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG Message + * + * details: + * HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG message is sent by host to + * configure RXDMA rings. + * The configuration is per ring based and includes both packet subtypes + * and PPDU/MPDU TLVs. + * + * The message would appear as follows: + * + * |31 26|25|24|23 16|15 8|7 0| + * |-----------------+----------------+----------------+---------------| + * | rsvd1 |PS|SS| ring_id | pdev_id | msg_type | + * |-------------------------------------------------------------------| + * | rsvd2 | ring_buffer_size | + * |-------------------------------------------------------------------| + * | packet_type_enable_flags_0 | + * |-------------------------------------------------------------------| + * | packet_type_enable_flags_1 | + * |-------------------------------------------------------------------| + * | packet_type_enable_flags_2 | + * |-------------------------------------------------------------------| + * | packet_type_enable_flags_3 | + * |-------------------------------------------------------------------| + * | tlv_filter_in_flags | + * |-------------------------------------------------------------------| + * Where: + * PS = pkt_swap + * SS = status_swap + * The message is interpreted as follows: + * dword0 - b'0:7 - msg_type: This will be set to + * HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG + * b'8:15 - pdev_id: + * 0 (for rings at SOC/UMAC level), + * 1/2/3 mac id (for rings at LMAC level) + * b'16:23 - ring_id : Identify the ring to configure. + * More details can be got from enum htt_srng_ring_id + * b'24 - status_swap: 1 is to swap status TLV + * b'25 - pkt_swap: 1 is to swap packet TLV + * b'26:31 - rsvd1: reserved for future use + * dword1 - b'0:16 - ring_buffer_size: size of bufferes referenced by rx ring, + * in byte units. + * Valid only for HW_TO_SW_RING and SW_TO_HW_RING + * - b'16:31 - rsvd2: Reserved for future use + * dword2 - b'0:31 - packet_type_enable_flags_0: + * Enable MGMT packet from 0b0000 to 0b1001 + * bits from low to high: FP, MD, MO - 3 bits + * FP: Filter_Pass + * MD: Monitor_Direct + * MO: Monitor_Other + * 10 mgmt subtypes * 3 bits -> 30 bits + * Refer to PKT_TYPE_ENABLE_FLAG0_xxx_MGMT_xxx defs + * dword3 - b'0:31 - packet_type_enable_flags_1: + * Enable MGMT packet from 0b1010 to 0b1111 + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG1_xxx_MGMT_xxx defs + * dword4 - b'0:31 - packet_type_enable_flags_2: + * Enable CTRL packet from 0b0000 to 0b1001 + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG2_xxx_CTRL_xxx defs + * dword5 - b'0:31 - packet_type_enable_flags_3: + * Enable CTRL packet from 0b1010 to 0b1111, + * MCAST_DATA, UCAST_DATA, NULL_DATA + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG3_xxx_CTRL_xxx defs + * dword6 - b'0:31 - tlv_filter_in_flags: + * Filter in Attention/MPDU/PPDU/Header/User tlvs + * Refer to CFG_TLV_FILTER_IN_FLAG defs + */ + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID GENMASK(15, 8) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID GENMASK(23, 16) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS BIT(24) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS BIT(25) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) + +enum htt_rx_filter_tlv_flags { + HTT_RX_FILTER_TLV_FLAGS_MPDU_START = BIT(0), + HTT_RX_FILTER_TLV_FLAGS_MSDU_START = BIT(1), + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET = BIT(2), + HTT_RX_FILTER_TLV_FLAGS_MSDU_END = BIT(3), + HTT_RX_FILTER_TLV_FLAGS_MPDU_END = BIT(4), + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER = BIT(5), + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER = BIT(6), + HTT_RX_FILTER_TLV_FLAGS_ATTENTION = BIT(7), + HTT_RX_FILTER_TLV_FLAGS_PPDU_START = BIT(8), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END = BIT(9), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS = BIT(10), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT = BIT(11), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE = BIT(12), +}; + +enum htt_rx_mgmt_pkt_filter_tlv_flags0 { + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(0), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(1), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(2), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(3), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(4), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(5), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(6), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(7), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(8), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(9), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(10), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(11), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(12), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(13), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(14), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(15), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(16), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(17), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(18), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(19), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(20), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(21), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(22), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(23), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(24), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(25), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(26), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(27), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(28), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(29), +}; + +enum htt_rx_mgmt_pkt_filter_tlv_flags1 { + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(0), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(1), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(2), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(3), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(4), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(5), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(6), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(7), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(8), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(9), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(10), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(11), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(12), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(13), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(14), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(15), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(16), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(17), +}; + +enum htt_rx_ctrl_pkt_filter_tlv_flags2 { + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(0), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(1), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(2), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(3), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(4), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(5), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(6), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(7), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(8), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(9), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(10), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(11), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(12), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(13), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(14), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(15), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(16), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(17), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(18), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(19), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(20), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(21), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(22), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(23), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(24), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(25), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(26), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(27), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(28), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(29), +}; + +enum htt_rx_ctrl_pkt_filter_tlv_flags3 { + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(0), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(1), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(2), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(3), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(4), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(5), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(6), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(7), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(8), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(9), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(10), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(11), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(12), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(13), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(14), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(15), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(16), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(17), +}; + +enum htt_rx_data_pkt_filter_tlv_flasg3 { + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(18), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(19), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(20), + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(21), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(22), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(23), + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(24), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(25), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(26), +}; + +#define HTT_RX_FP_MGMT_FILTER_FLAGS0 \ + (HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_MD_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_MO_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_FP_MGMT_FILTER_FLAGS1 (HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_MD_MGMT_FILTER_FLAGS1 (HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_MO_MGMT_FILTER_FLAGS1 (HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_FP_CTRL_FILTER_FLASG2 (HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_MD_CTRL_FILTER_FLASG2 (HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_MO_CTRL_FILTER_FLASG2 (HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_FP_CTRL_FILTER_FLASG3 (HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_MD_CTRL_FILTER_FLASG3 (HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_MO_CTRL_FILTER_FLASG3 (HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_FP_DATA_FILTER_FLASG3 (HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MD_DATA_FILTER_FLASG3 (HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MO_DATA_FILTER_FLASG3 (HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 \ + (HTT_RX_FP_MGMT_FILTER_FLAGS0 | \ + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7) + +#define HTT_RX_MON_MO_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MO_MGMT_FILTER_FLAGS0 | \ + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7) + +#define HTT_RX_MON_FP_MGMT_FILTER_FLAGS1 \ + (HTT_RX_FP_MGMT_FILTER_FLAGS1 | \ + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15) + +#define HTT_RX_MON_MO_MGMT_FILTER_FLAGS1 \ + (HTT_RX_MO_MGMT_FILTER_FLAGS1 | \ + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15) + +#define HTT_RX_MON_FP_CTRL_FILTER_FLASG2 \ + (HTT_RX_FP_CTRL_FILTER_FLASG2 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT) + +#define HTT_RX_MON_MO_CTRL_FILTER_FLASG2 \ + (HTT_RX_MO_CTRL_FILTER_FLASG2 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT) + +#define HTT_RX_MON_FP_CTRL_FILTER_FLASG3 HTT_RX_FP_CTRL_FILTER_FLASG3 + +#define HTT_RX_MON_MO_CTRL_FILTER_FLASG3 HTT_RX_MO_CTRL_FILTER_FLASG3 + +#define HTT_RX_MON_FP_DATA_FILTER_FLASG3 HTT_RX_FP_DATA_FILTER_FLASG3 + +#define HTT_RX_MON_MO_DATA_FILTER_FLASG3 HTT_RX_MO_DATA_FILTER_FLASG3 + +#define HTT_RX_MON_FILTER_TLV_FLAGS \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE) + +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE) + +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_ATTENTION) + +struct htt_rx_ring_selection_cfg_cmd { + u32 info0; + u32 info1; + u32 pkt_type_en_flags0; + u32 pkt_type_en_flags1; + u32 pkt_type_en_flags2; + u32 pkt_type_en_flags3; + u32 rx_filter_tlv; +} __packed; + +struct htt_rx_ring_tlv_filter { + u32 rx_filter; /* see htt_rx_filter_tlv_flags */ + u32 pkt_filter_flags0; /* MGMT */ + u32 pkt_filter_flags1; /* MGMT */ + u32 pkt_filter_flags2; /* CTRL */ + u32 pkt_filter_flags3; /* DATA */ +}; + +/* HTT message target->host */ + +enum htt_t2h_msg_type { + HTT_T2H_MSG_TYPE_VERSION_CONF, + HTT_T2H_MSG_TYPE_RX_ADDBA = 0x5, + HTT_T2H_MSG_TYPE_PKTLOG = 0x8, + HTT_T2H_MSG_TYPE_SEC_IND = 0xb, + HTT_T2H_MSG_TYPE_PEER_MAP = 0x1e, + HTT_T2H_MSG_TYPE_PEER_UNMAP = 0x1f, + HTT_T2H_MSG_TYPE_PPDU_STATS_IND = 0x1d, + HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c, +}; + +#define HTT_TARGET_VERSION_MAJOR 3 + +#define HTT_T2H_MSG_TYPE GENMASK(7, 0) +#define HTT_T2H_VERSION_CONF_MINOR GENMASK(15, 8) +#define HTT_T2H_VERSION_CONF_MAJOR GENMASK(23, 16) + +struct htt_t2h_version_conf_msg { + u32 version; +} __packed; + +#define HTT_T2H_PEER_MAP_INFO_VDEV_ID GENMASK(15, 8) +#define HTT_T2H_PEER_MAP_INFO_PEER_ID GENMASK(31, 16) +#define HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0) +#define HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID GENMASK(31, 16) +#define HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL GENMASK(15, 0) +#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M BIT(16) +#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S 16 + +struct htt_t2h_peer_map_event { + u32 info; + u32 mac_addr_l32; + u32 info1; + u32 info2; +} __packed; + +#define HTT_T2H_PEER_UNMAP_INFO_VDEV_ID HTT_T2H_PEER_MAP_INFO_VDEV_ID +#define HTT_T2H_PEER_UNMAP_INFO_PEER_ID HTT_T2H_PEER_MAP_INFO_PEER_ID +#define HTT_T2H_PEER_UNMAP_INFO1_MAC_ADDR_H16 \ + HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 +#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_M HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M +#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_S HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S + +struct htt_t2h_peer_unmap_event { + u32 info; + u32 mac_addr_l32; + u32 info1; +} __packed; + +struct htt_resp_msg { + union { + struct htt_t2h_version_conf_msg version_msg; + struct htt_t2h_peer_map_event peer_map_ev; + struct htt_t2h_peer_unmap_event peer_unmap_ev; + }; +} __packed; + +/* ppdu stats + * + * @details + * The following field definitions describe the format of the HTT target + * to host ppdu stats indication message. + * + * + * |31 16|15 12|11 10|9 8|7 0 | + * |----------------------------------------------------------------------| + * | payload_size | rsvd |pdev_id|mac_id | msg type | + * |----------------------------------------------------------------------| + * | ppdu_id | + * |----------------------------------------------------------------------| + * | Timestamp in us | + * |----------------------------------------------------------------------| + * | reserved | + * |----------------------------------------------------------------------| + * | type-specific stats info | + * | (see htt_ppdu_stats.h) | + * |----------------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a PPDU STATS indication + * message. + * Value: 0x1d + * - mac_id + * Bits 9:8 + * Purpose: mac_id of this ppdu_id + * Value: 0-3 + * - pdev_id + * Bits 11:10 + * Purpose: pdev_id of this ppdu_id + * Value: 0-3 + * 0 (for rings at SOC level), + * 1/2/3 PDEV -> 0/1/2 + * - payload_size + * Bits 31:16 + * Purpose: total tlv size + * Value: payload_size in bytes + */ + +#define HTT_T2H_PPDU_STATS_INFO_PDEV_ID GENMASK(11, 10) +#define HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE GENMASK(31, 16) + +struct ath11k_htt_ppdu_stats_msg { + u32 info; + u32 ppdu_id; + u32 timestamp; + u32 rsvd; + u8 data[0]; +} __packed; + +struct htt_tlv { + u32 header; + u8 value[0]; +} __packed; + +#define HTT_TLV_TAG GENMASK(11, 0) +#define HTT_TLV_LEN GENMASK(23, 12) + +enum HTT_PPDU_STATS_BW { + HTT_PPDU_STATS_BANDWIDTH_5MHZ = 0, + HTT_PPDU_STATS_BANDWIDTH_10MHZ = 1, + HTT_PPDU_STATS_BANDWIDTH_20MHZ = 2, + HTT_PPDU_STATS_BANDWIDTH_40MHZ = 3, + HTT_PPDU_STATS_BANDWIDTH_80MHZ = 4, + HTT_PPDU_STATS_BANDWIDTH_160MHZ = 5, /* includes 80+80 */ + HTT_PPDU_STATS_BANDWIDTH_DYN = 6, +}; + +#define HTT_PPDU_STATS_CMN_FLAGS_FRAME_TYPE_M GENMASK(7, 0) +#define HTT_PPDU_STATS_CMN_FLAGS_QUEUE_TYPE_M GENMASK(15, 8) +/* bw - HTT_PPDU_STATS_BW */ +#define HTT_PPDU_STATS_CMN_FLAGS_BW_M GENMASK(19, 16) + +struct htt_ppdu_stats_common { + u32 ppdu_id; + u16 sched_cmdid; + u8 ring_id; + u8 num_users; + u32 flags; /* %HTT_PPDU_STATS_COMMON_FLAGS_*/ + u32 chain_mask; + u32 fes_duration_us; /* frame exchange sequence */ + u32 ppdu_sch_eval_start_tstmp_us; + u32 ppdu_sch_end_tstmp_us; + u32 ppdu_start_tstmp_us; + /* BIT [15 : 0] - phy mode (WLAN_PHY_MODE) with which ppdu was transmitted + * BIT [31 : 16] - bandwidth (in MHz) with which ppdu was transmitted + */ + u16 phy_mode; + u16 bw_mhz; +} __packed; + +#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) + +#define HTT_PPDU_STATS_USER_RATE_INFO1_RESP_TYPE_VALD_M BIT(0) +#define HTT_PPDU_STATS_USER_RATE_INFO1_PPDU_TYPE_M GENMASK(5, 1) + +#define HTT_PPDU_STATS_USER_RATE_FLAGS_LTF_SIZE_M GENMASK(1, 0) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_STBC_M BIT(2) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_HE_RE_M BIT(3) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_TXBF_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_BW_M GENMASK(11, 8) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_NSS_M GENMASK(15, 12) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_MCS_M GENMASK(19, 16) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_PREAMBLE_M GENMASK(23, 20) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M GENMASK(27, 24) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_DCM_M BIT(28) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_LDPC_M BIT(29) + +#define HTT_USR_RATE_PREAMBLE(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_PREAMBLE_M, _val) +#define HTT_USR_RATE_BW(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_BW_M, _val) +#define HTT_USR_RATE_NSS(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_NSS_M, _val) +#define HTT_USR_RATE_MCS(_val) \ + 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_PPDU_STATS_USER_RATE_RESP_FLAGS_LTF_SIZE_M GENMASK(1, 0) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_STBC_M BIT(2) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_HE_RE_M BIT(3) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_TXBF_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_BW_M GENMASK(11, 8) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_NSS_M GENMASK(15, 12) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_MCS_M GENMASK(19, 16) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_PREAMBLE_M GENMASK(23, 20) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_GI_M GENMASK(27, 24) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_DCM_M BIT(28) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LDPC_M BIT(29) + +struct htt_ppdu_stats_user_rate { + u8 tid_num; + u8 reserved0; + u16 sw_peer_id; + u32 info0; /* %HTT_PPDU_STATS_USER_RATE_INFO0_*/ + u16 ru_end; + u16 ru_start; + u16 resp_ru_end; + u16 resp_ru_start; + u32 info1; /* %HTT_PPDU_STATS_USER_RATE_INFO1_ */ + u32 rate_flags; /* %HTT_PPDU_STATS_USER_RATE_FLAGS_ */ + /* Note: resp_rate_info is only valid for if resp_type is UL */ + u32 resp_rate_flags; /* %HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_ */ +} __packed; + +#define HTT_PPDU_STATS_TX_INFO_FLAGS_RATECODE_M GENMASK(7, 0) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_IS_AMPDU_M BIT(8) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_BA_ACK_FAILED_M GENMASK(10, 9) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_BW_M GENMASK(13, 11) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_SGI_M BIT(14) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M GENMASK(31, 16) + +#define HTT_TX_INFO_IS_AMSDU(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_IS_AMPDU_M, _flags) +#define HTT_TX_INFO_BA_ACK_FAILED(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_BA_ACK_FAILED_M, _flags) +#define HTT_TX_INFO_RATECODE(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_RATECODE_M, _flags) +#define HTT_TX_INFO_PEERID(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M, _flags) + +struct htt_tx_ppdu_stats_info { + struct htt_tlv tlv_hdr; + u32 tx_success_bytes; + u32 tx_retry_bytes; + u32 tx_failed_bytes; + u32 flags; /* %HTT_PPDU_STATS_TX_INFO_FLAGS_ */ + u16 tx_success_msdus; + u16 tx_retry_msdus; + u16 tx_failed_msdus; + u16 tx_duration; /* united in us */ +} __packed; + +enum htt_ppdu_stats_usr_compln_status { + HTT_PPDU_STATS_USER_STATUS_OK, + HTT_PPDU_STATS_USER_STATUS_FILTERED, + HTT_PPDU_STATS_USER_STATUS_RESP_TIMEOUT, + HTT_PPDU_STATS_USER_STATUS_RESP_MISMATCH, + HTT_PPDU_STATS_USER_STATUS_ABORT, +}; + +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRY_M GENMASK(3, 0) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_SHORT_RETRY_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_IS_AMPDU_M BIT(8) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_RESP_TYPE_M GENMASK(12, 9) + +#define HTT_USR_CMPLTN_IS_AMPDU(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_IS_AMPDU_M, _val) +#define HTT_USR_CMPLTN_LONG_RETRY(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRY_M, _val) +#define HTT_USR_CMPLTN_SHORT_RETRY(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_SHORT_RETRY_M, _val) + +struct htt_ppdu_stats_usr_cmpltn_cmn { + u8 status; + u8 tid_num; + u16 sw_peer_id; + /* RSSI value of last ack packet (units = dB above noise floor) */ + u32 ack_rssi; + u16 mpdu_tried; + u16 mpdu_success; + u32 flags; /* %HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRIES*/ +} __packed; + +#define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MPDU_M GENMASK(8, 0) +#define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M GENMASK(24, 9) +#define HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM GENMASK(31, 25) + +#define HTT_PPDU_STATS_NON_QOS_TID 16 + +struct htt_ppdu_stats_usr_cmpltn_ack_ba_status { + u32 ppdu_id; + u16 sw_peer_id; + u16 reserved0; + u32 info; /* %HTT_PPDU_STATS_USR_CMPLTN_CMN_INFO_ */ + u16 current_seq; + u16 start_seq; + u32 success_bytes; +} __packed; + +struct htt_ppdu_stats_usr_cmn_array { + struct htt_tlv tlv_hdr; + u32 num_ppdu_stats; + /* tx_ppdu_stats_info is filled by multiple struct htt_tx_ppdu_stats_info + * elements. + * tx_ppdu_stats_info is variable length, with length = + * number_of_ppdu_stats * sizeof (struct htt_tx_ppdu_stats_info) + */ + struct htt_tx_ppdu_stats_info tx_ppdu_info[0]; +} __packed; + +struct htt_ppdu_user_stats { + u16 peer_id; + u32 tlv_flags; + bool is_valid_peer_id; + struct htt_ppdu_stats_user_rate rate; + struct htt_ppdu_stats_usr_cmpltn_cmn cmpltn_cmn; + struct htt_ppdu_stats_usr_cmpltn_ack_ba_status ack_ba; +}; + +#define HTT_PPDU_STATS_MAX_USERS 8 +#define HTT_PPDU_DESC_MAX_DEPTH 16 + +struct htt_ppdu_stats { + struct htt_ppdu_stats_common common; + struct htt_ppdu_user_stats user_stats[HTT_PPDU_STATS_MAX_USERS]; +}; + +struct htt_ppdu_stats_info { + u32 ppdu_id; + struct htt_ppdu_stats ppdu_stats; + struct list_head list; +}; + +/** + * @brief target -> host packet log message + * + * @details + * The following field definitions describe the format of the packet log + * message sent from the target to the host. + * The message consists of a 4-octet header,followed by a variable number + * of 32-bit character values. + * + * |31 16|15 12|11 10|9 8|7 0| + * |------------------------------------------------------------------| + * | payload_size | rsvd |pdev_id|mac_id| msg type | + * |------------------------------------------------------------------| + * | payload | + * |------------------------------------------------------------------| + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this as a pktlog message + * Value: HTT_T2H_MSG_TYPE_PKTLOG + * - mac_id + * Bits 9:8 + * Purpose: identifies which MAC/PHY instance generated this pktlog info + * Value: 0-3 + * - pdev_id + * Bits 11:10 + * Purpose: pdev_id + * Value: 0-3 + * 0 (for rings at SOC level), + * 1/2/3 PDEV -> 0/1/2 + * - payload_size + * Bits 31:16 + * Purpose: explicitly specify the payload size + * Value: payload size in bytes (payload size is a multiple of 4 bytes) + */ +struct htt_pktlog_msg { + u32 hdr; + u8 payload[0]; +}; + +/** + * @brief host -> target FW extended statistics retrieve + * + * @details + * The following field definitions describe the format of the HTT host + * to target FW extended stats retrieve message. + * The message specifies the type of stats the host wants to retrieve. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | reserved | stats type | pdev_mask | msg type | + * |-----------------------------------------------------------| + * | config param [0] | + * |-----------------------------------------------------------| + * | config param [1] | + * |-----------------------------------------------------------| + * | config param [2] | + * |-----------------------------------------------------------| + * | config param [3] | + * |-----------------------------------------------------------| + * | reserved | + * |-----------------------------------------------------------| + * | cookie LSBs | + * |-----------------------------------------------------------| + * | cookie MSBs | + * |-----------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a extended stats upload request message + * Value: 0x10 + * - PDEV_MASK + * Bits 8:15 + * Purpose: identifies the mask of PDEVs to retrieve stats from + * Value: This is a overloaded field, refer to usage and interpretation of + * PDEV in interface document. + * Bit 8 : Reserved for SOC stats + * Bit 9 - 15 : Indicates PDEV_MASK in DBDC + * Indicates MACID_MASK in DBS + * - STATS_TYPE + * Bits 23:16 + * Purpose: identifies which FW statistics to upload + * Value: Defined by htt_dbg_ext_stats_type (see htt_stats.h) + * - Reserved + * Bits 31:24 + * - CONFIG_PARAM [0] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [1] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [2] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [3] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - Reserved [31:0] for future use. + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + */ + +struct htt_ext_stats_cfg_hdr { + u8 msg_type; + u8 pdev_mask; + u8 stats_type; + u8 reserved; +} __packed; + +struct htt_ext_stats_cfg_cmd { + struct htt_ext_stats_cfg_hdr hdr; + u32 cfg_param0; + u32 cfg_param1; + u32 cfg_param2; + u32 cfg_param3; + u32 reserved; + u32 cookie_lsb; + u32 cookie_msb; +} __packed; + +/* htt stats config default params */ +#define HTT_STAT_DEFAULT_RESET_START_OFFSET 0 +#define HTT_STAT_DEFAULT_CFG0_ALL_HWQS 0xffffffff +#define HTT_STAT_DEFAULT_CFG0_ALL_TXQS 0xffffffff +#define HTT_STAT_DEFAULT_CFG0_ALL_CMDQS 0xffff +#define HTT_STAT_DEFAULT_CFG0_ALL_RINGS 0xffff +#define HTT_STAT_DEFAULT_CFG0_ACTIVE_PEERS 0xff +#define HTT_STAT_DEFAULT_CFG0_CCA_CUMULATIVE 0x00 +#define HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS 0x00 + +/* HTT_DBG_EXT_STATS_PEER_INFO + * PARAMS: + * @config_param0: + * [Bit0] - [0] for sw_peer_id, [1] for mac_addr based request + * [Bit15 : Bit 1] htt_peer_stats_req_mode_t + * [Bit31 : Bit16] sw_peer_id + * @config_param1: + * peer_stats_req_type_mask:32 (enum htt_peer_stats_tlv_enum) + * 0 bit htt_peer_stats_cmn_tlv + * 1 bit htt_peer_details_tlv + * 2 bit htt_tx_peer_rate_stats_tlv + * 3 bit htt_rx_peer_rate_stats_tlv + * 4 bit htt_tx_tid_stats_tlv/htt_tx_tid_stats_v1_tlv + * 5 bit htt_rx_tid_stats_tlv + * 6 bit htt_msdu_flow_stats_tlv + * @config_param2: [Bit31 : Bit0] mac_addr31to0 + * @config_param3: [Bit15 : Bit0] mac_addr47to32 + * [Bit31 : Bit16] reserved + */ +#define HTT_STAT_PEER_INFO_MAC_ADDR BIT(0) +#define HTT_STAT_DEFAULT_PEER_REQ_TYPE 0x7f + +/* Used to set different configs to the specified stats type.*/ +struct htt_ext_stats_cfg_params { + u32 cfg0; + u32 cfg1; + u32 cfg2; + u32 cfg3; +}; + +/** + * @brief target -> host extended statistics upload + * + * @details + * The following field definitions describe the format of the HTT target + * to host stats upload confirmation message. + * The message contains a cookie echoed from the HTT host->target stats + * upload request, which identifies which request the confirmation is + * for, and a single stats can span over multiple HTT stats indication + * due to the HTT message size limitation so every HTT ext stats indication + * will have tag-length-value stats information elements. + * The tag-length header for each HTT stats IND message also includes a + * status field, to indicate whether the request for the stat type in + * question was fully met, partially met, unable to be met, or invalid + * (if the stat type in question is disabled in the target). + * A Done bit 1's indicate the end of the of stats info elements. + * + * + * |31 16|15 12|11|10 8|7 5|4 0| + * |--------------------------------------------------------------| + * | reserved | msg type | + * |--------------------------------------------------------------| + * | cookie LSBs | + * |--------------------------------------------------------------| + * | cookie MSBs | + * |--------------------------------------------------------------| + * | stats entry length | rsvd | D| S | stat type | + * |--------------------------------------------------------------| + * | type-specific stats info | + * | (see htt_stats.h) | + * |--------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a extended statistics upload confirmation + * message. + * Value: 0x1c + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * + * Stats Information Element tag-length header fields: + * - STAT_TYPE + * Bits 7:0 + * Purpose: identifies the type of statistics info held in the + * following information element + * Value: htt_dbg_ext_stats_type + * - STATUS + * Bits 10:8 + * Purpose: indicate whether the requested stats are present + * Value: htt_dbg_ext_stats_status + * - DONE + * Bits 11 + * Purpose: + * Indicates the completion of the stats entry, this will be the last + * stats conf HTT segment for the requested stats type. + * Value: + * 0 -> the stats retrieval is ongoing + * 1 -> the stats retrieval is complete + * - LENGTH + * Bits 31:16 + * Purpose: indicate the stats information size + * Value: This field specifies the number of bytes of stats information + * that follows the element tag-length header. + * It is expected but not required that this length is a multiple of + * 4 bytes. + */ + +#define HTT_T2H_EXT_STATS_INFO1_LENGTH GENMASK(31, 16) + +struct ath11k_htt_extd_stats_msg { + u32 info0; + u64 cookie; + u32 info1; + u8 data[0]; +} __packed; + +struct htt_mac_addr { + u32 mac_addr_l32; + u32 mac_addr_h16; +}; + +static inline void ath11k_dp_get_mac_addr(u32 addr_l32, u16 addr_h16, u8 *addr) +{ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { + addr_l32 = swab32(addr_l32); + addr_h16 = swab16(addr_h16); + } + + memcpy(addr, &addr_l32, 4); + memcpy(addr + 4, &addr_h16, ETH_ALEN - 4); +} + +int ath11k_dp_service_srng(struct ath11k_base *ab, + struct ath11k_ext_irq_grp *irq_grp, + int budget); +int ath11k_dp_htt_connect(struct ath11k_dp *dp); +void ath11k_dp_vdev_tx_attach(struct ath11k *ar, struct ath11k_vif *arvif); +void ath11k_dp_free(struct ath11k_base *ab); +int ath11k_dp_alloc(struct ath11k_base *ab); +int ath11k_dp_pdev_alloc(struct ath11k_base *ab); +void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab); +void ath11k_dp_pdev_free(struct ath11k_base *ab); +int ath11k_dp_tx_htt_srng_setup(struct ath11k_base *ab, u32 ring_id, + int mac_id, enum hal_ring_type ring_type); +int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr); +void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr); +void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring); +int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, + enum hal_ring_type type, int ring_num, + int mac_id, int num_entries); +void ath11k_dp_link_desc_cleanup(struct ath11k_base *ab, + struct dp_link_desc_bank *desc_bank, + u32 ring_type, struct dp_srng *ring); +int ath11k_dp_link_desc_setup(struct ath11k_base *ab, + struct dp_link_desc_bank *link_desc_banks, + u32 ring_type, struct hal_srng *srng, + u32 n_link_desc); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c new file mode 100644 index 000000000000..b08da839b7d9 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -0,0 +1,4195 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/ieee80211.h> +#include "core.h" +#include "debug.h" +#include "hal_desc.h" +#include "hw.h" +#include "dp_rx.h" +#include "hal_rx.h" +#include "dp_tx.h" +#include "peer.h" + +static u8 *ath11k_dp_rx_h_80211_hdr(struct hal_rx_desc *desc) +{ + return desc->hdr_status; +} + +static enum hal_encrypt_type ath11k_dp_rx_h_mpdu_start_enctype(struct hal_rx_desc *desc) +{ + if (!(__le32_to_cpu(desc->mpdu_start.info1) & + RX_MPDU_START_INFO1_ENCRYPT_INFO_VALID)) + return HAL_ENCRYPT_TYPE_OPEN; + + return FIELD_GET(RX_MPDU_START_INFO2_ENC_TYPE, + __le32_to_cpu(desc->mpdu_start.info2)); +} + +static u8 ath11k_dp_rx_h_mpdu_start_decap_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO5_DECAP_TYPE, + __le32_to_cpu(desc->mpdu_start.info5)); +} + +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_first_mpdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO1_FIRST_MPDU, + __le32_to_cpu(desc->attention.info1)); +} + +static bool ath11k_dp_rx_h_attn_l4_cksum_fail(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO1_TCP_UDP_CKSUM_FAIL, + __le32_to_cpu(desc->attention.info1)); +} + +static bool ath11k_dp_rx_h_attn_ip_cksum_fail(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO1_IP_CKSUM_FAIL, + __le32_to_cpu(desc->attention.info1)); +} + +static bool ath11k_dp_rx_h_attn_is_decrypted(struct hal_rx_desc *desc) +{ + return (FIELD_GET(RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE, + __le32_to_cpu(desc->attention.info2)) == + RX_DESC_DECRYPT_STATUS_CODE_OK); +} + +static u32 ath11k_dp_rx_h_attn_mpdu_err(struct hal_rx_desc *desc) +{ + u32 info = __le32_to_cpu(desc->attention.info1); + u32 errmap = 0; + + if (info & RX_ATTENTION_INFO1_FCS_ERR) + errmap |= DP_RX_MPDU_ERR_FCS; + + if (info & RX_ATTENTION_INFO1_DECRYPT_ERR) + errmap |= DP_RX_MPDU_ERR_DECRYPT; + + if (info & RX_ATTENTION_INFO1_TKIP_MIC_ERR) + errmap |= DP_RX_MPDU_ERR_TKIP_MIC; + + if (info & RX_ATTENTION_INFO1_A_MSDU_ERROR) + errmap |= DP_RX_MPDU_ERR_AMSDU_ERR; + + if (info & RX_ATTENTION_INFO1_OVERFLOW_ERR) + errmap |= DP_RX_MPDU_ERR_OVERFLOW; + + if (info & RX_ATTENTION_INFO1_MSDU_LEN_ERR) + errmap |= DP_RX_MPDU_ERR_MSDU_LEN; + + if (info & RX_ATTENTION_INFO1_MPDU_LEN_ERR) + errmap |= DP_RX_MPDU_ERR_MPDU_LEN; + + return errmap; +} + +static u16 ath11k_dp_rx_h_msdu_start_msdu_len(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO1_MSDU_LENGTH, + __le32_to_cpu(desc->msdu_start.info1)); +} + +static u8 ath11k_dp_rx_h_msdu_start_sgi(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_SGI, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u8 ath11k_dp_rx_h_msdu_start_rate_mcs(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RATE_MCS, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u8 ath11k_dp_rx_h_msdu_start_rx_bw(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RECV_BW, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u32 ath11k_dp_rx_h_msdu_start_freq(struct hal_rx_desc *desc) +{ + return __le32_to_cpu(desc->msdu_start.phy_meta_data); +} + +static u8 ath11k_dp_rx_h_msdu_start_pkt_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_PKT_TYPE, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u8 ath11k_dp_rx_h_msdu_start_nss(struct hal_rx_desc *desc) +{ + u8 mimo_ss_bitmap = FIELD_GET(RX_MSDU_START_INFO3_MIMO_SS_BITMAP, + __le32_to_cpu(desc->msdu_start.info3)); + + return hweight8(mimo_ss_bitmap); +} + +static u8 ath11k_dp_rx_h_msdu_end_l3pad(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_END_INFO2_L3_HDR_PADDING, + __le32_to_cpu(desc->msdu_end.info2)); +} + +static bool ath11k_dp_rx_h_msdu_end_first_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO2_FIRST_MSDU, + __le32_to_cpu(desc->msdu_end.info2)); +} + +static bool ath11k_dp_rx_h_msdu_end_last_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO2_LAST_MSDU, + __le32_to_cpu(desc->msdu_end.info2)); +} + +static void ath11k_dp_rx_desc_end_tlv_copy(struct hal_rx_desc *fdesc, + struct hal_rx_desc *ldesc) +{ + memcpy((u8 *)&fdesc->msdu_end, (u8 *)&ldesc->msdu_end, + sizeof(struct rx_msdu_end)); + memcpy((u8 *)&fdesc->attention, (u8 *)&ldesc->attention, + sizeof(struct rx_attention)); + memcpy((u8 *)&fdesc->mpdu_end, (u8 *)&ldesc->mpdu_end, + sizeof(struct rx_mpdu_end)); +} + +static u32 ath11k_dp_rxdesc_get_mpdulen_err(struct hal_rx_desc *rx_desc) +{ + struct rx_attention *rx_attn; + + rx_attn = &rx_desc->attention; + + return FIELD_GET(RX_ATTENTION_INFO1_MPDU_LEN_ERR, + __le32_to_cpu(rx_attn->info1)); +} + +static u32 ath11k_dp_rxdesc_get_decap_format(struct hal_rx_desc *rx_desc) +{ + struct rx_msdu_start *rx_msdu_start; + + rx_msdu_start = &rx_desc->msdu_start; + + return FIELD_GET(RX_MSDU_START_INFO2_DECAP_FORMAT, + __le32_to_cpu(rx_msdu_start->info2)); +} + +static u8 *ath11k_dp_rxdesc_get_80211hdr(struct hal_rx_desc *rx_desc) +{ + u8 *rx_pkt_hdr; + + rx_pkt_hdr = &rx_desc->msdu_payload[0]; + + return rx_pkt_hdr; +} + +static bool ath11k_dp_rxdesc_mpdu_valid(struct hal_rx_desc *rx_desc) +{ + u32 tlv_tag; + + tlv_tag = FIELD_GET(HAL_TLV_HDR_TAG, + __le32_to_cpu(rx_desc->mpdu_start_tag)); + + return tlv_tag == HAL_RX_MPDU_START ? true : false; +} + +static u32 ath11k_dp_rxdesc_get_ppduid(struct hal_rx_desc *rx_desc) +{ + return __le16_to_cpu(rx_desc->mpdu_start.phy_ppdu_id); +} + +/* Returns number of Rx buffers replenished */ +int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, + enum hal_rx_buf_return_buf_manager mgr, + gfp_t gfp) +{ + struct hal_srng *srng; + u32 *desc; + struct sk_buff *skb; + int num_free; + int num_remain; + int buf_id; + u32 cookie; + dma_addr_t paddr; + + req_entries = min(req_entries, rx_ring->bufs_max); + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + num_free = ath11k_hal_srng_src_num_free(ab, srng, true); + if (!req_entries && (num_free > (rx_ring->bufs_max * 3) / 4)) + req_entries = num_free; + + req_entries = min(num_free, req_entries); + num_remain = req_entries; + + while (num_remain > 0) { + skb = dev_alloc_skb(DP_RX_BUFFER_SIZE + + DP_RX_BUFFER_ALIGN_SIZE); + if (!skb) + break; + + if (!IS_ALIGNED((unsigned long)skb->data, + DP_RX_BUFFER_ALIGN_SIZE)) { + skb_pull(skb, + PTR_ALIGN(skb->data, DP_RX_BUFFER_ALIGN_SIZE) - + skb->data); + } + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (dma_mapping_error(ab->dev, paddr)) + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); + buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, + rx_ring->bufs_max * 3, gfp); + spin_unlock_bh(&rx_ring->idr_lock); + if (buf_id < 0) + goto fail_dma_unmap; + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) + goto fail_idr_remove; + + ATH11K_SKB_RXCB(skb)->paddr = paddr; + + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + + num_remain--; + + ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr); + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; + +fail_idr_remove: + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); +fail_dma_unmap: + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); +fail_free_skb: + dev_kfree_skb_any(skb); + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; +} + +static int ath11k_dp_rxdma_buf_ring_free(struct ath11k *ar, + struct dp_rxdma_ring *rx_ring) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct sk_buff *skb; + int buf_id; + + spin_lock_bh(&rx_ring->idr_lock); + idr_for_each_entry(&rx_ring->bufs_idr, skb, buf_id) { + idr_remove(&rx_ring->bufs_idr, buf_id); + /* TODO: Understand where internal driver does this dma_unmap of + * of rxdma_buffer. + */ + dma_unmap_single(ar->ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } + + idr_destroy(&rx_ring->bufs_idr); + spin_unlock_bh(&rx_ring->idr_lock); + + rx_ring = &dp->rx_mon_status_refill_ring; + + spin_lock_bh(&rx_ring->idr_lock); + idr_for_each_entry(&rx_ring->bufs_idr, skb, buf_id) { + idr_remove(&rx_ring->bufs_idr, buf_id); + /* XXX: Understand where internal driver does this dma_unmap of + * of rxdma_buffer. + */ + dma_unmap_single(ar->ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_BIDIRECTIONAL); + dev_kfree_skb_any(skb); + } + + idr_destroy(&rx_ring->bufs_idr); + spin_unlock_bh(&rx_ring->idr_lock); + return 0; +} + +static int ath11k_dp_rxdma_pdev_buf_free(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + + rx_ring = &dp->rxdma_mon_buf_ring; + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + + rx_ring = &dp->rx_mon_status_refill_ring; + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + return 0; +} + +static int ath11k_dp_rxdma_ring_buf_setup(struct ath11k *ar, + struct dp_rxdma_ring *rx_ring, + u32 ringtype) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + int num_entries; + + num_entries = rx_ring->refill_buf_ring.size / + ath11k_hal_srng_get_entrysize(ringtype); + + rx_ring->bufs_max = num_entries; + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, rx_ring, num_entries, + HAL_RX_BUF_RBM_SW3_BM, GFP_KERNEL); + return 0; +} + +static int ath11k_dp_rxdma_pdev_buf_setup(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF); + + rx_ring = &dp->rxdma_mon_buf_ring; + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_BUF); + + rx_ring = &dp->rx_mon_status_refill_ring; + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_STATUS); + + return 0; +} + +static void ath11k_dp_rx_pdev_srng_free(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + + ath11k_dp_srng_cleanup(ar->ab, &dp->rx_refill_buf_ring.refill_buf_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_err_dst_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->rx_mon_status_refill_ring.refill_buf_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_mon_buf_ring.refill_buf_ring); +} + +void ath11k_dp_pdev_reo_cleanup(struct ath11k_base *ab) +{ + struct ath11k_pdev_dp *dp; + struct ath11k *ar; + 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); + } +} + +int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev_dp *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, + DP_REO_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup reo_dst_ring\n"); + goto err_reo_cleanup; + } + } + + return 0; + +err_reo_cleanup: + ath11k_dp_pdev_reo_cleanup(ab); + + return ret; +} + +static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_srng *srng = NULL; + int ret; + + ret = ath11k_dp_srng_setup(ar->ab, + &dp->rx_refill_buf_ring.refill_buf_ring, + HAL_RXDMA_BUF, 0, + dp->mac_id, DP_RXDMA_BUF_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rx_refill_buf_ring\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_err_dst_ring, + HAL_RXDMA_DST, 0, dp->mac_id, + DP_RXDMA_ERR_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rxdma_err_dst_ring\n"); + return ret; + } + + srng = &dp->rx_mon_status_refill_ring.refill_buf_ring; + ret = ath11k_dp_srng_setup(ar->ab, + srng, + HAL_RXDMA_MONITOR_STATUS, 0, dp->mac_id, + DP_RXDMA_MON_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup rx_mon_status_refill_ring\n"); + return ret; + } + ret = ath11k_dp_srng_setup(ar->ab, + &dp->rxdma_mon_buf_ring.refill_buf_ring, + HAL_RXDMA_MONITOR_BUF, 0, dp->mac_id, + DP_RXDMA_MONITOR_BUF_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_BUF\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_mon_dst_ring, + HAL_RXDMA_MONITOR_DST, 0, dp->mac_id, + DP_RXDMA_MONITOR_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_DST\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_mon_desc_ring, + HAL_RXDMA_MONITOR_DESC, 0, dp->mac_id, + DP_RXDMA_MONITOR_DESC_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_DESC\n"); + return ret; + } + + return 0; +} + +void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct dp_reo_cmd *cmd, *tmp; + struct dp_reo_cache_flush_elem *cmd_cache, *tmp_cache; + + spin_lock_bh(&dp->reo_cmd_lock); + list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { + list_del(&cmd->list); + dma_unmap_single(ab->dev, cmd->data.paddr, + cmd->data.size, DMA_BIDIRECTIONAL); + kfree(cmd->data.vaddr); + kfree(cmd); + } + + list_for_each_entry_safe(cmd_cache, tmp_cache, + &dp->reo_cmd_cache_flush_list, list) { + list_del(&cmd_cache->list); + dma_unmap_single(ab->dev, cmd_cache->data.paddr, + cmd_cache->data.size, DMA_BIDIRECTIONAL); + kfree(cmd_cache->data.vaddr); + kfree(cmd_cache); + } + spin_unlock_bh(&dp->reo_cmd_lock); +} + +static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx, + enum hal_reo_cmd_status status) +{ + struct dp_rx_tid *rx_tid = ctx; + + if (status != HAL_REO_CMD_SUCCESS) + ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", + rx_tid->tid, status); + + dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); +} + +static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab, + struct dp_rx_tid *rx_tid) +{ + struct ath11k_hal_reo_cmd cmd = {0}; + unsigned long tot_desc_sz, desc_sz; + int ret; + + tot_desc_sz = rx_tid->size; + desc_sz = ath11k_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID); + + while (tot_desc_sz > desc_sz) { + tot_desc_sz -= desc_sz; + cmd.addr_lo = lower_32_bits(rx_tid->paddr + tot_desc_sz); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid, + HAL_REO_CMD_FLUSH_CACHE, &cmd, + NULL); + if (ret) + ath11k_warn(ab, + "failed to send HAL_REO_CMD_FLUSH_CACHE, tid %d (%d)\n", + rx_tid->tid, ret); + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS; + ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid, + HAL_REO_CMD_FLUSH_CACHE, + &cmd, ath11k_dp_reo_cmd_free); + if (ret) { + ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", + rx_tid->tid, ret); + dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + } +} + +static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, + enum hal_reo_cmd_status status) +{ + struct ath11k_base *ab = dp->ab; + struct dp_rx_tid *rx_tid = ctx; + struct dp_reo_cache_flush_elem *elem, *tmp; + + if (status == HAL_REO_CMD_DRAIN) { + goto free_desc; + } else if (status != HAL_REO_CMD_SUCCESS) { + /* Shouldn't happen! Cleanup in case of other failure? */ + ath11k_warn(ab, "failed to delete rx tid %d hw descriptor %d\n", + rx_tid->tid, status); + return; + } + + elem = kzalloc(sizeof(*elem), GFP_ATOMIC); + if (!elem) + goto free_desc; + + elem->ts = jiffies; + memcpy(&elem->data, rx_tid, sizeof(*rx_tid)); + + spin_lock_bh(&dp->reo_cmd_lock); + list_add_tail(&elem->list, &dp->reo_cmd_cache_flush_list); + spin_unlock_bh(&dp->reo_cmd_lock); + + /* Flush and invalidate aged REO desc from HW cache */ + spin_lock_bh(&dp->reo_cmd_lock); + list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_cache_flush_list, + list) { + if (time_after(jiffies, elem->ts + + msecs_to_jiffies(DP_REO_DESC_FREE_TIMEOUT_MS))) { + list_del(&elem->list); + spin_unlock_bh(&dp->reo_cmd_lock); + + ath11k_dp_reo_cache_flush(ab, &elem->data); + kfree(elem); + spin_lock_bh(&dp->reo_cmd_lock); + } + } + spin_unlock_bh(&dp->reo_cmd_lock); + + return; +free_desc: + dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); +} + +static 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]; + int ret; + + if (!rx_tid->active) + return; + + cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.upd0 |= HAL_REO_CMD_UPD0_VLD; + ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid, + HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, + ath11k_dp_rx_tid_del_func); + if (ret) { + ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", + tid, ret); + dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + } + + rx_tid->active = false; +} + +void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer) +{ + int i; + + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + ath11k_peer_rx_tid_delete(ar, peer, i); +} + +static int ath11k_peer_rx_tid_reo_update(struct ath11k *ar, + struct ath11k_peer *peer, + struct dp_rx_tid *rx_tid, + u32 ba_win_sz, u16 ssn, + bool update_ssn) +{ + struct ath11k_hal_reo_cmd cmd = {0}; + int ret; + + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; + cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE; + cmd.ba_window_size = ba_win_sz; + + if (update_ssn) { + cmd.upd0 |= HAL_REO_CMD_UPD0_SSN; + cmd.upd2 = FIELD_PREP(HAL_REO_CMD_UPD2_SSN, ssn); + } + + ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid, + HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, + NULL); + if (ret) { + ath11k_warn(ar->ab, "failed to update rx tid queue, tid %d (%d)\n", + rx_tid->tid, ret); + return ret; + } + + rx_tid->ba_win_sz = ba_win_sz; + + return 0; +} + +static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab, + const u8 *peer_mac, int vdev_id, u8 tid) +{ + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + + 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 free up rx tid mem\n"); + goto unlock_exit; + } + + rx_tid = &peer->rx_tid[tid]; + if (!rx_tid->active) + goto unlock_exit; + + dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + + rx_tid->active = false; + +unlock_exit: + spin_unlock_bh(&ab->base_lock); +} + +int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, + u8 tid, u32 ba_win_sz, u16 ssn) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + u32 hw_desc_sz; + u32 *addr_aligned; + void *vaddr; + dma_addr_t paddr; + int ret; + + 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 rx tid\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; + } + + rx_tid = &peer->rx_tid[tid]; + /* Update the tid queue if it is already setup */ + if (rx_tid->active) { + paddr = rx_tid->paddr; + ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid, + ba_win_sz, ssn, true); + spin_unlock_bh(&ab->base_lock); + if (ret) { + ath11k_warn(ab, "failed to update reo for rx tid %d\n", tid); + return ret; + } + + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, + peer_mac, paddr, + tid, 1, ba_win_sz); + if (ret) + ath11k_warn(ab, "failed to send wmi command to update rx reorder queue, tid :%d (%d)\n", + tid, ret); + return ret; + } + + rx_tid->tid = tid; + + rx_tid->ba_win_sz = ba_win_sz; + + /* TODO: Optimize the memory allocation for qos tid based on the + * the actual BA window size in REO tid update path. + */ + if (tid == HAL_DESC_REO_NON_QOS_TID) + hw_desc_sz = ath11k_hal_reo_qdesc_size(ba_win_sz, tid); + else + hw_desc_sz = ath11k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); + + vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_KERNEL); + if (!vaddr) { + spin_unlock_bh(&ab->base_lock); + return -ENOMEM; + } + + addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); + + ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, ssn); + + paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, + DMA_BIDIRECTIONAL); + + ret = dma_mapping_error(ab->dev, paddr); + if (ret) { + spin_unlock_bh(&ab->base_lock); + goto err_mem_free; + } + + rx_tid->vaddr = vaddr; + rx_tid->paddr = paddr; + rx_tid->size = hw_desc_sz; + rx_tid->active = true; + + spin_unlock_bh(&ab->base_lock); + + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, + paddr, tid, 1, ba_win_sz); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rx reorder queue, tid :%d (%d)\n", + tid, ret); + ath11k_dp_rx_tid_mem_free(ab, peer_mac, vdev_id, tid); + } + + return ret; + +err_mem_free: + kfree(vaddr); + + return ret; +} + +int ath11k_dp_rx_ampdu_start(struct ath11k *ar, + struct ieee80211_ampdu_params *params) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_sta *arsta = (void *)params->sta->drv_priv; + int vdev_id = arsta->arvif->vdev_id; + int ret; + + ret = ath11k_peer_rx_tid_setup(ar, params->sta->addr, vdev_id, + params->tid, params->buf_size, + params->ssn); + if (ret) + ath11k_warn(ab, "failed to setup rx tid %d\n", ret); + + return ret; +} + +int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, + struct ieee80211_ampdu_params *params) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + struct ath11k_sta *arsta = (void *)params->sta->drv_priv; + int vdev_id = arsta->arvif->vdev_id; + dma_addr_t paddr; + bool active; + int ret; + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, vdev_id, params->sta->addr); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to stop rx aggregation\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; + } + + paddr = peer->rx_tid[params->tid].paddr; + active = peer->rx_tid[params->tid].active; + + if (!active) { + spin_unlock_bh(&ab->base_lock); + return 0; + } + + ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false); + spin_unlock_bh(&ab->base_lock); + if (ret) { + ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n", + params->tid, ret); + return ret; + } + + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, + params->sta->addr, paddr, + params->tid, 1, 1); + if (ret) + ath11k_warn(ab, "failed to send wmi to delete rx tid %d\n", + ret); + + return ret; +} + +static int ath11k_get_ppdu_user_index(struct htt_ppdu_stats *ppdu_stats, + u16 peer_id) +{ + int i; + + for (i = 0; i < HTT_PPDU_STATS_MAX_USERS - 1; i++) { + if (ppdu_stats->user_stats[i].is_valid_peer_id) { + if (peer_id == ppdu_stats->user_stats[i].peer_id) + return i; + } else { + return i; + } + } + + return -EINVAL; +} + +static int ath11k_htt_tlv_ppdu_stats_parse(struct ath11k_base *ab, + u16 tag, u16 len, const void *ptr, + void *data) +{ + struct htt_ppdu_stats_info *ppdu_info; + struct htt_ppdu_user_stats *user_stats; + int cur_user; + u16 peer_id; + + ppdu_info = (struct htt_ppdu_stats_info *)data; + + switch (tag) { + case HTT_PPDU_STATS_TAG_COMMON: + if (len < sizeof(struct htt_ppdu_stats_common)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + memcpy((void *)&ppdu_info->ppdu_stats.common, ptr, + sizeof(struct htt_ppdu_stats_common)); + break; + case HTT_PPDU_STATS_TAG_USR_RATE: + if (len < sizeof(struct htt_ppdu_stats_user_rate)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + peer_id = ((struct htt_ppdu_stats_user_rate *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, + peer_id); + if (cur_user < 0) + return -EINVAL; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->rate, ptr, + sizeof(struct htt_ppdu_stats_user_rate)); + user_stats->tlv_flags |= BIT(tag); + break; + case HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON: + if (len < sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + peer_id = ((struct htt_ppdu_stats_usr_cmpltn_cmn *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, + peer_id); + if (cur_user < 0) + return -EINVAL; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->cmpltn_cmn, ptr, + sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn)); + user_stats->tlv_flags |= BIT(tag); + break; + case HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS: + if (len < + sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + peer_id = + ((struct htt_ppdu_stats_usr_cmpltn_ack_ba_status *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, + peer_id); + if (cur_user < 0) + return -EINVAL; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->ack_ba, ptr, + sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)); + user_stats->tlv_flags |= BIT(tag); + break; + } + return 0; +} + +int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath11k_base *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data) +{ + const struct htt_tlv *tlv; + const void *begin = ptr; + u16 tlv_tag, tlv_len; + int ret = -EINVAL; + + while (len > 0) { + if (len < sizeof(*tlv)) { + ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", + ptr - begin, len, sizeof(*tlv)); + return -EINVAL; + } + tlv = (struct htt_tlv *)ptr; + tlv_tag = FIELD_GET(HTT_TLV_TAG, tlv->header); + tlv_len = FIELD_GET(HTT_TLV_LEN, tlv->header); + ptr += sizeof(*tlv); + len -= sizeof(*tlv); + + if (tlv_len > len) { + ath11k_err(ab, "htt tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n", + tlv_tag, ptr - begin, len, tlv_len); + return -EINVAL; + } + ret = iter(ab, tlv_tag, tlv_len, ptr, data); + if (ret == -ENOMEM) + return ret; + + ptr += tlv_len; + len -= tlv_len; + } + return 0; +} + +static u32 ath11k_bw_to_mac80211_bwflags(u8 bw) +{ + u32 bwflags = 0; + + switch (bw) { + case ATH11K_BW_40: + bwflags = IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case ATH11K_BW_80: + bwflags = IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + case ATH11K_BW_160: + bwflags = IEEE80211_TX_RC_160_MHZ_WIDTH; + break; + } + + return bwflags; +} + +static void +ath11k_update_per_peer_tx_stats(struct ath11k *ar, + struct htt_ppdu_stats *ppdu_stats, u8 user) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + 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; + u32 succ_bytes = 0; + u16 rate = 0, succ_pkts = 0; + u32 tx_duration = 0; + u8 tid = HTT_PPDU_STATS_NON_QOS_TID; + bool is_ampdu = false; + + if (!usr_stats) + return; + + if (!(usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_RATE))) + return; + + if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) + is_ampdu = + HTT_USR_CMPLTN_IS_AMPDU(usr_stats->cmpltn_cmn.flags); + + if (usr_stats->tlv_flags & + BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS)) { + succ_bytes = usr_stats->ack_ba.success_bytes; + succ_pkts = FIELD_GET(HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M, + usr_stats->ack_ba.info); + tid = FIELD_GET(HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM, + usr_stats->ack_ba.info); + } + + if (common->fes_duration_us) + tx_duration = common->fes_duration_us; + + user_rate = &usr_stats->rate; + flags = HTT_USR_RATE_PREAMBLE(user_rate->rate_flags); + bw = HTT_USR_RATE_BW(user_rate->rate_flags) - 2; + nss = HTT_USR_RATE_NSS(user_rate->rate_flags) + 1; + mcs = HTT_USR_RATE_MCS(user_rate->rate_flags); + sgi = HTT_USR_RATE_GI(user_rate->rate_flags); + + /* 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) { + ath11k_warn(ab, "Invalid VHT mcs %hhd peer stats", mcs); + return; + } + + if (flags == WMI_RATE_PREAMBLE_HT && (mcs > 7 || nss < 1)) { + ath11k_warn(ab, "Invalid HT mcs %hhd nss %hhd peer stats", + mcs, nss); + return; + } + + if (flags == WMI_RATE_PREAMBLE_CCK || flags == WMI_RATE_PREAMBLE_OFDM) { + ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, + flags, + &rate_idx, + &rate); + if (ret < 0) + return; + } + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, usr_stats->peer_id); + + if (!peer || !peer->sta) { + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + return; + } + + sta = peer->sta; + 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) { + 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) { + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + arsta->tx_info.status.rates[0].flags |= + IEEE80211_TX_RC_SHORT_GI; + } + 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. + */ + if (tid < HTT_PPDU_STATS_NON_QOS_TID) { + memset(peer_stats, 0, sizeof(*peer_stats)); + peer_stats->succ_pkts = succ_pkts; + peer_stats->succ_bytes = succ_bytes; + peer_stats->is_ampdu = is_ampdu; + peer_stats->duration = tx_duration; + peer_stats->ba_fails = + HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + + HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) + ath11k_accumulate_per_peer_tx_stats(arsta, + peer_stats, rate_idx); + } + + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +} + +static void ath11k_htt_update_ppdu_stats(struct ath11k *ar, + struct htt_ppdu_stats *ppdu_stats) +{ + u8 user; + + for (user = 0; user < HTT_PPDU_STATS_MAX_USERS - 1; user++) + ath11k_update_per_peer_tx_stats(ar, ppdu_stats, user); +} + +static +struct htt_ppdu_stats_info *ath11k_dp_htt_get_ppdu_desc(struct ath11k *ar, + u32 ppdu_id) +{ + struct htt_ppdu_stats_info *ppdu_info; + + spin_lock_bh(&ar->data_lock); + if (!list_empty(&ar->ppdu_stats_info)) { + list_for_each_entry(ppdu_info, &ar->ppdu_stats_info, list) { + if (ppdu_info->ppdu_id == ppdu_id) { + spin_unlock_bh(&ar->data_lock); + return ppdu_info; + } + } + + if (ar->ppdu_stat_list_depth > HTT_PPDU_DESC_MAX_DEPTH) { + ppdu_info = list_first_entry(&ar->ppdu_stats_info, + typeof(*ppdu_info), list); + list_del(&ppdu_info->list); + ar->ppdu_stat_list_depth--; + ath11k_htt_update_ppdu_stats(ar, &ppdu_info->ppdu_stats); + kfree(ppdu_info); + } + } + spin_unlock_bh(&ar->data_lock); + + ppdu_info = kzalloc(sizeof(*ppdu_info), GFP_KERNEL); + if (!ppdu_info) + return NULL; + + spin_lock_bh(&ar->data_lock); + list_add_tail(&ppdu_info->list, &ar->ppdu_stats_info); + ar->ppdu_stat_list_depth++; + spin_unlock_bh(&ar->data_lock); + + return ppdu_info; +} + +static int ath11k_htt_pull_ppdu_stats(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_htt_ppdu_stats_msg *msg; + struct htt_ppdu_stats_info *ppdu_info; + struct ath11k *ar; + int ret; + u8 pdev_id; + u32 ppdu_id, len; + + msg = (struct ath11k_htt_ppdu_stats_msg *)skb->data; + len = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE, msg->info); + pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, msg->info); + ppdu_id = msg->ppdu_id; + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { + ret = -EINVAL; + goto exit; + } + + if (ath11k_debug_is_pktlog_lite_mode_enabled(ar)) + trace_ath11k_htt_ppdu_stats(ar, skb->data, len); + + ppdu_info = ath11k_dp_htt_get_ppdu_desc(ar, ppdu_id); + if (!ppdu_info) { + ret = -EINVAL; + goto exit; + } + + ppdu_info->ppdu_id = ppdu_id; + ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, + ath11k_htt_tlv_ppdu_stats_parse, + (void *)ppdu_info); + if (ret) { + ath11k_warn(ab, "Failed to parse tlv %d\n", ret); + goto exit; + } + +exit: + rcu_read_unlock(); + + return ret; +} + +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 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) { + ath11k_warn(ab, "invalid pdev id %d on htt pktlog\n", pdev_id); + return; + } + + trace_ath11k_htt_pktlog(ar, data->payload, len); +} + +void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_dp *dp = &ab->dp; + struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data; + enum htt_t2h_msg_type type = FIELD_GET(HTT_T2H_MSG_TYPE, *(u32 *)resp); + u16 peer_id; + u8 vdev_id; + u8 mac_addr[ETH_ALEN]; + u16 peer_mac_h16; + u16 ast_hash; + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "dp_htt rx msg type :0x%0x\n", type); + + switch (type) { + case HTT_T2H_MSG_TYPE_VERSION_CONF: + dp->htt_tgt_ver_major = FIELD_GET(HTT_T2H_VERSION_CONF_MAJOR, + resp->version_msg.version); + dp->htt_tgt_ver_minor = FIELD_GET(HTT_T2H_VERSION_CONF_MINOR, + resp->version_msg.version); + complete(&dp->htt_tgt_version_received); + break; + case HTT_T2H_MSG_TYPE_PEER_MAP: + vdev_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_VDEV_ID, + resp->peer_map_ev.info); + peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_PEER_ID, + resp->peer_map_ev.info); + peer_mac_h16 = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16, + resp->peer_map_ev.info1); + ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, + peer_mac_h16, mac_addr); + ast_hash = FIELD_GET(HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL, + resp->peer_map_ev.info2); + ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash); + break; + case HTT_T2H_MSG_TYPE_PEER_UNMAP: + peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID, + resp->peer_unmap_ev.info); + ath11k_peer_unmap_event(ab, peer_id); + break; + case HTT_T2H_MSG_TYPE_PPDU_STATS_IND: + ath11k_htt_pull_ppdu_stats(ab, skb); + break; + case HTT_T2H_MSG_TYPE_EXT_STATS_CONF: + ath11k_dbg_htt_ext_stats_handler(ab, skb); + break; + case HTT_T2H_MSG_TYPE_PKTLOG: + ath11k_htt_pktlog(ab, skb); + break; + default: + ath11k_warn(ab, "htt event %d not handled\n", type); + break; + } + + dev_kfree_skb_any(skb); +} + +static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar, + struct sk_buff_head *msdu_list, + struct sk_buff *first, struct sk_buff *last, + u8 l3pad_bytes, int msdu_len) +{ + struct sk_buff *skb; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(first); + int buf_first_hdr_len, buf_first_len; + struct hal_rx_desc *ldesc; + int space_extra; + int rem_len; + int buf_len; + + /* As the msdu is spread across multiple rx buffers, + * find the offset to the start of msdu for computing + * the length of the msdu in the first buffer. + */ + buf_first_hdr_len = HAL_RX_DESC_SIZE + l3pad_bytes; + buf_first_len = DP_RX_BUFFER_SIZE - buf_first_hdr_len; + + if (WARN_ON_ONCE(msdu_len <= buf_first_len)) { + skb_put(first, buf_first_hdr_len + msdu_len); + skb_pull(first, buf_first_hdr_len); + return 0; + } + + ldesc = (struct hal_rx_desc *)last->data; + rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(ldesc); + rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(ldesc); + + /* MSDU spans over multiple buffers because the length of the MSDU + * exceeds DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE. So assume the data + * in the first buf is of length DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE. + */ + skb_put(first, DP_RX_BUFFER_SIZE); + skb_pull(first, buf_first_hdr_len); + + /* When an MSDU spread over multiple buffers attention, MSDU_END and + * MPDU_END tlvs are valid only in the last buffer. Copy those tlvs. + */ + ath11k_dp_rx_desc_end_tlv_copy(rxcb->rx_desc, ldesc); + + space_extra = msdu_len - (buf_first_len + skb_tailroom(first)); + if (space_extra > 0 && + (pskb_expand_head(first, 0, space_extra, GFP_ATOMIC) < 0)) { + /* Free up all buffers of the MSDU */ + while ((skb = __skb_dequeue(msdu_list)) != NULL) { + rxcb = ATH11K_SKB_RXCB(skb); + if (!rxcb->is_continuation) { + dev_kfree_skb_any(skb); + break; + } + dev_kfree_skb_any(skb); + } + return -ENOMEM; + } + + rem_len = msdu_len - buf_first_len; + while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) { + rxcb = ATH11K_SKB_RXCB(skb); + if (rxcb->is_continuation) + buf_len = DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE; + else + buf_len = rem_len; + + if (buf_len > (DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE)) { + WARN_ON_ONCE(1); + dev_kfree_skb_any(skb); + return -EINVAL; + } + + skb_put(skb, buf_len + HAL_RX_DESC_SIZE); + skb_pull(skb, HAL_RX_DESC_SIZE); + skb_copy_from_linear_data(skb, skb_put(first, buf_len), + buf_len); + dev_kfree_skb_any(skb); + + rem_len -= buf_len; + if (!rxcb->is_continuation) + break; + } + + return 0; +} + +static struct sk_buff *ath11k_dp_rx_get_msdu_last_buf(struct sk_buff_head *msdu_list, + struct sk_buff *first) +{ + struct sk_buff *skb; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(first); + + if (!rxcb->is_continuation) + return first; + + skb_queue_walk(msdu_list, skb) { + rxcb = ATH11K_SKB_RXCB(skb); + if (!rxcb->is_continuation) + return skb; + } + + 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); + bool ip_csum_fail, l4_csum_fail; + + ip_csum_fail = ath11k_dp_rx_h_attn_ip_cksum_fail(rxcb->rx_desc); + l4_csum_fail = ath11k_dp_rx_h_attn_l4_cksum_fail(rxcb->rx_desc); + + msdu->ip_summed = (ip_csum_fail || l4_csum_fail) ? + CHECKSUM_NONE : CHECKSUM_UNNECESSARY; +} + +static int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, + enum hal_encrypt_type enctype) +{ + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return 0; + case HAL_ENCRYPT_TYPE_CCMP_128: + return IEEE80211_CCMP_MIC_LEN; + case HAL_ENCRYPT_TYPE_CCMP_256: + return IEEE80211_CCMP_256_MIC_LEN; + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return IEEE80211_GCMP_MIC_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: + case HAL_ENCRYPT_TYPE_WAPI: + break; + } + + ath11k_warn(ar->ab, "unsupported encryption type %d for mic len\n", enctype); + return 0; +} + +static int ath11k_dp_rx_crypto_param_len(struct ath11k *ar, + enum hal_encrypt_type enctype) +{ + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + return 0; + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return IEEE80211_TKIP_IV_LEN; + case HAL_ENCRYPT_TYPE_CCMP_128: + return IEEE80211_CCMP_HDR_LEN; + case HAL_ENCRYPT_TYPE_CCMP_256: + return IEEE80211_CCMP_256_HDR_LEN; + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return IEEE80211_GCMP_HDR_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: + case HAL_ENCRYPT_TYPE_WAPI: + break; + } + + ath11k_warn(ar->ab, "unsupported encryption type %d\n", enctype); + return 0; +} + +static int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar, + enum hal_encrypt_type enctype) +{ + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + case HAL_ENCRYPT_TYPE_CCMP_128: + case HAL_ENCRYPT_TYPE_CCMP_256: + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return 0; + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return IEEE80211_TKIP_ICV_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: + case HAL_ENCRYPT_TYPE_WAPI: + break; + } + + ath11k_warn(ar->ab, "unsupported encryption type %d\n", enctype); + return 0; +} + +static void ath11k_dp_rx_h_undecap_nwifi(struct ath11k *ar, + struct sk_buff *msdu, + u8 *first_hdr, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status) +{ + struct ieee80211_hdr *hdr; + size_t hdr_len; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + + /* pull decapped header and copy SA & DA */ + hdr = (struct ieee80211_hdr *)msdu->data; + 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 (!(status->flag & RX_FLAG_IV_STRIPPED)) { + memcpy(skb_push(msdu, + ath11k_dp_rx_crypto_param_len(ar, enctype)), + (void *)hdr + hdr_len, + ath11k_dp_rx_crypto_param_len(ar, enctype)); + } + + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + + /* original 802.11 header has a different DA and in + * case of 4addr it may also have different SA + */ + hdr = (struct ieee80211_hdr *)msdu->data; + ether_addr_copy(ieee80211_get_DA(hdr), da); + ether_addr_copy(ieee80211_get_SA(hdr), sa); +} + +static void ath11k_dp_rx_h_undecap_raw(struct ath11k *ar, struct sk_buff *msdu, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status, + bool decrypted) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ieee80211_hdr *hdr; + size_t hdr_len; + size_t crypto_len; + + if (!rxcb->is_first_msdu || + !(rxcb->is_first_msdu && rxcb->is_last_msdu)) { + WARN_ON_ONCE(1); + return; + } + + skb_trim(msdu, msdu->len - FCS_LEN); + + if (!decrypted) + return; + + hdr = (void *)msdu->data; + + /* Tail */ + if (status->flag & RX_FLAG_IV_STRIPPED) { + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_mic_len(ar, enctype)); + + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_icv_len(ar, enctype)); + } else { + /* MIC */ + if (status->flag & RX_FLAG_MIC_STRIPPED) + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_mic_len(ar, enctype)); + + /* ICV */ + if (status->flag & RX_FLAG_ICV_STRIPPED) + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_icv_len(ar, enctype)); + } + + /* MMIC */ + if ((status->flag & RX_FLAG_MMIC_STRIPPED) && + !ieee80211_has_morefrags(hdr->frame_control) && + enctype == HAL_ENCRYPT_TYPE_TKIP_MIC) + skb_trim(msdu, msdu->len - IEEE80211_CCMP_MIC_LEN); + + /* Head */ + if (status->flag & 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 + crypto_len, + (void *)msdu->data, hdr_len); + skb_pull(msdu, crypto_len); + } +} + +static void *ath11k_dp_rx_h_find_rfc1042(struct ath11k *ar, + struct sk_buff *msdu, + enum hal_encrypt_type enctype) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ieee80211_hdr *hdr; + size_t hdr_len, crypto_len; + void *rfc1042; + bool is_amsdu; + + is_amsdu = !(rxcb->is_first_msdu && rxcb->is_last_msdu); + hdr = (struct ieee80211_hdr *)ath11k_dp_rx_h_80211_hdr(rxcb->rx_desc); + rfc1042 = hdr; + + if (rxcb->is_first_msdu) { + hdr_len = ieee80211_hdrlen(hdr->frame_control); + crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype); + + rfc1042 += hdr_len + crypto_len; + } + + if (is_amsdu) + rfc1042 += sizeof(struct ath11k_dp_amsdu_subframe_hdr); + + return rfc1042; +} + +static void ath11k_dp_rx_h_undecap_eth(struct ath11k *ar, + struct sk_buff *msdu, + u8 *first_hdr, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status) +{ + struct ieee80211_hdr *hdr; + struct ethhdr *eth; + size_t hdr_len; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + void *rfc1042; + + rfc1042 = ath11k_dp_rx_h_find_rfc1042(ar, msdu, enctype); + if (WARN_ON_ONCE(!rfc1042)) + return; + + /* pull decapped header and copy SA & DA */ + eth = (struct ethhdr *)msdu->data; + ether_addr_copy(da, eth->h_dest); + ether_addr_copy(sa, eth->h_source); + skb_pull(msdu, sizeof(struct ethhdr)); + + /* push rfc1042/llc/snap */ + memcpy(skb_push(msdu, sizeof(struct ath11k_dp_rfc1042_hdr)), rfc1042, + sizeof(struct ath11k_dp_rfc1042_hdr)); + + /* push original 802.11 header */ + hdr = (struct ieee80211_hdr *)first_hdr; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { + memcpy(skb_push(msdu, + ath11k_dp_rx_crypto_param_len(ar, enctype)), + (void *)hdr + hdr_len, + ath11k_dp_rx_crypto_param_len(ar, enctype)); + } + + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + + /* original 802.11 header has a different DA and in + * case of 4addr it may also have different SA + */ + hdr = (struct ieee80211_hdr *)msdu->data; + ether_addr_copy(ieee80211_get_DA(hdr), da); + ether_addr_copy(ieee80211_get_SA(hdr), sa); +} + +static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu, + struct hal_rx_desc *rx_desc, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status, + bool decrypted) +{ + u8 *first_hdr; + u8 decap; + + first_hdr = ath11k_dp_rx_h_80211_hdr(rx_desc); + decap = ath11k_dp_rx_h_mpdu_start_decap_type(rx_desc); + + switch (decap) { + case DP_RX_DECAP_TYPE_NATIVE_WIFI: + ath11k_dp_rx_h_undecap_nwifi(ar, msdu, first_hdr, + enctype, status); + break; + case DP_RX_DECAP_TYPE_RAW: + ath11k_dp_rx_h_undecap_raw(ar, msdu, enctype, status, + decrypted); + break; + case DP_RX_DECAP_TYPE_ETHERNET2_DIX: + ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr, + enctype, status); + break; + case DP_RX_DECAP_TYPE_8023: + /* TODO: Handle undecap for these formats */ + break; + } +} + +static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, + struct sk_buff_head *amsdu_list, + struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_hdr *hdr; + enum hal_encrypt_type enctype; + struct sk_buff *last_msdu; + struct sk_buff *msdu; + struct ath11k_skb_rxcb *last_rxcb; + bool is_decrypted; + u32 err_bitmap; + u8 *qos; + + if (skb_queue_empty(amsdu_list)) + return; + + hdr = (struct ieee80211_hdr *)ath11k_dp_rx_h_80211_hdr(rx_desc); + + /* 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; + } + + 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); + + err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(last_rxcb->rx_desc); + + /* 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 | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED); + + 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; + + 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); + } +} + +static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_supported_band *sband; + enum rx_msdu_start_pkt_type pkt_type; + u8 bw; + u8 rate_mcs, nss; + u8 sgi; + bool is_cck; + + pkt_type = ath11k_dp_rx_h_msdu_start_pkt_type(rx_desc); + bw = ath11k_dp_rx_h_msdu_start_rx_bw(rx_desc); + rate_mcs = ath11k_dp_rx_h_msdu_start_rate_mcs(rx_desc); + nss = ath11k_dp_rx_h_msdu_start_nss(rx_desc); + sgi = ath11k_dp_rx_h_msdu_start_sgi(rx_desc); + + switch (pkt_type) { + case RX_MSDU_START_PKT_TYPE_11A: + case RX_MSDU_START_PKT_TYPE_11B: + is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); + sband = &ar->mac.sbands[rx_status->band]; + rx_status->rate_idx = ath11k_mac_hw_rate_to_idx(sband, rate_mcs, + is_cck); + break; + case RX_MSDU_START_PKT_TYPE_11N: + rx_status->encoding = RX_ENC_HT; + if (rate_mcs > ATH11K_HT_MCS_MAX) { + ath11k_warn(ar->ab, + "Received with invalid mcs in HT mode %d\n", + rate_mcs); + break; + } + rx_status->rate_idx = rate_mcs + (8 * (nss - 1)); + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); + break; + case RX_MSDU_START_PKT_TYPE_11AC: + rx_status->encoding = RX_ENC_VHT; + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH11K_VHT_MCS_MAX) { + ath11k_warn(ar->ab, + "Received with invalid mcs in VHT mode %d\n", + rate_mcs); + break; + } + rx_status->nss = nss; + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); + break; + case RX_MSDU_START_PKT_TYPE_11AX: + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH11K_HE_MCS_MAX) { + ath11k_warn(ar->ab, + "Received with invalid mcs in HE mode %d\n", + rate_mcs); + break; + } + rx_status->encoding = RX_ENC_HE; + rx_status->nss = nss; + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); + break; + } +} + +static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + u8 channel_num; + + rx_status->freq = 0; + rx_status->rate_idx = 0; + rx_status->nss = 0; + rx_status->encoding = RX_ENC_LEGACY; + rx_status->bw = RATE_INFO_BW_20; + + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + channel_num = ath11k_dp_rx_h_msdu_start_freq(rx_desc); + + if (channel_num >= 1 && channel_num <= 14) { + rx_status->band = NL80211_BAND_2GHZ; + } 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; + } + + rx_status->freq = ieee80211_channel_to_frequency(channel_num, + rx_status->band); + + 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) +{ + u8 *qc; + int tid; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return ""; + + qc = ieee80211_get_qos_ctl(hdr); + tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + snprintf(out, size, "tid %d", tid); + + return out; +} + +static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *napi, + struct sk_buff *msdu) +{ + static const struct ieee80211_radiotap_he known = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), + }; + struct ieee80211_rx_status *status; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + struct ieee80211_radiotap_he *he = NULL; + char tid[32]; + + status = IEEE80211_SKB_RXCB(msdu); + if (status->encoding == RX_ENC_HE) { + he = skb_push(msdu, sizeof(known)); + memcpy(he, &known, sizeof(known)); + status->flag |= RX_FLAG_RADIOTAP_HE; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + msdu, + msdu->len, + ieee80211_get_SA(hdr), + ath11k_print_get_tid(hdr, tid, sizeof(tid)), + is_multicast_ether_addr(ieee80211_get_DA(hdr)) ? + "mcast" : "ucast", + (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4, + (status->encoding == RX_ENC_LEGACY) ? "legacy" : "", + (status->encoding == RX_ENC_HT) ? "ht" : "", + (status->encoding == RX_ENC_VHT) ? "vht" : "", + (status->encoding == RX_ENC_HE) ? "he" : "", + (status->bw == RATE_INFO_BW_40) ? "40" : "", + (status->bw == RATE_INFO_BW_80) ? "80" : "", + (status->bw == RATE_INFO_BW_160) ? "160" : "", + status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "", + status->rate_idx, + status->nss, + status->freq, + status->band, status->flag, + !!(status->flag & RX_FLAG_FAILED_FCS_CRC), + !!(status->flag & RX_FLAG_MMIC_ERROR), + !!(status->flag & RX_FLAG_AMSDU_MORE)); + + /* TODO: trace rx packet */ + + 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) +{ + struct sk_buff *msdu; + struct sk_buff *first_subframe; + struct ieee80211_rx_status *status; + + first_subframe = skb_peek(amsdu_list); + + 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; + + if (msdu == first_subframe) { + first_subframe = NULL; + rxs->flag &= ~RX_FLAG_ALLOW_SAME_PN; + } else { + rxs->flag |= RX_FLAG_ALLOW_SAME_PN; + } + rxs->flag |= RX_FLAG_SKIP_MONITOR; + + status = IEEE80211_SKB_RXCB(msdu); + *status = *rxs; + } +} + +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) +{ + struct ath11k *ar; + struct sk_buff *msdu; + struct ath11k_pdev *pdev; + + if (skb_queue_empty(pending_q)) + 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) { + dev_kfree_skb_any(msdu); + continue; + } + + 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) +{ + 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 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 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; + + __skb_queue_head_init(&msdu_list); + + srng = &ab->hal.srng_list[dp->reo_dst_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + +try_again: + while ((rx_desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { + struct hal_reo_dest_ring *desc = (struct hal_reo_dest_ring *)rx_desc; + enum hal_reo_dest_ring_push_reason push_reason; + u32 cookie; + + cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + desc->buf_addr_info.info1); + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + cookie); + spin_lock_bh(&rx_ring->idr_lock); + msdu = idr_find(&rx_ring->bufs_idr, buf_id); + if (!msdu) { + ath11k_warn(ab, "frame rx with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(msdu); + dma_unmap_single(ab->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + num_buffs_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]++; + continue; + } + + rxcb->is_first_msdu = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); + rxcb->is_last_msdu = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); + rxcb->is_continuation = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); + rxcb->mac_id = mac_id; + __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) { + done = true; + break; + } + } + + /* Hw might have updated the head pointer after we cached it. + * In this case, even though there are entries in the ring we'll + * get rx_desc NULL. Give the read another try with updated cached + * head pointer so that we can reap complete MPDU in the current + * rx processing. + */ + if (!done && ath11k_hal_srng_dst_num_free(ab, srng, true)) { + ath11k_hal_srng_access_end(ab, srng); + goto try_again; + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + if (!num_buffs_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. + */ + continue; + } + + ath11k_dp_rx_process_amsdu(ar, &amsdu_list, rx_status); + + ath11k_dp_rx_pre_deliver_amsdu(ar, &amsdu_list, rx_status); + skb_queue_splice_tail(&amsdu_list, pending_q); + } + + while (quota && (msdu = __skb_dequeue(pending_q))) { + ath11k_dp_rx_deliver_msdu(ar, napi, msdu); + quota--; + } + +rcu_unlock: + rcu_read_unlock(); +exit: + return budget - quota; +} + +static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; + u32 num_msdu; + + if (!rx_stats) + return; + + num_msdu = ppdu_info->tcp_msdu_count + ppdu_info->tcp_ack_msdu_count + + ppdu_info->udp_msdu_count + ppdu_info->other_msdu_count; + + rx_stats->num_msdu += num_msdu; + rx_stats->tcp_msdu_count += ppdu_info->tcp_msdu_count + + ppdu_info->tcp_ack_msdu_count; + rx_stats->udp_msdu_count += ppdu_info->udp_msdu_count; + rx_stats->other_msdu_count += ppdu_info->other_msdu_count; + + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11A || + ppdu_info->preamble_type == HAL_RX_PREAMBLE_11B) { + ppdu_info->nss = 1; + ppdu_info->mcs = HAL_RX_MAX_MCS; + ppdu_info->tid = IEEE80211_NUM_TIDS; + } + + if (ppdu_info->nss > 0 && ppdu_info->nss <= HAL_RX_MAX_NSS) + rx_stats->nss_count[ppdu_info->nss - 1] += num_msdu; + + if (ppdu_info->mcs <= HAL_RX_MAX_MCS) + rx_stats->mcs_count[ppdu_info->mcs] += num_msdu; + + if (ppdu_info->gi < HAL_RX_GI_MAX) + rx_stats->gi_count[ppdu_info->gi] += num_msdu; + + if (ppdu_info->bw < HAL_RX_BW_MAX) + rx_stats->bw_count[ppdu_info->bw] += num_msdu; + + if (ppdu_info->ldpc < HAL_RX_SU_MU_CODING_MAX) + rx_stats->coding_count[ppdu_info->ldpc] += num_msdu; + + if (ppdu_info->tid <= IEEE80211_NUM_TIDS) + rx_stats->tid_count[ppdu_info->tid] += num_msdu; + + if (ppdu_info->preamble_type < HAL_RX_PREAMBLE_MAX) + rx_stats->pream_cnt[ppdu_info->preamble_type] += num_msdu; + + if (ppdu_info->reception_type < HAL_RX_RECEPTION_TYPE_MAX) + rx_stats->reception_type[ppdu_info->reception_type] += num_msdu; + + if (ppdu_info->is_stbc) + rx_stats->stbc_count += num_msdu; + + if (ppdu_info->beamformed) + rx_stats->beamformed_count += num_msdu; + + if (ppdu_info->num_mpdu_fcs_ok > 1) + rx_stats->ampdu_msdu_count += num_msdu; + else + rx_stats->non_ampdu_msdu_count += num_msdu; + + rx_stats->num_mpdu_fcs_ok += ppdu_info->num_mpdu_fcs_ok; + rx_stats->num_mpdu_fcs_err += ppdu_info->num_mpdu_fcs_err; + + arsta->rssi_comb = ppdu_info->rssi_comb; + rx_stats->rx_duration += ppdu_info->rx_duration; + arsta->rx_duration = rx_stats->rx_duration; +} + +static struct sk_buff *ath11k_dp_rx_alloc_mon_status_buf(struct ath11k_base *ab, + struct dp_rxdma_ring *rx_ring, + int *buf_id, gfp_t gfp) +{ + struct sk_buff *skb; + dma_addr_t paddr; + + skb = dev_alloc_skb(DP_RX_BUFFER_SIZE + + DP_RX_BUFFER_ALIGN_SIZE); + + if (!skb) + goto fail_alloc_skb; + + if (!IS_ALIGNED((unsigned long)skb->data, + DP_RX_BUFFER_ALIGN_SIZE)) { + skb_pull(skb, PTR_ALIGN(skb->data, DP_RX_BUFFER_ALIGN_SIZE) - + skb->data); + } + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(ab->dev, paddr))) + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); + *buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, + rx_ring->bufs_max, gfp); + spin_unlock_bh(&rx_ring->idr_lock); + if (*buf_id < 0) + goto fail_dma_unmap; + + ATH11K_SKB_RXCB(skb)->paddr = paddr; + return skb; + +fail_dma_unmap: + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_BIDIRECTIONAL); +fail_free_skb: + dev_kfree_skb_any(skb); +fail_alloc_skb: + return NULL; +} + +int ath11k_dp_rx_mon_status_bufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, + enum hal_rx_buf_return_buf_manager mgr, + gfp_t gfp) +{ + struct hal_srng *srng; + u32 *desc; + struct sk_buff *skb; + int num_free; + int num_remain; + int buf_id; + u32 cookie; + dma_addr_t paddr; + + req_entries = min(req_entries, rx_ring->bufs_max); + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + num_free = ath11k_hal_srng_src_num_free(ab, srng, true); + + req_entries = min(num_free, req_entries); + num_remain = req_entries; + + while (num_remain > 0) { + skb = ath11k_dp_rx_alloc_mon_status_buf(ab, rx_ring, + &buf_id, gfp); + if (!skb) + break; + paddr = ATH11K_SKB_RXCB(skb)->paddr; + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) + goto fail_desc_get; + + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + + num_remain--; + + ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr); + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; + +fail_desc_get: + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_BIDIRECTIONAL); + dev_kfree_skb_any(skb); + ath11k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; +} + +static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id, + int *budget, struct sk_buff_head *skb_list) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_mon_status_refill_ring; + struct hal_srng *srng; + void *rx_mon_status_desc; + struct sk_buff *skb; + struct ath11k_skb_rxcb *rxcb; + struct hal_tlv_hdr *tlv; + u32 cookie; + int buf_id; + dma_addr_t paddr; + u8 rbm; + int num_buffs_reaped = 0; + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + while (*budget) { + *budget -= 1; + rx_mon_status_desc = + ath11k_hal_srng_src_peek(ab, srng); + if (!rx_mon_status_desc) + break; + + ath11k_hal_rx_buf_addr_info_get(rx_mon_status_desc, &paddr, + &cookie, &rbm); + if (paddr) { + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie); + + spin_lock_bh(&rx_ring->idr_lock); + skb = idr_find(&rx_ring->bufs_idr, buf_id); + if (!skb) { + ath11k_warn(ab, "rx monitor status with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(skb); + + dma_sync_single_for_cpu(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + dma_unmap_single(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_BIDIRECTIONAL); + + tlv = (struct hal_tlv_hdr *)skb->data; + if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != + HAL_RX_STATUS_BUFFER_DONE) { + ath11k_hal_srng_src_get_next_entry(ab, srng); + continue; + } + + __skb_queue_tail(skb_list, skb); + } + + skb = ath11k_dp_rx_alloc_mon_status_buf(ab, rx_ring, + &buf_id, GFP_ATOMIC); + + if (!skb) { + ath11k_hal_rx_buf_addr_info_set(rx_mon_status_desc, 0, 0, + HAL_RX_BUF_RBM_SW3_BM); + num_buffs_reaped++; + break; + } + rxcb = ATH11K_SKB_RXCB(skb); + + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + + ath11k_hal_rx_buf_addr_info_set(rx_mon_status_desc, rxcb->paddr, + cookie, HAL_RX_BUF_RBM_SW3_BM); + ath11k_hal_srng_src_get_next_entry(ab, srng); + num_buffs_reaped++; + } + ath11k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + return num_buffs_reaped; +} + +int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + enum hal_rx_mon_status hal_status; + struct sk_buff *skb; + struct sk_buff_head skb_list; + struct hal_rx_mon_ppdu_info ppdu_info; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + int num_buffs_reaped = 0; + + __skb_queue_head_init(&skb_list); + + num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ab, mac_id, &budget, + &skb_list); + if (!num_buffs_reaped) + goto exit; + + while ((skb = __skb_dequeue(&skb_list))) { + memset(&ppdu_info, 0, sizeof(ppdu_info)); + ppdu_info.peer_id = HAL_INVALID_PEERID; + + if (ath11k_debug_is_pktlog_rx_stats_enabled(ar)) + trace_ath11k_htt_rxdesc(ar, skb->data, DP_RX_BUFFER_SIZE); + + hal_status = ath11k_hal_rx_parse_mon_status(ab, &ppdu_info, skb); + + if (ppdu_info.peer_id == HAL_INVALID_PEERID || + hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { + dev_kfree_skb_any(skb); + continue; + } + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ppdu_info.peer_id); + + if (!peer || !peer->sta) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "failed to find the peer with peer_id %d\n", + ppdu_info.peer_id); + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + dev_kfree_skb_any(skb); + continue; + } + + arsta = (struct ath11k_sta *)peer->sta->drv_priv; + ath11k_dp_rx_update_peer_stats(arsta, &ppdu_info); + + if (ath11k_debug_is_pktlog_peer_valid(ar, peer->addr)) + trace_ath11k_htt_rxdesc(ar, skb->data, DP_RX_BUFFER_SIZE); + + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + + dev_kfree_skb_any(skb); + } +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) +{ + 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_frag_h_mpdu(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + u8 rx_channel; + enum hal_encrypt_type enctype; + bool is_decrypted; + u32 err_bitmap; + + 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); + + 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; + + rx_status->encoding = RX_ENC_LEGACY; + rx_status->bw = RATE_INFO_BW_20; + + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + rx_channel = ath11k_dp_rx_h_msdu_start_freq(rx_desc); + + 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); + return; + } + + rx_status->freq = ieee80211_channel_to_frequency(rx_channel, + rx_status->band); + ath11k_dp_rx_h_rate(ar, rx_desc, rx_status); + + /* Rx fragments are received in raw mode */ + skb_trim(msdu, msdu->len - FCS_LEN); + + 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)); + } +} + +static int +ath11k_dp_process_rx_err_buf(struct ath11k *ar, struct napi_struct *napi, + int buf_id, bool frag) +{ + 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; + + spin_lock_bh(&rx_ring->idr_lock); + msdu = idr_find(&rx_ring->bufs_idr, buf_id); + if (!msdu) { + ath11k_warn(ar->ab, "rx err buf with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + return -EINVAL; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(msdu); + dma_unmap_single(ar->ab->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + if (!frag) { + /* Process only rx fragments below, and drop + * msdu's indicated due to error reasons. + */ + dev_kfree_skb_any(msdu); + return 0; + } + + rcu_read_lock(); + if (!rcu_dereference(ar->ab->pdevs_active[ar->pdev_idx])) { + dev_kfree_skb_any(msdu); + goto exit; + } + + if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { + dev_kfree_skb_any(msdu); + goto exit; + } + + 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); + +exit: + rcu_read_unlock(); + return 0; +} + +int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, + int budget) +{ + u32 msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; + struct dp_link_desc_bank *link_desc_banks; + enum hal_rx_buf_return_buf_manager rbm; + int tot_n_bufs_reaped, quota, ret, i; + int n_bufs_reaped[MAX_RADIOS] = {0}; + struct dp_rxdma_ring *rx_ring; + struct dp_srng *reo_except; + u32 desc_bank, num_msdus; + struct hal_srng *srng; + struct ath11k_dp *dp; + void *link_desc_va; + int buf_id, mac_id; + struct ath11k *ar; + dma_addr_t paddr; + u32 *desc; + bool is_frag; + + tot_n_bufs_reaped = 0; + quota = budget; + + dp = &ab->dp; + reo_except = &dp->reo_except_ring; + link_desc_banks = dp->link_desc_banks; + + srng = &ab->hal.srng_list[reo_except->ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + while (budget && + (desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { + struct hal_reo_dest_ring *reo_desc = (struct hal_reo_dest_ring *)desc; + + ab->soc_stats.err_ring_pkts++; + ret = ath11k_hal_desc_reo_parse_err(ab, desc, &paddr, + &desc_bank); + if (ret) { + ath11k_warn(ab, "failed to parse error reo desc %d\n", + ret); + continue; + } + link_desc_va = link_desc_banks[desc_bank].vaddr + + (paddr - link_desc_banks[desc_bank].paddr); + ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, msdu_cookies, + &rbm); + if (rbm != HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST && + rbm != HAL_RX_BUF_RBM_SW3_BM) { + ab->soc_stats.invalid_rbm++; + ath11k_warn(ab, "invalid return buffer manager %d\n", rbm); + ath11k_dp_rx_link_desc_return(ab, desc, + HAL_WBM_REL_BM_ACT_REL_MSDU); + continue; + } + + 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); + + for (i = 0; i < num_msdus; i++) { + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + msdu_cookies[i]); + + mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, + msdu_cookies[i]); + + ar = ab->pdevs[mac_id].ar; + + if (!ath11k_dp_process_rx_err_buf(ar, napi, buf_id, + is_frag)) { + n_bufs_reaped[mac_id]++; + tot_n_bufs_reaped++; + } + } + + if (tot_n_bufs_reaped >= quota) { + tot_n_bufs_reaped = quota; + goto exit; + } + + budget = quota - tot_n_bufs_reaped; + } + +exit: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + for (i = 0; i < ab->num_radios; i++) { + if (!n_bufs_reaped[i]) + continue; + + ar = ab->pdevs[i].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, n_bufs_reaped[i], + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); + } + + return tot_n_bufs_reaped; +} + +static void ath11k_dp_rx_null_q_desc_sg_drop(struct ath11k *ar, + int msdu_len, + struct sk_buff_head *msdu_list) +{ + struct sk_buff *skb, *tmp; + struct ath11k_skb_rxcb *rxcb; + int n_buffs; + + n_buffs = DIV_ROUND_UP(msdu_len, + (DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE)); + + skb_queue_walk_safe(msdu_list, skb, tmp) { + rxcb = ATH11K_SKB_RXCB(skb); + if (rxcb->err_rel_src == HAL_WBM_REL_SRC_MODULE_REO && + rxcb->err_code == HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO) { + if (!n_buffs) + break; + __skb_unlink(skb, msdu_list); + dev_kfree_skb_any(skb); + n_buffs--; + } + } +} + +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; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + + msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(desc); + + if ((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); + return -EINVAL; + } + + if (!ath11k_dp_rx_h_attn_msdu_done(desc)) { + ath11k_warn(ar->ab, + "msdu_done bit not set in null_q_des processing\n"); + __skb_queue_purge(msdu_list); + return -EIO; + } + + /* Handle NULL queue descriptor violations arising out a missing + * REO queue for a given peer or a given TID. This typically + * may happen if a packet is received on a QOS enabled TID before the + * ADDBA negotiation for that TID, when the TID queue is setup. Or + * it may also happen for MC/BC frames if they are not routed to the + * non-QOS TID queue, in the absence of any other default TID queue. + * 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; + + 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, &amsdu_list, desc, status); + + /* Please note that caller will having the access to msdu and completing + * rx with mac80211. Need not worry about cleaning up amsdu_list. + */ + + return 0; +} + +static bool ath11k_dp_rx_h_reo_err(struct ath11k *ar, struct sk_buff *msdu, + struct ieee80211_rx_status *status, + struct sk_buff_head *msdu_list) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + bool drop = false; + + ar->ab->soc_stats.reo_error[rxcb->err_code]++; + + switch (rxcb->err_code) { + case HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO: + if (ath11k_dp_rx_h_null_q_desc(ar, msdu, status, msdu_list)) + drop = true; + break; + default: + /* TODO: Review other errors and process them to mac80211 + * as appropriate. + */ + drop = true; + break; + } + + return drop; +} + +static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, + struct ieee80211_rx_status *status) +{ + u16 msdu_len; + struct hal_rx_desc *desc = (struct hal_rx_desc *)msdu->data; + u8 l3pad_bytes; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + + 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); + msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(desc); + 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); + + status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | + RX_FLAG_DECRYPTED); + + ath11k_dp_rx_h_undecap(ar, msdu, desc, + HAL_ENCRYPT_TYPE_TKIP_MIC, status, false); +} + +static bool ath11k_dp_rx_h_rxdma_err(struct ath11k *ar, struct sk_buff *msdu, + struct ieee80211_rx_status *status) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + bool drop = false; + + ar->ab->soc_stats.rxdma_error[rxcb->err_code]++; + + switch (rxcb->err_code) { + case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: + ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status); + break; + default: + /* TODO: Review other rxdma error code to check if anything is + * worth reporting to mac80211 + */ + drop = true; + break; + } + + return drop; +} + +static void ath11k_dp_rx_wbm_err(struct ath11k *ar, + struct napi_struct *napi, + struct sk_buff *msdu, + struct sk_buff_head *msdu_list) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ieee80211_rx_status rxs = {0}; + struct ieee80211_rx_status *status; + bool drop = true; + + switch (rxcb->err_rel_src) { + case HAL_WBM_REL_SRC_MODULE_REO: + drop = ath11k_dp_rx_h_reo_err(ar, msdu, &rxs, msdu_list); + break; + case HAL_WBM_REL_SRC_MODULE_RXDMA: + drop = ath11k_dp_rx_h_rxdma_err(ar, msdu, &rxs); + break; + default: + /* msdu will get freed */ + break; + } + + if (drop) { + dev_kfree_skb_any(msdu); + return; + } + + status = IEEE80211_SKB_RXCB(msdu); + *status = rxs; + + ath11k_dp_rx_deliver_msdu(ar, napi, msdu); +} + +int ath11k_dp_rx_process_wbm_err(struct ath11k_base *ab, + struct napi_struct *napi, int budget) +{ + struct ath11k *ar; + struct ath11k_dp *dp = &ab->dp; + struct dp_rxdma_ring *rx_ring; + struct hal_rx_wbm_rel_info err_info; + struct hal_srng *srng; + struct sk_buff *msdu; + struct sk_buff_head msdu_list[MAX_RADIOS]; + struct ath11k_skb_rxcb *rxcb; + u32 *rx_desc; + int buf_id, mac_id; + int num_buffs_reaped[MAX_RADIOS] = {0}; + int total_num_buffs_reaped = 0; + int ret, i; + + for (i = 0; i < MAX_RADIOS; i++) + __skb_queue_head_init(&msdu_list[i]); + + srng = &ab->hal.srng_list[dp->rx_rel_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + while (budget) { + rx_desc = ath11k_hal_srng_dst_get_next_entry(ab, srng); + if (!rx_desc) + break; + + ret = ath11k_hal_wbm_desc_parse_err(ab, rx_desc, &err_info); + if (ret) { + ath11k_warn(ab, + "failed to parse rx error in wbm_rel ring desc %d\n", + ret); + continue; + } + + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, err_info.cookie); + mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, err_info.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) { + ath11k_warn(ab, "frame rx with invalid buf_id %d pdev %d\n", + buf_id, mac_id); + spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(msdu); + dma_unmap_single(ab->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + num_buffs_reaped[mac_id]++; + total_num_buffs_reaped++; + budget--; + + if (err_info.push_reason != + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) { + dev_kfree_skb_any(msdu); + continue; + } + + rxcb->err_rel_src = err_info.err_rel_src; + rxcb->err_code = err_info.err_code; + rxcb->rx_desc = (struct hal_rx_desc *)msdu->data; + __skb_queue_tail(&msdu_list[mac_id], msdu); + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + if (!total_num_buffs_reaped) + goto done; + + for (i = 0; i < ab->num_radios; i++) { + if (!num_buffs_reaped[i]) + continue; + + ar = ab->pdevs[i].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, num_buffs_reaped[i], + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); + } + + rcu_read_lock(); + for (i = 0; i < ab->num_radios; i++) { + if (!rcu_dereference(ab->pdevs_active[i])) { + __skb_queue_purge(&msdu_list[i]); + continue; + } + + ar = ab->pdevs[i].ar; + + if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { + __skb_queue_purge(&msdu_list[i]); + continue; + } + + while ((msdu = __skb_dequeue(&msdu_list[i])) != NULL) + ath11k_dp_rx_wbm_err(ar, napi, msdu, &msdu_list[i]); + } + rcu_read_unlock(); +done: + return total_num_buffs_reaped; +} + +int ath11k_dp_process_rxdma_err(struct ath11k_base *ab, int mac_id, int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct dp_srng *err_ring = &ar->dp.rxdma_err_dst_ring; + struct dp_rxdma_ring *rx_ring = &ar->dp.rx_refill_buf_ring; + struct dp_link_desc_bank *link_desc_banks = ab->dp.link_desc_banks; + struct hal_srng *srng; + u32 msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; + enum hal_rx_buf_return_buf_manager rbm; + enum hal_reo_entr_rxdma_ecode rxdma_err_code; + struct ath11k_skb_rxcb *rxcb; + struct sk_buff *skb; + struct hal_reo_entrance_ring *entr_ring; + void *desc; + int num_buf_freed = 0; + int quota = budget; + dma_addr_t paddr; + u32 desc_bank; + void *link_desc_va; + int num_msdus; + int i; + int buf_id; + + srng = &ab->hal.srng_list[err_ring->ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + while (quota-- && + (desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { + ath11k_hal_rx_reo_ent_paddr_get(ab, desc, &paddr, &desc_bank); + + entr_ring = (struct hal_reo_entrance_ring *)desc; + rxdma_err_code = + FIELD_GET(HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE, + entr_ring->info1); + ab->soc_stats.rxdma_error[rxdma_err_code]++; + + link_desc_va = link_desc_banks[desc_bank].vaddr + + (paddr - link_desc_banks[desc_bank].paddr); + ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, + msdu_cookies, &rbm); + + for (i = 0; i < num_msdus; i++) { + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + msdu_cookies[i]); + + spin_lock_bh(&rx_ring->idr_lock); + skb = idr_find(&rx_ring->bufs_idr, buf_id); + if (!skb) { + ath11k_warn(ab, "rxdma error with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(skb); + dma_unmap_single(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + + num_buf_freed++; + } + + ath11k_dp_rx_link_desc_return(ab, desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + if (num_buf_freed) + ath11k_dp_rxbufs_replenish(ab, mac_id, rx_ring, num_buf_freed, + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); + + return budget - quota; +} + +void ath11k_dp_process_reo_status(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng; + struct dp_reo_cmd *cmd, *tmp; + bool found = false; + u32 *reo_desc; + u16 tag; + struct hal_reo_status reo_status; + + srng = &ab->hal.srng_list[dp->reo_status_ring.ring_id]; + + memset(&reo_status, 0, sizeof(reo_status)); + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + while ((reo_desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { + tag = FIELD_GET(HAL_SRNG_TLV_HDR_TAG, *reo_desc); + + switch (tag) { + case HAL_REO_GET_QUEUE_STATS_STATUS: + ath11k_hal_reo_status_queue_stats(ab, reo_desc, + &reo_status); + break; + case HAL_REO_FLUSH_QUEUE_STATUS: + ath11k_hal_reo_flush_queue_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_FLUSH_CACHE_STATUS: + ath11k_hal_reo_flush_cache_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_UNBLOCK_CACHE_STATUS: + ath11k_hal_reo_unblk_cache_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_FLUSH_TIMEOUT_LIST_STATUS: + ath11k_hal_reo_flush_timeout_list_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_DESCRIPTOR_THRESHOLD_REACHED_STATUS: + ath11k_hal_reo_desc_thresh_reached_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_UPDATE_RX_REO_QUEUE_STATUS: + ath11k_hal_reo_update_rx_reo_queue_status(ab, reo_desc, + &reo_status); + break; + default: + ath11k_warn(ab, "Unknown reo status type %d\n", tag); + continue; + } + + spin_lock_bh(&dp->reo_cmd_lock); + list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { + if (reo_status.uniform_hdr.cmd_num == cmd->cmd_num) { + found = true; + list_del(&cmd->list); + break; + } + } + spin_unlock_bh(&dp->reo_cmd_lock); + + if (found) { + cmd->handler(dp, (void *)&cmd->data, + reo_status.uniform_hdr.cmd_status); + kfree(cmd); + } + + found = false; + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); +} + +void ath11k_dp_rx_pdev_free(struct ath11k_base *ab, int mac_id) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + + ath11k_dp_rx_pdev_srng_free(ar); + ath11k_dp_rxdma_pdev_buf_free(ar); +} + +int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k_pdev_dp *dp = &ar->dp; + u32 ring_id; + int ret; + + ret = ath11k_dp_rx_pdev_srng_alloc(ar); + if (ret) { + ath11k_warn(ab, "failed to setup rx srngs\n"); + return ret; + } + + ret = ath11k_dp_rxdma_pdev_buf_setup(ar); + if (ret) { + ath11k_warn(ab, "failed to setup rxdma ring\n"); + return ret; + } + + ring_id = dp->rx_refill_buf_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id, HAL_RXDMA_BUF); + if (ret) { + ath11k_warn(ab, "failed to configure rx_refill_buf_ring %d\n", + ret); + return ret; + } + + ring_id = dp->rxdma_err_dst_ring.ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id, HAL_RXDMA_DST); + if (ret) { + ath11k_warn(ab, "failed to configure rxdma_err_dest_ring %d\n", + ret); + return ret; + } + + ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, + mac_id, HAL_RXDMA_MONITOR_BUF); + if (ret) { + ath11k_warn(ab, "failed to configure rxdma_mon_buf_ring %d\n", + ret); + return ret; + } + ret = ath11k_dp_tx_htt_srng_setup(ab, + dp->rxdma_mon_dst_ring.ring_id, + mac_id, HAL_RXDMA_MONITOR_DST); + if (ret) { + ath11k_warn(ab, "failed to configure rxdma_mon_dst_ring %d\n", + ret); + return ret; + } + ret = ath11k_dp_tx_htt_srng_setup(ab, + dp->rxdma_mon_desc_ring.ring_id, + mac_id, HAL_RXDMA_MONITOR_DESC); + if (ret) { + ath11k_warn(ab, "failed to configure rxdma_mon_dst_ring %d\n", + ret); + return ret; + } + ring_id = dp->rx_mon_status_refill_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id, + HAL_RXDMA_MONITOR_STATUS); + if (ret) { + ath11k_warn(ab, + "failed to configure mon_status_refill_ring %d\n", + ret); + return ret; + } + return 0; +} + +static void ath11k_dp_mon_set_frag_len(u32 *total_len, u32 *frag_len) +{ + if (*total_len >= (DP_RX_BUFFER_SIZE - sizeof(struct hal_rx_desc))) { + *frag_len = DP_RX_BUFFER_SIZE - sizeof(struct hal_rx_desc); + *total_len -= *frag_len; + } else { + *frag_len = *total_len; + *total_len = 0; + } +} + +static +int ath11k_dp_rx_monitor_link_desc_return(struct ath11k *ar, + void *p_last_buf_addr_info, + u8 mac_id) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_srng *dp_srng; + void *hal_srng; + void *src_srng_desc; + int ret = 0; + + dp_srng = &dp->rxdma_mon_desc_ring; + hal_srng = &ar->ab->hal.srng_list[dp_srng->ring_id]; + + ath11k_hal_srng_access_begin(ar->ab, hal_srng); + + src_srng_desc = ath11k_hal_srng_src_get_next_entry(ar->ab, hal_srng); + + if (src_srng_desc) { + struct ath11k_buffer_addr *src_desc = + (struct ath11k_buffer_addr *)src_srng_desc; + + *src_desc = *((struct ath11k_buffer_addr *)p_last_buf_addr_info); + } else { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "Monitor Link Desc Ring %d Full", mac_id); + ret = -ENOMEM; + } + + ath11k_hal_srng_access_end(ar->ab, hal_srng); + return ret; +} + +static +void ath11k_dp_rx_mon_next_link_desc_get(void *rx_msdu_link_desc, + dma_addr_t *paddr, u32 *sw_cookie, + void **pp_buf_addr_info) +{ + struct hal_rx_msdu_link *msdu_link = + (struct hal_rx_msdu_link *)rx_msdu_link_desc; + struct ath11k_buffer_addr *buf_addr_info; + u8 rbm = 0; + + buf_addr_info = (struct ath11k_buffer_addr *)&msdu_link->buf_addr_info; + + ath11k_hal_rx_buf_addr_info_get(buf_addr_info, paddr, sw_cookie, &rbm); + + *pp_buf_addr_info = (void *)buf_addr_info; +} + +static int ath11k_dp_pkt_set_pktlen(struct sk_buff *skb, u32 len) +{ + if (skb->len > len) { + skb_trim(skb, len); + } else { + if (skb_tailroom(skb) < len - skb->len) { + if ((pskb_expand_head(skb, 0, + len - skb->len - skb_tailroom(skb), + GFP_ATOMIC))) { + dev_kfree_skb_any(skb); + return -ENOMEM; + } + } + skb_put(skb, (len - skb->len)); + } + return 0; +} + +static void ath11k_hal_rx_msdu_list_get(struct ath11k *ar, + void *msdu_link_desc, + struct hal_rx_msdu_list *msdu_list, + u16 *num_msdus) +{ + struct hal_rx_msdu_details *msdu_details = NULL; + struct rx_msdu_desc *msdu_desc_info = NULL; + struct hal_rx_msdu_link *msdu_link = NULL; + int i; + u32 last = FIELD_PREP(RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU, 1); + u32 first = FIELD_PREP(RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU, 1); + u8 tmp = 0; + + msdu_link = (struct hal_rx_msdu_link *)msdu_link_desc; + msdu_details = &msdu_link->msdu_link[0]; + + for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) { + if (FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + msdu_details[i].buf_addr_info.info0) == 0) { + msdu_desc_info = &msdu_details[i - 1].rx_msdu_info; + msdu_desc_info->info0 |= last; + ; + break; + } + msdu_desc_info = &msdu_details[i].rx_msdu_info; + + if (!i) + msdu_desc_info->info0 |= first; + else if (i == (HAL_RX_NUM_MSDU_DESC - 1)) + msdu_desc_info->info0 |= last; + msdu_list->msdu_info[i].msdu_flags = msdu_desc_info->info0; + msdu_list->msdu_info[i].msdu_len = + HAL_RX_MSDU_PKT_LENGTH_GET(msdu_desc_info->info0); + msdu_list->sw_cookie[i] = + FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + msdu_details[i].buf_addr_info.info1); + tmp = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, + msdu_details[i].buf_addr_info.info1); + msdu_list->rbm[i] = tmp; + } + *num_msdus = i; +} + +static u32 ath11k_dp_rx_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id, + u32 *rx_bufs_used) +{ + u32 ret = 0; + + if ((*ppdu_id < msdu_ppdu_id) && + ((msdu_ppdu_id - *ppdu_id) < DP_NOT_PPDU_ID_WRAP_AROUND)) { + *ppdu_id = msdu_ppdu_id; + ret = msdu_ppdu_id; + } else if ((*ppdu_id > msdu_ppdu_id) && + ((*ppdu_id - msdu_ppdu_id) > DP_NOT_PPDU_ID_WRAP_AROUND)) { + /* mon_dst is behind than mon_status + * skip dst_ring and free it + */ + *rx_bufs_used += 1; + *ppdu_id = msdu_ppdu_id; + ret = msdu_ppdu_id; + } + return ret; +} + +static void ath11k_dp_mon_get_buf_len(struct hal_rx_msdu_desc_info *info, + bool *is_frag, u32 *total_len, + u32 *frag_len, u32 *msdu_cnt) +{ + if (info->msdu_flags & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION) { + if (!*is_frag) { + *total_len = info->msdu_len; + *is_frag = true; + } + ath11k_dp_mon_set_frag_len(total_len, + frag_len); + } else { + if (*is_frag) { + ath11k_dp_mon_set_frag_len(total_len, + frag_len); + } else { + *frag_len = info->msdu_len; + } + *is_frag = false; + *msdu_cnt -= 1; + } +} + +static u32 +ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, + void *ring_entry, struct sk_buff **head_msdu, + struct sk_buff **tail_msdu, u32 *npackets, + u32 *ppdu_id) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + struct dp_rxdma_ring *rx_ring = &dp->rxdma_mon_buf_ring; + struct sk_buff *msdu = NULL, *last = NULL; + struct hal_rx_msdu_list msdu_list; + void *p_buf_addr_info, *p_last_buf_addr_info; + struct hal_rx_desc *rx_desc; + void *rx_msdu_link_desc; + dma_addr_t paddr; + u16 num_msdus = 0; + u32 rx_buf_size, rx_pkt_offset, sw_cookie; + u32 rx_bufs_used = 0, i = 0; + u32 msdu_ppdu_id = 0, msdu_cnt = 0; + u32 total_len = 0, frag_len = 0; + bool is_frag, is_first_msdu; + bool drop_mpdu = false; + struct ath11k_skb_rxcb *rxcb; + struct hal_reo_entrance_ring *ent_desc = + (struct hal_reo_entrance_ring *)ring_entry; + int buf_id; + + ath11k_hal_rx_reo_ent_buf_paddr_get(ring_entry, &paddr, + &sw_cookie, &p_last_buf_addr_info, + &msdu_cnt); + + if (FIELD_GET(HAL_REO_ENTR_RING_INFO1_RXDMA_PUSH_REASON, + ent_desc->info1) == + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) { + u8 rxdma_err = + FIELD_GET(HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE, + ent_desc->info1); + if (rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR || + rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR || + rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR) { + drop_mpdu = true; + pmon->rx_mon_stats.dest_mpdu_drop++; + } + } + + is_frag = false; + is_first_msdu = true; + + do { + if (pmon->mon_last_linkdesc_paddr == paddr) { + pmon->rx_mon_stats.dup_mon_linkdesc_cnt++; + return rx_bufs_used; + } + + rx_msdu_link_desc = + (void *)pmon->link_desc_banks[sw_cookie].vaddr + + (paddr - pmon->link_desc_banks[sw_cookie].paddr); + + ath11k_hal_rx_msdu_list_get(ar, rx_msdu_link_desc, &msdu_list, + &num_msdus); + + for (i = 0; i < num_msdus; i++) { + u32 l2_hdr_offset; + + if (pmon->mon_last_buf_cookie == msdu_list.sw_cookie[i]) { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "i %d last_cookie %d is same\n", + i, pmon->mon_last_buf_cookie); + drop_mpdu = true; + pmon->rx_mon_stats.dup_mon_buf_cnt++; + continue; + } + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + msdu_list.sw_cookie[i]); + + spin_lock_bh(&rx_ring->idr_lock); + msdu = idr_find(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + if (!msdu) { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "msdu_pop: invalid buf_id %d\n", buf_id); + break; + } + rxcb = ATH11K_SKB_RXCB(msdu); + if (!rxcb->unmapped) { + dma_unmap_single(ar->ab->dev, rxcb->paddr, + msdu->len + + skb_tailroom(msdu), + DMA_FROM_DEVICE); + rxcb->unmapped = 1; + } + if (drop_mpdu) { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "i %d drop msdu %p *ppdu_id %x\n", + i, msdu, *ppdu_id); + dev_kfree_skb_any(msdu); + msdu = NULL; + goto next_msdu; + } + + rx_desc = (struct hal_rx_desc *)msdu->data; + + rx_pkt_offset = sizeof(struct hal_rx_desc); + l2_hdr_offset = ath11k_dp_rx_h_msdu_end_l3pad(rx_desc); + + if (is_first_msdu) { + if (!ath11k_dp_rxdesc_mpdu_valid(rx_desc)) { + drop_mpdu = true; + dev_kfree_skb_any(msdu); + msdu = NULL; + pmon->mon_last_linkdesc_paddr = paddr; + goto next_msdu; + } + + msdu_ppdu_id = + ath11k_dp_rxdesc_get_ppduid(rx_desc); + + if (ath11k_dp_rx_mon_comp_ppduid(msdu_ppdu_id, + ppdu_id, + &rx_bufs_used)) { + if (rx_bufs_used) { + drop_mpdu = true; + dev_kfree_skb_any(msdu); + msdu = NULL; + goto next_msdu; + } + return rx_bufs_used; + } + pmon->mon_last_linkdesc_paddr = paddr; + is_first_msdu = false; + } + ath11k_dp_mon_get_buf_len(&msdu_list.msdu_info[i], + &is_frag, &total_len, + &frag_len, &msdu_cnt); + rx_buf_size = rx_pkt_offset + l2_hdr_offset + frag_len; + + ath11k_dp_pkt_set_pktlen(msdu, rx_buf_size); + + if (!(*head_msdu)) + *head_msdu = msdu; + else if (last) + last->next = msdu; + + last = msdu; +next_msdu: + pmon->mon_last_buf_cookie = msdu_list.sw_cookie[i]; + rx_bufs_used++; + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + } + + ath11k_dp_rx_mon_next_link_desc_get(rx_msdu_link_desc, &paddr, + &sw_cookie, + &p_buf_addr_info); + + if (ath11k_dp_rx_monitor_link_desc_return(ar, + p_last_buf_addr_info, + dp->mac_id)) + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "dp_rx_monitor_link_desc_return failed"); + + p_last_buf_addr_info = p_buf_addr_info; + + } while (paddr && msdu_cnt); + + if (last) + last->next = NULL; + + *tail_msdu = msdu; + + if (msdu_cnt == 0) + *npackets = 1; + + return rx_bufs_used; +} + +static void ath11k_dp_rx_msdus_set_payload(struct sk_buff *msdu) +{ + u32 rx_pkt_offset, l2_hdr_offset; + + rx_pkt_offset = sizeof(struct hal_rx_desc); + l2_hdr_offset = ath11k_dp_rx_h_msdu_end_l3pad((struct hal_rx_desc *)msdu->data); + skb_pull(msdu, rx_pkt_offset + l2_hdr_offset); +} + +static struct sk_buff * +ath11k_dp_rx_mon_merg_msdus(struct ath11k *ar, + u32 mac_id, struct sk_buff *head_msdu, + struct sk_buff *last_msdu, + struct ieee80211_rx_status *rxs) +{ + struct sk_buff *msdu, *mpdu_buf, *prev_buf; + u32 decap_format, wifi_hdr_len; + struct hal_rx_desc *rx_desc; + char *hdr_desc; + u8 *dest; + struct ieee80211_hdr_3addr *wh; + + mpdu_buf = NULL; + + if (!head_msdu) + goto err_merge_fail; + + rx_desc = (struct hal_rx_desc *)head_msdu->data; + + if (ath11k_dp_rxdesc_get_mpdulen_err(rx_desc)) + return NULL; + + decap_format = ath11k_dp_rxdesc_get_decap_format(rx_desc); + + ath11k_dp_rx_h_ppdu(ar, rx_desc, rxs); + + if (decap_format == DP_RX_DECAP_TYPE_RAW) { + ath11k_dp_rx_msdus_set_payload(head_msdu); + + prev_buf = head_msdu; + msdu = head_msdu->next; + + while (msdu) { + ath11k_dp_rx_msdus_set_payload(msdu); + + prev_buf = msdu; + msdu = msdu->next; + } + + prev_buf->next = NULL; + + skb_trim(prev_buf, prev_buf->len - HAL_RX_FCS_LEN); + } else if (decap_format == DP_RX_DECAP_TYPE_NATIVE_WIFI) { + __le16 qos_field; + u8 qos_pkt = 0; + + rx_desc = (struct hal_rx_desc *)head_msdu->data; + hdr_desc = ath11k_dp_rxdesc_get_80211hdr(rx_desc); + + /* Base size */ + wifi_hdr_len = sizeof(struct ieee80211_hdr_3addr); + wh = (struct ieee80211_hdr_3addr *)hdr_desc; + + if (ieee80211_is_data_qos(wh->frame_control)) { + struct ieee80211_qos_hdr *qwh = + (struct ieee80211_qos_hdr *)hdr_desc; + + qos_field = qwh->qos_ctrl; + qos_pkt = 1; + } + msdu = head_msdu; + + while (msdu) { + rx_desc = (struct hal_rx_desc *)msdu->data; + hdr_desc = ath11k_dp_rxdesc_get_80211hdr(rx_desc); + + if (qos_pkt) { + dest = skb_push(msdu, sizeof(__le16)); + if (!dest) + goto err_merge_fail; + memcpy(dest, hdr_desc, wifi_hdr_len); + memcpy(dest + wifi_hdr_len, + (u8 *)&qos_field, sizeof(__le16)); + } + ath11k_dp_rx_msdus_set_payload(msdu); + prev_buf = msdu; + msdu = msdu->next; + } + dest = skb_put(prev_buf, HAL_RX_FCS_LEN); + if (!dest) + goto err_merge_fail; + + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "mpdu_buf %pK mpdu_buf->len %u", + prev_buf, prev_buf->len); + } else { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "decap format %d is not supported!\n", + decap_format); + goto err_merge_fail; + } + + return head_msdu; + +err_merge_fail: + if (mpdu_buf && decap_format != DP_RX_DECAP_TYPE_RAW) { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "err_merge_fail mpdu_buf %pK", mpdu_buf); + /* Free the head buffer */ + dev_kfree_skb_any(mpdu_buf); + } + return NULL; +} + +static int ath11k_dp_rx_mon_deliver(struct ath11k *ar, u32 mac_id, + struct sk_buff *head_msdu, + struct sk_buff *tail_msdu, + struct napi_struct *napi) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct sk_buff *mon_skb, *skb_next, *header; + struct ieee80211_rx_status *rxs = &dp->rx_status, *status; + + mon_skb = ath11k_dp_rx_mon_merg_msdus(ar, mac_id, head_msdu, + tail_msdu, rxs); + + if (!mon_skb) + goto mon_deliver_fail; + + header = mon_skb; + + rxs->flag = 0; + do { + skb_next = mon_skb->next; + if (!skb_next) + rxs->flag &= ~RX_FLAG_AMSDU_MORE; + else + rxs->flag |= RX_FLAG_AMSDU_MORE; + + if (mon_skb == header) { + header = NULL; + rxs->flag &= ~RX_FLAG_ALLOW_SAME_PN; + } else { + rxs->flag |= RX_FLAG_ALLOW_SAME_PN; + } + rxs->flag |= RX_FLAG_ONLY_MONITOR; + + status = IEEE80211_SKB_RXCB(mon_skb); + *status = *rxs; + + ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb); + mon_skb = skb_next; + } while (mon_skb); + rxs->flag = 0; + + return 0; + +mon_deliver_fail: + mon_skb = head_msdu; + while (mon_skb) { + skb_next = mon_skb->next; + dev_kfree_skb_any(mon_skb); + mon_skb = skb_next; + } + return -EINVAL; +} + +static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, u32 quota, + struct napi_struct *napi) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + void *ring_entry; + void *mon_dst_srng; + u32 ppdu_id; + u32 rx_bufs_used; + struct ath11k_pdev_mon_stats *rx_mon_stats; + u32 npackets = 0; + + mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id]; + + if (!mon_dst_srng) { + ath11k_warn(ar->ab, + "HAL Monitor Destination Ring Init Failed -- %pK", + mon_dst_srng); + return; + } + + spin_lock_bh(&pmon->mon_lock); + + ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng); + + ppdu_id = pmon->mon_ppdu_info.ppdu_id; + rx_bufs_used = 0; + rx_mon_stats = &pmon->rx_mon_stats; + + while ((ring_entry = ath11k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) { + struct sk_buff *head_msdu, *tail_msdu; + + head_msdu = NULL; + tail_msdu = NULL; + + rx_bufs_used += ath11k_dp_rx_mon_mpdu_pop(ar, ring_entry, + &head_msdu, + &tail_msdu, + &npackets, &ppdu_id); + + if (ppdu_id != pmon->mon_ppdu_info.ppdu_id) { + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "dest_rx: new ppdu_id %x != status ppdu_id %x", + ppdu_id, pmon->mon_ppdu_info.ppdu_id); + break; + } + if (head_msdu && tail_msdu) { + ath11k_dp_rx_mon_deliver(ar, dp->mac_id, head_msdu, + tail_msdu, napi); + rx_mon_stats->dest_mpdu_done++; + } + + ring_entry = ath11k_hal_srng_dst_get_next_entry(ar->ab, + mon_dst_srng); + } + ath11k_hal_srng_access_end(ar->ab, mon_dst_srng); + + spin_unlock_bh(&pmon->mon_lock); + + if (rx_bufs_used) { + rx_mon_stats->dest_ppdu_done++; + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, + &dp->rxdma_mon_buf_ring, + rx_bufs_used, + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); + } +} + +static void ath11k_dp_rx_mon_status_process_tlv(struct ath11k *ar, + u32 quota, + struct napi_struct *napi) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + struct hal_rx_mon_ppdu_info *ppdu_info; + struct sk_buff *status_skb; + u32 tlv_status = HAL_TLV_STATUS_BUF_DONE; + struct ath11k_pdev_mon_stats *rx_mon_stats; + + ppdu_info = &pmon->mon_ppdu_info; + rx_mon_stats = &pmon->rx_mon_stats; + + if (pmon->mon_ppdu_status != DP_PPDU_STATUS_START) + return; + + while (!skb_queue_empty(&pmon->rx_status_q)) { + status_skb = skb_dequeue(&pmon->rx_status_q); + + tlv_status = ath11k_hal_rx_parse_mon_status(ar->ab, ppdu_info, + status_skb); + if (tlv_status == HAL_TLV_STATUS_PPDU_DONE) { + rx_mon_stats->status_ppdu_done++; + pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; + ath11k_dp_rx_mon_dest_process(ar, quota, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } + dev_kfree_skb_any(status_skb); + } +} + +static int ath11k_dp_mon_process_rx(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + int num_buffs_reaped = 0; + + num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ar->ab, dp->mac_id, &budget, + &pmon->rx_status_q); + if (num_buffs_reaped) + ath11k_dp_rx_mon_status_process_tlv(ar, budget, napi); + + return num_buffs_reaped; +} + +int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + int ret = 0; + + if (test_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags)) + ret = ath11k_dp_mon_process_rx(ab, mac_id, napi, budget); + else + ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); + return ret; +} + +static int ath11k_dp_rx_pdev_mon_status_attach(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + + skb_queue_head_init(&pmon->rx_status_q); + + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + + memset(&pmon->rx_mon_stats, 0, + sizeof(pmon->rx_mon_stats)); + return 0; +} + +int ath11k_dp_rx_pdev_mon_attach(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = &dp->mon_data; + struct hal_srng *mon_desc_srng = NULL; + struct dp_srng *dp_srng; + int ret = 0; + u32 n_link_desc = 0; + + ret = ath11k_dp_rx_pdev_mon_status_attach(ar); + if (ret) { + ath11k_warn(ar->ab, "pdev_mon_status_attach() failed"); + return ret; + } + + dp_srng = &dp->rxdma_mon_desc_ring; + n_link_desc = dp_srng->size / + ath11k_hal_srng_get_entrysize(HAL_RXDMA_MONITOR_DESC); + mon_desc_srng = + &ar->ab->hal.srng_list[dp->rxdma_mon_desc_ring.ring_id]; + + ret = ath11k_dp_link_desc_setup(ar->ab, pmon->link_desc_banks, + HAL_RXDMA_MONITOR_DESC, mon_desc_srng, + n_link_desc); + if (ret) { + ath11k_warn(ar->ab, "mon_link_desc_pool_setup() failed"); + return ret; + } + pmon->mon_last_linkdesc_paddr = 0; + pmon->mon_last_buf_cookie = DP_RX_DESC_COOKIE_MAX + 1; + spin_lock_init(&pmon->mon_lock); + return 0; +} + +static int ath11k_dp_mon_link_free(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = &dp->mon_data; + + ath11k_dp_link_desc_cleanup(ar->ab, pmon->link_desc_banks, + HAL_RXDMA_MONITOR_DESC, + &dp->rxdma_mon_desc_ring); + return 0; +} + +int ath11k_dp_rx_pdev_mon_detach(struct ath11k *ar) +{ + ath11k_dp_mon_link_free(ar); + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.h b/drivers/net/wireless/ath/ath11k/dp_rx.h new file mode 100644 index 000000000000..eec5deaa59ad --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp_rx.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#ifndef ATH11K_DP_RX_H +#define ATH11K_DP_RX_H + +#include "core.h" +#include "rx_desc.h" +#include "debug.h" + +#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) +#define DP_RX_MPDU_ERR_AMSDU_ERR BIT(3) +#define DP_RX_MPDU_ERR_OVERFLOW BIT(4) +#define DP_RX_MPDU_ERR_MSDU_LEN BIT(5) +#define DP_RX_MPDU_ERR_MPDU_LEN BIT(6) +#define DP_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7) + +enum dp_rx_decap_type { + DP_RX_DECAP_TYPE_RAW, + DP_RX_DECAP_TYPE_NATIVE_WIFI, + DP_RX_DECAP_TYPE_ETHERNET2_DIX, + DP_RX_DECAP_TYPE_8023, +}; + +struct ath11k_dp_amsdu_subframe_hdr { + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + __be16 len; +} __packed; + +struct ath11k_dp_rfc1042_hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + __be16 snap_type; +} __packed; + +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); +void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer); +int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, + u8 tid, u32 ba_win_sz, u16 ssn); +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); +void ath11k_dp_pdev_reo_cleanup(struct ath11k_base *ab); +int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int pdev_idx); +void ath11k_dp_rx_pdev_free(struct ath11k_base *ab, int pdev_idx); +void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab); +void ath11k_dp_process_reo_status(struct ath11k_base *ab); +int ath11k_dp_process_rxdma_err(struct ath11k_base *ab, int mac_id, int budget); +int ath11k_dp_rx_process_wbm_err(struct ath11k_base *ab, + struct napi_struct *napi, int budget); +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, + int budget); +int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, + enum hal_rx_buf_return_buf_manager mgr, + gfp_t gfp); +int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath11k_base *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data); +int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget); +int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget); +int ath11k_dp_rx_mon_status_bufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, + enum hal_rx_buf_return_buf_manager mgr, + gfp_t gfp); +int ath11k_dp_rx_pdev_mon_detach(struct ath11k *ar); +int ath11k_dp_rx_pdev_mon_attach(struct ath11k *ar); + +#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 new file mode 100644 index 000000000000..6d7d33761caf --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -0,0 +1,962 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "core.h" +#include "dp_tx.h" +#include "debug.h" +#include "hw.h" + +/* NOTE: Any of the mapped ring id value must not exceed DP_TCL_NUM_RING_MAX */ +static const u8 +ath11k_txq_tcl_ring_map[ATH11K_HW_MAX_QUEUES] = { 0x0, 0x1, 0x2, 0x2 }; + +static enum hal_tcl_encap_type +ath11k_dp_tx_get_encap_type(struct ath11k_vif *arvif, struct sk_buff *skb) +{ + /* TODO: Determine encap type based on vif_type and configuration */ + return HAL_TCL_ENCAP_TYPE_NATIVE_WIFI; +} + +static void ath11k_dp_tx_encap_nwifi(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + u8 *qos_ctl; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return; + + qos_ctl = ieee80211_get_qos_ctl(hdr); + memmove(skb->data + IEEE80211_QOS_CTL_LEN, + skb->data, (void *)qos_ctl - (void *)skb->data); + skb_pull(skb, IEEE80211_QOS_CTL_LEN); + + hdr = (void *)skb->data; + hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA); +} + +static u8 ath11k_dp_tx_get_tid(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return HAL_DESC_REO_NON_QOS_TID; + else + return skb->priority & IEEE80211_QOS_CTL_TID_MASK; +} + +static enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return HAL_ENCRYPT_TYPE_WEP_40; + case WLAN_CIPHER_SUITE_WEP104: + return HAL_ENCRYPT_TYPE_WEP_104; + case WLAN_CIPHER_SUITE_TKIP: + return HAL_ENCRYPT_TYPE_TKIP_MIC; + case WLAN_CIPHER_SUITE_CCMP: + return HAL_ENCRYPT_TYPE_CCMP_128; + case WLAN_CIPHER_SUITE_CCMP_256: + return HAL_ENCRYPT_TYPE_CCMP_256; + case WLAN_CIPHER_SUITE_GCMP: + return HAL_ENCRYPT_TYPE_GCMP_128; + case WLAN_CIPHER_SUITE_GCMP_256: + return HAL_ENCRYPT_TYPE_AES_GCMP_256; + default: + return HAL_ENCRYPT_TYPE_OPEN; + } +} + +int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, + struct sk_buff *skb) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_dp *dp = &ab->dp; + struct hal_tx_info ti = {0}; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); + struct hal_srng *tcl_ring; + struct ieee80211_hdr *hdr = (void *)skb->data; + struct dp_tx_ring *tx_ring; + void *hal_tcl_desc; + u8 pool_id; + u8 hal_ring_id; + int ret; + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) + return -ESHUTDOWN; + + if (!ieee80211_is_data(hdr->frame_control)) + return -ENOTSUPP; + + pool_id = skb_get_queue_mapping(skb) & (ATH11K_HW_MAX_QUEUES - 1); + ti.ring_id = ath11k_txq_tcl_ring_map[pool_id]; + + tx_ring = &dp->tx_ring[ti.ring_id]; + + spin_lock_bh(&tx_ring->tx_idr_lock); + ret = idr_alloc(&tx_ring->txbuf_idr, skb, 0, + DP_TX_IDR_SIZE - 1, GFP_ATOMIC); + spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (ret < 0) + return -ENOSPC; + + ti.desc_id = FIELD_PREP(DP_TX_DESC_ID_MAC_ID, ar->pdev_idx) | + FIELD_PREP(DP_TX_DESC_ID_MSDU_ID, ret) | + FIELD_PREP(DP_TX_DESC_ID_POOL_ID, pool_id); + ti.encap_type = ath11k_dp_tx_get_encap_type(arvif, skb); + ti.meta_data_flags = arvif->tcl_metadata; + + if (info->control.hw_key) + ti.encrypt_type = + ath11k_dp_tx_get_encrypt_type(info->control.hw_key->cipher); + else + ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; + + ti.addr_search_flags = arvif->hal_addr_search_flags; + ti.search_type = arvif->search_type; + ti.type = HAL_TCL_DESC_TYPE_BUFFER; + ti.pkt_offset = 0; + ti.lmac_id = ar->lmac_id; + ti.bss_ast_hash = arvif->ast_hash; + ti.dscp_tid_tbl_idx = 0; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + ti.flags0 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_IP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_UDP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_UDP6_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TCP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TCP6_CKSUM_EN, 1); + } + + if (ieee80211_vif_is_mesh(arvif->vif)) + ti.flags1 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_MESH_ENABLE, 1); + + ti.flags1 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE, 1); + + ti.tid = ath11k_dp_tx_get_tid(skb); + + switch (ti.encap_type) { + case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI: + ath11k_dp_tx_encap_nwifi(skb); + break; + case HAL_TCL_ENCAP_TYPE_RAW: + /* TODO: for CHECKSUM_PARTIAL case in raw mode, HW checksum offload + * is not applicable, hence manual checksum calculation using + * skb_checksum_help() is needed + */ + case HAL_TCL_ENCAP_TYPE_ETHERNET: + case HAL_TCL_ENCAP_TYPE_802_3: + /* TODO: Take care of other encap modes as well */ + ret = -EINVAL; + goto fail_remove_idr; + } + + ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(ab->dev, ti.paddr)) { + ath11k_warn(ab, "failed to DMA map data Tx buffer\n"); + ret = -ENOMEM; + goto fail_remove_idr; + } + + ti.data_len = skb->len; + skb_cb->paddr = ti.paddr; + skb_cb->vif = arvif->vif; + skb_cb->ar = ar; + + hal_ring_id = tx_ring->tcl_data_ring.ring_id; + tcl_ring = &ab->hal.srng_list[hal_ring_id]; + + spin_lock_bh(&tcl_ring->lock); + + ath11k_hal_srng_access_begin(ab, tcl_ring); + + hal_tcl_desc = (void *)ath11k_hal_srng_src_get_next_entry(ab, tcl_ring); + if (!hal_tcl_desc) { + /* NOTE: It is highly unlikely we'll be running out of tcl_ring + * desc because the desc is directly enqueued onto hw queue. + * So add tx packet throttling logic in future if required. + */ + ath11k_hal_srng_access_end(ab, tcl_ring); + spin_unlock_bh(&tcl_ring->lock); + ret = -ENOMEM; + goto fail_unmap_dma; + } + + ath11k_hal_tx_cmd_desc_setup(ab, hal_tcl_desc + + sizeof(struct hal_tlv_hdr), &ti); + + ath11k_hal_srng_access_end(ab, tcl_ring); + + spin_unlock_bh(&tcl_ring->lock); + + atomic_inc(&ar->dp.num_tx_pending); + + return 0; + +fail_unmap_dma: + dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); + +fail_remove_idr: + spin_lock_bh(&tx_ring->tx_idr_lock); + idr_remove(&tx_ring->txbuf_idr, + FIELD_GET(DP_TX_DESC_ID_MSDU_ID, ti.desc_id)); + spin_unlock_bh(&tx_ring->tx_idr_lock); + + return ret; +} + +static void ath11k_dp_tx_free_txbuf(struct ath11k_base *ab, u8 mac_id, + int msdu_id, + struct dp_tx_ring *tx_ring) +{ + struct ath11k *ar; + struct sk_buff *msdu; + struct ath11k_skb_cb *skb_cb; + + spin_lock_bh(&tx_ring->tx_idr_lock); + msdu = idr_find(&tx_ring->txbuf_idr, msdu_id); + if (!msdu) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", + msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); + return; + } + + skb_cb = ATH11K_SKB_CB(msdu); + + idr_remove(&tx_ring->txbuf_idr, msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + dev_kfree_skb_any(msdu); + + ar = ab->pdevs[mac_id].ar; + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); +} + +static void +ath11k_dp_tx_htt_tx_complete_buf(struct ath11k_base *ab, + struct dp_tx_ring *tx_ring, + struct ath11k_dp_htt_wbm_tx_status *ts) +{ + struct sk_buff *msdu; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; + struct ath11k *ar; + + spin_lock_bh(&tx_ring->tx_idr_lock); + msdu = idr_find(&tx_ring->txbuf_idr, ts->msdu_id); + if (!msdu) { + ath11k_warn(ab, "htt tx completion for unknown msdu_id %d\n", + ts->msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); + return; + } + + skb_cb = ATH11K_SKB_CB(msdu); + info = IEEE80211_SKB_CB(msdu); + + ar = skb_cb->ar; + + idr_remove(&tx_ring->txbuf_idr, ts->msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + + memset(&info->status, 0, sizeof(info->status)); + + if (ts->acked) { + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + + ts->ack_rssi; + info->status.is_valid_ack_signal = true; + } else { + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + } + } + + ieee80211_tx_status(ar->hw, msdu); +} + +static void +ath11k_dp_tx_process_htt_tx_complete(struct ath11k_base *ab, + void *desc, u8 mac_id, + u32 msdu_id, struct dp_tx_ring *tx_ring) +{ + struct htt_tx_wbm_completion *status_desc; + struct ath11k_dp_htt_wbm_tx_status ts = {0}; + enum hal_wbm_htt_tx_comp_status wbm_status; + + status_desc = desc + HTT_TX_WBM_COMP_STATUS_OFFSET; + + wbm_status = FIELD_GET(HTT_TX_WBM_COMP_INFO0_STATUS, + status_desc->info0); + + switch (wbm_status) { + case HAL_WBM_REL_HTT_TX_COMP_STATUS_OK: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: + ts.acked = (wbm_status == HAL_WBM_REL_HTT_TX_COMP_STATUS_OK); + ts.msdu_id = msdu_id; + ts.ack_rssi = FIELD_GET(HTT_TX_WBM_COMP_INFO1_ACK_RSSI, + status_desc->info1); + ath11k_dp_tx_htt_tx_complete_buf(ab, tx_ring, &ts); + break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT: + ath11k_dp_tx_free_txbuf(ab, mac_id, msdu_id, tx_ring); + break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY: + /* This event is to be handled only when the driver decides to + * use WDS offload functionality. + */ + break; + default: + ath11k_warn(ab, "Unknown htt tx status %d\n", wbm_status); + break; + } +} + +static void ath11k_dp_tx_cache_peer_stats(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ + struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; + + if (ts->try_cnt > 1) { + peer_stats->retry_pkts += ts->try_cnt - 1; + peer_stats->retry_bytes += (ts->try_cnt - 1) * msdu->len; + + if (ts->status != HAL_WBM_TQM_REL_REASON_FRAME_ACKED) { + peer_stats->failed_pkts += 1; + peer_stats->failed_bytes += msdu->len; + } + } +} + +static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ + struct ath11k_base *ab = ar->ab; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; + + if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) { + /* Must not happen */ + return; + } + + skb_cb = ATH11K_SKB_CB(msdu); + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + + rcu_read_lock(); + + if (!rcu_dereference(ab->pdevs_active[ar->pdev_idx])) { + dev_kfree_skb_any(msdu); + goto exit; + } + + if (!skb_cb->vif) { + dev_kfree_skb_any(msdu); + goto exit; + } + + info = IEEE80211_SKB_CB(msdu); + memset(&info->status, 0, sizeof(info->status)); + + /* skip tx rate update from ieee80211_status*/ + info->status.rates[0].idx = -1; + + if (ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED && + !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + + ts->ack_rssi; + info->status.is_valid_ack_signal = true; + } + + if (ts->status == HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX && + (info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) { + if (ts->flags & HAL_TX_STATUS_FLAGS_FIRST_MSDU) { + if (ar->last_ppdu_id == 0) { + ar->last_ppdu_id = ts->ppdu_id; + } else if (ar->last_ppdu_id == ts->ppdu_id || + ar->cached_ppdu_id == ar->last_ppdu_id) { + ar->cached_ppdu_id = ar->last_ppdu_id; + ar->cached_stats.is_ampdu = true; + ath11k_update_per_peer_stats_from_txcompl(ar, msdu, ts); + memset(&ar->cached_stats, 0, + sizeof(struct ath11k_per_peer_tx_stats)); + } else { + ar->cached_stats.is_ampdu = false; + ath11k_update_per_peer_stats_from_txcompl(ar, msdu, ts); + memset(&ar->cached_stats, 0, + sizeof(struct ath11k_per_peer_tx_stats)); + } + ar->last_ppdu_id = ts->ppdu_id; + } + + ath11k_dp_tx_cache_peer_stats(ar, msdu, ts); + } + + /* NOTE: Tx rate status reporting. Tx completion status does not have + * necessary information (for example nss) to build the tx rate. + * Might end up reporting it out-of-band from HTT stats. + */ + + ieee80211_tx_status(ar->hw, msdu); + +exit: + rcu_read_unlock(); +} + +static inline void ath11k_dp_tx_status_parse(struct ath11k_base *ab, + struct hal_wbm_release_ring *desc, + struct hal_tx_status *ts) +{ + ts->buf_rel_source = + FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, desc->info0); + if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && + ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM) + return; + + if (ts->buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) + return; + + ts->status = FIELD_GET(HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON, + desc->info0); + ts->ppdu_id = FIELD_GET(HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER, + desc->info1); + ts->try_cnt = FIELD_GET(HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT, + desc->info1); + ts->ack_rssi = FIELD_GET(HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI, + desc->info2); + if (desc->info2 & HAL_WBM_RELEASE_INFO2_FIRST_MSDU) + ts->flags |= HAL_TX_STATUS_FLAGS_FIRST_MSDU; + ts->peer_id = FIELD_GET(HAL_WBM_RELEASE_INFO3_PEER_ID, desc->info3); + ts->tid = FIELD_GET(HAL_WBM_RELEASE_INFO3_TID, desc->info3); + if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_VALID) + ts->rate_stats = desc->rate_stats.info0; + else + ts->rate_stats = 0; +} + +void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) +{ + struct ath11k *ar; + struct ath11k_dp *dp = &ab->dp; + int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id; + struct hal_srng *status_ring = &ab->hal.srng_list[hal_ring_id]; + struct sk_buff *msdu; + struct hal_tx_status ts = { 0 }; + struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; + u32 *desc; + u32 msdu_id; + u8 mac_id; + + ath11k_hal_srng_access_begin(ab, status_ring); + + while ((ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head) != + tx_ring->tx_status_tail) && + (desc = ath11k_hal_srng_dst_get_next_entry(ab, status_ring))) { + memcpy(&tx_ring->tx_status[tx_ring->tx_status_head], + desc, sizeof(struct hal_wbm_release_ring)); + tx_ring->tx_status_head = + ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head); + } + + if ((ath11k_hal_srng_dst_peek(ab, status_ring) != NULL) && + (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head) == tx_ring->tx_status_tail)) { + /* TODO: Process pending tx_status messages when kfifo_is_full() */ + ath11k_warn(ab, "Unable to process some of the tx_status ring desc because status_fifo is full\n"); + } + + ath11k_hal_srng_access_end(ab, status_ring); + + while (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail) != tx_ring->tx_status_head) { + struct hal_wbm_release_ring *tx_status; + u32 desc_id; + + tx_ring->tx_status_tail = + ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail); + tx_status = &tx_ring->tx_status[tx_ring->tx_status_tail]; + ath11k_dp_tx_status_parse(ab, tx_status, &ts); + + desc_id = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + tx_status->buf_addr_info.info1); + mac_id = FIELD_GET(DP_TX_DESC_ID_MAC_ID, desc_id); + msdu_id = FIELD_GET(DP_TX_DESC_ID_MSDU_ID, desc_id); + + if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) { + ath11k_dp_tx_process_htt_tx_complete(ab, + (void *)tx_status, + mac_id, msdu_id, + tx_ring); + continue; + } + + spin_lock_bh(&tx_ring->tx_idr_lock); + msdu = idr_find(&tx_ring->txbuf_idr, msdu_id); + if (!msdu) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", + msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); + continue; + } + idr_remove(&tx_ring->txbuf_idr, msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); + + ar = ab->pdevs[mac_id].ar; + + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); + + ath11k_dp_tx_complete_msdu(ar, msdu, &ts); + } +} + +int ath11k_dp_tx_send_reo_cmd(struct ath11k_base *ab, struct dp_rx_tid *rx_tid, + enum hal_reo_cmd_type type, + struct ath11k_hal_reo_cmd *cmd, + void (*cb)(struct ath11k_dp *, void *, + enum hal_reo_cmd_status)) +{ + struct ath11k_dp *dp = &ab->dp; + struct dp_reo_cmd *dp_cmd; + struct hal_srng *cmd_ring; + int cmd_num; + + cmd_ring = &ab->hal.srng_list[dp->reo_cmd_ring.ring_id]; + cmd_num = ath11k_hal_reo_cmd_send(ab, cmd_ring, type, cmd); + + /* reo cmd ring descriptors has cmd_num starting from 1 */ + if (cmd_num <= 0) + return -EINVAL; + + if (!cb) + return 0; + + /* Can this be optimized so that we keep the pending command list only + * for tid delete command to free up the resoruce on the command status + * indication? + */ + dp_cmd = kzalloc(sizeof(*dp_cmd), GFP_ATOMIC); + + if (!dp_cmd) + return -ENOMEM; + + memcpy(&dp_cmd->data, rx_tid, sizeof(struct dp_rx_tid)); + dp_cmd->cmd_num = cmd_num; + dp_cmd->handler = cb; + + spin_lock_bh(&dp->reo_cmd_lock); + list_add_tail(&dp_cmd->list, &dp->reo_cmd_list); + spin_unlock_bh(&dp->reo_cmd_lock); + + return 0; +} + +static int +ath11k_dp_tx_get_ring_id_type(struct ath11k_base *ab, + int mac_id, u32 ring_id, + enum hal_ring_type ring_type, + enum htt_srng_ring_type *htt_ring_type, + enum htt_srng_ring_id *htt_ring_id) +{ + int lmac_ring_id_offset = 0; + int ret = 0; + + switch (ring_type) { + case HAL_RXDMA_BUF: + lmac_ring_id_offset = mac_id * HAL_SRNG_RINGS_PER_LMAC; + if (!(ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF + + lmac_ring_id_offset) || + ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_BUF + + lmac_ring_id_offset))) { + ret = -EINVAL; + } + *htt_ring_id = HTT_RXDMA_HOST_BUF_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + case HAL_RXDMA_DST: + *htt_ring_id = HTT_RXDMA_NON_MONITOR_DEST_RING; + *htt_ring_type = HTT_HW_TO_SW_RING; + break; + case HAL_RXDMA_MONITOR_BUF: + *htt_ring_id = HTT_RXDMA_MONITOR_BUF_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + case HAL_RXDMA_MONITOR_STATUS: + *htt_ring_id = HTT_RXDMA_MONITOR_STATUS_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + case HAL_RXDMA_MONITOR_DST: + *htt_ring_id = HTT_RXDMA_MONITOR_DEST_RING; + *htt_ring_type = HTT_HW_TO_SW_RING; + break; + case HAL_RXDMA_MONITOR_DESC: + *htt_ring_id = HTT_RXDMA_MONITOR_DESC_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + default: + ath11k_warn(ab, "Unsupported ring type in DP :%d\n", ring_type); + ret = -EINVAL; + } + return ret; +} + +int ath11k_dp_tx_htt_srng_setup(struct ath11k_base *ab, u32 ring_id, + int mac_id, enum hal_ring_type ring_type) +{ + struct htt_srng_setup_cmd *cmd; + struct hal_srng *srng = &ab->hal.srng_list[ring_id]; + struct hal_srng_params params; + struct sk_buff *skb; + u32 ring_entry_sz; + int len = sizeof(*cmd); + dma_addr_t hp_addr, tp_addr; + enum htt_srng_ring_type htt_ring_type; + enum htt_srng_ring_id htt_ring_id; + int ret; + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + memset(¶ms, 0, sizeof(params)); + ath11k_hal_srng_get_params(ab, srng, ¶ms); + + hp_addr = ath11k_hal_srng_get_hp_addr(ab, srng); + tp_addr = ath11k_hal_srng_get_tp_addr(ab, srng); + + ret = ath11k_dp_tx_get_ring_id_type(ab, mac_id, ring_id, + ring_type, &htt_ring_type, + &htt_ring_id); + if (ret) + goto err_free; + + skb_put(skb, len); + cmd = (struct htt_srng_setup_cmd *)skb->data; + cmd->info0 = FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_MSG_TYPE, + HTT_H2T_MSG_TYPE_SRING_SETUP); + if (htt_ring_type == HTT_SW_TO_HW_RING || + htt_ring_type == HTT_HW_TO_SW_RING) + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID, + DP_SW2HW_MACID(mac_id)); + else + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID, + mac_id); + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_RING_TYPE, + htt_ring_type); + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_RING_ID, htt_ring_id); + + cmd->ring_base_addr_lo = params.ring_base_paddr & + HAL_ADDR_LSB_REG_MASK; + + cmd->ring_base_addr_hi = (u64)params.ring_base_paddr >> + HAL_ADDR_MSB_REG_SHIFT; + + ret = ath11k_hal_srng_get_entrysize(ring_type); + if (ret < 0) + goto err_free; + + ring_entry_sz = ret; + + ring_entry_sz >>= 2; + cmd->info1 = FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_ENTRY_SIZE, + ring_entry_sz); + cmd->info1 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_SIZE, + params.num_entries * ring_entry_sz); + cmd->info1 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_MSI_SWAP, + !!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP)); + cmd->info1 |= FIELD_PREP( + HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_TLV_SWAP, + !!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP)); + cmd->info1 |= FIELD_PREP( + HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_HOST_FW_SWAP, + !!(params.flags & HAL_SRNG_FLAGS_RING_PTR_SWAP)); + if (htt_ring_type == HTT_SW_TO_HW_RING) + cmd->info1 |= HTT_SRNG_SETUP_CMD_INFO1_RING_LOOP_CNT_DIS; + + cmd->ring_head_off32_remote_addr_lo = hp_addr & HAL_ADDR_LSB_REG_MASK; + cmd->ring_head_off32_remote_addr_hi = (u64)hp_addr >> + HAL_ADDR_MSB_REG_SHIFT; + + cmd->ring_tail_off32_remote_addr_lo = tp_addr & HAL_ADDR_LSB_REG_MASK; + cmd->ring_tail_off32_remote_addr_hi = (u64)tp_addr >> + HAL_ADDR_MSB_REG_SHIFT; + + cmd->ring_msi_addr_lo = 0; + cmd->ring_msi_addr_hi = 0; + cmd->msi_data = 0; + + cmd->intr_info = FIELD_PREP( + HTT_SRNG_SETUP_CMD_INTR_INFO_BATCH_COUNTER_THRESH, + params.intr_batch_cntr_thres_entries * ring_entry_sz); + cmd->intr_info |= FIELD_PREP( + HTT_SRNG_SETUP_CMD_INTR_INFO_INTR_TIMER_THRESH, + params.intr_timer_thres_us >> 3); + + cmd->info2 = 0; + if (params.flags & HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN) { + cmd->info2 = FIELD_PREP( + HTT_SRNG_SETUP_CMD_INFO2_INTR_LOW_THRESH, + params.low_threshold); + } + + ret = ath11k_htc_send(&ab->htc, ab->dp.eid, skb); + if (ret) + goto err_free; + + return 0; + +err_free: + dev_kfree_skb_any(skb); + + return ret; +} + +#define HTT_TARGET_VERSION_TIMEOUT_HZ (3 * HZ) + +int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct sk_buff *skb; + struct htt_ver_req_cmd *cmd; + int len = sizeof(*cmd); + int ret; + + init_completion(&dp->htt_tgt_version_received); + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + cmd = (struct htt_ver_req_cmd *)skb->data; + cmd->ver_reg_info = FIELD_PREP(HTT_VER_REQ_INFO_MSG_ID, + HTT_H2T_MSG_TYPE_VERSION_REQ); + + ret = ath11k_htc_send(&ab->htc, dp->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + ret = wait_for_completion_timeout(&dp->htt_tgt_version_received, + HTT_TARGET_VERSION_TIMEOUT_HZ); + if (ret == 0) { + ath11k_warn(ab, "htt target version request timed out\n"); + return -ETIMEDOUT; + } + + if (dp->htt_tgt_ver_major != HTT_TARGET_VERSION_MAJOR) { + ath11k_err(ab, "unsupported htt major version %d supported version is %d\n", + dp->htt_tgt_ver_major, HTT_TARGET_VERSION_MAJOR); + return -ENOTSUPP; + } + + return 0; +} + +int ath11k_dp_tx_htt_h2t_ppdu_stats_req(struct ath11k *ar, u32 mask) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_dp *dp = &ab->dp; + struct sk_buff *skb; + struct htt_ppdu_stats_cfg_cmd *cmd; + int len = sizeof(*cmd); + u8 pdev_mask; + int ret; + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + cmd = (struct htt_ppdu_stats_cfg_cmd *)skb->data; + cmd->msg = FIELD_PREP(HTT_PPDU_STATS_CFG_MSG_TYPE, + HTT_H2T_MSG_TYPE_PPDU_STATS_CFG); + + pdev_mask = 1 << (ar->pdev_idx); + cmd->msg |= FIELD_PREP(HTT_PPDU_STATS_CFG_PDEV_ID, pdev_mask); + cmd->msg |= FIELD_PREP(HTT_PPDU_STATS_CFG_TLV_TYPE_BITMASK, mask); + + ret = ath11k_htc_send(&ab->htc, dp->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath11k_dp_tx_htt_rx_filter_setup(struct ath11k_base *ab, u32 ring_id, + int mac_id, enum hal_ring_type ring_type, + int rx_buf_size, + struct htt_rx_ring_tlv_filter *tlv_filter) +{ + struct htt_rx_ring_selection_cfg_cmd *cmd; + struct hal_srng *srng = &ab->hal.srng_list[ring_id]; + struct hal_srng_params params; + struct sk_buff *skb; + int len = sizeof(*cmd); + enum htt_srng_ring_type htt_ring_type; + enum htt_srng_ring_id htt_ring_id; + int ret; + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + memset(¶ms, 0, sizeof(params)); + ath11k_hal_srng_get_params(ab, srng, ¶ms); + + ret = ath11k_dp_tx_get_ring_id_type(ab, mac_id, ring_id, + ring_type, &htt_ring_type, + &htt_ring_id); + if (ret) + goto err_free; + + skb_put(skb, len); + cmd = (struct htt_rx_ring_selection_cfg_cmd *)skb->data; + cmd->info0 = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE, + HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG); + if (htt_ring_type == HTT_SW_TO_HW_RING || + htt_ring_type == HTT_HW_TO_SW_RING) + cmd->info0 |= + FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID, + DP_SW2HW_MACID(mac_id)); + else + cmd->info0 |= + FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID, + mac_id); + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID, + htt_ring_id); + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS, + !!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP)); + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS, + !!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP)); + + cmd->info1 = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE, + rx_buf_size); + cmd->pkt_type_en_flags0 = tlv_filter->pkt_filter_flags0; + cmd->pkt_type_en_flags1 = tlv_filter->pkt_filter_flags1; + cmd->pkt_type_en_flags2 = tlv_filter->pkt_filter_flags2; + cmd->pkt_type_en_flags3 = tlv_filter->pkt_filter_flags3; + cmd->rx_filter_tlv = tlv_filter->rx_filter; + + ret = ath11k_htc_send(&ab->htc, ab->dp.eid, skb); + if (ret) + goto err_free; + + return 0; + +err_free: + dev_kfree_skb_any(skb); + + return ret; +} + +int +ath11k_dp_tx_htt_h2t_ext_stats_req(struct ath11k *ar, u8 type, + struct htt_ext_stats_cfg_params *cfg_params, + u64 cookie) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_dp *dp = &ab->dp; + struct sk_buff *skb; + struct htt_ext_stats_cfg_cmd *cmd; + int len = sizeof(*cmd); + int ret; + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + + cmd = (struct htt_ext_stats_cfg_cmd *)skb->data; + memset(cmd, 0, sizeof(*cmd)); + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_EXT_STATS_CFG; + + cmd->hdr.pdev_mask = 1 << ar->pdev->pdev_id; + + cmd->hdr.stats_type = type; + cmd->cfg_param0 = cfg_params->cfg0; + cmd->cfg_param1 = cfg_params->cfg1; + cmd->cfg_param2 = cfg_params->cfg2; + cmd->cfg_param3 = cfg_params->cfg3; + cmd->cookie_lsb = lower_32_bits(cookie); + cmd->cookie_msb = upper_32_bits(cookie); + + ret = ath11k_htc_send(&ab->htc, dp->eid, skb); + if (ret) { + ath11k_warn(ab, "failed to send htt type stats request: %d", + ret); + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath11k_dp_tx_htt_monitor_mode_ring_config(struct ath11k *ar, bool reset) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + int ret = 0, ring_id = 0; + + ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; + + if (!reset) { + tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING; + tlv_filter.pkt_filter_flags0 = + HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 | + HTT_RX_MON_MO_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = + HTT_RX_MON_FP_MGMT_FILTER_FLAGS1 | + HTT_RX_MON_MO_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = + HTT_RX_MON_FP_CTRL_FILTER_FLASG2 | + HTT_RX_MON_MO_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = + HTT_RX_MON_FP_CTRL_FILTER_FLASG3 | + HTT_RX_MON_MO_CTRL_FILTER_FLASG3 | + HTT_RX_MON_FP_DATA_FILTER_FLASG3 | + HTT_RX_MON_MO_DATA_FILTER_FLASG3; + } + + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, dp->mac_id, + HAL_RXDMA_MONITOR_BUF, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) + return ret; + + ring_id = dp->rx_mon_status_refill_ring.refill_buf_ring.ring_id; + if (!reset) + tlv_filter.rx_filter = + HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING; + else + tlv_filter = ath11k_mac_mon_status_filter_default; + + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, dp->mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + return ret; +} diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.h b/drivers/net/wireless/ath/ath11k/dp_tx.h new file mode 100644 index 000000000000..f8a9f9c8e444 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp_tx.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_DP_TX_H +#define ATH11K_DP_TX_H + +#include "core.h" +#include "hal_tx.h" + +struct ath11k_dp_htt_wbm_tx_status { + u32 msdu_id; + bool acked; + int ack_rssi; +}; + +int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab); +int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, + struct sk_buff *skb); +void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id); +int ath11k_dp_tx_send_reo_cmd(struct ath11k_base *ab, struct dp_rx_tid *rx_tid, + enum hal_reo_cmd_type type, + struct ath11k_hal_reo_cmd *cmd, + void (*func)(struct ath11k_dp *, void *, + enum hal_reo_cmd_status)); + +int ath11k_dp_tx_htt_h2t_ppdu_stats_req(struct ath11k *ar, u32 mask); +int +ath11k_dp_tx_htt_h2t_ext_stats_req(struct ath11k *ar, u8 type, + struct htt_ext_stats_cfg_params *cfg_params, + u64 cookie); +int ath11k_dp_tx_htt_monitor_mode_ring_config(struct ath11k *ar, bool reset); + +int ath11k_dp_tx_htt_rx_filter_setup(struct ath11k_base *ab, u32 ring_id, + int mac_id, enum hal_ring_type ring_type, + int rx_buf_size, + struct htt_rx_ring_tlv_filter *tlv_filter); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c new file mode 100644 index 000000000000..b58ac11c2747 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal.c @@ -0,0 +1,1124 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#include <linux/dma-mapping.h> +#include "ahb.h" +#include "hal_tx.h" +#include "debug.h" +#include "hal_desc.h" + +static const struct hal_srng_config hw_srng_config[] = { + /* TODO: max_rings can populated by querying HW capabilities */ + { /* REO_DST */ + .start_ring_id = HAL_SRNG_RING_ID_REO2SW1, + .max_rings = 4, + .entry_size = sizeof(struct hal_reo_dest_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_HP, + }, + .reg_size = { + HAL_REO2_RING_BASE_LSB - HAL_REO1_RING_BASE_LSB, + HAL_REO2_RING_HP - HAL_REO1_RING_HP, + }, + .max_size = HAL_REO_REO2SW1_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_EXCEPTION */ + /* Designating REO2TCL ring as exception ring. This ring is + * similar to other REO2SW rings though it is named as REO2TCL. + * Any of theREO2SW rings can be used as exception ring. + */ + .start_ring_id = HAL_SRNG_RING_ID_REO2TCL, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_dest_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_HP, + }, + .max_size = HAL_REO_REO2TCL_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_REINJECT */ + .start_ring_id = HAL_SRNG_RING_ID_SW2REO, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_HP, + }, + .max_size = HAL_REO_SW2REO_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_CMD */ + .start_ring_id = HAL_SRNG_RING_ID_REO_CMD, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_reo_get_queue_stats)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_HP, + }, + .max_size = HAL_REO_CMD_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_REO_STATUS, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_reo_get_queue_stats_status)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO_STATUS_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_HP, + }, + .max_size = HAL_REO_STATUS_RING_BASE_MSB_RING_SIZE, + }, + { /* TCL_DATA */ + .start_ring_id = HAL_SRNG_RING_ID_SW2TCL1, + .max_rings = 3, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_tcl_data_cmd)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP, + }, + .reg_size = { + HAL_TCL2_RING_BASE_LSB - HAL_TCL1_RING_BASE_LSB, + HAL_TCL2_RING_HP - HAL_TCL1_RING_HP, + }, + .max_size = HAL_SW2TCL1_RING_BASE_MSB_RING_SIZE, + }, + { /* TCL_CMD */ + .start_ring_id = HAL_SRNG_RING_ID_SW2TCL_CMD, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_tcl_gse_cmd)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_HP, + }, + .max_size = HAL_SW2TCL1_CMD_RING_BASE_MSB_RING_SIZE, + }, + { /* TCL_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_TCL_STATUS, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_tcl_status_ring)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL_STATUS_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP, + }, + .max_size = HAL_TCL_STATUS_RING_BASE_MSB_RING_SIZE, + }, + { /* CE_SRC */ + .start_ring_id = HAL_SRNG_RING_ID_CE0_SRC, + .max_rings = 12, + .entry_size = sizeof(struct hal_ce_srng_src_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + + HAL_CE_DST_RING_BASE_LSB), + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_HP, + }, + .reg_size = { + (HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG), + (HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG), + }, + .max_size = HAL_CE_SRC_RING_BASE_MSB_RING_SIZE, + }, + { /* CE_DST */ + .start_ring_id = HAL_SRNG_RING_ID_CE0_DST, + .max_rings = 12, + .entry_size = sizeof(struct hal_ce_srng_dest_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_CE0_DST_REG + + HAL_CE_DST_RING_BASE_LSB), + HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_HP, + }, + .reg_size = { + (HAL_SEQ_WCSS_UMAC_CE1_DST_REG - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG), + (HAL_SEQ_WCSS_UMAC_CE1_DST_REG - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG), + }, + .max_size = HAL_CE_DST_RING_BASE_MSB_RING_SIZE, + }, + { /* CE_DST_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_CE0_DST_STATUS, + .max_rings = 12, + .entry_size = sizeof(struct hal_ce_srng_dst_status_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_CE0_DST_REG + + HAL_CE_DST_STATUS_RING_BASE_LSB), + (HAL_SEQ_WCSS_UMAC_CE0_DST_REG + + HAL_CE_DST_STATUS_RING_HP), + }, + .reg_size = { + (HAL_SEQ_WCSS_UMAC_CE1_DST_REG - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG), + (HAL_SEQ_WCSS_UMAC_CE1_DST_REG - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG), + }, + .max_size = HAL_CE_DST_STATUS_RING_BASE_MSB_RING_SIZE, + }, + { /* WBM_IDLE_LINK */ + .start_ring_id = HAL_SRNG_RING_ID_WBM_IDLE_LINK, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_link_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_IDLE_LINK_RING_BASE_LSB), + (HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_HP), + }, + .max_size = HAL_WBM_IDLE_LINK_RING_BASE_MSB_RING_SIZE, + }, + { /* SW2WBM_RELEASE */ + .start_ring_id = HAL_SRNG_RING_ID_WBM_SW_RELEASE, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_release_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_RELEASE_RING_BASE_LSB), + (HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_RELEASE_RING_HP), + }, + .max_size = HAL_SW2WBM_RELEASE_RING_BASE_MSB_RING_SIZE, + }, + { /* WBM2SW_RELEASE */ + .start_ring_id = HAL_SRNG_RING_ID_WBM2SW0_RELEASE, + .max_rings = 4, + .entry_size = sizeof(struct hal_wbm_release_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM0_RELEASE_RING_BASE_LSB), + (HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM0_RELEASE_RING_HP), + }, + .reg_size = { + (HAL_WBM1_RELEASE_RING_BASE_LSB - + HAL_WBM0_RELEASE_RING_BASE_LSB), + (HAL_WBM1_RELEASE_RING_HP - HAL_WBM0_RELEASE_RING_HP), + }, + .max_size = HAL_WBM2SW_RELEASE_RING_BASE_MSB_RING_SIZE, + }, + { /* RXDMA_BUF */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF, + .max_rings = 2, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_DST */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_BUF */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA2_BUF, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_DST */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_DESC */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_DESC, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA DIR BUF */ + .start_ring_id = HAL_SRNG_RING_ID_RXDMA_DIR_BUF, + .max_rings = 1, + .entry_size = 8 >> 2, /* TODO: Define the struct */ + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, +}; + +static int ath11k_hal_alloc_cont_rdp(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + size_t size; + + size = sizeof(u32) * HAL_SRNG_RING_ID_MAX; + hal->rdp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->rdp.paddr, + GFP_KERNEL); + if (!hal->rdp.vaddr) + return -ENOMEM; + + return 0; +} + +static void ath11k_hal_free_cont_rdp(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + size_t size; + + if (!hal->rdp.vaddr) + return; + + size = sizeof(u32) * HAL_SRNG_RING_ID_MAX; + dma_free_coherent(ab->dev, size, + hal->rdp.vaddr, hal->rdp.paddr); + hal->rdp.vaddr = NULL; +} + +static int ath11k_hal_alloc_cont_wrp(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + size_t size; + + size = sizeof(u32) * HAL_SRNG_NUM_LMAC_RINGS; + hal->wrp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->wrp.paddr, + GFP_KERNEL); + if (!hal->wrp.vaddr) + return -ENOMEM; + + return 0; +} + +static void ath11k_hal_free_cont_wrp(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + size_t size; + + if (!hal->wrp.vaddr) + return; + + size = sizeof(u32) * HAL_SRNG_NUM_LMAC_RINGS; + dma_free_coherent(ab->dev, size, + hal->wrp.vaddr, hal->wrp.paddr); + hal->wrp.vaddr = NULL; +} + +static void ath11k_hal_ce_dst_setup(struct ath11k_base *ab, + struct hal_srng *srng, int ring_num) +{ + const struct hal_srng_config *srng_config = &hw_srng_config[HAL_CE_DST]; + u32 addr; + u32 val; + + addr = HAL_CE_DST_RING_CTRL + + srng_config->reg_start[HAL_SRNG_REG_GRP_R0] + + ring_num * srng_config->reg_size[HAL_SRNG_REG_GRP_R0]; + val = ath11k_ahb_read32(ab, addr); + val &= ~HAL_CE_DST_R0_DEST_CTRL_MAX_LEN; + val |= FIELD_PREP(HAL_CE_DST_R0_DEST_CTRL_MAX_LEN, + srng->u.dst_ring.max_buffer_length); + ath11k_ahb_write32(ab, addr, val); +} + +static void ath11k_hal_srng_dst_hw_init(struct ath11k_base *ab, + struct hal_srng *srng) +{ + struct ath11k_hal *hal = &ab->hal; + u32 val; + u64 hp_addr; + u32 reg_base; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + + if (srng->flags & HAL_SRNG_FLAGS_MSI_INTR) { + ath11k_ahb_write32(ab, reg_base + + HAL_REO1_RING_MSI1_BASE_LSB_OFFSET, + (u32)srng->msi_addr); + + val = FIELD_PREP(HAL_REO1_RING_MSI1_BASE_MSB_ADDR, + ((u64)srng->msi_addr >> + HAL_ADDR_MSB_REG_SHIFT)) | + HAL_REO1_RING_MSI1_BASE_MSB_MSI1_ENABLE; + ath11k_ahb_write32(ab, reg_base + + HAL_REO1_RING_MSI1_BASE_MSB_OFFSET, val); + + ath11k_ahb_write32(ab, + reg_base + HAL_REO1_RING_MSI1_DATA_OFFSET, + srng->msi_data); + } + + ath11k_ahb_write32(ab, reg_base, (u32)srng->ring_base_paddr); + + val = FIELD_PREP(HAL_REO1_RING_BASE_MSB_RING_BASE_ADDR_MSB, + ((u64)srng->ring_base_paddr >> + HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_REO1_RING_BASE_MSB_RING_SIZE, + (srng->entry_size * srng->num_entries)); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_BASE_MSB_OFFSET, val); + + val = FIELD_PREP(HAL_REO1_RING_ID_RING_ID, srng->ring_id) | + FIELD_PREP(HAL_REO1_RING_ID_ENTRY_SIZE, srng->entry_size); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_ID_OFFSET, val); + + /* interrupt setup */ + val = FIELD_PREP(HAL_REO1_RING_PRDR_INT_SETUP_INTR_TMR_THOLD, + (srng->intr_timer_thres_us >> 3)); + + val |= FIELD_PREP(HAL_REO1_RING_PRDR_INT_SETUP_BATCH_COUNTER_THOLD, + (srng->intr_batch_cntr_thres_entries * + srng->entry_size)); + + ath11k_ahb_write32(ab, + reg_base + HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET, + val); + + hp_addr = hal->rdp.paddr + + ((unsigned long)srng->u.dst_ring.hp_addr - + (unsigned long)hal->rdp.vaddr); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_HP_ADDR_LSB_OFFSET, + hp_addr & HAL_ADDR_LSB_REG_MASK); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_HP_ADDR_MSB_OFFSET, + hp_addr >> HAL_ADDR_MSB_REG_SHIFT); + + /* Initialize head and tail pointers to indicate ring is empty */ + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; + ath11k_ahb_write32(ab, reg_base, 0); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_TP_OFFSET, 0); + *srng->u.dst_ring.hp_addr = 0; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + val = 0; + if (srng->flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP) + val |= HAL_REO1_RING_MISC_DATA_TLV_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_RING_PTR_SWAP) + val |= HAL_REO1_RING_MISC_HOST_FW_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_MSI_SWAP) + val |= HAL_REO1_RING_MISC_MSI_SWAP; + val |= HAL_REO1_RING_MISC_SRNG_ENABLE; + + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_MISC_OFFSET, val); +} + +static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, + struct hal_srng *srng) +{ + struct ath11k_hal *hal = &ab->hal; + u32 val; + u64 tp_addr; + u32 reg_base; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + + if (srng->flags & HAL_SRNG_FLAGS_MSI_INTR) { + ath11k_ahb_write32(ab, reg_base + + HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET, + (u32)srng->msi_addr); + + val = FIELD_PREP(HAL_TCL1_RING_MSI1_BASE_MSB_ADDR, + ((u64)srng->msi_addr >> + HAL_ADDR_MSB_REG_SHIFT)) | + HAL_TCL1_RING_MSI1_BASE_MSB_MSI1_ENABLE; + ath11k_ahb_write32(ab, reg_base + + HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET, + val); + + ath11k_ahb_write32(ab, reg_base + + HAL_TCL1_RING_MSI1_DATA_OFFSET, + srng->msi_data); + } + + ath11k_ahb_write32(ab, reg_base, (u32)srng->ring_base_paddr); + + val = FIELD_PREP(HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB, + ((u64)srng->ring_base_paddr >> + HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_TCL1_RING_BASE_MSB_RING_SIZE, + (srng->entry_size * srng->num_entries)); + ath11k_ahb_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET, val); + + val = FIELD_PREP(HAL_REO1_RING_ID_ENTRY_SIZE, srng->entry_size); + ath11k_ahb_write32(ab, reg_base + HAL_TCL1_RING_ID_OFFSET, val); + + /* interrupt setup */ + /* NOTE: IPQ8074 v2 requires the interrupt timer threshold in the + * unit of 8 usecs instead of 1 usec (as required by v1). + */ + val = FIELD_PREP(HAL_TCL1_RING_CONSR_INT_SETUP_IX0_INTR_TMR_THOLD, + srng->intr_timer_thres_us); + + val |= FIELD_PREP(HAL_TCL1_RING_CONSR_INT_SETUP_IX0_BATCH_COUNTER_THOLD, + (srng->intr_batch_cntr_thres_entries * + srng->entry_size)); + + ath11k_ahb_write32(ab, + reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET, + val); + + val = 0; + if (srng->flags & HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN) { + val |= FIELD_PREP(HAL_TCL1_RING_CONSR_INT_SETUP_IX1_LOW_THOLD, + srng->u.src_ring.low_threshold); + } + ath11k_ahb_write32(ab, + reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET, + val); + + if (srng->ring_id != HAL_SRNG_RING_ID_WBM_IDLE_LINK) { + tp_addr = hal->rdp.paddr + + ((unsigned long)srng->u.src_ring.tp_addr - + (unsigned long)hal->rdp.vaddr); + ath11k_ahb_write32(ab, + reg_base + HAL_TCL1_RING_TP_ADDR_LSB_OFFSET, + tp_addr & HAL_ADDR_LSB_REG_MASK); + ath11k_ahb_write32(ab, + reg_base + HAL_TCL1_RING_TP_ADDR_MSB_OFFSET, + tp_addr >> HAL_ADDR_MSB_REG_SHIFT); + } + + /* Initialize head and tail pointers to indicate ring is empty */ + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; + ath11k_ahb_write32(ab, reg_base, 0); + ath11k_ahb_write32(ab, reg_base + HAL_TCL1_RING_TP_OFFSET, 0); + *srng->u.src_ring.tp_addr = 0; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + val = 0; + if (srng->flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP) + val |= HAL_TCL1_RING_MISC_DATA_TLV_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_RING_PTR_SWAP) + val |= HAL_TCL1_RING_MISC_HOST_FW_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_MSI_SWAP) + val |= HAL_TCL1_RING_MISC_MSI_SWAP; + + /* Loop count is not used for SRC rings */ + val |= HAL_TCL1_RING_MISC_MSI_LOOPCNT_DISABLE; + + val |= HAL_TCL1_RING_MISC_SRNG_ENABLE; + + ath11k_ahb_write32(ab, reg_base + HAL_TCL1_RING_MISC_OFFSET, val); +} + +static void ath11k_hal_srng_hw_init(struct ath11k_base *ab, + struct hal_srng *srng) +{ + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + ath11k_hal_srng_src_hw_init(ab, srng); + else + ath11k_hal_srng_dst_hw_init(ab, srng); +} + +static int ath11k_hal_srng_get_ring_id(struct ath11k_base *ab, + enum hal_ring_type type, + int ring_num, int mac_id) +{ + const struct hal_srng_config *srng_config = &hw_srng_config[type]; + int ring_id; + + if (ring_num >= srng_config->max_rings) { + ath11k_warn(ab, "invalid ring number :%d\n", ring_num); + return -EINVAL; + } + + ring_id = srng_config->start_ring_id + ring_num; + if (srng_config->lmac_ring) + ring_id += mac_id * HAL_SRNG_RINGS_PER_LMAC; + + if (WARN_ON(ring_id >= HAL_SRNG_RING_ID_MAX)) + return -EINVAL; + + return ring_id; +} + +int ath11k_hal_srng_get_entrysize(u32 ring_type) +{ + const struct hal_srng_config *srng_config; + + if (WARN_ON(ring_type >= HAL_MAX_RING_TYPES)) + return -EINVAL; + + srng_config = &hw_srng_config[ring_type]; + + return (srng_config->entry_size << 2); +} + +int ath11k_hal_srng_get_max_entries(u32 ring_type) +{ + const struct hal_srng_config *srng_config; + + if (WARN_ON(ring_type >= HAL_MAX_RING_TYPES)) + return -EINVAL; + + srng_config = &hw_srng_config[ring_type]; + + return (srng_config->max_size / srng_config->entry_size); +} + +void ath11k_hal_srng_get_params(struct ath11k_base *ab, struct hal_srng *srng, + struct hal_srng_params *params) +{ + params->ring_base_paddr = srng->ring_base_paddr; + params->ring_base_vaddr = srng->ring_base_vaddr; + params->num_entries = srng->num_entries; + params->intr_timer_thres_us = srng->intr_timer_thres_us; + params->intr_batch_cntr_thres_entries = + srng->intr_batch_cntr_thres_entries; + params->low_threshold = srng->u.src_ring.low_threshold; + params->flags = srng->flags; +} + +dma_addr_t ath11k_hal_srng_get_hp_addr(struct ath11k_base *ab, + struct hal_srng *srng) +{ + if (!(srng->flags & HAL_SRNG_FLAGS_LMAC_RING)) + return 0; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + return ab->hal.wrp.paddr + + ((unsigned long)srng->u.src_ring.hp_addr - + (unsigned long)ab->hal.wrp.vaddr); + else + return ab->hal.rdp.paddr + + ((unsigned long)srng->u.dst_ring.hp_addr - + (unsigned long)ab->hal.rdp.vaddr); +} + +dma_addr_t ath11k_hal_srng_get_tp_addr(struct ath11k_base *ab, + struct hal_srng *srng) +{ + if (!(srng->flags & HAL_SRNG_FLAGS_LMAC_RING)) + return 0; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + return ab->hal.rdp.paddr + + ((unsigned long)srng->u.src_ring.tp_addr - + (unsigned long)ab->hal.rdp.vaddr); + else + return ab->hal.wrp.paddr + + ((unsigned long)srng->u.dst_ring.tp_addr - + (unsigned long)ab->hal.wrp.vaddr); +} + +u32 ath11k_hal_ce_get_desc_size(enum hal_ce_desc type) +{ + switch (type) { + case HAL_CE_DESC_SRC: + return sizeof(struct hal_ce_srng_src_desc); + case HAL_CE_DESC_DST: + return sizeof(struct hal_ce_srng_dest_desc); + case HAL_CE_DESC_DST_STATUS: + return sizeof(struct hal_ce_srng_dst_status_desc); + } + + return 0; +} + +void ath11k_hal_ce_src_set_desc(void *buf, dma_addr_t paddr, u32 len, u32 id, + u8 byte_swap_data) +{ + struct hal_ce_srng_src_desc *desc = (struct hal_ce_srng_src_desc *)buf; + + desc->buffer_addr_low = paddr & HAL_ADDR_LSB_REG_MASK; + desc->buffer_addr_info = + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_ADDR_HI, + ((u64)paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_BYTE_SWAP, + byte_swap_data) | + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_GATHER, 0) | + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_LEN, len); + desc->meta_info = FIELD_PREP(HAL_CE_SRC_DESC_META_INFO_DATA, id); +} + +void ath11k_hal_ce_dst_set_desc(void *buf, dma_addr_t paddr) +{ + struct hal_ce_srng_dest_desc *desc = + (struct hal_ce_srng_dest_desc *)buf; + + desc->buffer_addr_low = paddr & HAL_ADDR_LSB_REG_MASK; + desc->buffer_addr_info = + FIELD_PREP(HAL_CE_DEST_DESC_ADDR_INFO_ADDR_HI, + ((u64)paddr >> HAL_ADDR_MSB_REG_SHIFT)); +} + +u32 ath11k_hal_ce_dst_status_get_length(void *buf) +{ + struct hal_ce_srng_dst_status_desc *desc = + (struct hal_ce_srng_dst_status_desc *)buf; + u32 len; + + len = FIELD_GET(HAL_CE_DST_STATUS_DESC_FLAGS_LEN, desc->flags); + desc->flags &= ~HAL_CE_DST_STATUS_DESC_FLAGS_LEN; + + return len; +} + +void ath11k_hal_set_link_desc_addr(struct hal_wbm_link_desc *desc, u32 cookie, + dma_addr_t paddr) +{ + desc->buf_addr_info.info0 = FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + (paddr & HAL_ADDR_LSB_REG_MASK)); + desc->buf_addr_info.info1 = FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, + ((u64)paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, 1) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, cookie); +} + +u32 *ath11k_hal_srng_dst_peek(struct ath11k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + if (srng->u.dst_ring.tp != srng->u.dst_ring.cached_hp) + return (srng->ring_base_vaddr + srng->u.dst_ring.tp); + + return NULL; +} + +u32 *ath11k_hal_srng_dst_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng) +{ + u32 *desc; + + lockdep_assert_held(&srng->lock); + + if (srng->u.dst_ring.tp == srng->u.dst_ring.cached_hp) + return NULL; + + desc = srng->ring_base_vaddr + srng->u.dst_ring.tp; + + srng->u.dst_ring.tp = (srng->u.dst_ring.tp + srng->entry_size) % + srng->ring_size; + + return desc; +} + +int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng, + bool sync_hw_ptr) +{ + u32 tp, hp; + + lockdep_assert_held(&srng->lock); + + tp = srng->u.dst_ring.tp; + + if (sync_hw_ptr) { + hp = *srng->u.dst_ring.hp_addr; + srng->u.dst_ring.cached_hp = hp; + } else { + hp = srng->u.dst_ring.cached_hp; + } + + if (hp >= tp) + return (hp - tp) / srng->entry_size; + else + return (srng->ring_size - tp + hp) / srng->entry_size; +} + +/* Returns number of available entries in src ring */ +int ath11k_hal_srng_src_num_free(struct ath11k_base *ab, struct hal_srng *srng, + bool sync_hw_ptr) +{ + u32 tp, hp; + + lockdep_assert_held(&srng->lock); + + hp = srng->u.src_ring.hp; + + if (sync_hw_ptr) { + tp = *srng->u.src_ring.tp_addr; + srng->u.src_ring.cached_tp = tp; + } else { + tp = srng->u.src_ring.cached_tp; + } + + if (tp > hp) + return ((tp - hp) / srng->entry_size) - 1; + else + return ((srng->ring_size - hp + tp) / srng->entry_size) - 1; +} + +u32 *ath11k_hal_srng_src_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng) +{ + u32 *desc; + u32 next_hp; + + lockdep_assert_held(&srng->lock); + + /* TODO: Using % is expensive, but we have to do this since size of some + * SRNG rings is not power of 2 (due to descriptor sizes). Need to see + * if separate function is defined for rings having power of 2 ring size + * (TCL2SW, REO2SW, SW2RXDMA and CE rings) so that we can avoid the + * overhead of % by using mask (with &). + */ + next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size; + + if (next_hp == srng->u.src_ring.cached_tp) + return NULL; + + desc = srng->ring_base_vaddr + srng->u.src_ring.hp; + srng->u.src_ring.hp = next_hp; + + /* TODO: Reap functionality is not used by all rings. If particular + * ring does not use reap functionality, we need not update reap_hp + * with next_hp pointer. Need to make sure a separate function is used + * before doing any optimization by removing below code updating + * reap_hp. + */ + srng->u.src_ring.reap_hp = next_hp; + + return desc; +} + +u32 *ath11k_hal_srng_src_reap_next(struct ath11k_base *ab, + struct hal_srng *srng) +{ + u32 *desc; + u32 next_reap_hp; + + lockdep_assert_held(&srng->lock); + + next_reap_hp = (srng->u.src_ring.reap_hp + srng->entry_size) % + srng->ring_size; + + if (next_reap_hp == srng->u.src_ring.cached_tp) + return NULL; + + desc = srng->ring_base_vaddr + next_reap_hp; + srng->u.src_ring.reap_hp = next_reap_hp; + + return desc; +} + +u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab, + struct hal_srng *srng) +{ + u32 *desc; + + lockdep_assert_held(&srng->lock); + + if (srng->u.src_ring.hp == srng->u.src_ring.reap_hp) + return NULL; + + desc = srng->ring_base_vaddr + srng->u.src_ring.hp; + srng->u.src_ring.hp = (srng->u.src_ring.hp + srng->entry_size) % + srng->ring_size; + + return desc; +} + +u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + if (((srng->u.src_ring.hp + srng->entry_size) % srng->ring_size) == + srng->u.src_ring.cached_tp) + return NULL; + + return srng->ring_base_vaddr + srng->u.src_ring.hp; +} + +void ath11k_hal_srng_access_begin(struct ath11k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + srng->u.src_ring.cached_tp = + *(volatile u32 *)srng->u.src_ring.tp_addr; + else + srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr; +} + +/* Update cached ring head/tail pointers to HW. ath11k_hal_srng_access_begin() + * should have been called before this. + */ +void ath11k_hal_srng_access_end(struct ath11k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + /* TODO: See if we need a write memory barrier here */ + if (srng->flags & HAL_SRNG_FLAGS_LMAC_RING) { + /* 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) + *srng->u.src_ring.hp_addr = srng->u.src_ring.hp; + else + *srng->u.dst_ring.tp_addr = srng->u.dst_ring.tp; + } else { + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + ath11k_ahb_write32(ab, + (unsigned long)srng->u.src_ring.hp_addr - + (unsigned long)ab->mem, + srng->u.src_ring.hp); + } else { + ath11k_ahb_write32(ab, + (unsigned long)srng->u.dst_ring.tp_addr - + (unsigned long)ab->mem, + srng->u.dst_ring.tp); + } + } +} + +void ath11k_hal_setup_link_idle_list(struct ath11k_base *ab, + struct hal_wbm_idle_scatter_list *sbuf, + u32 nsbufs, u32 tot_link_desc, + u32 end_offset) +{ + struct ath11k_buffer_addr *link_addr; + int i; + u32 reg_scatter_buf_sz = HAL_WBM_IDLE_SCATTER_BUF_SIZE / 64; + + link_addr = (void *)sbuf[0].vaddr + HAL_WBM_IDLE_SCATTER_BUF_SIZE; + + for (i = 1; i < nsbufs; i++) { + link_addr->info0 = sbuf[i].paddr & HAL_ADDR_LSB_REG_MASK; + link_addr->info1 = FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + (u64)sbuf[i].paddr >> HAL_ADDR_MSB_REG_SHIFT) | + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG, + BASE_ADDR_MATCH_TAG_VAL); + + link_addr = (void *)sbuf[i].vaddr + + HAL_WBM_IDLE_SCATTER_BUF_SIZE; + } + + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_R0_IDLE_LIST_CONTROL_ADDR, + FIELD_PREP(HAL_WBM_SCATTER_BUFFER_SIZE, reg_scatter_buf_sz) | + FIELD_PREP(HAL_WBM_LINK_DESC_IDLE_LIST_MODE, 0x1)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_R0_IDLE_LIST_SIZE_ADDR, + FIELD_PREP(HAL_WBM_SCATTER_RING_SIZE_OF_IDLE_LINK_DESC_LIST, + reg_scatter_buf_sz * nsbufs)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_RING_BASE_LSB, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + sbuf[0].paddr & HAL_ADDR_LSB_REG_MASK)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_RING_BASE_MSB, + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + (u64)sbuf[0].paddr >> HAL_ADDR_MSB_REG_SHIFT) | + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG, + BASE_ADDR_MATCH_TAG_VAL)); + + /* Setup head and tail pointers for the idle list */ + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + sbuf[nsbufs - 1].paddr)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX1, + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + ((u64)sbuf[nsbufs - 1].paddr >> + HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_WBM_SCATTERED_DESC_HEAD_P_OFFSET_IX1, + (end_offset >> 2))); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + sbuf[0].paddr)); + + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX0, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + sbuf[0].paddr)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX1, + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + ((u64)sbuf[0].paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_WBM_SCATTERED_DESC_TAIL_P_OFFSET_IX1, + 0)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HP_ADDR, + 2 * tot_link_desc); + + /* Enable the SRNG */ + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_IDLE_LINK_RING_MISC_ADDR, 0x40); +} + +int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, + int ring_num, int mac_id, + struct hal_srng_params *params) +{ + struct ath11k_hal *hal = &ab->hal; + const struct hal_srng_config *srng_config = &hw_srng_config[type]; + struct hal_srng *srng; + int ring_id; + u32 lmac_idx; + int i; + u32 reg_base; + + ring_id = ath11k_hal_srng_get_ring_id(ab, type, ring_num, mac_id); + if (ring_id < 0) + return ring_id; + + srng = &hal->srng_list[ring_id]; + + srng->ring_id = ring_id; + srng->ring_dir = srng_config->ring_dir; + srng->ring_base_paddr = params->ring_base_paddr; + srng->ring_base_vaddr = params->ring_base_vaddr; + srng->entry_size = srng_config->entry_size; + srng->num_entries = params->num_entries; + srng->ring_size = srng->entry_size * srng->num_entries; + srng->intr_batch_cntr_thres_entries = + params->intr_batch_cntr_thres_entries; + srng->intr_timer_thres_us = params->intr_timer_thres_us; + srng->flags = params->flags; + spin_lock_init(&srng->lock); + + for (i = 0; i < HAL_SRNG_NUM_REG_GRP; i++) { + srng->hwreg_base[i] = srng_config->reg_start[i] + + (ring_num * srng_config->reg_size[i]); + } + + memset(srng->ring_base_vaddr, 0, + (srng->entry_size * srng->num_entries) << 2); + + /* TODO: Add comments on these swap configurations */ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + srng->flags |= HAL_SRNG_FLAGS_MSI_SWAP | HAL_SRNG_FLAGS_DATA_TLV_SWAP | + HAL_SRNG_FLAGS_RING_PTR_SWAP; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + srng->u.src_ring.hp = 0; + srng->u.src_ring.cached_tp = 0; + srng->u.src_ring.reap_hp = srng->ring_size - srng->entry_size; + srng->u.src_ring.tp_addr = (void *)(hal->rdp.vaddr + ring_id); + srng->u.src_ring.low_threshold = params->low_threshold * + srng->entry_size; + if (srng_config->lmac_ring) { + lmac_idx = ring_id - HAL_SRNG_RING_ID_LMAC1_ID_START; + srng->u.src_ring.hp_addr = (void *)(hal->wrp.vaddr + + lmac_idx); + srng->flags |= HAL_SRNG_FLAGS_LMAC_RING; + } else { + srng->u.src_ring.hp_addr = + (u32 *)((unsigned long)ab->mem + reg_base); + } + } else { + /* During initialization loop count in all the descriptors + * will be set to zero, and HW will set it to 1 on completing + * descriptor update in first loop, and increments it by 1 on + * subsequent loops (loop count wraps around after reaching + * 0xffff). The 'loop_cnt' in SW ring state is the expected + * loop count in descriptors updated by HW (to be processed + * by SW). + */ + srng->u.dst_ring.loop_cnt = 1; + srng->u.dst_ring.tp = 0; + srng->u.dst_ring.cached_hp = 0; + srng->u.dst_ring.hp_addr = (void *)(hal->rdp.vaddr + ring_id); + if (srng_config->lmac_ring) { + /* For LMAC rings, tail pointer updates will be done + * through FW by writing to a shared memory location + */ + lmac_idx = ring_id - HAL_SRNG_RING_ID_LMAC1_ID_START; + srng->u.dst_ring.tp_addr = (void *)(hal->wrp.vaddr + + lmac_idx); + srng->flags |= HAL_SRNG_FLAGS_LMAC_RING; + } else { + srng->u.dst_ring.tp_addr = + (u32 *)((unsigned long)ab->mem + reg_base + + (HAL_REO1_RING_TP - HAL_REO1_RING_HP)); + } + } + + if (srng_config->lmac_ring) + return ring_id; + + ath11k_hal_srng_hw_init(ab, srng); + + if (type == HAL_CE_DST) { + srng->u.dst_ring.max_buffer_length = params->max_buffer_len; + ath11k_hal_ce_dst_setup(ab, srng, ring_num); + } + + return ring_id; +} + +int ath11k_hal_srng_init(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + int ret; + + memset(hal, 0, sizeof(*hal)); + + hal->srng_config = hw_srng_config; + + ret = ath11k_hal_alloc_cont_rdp(ab); + if (ret) + goto err_hal; + + ret = ath11k_hal_alloc_cont_wrp(ab); + if (ret) + goto err_free_cont_rdp; + + return 0; + +err_free_cont_rdp: + ath11k_hal_free_cont_rdp(ab); + +err_hal: + return ret; +} + +void ath11k_hal_srng_deinit(struct ath11k_base *ab) +{ + ath11k_hal_free_cont_rdp(ab); + ath11k_hal_free_cont_wrp(ab); +} diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h new file mode 100644 index 000000000000..5b13ccdf39e4 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal.h @@ -0,0 +1,897 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HAL_H +#define ATH11K_HAL_H + +#include "hal_desc.h" +#include "rx_desc.h" + +struct ath11k_base; + +#define HAL_LINK_DESC_SIZE (32 << 2) +#define HAL_LINK_DESC_ALIGN 128 +#define HAL_NUM_MPDUS_PER_LINK_DESC 6 +#define HAL_NUM_TX_MSDUS_PER_LINK_DESC 7 +#define HAL_NUM_RX_MSDUS_PER_LINK_DESC 6 +#define HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC 12 +#define HAL_MAX_AVAIL_BLK_RES 3 + +#define HAL_RING_BASE_ALIGN 8 + +#define HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX 32704 +/* TODO: Check with hw team on the supported scatter buf size */ +#define HAL_WBM_IDLE_SCATTER_NEXT_PTR_SIZE 8 +#define HAL_WBM_IDLE_SCATTER_BUF_SIZE (HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX - \ + HAL_WBM_IDLE_SCATTER_NEXT_PTR_SIZE) + +#define HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX 48 +#define HAL_DSCP_TID_TBL_SIZE 24 + +/* calculate the register address from bar0 of shadow register x */ +#define SHADOW_BASE_ADDRESS 0x00003024 +#define SHADOW_NUM_REGISTERS 36 + +/* WCSS Relative address */ +#define HAL_SEQ_WCSS_UMAC_REO_REG 0x00a38000 +#define HAL_SEQ_WCSS_UMAC_TCL_REG 0x00a44000 +#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG 0x00a00000 +#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG 0x00a01000 +#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG 0x00a02000 +#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG 0x00a03000 +#define HAL_SEQ_WCSS_UMAC_WBM_REG 0x00a34000 + +/* SW2TCL(x) R0 ring configuration address */ +#define HAL_TCL1_RING_CMN_CTRL_REG 0x00000014 +#define HAL_TCL1_RING_DSCP_TID_MAP 0x0000002c +#define HAL_TCL1_RING_BASE_LSB 0x00000510 +#define HAL_TCL1_RING_BASE_MSB 0x00000514 +#define HAL_TCL1_RING_ID 0x00000518 +#define HAL_TCL1_RING_MISC 0x00000520 +#define HAL_TCL1_RING_TP_ADDR_LSB 0x0000052c +#define HAL_TCL1_RING_TP_ADDR_MSB 0x00000530 +#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0 0x00000540 +#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1 0x00000544 +#define HAL_TCL1_RING_MSI1_BASE_LSB 0x00000558 +#define HAL_TCL1_RING_MSI1_BASE_MSB 0x0000055c +#define HAL_TCL1_RING_MSI1_DATA 0x00000560 +#define HAL_TCL2_RING_BASE_LSB 0x00000568 +#define HAL_TCL_RING_BASE_LSB 0x00000618 + +#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET \ + (HAL_TCL1_RING_MSI1_BASE_LSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET \ + (HAL_TCL1_RING_MSI1_BASE_MSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_MSI1_DATA_OFFSET \ + (HAL_TCL1_RING_MSI1_DATA - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_BASE_MSB_OFFSET \ + (HAL_TCL1_RING_BASE_MSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_ID_OFFSET \ + (HAL_TCL1_RING_ID - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0 - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1 - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET \ + (HAL_TCL1_RING_TP_ADDR_LSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET \ + (HAL_TCL1_RING_TP_ADDR_MSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_MISC_OFFSET \ + (HAL_TCL1_RING_MISC - HAL_TCL1_RING_BASE_LSB) + +/* SW2TCL(x) R2 ring pointers (head/tail) address */ +#define HAL_TCL1_RING_HP 0x00002000 +#define HAL_TCL1_RING_TP 0x00002004 +#define HAL_TCL2_RING_HP 0x00002008 +#define HAL_TCL_RING_HP 0x00002018 + +#define HAL_TCL1_RING_TP_OFFSET \ + (HAL_TCL1_RING_TP - HAL_TCL1_RING_HP) + +/* TCL STATUS ring address */ +#define HAL_TCL_STATUS_RING_BASE_LSB 0x00000720 +#define HAL_TCL_STATUS_RING_HP 0x00002030 + +/* REO2SW(x) R0 ring configuration address */ +#define HAL_REO1_GEN_ENABLE 0x00000000 +#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 +#define HAL_REO1_RING_BASE_MSB 0x000002a0 +#define HAL_REO1_RING_ID 0x000002a4 +#define HAL_REO1_RING_MISC 0x000002ac +#define HAL_REO1_RING_HP_ADDR_LSB 0x000002b0 +#define HAL_REO1_RING_HP_ADDR_MSB 0x000002b4 +#define HAL_REO1_RING_PRODUCER_INT_SETUP 0x000002c0 +#define HAL_REO1_RING_MSI1_BASE_LSB 0x000002e4 +#define HAL_REO1_RING_MSI1_BASE_MSB 0x000002e8 +#define HAL_REO1_RING_MSI1_DATA 0x000002ec +#define HAL_REO2_RING_BASE_LSB 0x000002f4 +#define HAL_REO1_AGING_THRESH_IX_0 0x00000564 +#define HAL_REO1_AGING_THRESH_IX_1 0x00000568 +#define HAL_REO1_AGING_THRESH_IX_2 0x0000056c +#define HAL_REO1_AGING_THRESH_IX_3 0x00000570 + +#define HAL_REO1_RING_MSI1_BASE_LSB_OFFSET \ + (HAL_REO1_RING_MSI1_BASE_LSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_MSI1_BASE_MSB_OFFSET \ + (HAL_REO1_RING_MSI1_BASE_MSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_MSI1_DATA_OFFSET \ + (HAL_REO1_RING_MSI1_DATA - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_BASE_MSB_OFFSET \ + (HAL_REO1_RING_BASE_MSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_ID_OFFSET (HAL_REO1_RING_ID - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET \ + (HAL_REO1_RING_PRODUCER_INT_SETUP - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_HP_ADDR_LSB_OFFSET \ + (HAL_REO1_RING_HP_ADDR_LSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_HP_ADDR_MSB_OFFSET \ + (HAL_REO1_RING_HP_ADDR_MSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_MISC_OFFSET (HAL_REO1_RING_MISC - HAL_REO1_RING_BASE_LSB) + +/* REO2SW(x) R2 ring pointers (head/tail) address */ +#define HAL_REO1_RING_HP 0x00003038 +#define HAL_REO1_RING_TP 0x0000303c +#define HAL_REO2_RING_HP 0x00003040 + +#define HAL_REO1_RING_TP_OFFSET (HAL_REO1_RING_TP - HAL_REO1_RING_HP) + +/* REO2TCL R0 ring configuration address */ +#define HAL_REO_TCL_RING_BASE_LSB 0x000003fc + +/* REO2TCL R2 ring pointer (head/tail) address */ +#define HAL_REO_TCL_RING_HP 0x00003058 + +/* REO CMD R0 address */ +#define HAL_REO_CMD_RING_BASE_LSB 0x00000194 + +/* REO CMD R2 address */ +#define HAL_REO_CMD_HP 0x00003020 + +/* SW2REO R0 address */ +#define HAL_SW2REO_RING_BASE_LSB 0x000001ec + +/* SW2REO R2 address */ +#define HAL_SW2REO_RING_HP 0x00003028 + +/* CE ring R0 address */ +#define HAL_CE_DST_RING_BASE_LSB 0x00000000 +#define HAL_CE_DST_STATUS_RING_BASE_LSB 0x00000058 +#define HAL_CE_DST_RING_CTRL 0x000000b0 + +/* CE ring R2 address */ +#define HAL_CE_DST_RING_HP 0x00000400 +#define HAL_CE_DST_STATUS_RING_HP 0x00000408 + +/* REO status address */ +#define HAL_REO_STATUS_RING_BASE_LSB 0x00000504 +#define HAL_REO_STATUS_HP 0x00003070 + +/* WBM Idle R0 address */ +#define HAL_WBM_IDLE_LINK_RING_BASE_LSB 0x00000860 +#define HAL_WBM_IDLE_LINK_RING_MISC_ADDR 0x00000870 +#define HAL_WBM_R0_IDLE_LIST_CONTROL_ADDR 0x00000048 +#define HAL_WBM_R0_IDLE_LIST_SIZE_ADDR 0x0000004c +#define HAL_WBM_SCATTERED_RING_BASE_LSB 0x00000058 +#define HAL_WBM_SCATTERED_RING_BASE_MSB 0x0000005c +#define HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0 0x00000068 +#define HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX1 0x0000006c +#define HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX0 0x00000078 +#define HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX1 0x0000007c +#define HAL_WBM_SCATTERED_DESC_PTR_HP_ADDR 0x00000084 + +/* WBM Idle R2 address */ +#define HAL_WBM_IDLE_LINK_RING_HP 0x000030b0 + +/* SW2WBM R0 release address */ +#define HAL_WBM_RELEASE_RING_BASE_LSB 0x000001d8 + +/* SW2WBM R2 release address */ +#define HAL_WBM_RELEASE_RING_HP 0x00003018 + +/* WBM2SW R0 release address */ +#define HAL_WBM0_RELEASE_RING_BASE_LSB 0x00000910 +#define HAL_WBM1_RELEASE_RING_BASE_LSB 0x00000968 + +/* WBM2SW R2 release address */ +#define HAL_WBM0_RELEASE_RING_HP 0x000030c0 +#define HAL_WBM1_RELEASE_RING_HP 0x000030c8 + +/* TCL ring feild mask and offset */ +#define HAL_TCL1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8) +#define HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0) +#define HAL_TCL1_RING_ID_ENTRY_SIZE GENMASK(7, 0) +#define HAL_TCL1_RING_MISC_MSI_LOOPCNT_DISABLE BIT(1) +#define HAL_TCL1_RING_MISC_MSI_SWAP BIT(3) +#define HAL_TCL1_RING_MISC_HOST_FW_SWAP BIT(4) +#define HAL_TCL1_RING_MISC_DATA_TLV_SWAP BIT(5) +#define HAL_TCL1_RING_MISC_SRNG_ENABLE BIT(6) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_INTR_TMR_THOLD GENMASK(31, 16) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_BATCH_COUNTER_THOLD GENMASK(14, 0) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_LOW_THOLD GENMASK(15, 0) +#define HAL_TCL1_RING_MSI1_BASE_MSB_MSI1_ENABLE BIT(8) +#define HAL_TCL1_RING_MSI1_BASE_MSB_ADDR GENMASK(7, 0) +#define HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN BIT(17) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP GENMASK(31, 0) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP0 GENMASK(2, 0) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP1 GENMASK(5, 3) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP2 GENMASK(8, 6) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP3 GENMASK(11, 9) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP4 GENMASK(14, 12) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP5 GENMASK(17, 15) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP6 GENMASK(20, 18) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP7 GENMASK(23, 21) + +/* REO ring feild mask and offset */ +#define HAL_REO1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8) +#define HAL_REO1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0) +#define HAL_REO1_RING_ID_RING_ID GENMASK(15, 8) +#define HAL_REO1_RING_ID_ENTRY_SIZE GENMASK(7, 0) +#define HAL_REO1_RING_MISC_MSI_SWAP BIT(3) +#define HAL_REO1_RING_MISC_HOST_FW_SWAP BIT(4) +#define HAL_REO1_RING_MISC_DATA_TLV_SWAP BIT(5) +#define HAL_REO1_RING_MISC_SRNG_ENABLE BIT(6) +#define HAL_REO1_RING_PRDR_INT_SETUP_INTR_TMR_THOLD GENMASK(31, 16) +#define HAL_REO1_RING_PRDR_INT_SETUP_BATCH_COUNTER_THOLD GENMASK(14, 0) +#define HAL_REO1_RING_MSI1_BASE_MSB_MSI1_ENABLE BIT(8) +#define HAL_REO1_RING_MSI1_BASE_MSB_ADDR GENMASK(7, 0) +#define HAL_REO1_GEN_ENABLE_FRAG_DST_RING GENMASK(25, 23) +#define HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE BIT(2) +#define HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE BIT(3) + +/* CE ring bit field mask and shift */ +#define HAL_CE_DST_R0_DEST_CTRL_MAX_LEN GENMASK(15, 0) + +#define HAL_ADDR_LSB_REG_MASK 0xffffffff + +#define HAL_ADDR_MSB_REG_SHIFT 32 + +/* WBM ring bit field mask and shift */ +#define HAL_WBM_LINK_DESC_IDLE_LIST_MODE BIT(1) +#define HAL_WBM_SCATTER_BUFFER_SIZE GENMASK(10, 2) +#define HAL_WBM_SCATTER_RING_SIZE_OF_IDLE_LINK_DESC_LIST GENMASK(31, 16) +#define HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32 GENMASK(7, 0) +#define HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG GENMASK(31, 8) + +#define HAL_WBM_SCATTERED_DESC_HEAD_P_OFFSET_IX1 GENMASK(20, 8) +#define HAL_WBM_SCATTERED_DESC_TAIL_P_OFFSET_IX1 GENMASK(20, 8) + +#define BASE_ADDR_MATCH_TAG_VAL 0x5 + +#define HAL_REO_REO2SW1_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_REO_REO2TCL_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_REO_SW2REO_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_REO_CMD_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_REO_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_SW2TCL1_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_SW2TCL1_CMD_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_TCL_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_CE_SRC_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_CE_DST_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_CE_DST_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_WBM_IDLE_LINK_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_SW2WBM_RELEASE_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_WBM2SW_RELEASE_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_RXDMA_RING_MAX_SIZE 0x0000ffff + +#define HAL_RX_DESC_SIZE (sizeof(struct hal_rx_desc)) + +/* Add any other errors here and return them in + * ath11k_hal_rx_desc_get_err(). + */ + +enum hal_srng_ring_id { + HAL_SRNG_RING_ID_REO2SW1 = 0, + HAL_SRNG_RING_ID_REO2SW2, + HAL_SRNG_RING_ID_REO2SW3, + HAL_SRNG_RING_ID_REO2SW4, + HAL_SRNG_RING_ID_REO2TCL, + HAL_SRNG_RING_ID_SW2REO, + + HAL_SRNG_RING_ID_REO_CMD = 8, + HAL_SRNG_RING_ID_REO_STATUS, + + HAL_SRNG_RING_ID_SW2TCL1 = 16, + HAL_SRNG_RING_ID_SW2TCL2, + HAL_SRNG_RING_ID_SW2TCL3, + HAL_SRNG_RING_ID_SW2TCL4, + + HAL_SRNG_RING_ID_SW2TCL_CMD = 24, + HAL_SRNG_RING_ID_TCL_STATUS, + + HAL_SRNG_RING_ID_CE0_SRC = 32, + HAL_SRNG_RING_ID_CE1_SRC, + HAL_SRNG_RING_ID_CE2_SRC, + HAL_SRNG_RING_ID_CE3_SRC, + HAL_SRNG_RING_ID_CE4_SRC, + HAL_SRNG_RING_ID_CE5_SRC, + HAL_SRNG_RING_ID_CE6_SRC, + HAL_SRNG_RING_ID_CE7_SRC, + HAL_SRNG_RING_ID_CE8_SRC, + HAL_SRNG_RING_ID_CE9_SRC, + HAL_SRNG_RING_ID_CE10_SRC, + HAL_SRNG_RING_ID_CE11_SRC, + + HAL_SRNG_RING_ID_CE0_DST = 56, + HAL_SRNG_RING_ID_CE1_DST, + HAL_SRNG_RING_ID_CE2_DST, + HAL_SRNG_RING_ID_CE3_DST, + HAL_SRNG_RING_ID_CE4_DST, + HAL_SRNG_RING_ID_CE5_DST, + HAL_SRNG_RING_ID_CE6_DST, + HAL_SRNG_RING_ID_CE7_DST, + HAL_SRNG_RING_ID_CE8_DST, + HAL_SRNG_RING_ID_CE9_DST, + HAL_SRNG_RING_ID_CE10_DST, + HAL_SRNG_RING_ID_CE11_DST, + + HAL_SRNG_RING_ID_CE0_DST_STATUS = 80, + HAL_SRNG_RING_ID_CE1_DST_STATUS, + HAL_SRNG_RING_ID_CE2_DST_STATUS, + HAL_SRNG_RING_ID_CE3_DST_STATUS, + HAL_SRNG_RING_ID_CE4_DST_STATUS, + HAL_SRNG_RING_ID_CE5_DST_STATUS, + HAL_SRNG_RING_ID_CE6_DST_STATUS, + HAL_SRNG_RING_ID_CE7_DST_STATUS, + HAL_SRNG_RING_ID_CE8_DST_STATUS, + HAL_SRNG_RING_ID_CE9_DST_STATUS, + HAL_SRNG_RING_ID_CE10_DST_STATUS, + HAL_SRNG_RING_ID_CE11_DST_STATUS, + + HAL_SRNG_RING_ID_WBM_IDLE_LINK = 104, + HAL_SRNG_RING_ID_WBM_SW_RELEASE, + HAL_SRNG_RING_ID_WBM2SW0_RELEASE, + HAL_SRNG_RING_ID_WBM2SW1_RELEASE, + HAL_SRNG_RING_ID_WBM2SW2_RELEASE, + HAL_SRNG_RING_ID_WBM2SW3_RELEASE, + + HAL_SRNG_RING_ID_UMAC_ID_END = 127, + HAL_SRNG_RING_ID_LMAC1_ID_START, + + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF = HAL_SRNG_RING_ID_LMAC1_ID_START, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_BUF, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA2_BUF, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_STATBUF, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, + HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0, + HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_DESC, + HAL_SRNG_RING_ID_RXDMA_DIR_BUF, + + HAL_SRNG_RING_ID_LMAC1_ID_END = 143 +}; + +/* SRNG registers are split into two groups R0 and R2 */ +#define HAL_SRNG_REG_GRP_R0 0 +#define HAL_SRNG_REG_GRP_R2 1 +#define HAL_SRNG_NUM_REG_GRP 2 + +#define HAL_SRNG_NUM_LMACS 3 +#define HAL_SRNG_REO_EXCEPTION HAL_SRNG_RING_ID_REO2SW1 +#define HAL_SRNG_RINGS_PER_LMAC (HAL_SRNG_RING_ID_LMAC1_ID_END - \ + HAL_SRNG_RING_ID_LMAC1_ID_START) +#define HAL_SRNG_NUM_LMAC_RINGS (HAL_SRNG_NUM_LMACS * HAL_SRNG_RINGS_PER_LMAC) +#define HAL_SRNG_RING_ID_MAX (HAL_SRNG_RING_ID_UMAC_ID_END + \ + HAL_SRNG_NUM_LMAC_RINGS) + +enum hal_ring_type { + HAL_REO_DST, + HAL_REO_EXCEPTION, + HAL_REO_REINJECT, + HAL_REO_CMD, + HAL_REO_STATUS, + HAL_TCL_DATA, + HAL_TCL_CMD, + HAL_TCL_STATUS, + HAL_CE_SRC, + HAL_CE_DST, + HAL_CE_DST_STATUS, + HAL_WBM_IDLE_LINK, + HAL_SW2WBM_RELEASE, + HAL_WBM2SW_RELEASE, + HAL_RXDMA_BUF, + HAL_RXDMA_DST, + HAL_RXDMA_MONITOR_BUF, + HAL_RXDMA_MONITOR_STATUS, + HAL_RXDMA_MONITOR_DST, + HAL_RXDMA_MONITOR_DESC, + HAL_RXDMA_DIR_BUF, + HAL_MAX_RING_TYPES, +}; + +#define HAL_RX_MAX_BA_WINDOW 256 + +#define HAL_DEFAULT_REO_TIMEOUT_USEC (40 * 1000) + +/** + * enum hal_reo_cmd_type: Enum for REO command type + * @CMD_GET_QUEUE_STATS: Get REO queue status/stats + * @CMD_FLUSH_QUEUE: Flush all frames in REO queue + * @CMD_FLUSH_CACHE: Flush descriptor entries in the cache + * @CMD_UNBLOCK_CACHE: Unblock a descriptor's address that was blocked + * earlier with a 'REO_FLUSH_CACHE' command + * @CMD_FLUSH_TIMEOUT_LIST: Flush buffers/descriptors from timeout list + * @CMD_UPDATE_RX_REO_QUEUE: Update REO queue settings + */ +enum hal_reo_cmd_type { + HAL_REO_CMD_GET_QUEUE_STATS = 0, + HAL_REO_CMD_FLUSH_QUEUE = 1, + HAL_REO_CMD_FLUSH_CACHE = 2, + HAL_REO_CMD_UNBLOCK_CACHE = 3, + HAL_REO_CMD_FLUSH_TIMEOUT_LIST = 4, + HAL_REO_CMD_UPDATE_RX_QUEUE = 5, +}; + +/** + * enum hal_reo_cmd_status: Enum for execution status of REO command + * @HAL_REO_CMD_SUCCESS: Command has successfully executed + * @HAL_REO_CMD_BLOCKED: Command could not be executed as the queue + * or cache was blocked + * @HAL_REO_CMD_FAILED: Command execution failed, could be due to + * invalid queue desc + * @HAL_REO_CMD_RESOURCE_BLOCKED: + * @HAL_REO_CMD_DRAIN: + */ +enum hal_reo_cmd_status { + HAL_REO_CMD_SUCCESS = 0, + HAL_REO_CMD_BLOCKED = 1, + HAL_REO_CMD_FAILED = 2, + HAL_REO_CMD_RESOURCE_BLOCKED = 3, + HAL_REO_CMD_DRAIN = 0xff, +}; + +struct hal_wbm_idle_scatter_list { + dma_addr_t paddr; + struct hal_wbm_link_desc *vaddr; +}; + +struct hal_srng_params { + dma_addr_t ring_base_paddr; + u32 *ring_base_vaddr; + int num_entries; + u32 intr_batch_cntr_thres_entries; + u32 intr_timer_thres_us; + u32 flags; + u32 max_buffer_len; + u32 low_threshold; + + /* Add more params as needed */ +}; + +enum hal_srng_dir { + HAL_SRNG_DIR_SRC, + HAL_SRNG_DIR_DST +}; + +/* srng flags */ +#define HAL_SRNG_FLAGS_MSI_SWAP 0x00000008 +#define HAL_SRNG_FLAGS_RING_PTR_SWAP 0x00000010 +#define HAL_SRNG_FLAGS_DATA_TLV_SWAP 0x00000020 +#define HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN 0x00010000 +#define HAL_SRNG_FLAGS_MSI_INTR 0x00020000 +#define HAL_SRNG_FLAGS_LMAC_RING 0x80000000 + +#define HAL_SRNG_TLV_HDR_TAG GENMASK(9, 1) +#define HAL_SRNG_TLV_HDR_LEN GENMASK(25, 10) + +/* Common SRNG ring structure for source and destination rings */ +struct hal_srng { + /* Unique SRNG ring ID */ + u8 ring_id; + + /* Ring initialization done */ + u8 initialized; + + /* Interrupt/MSI value assigned to this ring */ + int irq; + + /* Physical base address of the ring */ + dma_addr_t ring_base_paddr; + + /* Virtual base address of the ring */ + u32 *ring_base_vaddr; + + /* Number of entries in ring */ + u32 num_entries; + + /* Ring size */ + u32 ring_size; + + /* Ring size mask */ + u32 ring_size_mask; + + /* Size of ring entry */ + u32 entry_size; + + /* Interrupt timer threshold - in micro seconds */ + u32 intr_timer_thres_us; + + /* Interrupt batch counter threshold - in number of ring entries */ + u32 intr_batch_cntr_thres_entries; + + /* MSI Address */ + dma_addr_t msi_addr; + + /* MSI data */ + u32 msi_data; + + /* Misc flags */ + u32 flags; + + /* Lock for serializing ring index updates */ + spinlock_t lock; + + /* Start offset of SRNG register groups for this ring + * TBD: See if this is required - register address can be derived + * from ring ID + */ + u32 hwreg_base[HAL_SRNG_NUM_REG_GRP]; + + /* Source or Destination ring */ + enum hal_srng_dir ring_dir; + + union { + struct { + /* SW tail pointer */ + u32 tp; + + /* Shadow head pointer location to be updated by HW */ + volatile u32 *hp_addr; + + /* Cached head pointer */ + u32 cached_hp; + + /* Tail pointer location to be updated by SW - This + * will be a register address and need not be + * accessed through SW structure + */ + u32 *tp_addr; + + /* Current SW loop cnt */ + u32 loop_cnt; + + /* max transfer size */ + u16 max_buffer_length; + } dst_ring; + + struct { + /* SW head pointer */ + u32 hp; + + /* SW reap head pointer */ + u32 reap_hp; + + /* Shadow tail pointer location to be updated by HW */ + u32 *tp_addr; + + /* Cached tail pointer */ + u32 cached_tp; + + /* Head pointer location to be updated by SW - This + * will be a register address and need not be accessed + * through SW structure + */ + u32 *hp_addr; + + /* Low threshold - in number of ring entries */ + u32 low_threshold; + } src_ring; + } u; +}; + +/* Interrupt mitigation - Batch threshold in terms of numer of frames */ +#define HAL_SRNG_INT_BATCH_THRESHOLD_TX 256 +#define HAL_SRNG_INT_BATCH_THRESHOLD_RX 128 +#define HAL_SRNG_INT_BATCH_THRESHOLD_OTHER 1 + +/* Interrupt mitigation - timer threshold in us */ +#define HAL_SRNG_INT_TIMER_THRESHOLD_TX 1000 +#define HAL_SRNG_INT_TIMER_THRESHOLD_RX 500 +#define HAL_SRNG_INT_TIMER_THRESHOLD_OTHER 1000 + +/* HW SRNG configuration table */ +struct hal_srng_config { + int start_ring_id; + u16 max_rings; + u16 entry_size; + u32 reg_start[HAL_SRNG_NUM_REG_GRP]; + u16 reg_size[HAL_SRNG_NUM_REG_GRP]; + u8 lmac_ring; + enum hal_srng_dir ring_dir; + u32 max_size; +}; + +/** + * enum hal_rx_buf_return_buf_manager + * + * @HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST: Buffer returned to WBM idle buffer list + * @HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST: Descriptor returned to WBM idle + * descriptor list. + * @HAL_RX_BUF_RBM_FW_BM: Buffer returned to FW + * @HAL_RX_BUF_RBM_SW0_BM: For Tx completion -- returned to host + * @HAL_RX_BUF_RBM_SW1_BM: For Tx completion -- returned to host + * @HAL_RX_BUF_RBM_SW2_BM: For Tx completion -- returned to host + * @HAL_RX_BUF_RBM_SW3_BM: For Rx release -- returned to host + */ + +enum hal_rx_buf_return_buf_manager { + HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST, + HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST, + HAL_RX_BUF_RBM_FW_BM, + HAL_RX_BUF_RBM_SW0_BM, + HAL_RX_BUF_RBM_SW1_BM, + HAL_RX_BUF_RBM_SW2_BM, + HAL_RX_BUF_RBM_SW3_BM, +}; + +#define HAL_SRNG_DESC_LOOP_CNT 0xf0000000 + +#define HAL_REO_CMD_FLG_NEED_STATUS BIT(0) +#define HAL_REO_CMD_FLG_STATS_CLEAR BIT(1) +#define HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER BIT(2) +#define HAL_REO_CMD_FLG_FLUSH_RELEASE_BLOCKING BIT(3) +#define HAL_REO_CMD_FLG_FLUSH_NO_INVAL BIT(4) +#define HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS BIT(5) +#define HAL_REO_CMD_FLG_FLUSH_ALL BIT(6) +#define HAL_REO_CMD_FLG_UNBLK_RESOURCE BIT(7) +#define HAL_REO_CMD_FLG_UNBLK_CACHE BIT(8) + +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO0_UPD_* feilds */ +#define HAL_REO_CMD_UPD0_RX_QUEUE_NUM BIT(8) +#define HAL_REO_CMD_UPD0_VLD BIT(9) +#define HAL_REO_CMD_UPD0_ALDC BIT(10) +#define HAL_REO_CMD_UPD0_DIS_DUP_DETECTION BIT(11) +#define HAL_REO_CMD_UPD0_SOFT_REORDER_EN BIT(12) +#define HAL_REO_CMD_UPD0_AC BIT(13) +#define HAL_REO_CMD_UPD0_BAR BIT(14) +#define HAL_REO_CMD_UPD0_RETRY BIT(15) +#define HAL_REO_CMD_UPD0_CHECK_2K_MODE BIT(16) +#define HAL_REO_CMD_UPD0_OOR_MODE BIT(17) +#define HAL_REO_CMD_UPD0_BA_WINDOW_SIZE BIT(18) +#define HAL_REO_CMD_UPD0_PN_CHECK BIT(19) +#define HAL_REO_CMD_UPD0_EVEN_PN BIT(20) +#define HAL_REO_CMD_UPD0_UNEVEN_PN BIT(21) +#define HAL_REO_CMD_UPD0_PN_HANDLE_ENABLE BIT(22) +#define HAL_REO_CMD_UPD0_PN_SIZE BIT(23) +#define HAL_REO_CMD_UPD0_IGNORE_AMPDU_FLG BIT(24) +#define HAL_REO_CMD_UPD0_SVLD BIT(25) +#define HAL_REO_CMD_UPD0_SSN BIT(26) +#define HAL_REO_CMD_UPD0_SEQ_2K_ERR BIT(27) +#define HAL_REO_CMD_UPD0_PN_ERR BIT(28) +#define HAL_REO_CMD_UPD0_PN_VALID BIT(29) +#define HAL_REO_CMD_UPD0_PN BIT(30) + +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO1_* feilds */ +#define HAL_REO_CMD_UPD1_VLD BIT(16) +#define HAL_REO_CMD_UPD1_ALDC GENMASK(18, 17) +#define HAL_REO_CMD_UPD1_DIS_DUP_DETECTION BIT(19) +#define HAL_REO_CMD_UPD1_SOFT_REORDER_EN BIT(20) +#define HAL_REO_CMD_UPD1_AC GENMASK(22, 21) +#define HAL_REO_CMD_UPD1_BAR BIT(23) +#define HAL_REO_CMD_UPD1_RETRY BIT(24) +#define HAL_REO_CMD_UPD1_CHECK_2K_MODE BIT(25) +#define HAL_REO_CMD_UPD1_OOR_MODE BIT(26) +#define HAL_REO_CMD_UPD1_PN_CHECK BIT(27) +#define HAL_REO_CMD_UPD1_EVEN_PN BIT(28) +#define HAL_REO_CMD_UPD1_UNEVEN_PN BIT(29) +#define HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE BIT(30) +#define HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG BIT(31) + +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO2_* feilds */ +#define HAL_REO_CMD_UPD2_SVLD BIT(10) +#define HAL_REO_CMD_UPD2_SSN GENMASK(22, 11) +#define HAL_REO_CMD_UPD2_SEQ_2K_ERR BIT(23) +#define HAL_REO_CMD_UPD2_PN_ERR BIT(24) + +#define HAL_REO_DEST_RING_CTRL_HASH_RING_MAP GENMASK(31, 8) + +struct ath11k_hal_reo_cmd { + u32 addr_lo; + u32 flag; + u32 upd0; + u32 upd1; + u32 upd2; + u32 pn[4]; + u16 rx_queue_num; + u16 min_rel; + u16 min_fwd; + u8 addr_hi; + u8 ac_list; + u8 blocking_idx; + u16 ba_window_size; + u8 pn_size; +}; + +enum hal_pn_type { + HAL_PN_TYPE_NONE, + HAL_PN_TYPE_WPA, + HAL_PN_TYPE_WAPI_EVEN, + HAL_PN_TYPE_WAPI_UNEVEN, +}; + +enum hal_ce_desc { + HAL_CE_DESC_SRC, + HAL_CE_DESC_DST, + HAL_CE_DESC_DST_STATUS, +}; + +struct hal_reo_status_header { + u16 cmd_num; + enum hal_reo_cmd_status cmd_status; + u16 cmd_exe_time; + u32 timestamp; +}; + +struct hal_reo_status_queue_stats { + u16 ssn; + u16 curr_idx; + u32 pn[4]; + u32 last_rx_queue_ts; + u32 last_rx_dequeue_ts; + u32 rx_bitmap[8]; /* Bitmap from 0-255 */ + u32 curr_mpdu_cnt; + u32 curr_msdu_cnt; + u16 fwd_due_to_bar_cnt; + u16 dup_cnt; + u32 frames_in_order_cnt; + u32 num_mpdu_processed_cnt; + u32 num_msdu_processed_cnt; + u32 total_num_processed_byte_cnt; + u32 late_rx_mpdu_cnt; + u32 reorder_hole_cnt; + u8 timeout_cnt; + u8 bar_rx_cnt; + u8 num_window_2k_jump_cnt; +}; + +struct hal_reo_status_flush_queue { + bool err_detected; +}; + +enum hal_reo_status_flush_cache_err_code { + HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_SUCCESS, + HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_IN_USE, + HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_NOT_FOUND, +}; + +struct hal_reo_status_flush_cache { + bool err_detected; + enum hal_reo_status_flush_cache_err_code err_code; + bool cache_controller_flush_status_hit; + u8 cache_controller_flush_status_desc_type; + u8 cache_controller_flush_status_client_id; + u8 cache_controller_flush_status_err; + u8 cache_controller_flush_status_cnt; +}; + +enum hal_reo_status_unblock_cache_type { + HAL_REO_STATUS_UNBLOCK_BLOCKING_RESOURCE, + HAL_REO_STATUS_UNBLOCK_ENTIRE_CACHE_USAGE, +}; + +struct hal_reo_status_unblock_cache { + bool err_detected; + enum hal_reo_status_unblock_cache_type unblock_type; +}; + +struct hal_reo_status_flush_timeout_list { + bool err_detected; + bool list_empty; + u16 release_desc_cnt; + u16 fwd_buf_cnt; +}; + +enum hal_reo_threshold_idx { + HAL_REO_THRESHOLD_IDX_DESC_COUNTER0, + HAL_REO_THRESHOLD_IDX_DESC_COUNTER1, + HAL_REO_THRESHOLD_IDX_DESC_COUNTER2, + HAL_REO_THRESHOLD_IDX_DESC_COUNTER_SUM, +}; + +struct hal_reo_status_desc_thresh_reached { + enum hal_reo_threshold_idx threshold_idx; + u32 link_desc_counter0; + u32 link_desc_counter1; + u32 link_desc_counter2; + u32 link_desc_counter_sum; +}; + +struct hal_reo_status { + struct hal_reo_status_header uniform_hdr; + u8 loop_cnt; + union { + struct hal_reo_status_queue_stats queue_stats; + struct hal_reo_status_flush_queue flush_queue; + struct hal_reo_status_flush_cache flush_cache; + struct hal_reo_status_unblock_cache unblock_cache; + struct hal_reo_status_flush_timeout_list timeout_list; + struct hal_reo_status_desc_thresh_reached desc_thresh_reached; + } u; +}; + +/** + * HAL context to be used to access SRNG APIs (currently used by data path + * and transport (CE) modules) + */ +struct ath11k_hal { + /* HAL internal state for all SRNG rings. + */ + struct hal_srng srng_list[HAL_SRNG_RING_ID_MAX]; + + /* SRNG configuration table */ + const struct hal_srng_config *srng_config; + + /* Remote pointer memory for HW/FW updates */ + struct { + u32 *vaddr; + dma_addr_t paddr; + } rdp; + + /* Shared memory for ring pointer updates from host to FW */ + struct { + u32 *vaddr; + dma_addr_t paddr; + } wrp; + + /* Available REO blocking resources bitmap */ + u8 avail_blk_resource; + + u8 current_blk_index; + + /* shadow register configuration */ + u32 shadow_reg_addr[SHADOW_NUM_REGISTERS]; + int num_shadow_reg_configured; +}; + +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); +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_setup_link_idle_list(struct ath11k_base *ab, + struct hal_wbm_idle_scatter_list *sbuf, + u32 nsbufs, u32 tot_link_desc, + u32 end_offset); + +dma_addr_t ath11k_hal_srng_get_tp_addr(struct ath11k_base *ab, + struct hal_srng *srng); +dma_addr_t ath11k_hal_srng_get_hp_addr(struct ath11k_base *ab, + struct hal_srng *srng); +void ath11k_hal_set_link_desc_addr(struct hal_wbm_link_desc *desc, u32 cookie, + dma_addr_t paddr); +u32 ath11k_hal_ce_get_desc_size(enum hal_ce_desc type); +void ath11k_hal_ce_src_set_desc(void *buf, dma_addr_t paddr, u32 len, u32 id, + u8 byte_swap_data); +void ath11k_hal_ce_dst_set_desc(void *buf, dma_addr_t paddr); +u32 ath11k_hal_ce_dst_status_get_length(void *buf); +int ath11k_hal_srng_get_entrysize(u32 ring_type); +int ath11k_hal_srng_get_max_entries(u32 ring_type); +void ath11k_hal_srng_get_params(struct ath11k_base *ab, struct hal_srng *srng, + struct hal_srng_params *params); +u32 *ath11k_hal_srng_dst_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng); +u32 *ath11k_hal_srng_dst_peek(struct ath11k_base *ab, struct hal_srng *srng); +int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng, + bool sync_hw_ptr); +u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng); +u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab, + struct hal_srng *srng); +u32 *ath11k_hal_srng_src_reap_next(struct ath11k_base *ab, + struct hal_srng *srng); +u32 *ath11k_hal_srng_src_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng); +int ath11k_hal_srng_src_num_free(struct ath11k_base *ab, struct hal_srng *srng, + bool sync_hw_ptr); +void ath11k_hal_srng_access_begin(struct ath11k_base *ab, + struct hal_srng *srng); +void ath11k_hal_srng_access_end(struct ath11k_base *ab, struct hal_srng *srng); +int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, + int ring_num, int mac_id, + struct hal_srng_params *params); +int ath11k_hal_srng_init(struct ath11k_base *ath11k); +void ath11k_hal_srng_deinit(struct ath11k_base *ath11k); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/hal_desc.h b/drivers/net/wireless/ath/ath11k/hal_desc.h new file mode 100644 index 000000000000..5e200380cca4 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_desc.h @@ -0,0 +1,2468 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#ifndef ATH11K_HAL_DESC_H +#define ATH11K_HAL_DESC_H + +#define BUFFER_ADDR_INFO0_ADDR GENMASK(31, 0) + +#define BUFFER_ADDR_INFO1_ADDR GENMASK(7, 0) +#define BUFFER_ADDR_INFO1_RET_BUF_MGR GENMASK(10, 8) +#define BUFFER_ADDR_INFO1_SW_COOKIE GENMASK(31, 11) + +struct ath11k_buffer_addr { + u32 info0; + u32 info1; +} __packed; + +/* ath11k_buffer_addr + * + * info0 + * Address (lower 32 bits) of the msdu buffer or msdu extension + * descriptor or Link descriptor + * + * addr + * Address (upper 8 bits) of the msdu buffer or msdu extension + * descriptor or Link descriptor + * + * return_buffer_manager (RBM) + * Consumer: WBM + * Producer: SW/FW + * Indicates to which buffer manager the buffer or MSDU_EXTENSION + * descriptor or link descriptor that is being pointed to shall be + * returned after the frame has been processed. It is used by WBM + * for routing purposes. + * + * Values are defined in enum %HAL_RX_BUF_RBM_ + * + * sw_buffer_cookie + * Cookie field exclusively used by SW. HW ignores the contents, + * accept that it passes the programmed value on to other + * descriptors together with the physical address. + * + * Field can be used by SW to for example associate the buffers + * physical address with the virtual address. + */ + +enum hal_tlv_tag { + HAL_MACTX_CBF_START = 0 /* 0x0 */, + HAL_PHYRX_DATA = 1 /* 0x1 */, + HAL_PHYRX_CBF_DATA_RESP = 2 /* 0x2 */, + HAL_PHYRX_ABORT_REQUEST = 3 /* 0x3 */, + HAL_PHYRX_USER_ABORT_NOTIFICATION = 4 /* 0x4 */, + HAL_MACTX_DATA_RESP = 5 /* 0x5 */, + HAL_MACTX_CBF_DATA = 6 /* 0x6 */, + HAL_MACTX_CBF_DONE = 7 /* 0x7 */, + HAL_MACRX_CBF_READ_REQUEST = 8 /* 0x8 */, + HAL_MACRX_CBF_DATA_REQUEST = 9 /* 0x9 */, + HAL_MACRX_EXPECT_NDP_RECEPTION = 10 /* 0xa */, + HAL_MACRX_FREEZE_CAPTURE_CHANNEL = 11 /* 0xb */, + HAL_MACRX_NDP_TIMEOUT = 12 /* 0xc */, + HAL_MACRX_ABORT_ACK = 13 /* 0xd */, + HAL_MACRX_REQ_IMPLICIT_FB = 14 /* 0xe */, + HAL_MACRX_CHAIN_MASK = 15 /* 0xf */, + HAL_MACRX_NAP_USER = 16 /* 0x10 */, + HAL_MACRX_ABORT_REQUEST = 17 /* 0x11 */, + HAL_PHYTX_OTHER_TRANSMIT_INFO16 = 18 /* 0x12 */, + HAL_PHYTX_ABORT_ACK = 19 /* 0x13 */, + HAL_PHYTX_ABORT_REQUEST = 20 /* 0x14 */, + HAL_PHYTX_PKT_END = 21 /* 0x15 */, + HAL_PHYTX_PPDU_HEADER_INFO_REQUEST = 22 /* 0x16 */, + HAL_PHYTX_REQUEST_CTRL_INFO = 23 /* 0x17 */, + HAL_PHYTX_DATA_REQUEST = 24 /* 0x18 */, + HAL_PHYTX_BF_CV_LOADING_DONE = 25 /* 0x19 */, + HAL_PHYTX_NAP_ACK = 26 /* 0x1a */, + HAL_PHYTX_NAP_DONE = 27 /* 0x1b */, + HAL_PHYTX_OFF_ACK = 28 /* 0x1c */, + HAL_PHYTX_ON_ACK = 29 /* 0x1d */, + HAL_PHYTX_SYNTH_OFF_ACK = 30 /* 0x1e */, + HAL_PHYTX_DEBUG16 = 31 /* 0x1f */, + HAL_MACTX_ABORT_REQUEST = 32 /* 0x20 */, + HAL_MACTX_ABORT_ACK = 33 /* 0x21 */, + HAL_MACTX_PKT_END = 34 /* 0x22 */, + HAL_MACTX_PRE_PHY_DESC = 35 /* 0x23 */, + HAL_MACTX_BF_PARAMS_COMMON = 36 /* 0x24 */, + HAL_MACTX_BF_PARAMS_PER_USER = 37 /* 0x25 */, + HAL_MACTX_PREFETCH_CV = 38 /* 0x26 */, + HAL_MACTX_USER_DESC_COMMON = 39 /* 0x27 */, + HAL_MACTX_USER_DESC_PER_USER = 40 /* 0x28 */, + HAL_EXAMPLE_USER_TLV_16 = 41 /* 0x29 */, + HAL_EXAMPLE_TLV_16 = 42 /* 0x2a */, + HAL_MACTX_PHY_OFF = 43 /* 0x2b */, + HAL_MACTX_PHY_ON = 44 /* 0x2c */, + HAL_MACTX_SYNTH_OFF = 45 /* 0x2d */, + HAL_MACTX_EXPECT_CBF_COMMON = 46 /* 0x2e */, + HAL_MACTX_EXPECT_CBF_PER_USER = 47 /* 0x2f */, + HAL_MACTX_PHY_DESC = 48 /* 0x30 */, + HAL_MACTX_L_SIG_A = 49 /* 0x31 */, + HAL_MACTX_L_SIG_B = 50 /* 0x32 */, + HAL_MACTX_HT_SIG = 51 /* 0x33 */, + HAL_MACTX_VHT_SIG_A = 52 /* 0x34 */, + HAL_MACTX_VHT_SIG_B_SU20 = 53 /* 0x35 */, + HAL_MACTX_VHT_SIG_B_SU40 = 54 /* 0x36 */, + HAL_MACTX_VHT_SIG_B_SU80 = 55 /* 0x37 */, + HAL_MACTX_VHT_SIG_B_SU160 = 56 /* 0x38 */, + HAL_MACTX_VHT_SIG_B_MU20 = 57 /* 0x39 */, + HAL_MACTX_VHT_SIG_B_MU40 = 58 /* 0x3a */, + HAL_MACTX_VHT_SIG_B_MU80 = 59 /* 0x3b */, + HAL_MACTX_VHT_SIG_B_MU160 = 60 /* 0x3c */, + HAL_MACTX_SERVICE = 61 /* 0x3d */, + HAL_MACTX_HE_SIG_A_SU = 62 /* 0x3e */, + HAL_MACTX_HE_SIG_A_MU_DL = 63 /* 0x3f */, + HAL_MACTX_HE_SIG_A_MU_UL = 64 /* 0x40 */, + HAL_MACTX_HE_SIG_B1_MU = 65 /* 0x41 */, + HAL_MACTX_HE_SIG_B2_MU = 66 /* 0x42 */, + HAL_MACTX_HE_SIG_B2_OFDMA = 67 /* 0x43 */, + HAL_MACTX_DELETE_CV = 68 /* 0x44 */, + HAL_MACTX_MU_UPLINK_COMMON = 69 /* 0x45 */, + HAL_MACTX_MU_UPLINK_USER_SETUP = 70 /* 0x46 */, + HAL_MACTX_OTHER_TRANSMIT_INFO = 71 /* 0x47 */, + HAL_MACTX_PHY_NAP = 72 /* 0x48 */, + HAL_MACTX_DEBUG = 73 /* 0x49 */, + HAL_PHYRX_ABORT_ACK = 74 /* 0x4a */, + HAL_PHYRX_GENERATED_CBF_DETAILS = 75 /* 0x4b */, + HAL_PHYRX_RSSI_LEGACY = 76 /* 0x4c */, + HAL_PHYRX_RSSI_HT = 77 /* 0x4d */, + HAL_PHYRX_USER_INFO = 78 /* 0x4e */, + HAL_PHYRX_PKT_END = 79 /* 0x4f */, + HAL_PHYRX_DEBUG = 80 /* 0x50 */, + HAL_PHYRX_CBF_TRANSFER_DONE = 81 /* 0x51 */, + HAL_PHYRX_CBF_TRANSFER_ABORT = 82 /* 0x52 */, + HAL_PHYRX_L_SIG_A = 83 /* 0x53 */, + HAL_PHYRX_L_SIG_B = 84 /* 0x54 */, + HAL_PHYRX_HT_SIG = 85 /* 0x55 */, + HAL_PHYRX_VHT_SIG_A = 86 /* 0x56 */, + HAL_PHYRX_VHT_SIG_B_SU20 = 87 /* 0x57 */, + HAL_PHYRX_VHT_SIG_B_SU40 = 88 /* 0x58 */, + HAL_PHYRX_VHT_SIG_B_SU80 = 89 /* 0x59 */, + HAL_PHYRX_VHT_SIG_B_SU160 = 90 /* 0x5a */, + HAL_PHYRX_VHT_SIG_B_MU20 = 91 /* 0x5b */, + HAL_PHYRX_VHT_SIG_B_MU40 = 92 /* 0x5c */, + HAL_PHYRX_VHT_SIG_B_MU80 = 93 /* 0x5d */, + HAL_PHYRX_VHT_SIG_B_MU160 = 94 /* 0x5e */, + HAL_PHYRX_HE_SIG_A_SU = 95 /* 0x5f */, + HAL_PHYRX_HE_SIG_A_MU_DL = 96 /* 0x60 */, + HAL_PHYRX_HE_SIG_A_MU_UL = 97 /* 0x61 */, + HAL_PHYRX_HE_SIG_B1_MU = 98 /* 0x62 */, + HAL_PHYRX_HE_SIG_B2_MU = 99 /* 0x63 */, + HAL_PHYRX_HE_SIG_B2_OFDMA = 100 /* 0x64 */, + HAL_PHYRX_OTHER_RECEIVE_INFO = 101 /* 0x65 */, + HAL_PHYRX_COMMON_USER_INFO = 102 /* 0x66 */, + HAL_PHYRX_DATA_DONE = 103 /* 0x67 */, + HAL_RECEIVE_RSSI_INFO = 104 /* 0x68 */, + HAL_RECEIVE_USER_INFO = 105 /* 0x69 */, + HAL_MIMO_CONTROL_INFO = 106 /* 0x6a */, + HAL_RX_LOCATION_INFO = 107 /* 0x6b */, + HAL_COEX_TX_REQ = 108 /* 0x6c */, + HAL_DUMMY = 109 /* 0x6d */, + HAL_RX_TIMING_OFFSET_INFO = 110 /* 0x6e */, + HAL_EXAMPLE_TLV_32_NAME = 111 /* 0x6f */, + HAL_MPDU_LIMIT = 112 /* 0x70 */, + HAL_NA_LENGTH_END = 113 /* 0x71 */, + HAL_OLE_BUF_STATUS = 114 /* 0x72 */, + HAL_PCU_PPDU_SETUP_DONE = 115 /* 0x73 */, + HAL_PCU_PPDU_SETUP_END = 116 /* 0x74 */, + HAL_PCU_PPDU_SETUP_INIT = 117 /* 0x75 */, + HAL_PCU_PPDU_SETUP_START = 118 /* 0x76 */, + HAL_PDG_FES_SETUP = 119 /* 0x77 */, + HAL_PDG_RESPONSE = 120 /* 0x78 */, + HAL_PDG_TX_REQ = 121 /* 0x79 */, + HAL_SCH_WAIT_INSTR = 122 /* 0x7a */, + HAL_SCHEDULER_TLV = 123 /* 0x7b */, + HAL_TQM_FLOW_EMPTY_STATUS = 124 /* 0x7c */, + HAL_TQM_FLOW_NOT_EMPTY_STATUS = 125 /* 0x7d */, + HAL_TQM_GEN_MPDU_LENGTH_LIST = 126 /* 0x7e */, + HAL_TQM_GEN_MPDU_LENGTH_LIST_STATUS = 127 /* 0x7f */, + HAL_TQM_GEN_MPDUS = 128 /* 0x80 */, + HAL_TQM_GEN_MPDUS_STATUS = 129 /* 0x81 */, + HAL_TQM_REMOVE_MPDU = 130 /* 0x82 */, + HAL_TQM_REMOVE_MPDU_STATUS = 131 /* 0x83 */, + HAL_TQM_REMOVE_MSDU = 132 /* 0x84 */, + HAL_TQM_REMOVE_MSDU_STATUS = 133 /* 0x85 */, + HAL_TQM_UPDATE_TX_MPDU_COUNT = 134 /* 0x86 */, + HAL_TQM_WRITE_CMD = 135 /* 0x87 */, + HAL_OFDMA_TRIGGER_DETAILS = 136 /* 0x88 */, + HAL_TX_DATA = 137 /* 0x89 */, + HAL_TX_FES_SETUP = 138 /* 0x8a */, + HAL_RX_PACKET = 139 /* 0x8b */, + HAL_EXPECTED_RESPONSE = 140 /* 0x8c */, + HAL_TX_MPDU_END = 141 /* 0x8d */, + HAL_TX_MPDU_START = 142 /* 0x8e */, + HAL_TX_MSDU_END = 143 /* 0x8f */, + HAL_TX_MSDU_START = 144 /* 0x90 */, + HAL_TX_SW_MODE_SETUP = 145 /* 0x91 */, + HAL_TXPCU_BUFFER_STATUS = 146 /* 0x92 */, + HAL_TXPCU_USER_BUFFER_STATUS = 147 /* 0x93 */, + HAL_DATA_TO_TIME_CONFIG = 148 /* 0x94 */, + HAL_EXAMPLE_USER_TLV_32 = 149 /* 0x95 */, + HAL_MPDU_INFO = 150 /* 0x96 */, + HAL_PDG_USER_SETUP = 151 /* 0x97 */, + HAL_TX_11AH_SETUP = 152 /* 0x98 */, + HAL_REO_UPDATE_RX_REO_QUEUE_STATUS = 153 /* 0x99 */, + HAL_TX_PEER_ENTRY = 154 /* 0x9a */, + HAL_TX_RAW_OR_NATIVE_FRAME_SETUP = 155 /* 0x9b */, + HAL_EXAMPLE_STRUCT_NAME = 156 /* 0x9c */, + HAL_PCU_PPDU_SETUP_END_INFO = 157 /* 0x9d */, + HAL_PPDU_RATE_SETTING = 158 /* 0x9e */, + HAL_PROT_RATE_SETTING = 159 /* 0x9f */, + HAL_RX_MPDU_DETAILS = 160 /* 0xa0 */, + HAL_EXAMPLE_USER_TLV_42 = 161 /* 0xa1 */, + HAL_RX_MSDU_LINK = 162 /* 0xa2 */, + HAL_RX_REO_QUEUE = 163 /* 0xa3 */, + HAL_ADDR_SEARCH_ENTRY = 164 /* 0xa4 */, + HAL_SCHEDULER_CMD = 165 /* 0xa5 */, + HAL_TX_FLUSH = 166 /* 0xa6 */, + HAL_TQM_ENTRANCE_RING = 167 /* 0xa7 */, + HAL_TX_DATA_WORD = 168 /* 0xa8 */, + HAL_TX_MPDU_DETAILS = 169 /* 0xa9 */, + HAL_TX_MPDU_LINK = 170 /* 0xaa */, + HAL_TX_MPDU_LINK_PTR = 171 /* 0xab */, + HAL_TX_MPDU_QUEUE_HEAD = 172 /* 0xac */, + HAL_TX_MPDU_QUEUE_EXT = 173 /* 0xad */, + HAL_TX_MPDU_QUEUE_EXT_PTR = 174 /* 0xae */, + HAL_TX_MSDU_DETAILS = 175 /* 0xaf */, + HAL_TX_MSDU_EXTENSION = 176 /* 0xb0 */, + HAL_TX_MSDU_FLOW = 177 /* 0xb1 */, + HAL_TX_MSDU_LINK = 178 /* 0xb2 */, + HAL_TX_MSDU_LINK_ENTRY_PTR = 179 /* 0xb3 */, + HAL_RESPONSE_RATE_SETTING = 180 /* 0xb4 */, + HAL_TXPCU_BUFFER_BASICS = 181 /* 0xb5 */, + HAL_UNIFORM_DESCRIPTOR_HEADER = 182 /* 0xb6 */, + HAL_UNIFORM_TQM_CMD_HEADER = 183 /* 0xb7 */, + HAL_UNIFORM_TQM_STATUS_HEADER = 184 /* 0xb8 */, + HAL_USER_RATE_SETTING = 185 /* 0xb9 */, + HAL_WBM_BUFFER_RING = 186 /* 0xba */, + HAL_WBM_LINK_DESCRIPTOR_RING = 187 /* 0xbb */, + HAL_WBM_RELEASE_RING = 188 /* 0xbc */, + HAL_TX_FLUSH_REQ = 189 /* 0xbd */, + HAL_RX_MSDU_DETAILS = 190 /* 0xbe */, + HAL_TQM_WRITE_CMD_STATUS = 191 /* 0xbf */, + HAL_TQM_GET_MPDU_QUEUE_STATS = 192 /* 0xc0 */, + HAL_TQM_GET_MSDU_FLOW_STATS = 193 /* 0xc1 */, + HAL_EXAMPLE_USER_CTLV_32 = 194 /* 0xc2 */, + HAL_TX_FES_STATUS_START = 195 /* 0xc3 */, + HAL_TX_FES_STATUS_USER_PPDU = 196 /* 0xc4 */, + HAL_TX_FES_STATUS_USER_RESPONSE = 197 /* 0xc5 */, + HAL_TX_FES_STATUS_END = 198 /* 0xc6 */, + HAL_RX_TRIG_INFO = 199 /* 0xc7 */, + HAL_RXPCU_TX_SETUP_CLEAR = 200 /* 0xc8 */, + HAL_RX_FRAME_BITMAP_REQ = 201 /* 0xc9 */, + HAL_RX_FRAME_BITMAP_ACK = 202 /* 0xca */, + HAL_COEX_RX_STATUS = 203 /* 0xcb */, + HAL_RX_START_PARAM = 204 /* 0xcc */, + HAL_RX_PPDU_START = 205 /* 0xcd */, + HAL_RX_PPDU_END = 206 /* 0xce */, + HAL_RX_MPDU_START = 207 /* 0xcf */, + HAL_RX_MPDU_END = 208 /* 0xd0 */, + HAL_RX_MSDU_START = 209 /* 0xd1 */, + HAL_RX_MSDU_END = 210 /* 0xd2 */, + HAL_RX_ATTENTION = 211 /* 0xd3 */, + HAL_RECEIVED_RESPONSE_INFO = 212 /* 0xd4 */, + HAL_RX_PHY_SLEEP = 213 /* 0xd5 */, + HAL_RX_HEADER = 214 /* 0xd6 */, + HAL_RX_PEER_ENTRY = 215 /* 0xd7 */, + HAL_RX_FLUSH = 216 /* 0xd8 */, + HAL_RX_RESPONSE_REQUIRED_INFO = 217 /* 0xd9 */, + HAL_RX_FRAMELESS_BAR_DETAILS = 218 /* 0xda */, + HAL_TQM_GET_MPDU_QUEUE_STATS_STATUS = 219 /* 0xdb */, + HAL_TQM_GET_MSDU_FLOW_STATS_STATUS = 220 /* 0xdc */, + HAL_TX_CBF_INFO = 221 /* 0xdd */, + HAL_PCU_PPDU_SETUP_USER = 222 /* 0xde */, + HAL_RX_MPDU_PCU_START = 223 /* 0xdf */, + HAL_RX_PM_INFO = 224 /* 0xe0 */, + HAL_RX_USER_PPDU_END = 225 /* 0xe1 */, + HAL_RX_PRE_PPDU_START = 226 /* 0xe2 */, + HAL_RX_PREAMBLE = 227 /* 0xe3 */, + HAL_TX_FES_SETUP_COMPLETE = 228 /* 0xe4 */, + HAL_TX_LAST_MPDU_FETCHED = 229 /* 0xe5 */, + HAL_TXDMA_STOP_REQUEST = 230 /* 0xe6 */, + HAL_RXPCU_SETUP = 231 /* 0xe7 */, + HAL_RXPCU_USER_SETUP = 232 /* 0xe8 */, + HAL_TX_FES_STATUS_ACK_OR_BA = 233 /* 0xe9 */, + HAL_TQM_ACKED_MPDU = 234 /* 0xea */, + HAL_COEX_TX_RESP = 235 /* 0xeb */, + HAL_COEX_TX_STATUS = 236 /* 0xec */, + HAL_MACTX_COEX_PHY_CTRL = 237 /* 0xed */, + HAL_COEX_STATUS_BROADCAST = 238 /* 0xee */, + HAL_RESPONSE_START_STATUS = 239 /* 0xef */, + HAL_RESPONSE_END_STATUS = 240 /* 0xf0 */, + HAL_CRYPTO_STATUS = 241 /* 0xf1 */, + HAL_RECEIVED_TRIGGER_INFO = 242 /* 0xf2 */, + HAL_REO_ENTRANCE_RING = 243 /* 0xf3 */, + HAL_RX_MPDU_LINK = 244 /* 0xf4 */, + HAL_COEX_TX_STOP_CTRL = 245 /* 0xf5 */, + HAL_RX_PPDU_ACK_REPORT = 246 /* 0xf6 */, + HAL_RX_PPDU_NO_ACK_REPORT = 247 /* 0xf7 */, + HAL_SCH_COEX_STATUS = 248 /* 0xf8 */, + HAL_SCHEDULER_COMMAND_STATUS = 249 /* 0xf9 */, + HAL_SCHEDULER_RX_PPDU_NO_RESPONSE_STATUS = 250 /* 0xfa */, + HAL_TX_FES_STATUS_PROT = 251 /* 0xfb */, + HAL_TX_FES_STATUS_START_PPDU = 252 /* 0xfc */, + HAL_TX_FES_STATUS_START_PROT = 253 /* 0xfd */, + HAL_TXPCU_PHYTX_DEBUG32 = 254 /* 0xfe */, + HAL_TXPCU_PHYTX_OTHER_TRANSMIT_INFO32 = 255 /* 0xff */, + HAL_TX_MPDU_COUNT_TRANSFER_END = 256 /* 0x100 */, + HAL_WHO_ANCHOR_OFFSET = 257 /* 0x101 */, + HAL_WHO_ANCHOR_VALUE = 258 /* 0x102 */, + HAL_WHO_CCE_INFO = 259 /* 0x103 */, + HAL_WHO_COMMIT = 260 /* 0x104 */, + HAL_WHO_COMMIT_DONE = 261 /* 0x105 */, + HAL_WHO_FLUSH = 262 /* 0x106 */, + HAL_WHO_L2_LLC = 263 /* 0x107 */, + HAL_WHO_L2_PAYLOAD = 264 /* 0x108 */, + HAL_WHO_L3_CHECKSUM = 265 /* 0x109 */, + HAL_WHO_L3_INFO = 266 /* 0x10a */, + HAL_WHO_L4_CHECKSUM = 267 /* 0x10b */, + HAL_WHO_L4_INFO = 268 /* 0x10c */, + HAL_WHO_MSDU = 269 /* 0x10d */, + HAL_WHO_MSDU_MISC = 270 /* 0x10e */, + HAL_WHO_PACKET_DATA = 271 /* 0x10f */, + HAL_WHO_PACKET_HDR = 272 /* 0x110 */, + HAL_WHO_PPDU_END = 273 /* 0x111 */, + HAL_WHO_PPDU_START = 274 /* 0x112 */, + HAL_WHO_TSO = 275 /* 0x113 */, + HAL_WHO_WMAC_HEADER_PV0 = 276 /* 0x114 */, + HAL_WHO_WMAC_HEADER_PV1 = 277 /* 0x115 */, + HAL_WHO_WMAC_IV = 278 /* 0x116 */, + HAL_MPDU_INFO_END = 279 /* 0x117 */, + HAL_MPDU_INFO_BITMAP = 280 /* 0x118 */, + HAL_TX_QUEUE_EXTENSION = 281 /* 0x119 */, + HAL_RX_PEER_ENTRY_DETAILS = 282 /* 0x11a */, + HAL_RX_REO_QUEUE_REFERENCE = 283 /* 0x11b */, + HAL_RX_REO_QUEUE_EXT = 284 /* 0x11c */, + HAL_SCHEDULER_SELFGEN_RESPONSE_STATUS = 285 /* 0x11d */, + HAL_TQM_UPDATE_TX_MPDU_COUNT_STATUS = 286 /* 0x11e */, + HAL_TQM_ACKED_MPDU_STATUS = 287 /* 0x11f */, + HAL_TQM_ADD_MSDU_STATUS = 288 /* 0x120 */, + HAL_RX_MPDU_LINK_PTR = 289 /* 0x121 */, + HAL_REO_DESTINATION_RING = 290 /* 0x122 */, + HAL_TQM_LIST_GEN_DONE = 291 /* 0x123 */, + HAL_WHO_TERMINATE = 292 /* 0x124 */, + HAL_TX_LAST_MPDU_END = 293 /* 0x125 */, + HAL_TX_CV_DATA = 294 /* 0x126 */, + HAL_TCL_ENTRANCE_FROM_PPE_RING = 295 /* 0x127 */, + HAL_PPDU_TX_END = 296 /* 0x128 */, + HAL_PROT_TX_END = 297 /* 0x129 */, + HAL_PDG_RESPONSE_RATE_SETTING = 298 /* 0x12a */, + HAL_MPDU_INFO_GLOBAL_END = 299 /* 0x12b */, + HAL_TQM_SCH_INSTR_GLOBAL_END = 300 /* 0x12c */, + HAL_RX_PPDU_END_USER_STATS = 301 /* 0x12d */, + HAL_RX_PPDU_END_USER_STATS_EXT = 302 /* 0x12e */, + HAL_NO_ACK_REPORT = 303 /* 0x12f */, + HAL_ACK_REPORT = 304 /* 0x130 */, + HAL_UNIFORM_REO_CMD_HEADER = 305 /* 0x131 */, + HAL_REO_GET_QUEUE_STATS = 306 /* 0x132 */, + HAL_REO_FLUSH_QUEUE = 307 /* 0x133 */, + HAL_REO_FLUSH_CACHE = 308 /* 0x134 */, + HAL_REO_UNBLOCK_CACHE = 309 /* 0x135 */, + HAL_UNIFORM_REO_STATUS_HEADER = 310 /* 0x136 */, + HAL_REO_GET_QUEUE_STATS_STATUS = 311 /* 0x137 */, + HAL_REO_FLUSH_QUEUE_STATUS = 312 /* 0x138 */, + HAL_REO_FLUSH_CACHE_STATUS = 313 /* 0x139 */, + HAL_REO_UNBLOCK_CACHE_STATUS = 314 /* 0x13a */, + HAL_TQM_FLUSH_CACHE = 315 /* 0x13b */, + HAL_TQM_UNBLOCK_CACHE = 316 /* 0x13c */, + HAL_TQM_FLUSH_CACHE_STATUS = 317 /* 0x13d */, + HAL_TQM_UNBLOCK_CACHE_STATUS = 318 /* 0x13e */, + HAL_RX_PPDU_END_STATUS_DONE = 319 /* 0x13f */, + HAL_RX_STATUS_BUFFER_DONE = 320 /* 0x140 */, + HAL_BUFFER_ADDR_INFO = 321 /* 0x141 */, + HAL_RX_MSDU_DESC_INFO = 322 /* 0x142 */, + HAL_RX_MPDU_DESC_INFO = 323 /* 0x143 */, + HAL_TCL_DATA_CMD = 324 /* 0x144 */, + HAL_TCL_GSE_CMD = 325 /* 0x145 */, + HAL_TCL_EXIT_BASE = 326 /* 0x146 */, + HAL_TCL_COMPACT_EXIT_RING = 327 /* 0x147 */, + HAL_TCL_REGULAR_EXIT_RING = 328 /* 0x148 */, + HAL_TCL_EXTENDED_EXIT_RING = 329 /* 0x149 */, + HAL_UPLINK_COMMON_INFO = 330 /* 0x14a */, + HAL_UPLINK_USER_SETUP_INFO = 331 /* 0x14b */, + HAL_TX_DATA_SYNC = 332 /* 0x14c */, + HAL_PHYRX_CBF_READ_REQUEST_ACK = 333 /* 0x14d */, + HAL_TCL_STATUS_RING = 334 /* 0x14e */, + HAL_TQM_GET_MPDU_HEAD_INFO = 335 /* 0x14f */, + HAL_TQM_SYNC_CMD = 336 /* 0x150 */, + HAL_TQM_GET_MPDU_HEAD_INFO_STATUS = 337 /* 0x151 */, + HAL_TQM_SYNC_CMD_STATUS = 338 /* 0x152 */, + HAL_TQM_THRESHOLD_DROP_NOTIFICATION_STATUS = 339 /* 0x153 */, + HAL_TQM_DESCRIPTOR_THRESHOLD_REACHED_STATUS = 340 /* 0x154 */, + HAL_REO_FLUSH_TIMEOUT_LIST = 341 /* 0x155 */, + HAL_REO_FLUSH_TIMEOUT_LIST_STATUS = 342 /* 0x156 */, + HAL_REO_TO_PPE_RING = 343 /* 0x157 */, + HAL_RX_MPDU_INFO = 344 /* 0x158 */, + HAL_REO_DESCRIPTOR_THRESHOLD_REACHED_STATUS = 345 /* 0x159 */, + HAL_SCHEDULER_RX_SIFS_RESPONSE_TRIGGER_STATUS = 346 /* 0x15a */, + HAL_EXAMPLE_USER_TLV_32_NAME = 347 /* 0x15b */, + HAL_RX_PPDU_START_USER_INFO = 348 /* 0x15c */, + HAL_RX_RXPCU_CLASSIFICATION_OVERVIEW = 349 /* 0x15d */, + HAL_RX_RING_MASK = 350 /* 0x15e */, + HAL_WHO_CLASSIFY_INFO = 351 /* 0x15f */, + HAL_TXPT_CLASSIFY_INFO = 352 /* 0x160 */, + HAL_RXPT_CLASSIFY_INFO = 353 /* 0x161 */, + HAL_TX_FLOW_SEARCH_ENTRY = 354 /* 0x162 */, + HAL_RX_FLOW_SEARCH_ENTRY = 355 /* 0x163 */, + HAL_RECEIVED_TRIGGER_INFO_DETAILS = 356 /* 0x164 */, + HAL_COEX_MAC_NAP = 357 /* 0x165 */, + HAL_MACRX_ABORT_REQUEST_INFO = 358 /* 0x166 */, + HAL_MACTX_ABORT_REQUEST_INFO = 359 /* 0x167 */, + HAL_PHYRX_ABORT_REQUEST_INFO = 360 /* 0x168 */, + HAL_PHYTX_ABORT_REQUEST_INFO = 361 /* 0x169 */, + HAL_RXPCU_PPDU_END_INFO = 362 /* 0x16a */, + HAL_WHO_MESH_CONTROL = 363 /* 0x16b */, + HAL_L_SIG_A_INFO = 364 /* 0x16c */, + HAL_L_SIG_B_INFO = 365 /* 0x16d */, + HAL_HT_SIG_INFO = 366 /* 0x16e */, + HAL_VHT_SIG_A_INFO = 367 /* 0x16f */, + HAL_VHT_SIG_B_SU20_INFO = 368 /* 0x170 */, + HAL_VHT_SIG_B_SU40_INFO = 369 /* 0x171 */, + HAL_VHT_SIG_B_SU80_INFO = 370 /* 0x172 */, + HAL_VHT_SIG_B_SU160_INFO = 371 /* 0x173 */, + HAL_VHT_SIG_B_MU20_INFO = 372 /* 0x174 */, + HAL_VHT_SIG_B_MU40_INFO = 373 /* 0x175 */, + HAL_VHT_SIG_B_MU80_INFO = 374 /* 0x176 */, + HAL_VHT_SIG_B_MU160_INFO = 375 /* 0x177 */, + HAL_SERVICE_INFO = 376 /* 0x178 */, + HAL_HE_SIG_A_SU_INFO = 377 /* 0x179 */, + HAL_HE_SIG_A_MU_DL_INFO = 378 /* 0x17a */, + HAL_HE_SIG_A_MU_UL_INFO = 379 /* 0x17b */, + HAL_HE_SIG_B1_MU_INFO = 380 /* 0x17c */, + HAL_HE_SIG_B2_MU_INFO = 381 /* 0x17d */, + HAL_HE_SIG_B2_OFDMA_INFO = 382 /* 0x17e */, + HAL_PDG_SW_MODE_BW_START = 383 /* 0x17f */, + HAL_PDG_SW_MODE_BW_END = 384 /* 0x180 */, + HAL_PDG_WAIT_FOR_MAC_REQUEST = 385 /* 0x181 */, + HAL_PDG_WAIT_FOR_PHY_REQUEST = 386 /* 0x182 */, + HAL_SCHEDULER_END = 387 /* 0x183 */, + HAL_PEER_TABLE_ENTRY = 388 /* 0x184 */, + HAL_SW_PEER_INFO = 389 /* 0x185 */, + HAL_RXOLE_CCE_CLASSIFY_INFO = 390 /* 0x186 */, + HAL_TCL_CCE_CLASSIFY_INFO = 391 /* 0x187 */, + HAL_RXOLE_CCE_INFO = 392 /* 0x188 */, + HAL_TCL_CCE_INFO = 393 /* 0x189 */, + HAL_TCL_CCE_SUPERRULE = 394 /* 0x18a */, + HAL_CCE_RULE = 395 /* 0x18b */, + HAL_RX_PPDU_START_DROPPED = 396 /* 0x18c */, + HAL_RX_PPDU_END_DROPPED = 397 /* 0x18d */, + HAL_RX_PPDU_END_STATUS_DONE_DROPPED = 398 /* 0x18e */, + HAL_RX_MPDU_START_DROPPED = 399 /* 0x18f */, + HAL_RX_MSDU_START_DROPPED = 400 /* 0x190 */, + HAL_RX_MSDU_END_DROPPED = 401 /* 0x191 */, + HAL_RX_MPDU_END_DROPPED = 402 /* 0x192 */, + HAL_RX_ATTENTION_DROPPED = 403 /* 0x193 */, + HAL_TXPCU_USER_SETUP = 404 /* 0x194 */, + HAL_RXPCU_USER_SETUP_EXT = 405 /* 0x195 */, + HAL_CE_SRC_DESC = 406 /* 0x196 */, + HAL_CE_STAT_DESC = 407 /* 0x197 */, + HAL_RXOLE_CCE_SUPERRULE = 408 /* 0x198 */, + HAL_TX_RATE_STATS_INFO = 409 /* 0x199 */, + HAL_CMD_PART_0_END = 410 /* 0x19a */, + HAL_MACTX_SYNTH_ON = 411 /* 0x19b */, + HAL_SCH_CRITICAL_TLV_REFERENCE = 412 /* 0x19c */, + HAL_TQM_MPDU_GLOBAL_START = 413 /* 0x19d */, + HAL_EXAMPLE_TLV_32 = 414 /* 0x19e */, + HAL_TQM_UPDATE_TX_MSDU_FLOW = 415 /* 0x19f */, + HAL_TQM_UPDATE_TX_MPDU_QUEUE_HEAD = 416 /* 0x1a0 */, + HAL_TQM_UPDATE_TX_MSDU_FLOW_STATUS = 417 /* 0x1a1 */, + HAL_TQM_UPDATE_TX_MPDU_QUEUE_HEAD_STATUS = 418 /* 0x1a2 */, + HAL_REO_UPDATE_RX_REO_QUEUE = 419 /* 0x1a3 */, + HAL_CE_DST_DESC = 420 /* 0x1a4 */, + HAL_TLV_BASE = 511 /* 0x1ff */, +}; + +#define HAL_TLV_HDR_TAG GENMASK(9, 1) +#define HAL_TLV_HDR_LEN GENMASK(25, 10) + +#define HAL_TLV_ALIGN 4 + +struct hal_tlv_hdr { + u32 tl; + u8 value[0]; +} __packed; + +#define RX_MPDU_DESC_INFO0_MSDU_COUNT GENMASK(7, 0) +#define RX_MPDU_DESC_INFO0_SEQ_NUM GENMASK(19, 8) +#define RX_MPDU_DESC_INFO0_FRAG_FLAG BIT(20) +#define RX_MPDU_DESC_INFO0_MPDU_RETRY BIT(21) +#define RX_MPDU_DESC_INFO0_AMPDU_FLAG BIT(22) +#define RX_MPDU_DESC_INFO0_BAR_FRAME BIT(23) +#define RX_MPDU_DESC_INFO0_VALID_PN BIT(24) +#define RX_MPDU_DESC_INFO0_VALID_SA BIT(25) +#define RX_MPDU_DESC_INFO0_SA_IDX_TIMEOUT BIT(26) +#define RX_MPDU_DESC_INFO0_VALID_DA BIT(27) +#define RX_MPDU_DESC_INFO0_DA_MCBC BIT(28) +#define RX_MPDU_DESC_INFO0_DA_IDX_TIMEOUT BIT(29) +#define RX_MPDU_DESC_INFO0_RAW_MPDU BIT(30) + +struct rx_mpdu_desc { + u32 info0; /* %RX_MPDU_DESC_INFO */ + u32 meta_data; +} __packed; + +/* rx_mpdu_desc + * Producer: RXDMA + * Consumer: REO/SW/FW + * + * msdu_count + * The number of MSDUs within the MPDU + * + * mpdu_sequence_number + * The field can have two different meanings based on the setting + * of field 'bar_frame'. If 'bar_frame' is set, it means the MPDU + * start sequence number from the BAR frame otherwise it means + * the MPDU sequence number of the received frame. + * + * fragment_flag + * When set, this MPDU is a fragment and REO should forward this + * fragment MPDU to the REO destination ring without any reorder + * checks, pn checks or bitmap update. This implies that REO is + * forwarding the pointer to the MSDU link descriptor. + * + * mpdu_retry_bit + * The retry bit setting from the MPDU header of the received frame + * + * ampdu_flag + * Indicates the MPDU was received as part of an A-MPDU. + * + * bar_frame + * Indicates the received frame is a BAR frame. After processing, + * this frame shall be pushed to SW or deleted. + * + * valid_pn + * When not set, REO will not perform a PN sequence number check. + * + * valid_sa + * Indicates OLE found a valid SA entry for all MSDUs in this MPDU. + * + * sa_idx_timeout + * Indicates, at least 1 MSDU within the MPDU has an unsuccessful + * MAC source address search due to the expiration of search timer. + * + * valid_da + * When set, OLE found a valid DA entry for all MSDUs in this MPDU. + * + * da_mcbc + * Field Only valid if valid_da is set. Indicates at least one of + * the DA addresses is a Multicast or Broadcast address. + * + * da_idx_timeout + * Indicates, at least 1 MSDU within the MPDU has an unsuccessful + * MAC destination address search due to the expiration of search + * timer. + * + * raw_mpdu + * Field only valid when first_msdu_in_mpdu_flag is set. Indicates + * the contents in the MSDU buffer contains a 'RAW' MPDU. + */ + +enum hal_rx_msdu_desc_reo_dest_ind { + HAL_RX_MSDU_DESC_REO_DEST_IND_TCL, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW1, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW2, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW3, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW4, + HAL_RX_MSDU_DESC_REO_DEST_IND_RELEASE, + HAL_RX_MSDU_DESC_REO_DEST_IND_FW, +}; + +#define RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU BIT(0) +#define RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU BIT(1) +#define RX_MSDU_DESC_INFO0_MSDU_CONTINUATION BIT(2) +#define RX_MSDU_DESC_INFO0_MSDU_LENGTH GENMASK(16, 3) +#define RX_MSDU_DESC_INFO0_REO_DEST_IND GENMASK(21, 17) +#define RX_MSDU_DESC_INFO0_MSDU_DROP BIT(22) +#define RX_MSDU_DESC_INFO0_VALID_SA BIT(23) +#define RX_MSDU_DESC_INFO0_SA_IDX_TIMEOUT BIT(24) +#define RX_MSDU_DESC_INFO0_VALID_DA BIT(25) +#define RX_MSDU_DESC_INFO0_DA_MCBC BIT(26) +#define RX_MSDU_DESC_INFO0_DA_IDX_TIMEOUT BIT(27) + +#define HAL_RX_MSDU_PKT_LENGTH_GET(val) \ + (FIELD_GET(RX_MSDU_DESC_INFO0_MSDU_LENGTH, (val))) + +struct rx_msdu_desc { + u32 info0; + u32 rsvd0; +} __packed; + +/* rx_msdu_desc + * + * first_msdu_in_mpdu + * Indicates first msdu in mpdu. + * + * last_msdu_in_mpdu + * Indicates last msdu in mpdu. This flag can be true only when + * 'Msdu_continuation' set to 0. This implies that when an msdu + * is spread out over multiple buffers and thus msdu_continuation + * is set, only for the very last buffer of the msdu, can the + * 'last_msdu_in_mpdu' be set. + * + * When both first_msdu_in_mpdu and last_msdu_in_mpdu are set, + * the MPDU that this MSDU belongs to only contains a single MSDU. + * + * msdu_continuation + * When set, this MSDU buffer was not able to hold the entire MSDU. + * The next buffer will therefor contain additional information + * related to this MSDU. + * + * msdu_length + * Field is only valid in combination with the 'first_msdu_in_mpdu' + * being set. Full MSDU length in bytes after decapsulation. This + * field is still valid for MPDU frames without A-MSDU. It still + * represents MSDU length after decapsulation Or in case of RAW + * MPDUs, it indicates the length of the entire MPDU (without FCS + * field). + * + * reo_destination_indication + * The id of the reo exit ring where the msdu frame shall push + * after (MPDU level) reordering has finished. Values are defined + * in enum %HAL_RX_MSDU_DESC_REO_DEST_IND_. + * + * msdu_drop + * Indicates that REO shall drop this MSDU and not forward it to + * any other ring. + * + * valid_sa + * Indicates OLE found a valid SA entry for this MSDU. + * + * sa_idx_timeout + * Indicates, an unsuccessful MAC source address search due to + * the expiration of search timer for this MSDU. + * + * valid_da + * When set, OLE found a valid DA entry for this MSDU. + * + * da_mcbc + * Field Only valid if valid_da is set. Indicates the DA address + * is a Multicast or Broadcast address for this MSDU. + * + * da_idx_timeout + * Indicates, an unsuccessful MAC destination address search due + * to the expiration of search timer fot this MSDU. + */ + +enum hal_reo_dest_ring_buffer_type { + HAL_REO_DEST_RING_BUFFER_TYPE_MSDU, + HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC, +}; + +enum hal_reo_dest_ring_push_reason { + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED, + HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION, +}; + +enum hal_reo_dest_ring_error_code { + HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO, + HAL_REO_DEST_RING_ERROR_CODE_DESC_INVALID, + HAL_REO_DEST_RING_ERROR_CODE_AMPDU_IN_NON_BA, + HAL_REO_DEST_RING_ERROR_CODE_NON_BA_DUPLICATE, + HAL_REO_DEST_RING_ERROR_CODE_BA_DUPLICATE, + HAL_REO_DEST_RING_ERROR_CODE_FRAME_2K_JUMP, + HAL_REO_DEST_RING_ERROR_CODE_BAR_2K_JUMP, + HAL_REO_DEST_RING_ERROR_CODE_FRAME_OOR, + HAL_REO_DEST_RING_ERROR_CODE_BAR_OOR, + HAL_REO_DEST_RING_ERROR_CODE_NO_BA_SESSION, + HAL_REO_DEST_RING_ERROR_CODE_FRAME_SN_EQUALS_SSN, + HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED, + HAL_REO_DEST_RING_ERROR_CODE_2K_ERR_FLAG_SET, + HAL_REO_DEST_RING_ERROR_CODE_PN_ERR_FLAG_SET, + HAL_REO_DEST_RING_ERROR_CODE_DESC_BLOCKED, + HAL_REO_DEST_RING_ERROR_CODE_MAX, +}; + +#define HAL_REO_DEST_RING_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_DEST_RING_INFO0_BUFFER_TYPE BIT(8) +#define HAL_REO_DEST_RING_INFO0_PUSH_REASON GENMASK(10, 9) +#define HAL_REO_DEST_RING_INFO0_ERROR_CODE GENMASK(15, 11) +#define HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM GENMASK(31, 16) + +#define HAL_REO_DEST_RING_INFO1_REORDER_INFO_VALID BIT(0) +#define HAL_REO_DEST_RING_INFO1_REORDER_OPCODE GENMASK(4, 1) +#define HAL_REO_DEST_RING_INFO1_REORDER_SLOT_IDX GENMASK(12, 5) + +#define HAL_REO_DEST_RING_INFO2_RING_ID GENMASK(27, 20) +#define HAL_REO_DEST_RING_INFO2_LOOPING_COUNT GENMASK(31, 28) + +struct hal_reo_dest_ring { + struct ath11k_buffer_addr buf_addr_info; + struct rx_mpdu_desc rx_mpdu_info; + struct rx_msdu_desc rx_msdu_info; + u32 queue_addr_lo; + u32 info0; /* %HAL_REO_DEST_RING_INFO0_ */ + u32 info1; /* %HAL_REO_DEST_RING_INFO1_ */ + u32 rsvd0; + u32 rsvd1; + u32 rsvd2; + u32 rsvd3; + u32 rsvd4; + u32 rsvd5; + u32 info2; /* %HAL_REO_DEST_RING_INFO2_ */ +} __packed; + +/* hal_reo_dest_ring + * + * Producer: RXDMA + * Consumer: REO/SW/FW + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + * + * rx_mpdu_info + * General information related to the MPDU that is passed + * on from REO entrance ring to the REO destination ring. + * + * rx_msdu_info + * General information related to the MSDU that is passed + * on from RXDMA all the way to to the REO destination ring. + * + * queue_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * queue_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * buffer_type + * Indicates the type of address provided in the buf_addr_info. + * Values are defined in enum %HAL_REO_DEST_RING_BUFFER_TYPE_. + * + * push_reason + * Reason for pushing this frame to this exit ring. Values are + * defined in enum %HAL_REO_DEST_RING_PUSH_REASON_. + * + * error_code + * Valid only when 'push_reason' is set. All error codes are + * defined in enum %HAL_REO_DEST_RING_ERROR_CODE_. + * + * rx_queue_num + * Indicates the REO MPDU reorder queue id from which this frame + * originated. + * + * reorder_info_valid + * When set, REO has been instructed to not perform the actual + * re-ordering of frames for this queue, but just to insert + * the reorder opcodes. + * + * reorder_opcode + * Field is valid when 'reorder_info_valid' is set. This field is + * always valid for debug purpose as well. + * + * reorder_slot_idx + * Valid only when 'reorder_info_valid' is set. + * + * ring_id + * The buffer pointer ring id. + * 0 - Idle ring + * 1 - N refers to other rings. + * + * looping_count + * Indicates the number of times the producer of entries into + * this ring has looped around the ring. + */ + +enum hal_reo_entr_rxdma_ecode { + HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_FCS_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_UNECRYPTED_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LEN_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LIMIT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_WIFI_PARSE_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_PARSE_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_SA_TIMEOUT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_DA_TIMEOUT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_FLOW_TIMEOUT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MAX, +}; + +#define HAL_REO_ENTR_RING_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_ENTR_RING_INFO0_MPDU_BYTE_COUNT GENMASK(21, 8) +#define HAL_REO_ENTR_RING_INFO0_DEST_IND GENMASK(26, 22) +#define HAL_REO_ENTR_RING_INFO0_FRAMELESS_BAR BIT(27) + +#define HAL_REO_ENTR_RING_INFO1_RXDMA_PUSH_REASON GENMASK(1, 0) +#define HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE GENMASK(6, 2) + +struct hal_reo_entrance_ring { + struct ath11k_buffer_addr buf_addr_info; + struct rx_mpdu_desc rx_mpdu_info; + u32 queue_addr_lo; + u32 info0; /* %HAL_REO_ENTR_RING_INFO0_ */ + u32 info1; /* %HAL_REO_ENTR_RING_INFO1_ */ + u32 info2; /* %HAL_REO_DEST_RING_INFO2_ */ + +} __packed; + +/* hal_reo_entrance_ring + * + * Producer: RXDMA + * Consumer: REO + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + * + * rx_mpdu_info + * General information related to the MPDU that is passed + * on from REO entrance ring to the REO destination ring. + * + * queue_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * queue_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * mpdu_byte_count + * An approximation of the number of bytes received in this MPDU. + * Used to keeps stats on the amount of data flowing + * through a queue. + * + * reo_destination_indication + * The id of the reo exit ring where the msdu frame shall push + * after (MPDU level) reordering has finished. Values are defined + * in enum %HAL_RX_MSDU_DESC_REO_DEST_IND_. + * + * frameless_bar + * Indicates that this REO entrance ring struct contains BAR info + * from a multi TID BAR frame. The original multi TID BAR frame + * itself contained all the REO info for the first TID, but all + * the subsequent TID info and their linkage to the REO descriptors + * is passed down as 'frameless' BAR info. + * + * The only fields valid in this descriptor when this bit is set + * are queue_addr_lo, queue_addr_hi, mpdu_sequence_number, + * bar_frame and peer_meta_data. + * + * rxdma_push_reason + * Reason for pushing this frame to this exit ring. Values are + * defined in enum %HAL_REO_DEST_RING_PUSH_REASON_. + * + * rxdma_error_code + * Valid only when 'push_reason' is set. All error codes are + * defined in enum %HAL_REO_ENTR_RING_RXDMA_ECODE_. + * + * ring_id + * The buffer pointer ring id. + * 0 - Idle ring + * 1 - N refers to other rings. + * + * looping_count + * Indicates the number of times the producer of entries into + * this ring has looped around the ring. + */ + +#define HAL_REO_CMD_HDR_INFO0_CMD_NUMBER GENMASK(15, 0) +#define HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED BIT(16) + +struct hal_reo_cmd_hdr { + u32 info0; +} __packed; + +#define HAL_REO_GET_QUEUE_STATS_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_GET_QUEUE_STATS_INFO0_CLEAR_STATS BIT(8) + +struct hal_reo_get_queue_stats { + struct hal_reo_cmd_hdr cmd; + u32 queue_addr_lo; + u32 info0; + u32 rsvd0[6]; +} __packed; + +/* hal_reo_get_queue_stats + * Producer: SW + * Consumer: REO + * + * cmd + * Details for command execution tracking purposes. + * + * queue_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * queue_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * clear_stats + * Clear stats settings. When set, Clear the stats after + * generating the status. + * + * Following stats will be cleared. + * Timeout_count + * Forward_due_to_bar_count + * Duplicate_count + * Frames_in_order_count + * BAR_received_count + * MPDU_Frames_processed_count + * MSDU_Frames_processed_count + * Total_processed_byte_count + * Late_receive_MPDU_count + * window_jump_2k + * Hole_count + */ + +#define HAL_REO_FLUSH_QUEUE_INFO0_DESC_ADDR_HI GENMASK(7, 0) +#define HAL_REO_FLUSH_QUEUE_INFO0_BLOCK_DESC_ADDR BIT(8) +#define HAL_REO_FLUSH_QUEUE_INFO0_BLOCK_RESRC_IDX GENMASK(10, 9) + +struct hal_reo_flush_queue { + struct hal_reo_cmd_hdr cmd; + u32 desc_addr_lo; + u32 info0; + u32 rsvd0[6]; +} __packed; + +#define HAL_REO_FLUSH_CACHE_INFO0_CACHE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_FLUSH_CACHE_INFO0_FWD_ALL_MPDUS BIT(8) +#define HAL_REO_FLUSH_CACHE_INFO0_RELEASE_BLOCK_IDX BIT(9) +#define HAL_REO_FLUSH_CACHE_INFO0_BLOCK_RESRC_IDX GENMASK(11, 10) +#define HAL_REO_FLUSH_CACHE_INFO0_FLUSH_WO_INVALIDATE BIT(12) +#define HAL_REO_FLUSH_CACHE_INFO0_BLOCK_CACHE_USAGE BIT(13) +#define HAL_REO_FLUSH_CACHE_INFO0_FLUSH_ALL BIT(14) + +struct hal_reo_flush_cache { + struct hal_reo_cmd_hdr cmd; + u32 cache_addr_lo; + u32 info0; + u32 rsvd0[6]; +} __packed; + +#define HAL_TCL_DATA_CMD_INFO0_DESC_TYPE BIT(0) +#define HAL_TCL_DATA_CMD_INFO0_EPD BIT(1) +#define HAL_TCL_DATA_CMD_INFO0_ENCAP_TYPE GENMASK(3, 2) +#define HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE GENMASK(7, 4) +#define HAL_TCL_DATA_CMD_INFO0_SRC_BUF_SWAP BIT(8) +#define HAL_TCL_DATA_CMD_INFO0_LNK_META_SWAP BIT(9) +#define HAL_TCL_DATA_CMD_INFO0_SEARCH_TYPE GENMASK(13, 12) +#define HAL_TCL_DATA_CMD_INFO0_ADDR_EN GENMASK(15, 14) +#define HAL_TCL_DATA_CMD_INFO0_CMD_NUM GENMASK(31, 16) + +#define HAL_TCL_DATA_CMD_INFO1_DATA_LEN GENMASK(15, 0) +#define HAL_TCL_DATA_CMD_INFO1_IP4_CKSUM_EN BIT(16) +#define HAL_TCL_DATA_CMD_INFO1_UDP4_CKSUM_EN BIT(17) +#define HAL_TCL_DATA_CMD_INFO1_UDP6_CKSUM_EN BIT(18) +#define HAL_TCL_DATA_CMD_INFO1_TCP4_CKSUM_EN BIT(19) +#define HAL_TCL_DATA_CMD_INFO1_TCP6_CKSUM_EN BIT(20) +#define HAL_TCL_DATA_CMD_INFO1_TO_FW BIT(21) +#define HAL_TCL_DATA_CMD_INFO1_PKT_OFFSET GENMASK(31, 23) + +#define HAL_TCL_DATA_CMD_INFO2_BUF_TIMESTAMP GENMASK(18, 0) +#define HAL_TCL_DATA_CMD_INFO2_BUF_T_VALID BIT(19) +#define HAL_TCL_DATA_CMD_INFO2_MESH_ENABLE BIT(20) +#define HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE BIT(21) +#define HAL_TCL_DATA_CMD_INFO2_TID GENMASK(25, 22) +#define HAL_TCL_DATA_CMD_INFO2_LMAC_ID GENMASK(27, 26) + +#define HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX GENMASK(5, 0) +#define HAL_TCL_DATA_CMD_INFO3_SEARCH_INDEX GENMASK(25, 6) +#define HAL_TCL_DATA_CMD_INFO3_CACHE_SET_NUM GENMASK(29, 26) + +#define HAL_TCL_DATA_CMD_INFO4_RING_ID GENMASK(27, 20) +#define HAL_TCL_DATA_CMD_INFO4_LOOPING_COUNT GENMASK(31, 28) + +enum hal_encrypt_type { + HAL_ENCRYPT_TYPE_WEP_40, + HAL_ENCRYPT_TYPE_WEP_104, + HAL_ENCRYPT_TYPE_TKIP_NO_MIC, + HAL_ENCRYPT_TYPE_WEP_128, + HAL_ENCRYPT_TYPE_TKIP_MIC, + HAL_ENCRYPT_TYPE_WAPI, + HAL_ENCRYPT_TYPE_CCMP_128, + HAL_ENCRYPT_TYPE_OPEN, + HAL_ENCRYPT_TYPE_CCMP_256, + HAL_ENCRYPT_TYPE_GCMP_128, + HAL_ENCRYPT_TYPE_AES_GCMP_256, + HAL_ENCRYPT_TYPE_WAPI_GCM_SM4, +}; + +enum hal_tcl_encap_type { + HAL_TCL_ENCAP_TYPE_RAW, + HAL_TCL_ENCAP_TYPE_NATIVE_WIFI, + HAL_TCL_ENCAP_TYPE_ETHERNET, + HAL_TCL_ENCAP_TYPE_802_3 = 3, +}; + +enum hal_tcl_desc_type { + HAL_TCL_DESC_TYPE_BUFFER, + HAL_TCL_DESC_TYPE_EXT_DESC, +}; + +enum hal_wbm_htt_tx_comp_status { + HAL_WBM_REL_HTT_TX_COMP_STATUS_OK, + HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP, + HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL, + HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ, + HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT, + HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY, +}; + +struct hal_tcl_data_cmd { + struct ath11k_buffer_addr buf_addr_info; + u32 info0; + u32 info1; + u32 info2; + u32 info3; + u32 info4; +} __packed; + +/* hal_tcl_data_cmd + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + * + * desc_type + * Indicates the type of address provided in the buf_addr_info. + * Values are defined in enum %HAL_REO_DEST_RING_BUFFER_TYPE_. + * + * epd + * When this bit is set then input packet is an EPD type. + * + * encap_type + * Indicates the encapsulation that HW will perform. Values are + * defined in enum %HAL_TCL_ENCAP_TYPE_. + * + * encrypt_type + * Field only valid for encap_type: RAW + * Values are defined in enum %HAL_ENCRYPT_TYPE_. + * + * src_buffer_swap + * Treats source memory (packet buffer) organization as big-endian. + * 1'b0: Source memory is little endian + * 1'b1: Source memory is big endian + * + * link_meta_swap + * Treats link descriptor and Metadata as big-endian. + * 1'b0: memory is little endian + * 1'b1: memory is big endian + * + * search_type + * Search type select + * 0 - Normal search, 1 - Index based address search, + * 2 - Index based flow search + * + * addrx_en + * addry_en + * Address X/Y search enable in ASE correspondingly. + * 1'b0: Search disable + * 1'b1: Search Enable + * + * cmd_num + * This number can be used to match against status. + * + * data_length + * MSDU length in case of direct descriptor. Length of link + * extension descriptor in case of Link extension descriptor. + * + * *_checksum_en + * Enable checksum replacement for ipv4, udp_over_ipv4, ipv6, + * udp_over_ipv6, tcp_over_ipv4 and tcp_over_ipv6. + * + * to_fw + * Forward packet to FW along with classification result. The + * packet will not be forward to TQM when this bit is set. + * 1'b0: Use classification result to forward the packet. + * 1'b1: Override classification result & forward packet only to fw + * + * packet_offset + * Packet offset from Metadata in case of direct buffer descriptor. + * + * buffer_timestamp + * buffer_timestamp_valid + * Frame system entrance timestamp. It shall be filled by first + * module (SW, TCL or TQM) that sees the frames first. + * + * mesh_enable + * For raw WiFi frames, this indicates transmission to a mesh STA, + * enabling the interpretation of the 'Mesh Control Present' bit + * (bit 8) of QoS Control. + * For native WiFi frames, this indicates that a 'Mesh Control' + * field is present between the header and the LLC. + * + * hlos_tid_overwrite + * + * When set, TCL shall ignore the IP DSCP and VLAN PCP + * fields and use HLOS_TID as the final TID. Otherwise TCL + * shall consider the DSCP and PCP fields as well as HLOS_TID + * and choose a final TID based on the configured priority + * + * hlos_tid + * HLOS MSDU priority + * Field is used when HLOS_TID_overwrite is set. + * + * lmac_id + * TCL uses this LMAC_ID in address search, i.e, while + * finding matching entry for the packet in AST corresponding + * to given LMAC_ID + * + * If LMAC ID is all 1s (=> value 3), it indicates wildcard + * match for any MAC + * + * dscp_tid_table_num + * DSCP to TID mapping table number that need to be used + * for the MSDU. + * + * search_index + * The index that will be used for index based address or + * flow search. The field is valid when 'search_type' is 1 or 2. + * + * cache_set_num + * + * Cache set number that should be used to cache the index + * based search results, for address and flow search. This + * value should be equal to LSB four bits of the hash value of + * match data, in case of search index points to an entry which + * may be used in content based search also. The value can be + * anything when the entry pointed by search index will not be + * used for content based search. + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * + * looping_count + * + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_TCL_DESC_LEN sizeof(struct hal_tcl_data_cmd) + +enum hal_tcl_gse_ctrl { + HAL_TCL_GSE_CTRL_RD_STAT, + HAL_TCL_GSE_CTRL_SRCH_DIS, + HAL_TCL_GSE_CTRL_WR_BK_SINGLE, + HAL_TCL_GSE_CTRL_WR_BK_ALL, + HAL_TCL_GSE_CTRL_INVAL_SINGLE, + HAL_TCL_GSE_CTRL_INVAL_ALL, + HAL_TCL_GSE_CTRL_WR_BK_INVAL_SINGLE, + HAL_TCL_GSE_CTRL_WR_BK_INVAL_ALL, + HAL_TCL_GSE_CTRL_CLR_STAT_SINGLE, +}; + +/* hal_tcl_gse_ctrl + * + * rd_stat + * Report or Read statistics + * srch_dis + * Search disable. Report only Hash. + * wr_bk_single + * Write Back single entry + * wr_bk_all + * Write Back entire cache entry + * inval_single + * Invalidate single cache entry + * inval_all + * Invalidate entire cache + * wr_bk_inval_single + * Write back and invalidate single entry in cache + * wr_bk_inval_all + * Write back and invalidate entire cache + * clr_stat_single + * Clear statistics for single entry + */ + +#define HAL_TCL_GSE_CMD_INFO0_CTRL_BUF_ADDR_HI GENMASK(7, 0) +#define HAL_TCL_GSE_CMD_INFO0_GSE_CTRL GENMASK(11, 8) +#define HAL_TCL_GSE_CMD_INFO0_GSE_SEL BIT(12) +#define HAL_TCL_GSE_CMD_INFO0_STATUS_DEST_RING_ID BIT(13) +#define HAL_TCL_GSE_CMD_INFO0_SWAP BIT(14) + +#define HAL_TCL_GSE_CMD_INFO1_RING_ID GENMASK(27, 20) +#define HAL_TCL_GSE_CMD_INFO1_LOOPING_COUNT GENMASK(31, 28) + +struct hal_tcl_gse_cmd { + u32 ctrl_buf_addr_lo; + u32 info0; + u32 meta_data[2]; + u32 rsvd0[2]; + u32 info1; +} __packed; + +/* hal_tcl_gse_cmd + * + * ctrl_buf_addr_lo, ctrl_buf_addr_hi + * Address of a control buffer containing additional info needed + * for this command execution. + * + * gse_ctrl + * GSE control operations. This includes cache operations and table + * entry statistics read/clear operation. Values are defined in + * enum %HAL_TCL_GSE_CTRL. + * + * gse_sel + * To select the ASE/FSE to do the operation mention by GSE_ctrl. + * 0: FSE select 1: ASE select + * + * status_destination_ring_id + * TCL status ring to which the GSE status needs to be send. + * + * swap + * Bit to enable byte swapping of contents of buffer. + * + * meta_data + * Meta data to be returned in the status descriptor + */ + +enum hal_tcl_cache_op_res { + HAL_TCL_CACHE_OP_RES_DONE, + HAL_TCL_CACHE_OP_RES_NOT_FOUND, + HAL_TCL_CACHE_OP_RES_TIMEOUT, +}; + +#define HAL_TCL_STATUS_RING_INFO0_GSE_CTRL GENMASK(3, 0) +#define HAL_TCL_STATUS_RING_INFO0_GSE_SEL BIT(4) +#define HAL_TCL_STATUS_RING_INFO0_CACHE_OP_RES GENMASK(6, 5) +#define HAL_TCL_STATUS_RING_INFO0_MSDU_CNT GENMASK(31, 8) + +#define HAL_TCL_STATUS_RING_INFO1_HASH_IDX GENMASK(19, 0) + +#define HAL_TCL_STATUS_RING_INFO2_RING_ID GENMASK(27, 20) +#define HAL_TCL_STATUS_RING_INFO2_LOOPING_COUNT GENMASK(31, 28) + +struct hal_tcl_status_ring { + u32 info0; + u32 msdu_byte_count; + u32 msdu_timestamp; + u32 meta_data[2]; + u32 info1; + u32 rsvd0; + u32 info2; +} __packed; + +/* hal_tcl_status_ring + * + * gse_ctrl + * GSE control operations. This includes cache operations and table + * entry statistics read/clear operation. Values are defined in + * enum %HAL_TCL_GSE_CTRL. + * + * gse_sel + * To select the ASE/FSE to do the operation mention by GSE_ctrl. + * 0: FSE select 1: ASE select + * + * cache_op_res + * Cache operation result. Values are defined in enum + * %HAL_TCL_CACHE_OP_RES_. + * + * msdu_cnt + * msdu_byte_count + * MSDU count of Entry and MSDU byte count for entry 1. + * + * hash_indx + * Hash value of the entry in case of search failed or disabled. + */ + +#define HAL_CE_SRC_DESC_ADDR_INFO_ADDR_HI GENMASK(7, 0) +#define HAL_CE_SRC_DESC_ADDR_INFO_HASH_EN BIT(8) +#define HAL_CE_SRC_DESC_ADDR_INFO_BYTE_SWAP BIT(9) +#define HAL_CE_SRC_DESC_ADDR_INFO_DEST_SWAP BIT(10) +#define HAL_CE_SRC_DESC_ADDR_INFO_GATHER BIT(11) +#define HAL_CE_SRC_DESC_ADDR_INFO_LEN GENMASK(31, 16) + +#define HAL_CE_SRC_DESC_META_INFO_DATA GENMASK(15, 0) + +#define HAL_CE_SRC_DESC_FLAGS_RING_ID GENMASK(27, 20) +#define HAL_CE_SRC_DESC_FLAGS_LOOP_CNT HAL_SRNG_DESC_LOOP_CNT + +struct hal_ce_srng_src_desc { + u32 buffer_addr_low; + u32 buffer_addr_info; /* %HAL_CE_SRC_DESC_ADDR_INFO_ */ + u32 meta_info; /* %HAL_CE_SRC_DESC_META_INFO_ */ + u32 flags; /* %HAL_CE_SRC_DESC_FLAGS_ */ +} __packed; + +/* + * hal_ce_srng_src_desc + * + * buffer_addr_lo + * LSB 32 bits of the 40 Bit Pointer to the source buffer + * + * buffer_addr_hi + * MSB 8 bits of the 40 Bit Pointer to the source buffer + * + * toeplitz_en + * Enable generation of 32-bit Toeplitz-LFSR hash for + * data transfer. In case of gather field in first source + * ring entry of the gather copy cycle in taken into account. + * + * src_swap + * Treats source memory organization as big-endian. For + * each dword read (4 bytes), the byte 0 is swapped with byte 3 + * and byte 1 is swapped with byte 2. + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * dest_swap + * Treats destination memory organization as big-endian. + * For each dword write (4 bytes), the byte 0 is swapped with + * byte 3 and byte 1 is swapped with byte 2. + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * gather + * Enables gather of multiple copy engine source + * descriptors to one destination. + * + * ce_res_0 + * Reserved + * + * + * length + * Length of the buffer in units of octets of the current + * descriptor + * + * fw_metadata + * Meta data used by FW. + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * ce_res_1 + * Reserved + * + * ce_res_2 + * Reserved + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * Helps with debugging when dumping ring contents. + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_CE_DEST_DESC_ADDR_INFO_ADDR_HI GENMASK(7, 0) +#define HAL_CE_DEST_DESC_ADDR_INFO_RING_ID GENMASK(27, 20) +#define HAL_CE_DEST_DESC_ADDR_INFO_LOOP_CNT HAL_SRNG_DESC_LOOP_CNT + +struct hal_ce_srng_dest_desc { + u32 buffer_addr_low; + u32 buffer_addr_info; /* %HAL_CE_DEST_DESC_ADDR_INFO_ */ +} __packed; + +/* hal_ce_srng_dest_desc + * + * dst_buffer_low + * LSB 32 bits of the 40 Bit Pointer to the Destination + * buffer + * + * dst_buffer_high + * MSB 8 bits of the 40 Bit Pointer to the Destination + * buffer + * + * ce_res_4 + * Reserved + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * Helps with debugging when dumping ring contents. + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_CE_DST_STATUS_DESC_FLAGS_HASH_EN BIT(8) +#define HAL_CE_DST_STATUS_DESC_FLAGS_BYTE_SWAP BIT(9) +#define HAL_CE_DST_STATUS_DESC_FLAGS_DEST_SWAP BIT(10) +#define HAL_CE_DST_STATUS_DESC_FLAGS_GATHER BIT(11) +#define HAL_CE_DST_STATUS_DESC_FLAGS_LEN GENMASK(31, 16) + +#define HAL_CE_DST_STATUS_DESC_META_INFO_DATA GENMASK(7, 0) +#define HAL_CE_DST_STATUS_DESC_META_INFO_RING_ID GENMASK(27, 20) +#define HAL_CE_DST_STATUS_DESC_META_INFO_LOOP_CNT HAL_SRNG_DESC_LOOP_CNT + +struct hal_ce_srng_dst_status_desc { + u32 flags; /* %HAL_CE_DST_STATUS_DESC_FLAGS_ */ + u32 toeplitz_hash0; + u32 toeplitz_hash1; + u32 meta_info; /* HAL_CE_DST_STATUS_DESC_META_INFO_ */ +} __packed; + +/* hal_ce_srng_dst_status_desc + * + * ce_res_5 + * Reserved + * + * toeplitz_en + * + * src_swap + * Source memory buffer swapped + * + * dest_swap + * Destination memory buffer swapped + * + * gather + * Gather of multiple copy engine source descriptors to one + * destination enabled + * + * ce_res_6 + * Reserved + * + * length + * Sum of all the Lengths of the source descriptor in the + * gather chain + * + * toeplitz_hash_0 + * 32 LS bits of 64 bit Toeplitz LFSR hash result + * + * toeplitz_hash_1 + * 32 MS bits of 64 bit Toeplitz LFSR hash result + * + * fw_metadata + * Meta data used by FW + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * ce_res_7 + * Reserved + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * Helps with debugging when dumping ring contents. + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_TX_RATE_STATS_INFO0_VALID BIT(0) +#define HAL_TX_RATE_STATS_INFO0_BW GENMASK(2, 1) +#define HAL_TX_RATE_STATS_INFO0_PKT_TYPE GENMASK(6, 3) +#define HAL_TX_RATE_STATS_INFO0_STBC BIT(7) +#define HAL_TX_RATE_STATS_INFO0_LDPC BIT(8) +#define HAL_TX_RATE_STATS_INFO0_SGI GENMASK(10, 9) +#define HAL_TX_RATE_STATS_INFO0_MCS GENMASK(14, 11) +#define HAL_TX_RATE_STATS_INFO0_OFDMA_TX BIT(15) +#define HAL_TX_RATE_STATS_INFO0_TONES_IN_RU GENMASK(27, 16) + +enum hal_tx_rate_stats_bw { + HAL_TX_RATE_STATS_BW_20, + HAL_TX_RATE_STATS_BW_40, + HAL_TX_RATE_STATS_BW_80, + HAL_TX_RATE_STATS_BW_160, +}; + +enum hal_tx_rate_stats_pkt_type { + HAL_TX_RATE_STATS_PKT_TYPE_11A, + HAL_TX_RATE_STATS_PKT_TYPE_11B, + HAL_TX_RATE_STATS_PKT_TYPE_11N, + HAL_TX_RATE_STATS_PKT_TYPE_11AC, + HAL_TX_RATE_STATS_PKT_TYPE_11AX, +}; + +enum hal_tx_rate_stats_sgi { + HAL_TX_RATE_STATS_SGI_08US, + HAL_TX_RATE_STATS_SGI_04US, + HAL_TX_RATE_STATS_SGI_16US, + HAL_TX_RATE_STATS_SGI_32US, +}; + +struct hal_tx_rate_stats { + u32 info0; + u32 tsf; +} __packed; + +struct hal_wbm_link_desc { + struct ath11k_buffer_addr buf_addr_info; +} __packed; + +/* hal_wbm_link_desc + * + * Producer: WBM + * Consumer: WBM + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + */ + +enum hal_wbm_rel_src_module { + HAL_WBM_REL_SRC_MODULE_TQM, + HAL_WBM_REL_SRC_MODULE_RXDMA, + HAL_WBM_REL_SRC_MODULE_REO, + HAL_WBM_REL_SRC_MODULE_FW, + HAL_WBM_REL_SRC_MODULE_SW, +}; + +enum hal_wbm_rel_desc_type { + HAL_WBM_REL_DESC_TYPE_REL_MSDU, + HAL_WBM_REL_DESC_TYPE_MSDU_LINK, + HAL_WBM_REL_DESC_TYPE_MPDU_LINK, + HAL_WBM_REL_DESC_TYPE_MSDU_EXT, + HAL_WBM_REL_DESC_TYPE_QUEUE_EXT, +}; + +/* hal_wbm_rel_desc_type + * + * msdu_buffer + * The address points to an MSDU buffer + * + * msdu_link_descriptor + * The address points to an Tx MSDU link descriptor + * + * mpdu_link_descriptor + * The address points to an MPDU link descriptor + * + * msdu_ext_descriptor + * The address points to an MSDU extension descriptor + * + * queue_ext_descriptor + * The address points to an TQM queue extension descriptor. WBM should + * treat this is the same way as a link descriptor. + */ + +enum hal_wbm_rel_bm_act { + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE, + HAL_WBM_REL_BM_ACT_REL_MSDU, +}; + +/* hal_wbm_rel_bm_act + * + * put_in_idle_list + * Put the buffer or descriptor back in the idle list. In case of MSDU or + * MDPU link descriptor, BM does not need to check to release any + * individual MSDU buffers. + * + * release_msdu_list + * This BM action can only be used in combination with desc_type being + * msdu_link_descriptor. Field first_msdu_index points out which MSDU + * pointer in the MSDU link descriptor is the first of an MPDU that is + * released. BM shall release all the MSDU buffers linked to this first + * MSDU buffer pointer. All related MSDU buffer pointer entries shall be + * set to value 0, which represents the 'NULL' pointer. When all MSDU + * buffer pointers in the MSDU link descriptor are 'NULL', the MSDU link + * descriptor itself shall also be released. + */ + +#define HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE GENMASK(2, 0) +#define HAL_WBM_RELEASE_INFO0_BM_ACTION GENMASK(5, 3) +#define HAL_WBM_RELEASE_INFO0_DESC_TYPE GENMASK(8, 6) +#define HAL_WBM_RELEASE_INFO0_FIRST_MSDU_IDX GENMASK(12, 9) +#define HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON GENMASK(16, 13) +#define HAL_WBM_RELEASE_INFO0_RXDMA_PUSH_REASON GENMASK(18, 17) +#define HAL_WBM_RELEASE_INFO0_RXDMA_ERROR_CODE GENMASK(23, 19) +#define HAL_WBM_RELEASE_INFO0_REO_PUSH_REASON GENMASK(25, 24) +#define HAL_WBM_RELEASE_INFO0_REO_ERROR_CODE GENMASK(30, 26) +#define HAL_WBM_RELEASE_INFO0_WBM_INTERNAL_ERROR BIT(31) + +#define HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER GENMASK(23, 0) +#define HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT GENMASK(30, 24) + +#define HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI GENMASK(7, 0) +#define HAL_WBM_RELEASE_INFO2_SW_REL_DETAILS_VALID BIT(8) +#define HAL_WBM_RELEASE_INFO2_FIRST_MSDU BIT(9) +#define HAL_WBM_RELEASE_INFO2_LAST_MSDU BIT(10) +#define HAL_WBM_RELEASE_INFO2_MSDU_IN_AMSDU BIT(11) +#define HAL_WBM_RELEASE_INFO2_FW_TX_NOTIF_FRAME BIT(12) +#define HAL_WBM_RELEASE_INFO2_BUFFER_TIMESTAMP GENMASK(31, 13) + +#define HAL_WBM_RELEASE_INFO3_PEER_ID GENMASK(15, 0) +#define HAL_WBM_RELEASE_INFO3_TID GENMASK(19, 16) +#define HAL_WBM_RELEASE_INFO3_RING_ID GENMASK(27, 20) +#define HAL_WBM_RELEASE_INFO3_LOOPING_COUNT GENMASK(31, 28) + +#define HAL_WBM_REL_HTT_TX_COMP_INFO0_STATUS GENMASK(12, 9) +#define HAL_WBM_REL_HTT_TX_COMP_INFO0_REINJ_REASON GENMASK(16, 13) +#define HAL_WBM_REL_HTT_TX_COMP_INFO0_EXP_FRAME BIT(17) + +struct hal_wbm_release_ring { + struct ath11k_buffer_addr buf_addr_info; + u32 info0; + u32 info1; + u32 info2; + struct hal_tx_rate_stats rate_stats; + u32 info3; +} __packed; + +/* hal_wbm_release_ring + * + * Producer: SW/TQM/RXDMA/REO/SWITCH + * Consumer: WBM/SW/FW + * + * HTT tx status is overlayed on wbm_release ring on 4-byte words 2, 3, 4 and 5 + * for software based completions. + * + * buf_addr_info + * Details of the physical address of the buffer or link descriptor. + * + * release_source_module + * Indicates which module initiated the release of this buffer/descriptor. + * Values are defined in enum %HAL_WBM_REL_SRC_MODULE_. + * + * bm_action + * Field only valid when the field return_buffer_manager in + * Released_buff_or_desc_addr_info indicates: + * WBM_IDLE_BUF_LIST / WBM_IDLE_DESC_LIST + * Values are defined in enum %HAL_WBM_REL_BM_ACT_. + * + * buffer_or_desc_type + * Field only valid when WBM is marked as the return_buffer_manager in + * the Released_Buffer_address_info. Indicates that type of buffer or + * descriptor is being released. Values are in enum %HAL_WBM_REL_DESC_TYPE. + * + * first_msdu_index + * Field only valid for the bm_action release_msdu_list. The index of the + * first MSDU in an MSDU link descriptor all belonging to the same MPDU. + * + * tqm_release_reason + * Field only valid when Release_source_module is set to release_source_TQM + * Release reasons are defined in enum %HAL_WBM_TQM_REL_REASON_. + * + * rxdma_push_reason + * reo_push_reason + * Indicates why rxdma/reo pushed the frame to this ring and values are + * defined in enum %HAL_REO_DEST_RING_PUSH_REASON_. + * + * rxdma_error_code + * Field only valid when 'rxdma_push_reason' set to 'error_detected'. + * Values are defined in enum %HAL_REO_ENTR_RING_RXDMA_ECODE_. + * + * reo_error_code + * Field only valid when 'reo_push_reason' set to 'error_detected'. Values + * are defined in enum %HAL_REO_DEST_RING_ERROR_CODE_. + * + * wbm_internal_error + * Is set when WBM got a buffer pointer but the action was to push it to + * the idle link descriptor ring or do link related activity OR + * Is set when WBM got a link buffer pointer but the action was to push it + * to the buffer descriptor ring. + * + * tqm_status_number + * The value in this field is equal to tqm_cmd_number in TQM command. It is + * used to correlate the statu with TQM commands. Only valid when + * release_source_module is TQM. + * + * transmit_count + * The number of times the frame has been transmitted, valid only when + * release source in TQM. + * + * ack_frame_rssi + * This field is only valid when the source is TQM. If this frame is + * removed as the result of the reception of an ACK or BA, this field + * indicates the RSSI of the received ACK or BA frame. + * + * sw_release_details_valid + * This is set when WMB got a 'release_msdu_list' command from TQM and + * return buffer manager is not WMB. WBM will then de-aggregate all MSDUs + * and pass them one at a time on to the 'buffer owner'. + * + * first_msdu + * Field only valid when SW_release_details_valid is set. + * When set, this MSDU is the first MSDU pointed to in the + * 'release_msdu_list' command. + * + * last_msdu + * Field only valid when SW_release_details_valid is set. + * When set, this MSDU is the last MSDU pointed to in the + * 'release_msdu_list' command. + * + * msdu_part_of_amsdu + * Field only valid when SW_release_details_valid is set. + * When set, this MSDU was part of an A-MSDU in MPDU + * + * fw_tx_notify_frame + * Field only valid when SW_release_details_valid is set. + * + * buffer_timestamp + * Field only valid when SW_release_details_valid is set. + * This is the Buffer_timestamp field from the + * Timestamp in units of 1024 us + * + * struct hal_tx_rate_stats rate_stats + * Details for command execution tracking purposes. + * + * sw_peer_id + * tid + * Field only valid when Release_source_module is set to + * release_source_TQM + * + * 1) Release of msdu buffer due to drop_frame = 1. Flow is + * not fetched and hence sw_peer_id and tid = 0 + * + * buffer_or_desc_type = e_num 0 + * MSDU_rel_buffertqm_release_reason = e_num 1 + * tqm_rr_rem_cmd_rem + * + * 2) Release of msdu buffer due to Flow is not fetched and + * hence sw_peer_id and tid = 0 + * + * buffer_or_desc_type = e_num 0 + * MSDU_rel_buffertqm_release_reason = e_num 1 + * tqm_rr_rem_cmd_rem + * + * 3) Release of msdu link due to remove_mpdu or acked_mpdu + * command. + * + * buffer_or_desc_type = e_num1 + * msdu_link_descriptortqm_release_reason can be:e_num 1 + * tqm_rr_rem_cmd_reme_num 2 tqm_rr_rem_cmd_tx + * e_num 3 tqm_rr_rem_cmd_notxe_num 4 tqm_rr_rem_cmd_aged + * + * This field represents the TID from the TX_MSDU_FLOW + * descriptor or TX_MPDU_QUEUE descriptor + * + * rind_id + * For debugging. + * This field is filled in by the SRNG module. + * It help to identify the ring that is being looked + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Buffer Manager Ring has looped + * around the ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +/** + * enum hal_wbm_tqm_rel_reason - TQM release reason code + * @HAL_WBM_TQM_REL_REASON_FRAME_ACKED: ACK or BACK received for the frame + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_MPDU: Command remove_mpdus initiated by SW + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX: Command remove transmitted_mpdus + * initiated by sw. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_NOTX: Command remove untransmitted_mpdus + * initiated by sw. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_AGED_FRAMES: Command remove aged msdus or + * mpdus. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON1: Remove command initiated by + * fw with fw_reason1. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON2: Remove command initiated by + * fw with fw_reason2. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON3: Remove command initiated by + * fw with fw_reason3. + */ +enum hal_wbm_tqm_rel_reason { + HAL_WBM_TQM_REL_REASON_FRAME_ACKED, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_MPDU, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_NOTX, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_AGED_FRAMES, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON1, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON2, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON3, +}; + +struct hal_wbm_buffer_ring { + struct ath11k_buffer_addr buf_addr_info; +}; + +enum hal_desc_owner { + HAL_DESC_OWNER_WBM, + HAL_DESC_OWNER_SW, + HAL_DESC_OWNER_TQM, + HAL_DESC_OWNER_RXDMA, + HAL_DESC_OWNER_REO, + HAL_DESC_OWNER_SWITCH, +}; + +enum hal_desc_buf_type { + HAL_DESC_BUF_TYPE_TX_MSDU_LINK, + HAL_DESC_BUF_TYPE_TX_MPDU_LINK, + HAL_DESC_BUF_TYPE_TX_MPDU_QUEUE_HEAD, + HAL_DESC_BUF_TYPE_TX_MPDU_QUEUE_EXT, + HAL_DESC_BUF_TYPE_TX_FLOW, + HAL_DESC_BUF_TYPE_TX_BUFFER, + HAL_DESC_BUF_TYPE_RX_MSDU_LINK, + HAL_DESC_BUF_TYPE_RX_MPDU_LINK, + HAL_DESC_BUF_TYPE_RX_REO_QUEUE, + HAL_DESC_BUF_TYPE_RX_REO_QUEUE_EXT, + HAL_DESC_BUF_TYPE_RX_BUFFER, + HAL_DESC_BUF_TYPE_IDLE_LINK, +}; + +#define HAL_DESC_REO_OWNED 4 +#define HAL_DESC_REO_QUEUE_DESC 8 +#define HAL_DESC_REO_QUEUE_EXT_DESC 9 +#define HAL_DESC_REO_NON_QOS_TID 16 + +#define HAL_DESC_HDR_INFO0_OWNER GENMASK(3, 0) +#define HAL_DESC_HDR_INFO0_BUF_TYPE GENMASK(7, 4) +#define HAL_DESC_HDR_INFO0_DBG_RESERVED GENMASK(31, 8) + +struct hal_desc_header { + u32 info0; +} __packed; + +struct hal_rx_mpdu_link_ptr { + struct ath11k_buffer_addr addr_info; +} __packed; + +struct hal_rx_msdu_details { + struct ath11k_buffer_addr buf_addr_info; + struct rx_msdu_desc rx_msdu_info; +} __packed; + +#define HAL_RX_MSDU_LNK_INFO0_RX_QUEUE_NUMBER GENMASK(15, 0) +#define HAL_RX_MSDU_LNK_INFO0_FIRST_MSDU_LNK BIT(16) + +struct hal_rx_msdu_link { + struct hal_desc_header desc_hdr; + struct ath11k_buffer_addr buf_addr_info; + u32 info0; + u32 pn[4]; + struct hal_rx_msdu_details msdu_link[6]; +} __packed; + +struct hal_rx_reo_queue_ext { + struct hal_desc_header desc_hdr; + u32 rsvd; + struct hal_rx_mpdu_link_ptr mpdu_link[15]; +} __packed; + +/* hal_rx_reo_queue_ext + * Consumer: REO + * Producer: REO + * + * descriptor_header + * Details about which module owns this struct. + * + * mpdu_link + * Pointer to the next MPDU_link descriptor in the MPDU queue. + */ + +enum hal_rx_reo_queue_pn_size { + HAL_RX_REO_QUEUE_PN_SIZE_24, + HAL_RX_REO_QUEUE_PN_SIZE_48, + HAL_RX_REO_QUEUE_PN_SIZE_128, +}; + +#define HAL_RX_REO_QUEUE_RX_QUEUE_NUMBER GENMASK(15, 0) + +#define HAL_RX_REO_QUEUE_INFO0_VLD BIT(0) +#define HAL_RX_REO_QUEUE_INFO0_ASSOC_LNK_DESC_COUNTER GENMASK(2, 1) +#define HAL_RX_REO_QUEUE_INFO0_DIS_DUP_DETECTION BIT(3) +#define HAL_RX_REO_QUEUE_INFO0_SOFT_REORDER_EN BIT(4) +#define HAL_RX_REO_QUEUE_INFO0_AC GENMASK(6, 5) +#define HAL_RX_REO_QUEUE_INFO0_BAR BIT(7) +#define HAL_RX_REO_QUEUE_INFO0_RETRY BIT(8) +#define HAL_RX_REO_QUEUE_INFO0_CHECK_2K_MODE BIT(9) +#define HAL_RX_REO_QUEUE_INFO0_OOR_MODE BIT(10) +#define HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE GENMASK(18, 11) +#define HAL_RX_REO_QUEUE_INFO0_PN_CHECK BIT(19) +#define HAL_RX_REO_QUEUE_INFO0_EVEN_PN BIT(20) +#define HAL_RX_REO_QUEUE_INFO0_UNEVEN_PN BIT(21) +#define HAL_RX_REO_QUEUE_INFO0_PN_HANDLE_ENABLE BIT(22) +#define HAL_RX_REO_QUEUE_INFO0_PN_SIZE GENMASK(24, 23) +#define HAL_RX_REO_QUEUE_INFO0_IGNORE_AMPDU_FLG BIT(25) + +#define HAL_RX_REO_QUEUE_INFO1_SVLD BIT(0) +#define HAL_RX_REO_QUEUE_INFO1_SSN GENMASK(12, 1) +#define HAL_RX_REO_QUEUE_INFO1_CURRENT_IDX GENMASK(20, 13) +#define HAL_RX_REO_QUEUE_INFO1_SEQ_2K_ERR BIT(21) +#define HAL_RX_REO_QUEUE_INFO1_PN_ERR BIT(22) +#define HAL_RX_REO_QUEUE_INFO1_PN_VALID BIT(31) + +#define HAL_RX_REO_QUEUE_INFO2_MPDU_COUNT GENMASK(6, 0) +#define HAL_RX_REO_QUEUE_INFO2_MSDU_COUNT (31, 7) + +#define HAL_RX_REO_QUEUE_INFO3_TIMEOUT_COUNT GENMASK(9, 4) +#define HAL_RX_REO_QUEUE_INFO3_FWD_DUE_TO_BAR_CNT GENMASK(15, 10) +#define HAL_RX_REO_QUEUE_INFO3_DUPLICATE_COUNT GENMASK(31, 10) + +#define HAL_RX_REO_QUEUE_INFO4_FRAME_IN_ORD_COUNT GENMASK(23, 0) +#define HAL_RX_REO_QUEUE_INFO4_BAR_RECVD_COUNT GENMASK(31, 24) + +#define HAL_RX_REO_QUEUE_INFO5_LATE_RX_MPDU_COUNT GENMASK(11, 0) +#define HAL_RX_REO_QUEUE_INFO5_WINDOW_JUMP_2K GENMASK(15, 12) +#define HAL_RX_REO_QUEUE_INFO5_HOLE_COUNT GENMASK(31, 16) + +struct hal_rx_reo_queue { + struct hal_desc_header desc_hdr; + u32 rx_queue_num; + u32 info0; + u32 info1; + u32 pn[4]; + u32 last_rx_enqueue_timestamp; + u32 last_rx_dequeue_timestamp; + u32 next_aging_queue[2]; + u32 prev_aging_queue[2]; + u32 rx_bitmap[8]; + u32 info2; + u32 info3; + u32 info4; + u32 processed_mpdus; + u32 processed_msdus; + u32 processed_total_bytes; + u32 info5; + u32 rsvd[3]; + struct hal_rx_reo_queue_ext ext_desc[0]; +} __packed; + +/* hal_rx_reo_queue + * + * descriptor_header + * Details about which module owns this struct. Note that sub field + * Buffer_type shall be set to receive_reo_queue_descriptor. + * + * receive_queue_number + * Indicates the MPDU queue ID to which this MPDU link descriptor belongs. + * + * vld + * Valid bit indicating a session is established and the queue descriptor + * is valid. + * associated_link_descriptor_counter + * Indicates which of the 3 link descriptor counters shall be incremented + * or decremented when link descriptors are added or removed from this + * flow queue. + * disable_duplicate_detection + * When set, do not perform any duplicate detection. + * soft_reorder_enable + * When set, REO has been instructed to not perform the actual re-ordering + * of frames for this queue, but just to insert the reorder opcodes. + * ac + * Indicates the access category of the queue descriptor. + * bar + * Indicates if BAR has been received. + * retry + * Retry bit is checked if this bit is set. + * chk_2k_mode + * Indicates what type of operation is expected from Reo when the received + * frame SN falls within the 2K window. + * oor_mode + * Indicates what type of operation is expected when the received frame + * falls within the OOR window. + * ba_window_size + * Indicates the negotiated (window size + 1). Max of 256 bits. + * + * A value 255 means 256 bitmap, 63 means 64 bitmap, 0 (means non-BA + * session, with window size of 0). The 3 values here are the main values + * validated, but other values should work as well. + * + * A BA window size of 0 (=> one frame entry bitmat), means that there is + * no additional rx_reo_queue_ext desc. following rx_reo_queue in memory. + * A BA window size of 1 - 105, means that there is 1 rx_reo_queue_ext. + * A BA window size of 106 - 210, means that there are 2 rx_reo_queue_ext. + * A BA window size of 211 - 256, means that there are 3 rx_reo_queue_ext. + * pn_check_needed, pn_shall_be_even, pn_shall_be_uneven, pn_handling_enable, + * pn_size + * REO shall perform the PN increment check, even number check, uneven + * number check, PN error check and size of the PN field check. + * ignore_ampdu_flag + * REO shall ignore the ampdu_flag on entrance descriptor for this queue. + * + * svld + * Sequence number in next field is valid one. + * ssn + * Starting Sequence number of the session. + * current_index + * Points to last forwarded packet + * seq_2k_error_detected_flag + * REO has detected a 2k error jump in the sequence number and from that + * moment forward, all new frames are forwarded directly to FW, without + * duplicate detect, reordering, etc. + * pn_error_detected_flag + * REO has detected a PN error. + */ + +#define HAL_REO_UPD_RX_QUEUE_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RX_QUEUE_NUM BIT(8) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_VLD BIT(9) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_ASSOC_LNK_DESC_CNT BIT(10) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_DIS_DUP_DETECTION BIT(11) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SOFT_REORDER_EN BIT(12) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_AC BIT(13) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BAR BIT(14) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RETRY BIT(15) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_CHECK_2K_MODE BIT(16) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_OOR_MODE BIT(17) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BA_WINDOW_SIZE BIT(18) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_CHECK BIT(19) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_EVEN_PN BIT(20) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_UNEVEN_PN BIT(21) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_HANDLE_ENABLE BIT(22) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_SIZE BIT(23) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_IGNORE_AMPDU_FLG BIT(24) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SVLD BIT(25) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SSN BIT(26) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SEQ_2K_ERR BIT(27) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_ERR BIT(28) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_VALID BIT(29) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN BIT(30) + +#define HAL_REO_UPD_RX_QUEUE_INFO1_RX_QUEUE_NUMBER GENMASK(15, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO1_VLD BIT(16) +#define HAL_REO_UPD_RX_QUEUE_INFO1_ASSOC_LNK_DESC_COUNTER GENMASK(18, 17) +#define HAL_REO_UPD_RX_QUEUE_INFO1_DIS_DUP_DETECTION BIT(19) +#define HAL_REO_UPD_RX_QUEUE_INFO1_SOFT_REORDER_EN BIT(20) +#define HAL_REO_UPD_RX_QUEUE_INFO1_AC GENMASK(22, 21) +#define HAL_REO_UPD_RX_QUEUE_INFO1_BAR BIT(23) +#define HAL_REO_UPD_RX_QUEUE_INFO1_RETRY BIT(24) +#define HAL_REO_UPD_RX_QUEUE_INFO1_CHECK_2K_MODE BIT(25) +#define HAL_REO_UPD_RX_QUEUE_INFO1_OOR_MODE BIT(26) +#define HAL_REO_UPD_RX_QUEUE_INFO1_PN_CHECK BIT(27) +#define HAL_REO_UPD_RX_QUEUE_INFO1_EVEN_PN BIT(28) +#define HAL_REO_UPD_RX_QUEUE_INFO1_UNEVEN_PN BIT(29) +#define HAL_REO_UPD_RX_QUEUE_INFO1_PN_HANDLE_ENABLE BIT(30) +#define HAL_REO_UPD_RX_QUEUE_INFO1_IGNORE_AMPDU_FLG BIT(31) + +#define HAL_REO_UPD_RX_QUEUE_INFO2_BA_WINDOW_SIZE GENMASK(7, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_SIZE GENMASK(9, 8) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SVLD BIT(10) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SSN GENMASK(22, 11) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SEQ_2K_ERR BIT(23) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_ERR BIT(24) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_VALID BIT(25) + +struct hal_reo_update_rx_queue { + struct hal_reo_cmd_hdr cmd; + u32 queue_addr_lo; + u32 info0; + u32 info1; + u32 info2; + u32 pn[4]; +} __packed; + +#define HAL_REO_UNBLOCK_CACHE_INFO0_UNBLK_CACHE BIT(0) +#define HAL_REO_UNBLOCK_CACHE_INFO0_RESOURCE_IDX GENMASK(2, 1) + +struct hal_reo_unblock_cache { + struct hal_reo_cmd_hdr cmd; + u32 info0; + u32 rsvd[7]; +} __packed; + +enum hal_reo_exec_status { + HAL_REO_EXEC_STATUS_SUCCESS, + HAL_REO_EXEC_STATUS_BLOCKED, + HAL_REO_EXEC_STATUS_FAILED, + HAL_REO_EXEC_STATUS_RESOURCE_BLOCKED, +}; + +#define HAL_REO_STATUS_HDR_INFO0_STATUS_NUM GENMASK(15, 0) +#define HAL_REO_STATUS_HDR_INFO0_EXEC_TIME GENMASK(25, 16) +#define HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS GENMASK(27, 26) + +struct hal_reo_status_hdr { + u32 info0; + u32 timestamp; +} __packed; + +/* hal_reo_status_hdr + * Producer: REO + * Consumer: SW + * + * status_num + * The value in this field is equal to value of the reo command + * number. This field helps to correlate the statuses with the REO + * commands. + * + * execution_time (in us) + * The amount of time REO took to excecute the command. Note that + * this time does not include the duration of the command waiting + * in the command ring, before the execution started. + * + * execution_status + * Execution status of the command. Values are defined in + * enum %HAL_REO_EXEC_STATUS_. + */ +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_SSN GENMASK(11, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_CUR_IDX GENMASK(19, 12) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MPDU_COUNT GENMASK(6, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MSDU_COUNT GENMASK(31, 7) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_TIMEOUT_COUNT GENMASK(9, 4) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_FDTB_COUNT GENMASK(15, 10) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_DUPLICATE_COUNT GENMASK(31, 16) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_FIO_COUNT GENMASK(23, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_BAR_RCVD_CNT GENMASK(31, 24) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_LATE_RX_MPDU GENMASK(11, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_WINDOW_JMP2K GENMASK(15, 12) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_HOLE_COUNT GENMASK(31, 16) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO5_LOOPING_CNT GENMASK(31, 28) + +struct hal_reo_get_queue_stats_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 pn[4]; + u32 last_rx_enqueue_timestamp; + u32 last_rx_dequeue_timestamp; + u32 rx_bitmap[8]; + u32 info1; + u32 info2; + u32 info3; + u32 num_mpdu_frames; + u32 num_msdu_frames; + u32 total_bytes; + u32 info4; + u32 info5; +} __packed; + +/* hal_reo_get_queue_stats_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * ssn + * Starting Sequence number of the session, this changes whenever + * window moves (can be filled by SW then maintained by REO). + * + * current_index + * Points to last forwarded packet. + * + * pn + * Bits of the PN number. + * + * last_rx_enqueue_timestamp + * last_rx_dequeue_timestamp + * Timestamp of arrival of the last MPDU for this queue and + * Timestamp of forwarding an MPDU accordingly. + * + * rx_bitmap + * When a bit is set, the corresponding frame is currently held + * in the re-order queue. The bitmap is Fully managed by HW. + * + * current_mpdu_count + * current_msdu_count + * The number of MPDUs and MSDUs in the queue. + * + * timeout_count + * The number of times REO started forwarding frames even though + * there is a hole in the bitmap. Forwarding reason is timeout. + * + * forward_due_to_bar_count + * The number of times REO started forwarding frames even though + * there is a hole in the bitmap. Fwd reason is reception of BAR. + * + * duplicate_count + * The number of duplicate frames that have been detected. + * + * frames_in_order_count + * The number of frames that have been received in order (without + * a hole that prevented them from being forwarded immediately). + * + * bar_received_count + * The number of times a BAR frame is received. + * + * mpdu_frames_processed_count + * msdu_frames_processed_count + * The total number of MPDU/MSDU frames that have been processed. + * + * total_bytes + * An approximation of the number of bytes received for this queue. + * + * late_receive_mpdu_count + * The number of MPDUs received after the window had already moved + * on. The 'late' sequence window is defined as + * (Window SSN - 256) - (Window SSN - 1). + * + * window_jump_2k + * The number of times the window moved more than 2K + * + * hole_count + * The number of times a hole was created in the receive bitmap. + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_STATUS_LOOP_CNT GENMASK(31, 28) + +#define HAL_REO_FLUSH_QUEUE_INFO0_ERR_DETECTED BIT(0) +#define HAL_REO_FLUSH_QUEUE_INFO0_RSVD GENMASK(31, 1) +#define HAL_REO_FLUSH_QUEUE_INFO1_RSVD GENMASK(27, 0) + +struct hal_reo_flush_queue_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 rsvd0[21]; + u32 info1; +} __packed; + +/* hal_reo_flush_queue_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * Status of blocking resource + * + * 0 - No error has been detected while executing this command + * 1 - Error detected. The resource to be used for blocking was + * already in use. + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_IS_ERR BIT(0) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_BLOCK_ERR_CODE GENMASK(2, 1) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_STATUS_HIT BIT(8) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_DESC_TYPE GENMASK(11, 9) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_CLIENT_ID GENMASK(15, 12) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_ERR GENMASK(17, 16) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_COUNT GENMASK(25, 18) + +struct hal_reo_flush_cache_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 rsvd0[21]; + u32 info1; +} __packed; + +/* hal_reo_flush_cache_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * Status for blocking resource handling + * + * 0 - No error has been detected while executing this command + * 1 - An error in the blocking resource management was detected + * + * block_error_details + * only valid when error_detected is set + * + * 0 - No blocking related errors found + * 1 - Blocking resource is already in use + * 2 - Resource requested to be unblocked, was not blocked + * + * cache_controller_flush_status_hit + * The status that the cache controller returned on executing the + * flush command. + * + * 0 - miss; 1 - hit + * + * cache_controller_flush_status_desc_type + * Flush descriptor type + * + * cache_controller_flush_status_client_id + * Module who made the flush request + * + * In REO, this is always 0 + * + * cache_controller_flush_status_error + * Error condition + * + * 0 - No error found + * 1 - HW interface is still busy + * 2 - Line currently locked. Used for one line flush command + * 3 - At least one line is still locked. + * Used for cache flush command. + * + * cache_controller_flush_count + * The number of lines that were actually flushed out + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_IS_ERR BIT(0) +#define HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_TYPE BIT(1) + +struct hal_reo_unblock_cache_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 rsvd0[21]; + u32 info1; +} __packed; + +/* hal_reo_unblock_cache_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * 0 - No error has been detected while executing this command + * 1 - The blocking resource was not in use, and therefore it could + * not be unblocked. + * + * unblock_type + * Reference to the type of unblock command + * 0 - Unblock a blocking resource + * 1 - The entire cache usage is unblock + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_IS_ERR BIT(0) +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_LIST_EMPTY BIT(1) + +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_REL_DESC_COUNT GENMASK(15, 0) +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_FWD_BUF_COUNT GENMASK(31, 16) + +struct hal_reo_flush_timeout_list_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 info1; + u32 rsvd0[20]; + u32 info2; +} __packed; + +/* hal_reo_flush_timeout_list_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * 0 - No error has been detected while executing this command + * 1 - Command not properly executed and returned with error + * + * timeout_list_empty + * When set, REO has depleted the timeout list and all entries are + * gone. + * + * release_desc_count + * Producer: SW; Consumer: REO + * The number of link descriptor released + * + * forward_buf_count + * Producer: SW; Consumer: REO + * The number of buffers forwarded to the REO destination rings + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_DESC_THRESH_STATUS_INFO0_THRESH_INDEX GENMASK(1, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO1_LINK_DESC_COUNTER0 GENMASK(23, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO2_LINK_DESC_COUNTER1 GENMASK(23, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO3_LINK_DESC_COUNTER2 GENMASK(23, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO4_LINK_DESC_COUNTER_SUM GENMASK(23, 0) + +struct hal_reo_desc_thresh_reached_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 info1; + u32 info2; + u32 info3; + u32 info4; + u32 rsvd0[17]; + u32 info5; +} __packed; + +/* hal_reo_desc_thresh_reached_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * threshold_index + * The index of the threshold register whose value got reached + * + * link_descriptor_counter0 + * link_descriptor_counter1 + * link_descriptor_counter2 + * link_descriptor_counter_sum + * Value of the respective counters at generation of this message + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#endif /* ATH11K_HAL_DESC_H */ diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c new file mode 100644 index 000000000000..9e0f8064e427 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -0,0 +1,1190 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "ahb.h" +#include "debug.h" +#include "hal.h" +#include "hal_tx.h" +#include "hal_rx.h" +#include "hal_desc.h" + +static void ath11k_hal_reo_set_desc_hdr(struct hal_desc_header *hdr, + u8 owner, u8 buffer_type, u32 magic) +{ + hdr->info0 = FIELD_PREP(HAL_DESC_HDR_INFO0_OWNER, owner) | + FIELD_PREP(HAL_DESC_HDR_INFO0_BUF_TYPE, buffer_type); + + /* Magic pattern in reserved bits for debugging */ + hdr->info0 |= FIELD_PREP(HAL_DESC_HDR_INFO0_DBG_RESERVED, magic); +} + +static int ath11k_hal_reo_cmd_queue_stats(struct hal_tlv_hdr *tlv, + struct ath11k_hal_reo_cmd *cmd) +{ + struct hal_reo_get_queue_stats *desc; + + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_REO_GET_QUEUE_STATS) | + FIELD_PREP(HAL_TLV_HDR_LEN, sizeof(*desc)); + + desc = (struct hal_reo_get_queue_stats *)tlv->value; + memset(&desc->queue_addr_lo, 0, + (sizeof(*desc) - sizeof(struct hal_reo_cmd_hdr))); + + desc->cmd.info0 &= ~HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) + desc->cmd.info0 |= HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + + desc->queue_addr_lo = cmd->addr_lo; + desc->info0 = FIELD_PREP(HAL_REO_GET_QUEUE_STATS_INFO0_QUEUE_ADDR_HI, + cmd->addr_hi); + if (cmd->flag & HAL_REO_CMD_FLG_STATS_CLEAR) + desc->info0 |= HAL_REO_GET_QUEUE_STATS_INFO0_CLEAR_STATS; + + return FIELD_GET(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, desc->cmd.info0); +} + +static int ath11k_hal_reo_cmd_flush_cache(struct ath11k_hal *hal, struct hal_tlv_hdr *tlv, + struct ath11k_hal_reo_cmd *cmd) +{ + struct hal_reo_flush_cache *desc; + u8 avail_slot = ffz(hal->avail_blk_resource); + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER) { + if (avail_slot >= HAL_MAX_AVAIL_BLK_RES) + return -ENOSPC; + + hal->current_blk_index = avail_slot; + } + + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_REO_FLUSH_CACHE) | + FIELD_PREP(HAL_TLV_HDR_LEN, sizeof(*desc)); + + desc = (struct hal_reo_flush_cache *)tlv->value; + memset(&desc->cache_addr_lo, 0, + (sizeof(*desc) - sizeof(struct hal_reo_cmd_hdr))); + + desc->cmd.info0 &= ~HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) + desc->cmd.info0 |= HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + + desc->cache_addr_lo = cmd->addr_lo; + desc->info0 = FIELD_PREP(HAL_REO_FLUSH_CACHE_INFO0_CACHE_ADDR_HI, + cmd->addr_hi); + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS) + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_FWD_ALL_MPDUS; + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER) { + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_BLOCK_CACHE_USAGE; + desc->info0 |= + FIELD_PREP(HAL_REO_FLUSH_CACHE_INFO0_BLOCK_RESRC_IDX, + avail_slot); + } + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_NO_INVAL) + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_FLUSH_WO_INVALIDATE; + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_ALL) + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_FLUSH_ALL; + + return FIELD_GET(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, desc->cmd.info0); +} + +static int ath11k_hal_reo_cmd_update_rx_queue(struct hal_tlv_hdr *tlv, + struct ath11k_hal_reo_cmd *cmd) +{ + struct hal_reo_update_rx_queue *desc; + + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_REO_UPDATE_RX_REO_QUEUE) | + FIELD_PREP(HAL_TLV_HDR_LEN, sizeof(*desc)); + + desc = (struct hal_reo_update_rx_queue *)tlv->value; + memset(&desc->queue_addr_lo, 0, + (sizeof(*desc) - sizeof(struct hal_reo_cmd_hdr))); + + desc->cmd.info0 &= ~HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) + desc->cmd.info0 |= HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + + desc->queue_addr_lo = cmd->addr_lo; + desc->info0 = + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_QUEUE_ADDR_HI, + cmd->addr_hi) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RX_QUEUE_NUM, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_RX_QUEUE_NUM)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_VLD, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_VLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_ASSOC_LNK_DESC_CNT, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_ALDC)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_DIS_DUP_DETECTION, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_DIS_DUP_DETECTION)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SOFT_REORDER_EN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SOFT_REORDER_EN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_AC, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_AC)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BAR, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_BAR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RETRY, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_RETRY)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_CHECK_2K_MODE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_CHECK_2K_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_OOR_MODE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_OOR_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BA_WINDOW_SIZE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_BA_WINDOW_SIZE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_CHECK, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_CHECK)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_EVEN_PN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_EVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_UNEVEN_PN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_UNEVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_HANDLE_ENABLE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_HANDLE_ENABLE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_SIZE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_SIZE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_IGNORE_AMPDU_FLG, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_IGNORE_AMPDU_FLG)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SVLD, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SVLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SSN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SSN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SEQ_2K_ERR, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SEQ_2K_ERR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_VALID, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_VALID)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN)); + + desc->info1 = + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_RX_QUEUE_NUMBER, + cmd->rx_queue_num) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_VLD, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_VLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_ASSOC_LNK_DESC_COUNTER, + FIELD_GET(HAL_REO_CMD_UPD1_ALDC, cmd->upd1)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_DIS_DUP_DETECTION, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_DIS_DUP_DETECTION)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_SOFT_REORDER_EN, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_SOFT_REORDER_EN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_AC, + FIELD_GET(HAL_REO_CMD_UPD1_AC, cmd->upd1)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_BAR, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_BAR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_CHECK_2K_MODE, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_CHECK_2K_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_RETRY, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_RETRY)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_OOR_MODE, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_OOR_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_PN_CHECK, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_PN_CHECK)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_EVEN_PN, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_EVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_UNEVEN_PN, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_UNEVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_PN_HANDLE_ENABLE, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_IGNORE_AMPDU_FLG, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG)); + + if (cmd->pn_size == 24) + cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_24; + else if (cmd->pn_size == 48) + cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_48; + else if (cmd->pn_size == 128) + cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_128; + + if (cmd->ba_window_size < 1) + cmd->ba_window_size = 1; + + if (cmd->ba_window_size == 1) + cmd->ba_window_size++; + + desc->info2 = + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_BA_WINDOW_SIZE, + cmd->ba_window_size - 1) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_PN_SIZE, cmd->pn_size) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_SVLD, + !!(cmd->upd2 & HAL_REO_CMD_UPD2_SVLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_SSN, + FIELD_GET(HAL_REO_CMD_UPD2_SSN, cmd->upd2)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_SEQ_2K_ERR, + !!(cmd->upd2 & HAL_REO_CMD_UPD2_SEQ_2K_ERR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_PN_ERR, + !!(cmd->upd2 & HAL_REO_CMD_UPD2_PN_ERR)); + + return FIELD_GET(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, desc->cmd.info0); +} + +int ath11k_hal_reo_cmd_send(struct ath11k_base *ab, struct hal_srng *srng, + enum hal_reo_cmd_type type, + struct ath11k_hal_reo_cmd *cmd) +{ + struct hal_tlv_hdr *reo_desc; + int ret; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + reo_desc = (struct hal_tlv_hdr *)ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!reo_desc) { + ret = -ENOBUFS; + goto out; + } + + switch (type) { + case HAL_REO_CMD_GET_QUEUE_STATS: + ret = ath11k_hal_reo_cmd_queue_stats(reo_desc, cmd); + break; + case HAL_REO_CMD_FLUSH_CACHE: + ret = ath11k_hal_reo_cmd_flush_cache(&ab->hal, reo_desc, cmd); + break; + case HAL_REO_CMD_UPDATE_RX_QUEUE: + ret = ath11k_hal_reo_cmd_update_rx_queue(reo_desc, cmd); + break; + case HAL_REO_CMD_FLUSH_QUEUE: + case HAL_REO_CMD_UNBLOCK_CACHE: + case HAL_REO_CMD_FLUSH_TIMEOUT_LIST: + ath11k_warn(ab, "Unsupported reo command %d\n", type); + ret = -ENOTSUPP; + break; + default: + ath11k_warn(ab, "Unknown reo command %d\n", type); + ret = -EINVAL; + break; + } + +out: + ath11k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + return ret; +} + +void ath11k_hal_rx_buf_addr_info_set(void *desc, dma_addr_t paddr, + u32 cookie, u8 manager) +{ + struct ath11k_buffer_addr *binfo = (struct ath11k_buffer_addr *)desc; + u32 paddr_lo, paddr_hi; + + paddr_lo = lower_32_bits(paddr); + paddr_hi = upper_32_bits(paddr); + binfo->info0 = FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, paddr_lo); + binfo->info1 = FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, paddr_hi) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, cookie) | + FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, manager); +} + +void ath11k_hal_rx_buf_addr_info_get(void *desc, dma_addr_t *paddr, + u32 *cookie, u8 *rbm) +{ + struct ath11k_buffer_addr *binfo = (struct ath11k_buffer_addr *)desc; + + *paddr = + (((u64)FIELD_GET(BUFFER_ADDR_INFO1_ADDR, binfo->info1)) << 32) | + FIELD_GET(BUFFER_ADDR_INFO0_ADDR, binfo->info0); + *cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, binfo->info1); + *rbm = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, binfo->info1); +} + +void ath11k_hal_rx_msdu_link_info_get(void *link_desc, u32 *num_msdus, + u32 *msdu_cookies, + enum hal_rx_buf_return_buf_manager *rbm) +{ + struct hal_rx_msdu_link *link = (struct hal_rx_msdu_link *)link_desc; + struct hal_rx_msdu_details *msdu; + int i; + + *num_msdus = HAL_NUM_RX_MSDUS_PER_LINK_DESC; + + msdu = &link->msdu_link[0]; + *rbm = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, + msdu->buf_addr_info.info1); + + for (i = 0; i < *num_msdus; i++) { + msdu = &link->msdu_link[i]; + + if (!FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + msdu->buf_addr_info.info0)) { + *num_msdus = i; + break; + } + *msdu_cookies = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + msdu->buf_addr_info.info1); + msdu_cookies++; + } +} + +int ath11k_hal_desc_reo_parse_err(struct ath11k_base *ab, u32 *rx_desc, + dma_addr_t *paddr, u32 *desc_bank) +{ + struct hal_reo_dest_ring *desc = (struct hal_reo_dest_ring *)rx_desc; + enum hal_reo_dest_ring_push_reason push_reason; + enum hal_reo_dest_ring_error_code err_code; + + push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, + desc->info0); + err_code = FIELD_GET(HAL_REO_DEST_RING_INFO0_ERROR_CODE, + desc->info0); + ab->soc_stats.reo_error[err_code]++; + + if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED && + push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { + ath11k_warn(ab, "expected error push reason code, received %d\n", + push_reason); + return -EINVAL; + } + + if (FIELD_GET(HAL_REO_DEST_RING_INFO0_BUFFER_TYPE, desc->info0) != + HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC) { + ath11k_warn(ab, "expected buffer type link_desc"); + return -EINVAL; + } + + ath11k_hal_rx_reo_ent_paddr_get(ab, rx_desc, paddr, desc_bank); + + return 0; +} + +int ath11k_hal_wbm_desc_parse_err(struct ath11k_base *ab, void *desc, + struct hal_rx_wbm_rel_info *rel_info) +{ + struct hal_wbm_release_ring *wbm_desc = desc; + enum hal_wbm_rel_desc_type type; + enum hal_wbm_rel_src_module rel_src; + + type = FIELD_GET(HAL_WBM_RELEASE_INFO0_DESC_TYPE, + wbm_desc->info0); + /* We expect only WBM_REL buffer type */ + if (type != HAL_WBM_REL_DESC_TYPE_REL_MSDU) { + WARN_ON(1); + return -EINVAL; + } + + rel_src = FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, + wbm_desc->info0); + if (rel_src != HAL_WBM_REL_SRC_MODULE_RXDMA && + rel_src != HAL_WBM_REL_SRC_MODULE_REO) + return -EINVAL; + + if (FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, + wbm_desc->buf_addr_info.info1) != HAL_RX_BUF_RBM_SW3_BM) { + ab->soc_stats.invalid_rbm++; + return -EINVAL; + } + + rel_info->cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + wbm_desc->buf_addr_info.info1); + rel_info->err_rel_src = rel_src; + if (rel_src == HAL_WBM_REL_SRC_MODULE_REO) { + rel_info->push_reason = + FIELD_GET(HAL_WBM_RELEASE_INFO0_REO_PUSH_REASON, + wbm_desc->info0); + rel_info->err_code = + FIELD_GET(HAL_WBM_RELEASE_INFO0_REO_ERROR_CODE, + wbm_desc->info0); + } else { + rel_info->push_reason = + FIELD_GET(HAL_WBM_RELEASE_INFO0_RXDMA_PUSH_REASON, + wbm_desc->info0); + rel_info->err_code = + FIELD_GET(HAL_WBM_RELEASE_INFO0_RXDMA_ERROR_CODE, + wbm_desc->info0); + } + + rel_info->first_msdu = FIELD_GET(HAL_WBM_RELEASE_INFO2_FIRST_MSDU, + wbm_desc->info2); + rel_info->last_msdu = FIELD_GET(HAL_WBM_RELEASE_INFO2_LAST_MSDU, + wbm_desc->info2); + return 0; +} + +void ath11k_hal_rx_reo_ent_paddr_get(struct ath11k_base *ab, void *desc, + dma_addr_t *paddr, u32 *desc_bank) +{ + struct ath11k_buffer_addr *buff_addr = desc; + + *paddr = ((u64)(FIELD_GET(BUFFER_ADDR_INFO1_ADDR, buff_addr->info1)) << 32) | + FIELD_GET(BUFFER_ADDR_INFO0_ADDR, buff_addr->info0); + + *desc_bank = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, buff_addr->info1); +} + +void ath11k_hal_rx_msdu_link_desc_set(struct ath11k_base *ab, void *desc, + void *link_desc, + enum hal_wbm_rel_bm_act action) +{ + struct hal_wbm_release_ring *dst_desc = desc; + struct hal_wbm_release_ring *src_desc = link_desc; + + dst_desc->buf_addr_info = src_desc->buf_addr_info; + dst_desc->info0 |= FIELD_PREP(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, + HAL_WBM_REL_SRC_MODULE_SW) | + FIELD_PREP(HAL_WBM_RELEASE_INFO0_BM_ACTION, action) | + FIELD_PREP(HAL_WBM_RELEASE_INFO0_DESC_TYPE, + HAL_WBM_REL_DESC_TYPE_MSDU_LINK); +} + +void ath11k_hal_reo_status_queue_stats(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_get_queue_stats_status *desc = + (struct hal_reo_get_queue_stats_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + ath11k_dbg(ab, ATH11k_DBG_HAL, "Queue stats status:\n"); + ath11k_dbg(ab, ATH11k_DBG_HAL, "header: cmd_num %d status %d\n", + status->uniform_hdr.cmd_num, + status->uniform_hdr.cmd_status); + ath11k_dbg(ab, ATH11k_DBG_HAL, "ssn %ld cur_idx %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_SSN, + desc->info0), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_CUR_IDX, + desc->info0)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "pn = [%08x, %08x, %08x, %08x]\n", + desc->pn[0], desc->pn[1], desc->pn[2], desc->pn[3]); + ath11k_dbg(ab, ATH11k_DBG_HAL, "last_rx: enqueue_tstamp %08x dequeue_tstamp %08x\n", + desc->last_rx_enqueue_timestamp, + desc->last_rx_dequeue_timestamp); + ath11k_dbg(ab, ATH11k_DBG_HAL, "rx_bitmap [%08x %08x %08x %08x %08x %08x %08x %08x]\n", + desc->rx_bitmap[0], desc->rx_bitmap[1], desc->rx_bitmap[2], + desc->rx_bitmap[3], desc->rx_bitmap[4], desc->rx_bitmap[5], + desc->rx_bitmap[6], desc->rx_bitmap[7]); + ath11k_dbg(ab, ATH11k_DBG_HAL, "count: cur_mpdu %ld cur_msdu %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MPDU_COUNT, + desc->info1), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MSDU_COUNT, + desc->info1)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "fwd_timeout %ld fwd_bar %ld dup_count %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_TIMEOUT_COUNT, + desc->info2), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_FDTB_COUNT, + desc->info2), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_DUPLICATE_COUNT, + desc->info2)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "frames_in_order %ld bar_rcvd %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_FIO_COUNT, + desc->info3), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_BAR_RCVD_CNT, + desc->info3)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "num_mpdus %d num_msdus %d total_bytes %d\n", + desc->num_mpdu_frames, desc->num_msdu_frames, + desc->total_bytes); + ath11k_dbg(ab, ATH11k_DBG_HAL, "late_rcvd %ld win_jump_2k %ld hole_cnt %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_LATE_RX_MPDU, + desc->info4), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_WINDOW_JMP2K, + desc->info4), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_HOLE_COUNT, + desc->info4)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "looping count %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO5_LOOPING_CNT, + desc->info5)); +} + +int ath11k_hal_reo_process_status(u8 *reo_desc, u8 *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_status_hdr *hdr; + + hdr = (struct hal_reo_status_hdr *)tlv->value; + *status = FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, hdr->info0); + + return FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, hdr->info0); +} + +void ath11k_hal_reo_flush_queue_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_flush_queue_status *desc = + (struct hal_reo_flush_queue_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + status->u.flush_queue.err_detected = + FIELD_GET(HAL_REO_FLUSH_QUEUE_INFO0_ERR_DETECTED, + desc->info0); +} + +void ath11k_hal_reo_flush_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status) +{ + struct ath11k_hal *hal = &ab->hal; + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_flush_cache_status *desc = + (struct hal_reo_flush_cache_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + status->u.flush_cache.err_detected = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_IS_ERR, + desc->info0); + status->u.flush_cache.err_code = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_BLOCK_ERR_CODE, + desc->info0); + if (!status->u.flush_cache.err_code) + hal->avail_blk_resource |= BIT(hal->current_blk_index); + + status->u.flush_cache.cache_controller_flush_status_hit = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_STATUS_HIT, + desc->info0); + + status->u.flush_cache.cache_controller_flush_status_desc_type = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_DESC_TYPE, + desc->info0); + status->u.flush_cache.cache_controller_flush_status_client_id = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_CLIENT_ID, + desc->info0); + status->u.flush_cache.cache_controller_flush_status_err = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_ERR, + desc->info0); + status->u.flush_cache.cache_controller_flush_status_cnt = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_COUNT, + desc->info0); +} + +void ath11k_hal_reo_unblk_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status) +{ + struct ath11k_hal *hal = &ab->hal; + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_unblock_cache_status *desc = + (struct hal_reo_unblock_cache_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + status->u.unblock_cache.err_detected = + FIELD_GET(HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_IS_ERR, + desc->info0); + status->u.unblock_cache.unblock_type = + FIELD_GET(HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_TYPE, + desc->info0); + + if (!status->u.unblock_cache.err_detected && + status->u.unblock_cache.unblock_type == + HAL_REO_STATUS_UNBLOCK_BLOCKING_RESOURCE) + hal->avail_blk_resource &= ~BIT(hal->current_blk_index); +} + +void ath11k_hal_reo_flush_timeout_list_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_flush_timeout_list_status *desc = + (struct hal_reo_flush_timeout_list_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + status->u.timeout_list.err_detected = + FIELD_GET(HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_IS_ERR, + desc->info0); + status->u.timeout_list.list_empty = + FIELD_GET(HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_LIST_EMPTY, + desc->info0); + + status->u.timeout_list.release_desc_cnt = + FIELD_GET(HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_REL_DESC_COUNT, + desc->info1); + status->u.timeout_list.fwd_buf_cnt = + FIELD_GET(HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_FWD_BUF_COUNT, + desc->info1); +} + +void ath11k_hal_reo_desc_thresh_reached_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_desc_thresh_reached_status *desc = + (struct hal_reo_desc_thresh_reached_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + status->u.desc_thresh_reached.threshold_idx = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO0_THRESH_INDEX, + desc->info0); + + status->u.desc_thresh_reached.link_desc_counter0 = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO1_LINK_DESC_COUNTER0, + desc->info1); + + status->u.desc_thresh_reached.link_desc_counter1 = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO2_LINK_DESC_COUNTER1, + desc->info2); + + status->u.desc_thresh_reached.link_desc_counter2 = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO3_LINK_DESC_COUNTER2, + desc->info3); + + status->u.desc_thresh_reached.link_desc_counter_sum = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO4_LINK_DESC_COUNTER_SUM, + desc->info4); +} + +void ath11k_hal_reo_update_rx_reo_queue_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_status_hdr *desc = + (struct hal_reo_status_hdr *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->info0); +} + +u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid) +{ + u32 num_ext_desc; + + if (ba_window_size <= 1) { + if (tid != HAL_DESC_REO_NON_QOS_TID) + num_ext_desc = 1; + else + num_ext_desc = 0; + } else if (ba_window_size <= 105) { + num_ext_desc = 1; + } else if (ba_window_size <= 210) { + num_ext_desc = 2; + } else { + num_ext_desc = 3; + } + + return sizeof(struct hal_rx_reo_queue) + + (num_ext_desc * sizeof(struct hal_rx_reo_queue_ext)); +} + +void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, + u32 start_seq) +{ + struct hal_rx_reo_queue *qdesc = (struct hal_rx_reo_queue *)vaddr; + struct hal_rx_reo_queue_ext *ext_desc; + + memset(qdesc, 0, sizeof(*qdesc)); + + ath11k_hal_reo_set_desc_hdr(&qdesc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_DESC, + REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0); + + qdesc->rx_queue_num = FIELD_PREP(HAL_RX_REO_QUEUE_RX_QUEUE_NUMBER, tid); + + qdesc->info0 = + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_VLD, 1) | + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_ASSOC_LNK_DESC_COUNTER, 1) | + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_AC, ath11k_tid_to_ac(tid)); + + if (ba_window_size < 1) + ba_window_size = 1; + + if (ba_window_size == 1 && tid != HAL_DESC_REO_NON_QOS_TID) + ba_window_size++; + + if (ba_window_size == 1) + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_RETRY, 1); + + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE, + ba_window_size - 1); + + /* TODO: Set Ignore ampdu flags based on BA window size and/or + * AMPDU capabilities + */ + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_IGNORE_AMPDU_FLG, 1); + + qdesc->info1 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO1_SVLD, 0); + + if (start_seq <= 0xfff) + qdesc->info1 = FIELD_PREP(HAL_RX_REO_QUEUE_INFO1_SSN, + start_seq); + + if (tid == HAL_DESC_REO_NON_QOS_TID) + return; + + ext_desc = qdesc->ext_desc; + + /* TODO: HW queue descriptors are currently allocated for max BA + * window size for all QOS TIDs so that same descriptor can be used + * later when ADDBA request is recevied. This should be changed to + * allocate HW queue descriptors based on BA window size being + * negotiated (0 for non BA cases), and reallocate when BA window + * size changes and also send WMI message to FW to change the REO + * queue descriptor in Rx peer entry as part of dp_rx_tid_update. + */ + memset(ext_desc, 0, 3 * sizeof(*ext_desc)); + ath11k_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_EXT_DESC, + REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1); + ext_desc++; + ath11k_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_EXT_DESC, + REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2); + ext_desc++; + ath11k_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_EXT_DESC, + REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_3); +} + +void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab, + struct hal_srng *srng) +{ + struct hal_srng_params params; + struct hal_tlv_hdr *tlv; + struct hal_reo_get_queue_stats *desc; + int i, cmd_num = 1; + int entry_size; + u8 *entry; + + memset(¶ms, 0, sizeof(params)); + + entry_size = ath11k_hal_srng_get_entrysize(HAL_REO_CMD); + ath11k_hal_srng_get_params(ab, srng, ¶ms); + entry = (u8 *)params.ring_base_vaddr; + + for (i = 0; i < params.num_entries; i++) { + tlv = (struct hal_tlv_hdr *)entry; + desc = (struct hal_reo_get_queue_stats *)tlv->value; + desc->cmd.info0 = + FIELD_PREP(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, cmd_num++); + entry += entry_size; + } +} + +void ath11k_hal_reo_hw_setup(struct ath11k_base *ab) +{ + u32 reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; + u32 val; + + val = ath11k_ahb_read32(ab, reo_base + HAL_REO1_GEN_ENABLE); + + val &= ~HAL_REO1_GEN_ENABLE_FRAG_DST_RING; + val |= FIELD_PREP(HAL_REO1_GEN_ENABLE_FRAG_DST_RING, + HAL_SRNG_RING_ID_REO2SW1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE, 1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_GEN_ENABLE, val); + + ath11k_ahb_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_0, + HAL_DEFAULT_REO_TIMEOUT_USEC); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_1, + HAL_DEFAULT_REO_TIMEOUT_USEC); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_2, + HAL_DEFAULT_REO_TIMEOUT_USEC); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3, + HAL_DEFAULT_REO_TIMEOUT_USEC); +} + +static enum hal_rx_mon_status +ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + struct hal_rx_mon_ppdu_info *ppdu_info, + u32 tlv_tag, u8 *tlv_data) +{ + u32 info0, info1; + + switch (tlv_tag) { + case HAL_RX_PPDU_START: { + struct hal_rx_ppdu_start *ppdu_start = + (struct hal_rx_ppdu_start *)tlv_data; + + ppdu_info->ppdu_id = + FIELD_GET(HAL_RX_PPDU_START_INFO0_PPDU_ID, + __le32_to_cpu(ppdu_start->info0)); + ppdu_info->chan_num = __le32_to_cpu(ppdu_start->chan_num); + ppdu_info->ppdu_ts = __le32_to_cpu(ppdu_start->ppdu_start_ts); + break; + } + case HAL_RX_PPDU_END_USER_STATS: { + struct hal_rx_ppdu_end_user_stats *eu_stats = + (struct hal_rx_ppdu_end_user_stats *)tlv_data; + + info0 = __le32_to_cpu(eu_stats->info0); + info1 = __le32_to_cpu(eu_stats->info1); + + ppdu_info->tid = + ffs(FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP, + __le32_to_cpu(eu_stats->info6))) - 1; + ppdu_info->tcp_msdu_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO4_TCP_MSDU_CNT, + __le32_to_cpu(eu_stats->info4)); + ppdu_info->udp_msdu_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO4_UDP_MSDU_CNT, + __le32_to_cpu(eu_stats->info4)); + ppdu_info->other_msdu_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO5_OTHER_MSDU_CNT, + __le32_to_cpu(eu_stats->info5)); + ppdu_info->tcp_ack_msdu_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO5_TCP_ACK_MSDU_CNT, + __le32_to_cpu(eu_stats->info5)); + ppdu_info->preamble_type = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO1_PKT_TYPE, info1); + ppdu_info->num_mpdu_fcs_ok = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO1_MPDU_CNT_FCS_OK, + info1); + ppdu_info->num_mpdu_fcs_err = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR, + info0); + break; + } + case HAL_PHYRX_HT_SIG: { + struct hal_rx_ht_sig_info *ht_sig = + (struct hal_rx_ht_sig_info *)tlv_data; + + info0 = __le32_to_cpu(ht_sig->info0); + info1 = __le32_to_cpu(ht_sig->info1); + + ppdu_info->mcs = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO0_MCS, info0); + ppdu_info->bw = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO0_BW, info0); + ppdu_info->is_stbc = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO1_STBC, + info1); + ppdu_info->ldpc = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING, info1); + ppdu_info->gi = info1 & HAL_RX_HT_SIG_INFO_INFO1_GI; + + switch (ppdu_info->mcs) { + case 0 ... 7: + ppdu_info->nss = 1; + break; + case 8 ... 15: + ppdu_info->nss = 2; + break; + case 16 ... 23: + ppdu_info->nss = 3; + break; + case 24 ... 31: + ppdu_info->nss = 4; + break; + } + + if (ppdu_info->nss > 1) + ppdu_info->mcs = ppdu_info->mcs % 8; + + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_L_SIG_B: { + struct hal_rx_lsig_b_info *lsigb = + (struct hal_rx_lsig_b_info *)tlv_data; + + ppdu_info->rate = FIELD_GET(HAL_RX_LSIG_B_INFO_INFO0_RATE, + __le32_to_cpu(lsigb->info0)); + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_L_SIG_A: { + struct hal_rx_lsig_a_info *lsiga = + (struct hal_rx_lsig_a_info *)tlv_data; + + ppdu_info->rate = FIELD_GET(HAL_RX_LSIG_A_INFO_INFO0_RATE, + __le32_to_cpu(lsiga->info0)); + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_VHT_SIG_A: { + struct hal_rx_vht_sig_a_info *vht_sig = + (struct hal_rx_vht_sig_a_info *)tlv_data; + u32 nsts; + u32 group_id; + u8 gi_setting; + + info0 = __le32_to_cpu(vht_sig->info0); + info1 = __le32_to_cpu(vht_sig->info1); + + ppdu_info->ldpc = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_SU_MU_CODING, + info0); + ppdu_info->mcs = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_MCS, + info1); + gi_setting = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_GI_SETTING, + info1); + switch (gi_setting) { + case HAL_RX_VHT_SIG_A_NORMAL_GI: + ppdu_info->gi = HAL_RX_GI_0_8_US; + break; + case HAL_RX_VHT_SIG_A_SHORT_GI: + case HAL_RX_VHT_SIG_A_SHORT_GI_AMBIGUITY: + ppdu_info->gi = HAL_RX_GI_0_4_US; + break; + } + + ppdu_info->is_stbc = info0 & HAL_RX_VHT_SIG_A_INFO_INFO0_STBC; + nsts = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO0_NSTS, info0); + if (ppdu_info->is_stbc && nsts > 0) + nsts = ((nsts + 1) >> 1) - 1; + + ppdu_info->nss = (nsts & VHT_SIG_SU_NSS_MASK) + 1; + ppdu_info->bw = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO0_BW, + info0); + ppdu_info->beamformed = info1 & + HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED; + group_id = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID, + info0); + if (group_id == 0 || group_id == 63) + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + else + ppdu_info->reception_type = + HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + } + case HAL_PHYRX_HE_SIG_A_SU: { + struct hal_rx_he_sig_a_su_info *he_sig_a = + (struct hal_rx_he_sig_a_su_info *)tlv_data; + u32 nsts, cp_ltf, dcm; + + info0 = __le32_to_cpu(he_sig_a->info0); + info1 = __le32_to_cpu(he_sig_a->info1); + + ppdu_info->mcs = + FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS, + info0); + ppdu_info->bw = + FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW, + info0); + ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING, info0); + ppdu_info->is_stbc = info1 & + HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC; + ppdu_info->beamformed = info1 & + HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF; + dcm = info0 & HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM; + cp_ltf = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE, + info0); + nsts = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS, info0); + + switch (cp_ltf) { + case 0: + case 1: + ppdu_info->gi = HAL_RX_GI_0_8_US; + break; + case 2: + ppdu_info->gi = HAL_RX_GI_1_6_US; + break; + case 3: + if (dcm && ppdu_info->is_stbc) + ppdu_info->gi = HAL_RX_GI_0_8_US; + else + ppdu_info->gi = HAL_RX_GI_3_2_US; + break; + } + + ppdu_info->nss = nsts + 1; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_HE_SIG_A_MU_DL: { + struct hal_rx_he_sig_a_mu_dl_info *he_sig_a_mu_dl = + (struct hal_rx_he_sig_a_mu_dl_info *)tlv_data; + + u32 cp_ltf; + + info0 = __le32_to_cpu(he_sig_a_mu_dl->info0); + info1 = __le32_to_cpu(he_sig_a_mu_dl->info1); + + ppdu_info->bw = + FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW, + info0); + cp_ltf = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE, + info0); + + switch (cp_ltf) { + case 0: + case 1: + ppdu_info->gi = HAL_RX_GI_0_8_US; + break; + case 2: + ppdu_info->gi = HAL_RX_GI_1_6_US; + break; + case 3: + ppdu_info->gi = HAL_RX_GI_3_2_US; + break; + } + + ppdu_info->is_stbc = info1 & + HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + } + case HAL_PHYRX_HE_SIG_B1_MU: { + /* TODO: Check if resource unit(RU) allocation stats + * are required + */ + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + } + case HAL_PHYRX_HE_SIG_B2_MU: { + struct hal_rx_he_sig_b2_mu_info *he_sig_b2_mu = + (struct hal_rx_he_sig_b2_mu_info *)tlv_data; + + info0 = __le32_to_cpu(he_sig_b2_mu->info0); + + ppdu_info->mcs = + FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS, + info0); + ppdu_info->nss = + FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS, + info0) + 1; + ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING, + info0); + break; + } + case HAL_PHYRX_HE_SIG_B2_OFDMA: { + struct hal_rx_he_sig_b2_ofdma_info *he_sig_b2_ofdma = + (struct hal_rx_he_sig_b2_ofdma_info *)tlv_data; + + info0 = __le32_to_cpu(he_sig_b2_ofdma->info0); + + ppdu_info->mcs = + FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS, + info0); + ppdu_info->nss = + FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS, + info0) + 1; + ppdu_info->beamformed = + info0 & + HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF; + ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING, + info0); + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; + break; + } + case HAL_PHYRX_RSSI_LEGACY: { + struct hal_rx_phyrx_rssi_legacy_info *rssi = + (struct hal_rx_phyrx_rssi_legacy_info *)tlv_data; + + /* TODO: Please note that the combined rssi will not be accurate + * in MU case. Rssi in MU needs to be retrieved from + * PHYRX_OTHER_RECEIVE_INFO TLV. + */ + ppdu_info->rssi_comb = + FIELD_GET(HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB, + __le32_to_cpu(rssi->info0)); + break; + } + case HAL_RX_MPDU_START: { + struct hal_rx_mpdu_info *mpdu_info = + (struct hal_rx_mpdu_info *)tlv_data; + u16 peer_id; + + peer_id = FIELD_GET(HAL_RX_MPDU_INFO_INFO0_PEERID, + __le32_to_cpu(mpdu_info->info0)); + if (peer_id) + ppdu_info->peer_id = peer_id; + break; + } + case HAL_RXPCU_PPDU_END_INFO: { + struct hal_rx_ppdu_end_duration *ppdu_rx_duration = + (struct hal_rx_ppdu_end_duration *)tlv_data; + ppdu_info->rx_duration = + FIELD_GET(HAL_RX_PPDU_END_DURATION, + __le32_to_cpu(ppdu_rx_duration->info0)); + break; + } + case HAL_DUMMY: + return HAL_RX_MON_STATUS_BUF_DONE; + case HAL_RX_PPDU_END_STATUS_DONE: + case 0: + return HAL_RX_MON_STATUS_PPDU_DONE; + default: + break; + } + + return HAL_RX_MON_STATUS_PPDU_NOT_DONE; +} + +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) +{ + struct hal_tlv_hdr *tlv; + enum hal_rx_mon_status hal_status = HAL_RX_MON_STATUS_BUF_DONE; + u16 tlv_tag; + u16 tlv_len; + u8 *ptr = skb->data; + + do { + tlv = (struct hal_tlv_hdr *)ptr; + tlv_tag = FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl); + tlv_len = FIELD_GET(HAL_TLV_HDR_LEN, tlv->tl); + ptr += sizeof(*tlv); + + /* The actual length of PPDU_END is the combined length of many PHY + * TLVs that follow. Skip the TLV header and + * rx_rxpcu_classification_overview that follows the header to get to + * next TLV. + */ + if (tlv_tag == HAL_RX_PPDU_END) + tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview); + + hal_status = ath11k_hal_rx_parse_mon_status_tlv(ab, ppdu_info, + tlv_tag, ptr); + ptr += tlv_len; + ptr = PTR_ALIGN(ptr, HAL_TLV_ALIGN); + + if ((ptr - skb->data) >= DP_RX_BUFFER_SIZE) + break; + } while (hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE); + + return hal_status; +} + +void ath11k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, dma_addr_t *paddr, + u32 *sw_cookie, void **pp_buf_addr, + u32 *msdu_cnt) +{ + struct hal_reo_entrance_ring *reo_ent_ring = + (struct hal_reo_entrance_ring *)rx_desc; + struct ath11k_buffer_addr *buf_addr_info; + struct rx_mpdu_desc *rx_mpdu_desc_info_details; + + rx_mpdu_desc_info_details = + (struct rx_mpdu_desc *)&reo_ent_ring->rx_mpdu_info; + + *msdu_cnt = FIELD_GET(RX_MPDU_DESC_INFO0_MSDU_COUNT, + rx_mpdu_desc_info_details->info0); + + buf_addr_info = (struct ath11k_buffer_addr *)&reo_ent_ring->buf_addr_info; + + *paddr = (((u64)FIELD_GET(BUFFER_ADDR_INFO1_ADDR, + buf_addr_info->info1)) << 32) | + FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + buf_addr_info->info0); + + *sw_cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + buf_addr_info->info1); + + *pp_buf_addr = (void *)buf_addr_info; +} diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h new file mode 100644 index 000000000000..bb022c781c48 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HAL_RX_H +#define ATH11K_HAL_RX_H + +struct hal_rx_wbm_rel_info { + u32 cookie; + enum hal_wbm_rel_src_module err_rel_src; + enum hal_reo_dest_ring_push_reason push_reason; + u32 err_code; + bool first_msdu; + bool last_msdu; +}; + +#define HAL_INVALID_PEERID 0xffff +#define VHT_SIG_SU_NSS_MASK 0x7 + +#define HAL_RX_MAX_MCS 12 +#define HAL_RX_MAX_NSS 8 + +struct hal_rx_mon_status_tlv_hdr { + u32 hdr; + u8 value[0]; +}; + +enum hal_rx_su_mu_coding { + HAL_RX_SU_MU_CODING_BCC, + HAL_RX_SU_MU_CODING_LDPC, + HAL_RX_SU_MU_CODING_MAX, +}; + +enum hal_rx_gi { + HAL_RX_GI_0_8_US, + HAL_RX_GI_0_4_US, + HAL_RX_GI_1_6_US, + HAL_RX_GI_3_2_US, + HAL_RX_GI_MAX, +}; + +enum hal_rx_bw { + HAL_RX_BW_20MHZ, + HAL_RX_BW_40MHZ, + HAL_RX_BW_80MHZ, + HAL_RX_BW_160MHZ, + HAL_RX_BW_MAX, +}; + +enum hal_rx_preamble { + HAL_RX_PREAMBLE_11A, + HAL_RX_PREAMBLE_11B, + HAL_RX_PREAMBLE_11N, + HAL_RX_PREAMBLE_11AC, + HAL_RX_PREAMBLE_11AX, + HAL_RX_PREAMBLE_MAX, +}; + +enum hal_rx_reception_type { + HAL_RX_RECEPTION_TYPE_SU, + HAL_RX_RECEPTION_TYPE_MU_MIMO, + HAL_RX_RECEPTION_TYPE_MU_OFDMA, + HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO, + HAL_RX_RECEPTION_TYPE_MAX, +}; + +#define HAL_TLV_STATUS_PPDU_NOT_DONE 0 +#define HAL_TLV_STATUS_PPDU_DONE 1 +#define HAL_TLV_STATUS_BUF_DONE 2 +#define HAL_TLV_STATUS_PPDU_NON_STD_DONE 3 +#define HAL_RX_FCS_LEN 4 + +enum hal_rx_mon_status { + HAL_RX_MON_STATUS_PPDU_NOT_DONE, + HAL_RX_MON_STATUS_PPDU_DONE, + HAL_RX_MON_STATUS_BUF_DONE, +}; + +struct hal_rx_mon_ppdu_info { + u32 ppdu_id; + u32 ppdu_ts; + u32 num_mpdu_fcs_ok; + u32 num_mpdu_fcs_err; + u32 preamble_type; + u16 chan_num; + u16 tcp_msdu_count; + u16 tcp_ack_msdu_count; + u16 udp_msdu_count; + u16 other_msdu_count; + u16 peer_id; + u8 rate; + u8 mcs; + u8 nss; + u8 bw; + u8 is_stbc; + u8 gi; + u8 ldpc; + u8 beamformed; + u8 rssi_comb; + u8 tid; + u8 reception_type; + u64 rx_duration; +}; + +#define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) + +struct hal_rx_ppdu_start { + __le32 info0; + __le32 chan_num; + __le32 ppdu_start_ts; +} __packed; + +#define HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR GENMASK(25, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO1_MPDU_CNT_FCS_OK GENMASK(8, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_FC_VALID BIT(9) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_QOS_CTRL_VALID BIT(10) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_HT_CTRL_VALID BIT(11) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_PKT_TYPE GENMASK(23, 20) + +#define HAL_RX_PPDU_END_USER_STATS_INFO2_AST_INDEX GENMASK(15, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO2_FRAME_CTRL GENMASK(31, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO3_QOS_CTRL GENMASK(31, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO4_UDP_MSDU_CNT GENMASK(15, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO4_TCP_MSDU_CNT GENMASK(31, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO5_OTHER_MSDU_CNT GENMASK(15, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO5_TCP_ACK_MSDU_CNT GENMASK(31, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP GENMASK(15, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_EOSP_BITMAP GENMASK(31, 16) + +struct hal_rx_ppdu_end_user_stats { + __le32 rsvd0[2]; + __le32 info0; + __le32 info1; + __le32 info2; + __le32 info3; + __le32 ht_ctrl; + __le32 rsvd1[2]; + __le32 info4; + __le32 info5; + __le32 info6; + __le32 rsvd2[11]; +} __packed; + +#define HAL_RX_HT_SIG_INFO_INFO0_MCS GENMASK(6, 0) +#define HAL_RX_HT_SIG_INFO_INFO0_BW BIT(7) + +#define HAL_RX_HT_SIG_INFO_INFO1_STBC GENMASK(5, 4) +#define HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING BIT(6) +#define HAL_RX_HT_SIG_INFO_INFO1_GI BIT(7) + +struct hal_rx_ht_sig_info { + __le32 info0; + __le32 info1; +} __packed; + +#define HAL_RX_LSIG_B_INFO_INFO0_RATE GENMASK(3, 0) +#define HAL_RX_LSIG_B_INFO_INFO0_LEN GENMASK(15, 4) + +struct hal_rx_lsig_b_info { + __le32 info0; +} __packed; + +#define HAL_RX_LSIG_A_INFO_INFO0_RATE GENMASK(3, 0) +#define HAL_RX_LSIG_A_INFO_INFO0_LEN GENMASK(16, 5) +#define HAL_RX_LSIG_A_INFO_INFO0_PKT_TYPE GENMASK(27, 24) + +struct hal_rx_lsig_a_info { + __le32 info0; +} __packed; + +#define HAL_RX_VHT_SIG_A_INFO_INFO0_BW GENMASK(1, 0) +#define HAL_RX_VHT_SIG_A_INFO_INFO0_STBC BIT(3) +#define HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID GENMASK(9, 4) +#define HAL_RX_VHT_SIG_A_INFO_INFO0_NSTS GENMASK(21, 10) + +#define HAL_RX_VHT_SIG_A_INFO_INFO1_GI_SETTING GENMASK(1, 0) +#define HAL_RX_VHT_SIG_A_INFO_INFO1_SU_MU_CODING BIT(2) +#define HAL_RX_VHT_SIG_A_INFO_INFO1_MCS GENMASK(7, 4) +#define HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED BIT(8) + +struct hal_rx_vht_sig_a_info { + __le32 info0; + __le32 info1; +} __packed; + +enum hal_rx_vht_sig_a_gi_setting { + HAL_RX_VHT_SIG_A_NORMAL_GI = 0, + HAL_RX_VHT_SIG_A_SHORT_GI = 1, + HAL_RX_VHT_SIG_A_SHORT_GI_AMBIGUITY = 3, +}; + +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS GENMASK(6, 3) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM BIT(7) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW GENMASK(20, 19) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE GENMASK(22, 21) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS GENMASK(25, 23) + +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING BIT(7) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC BIT(9) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF BIT(10) + +struct hal_rx_he_sig_a_su_info { + __le32 info0; + __le32 info1; +} __packed; + +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW GENMASK(17, 15) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE GENMASK(24, 23) + +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC BIT(12) + +struct hal_rx_he_sig_a_mu_dl_info { + __le32 info0; + __le32 info1; +} __packed; + +#define HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION GENMASK(7, 0) + +struct hal_rx_he_sig_b1_mu_info { + __le32 info0; +} __packed; + +#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS GENMASK(18, 15) +#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING BIT(20) +#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS GENMASK(31, 29) + +struct hal_rx_he_sig_b2_mu_info { + __le32 info0; +} __packed; + +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS GENMASK(13, 11) +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF BIT(19) +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS GENMASK(18, 15) +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_DCM BIT(19) +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING BIT(20) + +struct hal_rx_he_sig_b2_ofdma_info { + __le32 info0; +} __packed; + +#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB GENMASK(15, 8) + +struct hal_rx_phyrx_rssi_legacy_info { + __le32 rsvd[35]; + __le32 info0; +} __packed; + +#define HAL_RX_MPDU_INFO_INFO0_PEERID GENMASK(31, 16) +struct hal_rx_mpdu_info { + __le32 rsvd0; + __le32 info0; + __le32 rsvd1[21]; +} __packed; + +#define HAL_RX_PPDU_END_DURATION GENMASK(23, 0) +struct hal_rx_ppdu_end_duration { + __le32 rsvd0[9]; + __le32 info0; + __le32 rsvd1[4]; +} __packed; + +struct hal_rx_rxpcu_classification_overview { + u32 rsvd0; +} __packed; + +struct hal_rx_msdu_desc_info { + u32 msdu_flags; + u16 msdu_len; /* 14 bits for length */ +}; + +#define HAL_RX_NUM_MSDU_DESC 6 +struct hal_rx_msdu_list { + struct hal_rx_msdu_desc_info msdu_info[HAL_RX_NUM_MSDU_DESC]; + u32 sw_cookie[HAL_RX_NUM_MSDU_DESC]; + u8 rbm[HAL_RX_NUM_MSDU_DESC]; +}; + +void ath11k_hal_reo_status_queue_stats(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_flush_queue_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_flush_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_flush_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_unblk_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_flush_timeout_list_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_desc_thresh_reached_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_update_rx_reo_queue_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status); +int ath11k_hal_reo_process_status(u8 *reo_desc, u8 *status); +void ath11k_hal_rx_msdu_link_info_get(void *link_desc, u32 *num_msdus, + u32 *msdu_cookies, + enum hal_rx_buf_return_buf_manager *rbm); +void ath11k_hal_rx_msdu_link_desc_set(struct ath11k_base *ab, void *desc, + void *link_desc, + enum hal_wbm_rel_bm_act action); +void ath11k_hal_rx_buf_addr_info_set(void *desc, dma_addr_t paddr, + u32 cookie, u8 manager); +void ath11k_hal_rx_buf_addr_info_get(void *desc, dma_addr_t *paddr, + u32 *cookie, u8 *rbm); +int ath11k_hal_desc_reo_parse_err(struct ath11k_base *ab, u32 *rx_desc, + dma_addr_t *paddr, u32 *desc_bank); +int ath11k_hal_wbm_desc_parse_err(struct ath11k_base *ab, void *desc, + struct hal_rx_wbm_rel_info *rel_info); +void ath11k_hal_rx_reo_ent_paddr_get(struct ath11k_base *ab, void *desc, + dma_addr_t *paddr, u32 *desc_bank); +void ath11k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, + dma_addr_t *paddr, u32 *sw_cookie, + void **pp_buf_addr_info, + u32 *msdu_cnt); +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); +#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 +#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_3 0xCDBEEF +#endif diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c new file mode 100644 index 000000000000..e4aa7e8a1284 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "ahb.h" +#include "hal.h" +#include "hal_tx.h" + +#define DSCP_TID_MAP_TBL_ENTRY_SIZE 64 + +/* dscp_tid_map - Default DSCP-TID mapping + * + * DSCP TID + * 000000 0 + * 001000 1 + * 010000 2 + * 011000 3 + * 100000 4 + * 101000 5 + * 110000 6 + * 111000 7 + */ +static const u8 dscp_tid_map[DSCP_TID_MAP_TBL_ENTRY_SIZE] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, +}; + +void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, + struct hal_tx_info *ti) +{ + struct hal_tcl_data_cmd *tcl_cmd = (struct hal_tcl_data_cmd *)cmd; + + tcl_cmd->buf_addr_info.info0 = + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, ti->paddr); + tcl_cmd->buf_addr_info.info1 = + FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, + ((uint64_t)ti->paddr >> HAL_ADDR_MSB_REG_SHIFT)); + tcl_cmd->buf_addr_info.info1 |= + FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, + (ti->ring_id + HAL_RX_BUF_RBM_SW0_BM)) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, ti->desc_id); + + tcl_cmd->info0 = + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_DESC_TYPE, ti->type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCAP_TYPE, ti->encap_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE, + ti->encrypt_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_SEARCH_TYPE, + ti->search_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ADDR_EN, + ti->addr_search_flags) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_CMD_NUM, + ti->meta_data_flags); + + tcl_cmd->info1 = ti->flags0 | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_DATA_LEN, ti->data_len) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_PKT_OFFSET, ti->pkt_offset); + + tcl_cmd->info2 = ti->flags1 | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID, ti->tid) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_LMAC_ID, ti->lmac_id); + + tcl_cmd->info3 = FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX, + ti->dscp_tid_tbl_idx) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_SEARCH_INDEX, + ti->bss_ast_hash); + tcl_cmd->info4 = 0; +} + +void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id) +{ + u32 ctrl_reg_val; + u32 addr; + u8 hw_map_val[HAL_DSCP_TID_TBL_SIZE]; + int i; + u32 value; + int cnt = 0; + + ctrl_reg_val = ath11k_ahb_read32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG); + /* Enable read/write access */ + ctrl_reg_val |= HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN; + ath11k_ahb_write32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG, ctrl_reg_val); + + addr = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_DSCP_TID_MAP + + (4 * id * (HAL_DSCP_TID_TBL_SIZE / 4)); + + /* Configure each DSCP-TID mapping in three bits there by configure + * three bytes in an iteration. + */ + for (i = 0; i < DSCP_TID_MAP_TBL_ENTRY_SIZE; i += 8) { + value = FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP0, + dscp_tid_map[i]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP1, + dscp_tid_map[i + 1]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP2, + dscp_tid_map[i + 2]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP3, + dscp_tid_map[i + 3]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP4, + dscp_tid_map[i + 4]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP5, + dscp_tid_map[i + 5]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP6, + dscp_tid_map[i + 6]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP7, + dscp_tid_map[i + 7]); + memcpy(&hw_map_val[cnt], (u8 *)&value, 3); + cnt += 3; + } + + for (i = 0; i < HAL_DSCP_TID_TBL_SIZE; i += 4) { + ath11k_ahb_write32(ab, addr, *(u32 *)&hw_map_val[i]); + addr += 4; + } + + /* Disable read/write access */ + ctrl_reg_val = ath11k_ahb_read32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG); + ctrl_reg_val &= ~HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN; + ath11k_ahb_write32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG, + ctrl_reg_val); +} + +void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab, struct hal_srng *srng) +{ + struct hal_srng_params params; + struct hal_tlv_hdr *tlv; + int i, entry_size; + u8 *desc; + + memset(¶ms, 0, sizeof(params)); + + entry_size = ath11k_hal_srng_get_entrysize(HAL_TCL_DATA); + ath11k_hal_srng_get_params(ab, srng, ¶ms); + desc = (u8 *)params.ring_base_vaddr; + + for (i = 0; i < params.num_entries; i++) { + tlv = (struct hal_tlv_hdr *)desc; + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_TCL_DATA_CMD) | + FIELD_PREP(HAL_TLV_HDR_LEN, + sizeof(struct hal_tcl_data_cmd)); + desc += entry_size; + } +} diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h new file mode 100644 index 000000000000..ce48a61bfb66 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HAL_TX_H +#define ATH11K_HAL_TX_H + +#include "hal_desc.h" + +#define HAL_TX_ADDRX_EN 1 +#define HAL_TX_ADDRY_EN 2 + +#define HAL_TX_ADDR_SEARCH_DEFAULT 0 +#define HAL_TX_ADDR_SEARCH_INDEX 1 + +struct hal_tx_info { + u16 meta_data_flags; /* %HAL_TCL_DATA_CMD_INFO0_META_ */ + u8 ring_id; + u32 desc_id; + enum hal_tcl_desc_type type; + enum hal_tcl_encap_type encap_type; + dma_addr_t paddr; + u32 data_len; + u32 pkt_offset; + enum hal_encrypt_type encrypt_type; + u32 flags0; /* %HAL_TCL_DATA_CMD_INFO1_ */ + u32 flags1; /* %HAL_TCL_DATA_CMD_INFO2_ */ + u16 addr_search_flags; /* %HAL_TCL_DATA_CMD_INFO0_ADDR(X/Y)_ */ + u16 bss_ast_hash; + u8 tid; + u8 search_type; /* %HAL_TX_ADDR_SEARCH_ */ + u8 lmac_id; + u8 dscp_tid_tbl_idx; +}; + +/* TODO: Check if the actual desc macros can be used instead */ +#define HAL_TX_STATUS_FLAGS_FIRST_MSDU BIT(0) +#define HAL_TX_STATUS_FLAGS_LAST_MSDU BIT(1) +#define HAL_TX_STATUS_FLAGS_MSDU_IN_AMSDU BIT(2) +#define HAL_TX_STATUS_FLAGS_RATE_STATS_VALID BIT(3) +#define HAL_TX_STATUS_FLAGS_RATE_LDPC BIT(4) +#define HAL_TX_STATUS_FLAGS_RATE_STBC BIT(5) +#define HAL_TX_STATUS_FLAGS_OFDMA BIT(6) + +#define HAL_TX_STATUS_DESC_LEN sizeof(struct hal_wbm_release_ring) + +/* Tx status parsed from srng desc */ +struct hal_tx_status { + enum hal_wbm_rel_src_module buf_rel_source; + enum hal_wbm_tqm_rel_reason status; + u8 ack_rssi; + u32 flags; /* %HAL_TX_STATUS_FLAGS_ */ + u32 ppdu_id; + u8 try_cnt; + u8 tid; + u16 peer_id; + u32 rate_stats; +}; + +void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, + struct hal_tx_info *ti); +void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id); +int ath11k_hal_reo_cmd_send(struct ath11k_base *ab, struct hal_srng *srng, + enum hal_reo_cmd_type type, + struct ath11k_hal_reo_cmd *cmd); +void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab, + struct hal_srng *srng); +#endif diff --git a/drivers/net/wireless/ath/ath11k/htc.c b/drivers/net/wireless/ath/ath11k/htc.c new file mode 100644 index 000000000000..8f54f58b83e6 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/htc.c @@ -0,0 +1,773 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#include <linux/skbuff.h> +#include <linux/ctype.h> + +#include "ahb.h" +#include "debug.h" + +struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ab, int size) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(size + sizeof(struct ath11k_htc_hdr)); + if (!skb) + return NULL; + + skb_reserve(skb, sizeof(struct ath11k_htc_hdr)); + + /* FW/HTC requires 4-byte aligned streams */ + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath11k_warn(ab, "Unaligned HTC tx skb\n"); + + return skb; +} + +static void ath11k_htc_control_tx_complete(struct ath11k_base *ab, + struct sk_buff *skb) +{ + kfree_skb(skb); +} + +static struct sk_buff *ath11k_htc_build_tx_ctrl_skb(void *ab) +{ + struct sk_buff *skb; + struct ath11k_skb_cb *skb_cb; + + skb = dev_alloc_skb(ATH11K_HTC_CONTROL_BUFFER_SIZE); + if (!skb) + return NULL; + + skb_reserve(skb, sizeof(struct ath11k_htc_hdr)); + WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4)); + + skb_cb = ATH11K_SKB_CB(skb); + memset(skb_cb, 0, sizeof(*skb_cb)); + + ath11k_dbg(ab, ATH11K_DBG_HTC, "%s: skb %pK\n", __func__, skb); + return skb; +} + +static inline void ath11k_htc_restore_tx_skb(struct ath11k_htc *htc, + struct sk_buff *skb) +{ + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); + + dma_unmap_single(htc->ab->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); + skb_pull(skb, sizeof(struct ath11k_htc_hdr)); +} + +static void ath11k_htc_prepare_tx_skb(struct ath11k_htc_ep *ep, + struct sk_buff *skb) +{ + struct ath11k_htc_hdr *hdr; + + hdr = (struct ath11k_htc_hdr *)skb->data; + + memset(hdr, 0, sizeof(*hdr)); + hdr->htc_info = FIELD_PREP(HTC_HDR_ENDPOINTID, ep->eid) | + FIELD_PREP(HTC_HDR_PAYLOADLEN, + (skb->len - sizeof(*hdr))) | + FIELD_PREP(HTC_HDR_FLAGS, + ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE); + + spin_lock_bh(&ep->htc->tx_lock); + hdr->ctrl_info = FIELD_PREP(HTC_HDR_CONTROLBYTES1, ep->seq_no++); + spin_unlock_bh(&ep->htc->tx_lock); +} + +int ath11k_htc_send(struct ath11k_htc *htc, + enum ath11k_htc_ep_id eid, + struct sk_buff *skb) +{ + struct ath11k_htc_ep *ep = &htc->endpoint[eid]; + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); + struct device *dev = htc->ab->dev; + struct ath11k_base *ab = htc->ab; + int credits = 0; + int ret; + + if (eid >= ATH11K_HTC_EP_COUNT) { + ath11k_warn(ab, "Invalid endpoint id: %d\n", eid); + return -ENOENT; + } + + skb_push(skb, sizeof(struct ath11k_htc_hdr)); + + if (ep->tx_credit_flow_enabled) { + credits = DIV_ROUND_UP(skb->len, htc->target_credit_size); + spin_lock_bh(&htc->tx_lock); + if (ep->tx_credits < credits) { + ath11k_dbg(ab, ATH11K_DBG_HTC, + "htc insufficient credits ep %d required %d available %d\n", + eid, credits, ep->tx_credits); + spin_unlock_bh(&htc->tx_lock); + ret = -EAGAIN; + goto err_pull; + } + ep->tx_credits -= credits; + ath11k_dbg(ab, ATH11K_DBG_HTC, + "htc ep %d consumed %d credits (total %d)\n", + eid, credits, ep->tx_credits); + spin_unlock_bh(&htc->tx_lock); + } + + ath11k_htc_prepare_tx_skb(ep, skb); + + skb_cb->eid = eid; + skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, skb_cb->paddr); + if (ret) { + ret = -EIO; + goto err_credits; + } + + ret = ath11k_ce_send(htc->ab, skb, ep->ul_pipe_id, ep->eid); + if (ret) + goto err_unmap; + + return 0; + +err_unmap: + dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); +err_credits: + if (ep->tx_credit_flow_enabled) { + spin_lock_bh(&htc->tx_lock); + ep->tx_credits += credits; + ath11k_dbg(ab, ATH11K_DBG_HTC, + "htc ep %d reverted %d credits back (total %d)\n", + eid, credits, ep->tx_credits); + spin_unlock_bh(&htc->tx_lock); + + if (ep->ep_ops.ep_tx_credits) + ep->ep_ops.ep_tx_credits(htc->ab); + } +err_pull: + skb_pull(skb, sizeof(struct ath11k_htc_hdr)); + return ret; +} + +static void +ath11k_htc_process_credit_report(struct ath11k_htc *htc, + const struct ath11k_htc_credit_report *report, + int len, + enum ath11k_htc_ep_id eid) +{ + struct ath11k_base *ab = htc->ab; + struct ath11k_htc_ep *ep; + int i, n_reports; + + if (len % sizeof(*report)) + ath11k_warn(ab, "Uneven credit report len %d", len); + + n_reports = len / sizeof(*report); + + spin_lock_bh(&htc->tx_lock); + for (i = 0; i < n_reports; i++, report++) { + if (report->eid >= ATH11K_HTC_EP_COUNT) + break; + + ep = &htc->endpoint[report->eid]; + ep->tx_credits += report->credits; + + ath11k_dbg(ab, ATH11K_DBG_HTC, "htc ep %d got %d credits (total %d)\n", + report->eid, report->credits, ep->tx_credits); + + if (ep->ep_ops.ep_tx_credits) { + spin_unlock_bh(&htc->tx_lock); + ep->ep_ops.ep_tx_credits(htc->ab); + spin_lock_bh(&htc->tx_lock); + } + } + spin_unlock_bh(&htc->tx_lock); +} + +static int ath11k_htc_process_trailer(struct ath11k_htc *htc, + u8 *buffer, + int length, + enum ath11k_htc_ep_id src_eid) +{ + struct ath11k_base *ab = htc->ab; + int status = 0; + struct ath11k_htc_record *record; + size_t len; + + while (length > 0) { + record = (struct ath11k_htc_record *)buffer; + + if (length < sizeof(record->hdr)) { + status = -EINVAL; + break; + } + + if (record->hdr.len > length) { + /* no room left in buffer for record */ + ath11k_warn(ab, "Invalid record length: %d\n", + record->hdr.len); + status = -EINVAL; + break; + } + + switch (record->hdr.id) { + case ATH11K_HTC_RECORD_CREDITS: + len = sizeof(struct ath11k_htc_credit_report); + if (record->hdr.len < len) { + ath11k_warn(ab, "Credit report too long\n"); + status = -EINVAL; + break; + } + ath11k_htc_process_credit_report(htc, + record->credit_report, + record->hdr.len, + src_eid); + break; + default: + ath11k_warn(ab, "Unhandled record: id:%d length:%d\n", + record->hdr.id, record->hdr.len); + break; + } + + if (status) + break; + + /* multiple records may be present in a trailer */ + buffer += sizeof(record->hdr) + record->hdr.len; + length -= sizeof(record->hdr) + record->hdr.len; + } + + return status; +} + +void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + int status = 0; + struct ath11k_htc *htc = &ab->htc; + struct ath11k_htc_hdr *hdr; + struct ath11k_htc_ep *ep; + u16 payload_len; + u32 trailer_len = 0; + size_t min_len; + u8 eid; + bool trailer_present; + + hdr = (struct ath11k_htc_hdr *)skb->data; + skb_pull(skb, sizeof(*hdr)); + + eid = FIELD_GET(HTC_HDR_ENDPOINTID, hdr->htc_info); + + if (eid >= ATH11K_HTC_EP_COUNT) { + ath11k_warn(ab, "HTC Rx: invalid eid %d\n", eid); + goto out; + } + + ep = &htc->endpoint[eid]; + + payload_len = FIELD_GET(HTC_HDR_PAYLOADLEN, hdr->htc_info); + + if (payload_len + sizeof(*hdr) > ATH11K_HTC_MAX_LEN) { + ath11k_warn(ab, "HTC rx frame too long, len: %zu\n", + payload_len + sizeof(*hdr)); + goto out; + } + + if (skb->len < payload_len) { + ath11k_warn(ab, "HTC Rx: insufficient length, got %d, expected %d\n", + skb->len, payload_len); + goto out; + } + + /* get flags to check for trailer */ + trailer_present = (FIELD_GET(HTC_HDR_FLAGS, hdr->htc_info)) & + ATH11K_HTC_FLAG_TRAILER_PRESENT; + + if (trailer_present) { + u8 *trailer; + + trailer_len = FIELD_GET(HTC_HDR_CONTROLBYTES0, hdr->ctrl_info); + min_len = sizeof(struct ath11k_htc_record_hdr); + + if ((trailer_len < min_len) || + (trailer_len > payload_len)) { + ath11k_warn(ab, "Invalid trailer length: %d\n", + trailer_len); + goto out; + } + + trailer = (u8 *)hdr; + trailer += sizeof(*hdr); + trailer += payload_len; + trailer -= trailer_len; + status = ath11k_htc_process_trailer(htc, trailer, + trailer_len, eid); + if (status) + goto out; + + skb_trim(skb, skb->len - trailer_len); + } + + if (trailer_len >= payload_len) + /* zero length packet with trailer data, just drop these */ + goto out; + + if (eid == ATH11K_HTC_EP_0) { + struct ath11k_htc_msg *msg = (struct ath11k_htc_msg *)skb->data; + + switch (FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id)) { + case ATH11K_HTC_MSG_READY_ID: + case ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID: + /* handle HTC control message */ + if (completion_done(&htc->ctl_resp)) { + /* this is a fatal error, target should not be + * sending unsolicited messages on the ep 0 + */ + ath11k_warn(ab, "HTC rx ctrl still processing\n"); + complete(&htc->ctl_resp); + goto out; + } + + htc->control_resp_len = + min_t(int, skb->len, + ATH11K_HTC_MAX_CTRL_MSG_LEN); + + memcpy(htc->control_resp_buffer, skb->data, + htc->control_resp_len); + + complete(&htc->ctl_resp); + break; + default: + ath11k_warn(ab, "ignoring unsolicited htc ep0 event\n"); + break; + } + goto out; + } + + ath11k_dbg(ab, ATH11K_DBG_HTC, "htc rx completion ep %d skb %pK\n", + eid, skb); + ep->ep_ops.ep_rx_complete(ab, skb); + + /* poll tx completion for interrupt disabled CE's */ + ath11k_ce_poll_send_completed(ab, ep->ul_pipe_id); + + /* skb is now owned by the rx completion handler */ + skb = NULL; +out: + kfree_skb(skb); +} + +static void ath11k_htc_control_rx_complete(struct ath11k_base *ab, + struct sk_buff *skb) +{ + /* This is unexpected. FW is not supposed to send regular rx on this + * endpoint. + */ + ath11k_warn(ab, "unexpected htc rx\n"); + kfree_skb(skb); +} + +static const char *htc_service_name(enum ath11k_htc_svc_id id) +{ + switch (id) { + case ATH11K_HTC_SVC_ID_RESERVED: + return "Reserved"; + case ATH11K_HTC_SVC_ID_RSVD_CTRL: + return "Control"; + case ATH11K_HTC_SVC_ID_WMI_CONTROL: + return "WMI"; + case ATH11K_HTC_SVC_ID_WMI_DATA_BE: + return "DATA BE"; + case ATH11K_HTC_SVC_ID_WMI_DATA_BK: + return "DATA BK"; + case ATH11K_HTC_SVC_ID_WMI_DATA_VI: + return "DATA VI"; + case ATH11K_HTC_SVC_ID_WMI_DATA_VO: + return "DATA VO"; + case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1: + return "WMI MAC1"; + case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2: + return "WMI MAC2"; + case ATH11K_HTC_SVC_ID_NMI_CONTROL: + return "NMI Control"; + case ATH11K_HTC_SVC_ID_NMI_DATA: + return "NMI Data"; + case ATH11K_HTC_SVC_ID_HTT_DATA_MSG: + return "HTT Data"; + case ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS: + return "RAW"; + case ATH11K_HTC_SVC_ID_IPA_TX: + return "IPA TX"; + case ATH11K_HTC_SVC_ID_PKT_LOG: + return "PKT LOG"; + } + + return "Unknown"; +} + +static void ath11k_htc_reset_endpoint_states(struct ath11k_htc *htc) +{ + struct ath11k_htc_ep *ep; + int i; + + for (i = ATH11K_HTC_EP_0; i < ATH11K_HTC_EP_COUNT; i++) { + ep = &htc->endpoint[i]; + ep->service_id = ATH11K_HTC_SVC_ID_UNUSED; + ep->max_ep_message_len = 0; + ep->max_tx_queue_depth = 0; + ep->eid = i; + ep->htc = htc; + ep->tx_credit_flow_enabled = true; + } +} + +static u8 ath11k_htc_get_credit_allocation(struct ath11k_htc *htc, + u16 service_id) +{ + u8 i, allocation = 0; + + for (i = 0; i < ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) { + if (htc->service_alloc_table[i].service_id == service_id) { + allocation = + htc->service_alloc_table[i].credit_allocation; + } + } + + return allocation; +} + +static int ath11k_htc_setup_target_buffer_assignments(struct ath11k_htc *htc) +{ + struct ath11k_htc_svc_tx_credits *serv_entry; + u32 svc_id[] = { + ATH11K_HTC_SVC_ID_WMI_CONTROL, + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1, + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2, + }; + int i, credits; + + credits = htc->total_transmit_credits; + serv_entry = htc->service_alloc_table; + + if ((htc->wmi_ep_count == 0) || + (htc->wmi_ep_count > ARRAY_SIZE(svc_id))) + return -EINVAL; + + /* Divide credits among number of endpoints for WMI */ + credits = credits / htc->wmi_ep_count; + for (i = 0; i < htc->wmi_ep_count; i++) { + serv_entry[i].service_id = svc_id[i]; + serv_entry[i].credit_allocation = credits; + } + + return 0; +} + +int ath11k_htc_wait_target(struct ath11k_htc *htc) +{ + int i, status = 0; + struct ath11k_base *ab = htc->ab; + unsigned long time_left; + struct ath11k_htc_ready *ready; + u16 message_id; + u16 credit_count; + u16 credit_size; + + time_left = wait_for_completion_timeout(&htc->ctl_resp, + ATH11K_HTC_WAIT_TIMEOUT_HZ); + if (!time_left) { + ath11k_warn(ab, "failed to receive control response completion, polling..\n"); + + for (i = 0; i < CE_COUNT; i++) + ath11k_ce_per_engine_service(htc->ab, i); + + time_left = + wait_for_completion_timeout(&htc->ctl_resp, + ATH11K_HTC_WAIT_TIMEOUT_HZ); + + if (!time_left) + status = -ETIMEDOUT; + } + + if (status < 0) { + ath11k_warn(ab, "ctl_resp never came in (%d)\n", status); + return status; + } + + if (htc->control_resp_len < sizeof(*ready)) { + ath11k_warn(ab, "Invalid HTC ready msg len:%d\n", + htc->control_resp_len); + return -ECOMM; + } + + ready = (struct ath11k_htc_ready *)htc->control_resp_buffer; + message_id = FIELD_GET(HTC_MSG_MESSAGEID, ready->id_credit_count); + credit_count = FIELD_GET(HTC_READY_MSG_CREDITCOUNT, + ready->id_credit_count); + credit_size = FIELD_GET(HTC_READY_MSG_CREDITSIZE, ready->size_ep); + + if (message_id != ATH11K_HTC_MSG_READY_ID) { + ath11k_warn(ab, "Invalid HTC ready msg: 0x%x\n", message_id); + return -ECOMM; + } + + htc->total_transmit_credits = credit_count; + htc->target_credit_size = credit_size; + + ath11k_dbg(ab, ATH11K_DBG_HTC, + "Target ready! transmit resources: %d size:%d\n", + htc->total_transmit_credits, htc->target_credit_size); + + if ((htc->total_transmit_credits == 0) || + (htc->target_credit_size == 0)) { + ath11k_warn(ab, "Invalid credit size received\n"); + return -ECOMM; + } + + ath11k_htc_setup_target_buffer_assignments(htc); + + return 0; +} + +int ath11k_htc_connect_service(struct ath11k_htc *htc, + struct ath11k_htc_svc_conn_req *conn_req, + struct ath11k_htc_svc_conn_resp *conn_resp) +{ + struct ath11k_base *ab = htc->ab; + struct ath11k_htc_conn_svc *req_msg; + struct ath11k_htc_conn_svc_resp resp_msg_dummy; + struct ath11k_htc_conn_svc_resp *resp_msg = &resp_msg_dummy; + enum ath11k_htc_ep_id assigned_eid = ATH11K_HTC_EP_COUNT; + struct ath11k_htc_ep *ep; + struct sk_buff *skb; + unsigned int max_msg_size = 0; + int length, status; + unsigned long time_left; + bool disable_credit_flow_ctrl = false; + u16 message_id, service_id, flags = 0; + u8 tx_alloc = 0; + + /* special case for HTC pseudo control service */ + if (conn_req->service_id == ATH11K_HTC_SVC_ID_RSVD_CTRL) { + disable_credit_flow_ctrl = true; + assigned_eid = ATH11K_HTC_EP_0; + max_msg_size = ATH11K_HTC_MAX_CTRL_MSG_LEN; + memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy)); + goto setup; + } + + tx_alloc = ath11k_htc_get_credit_allocation(htc, + conn_req->service_id); + if (!tx_alloc) + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot htc service %s does not allocate target credits\n", + htc_service_name(conn_req->service_id)); + + skb = ath11k_htc_build_tx_ctrl_skb(htc->ab); + if (!skb) { + ath11k_warn(ab, "Failed to allocate HTC packet\n"); + return -ENOMEM; + } + + length = sizeof(*req_msg); + skb_put(skb, length); + memset(skb->data, 0, length); + + req_msg = (struct ath11k_htc_conn_svc *)skb->data; + req_msg->msg_svc_id = FIELD_PREP(HTC_MSG_MESSAGEID, + ATH11K_HTC_MSG_CONNECT_SERVICE_ID); + + flags |= FIELD_PREP(ATH11K_HTC_CONN_FLAGS_RECV_ALLOC, tx_alloc); + + /* Only enable credit flow control for WMI ctrl service */ + if (!(conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL || + conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1 || + conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2)) { + flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + disable_credit_flow_ctrl = true; + } + + req_msg->flags_len = FIELD_PREP(HTC_SVC_MSG_CONNECTIONFLAGS, flags); + req_msg->msg_svc_id |= FIELD_PREP(HTC_SVC_MSG_SERVICE_ID, + conn_req->service_id); + + reinit_completion(&htc->ctl_resp); + + status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb); + if (status) { + kfree_skb(skb); + return status; + } + + /* wait for response */ + time_left = wait_for_completion_timeout(&htc->ctl_resp, + ATH11K_HTC_CONN_SVC_TIMEOUT_HZ); + if (!time_left) { + ath11k_err(ab, "Service connect timeout\n"); + return -ETIMEDOUT; + } + + /* we controlled the buffer creation, it's aligned */ + resp_msg = (struct ath11k_htc_conn_svc_resp *)htc->control_resp_buffer; + message_id = FIELD_GET(HTC_MSG_MESSAGEID, resp_msg->msg_svc_id); + service_id = FIELD_GET(HTC_SVC_RESP_MSG_SERVICEID, + resp_msg->msg_svc_id); + + if ((message_id != ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID) || + (htc->control_resp_len < sizeof(*resp_msg))) { + ath11k_err(ab, "Invalid resp message ID 0x%x", message_id); + return -EPROTO; + } + + ath11k_dbg(ab, ATH11K_DBG_HTC, + "HTC Service %s connect response: status: 0x%lx, assigned ep: 0x%lx\n", + htc_service_name(service_id), + FIELD_GET(HTC_SVC_RESP_MSG_STATUS, resp_msg->flags_len), + FIELD_GET(HTC_SVC_RESP_MSG_ENDPOINTID, resp_msg->flags_len)); + + conn_resp->connect_resp_code = FIELD_GET(HTC_SVC_RESP_MSG_STATUS, + resp_msg->flags_len); + + /* check response status */ + if (conn_resp->connect_resp_code != ATH11K_HTC_CONN_SVC_STATUS_SUCCESS) { + ath11k_err(ab, "HTC Service %s connect request failed: 0x%x)\n", + htc_service_name(service_id), + conn_resp->connect_resp_code); + return -EPROTO; + } + + assigned_eid = (enum ath11k_htc_ep_id)FIELD_GET( + HTC_SVC_RESP_MSG_ENDPOINTID, + resp_msg->flags_len); + + max_msg_size = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE, + resp_msg->flags_len); + +setup: + + if (assigned_eid >= ATH11K_HTC_EP_COUNT) + return -EPROTO; + + if (max_msg_size == 0) + return -EPROTO; + + ep = &htc->endpoint[assigned_eid]; + ep->eid = assigned_eid; + + if (ep->service_id != ATH11K_HTC_SVC_ID_UNUSED) + return -EPROTO; + + /* return assigned endpoint to caller */ + conn_resp->eid = assigned_eid; + conn_resp->max_msg_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE, + resp_msg->flags_len); + + /* setup the endpoint */ + ep->service_id = conn_req->service_id; + ep->max_tx_queue_depth = conn_req->max_send_queue_depth; + ep->max_ep_message_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE, + resp_msg->flags_len); + ep->tx_credits = tx_alloc; + + /* copy all the callbacks */ + ep->ep_ops = conn_req->ep_ops; + + status = ath11k_ahb_map_service_to_pipe(htc->ab, + ep->service_id, + &ep->ul_pipe_id, + &ep->dl_pipe_id); + if (status) + return status; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", + htc_service_name(ep->service_id), ep->ul_pipe_id, + ep->dl_pipe_id, ep->eid); + + if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { + ep->tx_credit_flow_enabled = false; + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot htc service '%s' eid %d TX flow control disabled\n", + htc_service_name(ep->service_id), assigned_eid); + } + + return status; +} + +int ath11k_htc_start(struct ath11k_htc *htc) +{ + struct sk_buff *skb; + int status = 0; + struct ath11k_base *ab = htc->ab; + struct ath11k_htc_setup_complete_extended *msg; + + skb = ath11k_htc_build_tx_ctrl_skb(htc->ab); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(*msg)); + memset(skb->data, 0, skb->len); + + msg = (struct ath11k_htc_setup_complete_extended *)skb->data; + msg->msg_id = FIELD_PREP(HTC_MSG_MESSAGEID, + ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID); + + ath11k_dbg(ab, ATH11K_DBG_HTC, "HTC is using TX credit flow control\n"); + + status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb); + if (status) { + kfree_skb(skb); + return status; + } + + return 0; +} + +int ath11k_htc_init(struct ath11k_base *ab) +{ + struct ath11k_htc *htc = &ab->htc; + struct ath11k_htc_svc_conn_req conn_req; + struct ath11k_htc_svc_conn_resp conn_resp; + int ret; + + spin_lock_init(&htc->tx_lock); + + ath11k_htc_reset_endpoint_states(htc); + + htc->ab = ab; + + switch (ab->wmi_ab.preferred_hw_mode) { + case WMI_HOST_HW_MODE_SINGLE: + htc->wmi_ep_count = 1; + break; + case WMI_HOST_HW_MODE_DBS: + case WMI_HOST_HW_MODE_DBS_OR_SBS: + htc->wmi_ep_count = 2; + break; + case WMI_HOST_HW_MODE_DBS_SBS: + htc->wmi_ep_count = 3; + break; + default: + htc->wmi_ep_count = 3; + break; + } + + /* setup our pseudo HTC control endpoint connection */ + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + conn_req.ep_ops.ep_tx_complete = ath11k_htc_control_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath11k_htc_control_rx_complete; + conn_req.max_send_queue_depth = ATH11K_NUM_CONTROL_TX_BUFFERS; + conn_req.service_id = ATH11K_HTC_SVC_ID_RSVD_CTRL; + + /* connect fake service */ + ret = ath11k_htc_connect_service(htc, &conn_req, &conn_resp); + if (ret) { + ath11k_err(ab, "could not connect to htc service (%d)\n", ret); + return ret; + } + + init_completion(&htc->ctl_resp); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/htc.h b/drivers/net/wireless/ath/ath11k/htc.h new file mode 100644 index 000000000000..f0a3387567ca --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/htc.h @@ -0,0 +1,313 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HTC_H +#define ATH11K_HTC_H + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/bug.h> +#include <linux/skbuff.h> +#include <linux/timer.h> + +struct ath11k_base; + +#define HTC_HDR_ENDPOINTID GENMASK(7, 0) +#define HTC_HDR_FLAGS GENMASK(15, 8) +#define HTC_HDR_PAYLOADLEN GENMASK(31, 16) +#define HTC_HDR_CONTROLBYTES0 GENMASK(7, 0) +#define HTC_HDR_CONTROLBYTES1 GENMASK(15, 8) +#define HTC_HDR_RESERVED GENMASK(31, 16) + +#define HTC_SVC_MSG_SERVICE_ID GENMASK(31, 16) +#define HTC_SVC_MSG_CONNECTIONFLAGS GENMASK(15, 0) +#define HTC_SVC_MSG_SERVICEMETALENGTH GENMASK(23, 16) +#define HTC_READY_MSG_CREDITCOUNT GENMASK(31, 16) +#define HTC_READY_MSG_CREDITSIZE GENMASK(15, 0) +#define HTC_READY_MSG_MAXENDPOINTS GENMASK(23, 16) + +#define HTC_READY_EX_MSG_HTCVERSION GENMASK(7, 0) +#define HTC_READY_EX_MSG_MAXMSGSPERHTCBUNDLE GENMASK(15, 8) + +#define HTC_SVC_RESP_MSG_SERVICEID GENMASK(31, 16) +#define HTC_SVC_RESP_MSG_STATUS GENMASK(7, 0) +#define HTC_SVC_RESP_MSG_ENDPOINTID GENMASK(15, 8) +#define HTC_SVC_RESP_MSG_MAXMSGSIZE GENMASK(31, 16) +#define HTC_SVC_RESP_MSG_SERVICEMETALENGTH GENMASK(7, 0) + +#define HTC_MSG_MESSAGEID GENMASK(15, 0) +#define HTC_SETUP_COMPLETE_EX_MSG_SETUPFLAGS GENMASK(31, 0) +#define HTC_SETUP_COMPLETE_EX_MSG_MAXMSGSPERBUNDLEDRECV GENMASK(7, 0) +#define HTC_SETUP_COMPLETE_EX_MSG_RSVD0 GENMASK(15, 8) +#define HTC_SETUP_COMPLETE_EX_MSG_RSVD1 GENMASK(23, 16) +#define HTC_SETUP_COMPLETE_EX_MSG_RSVD2 GENMASK(31, 24) + +enum ath11k_htc_tx_flags { + ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01, + ATH11K_HTC_FLAG_SEND_BUNDLE = 0x02 +}; + +enum ath11k_htc_rx_flags { + ATH11K_HTC_FLAG_TRAILER_PRESENT = 0x02, + ATH11K_HTC_FLAG_BUNDLE_MASK = 0xF0 +}; + +struct ath11k_htc_hdr { + u32 htc_info; + u32 ctrl_info; +} __packed __aligned(4); + +enum ath11k_htc_msg_id { + ATH11K_HTC_MSG_READY_ID = 1, + ATH11K_HTC_MSG_CONNECT_SERVICE_ID = 2, + ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3, + ATH11K_HTC_MSG_SETUP_COMPLETE_ID = 4, + ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID = 5, + ATH11K_HTC_MSG_SEND_SUSPEND_COMPLETE = 6 +}; + +enum ath11k_htc_version { + ATH11K_HTC_VERSION_2P0 = 0x00, /* 2.0 */ + ATH11K_HTC_VERSION_2P1 = 0x01, /* 2.1 */ +}; + +#define ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK GENMASK(1, 0) +#define ATH11K_HTC_CONN_FLAGS_RECV_ALLOC GENMASK(15, 8) + +enum ath11k_htc_conn_flags { + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH = 0x0, + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF = 0x1, + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2, + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY = 0x3, + ATH11K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE = 1 << 2, + ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3 +}; + +enum ath11k_htc_conn_svc_status { + ATH11K_HTC_CONN_SVC_STATUS_SUCCESS = 0, + ATH11K_HTC_CONN_SVC_STATUS_NOT_FOUND = 1, + ATH11K_HTC_CONN_SVC_STATUS_FAILED = 2, + ATH11K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3, + ATH11K_HTC_CONN_SVC_STATUS_NO_MORE_EP = 4 +}; + +struct ath11k_htc_ready { + u32 id_credit_count; + u32 size_ep; +} __packed; + +struct ath11k_htc_ready_extended { + struct ath11k_htc_ready base; + u32 ver_bundle; +} __packed; + +struct ath11k_htc_conn_svc { + u32 msg_svc_id; + u32 flags_len; +} __packed; + +struct ath11k_htc_conn_svc_resp { + u32 msg_svc_id; + u32 flags_len; + u32 svc_meta_pad; +} __packed; + +struct ath11k_htc_setup_complete_extended { + u32 msg_id; + u32 flags; + u32 max_msgs_per_bundled_recv; +} __packed; + +struct ath11k_htc_msg { + u32 msg_svc_id; + u32 flags_len; +} __packed __aligned(4); + +enum ath11k_htc_record_id { + ATH11K_HTC_RECORD_NULL = 0, + ATH11K_HTC_RECORD_CREDITS = 1 +}; + +struct ath11k_htc_record_hdr { + u8 id; /* @enum ath11k_htc_record_id */ + u8 len; + u8 pad0; + u8 pad1; +} __packed; + +struct ath11k_htc_credit_report { + u8 eid; /* @enum ath11k_htc_ep_id */ + u8 credits; + u8 pad0; + u8 pad1; +} __packed; + +struct ath11k_htc_record { + struct ath11k_htc_record_hdr hdr; + union { + struct ath11k_htc_credit_report credit_report[0]; + u8 pauload[0]; + }; +} __packed __aligned(4); + +/* note: the trailer offset is dynamic depending + * on payload length. this is only a struct layout draft + */ +struct ath11k_htc_frame { + struct ath11k_htc_hdr hdr; + union { + struct ath11k_htc_msg msg; + u8 payload[0]; + }; + struct ath11k_htc_record trailer[0]; +} __packed __aligned(4); + +enum ath11k_htc_svc_gid { + ATH11K_HTC_SVC_GRP_RSVD = 0, + ATH11K_HTC_SVC_GRP_WMI = 1, + ATH11K_HTC_SVC_GRP_NMI = 2, + ATH11K_HTC_SVC_GRP_HTT = 3, + ATH11K_HTC_SVC_GRP_CFG = 4, + ATH11K_HTC_SVC_GRP_IPA = 5, + ATH11K_HTC_SVC_GRP_PKTLOG = 6, + + ATH11K_HTC_SVC_GRP_TEST = 254, + ATH11K_HTC_SVC_GRP_LAST = 255, +}; + +#define SVC(group, idx) \ + (int)(((int)(group) << 8) | (int)(idx)) + +enum ath11k_htc_svc_id { + /* NOTE: service ID of 0x0000 is reserved and should never be used */ + ATH11K_HTC_SVC_ID_RESERVED = 0x0000, + ATH11K_HTC_SVC_ID_UNUSED = ATH11K_HTC_SVC_ID_RESERVED, + + ATH11K_HTC_SVC_ID_RSVD_CTRL = SVC(ATH11K_HTC_SVC_GRP_RSVD, 1), + ATH11K_HTC_SVC_ID_WMI_CONTROL = SVC(ATH11K_HTC_SVC_GRP_WMI, 0), + ATH11K_HTC_SVC_ID_WMI_DATA_BE = SVC(ATH11K_HTC_SVC_GRP_WMI, 1), + ATH11K_HTC_SVC_ID_WMI_DATA_BK = SVC(ATH11K_HTC_SVC_GRP_WMI, 2), + ATH11K_HTC_SVC_ID_WMI_DATA_VI = SVC(ATH11K_HTC_SVC_GRP_WMI, 3), + ATH11K_HTC_SVC_ID_WMI_DATA_VO = SVC(ATH11K_HTC_SVC_GRP_WMI, 4), + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1 = SVC(ATH11K_HTC_SVC_GRP_WMI, 5), + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2 = SVC(ATH11K_HTC_SVC_GRP_WMI, 6), + + ATH11K_HTC_SVC_ID_NMI_CONTROL = SVC(ATH11K_HTC_SVC_GRP_NMI, 0), + ATH11K_HTC_SVC_ID_NMI_DATA = SVC(ATH11K_HTC_SVC_GRP_NMI, 1), + + ATH11K_HTC_SVC_ID_HTT_DATA_MSG = SVC(ATH11K_HTC_SVC_GRP_HTT, 0), + + /* raw stream service (i.e. flash, tcmd, calibration apps) */ + ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH11K_HTC_SVC_GRP_TEST, 0), + ATH11K_HTC_SVC_ID_IPA_TX = SVC(ATH11K_HTC_SVC_GRP_IPA, 0), + ATH11K_HTC_SVC_ID_PKT_LOG = SVC(ATH11K_HTC_SVC_GRP_PKTLOG, 0), +}; + +#undef SVC + +enum ath11k_htc_ep_id { + ATH11K_HTC_EP_UNUSED = -1, + ATH11K_HTC_EP_0 = 0, + ATH11K_HTC_EP_1 = 1, + ATH11K_HTC_EP_2, + ATH11K_HTC_EP_3, + ATH11K_HTC_EP_4, + ATH11K_HTC_EP_5, + ATH11K_HTC_EP_6, + ATH11K_HTC_EP_7, + ATH11K_HTC_EP_8, + ATH11K_HTC_EP_COUNT, +}; + +struct ath11k_htc_ops { + void (*target_send_suspend_complete)(struct ath11k_base *ar); +}; + +struct ath11k_htc_ep_ops { + void (*ep_tx_complete)(struct ath11k_base *, struct sk_buff *); + void (*ep_rx_complete)(struct ath11k_base *, struct sk_buff *); + void (*ep_tx_credits)(struct ath11k_base *); +}; + +/* service connection information */ +struct ath11k_htc_svc_conn_req { + u16 service_id; + struct ath11k_htc_ep_ops ep_ops; + int max_send_queue_depth; +}; + +/* service connection response information */ +struct ath11k_htc_svc_conn_resp { + u8 buffer_len; + u8 actual_len; + enum ath11k_htc_ep_id eid; + unsigned int max_msg_len; + u8 connect_resp_code; +}; + +#define ATH11K_NUM_CONTROL_TX_BUFFERS 2 +#define ATH11K_HTC_MAX_LEN 4096 +#define ATH11K_HTC_MAX_CTRL_MSG_LEN 256 +#define ATH11K_HTC_WAIT_TIMEOUT_HZ (1 * HZ) +#define ATH11K_HTC_CONTROL_BUFFER_SIZE (ATH11K_HTC_MAX_CTRL_MSG_LEN + \ + sizeof(struct ath11k_htc_hdr)) +#define ATH11K_HTC_CONN_SVC_TIMEOUT_HZ (1 * HZ) +#define ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES 8 + +struct ath11k_htc_ep { + struct ath11k_htc *htc; + enum ath11k_htc_ep_id eid; + enum ath11k_htc_svc_id service_id; + struct ath11k_htc_ep_ops ep_ops; + + int max_tx_queue_depth; + int max_ep_message_len; + u8 ul_pipe_id; + u8 dl_pipe_id; + + u8 seq_no; /* for debugging */ + int tx_credits; + bool tx_credit_flow_enabled; +}; + +struct ath11k_htc_svc_tx_credits { + u16 service_id; + u8 credit_allocation; +}; + +struct ath11k_htc { + struct ath11k_base *ab; + struct ath11k_htc_ep endpoint[ATH11K_HTC_EP_COUNT]; + + /* protects endpoints */ + spinlock_t tx_lock; + + struct ath11k_htc_ops htc_ops; + + u8 control_resp_buffer[ATH11K_HTC_MAX_CTRL_MSG_LEN]; + int control_resp_len; + + struct completion ctl_resp; + + int total_transmit_credits; + struct ath11k_htc_svc_tx_credits + service_alloc_table[ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES]; + int target_credit_size; + u8 wmi_ep_count; +}; + +int ath11k_htc_init(struct ath11k_base *ar); +int ath11k_htc_wait_target(struct ath11k_htc *htc); +int ath11k_htc_start(struct ath11k_htc *htc); +int ath11k_htc_connect_service(struct ath11k_htc *htc, + struct ath11k_htc_svc_conn_req *conn_req, + struct ath11k_htc_svc_conn_resp *conn_resp); +int ath11k_htc_send(struct ath11k_htc *htc, enum ath11k_htc_ep_id eid, + struct sk_buff *packet); +struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ar, int size); +void ath11k_htc_rx_completion_handler(struct ath11k_base *ar, + struct sk_buff *skb); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h new file mode 100644 index 000000000000..dd39333ec0ea --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HW_H +#define ATH11K_HW_H + +/* Target configuration defines */ + +/* Num VDEVS per radio */ +#define TARGET_NUM_VDEVS (16 + 1) + +#define TARGET_NUM_PEERS_PDEV (512 + TARGET_NUM_VDEVS) + +/* Num of peers for Single Radio mode */ +#define TARGET_NUM_PEERS_SINGLE (TARGET_NUM_PEERS_PDEV) + +/* Num of peers for DBS */ +#define TARGET_NUM_PEERS_DBS (2 * TARGET_NUM_PEERS_PDEV) + +/* Num of peers for DBS_SBS */ +#define TARGET_NUM_PEERS_DBS_SBS (3 * TARGET_NUM_PEERS_PDEV) + +/* Max num of stations (per radio) */ +#define TARGET_NUM_STATIONS 512 + +#define TARGET_NUM_PEERS(x) TARGET_NUM_PEERS_##x +#define TARGET_NUM_PEER_KEYS 2 +#define TARGET_NUM_TIDS(x) (2 * TARGET_NUM_PEERS(x) + \ + 4 * TARGET_NUM_VDEVS + 8) + +#define TARGET_AST_SKID_LIMIT 16 +#define TARGET_NUM_OFFLD_PEERS 4 +#define TARGET_NUM_OFFLD_REORDER_BUFFS 4 + +#define TARGET_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(4)) +#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(4)) +#define TARGET_RX_TIMEOUT_LO_PRI 100 +#define TARGET_RX_TIMEOUT_HI_PRI 40 + +#define TARGET_DECAP_MODE_RAW 0 +#define TARGET_DECAP_MODE_NATIVE_WIFI 1 +#define TARGET_DECAP_MODE_ETH 2 + +#define TARGET_SCAN_MAX_PENDING_REQS 4 +#define TARGET_BMISS_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES 8 +#define TARGET_GTK_OFFLOAD_MAX_VDEV 3 +#define TARGET_NUM_MCAST_GROUPS 12 +#define TARGET_NUM_MCAST_TABLE_ELEMS 64 +#define TARGET_MCAST2UCAST_MODE 2 +#define TARGET_TX_DBG_LOG_SIZE 1024 +#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 +#define TARGET_VOW_CONFIG 0 +#define TARGET_NUM_MSDU_DESC (2500) +#define TARGET_MAX_FRAG_ENTRIES 6 +#define TARGET_MAX_BCN_OFFLD 16 +#define TARGET_NUM_WDS_ENTRIES 32 +#define TARGET_DMA_BURST_SIZE 1 +#define TARGET_RX_BATCHMODE 1 + +#define ATH11K_HW_MAX_QUEUES 4 + +#define ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK 0x4 + +#define ATH11K_FW_DIR "ath11k" + +/* IPQ8074 definitions */ +#define IPQ8074_FW_DIR "IPQ8074" +#define IPQ8074_MAX_BOARD_DATA_SZ (256 * 1024) +#define IPQ8074_MAX_CAL_DATA_SZ IPQ8074_MAX_BOARD_DATA_SZ + +#define ATH11K_BOARD_MAGIC "QCA-ATH11K-BOARD" +#define ATH11K_BOARD_API2_FILE "board-2.bin" +#define ATH11K_DEFAULT_BOARD_FILE "bdwlan.bin" +#define ATH11K_DEFAULT_CAL_FILE "caldata.bin" + +enum ath11k_hw_rate_cck { + ATH11K_HW_RATE_CCK_LP_11M = 0, + ATH11K_HW_RATE_CCK_LP_5_5M, + ATH11K_HW_RATE_CCK_LP_2M, + ATH11K_HW_RATE_CCK_LP_1M, + ATH11K_HW_RATE_CCK_SP_11M, + ATH11K_HW_RATE_CCK_SP_5_5M, + ATH11K_HW_RATE_CCK_SP_2M, +}; + +enum ath11k_hw_rate_ofdm { + ATH11K_HW_RATE_OFDM_48M = 0, + ATH11K_HW_RATE_OFDM_24M, + ATH11K_HW_RATE_OFDM_12M, + ATH11K_HW_RATE_OFDM_6M, + ATH11K_HW_RATE_OFDM_54M, + ATH11K_HW_RATE_OFDM_36M, + ATH11K_HW_RATE_OFDM_18M, + ATH11K_HW_RATE_OFDM_9M, +}; + +struct ath11k_hw_params { + const char *name; + struct { + const char *dir; + size_t board_size; + size_t cal_size; + } fw; +}; + +struct ath11k_fw_ie { + __le32 id; + __le32 len; + u8 data[0]; +}; + +enum ath11k_bd_ie_board_type { + ATH11K_BD_IE_BOARD_NAME = 0, + ATH11K_BD_IE_BOARD_DATA = 1, +}; + +enum ath11k_bd_ie_type { + /* contains sub IEs of enum ath11k_bd_ie_board_type */ + ATH11K_BD_IE_BOARD = 0, + ATH11K_BD_IE_BOARD_EXT = 1, +}; + +#endif diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c new file mode 100644 index 000000000000..6640662f5ede --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -0,0 +1,5907 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <net/mac80211.h> +#include <linux/etherdevice.h> +#include "mac.h" +#include "core.h" +#include "debug.h" +#include "wmi.h" +#include "hw.h" +#include "dp_tx.h" +#include "dp_rx.h" +#include "testmode.h" +#include "peer.h" + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_5GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static const struct ieee80211_channel ath11k_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static const struct ieee80211_channel ath11k_5ghz_channels[] = { + CHAN5G(36, 5180, 0), + CHAN5G(40, 5200, 0), + CHAN5G(44, 5220, 0), + CHAN5G(48, 5240, 0), + CHAN5G(52, 5260, 0), + CHAN5G(56, 5280, 0), + CHAN5G(60, 5300, 0), + CHAN5G(64, 5320, 0), + CHAN5G(100, 5500, 0), + CHAN5G(104, 5520, 0), + CHAN5G(108, 5540, 0), + CHAN5G(112, 5560, 0), + CHAN5G(116, 5580, 0), + CHAN5G(120, 5600, 0), + CHAN5G(124, 5620, 0), + CHAN5G(128, 5640, 0), + CHAN5G(132, 5660, 0), + CHAN5G(136, 5680, 0), + CHAN5G(140, 5700, 0), + CHAN5G(144, 5720, 0), + CHAN5G(149, 5745, 0), + CHAN5G(153, 5765, 0), + CHAN5G(157, 5785, 0), + CHAN5G(161, 5805, 0), + CHAN5G(165, 5825, 0), + CHAN5G(169, 5845, 0), + CHAN5G(173, 5865, 0), +}; + +static struct ieee80211_rate ath11k_legacy_rates[] = { + { .bitrate = 10, + .hw_value = ATH11K_HW_RATE_CCK_LP_1M }, + { .bitrate = 20, + .hw_value = ATH11K_HW_RATE_CCK_LP_2M, + .hw_value_short = ATH11K_HW_RATE_CCK_SP_2M, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, + .hw_value = ATH11K_HW_RATE_CCK_LP_5_5M, + .hw_value_short = ATH11K_HW_RATE_CCK_SP_5_5M, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, + .hw_value = ATH11K_HW_RATE_CCK_LP_11M, + .hw_value_short = ATH11K_HW_RATE_CCK_SP_11M, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + + { .bitrate = 60, .hw_value = ATH11K_HW_RATE_OFDM_6M }, + { .bitrate = 90, .hw_value = ATH11K_HW_RATE_OFDM_9M }, + { .bitrate = 120, .hw_value = ATH11K_HW_RATE_OFDM_12M }, + { .bitrate = 180, .hw_value = ATH11K_HW_RATE_OFDM_18M }, + { .bitrate = 240, .hw_value = ATH11K_HW_RATE_OFDM_24M }, + { .bitrate = 360, .hw_value = ATH11K_HW_RATE_OFDM_36M }, + { .bitrate = 480, .hw_value = ATH11K_HW_RATE_OFDM_48M }, + { .bitrate = 540, .hw_value = ATH11K_HW_RATE_OFDM_54M }, +}; + +static const int +ath11k_phymodes[NUM_NL80211_BANDS][ATH11K_CHAN_WIDTH_NUM] = { + [NL80211_BAND_2GHZ] = { + [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11AX_HE20_2G, + [NL80211_CHAN_WIDTH_20] = MODE_11AX_HE20_2G, + [NL80211_CHAN_WIDTH_40] = MODE_11AX_HE40_2G, + [NL80211_CHAN_WIDTH_80] = MODE_11AX_HE80_2G, + [NL80211_CHAN_WIDTH_80P80] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_160] = MODE_UNKNOWN, + }, + [NL80211_BAND_5GHZ] = { + [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11AX_HE20, + [NL80211_CHAN_WIDTH_20] = MODE_11AX_HE20, + [NL80211_CHAN_WIDTH_40] = MODE_11AX_HE40, + [NL80211_CHAN_WIDTH_80] = MODE_11AX_HE80, + [NL80211_CHAN_WIDTH_160] = MODE_11AX_HE160, + [NL80211_CHAN_WIDTH_80P80] = MODE_11AX_HE80_80, + }, +}; + +const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default = { + .rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START | + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE, + .pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0, + .pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1, + .pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2, + .pkt_filter_flags3 = HTT_RX_FP_DATA_FILTER_FLASG3 | + HTT_RX_FP_CTRL_FILTER_FLASG3 +}; + +#define ATH11K_MAC_FIRST_OFDM_RATE_IDX 4 +#define ath11k_g_rates ath11k_legacy_rates +#define ath11k_g_rates_size (ARRAY_SIZE(ath11k_legacy_rates)) +#define ath11k_a_rates (ath11k_legacy_rates + 4) +#define ath11k_a_rates_size (ARRAY_SIZE(ath11k_legacy_rates) - 4) + +#define ATH11K_MAC_SCAN_TIMEOUT_MSECS 200 /* in msecs */ + +static const u32 ath11k_smps_map[] = { + [WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC, + [WLAN_HT_CAP_SM_PS_DYNAMIC] = WMI_PEER_SMPS_DYNAMIC, + [WLAN_HT_CAP_SM_PS_INVALID] = WMI_PEER_SMPS_PS_NONE, + [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, +}; + +u8 ath11k_mac_bw_to_mac80211_bw(u8 bw) +{ + u8 ret = 0; + + switch (bw) { + case ATH11K_BW_20: + ret = RATE_INFO_BW_20; + break; + case ATH11K_BW_40: + ret = RATE_INFO_BW_40; + break; + case ATH11K_BW_80: + ret = RATE_INFO_BW_80; + break; + case ATH11K_BW_160: + ret = RATE_INFO_BW_160; + break; + } + + return ret; +} + +int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx, + u16 *rate) +{ + /* As default, it is OFDM rates */ + int i = ATH11K_MAC_FIRST_OFDM_RATE_IDX; + int max_rates_idx = ath11k_g_rates_size; + + if (preamble == WMI_RATE_PREAMBLE_CCK) { + hw_rc &= ~ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK; + i = 0; + max_rates_idx = ATH11K_MAC_FIRST_OFDM_RATE_IDX; + } + + while (i < max_rates_idx) { + if (hw_rc == ath11k_legacy_rates[i].hw_value) { + *rateidx = i; + *rate = ath11k_legacy_rates[i].bitrate; + return 0; + } + i++; + } + + return -EINVAL; +} + +static int get_num_chains(u32 mask) +{ + int num_chains = 0; + + while (mask) { + if (mask & BIT(0)) + num_chains++; + mask >>= 1; + } + + return num_chains; +} + +u8 ath11k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband, + u32 bitrate) +{ + int i; + + for (i = 0; i < sband->n_bitrates; i++) + if (sband->bitrates[i].bitrate == bitrate) + return i; + + return 0; +} + +static u32 +ath11k_mac_max_ht_nss(const u8 ht_mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ + int nss; + + for (nss = IEEE80211_HT_MCS_MASK_LEN - 1; nss >= 0; nss--) + if (ht_mcs_mask[nss]) + return nss + 1; + + return 1; +} + +static u32 +ath11k_mac_max_vht_nss(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) +{ + int nss; + + for (nss = NL80211_VHT_NSS_MAX - 1; nss >= 0; nss--) + if (vht_mcs_mask[nss]) + return nss + 1; + + return 1; +} + +static u8 ath11k_parse_mpdudensity(u8 mpdudensity) +{ +/* 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": + * 0 for no restriction + * 1 for 1/4 us + * 2 for 1/2 us + * 3 for 1 us + * 4 for 2 us + * 5 for 4 us + * 6 for 8 us + * 7 for 16 us + */ + switch (mpdudensity) { + case 0: + return 0; + case 1: + case 2: + case 3: + /* Our lower layer calculations limit our precision to + * 1 microsecond + */ + return 1; + case 4: + return 2; + case 5: + return 4; + case 6: + return 8; + case 7: + return 16; + default: + return 0; + } +} + +static int ath11k_mac_vif_chan(struct ieee80211_vif *vif, + struct cfg80211_chan_def *def) +{ + struct ieee80211_chanctx_conf *conf; + + rcu_read_lock(); + conf = rcu_dereference(vif->chanctx_conf); + if (!conf) { + rcu_read_unlock(); + return -ENOENT; + } + + *def = conf->def; + rcu_read_unlock(); + + return 0; +} + +static bool ath11k_mac_bitrate_is_cck(int bitrate) +{ + switch (bitrate) { + case 10: + case 20: + case 55: + case 110: + return true; + } + + return false; +} + +u8 ath11k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, + u8 hw_rate, bool cck) +{ + const struct ieee80211_rate *rate; + int i; + + for (i = 0; i < sband->n_bitrates; i++) { + rate = &sband->bitrates[i]; + + if (ath11k_mac_bitrate_is_cck(rate->bitrate) != cck) + continue; + + if (rate->hw_value == hw_rate) + return i; + else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE && + rate->hw_value_short == hw_rate) + return i; + } + + return 0; +} + +static u8 ath11k_mac_bitrate_to_rate(int bitrate) +{ + return DIV_ROUND_UP(bitrate, 5) | + (ath11k_mac_bitrate_is_cck(bitrate) ? BIT(7) : 0); +} + +static void ath11k_get_arvif_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath11k_vif_iter *arvif_iter = data; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + + if (arvif->vdev_id == arvif_iter->vdev_id) + arvif_iter->arvif = arvif; +} + +struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id) +{ + struct ath11k_vif_iter arvif_iter; + u32 flags; + + memset(&arvif_iter, 0, sizeof(struct ath11k_vif_iter)); + arvif_iter.vdev_id = vdev_id; + + flags = IEEE80211_IFACE_ITER_RESUME_ALL; + ieee80211_iterate_active_interfaces_atomic(ar->hw, + flags, + ath11k_get_arvif_iter, + &arvif_iter); + if (!arvif_iter.arvif) + return NULL; + + return arvif_iter.arvif; +} + +struct ath11k_vif *ath11k_mac_get_arvif_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; + } + } + + return NULL; +} + +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; + } + } + + return NULL; +} + +struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id) +{ + int i; + struct ath11k_pdev *pdev; + + if (WARN_ON(pdev_id > ab->num_radios)) + return NULL; + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + + if (pdev && pdev->pdev_id == pdev_id) + return (pdev->ar ? pdev->ar : NULL); + } + + return NULL; +} + +struct ath11k *ath11k_mac_get_ar_vdev_stop_status(struct ath11k_base *ab, + u32 vdev_id) +{ + int i; + struct ath11k_pdev *pdev; + struct ath11k *ar; + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) { + ar = pdev->ar; + + spin_lock_bh(&ar->data_lock); + if (ar->vdev_stop_status.stop_in_progress && + ar->vdev_stop_status.vdev_id == vdev_id) { + ar->vdev_stop_status.stop_in_progress = false; + spin_unlock_bh(&ar->data_lock); + return ar; + } + spin_unlock_bh(&ar->data_lock); + } + } + return NULL; +} + +static void ath11k_pdev_caps_update(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + + ar->max_tx_power = ab->target_caps.hw_max_tx_power; + + /* FIXME Set min_tx_power to ab->target_caps.hw_min_tx_power. + * But since the received value in svcrdy is same as hw_max_tx_power, + * we can set ar->min_tx_power to 0 currently until + * this is fixed in firmware + */ + ar->min_tx_power = 0; + + ar->txpower_limit_2g = ar->max_tx_power; + ar->txpower_limit_5g = ar->max_tx_power; + ar->txpower_scale = WMI_HOST_TP_SCALE_MAX; +} + +static int ath11k_mac_txpower_recalc(struct ath11k *ar) +{ + struct ath11k_pdev *pdev = ar->pdev; + struct ath11k_vif *arvif; + int ret, txpower = -1; + u32 param; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->txpower <= 0) + continue; + + if (txpower == -1) + txpower = arvif->txpower; + else + txpower = min(txpower, arvif->txpower); + } + + if (txpower == -1) + return 0; + + /* txpwr is set as 2 units per dBm in FW*/ + txpower = min_t(u32, max_t(u32, ar->min_tx_power, txpower), + ar->max_tx_power) * 2; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower to set in hw %d\n", + txpower / 2); + + if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) && + ar->txpower_limit_2g != txpower) { + param = WMI_PDEV_PARAM_TXPOWER_LIMIT2G; + ret = ath11k_wmi_pdev_set_param(ar, param, + txpower, ar->pdev->pdev_id); + if (ret) + goto fail; + ar->txpower_limit_2g = txpower; + } + + if ((pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) && + ar->txpower_limit_5g != txpower) { + param = WMI_PDEV_PARAM_TXPOWER_LIMIT5G; + ret = ath11k_wmi_pdev_set_param(ar, param, + txpower, ar->pdev->pdev_id); + if (ret) + goto fail; + ar->txpower_limit_5g = txpower; + } + + return 0; + +fail: + ath11k_warn(ar->ab, "failed to recalc txpower limit %d using pdev param %d: %d\n", + txpower / 2, param, ret); + return ret; +} + +static int ath11k_recalc_rtscts_prot(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + u32 vdev_param, rts_cts = 0; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + vdev_param = WMI_VDEV_PARAM_ENABLE_RTSCTS; + + /* Enable RTS/CTS protection for sw retries (when legacy stations + * are in BSS) or by default only for second rate series. + * TODO: Check if we need to enable CTS 2 Self in any case + */ + rts_cts = WMI_USE_RTS_CTS; + + if (arvif->num_legacy_stations > 0) + rts_cts |= WMI_RTSCTS_ACROSS_SW_RETRIES << 4; + else + rts_cts |= WMI_RTSCTS_FOR_SECOND_RATESERIES << 4; + + /* Need not send duplicate param value to firmware */ + if (arvif->rtscts_prot_mode == rts_cts) + return 0; + + arvif->rtscts_prot_mode = rts_cts; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %d recalc rts/cts prot %d\n", + arvif->vdev_id, rts_cts); + + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, rts_cts); + if (ret) + ath11k_warn(ar->ab, "failed to recalculate rts/cts prot for vdev %d: %d\n", + arvif->vdev_id, ret); + + return ret; +} + +static int ath11k_mac_set_kickout(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + u32 param; + int ret; + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_STA_KICKOUT_TH, + ATH11K_KICKOUT_THRESHOLD, + ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to set kickout threshold on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + param = WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, + ATH11K_KEEPALIVE_MIN_IDLE); + if (ret) { + ath11k_warn(ar->ab, "failed to set keepalive minimum idle time on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + param = WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, + ATH11K_KEEPALIVE_MAX_IDLE); + if (ret) { + ath11k_warn(ar->ab, "failed to set keepalive maximum idle time on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + param = WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, + ATH11K_KEEPALIVE_MAX_UNRESPONSIVE); + if (ret) { + ath11k_warn(ar->ab, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + return 0; +} + +void ath11k_mac_peer_cleanup_all(struct ath11k *ar) +{ + struct ath11k_peer *peer, *tmp; + struct ath11k_base *ab = ar->ab; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ab->base_lock); + list_for_each_entry_safe(peer, tmp, &ab->peers, list) { + ath11k_peer_rx_tid_cleanup(ar, peer); + list_del(&peer->list); + kfree(peer); + } + spin_unlock_bh(&ab->base_lock); + + ar->num_peers = 0; + ar->num_stations = 0; +} + +static int ath11k_monitor_vdev_up(struct ath11k *ar, int vdev_id) +{ + int ret = 0; + + ret = ath11k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); + if (ret) { + ath11k_warn(ar->ab, "failed to put up monitor vdev %i: %d\n", + vdev_id, ret); + return ret; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac monitor vdev %i started\n", + vdev_id); + return 0; +} + +static int ath11k_mac_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath11k *ar = hw->priv; + int ret = 0; + + /* mac80211 requires this op to be present and that's why + * there's an empty function, this can be extended when + * required. + */ + + mutex_lock(&ar->conf_mutex); + + /* TODO: Handle configuration changes as appropriate */ + + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct ieee80211_hw *hw = ar->hw; + struct ieee80211_vif *vif = arvif->vif; + struct ieee80211_mutable_offsets offs = {}; + struct sk_buff *bcn; + int ret; + + if (arvif->vdev_type != WMI_VDEV_TYPE_AP) + return 0; + + bcn = ieee80211_beacon_get_template(hw, vif, &offs); + if (!bcn) { + ath11k_warn(ab, "failed to get beacon template from mac80211\n"); + return -EPERM; + } + + ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn); + + kfree_skb(bcn); + + if (ret) + ath11k_warn(ab, "failed to submit beacon template command: %d\n", + ret); + + return ret; +} + +static void ath11k_control_beaconing(struct ath11k_vif *arvif, + struct ieee80211_bss_conf *info) +{ + struct ath11k *ar = arvif->ar; + int ret = 0; + + lockdep_assert_held(&arvif->ar->conf_mutex); + + if (!info->enable_beacon) { + ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) + ath11k_warn(ar->ab, "failed to down vdev_id %i: %d\n", + arvif->vdev_id, ret); + + arvif->is_up = false; + return; + } + + /* Install the beacon template to the FW */ + ret = ath11k_mac_setup_bcn_tmpl(arvif); + if (ret) { + ath11k_warn(ar->ab, "failed to update bcn tmpl during vdev up: %d\n", + ret); + return; + } + + arvif->tx_seq_no = 0x1000; + + arvif->aid = 0; + + ether_addr_copy(arvif->bssid, info->bssid); + + ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); + if (ret) { + ath11k_warn(ar->ab, "failed to bring up vdev %d: %i\n", + arvif->vdev_id, ret); + return; + } + + arvif->is_up = true; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id); +} + +static void ath11k_peer_assoc_h_basic(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + u32 aid; + + lockdep_assert_held(&ar->conf_mutex); + + if (vif->type == NL80211_IFTYPE_STATION) + aid = vif->bss_conf.aid; + else + aid = sta->aid; + + ether_addr_copy(arg->peer_mac, sta->addr); + arg->vdev_id = arvif->vdev_id; + arg->peer_associd = aid; + arg->auth_flag = true; + /* TODO: STA WAR in ath10k for listen interval required? */ + arg->peer_listen_intval = ar->hw->conf.listen_interval; + arg->peer_nss = 1; + arg->peer_caps = vif->bss_conf.assoc_capability; +} + +static void ath11k_peer_assoc_h_crypto(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ieee80211_bss_conf *info = &vif->bss_conf; + struct cfg80211_chan_def def; + struct cfg80211_bss *bss; + const u8 *rsnie = NULL; + const u8 *wpaie = NULL; + + lockdep_assert_held(&ar->conf_mutex); + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid, NULL, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + if (bss) { + const struct cfg80211_bss_ies *ies; + + rcu_read_lock(); + rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN); + + ies = rcu_dereference(bss->ies); + + wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPA, + ies->data, + ies->len); + rcu_read_unlock(); + cfg80211_put_bss(ar->hw->wiphy, bss); + } + + /* FIXME: base on RSN IE/WPA IE is a correct idea? */ + if (rsnie || wpaie) { + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "%s: rsn ie found\n", __func__); + arg->need_ptk_4_way = true; + } + + if (wpaie) { + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "%s: wpa ie found\n", __func__); + arg->need_gtk_2_way = true; + } + + if (sta->mfp) { + /* TODO: Need to check if FW supports PMF? */ + arg->is_pmf_enabled = true; + } + + /* TODO: safe_mode_enabled (bypass 4-way handshake) flag req? */ +} + +static void ath11k_peer_assoc_h_rates(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates; + struct cfg80211_chan_def def; + const struct ieee80211_supported_band *sband; + const struct ieee80211_rate *rates; + enum nl80211_band band; + u32 ratemask; + u8 rate; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + band = def.chan->band; + sband = ar->hw->wiphy->bands[band]; + ratemask = sta->supp_rates[band]; + ratemask &= arvif->bitrate_mask.control[band].legacy; + rates = sband->bitrates; + + rateset->num_rates = 0; + + for (i = 0; i < 32; i++, ratemask >>= 1, rates++) { + if (!(ratemask & 1)) + continue; + + rate = ath11k_mac_bitrate_to_rate(rates->bitrate); + rateset->rates[rateset->num_rates] = rate; + rateset->num_rates++; + } +} + +static bool +ath11k_peer_assoc_h_ht_masked(const u8 ht_mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ + int nss; + + for (nss = 0; nss < IEEE80211_HT_MCS_MASK_LEN; nss++) + if (ht_mcs_mask[nss]) + return false; + + return true; +} + +static bool +ath11k_peer_assoc_h_vht_masked(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) +{ + int nss; + + for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) + if (vht_mcs_mask[nss]) + return false; + + return true; +} + +static void ath11k_peer_assoc_h_ht(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + enum nl80211_band band; + const u8 *ht_mcs_mask; + int i, n; + u8 max_nss; + u32 stbc; + + lockdep_assert_held(&ar->conf_mutex); + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + if (!ht_cap->ht_supported) + return; + + band = def.chan->band; + ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; + + if (ath11k_peer_assoc_h_ht_masked(ht_mcs_mask)) + return; + + arg->ht_flag = true; + + arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + + ht_cap->ampdu_factor)) - 1; + + arg->peer_mpdu_density = + ath11k_parse_mpdudensity(ht_cap->ampdu_density); + + arg->peer_ht_caps = ht_cap->cap; + arg->peer_rate_caps |= WMI_HOST_RC_HT_FLAG; + + if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) + arg->ldpc_flag = true; + + if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { + arg->bw_40 = true; + arg->peer_rate_caps |= WMI_HOST_RC_CW40_FLAG; + } + + if (arvif->bitrate_mask.control[band].gi != NL80211_TXRATE_FORCE_LGI) { + if (ht_cap->cap & (IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40)) + arg->peer_rate_caps |= WMI_HOST_RC_SGI_FLAG; + } + + if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) { + arg->peer_rate_caps |= WMI_HOST_RC_TX_STBC_FLAG; + arg->stbc_flag = true; + } + + if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) { + stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC; + stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT; + stbc = stbc << WMI_HOST_RC_RX_STBC_FLAG_S; + arg->peer_rate_caps |= stbc; + arg->stbc_flag = true; + } + + if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2]) + arg->peer_rate_caps |= WMI_HOST_RC_TS_FLAG; + else if (ht_cap->mcs.rx_mask[1]) + arg->peer_rate_caps |= WMI_HOST_RC_DS_FLAG; + + for (i = 0, n = 0, max_nss = 0; i < IEEE80211_HT_MCS_MASK_LEN * 8; i++) + if ((ht_cap->mcs.rx_mask[i / 8] & BIT(i % 8)) && + (ht_mcs_mask[i / 8] & BIT(i % 8))) { + max_nss = (i / 8) + 1; + arg->peer_ht_rates.rates[n++] = i; + } + + /* This is a workaround for HT-enabled STAs which break the spec + * and have no HT capabilities RX mask (no HT RX MCS map). + * + * As per spec, in section 20.3.5 Modulation and coding scheme (MCS), + * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs. + * + * Firmware asserts if such situation occurs. + */ + if (n == 0) { + arg->peer_ht_rates.num_rates = 8; + for (i = 0; i < arg->peer_ht_rates.num_rates; i++) + arg->peer_ht_rates.rates[i] = i; + } else { + arg->peer_ht_rates.num_rates = n; + arg->peer_nss = min(sta->rx_nss, max_nss); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", + arg->peer_mac, + arg->peer_ht_rates.num_rates, + arg->peer_nss); +} + +static int ath11k_mac_get_max_vht_mcs_map(u16 mcs_map, int nss) +{ + switch ((mcs_map >> (2 * nss)) & 0x3) { + case IEEE80211_VHT_MCS_SUPPORT_0_7: return BIT(8) - 1; + case IEEE80211_VHT_MCS_SUPPORT_0_8: return BIT(9) - 1; + case IEEE80211_VHT_MCS_SUPPORT_0_9: return BIT(10) - 1; + } + return 0; +} + +static u16 +ath11k_peer_assoc_h_vht_limit(u16 tx_mcs_set, + const u16 vht_mcs_limit[NL80211_VHT_NSS_MAX]) +{ + int idx_limit; + int nss; + u16 mcs_map; + u16 mcs; + + for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) { + mcs_map = ath11k_mac_get_max_vht_mcs_map(tx_mcs_set, nss) & + vht_mcs_limit[nss]; + + if (mcs_map) + idx_limit = fls(mcs_map) - 1; + else + idx_limit = -1; + + switch (idx_limit) { + case 0: /* fall through */ + case 1: /* fall through */ + case 2: /* fall through */ + case 3: /* fall through */ + case 4: /* fall through */ + case 5: /* fall through */ + case 6: /* fall through */ + case 7: + mcs = IEEE80211_VHT_MCS_SUPPORT_0_7; + break; + case 8: + mcs = IEEE80211_VHT_MCS_SUPPORT_0_8; + break; + case 9: + mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; + break; + default: + WARN_ON(1); + /* fall through */ + case -1: + mcs = IEEE80211_VHT_MCS_NOT_SUPPORTED; + break; + } + + tx_mcs_set &= ~(0x3 << (nss * 2)); + tx_mcs_set |= mcs << (nss * 2); + } + + return tx_mcs_set; +} + +static void ath11k_peer_assoc_h_vht(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + enum nl80211_band band; + const u16 *vht_mcs_mask; + u8 ampdu_factor; + u8 max_nss, vht_mcs; + int i; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + if (!vht_cap->vht_supported) + return; + + band = def.chan->band; + vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; + + if (ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) + return; + + arg->vht_flag = true; + + /* TODO: similar flags required? */ + arg->vht_capable = true; + + if (def.chan->band == NL80211_BAND_2GHZ) + arg->vht_ng_flag = true; + + arg->peer_vht_caps = vht_cap->cap; + + ampdu_factor = (vht_cap->cap & + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >> + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + + /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to + * zero in VHT IE. Using it would result in degraded throughput. + * arg->peer_max_mpdu at this point contains HT max_mpdu so keep + * it if VHT max_mpdu is smaller. + */ + arg->peer_max_mpdu = max(arg->peer_max_mpdu, + (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR + + ampdu_factor)) - 1); + + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + arg->bw_80 = true; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_160) + arg->bw_160 = true; + + /* Calculate peer NSS capability from VHT capabilities if STA + * supports VHT. + */ + for (i = 0, max_nss = 0, vht_mcs = 0; i < NL80211_VHT_NSS_MAX; i++) { + vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >> + (2 * i) & 3; + + if (vht_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED && + vht_mcs_mask[i]) + max_nss = i + 1; + } + arg->peer_nss = min(sta->rx_nss, max_nss); + arg->rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest); + arg->rx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); + arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest); + arg->tx_mcs_set = ath11k_peer_assoc_h_vht_limit( + __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map), vht_mcs_mask); + + /* In IPQ8074 platform, VHT mcs rate 10 and 11 is enabled by default. + * VHT mcs rate 10 and 11 is not suppoerted in 11ac standard. + * so explicitly disable the VHT MCS rate 10 and 11 in 11ac mode. + */ + arg->tx_mcs_set &= ~IEEE80211_VHT_MCS_SUPPORT_0_11_MASK; + arg->tx_mcs_set |= IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11; + + /* TODO: Check */ + arg->tx_max_mcs_nss = 0xFF; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n", + sta->addr, arg->peer_max_mpdu, arg->peer_flags); + + /* TODO: rxnss_override */ +} + +static void ath11k_peer_assoc_h_he(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + const struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; + u16 v; + + if (!he_cap->has_he) + return; + + arg->he_flag = true; + + memcpy(&arg->peer_he_cap_macinfo, he_cap->he_cap_elem.mac_cap_info, + sizeof(arg->peer_he_cap_macinfo)); + memcpy(&arg->peer_he_cap_phyinfo, he_cap->he_cap_elem.phy_cap_info, + sizeof(arg->peer_he_cap_phyinfo)); + memcpy(&arg->peer_he_ops, &vif->bss_conf.he_operation, + sizeof(arg->peer_he_ops)); + + /* the top most byte is used to indicate BSS color info */ + arg->peer_he_ops &= 0xffffff; + + if (he_cap->he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + int bit = 7; + int nss, ru; + + arg->peer_ppet.numss_m1 = he_cap->ppe_thres[0] & + IEEE80211_PPE_THRES_NSS_MASK; + arg->peer_ppet.ru_bit_mask = + (he_cap->ppe_thres[0] & + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK) >> + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_POS; + + for (nss = 0; nss <= arg->peer_ppet.numss_m1; nss++) { + for (ru = 0; ru < 4; ru++) { + u32 val = 0; + int i; + + if ((arg->peer_ppet.ru_bit_mask & BIT(ru)) == 0) + continue; + for (i = 0; i < 6; i++) { + val >>= 1; + val |= ((he_cap->ppe_thres[bit / 8] >> + (bit % 8)) & 0x1) << 5; + bit++; + } + arg->peer_ppet.ppet16_ppet8_ru3_ru0[nss] |= + val << (ru * 6); + } + } + } + + if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_RES) + arg->twt_responder = true; + if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_REQ) + arg->twt_requester = true; + + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { + v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80p80); + arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v; + + v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80p80); + arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v; + + arg->peer_he_mcs_count++; + } + v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); + arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; + + v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160); + arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; + + arg->peer_he_mcs_count++; + /* fall through */ + + default: + v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); + arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; + + v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80); + arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; + + arg->peer_he_mcs_count++; + break; + } +} + +static void ath11k_peer_assoc_h_smps(struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + int smps; + + if (!ht_cap->ht_supported) + return; + + smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; + smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + + switch (smps) { + case WLAN_HT_CAP_SM_PS_STATIC: + arg->static_mimops_flag = true; + break; + case WLAN_HT_CAP_SM_PS_DYNAMIC: + arg->dynamic_mimops_flag = true; + break; + case WLAN_HT_CAP_SM_PS_DISABLED: + arg->spatial_mux_flag = true; + break; + default: + break; + } +} + +static void ath11k_peer_assoc_h_qos(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_AP: + if (sta->wme) { + /* TODO: Check WME vs QoS */ + arg->is_wme_set = true; + arg->qos_flag = true; + } + + if (sta->wme && sta->uapsd_queues) { + /* TODO: Check WME vs QoS */ + arg->is_wme_set = true; + arg->apsd_flag = true; + arg->peer_rate_caps |= WMI_HOST_RC_UAPSD_FLAG; + } + break; + case WMI_VDEV_TYPE_STA: + if (sta->wme) { + arg->is_wme_set = true; + arg->qos_flag = true; + } + break; + default: + break; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac peer %pM qos %d\n", + sta->addr, arg->qos_flag); +} + +static int ath11k_peer_assoc_qos_ap(struct ath11k *ar, + struct ath11k_vif *arvif, + struct ieee80211_sta *sta) +{ + struct ap_ps_params params; + u32 max_sp; + u32 uapsd; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + params.vdev_id = arvif->vdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n", + sta->uapsd_queues, sta->max_sp); + + uapsd = 0; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) + uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) + uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN; + + max_sp = 0; + if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP) + max_sp = sta->max_sp; + + params.param = WMI_AP_PS_PEER_PARAM_UAPSD; + params.value = uapsd; + ret = ath11k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, ¶ms); + if (ret) + goto err; + + params.param = WMI_AP_PS_PEER_PARAM_MAX_SP; + params.value = max_sp; + ret = ath11k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, ¶ms); + if (ret) + goto err; + + /* TODO revisit during testing */ + params.param = WMI_AP_PS_PEER_PARAM_SIFS_RESP_FRMTYPE; + params.value = DISABLE_SIFS_RESPONSE_TRIGGER; + ret = ath11k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, ¶ms); + if (ret) + goto err; + + params.param = WMI_AP_PS_PEER_PARAM_SIFS_RESP_UAPSD; + params.value = DISABLE_SIFS_RESPONSE_TRIGGER; + ret = ath11k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, ¶ms); + if (ret) + goto err; + + return 0; + +err: + ath11k_warn(ar->ab, "failed to set ap ps peer param %d for vdev %i: %d\n", + params.param, arvif->vdev_id, ret); + return ret; +} + +static bool ath11k_mac_sta_has_ofdm_only(struct ieee80211_sta *sta) +{ + return sta->supp_rates[NL80211_BAND_2GHZ] >> + ATH11K_MAC_FIRST_OFDM_RATE_IDX; +} + +static enum wmi_phy_mode ath11k_mac_get_phymode_vht(struct ath11k *ar, + struct ieee80211_sta *sta) +{ + if (sta->bandwidth == IEEE80211_STA_RX_BW_160) { + switch (sta->vht_cap.cap & + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: + return MODE_11AC_VHT160; + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: + return MODE_11AC_VHT80_80; + default: + /* not sure if this is a valid case? */ + return MODE_11AC_VHT160; + } + } + + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + return MODE_11AC_VHT80; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + return MODE_11AC_VHT40; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_20) + return MODE_11AC_VHT20; + + return MODE_UNKNOWN; +} + +static enum wmi_phy_mode ath11k_mac_get_phymode_he(struct ath11k *ar, + struct ieee80211_sta *sta) +{ + if (sta->bandwidth == IEEE80211_STA_RX_BW_160) { + if (sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) + return MODE_11AX_HE160; + else if (sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + return MODE_11AX_HE80_80; + /* not sure if this is a valid case? */ + return MODE_11AX_HE160; + } + + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + return MODE_11AX_HE80; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + return MODE_11AX_HE40; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_20) + return MODE_11AX_HE20; + + return MODE_UNKNOWN; +} + +static void ath11k_peer_assoc_h_phymode(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + enum nl80211_band band; + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; + enum wmi_phy_mode phymode = MODE_UNKNOWN; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + band = def.chan->band; + ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; + vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; + + switch (band) { + case NL80211_BAND_2GHZ: + if (sta->he_cap.has_he) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + phymode = MODE_11AX_HE80_2G; + else if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11AX_HE40_2G; + else + phymode = MODE_11AX_HE20_2G; + } else if (sta->vht_cap.vht_supported && + !ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11AC_VHT40; + else + phymode = MODE_11AC_VHT20; + } else if (sta->ht_cap.ht_supported && + !ath11k_peer_assoc_h_ht_masked(ht_mcs_mask)) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11NG_HT40; + else + phymode = MODE_11NG_HT20; + } else if (ath11k_mac_sta_has_ofdm_only(sta)) { + phymode = MODE_11G; + } else { + phymode = MODE_11B; + } + break; + case NL80211_BAND_5GHZ: + /* Check HE first */ + if (sta->he_cap.has_he) { + phymode = ath11k_mac_get_phymode_he(ar, sta); + } else if (sta->vht_cap.vht_supported && + !ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) { + phymode = ath11k_mac_get_phymode_vht(ar, sta); + } else if (sta->ht_cap.ht_supported && + !ath11k_peer_assoc_h_ht_masked(ht_mcs_mask)) { + if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) + phymode = MODE_11NA_HT40; + else + phymode = MODE_11NA_HT20; + } else { + phymode = MODE_11A; + } + break; + default: + break; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac peer %pM phymode %s\n", + sta->addr, ath11k_wmi_phymode_str(phymode)); + + arg->peer_phymode = phymode; + WARN_ON(phymode == MODE_UNKNOWN); +} + +static void ath11k_peer_assoc_prepare(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg, + bool reassoc) +{ + lockdep_assert_held(&ar->conf_mutex); + + memset(arg, 0, sizeof(*arg)); + + reinit_completion(&ar->peer_assoc_done); + + arg->peer_new_assoc = !reassoc; + ath11k_peer_assoc_h_basic(ar, vif, sta, arg); + ath11k_peer_assoc_h_crypto(ar, vif, sta, arg); + ath11k_peer_assoc_h_rates(ar, vif, sta, arg); + ath11k_peer_assoc_h_ht(ar, vif, sta, arg); + ath11k_peer_assoc_h_vht(ar, vif, sta, arg); + ath11k_peer_assoc_h_he(ar, vif, sta, arg); + ath11k_peer_assoc_h_qos(ar, vif, sta, arg); + ath11k_peer_assoc_h_phymode(ar, vif, sta, arg); + ath11k_peer_assoc_h_smps(sta, arg); + + /* TODO: amsdu_disable req? */ +} + +static int ath11k_setup_peer_smps(struct ath11k *ar, struct ath11k_vif *arvif, + const u8 *addr, + const struct ieee80211_sta_ht_cap *ht_cap) +{ + int smps; + + if (!ht_cap->ht_supported) + return 0; + + smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; + smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (smps >= ARRAY_SIZE(ath11k_smps_map)) + return -EINVAL; + + return ath11k_wmi_set_peer_param(ar, addr, arvif->vdev_id, + WMI_PEER_MIMO_PS_STATE, + ath11k_smps_map[smps]); +} + +static void ath11k_bss_assoc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct peer_assoc_params peer_arg; + struct ieee80211_sta *ap_sta; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %i assoc bssid %pM aid %d\n", + arvif->vdev_id, arvif->bssid, arvif->aid); + + rcu_read_lock(); + + ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (!ap_sta) { + ath11k_warn(ar->ab, "failed to find station entry for bss %pM vdev %i\n", + bss_conf->bssid, arvif->vdev_id); + rcu_read_unlock(); + return; + } + + ath11k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg, false); + + rcu_read_unlock(); + + ret = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + if (ret) { + ath11k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n", + bss_conf->bssid, arvif->vdev_id, ret); + return; + } + + if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { + ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", + bss_conf->bssid, arvif->vdev_id); + return; + } + + ret = ath11k_setup_peer_smps(ar, arvif, bss_conf->bssid, + &ap_sta->ht_cap); + if (ret) { + ath11k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n", + arvif->vdev_id, ret); + return; + } + + WARN_ON(arvif->is_up); + + arvif->aid = bss_conf->aid; + ether_addr_copy(arvif->bssid, bss_conf->bssid); + + ret = ath11k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d up: %d\n", + arvif->vdev_id, ret); + return; + } + + arvif->is_up = true; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac vdev %d up (associated) bssid %pM aid %d\n", + arvif->vdev_id, bss_conf->bssid, bss_conf->aid); + + /* Authorize BSS Peer */ + ret = ath11k_wmi_set_peer_param(ar, arvif->bssid, + arvif->vdev_id, + WMI_PEER_AUTHORIZE, + 1); + if (ret) + ath11k_warn(ar->ab, "Unable to authorize BSS peer: %d\n", ret); + + ret = ath11k_wmi_send_obss_spr_cmd(ar, arvif->vdev_id, + &bss_conf->he_obss_pd); + if (ret) + ath11k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n", + arvif->vdev_id, ret); +} + +static void ath11k_bss_disassoc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %i disassoc bssid %pM\n", + arvif->vdev_id, arvif->bssid); + + ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) + ath11k_warn(ar->ab, "failed to down vdev %i: %d\n", + arvif->vdev_id, ret); + + arvif->is_up = false; + + /* TODO: cancel connection_loss_work */ +} + +static u32 ath11k_mac_get_rate_hw_value(int bitrate) +{ + u32 preamble; + u16 hw_value; + int rate; + size_t i; + + if (ath11k_mac_bitrate_is_cck(bitrate)) + preamble = WMI_RATE_PREAMBLE_CCK; + else + preamble = WMI_RATE_PREAMBLE_OFDM; + + for (i = 0; i < ARRAY_SIZE(ath11k_legacy_rates); i++) { + if (ath11k_legacy_rates[i].bitrate != bitrate) + continue; + + hw_value = ath11k_legacy_rates[i].hw_value; + rate = ATH11K_HW_RATE_CODE(hw_value, 0, preamble); + + return rate; + } + + return -EINVAL; +} + +static void ath11k_recalculate_mgmt_rate(struct ath11k *ar, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *def) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + const struct ieee80211_supported_band *sband; + u8 basic_rate_idx; + int hw_rate_code; + u32 vdev_param; + u16 bitrate; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + sband = ar->hw->wiphy->bands[def->chan->band]; + basic_rate_idx = ffs(vif->bss_conf.basic_rates) - 1; + bitrate = sband->bitrates[basic_rate_idx].bitrate; + + hw_rate_code = ath11k_mac_get_rate_hw_value(bitrate); + if (hw_rate_code < 0) { + ath11k_warn(ar->ab, "bitrate not supported %d\n", bitrate); + return; + } + + vdev_param = WMI_VDEV_PARAM_MGMT_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, + hw_rate_code); + if (ret) + ath11k_warn(ar->ab, "failed to set mgmt tx rate %d\n", ret); + + vdev_param = WMI_VDEV_PARAM_BEACON_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, + hw_rate_code); + if (ret) + ath11k_warn(ar->ab, "failed to set beacon tx rate %d\n", ret); +} + +static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct cfg80211_chan_def def; + u32 param_id, param_value; + enum nl80211_band band; + u32 vdev_param; + int mcast_rate; + u32 preamble; + u16 hw_value; + u16 bitrate; + int ret = 0; + u8 rateidx; + u32 rate; + + mutex_lock(&ar->conf_mutex); + + if (changed & BSS_CHANGED_BEACON_INT) { + arvif->beacon_interval = info->beacon_int; + + param_id = WMI_VDEV_PARAM_BEACON_INTERVAL; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, + arvif->beacon_interval); + if (ret) + ath11k_warn(ar->ab, "Failed to set beacon interval for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Beacon interval: %d set for VDEV: %d\n", + arvif->beacon_interval, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_BEACON) { + param_id = WMI_PDEV_PARAM_BEACON_TX_MODE; + param_value = WMI_BEACON_STAGGERED_MODE; + ret = ath11k_wmi_pdev_set_param(ar, param_id, + param_value, ar->pdev->pdev_id); + if (ret) + ath11k_warn(ar->ab, "Failed to set beacon mode for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Set staggered beacon mode for VDEV: %d\n", + arvif->vdev_id); + + ret = ath11k_mac_setup_bcn_tmpl(arvif); + if (ret) + ath11k_warn(ar->ab, "failed to update bcn template: %d\n", + ret); + + if (vif->bss_conf.he_support) { + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_BA_MODE, + WMI_BA_MODE_BUFFER_SIZE_256); + if (ret) + ath11k_warn(ar->ab, + "failed to set BA BUFFER SIZE 256 for vdev: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Set BA BUFFER SIZE 256 for VDEV: %d\n", + arvif->vdev_id); + } + } + + if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) { + arvif->dtim_period = info->dtim_period; + + param_id = WMI_VDEV_PARAM_DTIM_PERIOD; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, + arvif->dtim_period); + + if (ret) + ath11k_warn(ar->ab, "Failed to set dtim period for VDEV %d: %i\n", + arvif->vdev_id, ret); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "DTIM period: %d set for VDEV: %d\n", + arvif->dtim_period, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_SSID && + vif->type == NL80211_IFTYPE_AP) { + arvif->u.ap.ssid_len = info->ssid_len; + if (info->ssid_len) + memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len); + arvif->u.ap.hidden_ssid = info->hidden_ssid; + } + + if (changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) + ether_addr_copy(arvif->bssid, info->bssid); + + if (changed & BSS_CHANGED_BEACON_ENABLED) + ath11k_control_beaconing(arvif, info); + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + u32 cts_prot; + + cts_prot = !!(info->use_cts_prot); + param_id = WMI_VDEV_PARAM_PROTECTION_MODE; + + if (arvif->is_started) { + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, cts_prot); + if (ret) + ath11k_warn(ar->ab, "Failed to set CTS prot for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Set CTS prot: %d for VDEV: %d\n", + cts_prot, arvif->vdev_id); + } else { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "defer protection mode setup, vdev is not ready yet\n"); + } + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + u32 slottime; + + if (info->use_short_slot) + slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */ + + else + slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */ + + param_id = WMI_VDEV_PARAM_SLOT_TIME; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, slottime); + if (ret) + ath11k_warn(ar->ab, "Failed to set erp slot for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Set slottime: %d for VDEV: %d\n", + slottime, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + u32 preamble; + + if (info->use_short_preamble) + preamble = WMI_VDEV_PREAMBLE_SHORT; + else + preamble = WMI_VDEV_PREAMBLE_LONG; + + param_id = WMI_VDEV_PARAM_PREAMBLE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, preamble); + if (ret) + ath11k_warn(ar->ab, "Failed to set preamble for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Set preamble: %d for VDEV: %d\n", + preamble, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ASSOC) { + if (info->assoc) + ath11k_bss_assoc(hw, vif, info); + else + ath11k_bss_disassoc(hw, vif); + } + + if (changed & BSS_CHANGED_TXPOWER) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev_id %i txpower %d\n", + arvif->vdev_id, info->txpower); + + arvif->txpower = info->txpower; + ath11k_mac_txpower_recalc(ar); + } + + if (changed & BSS_CHANGED_MCAST_RATE && + !ath11k_mac_vif_chan(arvif->vif, &def)) { + band = def.chan->band; + mcast_rate = vif->bss_conf.mcast_rate[band]; + + if (mcast_rate > 0) + rateidx = mcast_rate - 1; + else + rateidx = ffs(vif->bss_conf.basic_rates) - 1; + + if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) + rateidx += ATH11K_MAC_FIRST_OFDM_RATE_IDX; + + bitrate = ath11k_legacy_rates[rateidx].bitrate; + hw_value = ath11k_legacy_rates[rateidx].hw_value; + + if (ath11k_mac_bitrate_is_cck(bitrate)) + preamble = WMI_RATE_PREAMBLE_CCK; + else + preamble = WMI_RATE_PREAMBLE_OFDM; + + rate = ATH11K_HW_RATE_CODE(hw_value, 0, preamble); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac vdev %d mcast_rate %x\n", + arvif->vdev_id, rate); + + vdev_param = WMI_VDEV_PARAM_MCAST_DATA_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, rate); + if (ret) + ath11k_warn(ar->ab, + "failed to set mcast rate on vdev %i: %d\n", + arvif->vdev_id, ret); + + vdev_param = WMI_VDEV_PARAM_BCAST_DATA_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, rate); + if (ret) + ath11k_warn(ar->ab, + "failed to set bcast rate on vdev %i: %d\n", + arvif->vdev_id, ret); + } + + if (changed & BSS_CHANGED_BASIC_RATES && + !ath11k_mac_vif_chan(arvif->vif, &def)) + ath11k_recalculate_mgmt_rate(ar, vif, &def); + + if (changed & BSS_CHANGED_TWT) { + if (info->twt_requester || info->twt_responder) + ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id); + else + ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id); + } + + if (changed & BSS_CHANGED_HE_OBSS_PD) + ath11k_wmi_send_obss_spr_cmd(ar, arvif->vdev_id, + &info->he_obss_pd); + + mutex_unlock(&ar->conf_mutex); +} + +void __ath11k_mac_scan_finish(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + break; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + if (!ar->scan.is_roc) { + struct cfg80211_scan_info info = { + .aborted = (ar->scan.state == + ATH11K_SCAN_ABORTING), + }; + + ieee80211_scan_completed(ar->hw, &info); + } else if (ar->scan.roc_notify) { + ieee80211_remain_on_channel_expired(ar->hw); + } + /* fall through */ + case ATH11K_SCAN_STARTING: + ar->scan.state = ATH11K_SCAN_IDLE; + ar->scan_channel = NULL; + ar->scan.roc_freq = 0; + cancel_delayed_work(&ar->scan.timeout); + complete(&ar->scan.completed); + break; + } +} + +void ath11k_mac_scan_finish(struct ath11k *ar) +{ + spin_lock_bh(&ar->data_lock); + __ath11k_mac_scan_finish(ar); + spin_unlock_bh(&ar->data_lock); +} + +static int ath11k_scan_stop(struct ath11k *ar) +{ + struct scan_cancel_param arg = { + .req_type = WLAN_SCAN_CANCEL_SINGLE, + .scan_id = ATH11K_SCAN_ID, + }; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + /* TODO: Fill other STOP Params */ + arg.pdev_id = ar->pdev->pdev_id; + + ret = ath11k_wmi_send_scan_stop_cmd(ar, &arg); + if (ret) { + ath11k_warn(ar->ab, "failed to stop wmi scan: %d\n", ret); + goto out; + } + + ret = wait_for_completion_timeout(&ar->scan.completed, 3 * HZ); + if (ret == 0) { + ath11k_warn(ar->ab, + "failed to receive scan abort comple: timed out\n"); + ret = -ETIMEDOUT; + } else if (ret > 0) { + ret = 0; + } + +out: + /* Scan state should be updated upon scan completion but in case + * firmware fails to deliver the event (for whatever reason) it is + * desired to clean up scan state anyway. Firmware may have just + * dropped the scan completion event delivery due to transport pipe + * being overflown with data and/or it can recover on its own before + * next scan request is submitted. + */ + spin_lock_bh(&ar->data_lock); + if (ar->scan.state != ATH11K_SCAN_IDLE) + __ath11k_mac_scan_finish(ar); + spin_unlock_bh(&ar->data_lock); + + return ret; +} + +static void ath11k_scan_abort(struct ath11k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + /* This can happen if timeout worker kicked in and called + * abortion while scan completion was being processed. + */ + break; + case ATH11K_SCAN_STARTING: + case ATH11K_SCAN_ABORTING: + ath11k_warn(ar->ab, "refusing scan abortion due to invalid scan state: %d\n", + ar->scan.state); + break; + case ATH11K_SCAN_RUNNING: + ar->scan.state = ATH11K_SCAN_ABORTING; + spin_unlock_bh(&ar->data_lock); + + ret = ath11k_scan_stop(ar); + if (ret) + ath11k_warn(ar->ab, "failed to abort scan: %d\n", ret); + + spin_lock_bh(&ar->data_lock); + break; + } + + spin_unlock_bh(&ar->data_lock); +} + +static void ath11k_scan_timeout_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, + scan.timeout.work); + + mutex_lock(&ar->conf_mutex); + ath11k_scan_abort(ar); + mutex_unlock(&ar->conf_mutex); +} + +static int ath11k_start_scan(struct ath11k *ar, + struct scan_req_params *arg) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath11k_wmi_send_scan_start_cmd(ar, arg); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&ar->scan.started, 1 * HZ); + if (ret == 0) { + ret = ath11k_scan_stop(ar); + if (ret) + ath11k_warn(ar->ab, "failed to stop scan: %d\n", ret); + + return -ETIMEDOUT; + } + + /* If we failed to start the scan, return error code at + * this point. This is probably due to some issue in the + * firmware, but no need to wedge the driver due to that... + */ + spin_lock_bh(&ar->data_lock); + if (ar->scan.state == ATH11K_SCAN_IDLE) { + spin_unlock_bh(&ar->data_lock); + return -EINVAL; + } + spin_unlock_bh(&ar->data_lock); + + return 0; +} + +static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct cfg80211_scan_request *req = &hw_req->req; + struct scan_req_params arg; + int ret = 0; + int i; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + reinit_completion(&ar->scan.started); + reinit_completion(&ar->scan.completed); + ar->scan.state = ATH11K_SCAN_STARTING; + ar->scan.is_roc = false; + ar->scan.vdev_id = arvif->vdev_id; + ret = 0; + break; + case ATH11K_SCAN_STARTING: + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ret = -EBUSY; + break; + } + spin_unlock_bh(&ar->data_lock); + + if (ret) + goto exit; + + memset(&arg, 0, sizeof(arg)); + ath11k_wmi_start_scan_init(ar, &arg); + arg.vdev_id = arvif->vdev_id; + arg.scan_id = ATH11K_SCAN_ID; + + if (req->ie_len) { + arg.extraie.len = req->ie_len; + arg.extraie.ptr = kzalloc(req->ie_len, GFP_KERNEL); + memcpy(arg.extraie.ptr, req->ie, req->ie_len); + } + + if (req->n_ssids) { + arg.num_ssids = req->n_ssids; + for (i = 0; i < arg.num_ssids; i++) { + arg.ssid[i].length = req->ssids[i].ssid_len; + memcpy(&arg.ssid[i].ssid, req->ssids[i].ssid, + req->ssids[i].ssid_len); + } + } else { + arg.scan_flags |= WMI_SCAN_FLAG_PASSIVE; + } + + if (req->n_channels) { + arg.num_chan = req->n_channels; + for (i = 0; i < arg.num_chan; i++) + arg.chan_list[i] = req->channels[i]->center_freq; + } + + ret = ath11k_start_scan(ar, &arg); + if (ret) { + ath11k_warn(ar->ab, "failed to start hw scan: %d\n", ret); + spin_lock_bh(&ar->data_lock); + ar->scan.state = ATH11K_SCAN_IDLE; + spin_unlock_bh(&ar->data_lock); + } + + /* Add a 200ms margin to account for event/command processing */ + ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, + msecs_to_jiffies(arg.max_scan_time + + ATH11K_MAC_SCAN_TIMEOUT_MSECS)); + +exit: + if (req->ie_len) + kfree(arg.extraie.ptr); + + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath11k_mac_op_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + ath11k_scan_abort(ar); + mutex_unlock(&ar->conf_mutex); + + cancel_delayed_work_sync(&ar->scan.timeout); +} + +static int ath11k_install_key(struct ath11k_vif *arvif, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd, + const u8 *macaddr, u32 flags) +{ + int ret; + struct ath11k *ar = arvif->ar; + struct wmi_vdev_install_key_arg arg = { + .vdev_id = arvif->vdev_id, + .key_idx = key->keyidx, + .key_len = key->keylen, + .key_data = key->key, + .key_flags = flags, + .macaddr = macaddr, + }; + + lockdep_assert_held(&arvif->ar->conf_mutex); + + reinit_completion(&ar->install_key_done); + + if (cmd == DISABLE_KEY) { + /* TODO: Check if FW expects value other than NONE for del */ + /* arg.key_cipher = WMI_CIPHER_NONE; */ + arg.key_len = 0; + arg.key_data = NULL; + goto install; + } + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + arg.key_cipher = WMI_CIPHER_AES_CCM; + /* TODO: Re-check if flag is valid */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; + break; + case WLAN_CIPHER_SUITE_TKIP: + arg.key_cipher = WMI_CIPHER_TKIP; + arg.key_txmic_len = 8; + arg.key_rxmic_len = 8; + break; + case WLAN_CIPHER_SUITE_CCMP_256: + arg.key_cipher = WMI_CIPHER_AES_CCM; + break; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + arg.key_cipher = WMI_CIPHER_AES_GCM; + break; + default: + ath11k_warn(ar->ab, "cipher %d is not supported\n", key->cipher); + return -EOPNOTSUPP; + } + +install: + ret = ath11k_wmi_vdev_install_key(arvif->ar, &arg); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&ar->install_key_done, 1 * HZ)) + return -ETIMEDOUT; + + return ar->install_key_status ? -EINVAL : 0; +} + +static int ath11k_clear_peer_keys(struct ath11k_vif *arvif, + const u8 *addr) +{ + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + int first_errno = 0; + int ret; + int i; + u32 flags = 0; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, arvif->vdev_id, addr); + spin_unlock_bh(&ab->base_lock); + + if (!peer) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { + if (!peer->keys[i]) + continue; + + /* key flags are not required to delete the key */ + ret = ath11k_install_key(arvif, peer->keys[i], + DISABLE_KEY, addr, flags); + if (ret < 0 && first_errno == 0) + first_errno = ret; + + if (ret < 0) + ath11k_warn(ab, "failed to remove peer key %d: %d\n", + i, ret); + + spin_lock_bh(&ab->base_lock); + peer->keys[i] = NULL; + spin_unlock_bh(&ab->base_lock); + } + + return first_errno; +} + +static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_peer *peer; + const u8 *peer_addr; + int ret = 0; + u32 flags = 0; + + /* BIP needs to be done in software */ + if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC || + key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || + key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 || + key->cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256) + return 1; + + if (key->keyidx > WMI_MAX_KEY_INDEX) + return -ENOSPC; + + mutex_lock(&ar->conf_mutex); + + if (sta) + peer_addr = sta->addr; + else if (arvif->vdev_type == WMI_VDEV_TYPE_STA) + peer_addr = vif->bss_conf.bssid; + else + peer_addr = vif->addr; + + key->hw_key_idx = key->keyidx; + + /* the peer should not disappear in mid-way (unless FW goes awry) since + * we already hold conf_mutex. we just make sure its there now. + */ + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); + spin_unlock_bh(&ab->base_lock); + + if (!peer) { + if (cmd == SET_KEY) { + ath11k_warn(ab, "cannot install key for non-existent peer %pM\n", + peer_addr); + ret = -EOPNOTSUPP; + goto exit; + } else { + /* if the peer doesn't exist there is no key to disable + * anymore + */ + goto exit; + } + } + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + flags |= WMI_KEY_PAIRWISE; + else + flags |= WMI_KEY_GROUP; + + ret = ath11k_install_key(arvif, key, cmd, peer_addr, flags); + if (ret) { + ath11k_warn(ab, "ath11k_install_key failed (%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) + peer->keys[key->keyidx] = key; + else if (peer && cmd == DISABLE_KEY) + peer->keys[key->keyidx] = NULL; + else if (!peer) + /* impossible unless FW goes crazy */ + ath11k_warn(ab, "peer %pM disappeared!\n", peer_addr); + spin_unlock_bh(&ab->base_lock); + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int +ath11k_mac_bitrate_mask_num_vht_rates(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + int num_rates = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) + num_rates += hweight16(mask->control[band].vht_mcs[i]); + + return num_rates; +} + +static int +ath11k_mac_set_peer_vht_fixed_rate(struct ath11k_vif *arvif, + struct ieee80211_sta *sta, + const struct cfg80211_bitrate_mask *mask, + enum nl80211_band band) +{ + struct ath11k *ar = arvif->ar; + u8 vht_rate, nss; + u32 rate_code; + int ret, i; + + lockdep_assert_held(&ar->conf_mutex); + + nss = 0; + + for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) { + if (hweight16(mask->control[band].vht_mcs[i]) == 1) { + nss = i + 1; + vht_rate = ffs(mask->control[band].vht_mcs[i]) - 1; + } + } + + if (!nss) { + ath11k_warn(ar->ab, "No single VHT Fixed rate found to set for %pM", + sta->addr); + return -EINVAL; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Setting Fixed VHT Rate for peer %pM. Device will not switch to any other selected rates", + sta->addr); + + rate_code = ATH11K_HW_RATE_CODE(vht_rate, nss - 1, + WMI_RATE_PREAMBLE_VHT); + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_PARAM_FIXED_RATE, + rate_code); + if (ret) + ath11k_warn(ar->ab, + "failed to update STA %pM Fixed Rate %d: %d\n", + sta->addr, rate_code, ret); + + return ret; +} + +static int ath11k_station_assoc(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool reassoc) +{ + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct peer_assoc_params peer_arg; + int ret = 0; + struct cfg80211_chan_def def; + enum nl80211_band band; + struct cfg80211_bitrate_mask *mask; + u8 num_vht_rates; + + lockdep_assert_held(&ar->conf_mutex); + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return -EPERM; + + band = def.chan->band; + mask = &arvif->bitrate_mask; + + ath11k_peer_assoc_prepare(ar, vif, sta, &peer_arg, reassoc); + + ret = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + if (ret) { + ath11k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", + sta->addr, arvif->vdev_id, ret); + return ret; + } + + if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { + ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", + sta->addr, arvif->vdev_id); + return -ETIMEDOUT; + } + + num_vht_rates = ath11k_mac_bitrate_mask_num_vht_rates(ar, band, mask); + + /* If single VHT rate is configured (by set_bitrate_mask()), + * peer_assoc will disable VHT. This is now enabled by a peer specific + * fixed param. + * Note that all other rates and NSS will be disabled for this peer. + */ + if (sta->vht_cap.vht_supported && num_vht_rates == 1) { + ret = ath11k_mac_set_peer_vht_fixed_rate(arvif, sta, mask, + band); + if (ret) + return ret; + } + + /* Re-assoc is run only to update supported rates for given station. It + * doesn't make much sense to reconfigure the peer completely. + */ + if (reassoc) + return 0; + + ret = ath11k_setup_peer_smps(ar, arvif, sta->addr, + &sta->ht_cap); + if (ret) { + ath11k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n", + arvif->vdev_id, ret); + return ret; + } + + if (!sta->wme) { + arvif->num_legacy_stations++; + ret = ath11k_recalc_rtscts_prot(arvif); + if (ret) + return ret; + } + + if (sta->wme && sta->uapsd_queues) { + ret = ath11k_peer_assoc_qos_ap(ar, arvif, sta); + if (ret) { + ath11k_warn(ar->ab, "failed to set qos params for STA %pM for vdev %i: %d\n", + sta->addr, arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath11k_station_disassoc(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + if (!sta->wme) { + arvif->num_legacy_stations--; + ret = ath11k_recalc_rtscts_prot(arvif); + if (ret) + return ret; + } + + ret = ath11k_clear_peer_keys(arvif, sta->addr); + if (ret) { + ath11k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + return 0; +} + +static void ath11k_sta_rc_update_wk(struct work_struct *wk) +{ + struct ath11k *ar; + struct ath11k_vif *arvif; + struct ath11k_sta *arsta; + struct ieee80211_sta *sta; + struct cfg80211_chan_def def; + enum nl80211_band band; + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; + u32 changed, bw, nss, smps; + int err, num_vht_rates; + const struct cfg80211_bitrate_mask *mask; + struct peer_assoc_params peer_arg; + + arsta = container_of(wk, struct ath11k_sta, update_wk); + sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); + arvif = arsta->arvif; + ar = arvif->ar; + + if (WARN_ON(ath11k_mac_vif_chan(arvif->vif, &def))) + return; + + band = def.chan->band; + ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; + vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; + + spin_lock_bh(&ar->data_lock); + + changed = arsta->changed; + arsta->changed = 0; + + bw = arsta->bw; + nss = arsta->nss; + smps = arsta->smps; + + spin_unlock_bh(&ar->data_lock); + + mutex_lock(&ar->conf_mutex); + + nss = max_t(u32, 1, nss); + nss = min(nss, max(ath11k_mac_max_ht_nss(ht_mcs_mask), + ath11k_mac_max_vht_nss(vht_mcs_mask))); + + if (changed & IEEE80211_RC_BW_CHANGED) { + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_CHWIDTH, bw); + if (err) + ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", + sta->addr, bw, err); + } + + if (changed & IEEE80211_RC_NSS_CHANGED) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac update sta %pM nss %d\n", + sta->addr, nss); + + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_NSS, nss); + if (err) + ath11k_warn(ar->ab, "failed to update STA %pM nss %d: %d\n", + sta->addr, nss, err); + } + + if (changed & IEEE80211_RC_SMPS_CHANGED) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac update sta %pM smps %d\n", + sta->addr, smps); + + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_MIMO_PS_STATE, smps); + if (err) + ath11k_warn(ar->ab, "failed to update STA %pM smps %d: %d\n", + sta->addr, smps, err); + } + + if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { + mask = &arvif->bitrate_mask; + num_vht_rates = ath11k_mac_bitrate_mask_num_vht_rates(ar, band, + mask); + + /* Peer_assoc_prepare will reject vht rates in + * bitrate_mask if its not available in range format and + * sets vht tx_rateset as unsupported. So multiple VHT MCS + * setting(eg. MCS 4,5,6) per peer is not supported here. + * But, Single rate in VHT mask can be set as per-peer + * fixed rate. But even if any HT rates are configured in + * the bitrate mask, device will not switch to those rates + * when per-peer Fixed rate is set. + * TODO: Check RATEMASK_CMDID to support auto rates selection + * across HT/VHT and for multiple VHT MCS support. + */ + if (sta->vht_cap.vht_supported && num_vht_rates == 1) { + ath11k_mac_set_peer_vht_fixed_rate(arvif, sta, mask, + band); + } else { + /* If the peer is non-VHT or no fixed VHT rate + * is provided in the new bitrate mask we set the + * other rates using peer_assoc command. + */ + ath11k_peer_assoc_prepare(ar, arvif->vif, sta, + &peer_arg, true); + + err = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + if (err) + ath11k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", + sta->addr, arvif->vdev_id, err); + + if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) + ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", + sta->addr, arvif->vdev_id); + } + } + + mutex_unlock(&ar->conf_mutex); +} + +static int ath11k_mac_inc_num_stations(struct ath11k_vif *arvif, + struct ieee80211_sta *sta) +{ + struct ath11k *ar = arvif->ar; + + lockdep_assert_held(&ar->conf_mutex); + + if (arvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls) + return 0; + + if (ar->num_stations >= ar->max_num_stations) + return -ENOBUFS; + + ar->num_stations++; + + return 0; +} + +static void ath11k_mac_dec_num_stations(struct ath11k_vif *arvif, + struct ieee80211_sta *sta) +{ + struct ath11k *ar = arvif->ar; + + lockdep_assert_held(&ar->conf_mutex); + + if (arvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls) + return; + + ar->num_stations--; +} + +static int ath11k_mac_station_add(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct peer_create_params peer_param; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath11k_mac_inc_num_stations(arvif, sta); + if (ret) { + ath11k_warn(ab, "refusing to associate station: too many connected already (%d)\n", + ar->max_num_stations); + goto exit; + } + + arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); + if (!arsta->rx_stats) { + ret = -ENOMEM; + goto dec_num_station; + } + + peer_param.vdev_id = arvif->vdev_id; + peer_param.peer_addr = sta->addr; + peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; + + ret = ath11k_peer_create(ar, arvif, sta, &peer_param); + if (ret) { + ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + goto free_rx_stats; + } + + ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) { + arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL); + if (!arsta->tx_stats) { + ret = -ENOMEM; + goto free_peer; + } + } + + if (ieee80211_vif_is_mesh(vif)) { + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_USE_4ADDR, 1); + if (ret) { + ath11k_warn(ab, "failed to STA %pM 4addr capability: %d\n", + sta->addr, ret); + goto free_tx_stats; + } + } + + ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); + if (ret) { + ath11k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", + sta->addr, arvif->vdev_id, ret); + goto free_tx_stats; + } + + return 0; + +free_tx_stats: + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; +free_peer: + ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); +free_rx_stats: + kfree(arsta->rx_stats); + arsta->rx_stats = NULL; +dec_num_station: + ath11k_mac_dec_num_stations(arvif, sta); +exit: + return ret; +} + +static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + int ret = 0; + + /* cancel must be done outside the mutex to avoid deadlock */ + if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) + cancel_work_sync(&arsta->update_wk); + + mutex_lock(&ar->conf_mutex); + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + memset(arsta, 0, sizeof(*arsta)); + arsta->arvif = arvif; + INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk); + + ret = ath11k_mac_station_add(ar, vif, sta); + if (ret) + ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + } else if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) { + ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); + + ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); + if (ret) + ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + + ath11k_mac_dec_num_stations(arvif, sta); + + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; + + kfree(arsta->rx_stats); + arsta->rx_stats = NULL; + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || + vif->type == NL80211_IFTYPE_ADHOC)) { + ret = ath11k_station_assoc(ar, vif, sta, false); + if (ret) + ath11k_warn(ar->ab, "Failed to associate station: %pM\n", + sta->addr); + else + ath11k_info(ar->ab, + "Station %pM moved to assoc state\n", + sta->addr); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || + vif->type == NL80211_IFTYPE_ADHOC)) { + ret = ath11k_station_disassoc(ar, vif, sta); + if (ret) + ath11k_warn(ar->ab, "Failed to disassociate station: %pM\n", + sta->addr); + else + ath11k_info(ar->ab, + "Station %pM moved to disassociated state\n", + sta->addr); + } + + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret = 0; + s16 txpwr; + + if (sta->txpwr.type == NL80211_TX_POWER_AUTOMATIC) { + txpwr = 0; + } else { + txpwr = sta->txpwr.power; + if (!txpwr) + return -EINVAL; + } + + if (txpwr > ATH11K_TX_POWER_MAX_VAL || txpwr < ATH11K_TX_POWER_MIN_VAL) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + ret = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_USE_FIXED_PWR, txpwr); + if (ret) { + ath11k_warn(ar->ab, "failed to set tx power for station ret: %d\n", + ret); + goto out; + } + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath11k_mac_op_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct ath11k *ar = hw->priv; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct ath11k_peer *peer; + u32 bw, smps; + + spin_lock_bh(&ar->ab->base_lock); + + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); + if (!peer) { + spin_unlock_bh(&ar->ab->base_lock); + ath11k_warn(ar->ab, "mac sta rc update failed to find peer %pM on vdev %i\n", + sta->addr, arvif->vdev_id); + return; + } + + spin_unlock_bh(&ar->ab->base_lock); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n", + sta->addr, changed, sta->bandwidth, sta->rx_nss, + sta->smps_mode); + + spin_lock_bh(&ar->data_lock); + + if (changed & IEEE80211_RC_BW_CHANGED) { + bw = WMI_PEER_CHWIDTH_20MHZ; + + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_20: + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + case IEEE80211_STA_RX_BW_40: + bw = WMI_PEER_CHWIDTH_40MHZ; + break; + case IEEE80211_STA_RX_BW_80: + bw = WMI_PEER_CHWIDTH_80MHZ; + break; + case IEEE80211_STA_RX_BW_160: + bw = WMI_PEER_CHWIDTH_160MHZ; + break; + default: + ath11k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", + sta->bandwidth, sta->addr); + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + } + + arsta->bw = bw; + } + + if (changed & IEEE80211_RC_NSS_CHANGED) + arsta->nss = sta->rx_nss; + + if (changed & IEEE80211_RC_SMPS_CHANGED) { + smps = WMI_PEER_SMPS_PS_NONE; + + switch (sta->smps_mode) { + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_OFF: + smps = WMI_PEER_SMPS_PS_NONE; + break; + case IEEE80211_SMPS_STATIC: + smps = WMI_PEER_SMPS_STATIC; + break; + case IEEE80211_SMPS_DYNAMIC: + smps = WMI_PEER_SMPS_DYNAMIC; + break; + default: + ath11k_warn(ar->ab, "Invalid smps %d in sta rc update for %pM\n", + sta->smps_mode, sta->addr); + smps = WMI_PEER_SMPS_PS_NONE; + break; + } + + arsta->smps = smps; + } + + arsta->changed |= changed; + + spin_unlock_bh(&ar->data_lock); + + ieee80211_queue_work(hw, &arsta->update_wk); +} + +static int ath11k_conf_tx_uapsd(struct ath11k *ar, struct ieee80211_vif *vif, + u16 ac, bool enable) +{ + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + u32 value = 0; + int ret = 0; + + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + return 0; + + switch (ac) { + case IEEE80211_AC_VO: + value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN; + break; + case IEEE80211_AC_VI: + value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN; + break; + case IEEE80211_AC_BE: + value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN; + break; + case IEEE80211_AC_BK: + value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN; + break; + } + + if (enable) + arvif->u.sta.uapsd |= value; + else + arvif->u.sta.uapsd &= ~value; + + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + WMI_STA_PS_PARAM_UAPSD, + arvif->u.sta.uapsd); + if (ret) { + ath11k_warn(ar->ab, "could not set uapsd params %d\n", ret); + goto exit; + } + + if (arvif->u.sta.uapsd) + value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD; + else + value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + WMI_STA_PS_PARAM_RX_WAKE_POLICY, + value); + if (ret) + ath11k_warn(ar->ab, "could not set rx wake param %d\n", ret); + +exit: + return ret; +} + +static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct wmi_wmm_params_arg *p = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + switch (ac) { + case IEEE80211_AC_VO: + p = &arvif->wmm_params.ac_vo; + break; + case IEEE80211_AC_VI: + p = &arvif->wmm_params.ac_vi; + break; + case IEEE80211_AC_BE: + p = &arvif->wmm_params.ac_be; + break; + case IEEE80211_AC_BK: + p = &arvif->wmm_params.ac_bk; + break; + } + + if (WARN_ON(!p)) { + ret = -EINVAL; + goto exit; + } + + p->cwmin = params->cw_min; + p->cwmax = params->cw_max; + p->aifs = params->aifs; + p->txop = params->txop; + + ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id, + &arvif->wmm_params); + if (ret) { + ath11k_warn(ar->ab, "failed to set wmm params: %d\n", ret); + goto exit; + } + + ret = ath11k_conf_tx_uapsd(ar, vif, ac, params->uapsd); + + if (ret) + ath11k_warn(ar->ab, "failed to set sta uapsd: %d\n", ret); + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static struct ieee80211_sta_ht_cap +ath11k_create_ht_cap(struct ath11k *ar, u32 ar_ht_cap, u32 rate_cap_rx_chainmask) +{ + int i; + struct ieee80211_sta_ht_cap ht_cap = {0}; + u32 ar_vht_cap = ar->pdev->cap.vht_cap; + + if (!(ar_ht_cap & WMI_HT_CAP_ENABLED)) + return ht_cap; + + ht_cap.ht_supported = 1; + ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; + ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (ar_ht_cap & WMI_HT_CAP_HT20_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + + if (ar_ht_cap & WMI_HT_CAP_HT40_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + + if (ar_ht_cap & WMI_HT_CAP_DYNAMIC_SMPS) { + u32 smps; + + smps = WLAN_HT_CAP_SM_PS_DYNAMIC; + smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; + + ht_cap.cap |= smps; + } + + if (ar_ht_cap & WMI_HT_CAP_TX_STBC) + ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + + if (ar_ht_cap & WMI_HT_CAP_RX_STBC) { + u32 stbc; + + stbc = ar_ht_cap; + stbc &= WMI_HT_CAP_RX_STBC; + stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT; + stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT; + stbc &= IEEE80211_HT_CAP_RX_STBC; + + ht_cap.cap |= stbc; + } + + if (ar_ht_cap & WMI_HT_CAP_RX_LDPC) + ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; + + if (ar_ht_cap & WMI_HT_CAP_L_SIG_TXOP_PROT) + ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT; + + if (ar_vht_cap & WMI_VHT_CAP_MAX_MPDU_LEN_MASK) + ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + for (i = 0; i < ar->num_rx_chains; i++) { + if (rate_cap_rx_chainmask & BIT(i)) + ht_cap.mcs.rx_mask[i] = 0xFF; + } + + ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; + + return ht_cap; +} + +static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif) +{ + u32 value = 0; + struct ath11k *ar = arvif->ar; + int nsts; + int sound_dim; + u32 vht_cap = ar->pdev->cap.vht_cap; + u32 vdev_param = WMI_VDEV_PARAM_TXBF; + + if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) { + nsts = vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; + value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); + } + + if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)) { + sound_dim = vht_cap & + IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + if (sound_dim > (ar->num_tx_chains - 1)) + sound_dim = ar->num_tx_chains - 1; + value |= SM(sound_dim, WMI_BF_SOUND_DIM_OFFSET); + } + + if (!value) + return 0; + + if (vht_cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) { + value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER; + + if ((vht_cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) && + arvif->vdev_type == WMI_VDEV_TYPE_AP) + value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFER; + } + + /* TODO: SUBFEE not validated in HK, disable here until validated? */ + + if (vht_cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) { + value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE; + + if ((vht_cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) && + arvif->vdev_type == WMI_VDEV_TYPE_STA) + value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFEE; + } + + return ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, value); +} + +static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap) +{ + bool subfer, subfee; + int sound_dim = 0; + + subfer = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)); + subfee = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)); + + if (ar->num_tx_chains < 2) { + *vht_cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); + subfer = false; + } + + /* If SU Beaformer is not set, then disable MU Beamformer Capability */ + if (!subfer) + *vht_cap &= ~(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); + + /* If SU Beaformee is not set, then disable MU Beamformee Capability */ + if (!subfee) + *vht_cap &= ~(IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + + sound_dim = (*vht_cap & IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK); + sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + *vht_cap &= ~IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + + /* TODO: Need to check invalid STS and Sound_dim values set by FW? */ + + /* Enable Sounding Dimension Field only if SU BF is enabled */ + if (subfer) { + if (sound_dim > (ar->num_tx_chains - 1)) + sound_dim = ar->num_tx_chains - 1; + + sound_dim <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + sound_dim &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + *vht_cap |= sound_dim; + } + + /* Use the STS advertised by FW unless SU Beamformee is not supported*/ + if (!subfee) + *vht_cap &= ~(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK); +} + +static struct ieee80211_sta_vht_cap +ath11k_create_vht_cap(struct ath11k *ar, u32 rate_cap_tx_chainmask, + u32 rate_cap_rx_chainmask) +{ + struct ieee80211_sta_vht_cap vht_cap = {0}; + u16 txmcs_map, rxmcs_map; + int i; + + vht_cap.vht_supported = 1; + vht_cap.cap = ar->pdev->cap.vht_cap; + + ath11k_set_vht_txbf_cap(ar, &vht_cap.cap); + + /* TODO: Enable back VHT160 mode once association issues are fixed */ + /* Disabling VHT160 and VHT80+80 modes */ + vht_cap.cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; + + rxmcs_map = 0; + txmcs_map = 0; + for (i = 0; i < 8; i++) { + if (i < ar->num_tx_chains && rate_cap_tx_chainmask & BIT(i)) + txmcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2); + else + txmcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2); + + if (i < ar->num_rx_chains && rate_cap_rx_chainmask & BIT(i)) + rxmcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2); + else + rxmcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2); + } + + if (rate_cap_tx_chainmask <= 1) + vht_cap.cap &= ~IEEE80211_VHT_CAP_TXSTBC; + + vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_map); + vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_map); + + return vht_cap; +} + +static void ath11k_mac_setup_ht_vht_cap(struct ath11k *ar, + struct ath11k_pdev_cap *cap, + u32 *ht_cap_info) +{ + struct ieee80211_supported_band *band; + u32 rate_cap_tx_chainmask; + u32 rate_cap_rx_chainmask; + u32 ht_cap; + + rate_cap_tx_chainmask = ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift; + rate_cap_rx_chainmask = ar->cfg_rx_chainmask >> cap->rx_chain_mask_shift; + + if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) { + band = &ar->mac.sbands[NL80211_BAND_2GHZ]; + ht_cap = cap->band[NL80211_BAND_2GHZ].ht_cap_info; + if (ht_cap_info) + *ht_cap_info = ht_cap; + band->ht_cap = ath11k_create_ht_cap(ar, ht_cap, + rate_cap_rx_chainmask); + } + + if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) { + band = &ar->mac.sbands[NL80211_BAND_5GHZ]; + ht_cap = cap->band[NL80211_BAND_5GHZ].ht_cap_info; + if (ht_cap_info) + *ht_cap_info = ht_cap; + band->ht_cap = ath11k_create_ht_cap(ar, ht_cap, + rate_cap_rx_chainmask); + band->vht_cap = ath11k_create_vht_cap(ar, rate_cap_tx_chainmask, + rate_cap_rx_chainmask); + } +} + +static int ath11k_check_chain_mask(struct ath11k *ar, u32 ant, bool is_tx_ant) +{ + /* TODO: Check the request chainmask against the supported + * chainmask table which is advertised in extented_service_ready event + */ + + return 0; +} + +static void ath11k_gen_ppe_thresh(struct ath11k_ppe_threshold *fw_ppet, + u8 *he_ppet) +{ + int nss, ru; + u8 bit = 7; + + he_ppet[0] = fw_ppet->numss_m1 & IEEE80211_PPE_THRES_NSS_MASK; + he_ppet[0] |= (fw_ppet->ru_bit_mask << + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_POS) & + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK; + for (nss = 0; nss <= fw_ppet->numss_m1; nss++) { + for (ru = 0; ru < 4; ru++) { + u8 val; + int i; + + if ((fw_ppet->ru_bit_mask & BIT(ru)) == 0) + continue; + val = (fw_ppet->ppet16_ppet8_ru3_ru0[nss] >> (ru * 6)) & + 0x3f; + val = ((val >> 3) & 0x7) | ((val & 0x7) << 3); + for (i = 5; i >= 0; i--) { + he_ppet[bit / 8] |= + ((val >> i) & 0x1) << ((bit % 8)); + bit++; + } + } + } +} + +static void +ath11k_mac_filter_he_cap_mesh(struct ieee80211_he_cap_elem *he_cap_elem) +{ + u8 m; + + m = IEEE80211_HE_MAC_CAP0_TWT_RES | + IEEE80211_HE_MAC_CAP0_TWT_REQ; + he_cap_elem->mac_cap_info[0] &= ~m; + + m = IEEE80211_HE_MAC_CAP2_TRS | + IEEE80211_HE_MAC_CAP2_BCAST_TWT | + IEEE80211_HE_MAC_CAP2_MU_CASCADING; + he_cap_elem->mac_cap_info[2] &= ~m; + + m = IEEE80211_HE_MAC_CAP3_FLEX_TWT_SCHED | + IEEE80211_HE_MAC_CAP2_BCAST_TWT | + IEEE80211_HE_MAC_CAP2_MU_CASCADING; + he_cap_elem->mac_cap_info[3] &= ~m; + + m = IEEE80211_HE_MAC_CAP4_BSRP_BQRP_A_MPDU_AGG | + IEEE80211_HE_MAC_CAP4_BQR; + he_cap_elem->mac_cap_info[4] &= ~m; + + m = IEEE80211_HE_MAC_CAP5_SUBCHAN_SELECVITE_TRANSMISSION | + IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU | + IEEE80211_HE_MAC_CAP5_PUNCTURED_SOUNDING | + IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX; + he_cap_elem->mac_cap_info[5] &= ~m; + + m = IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO; + he_cap_elem->phy_cap_info[2] &= ~m; + + m = IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK; + he_cap_elem->phy_cap_info[3] &= ~m; + + m = IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + he_cap_elem->phy_cap_info[4] &= ~m; + + m = IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK; + he_cap_elem->phy_cap_info[5] &= ~m; + + m = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB | + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO; + he_cap_elem->phy_cap_info[6] &= ~m; + + m = IEEE80211_HE_PHY_CAP7_SRP_BASED_SR | + IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR | + IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ | + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; + he_cap_elem->phy_cap_info[7] &= ~m; + + m = IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI | + IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU; + he_cap_elem->phy_cap_info[8] &= ~m; + + m = IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB; + he_cap_elem->phy_cap_info[9] &= ~m; +} + +static int ath11k_mac_copy_he_cap(struct ath11k *ar, + struct ath11k_pdev_cap *cap, + struct ieee80211_sband_iftype_data *data, + int band) +{ + int i, idx = 0; + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + struct ieee80211_sta_he_cap *he_cap = &data[idx].he_cap; + struct ath11k_band_cap *band_cap = &cap->band[band]; + struct ieee80211_he_cap_elem *he_cap_elem = + &he_cap->he_cap_elem; + + switch (i) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + break; + + default: + continue; + } + + data[idx].types_mask = BIT(i); + he_cap->has_he = true; + memcpy(he_cap_elem->mac_cap_info, band_cap->he_cap_info, + sizeof(he_cap_elem->mac_cap_info)); + memcpy(he_cap_elem->phy_cap_info, band_cap->he_cap_phy_info, + sizeof(he_cap_elem->phy_cap_info)); + + he_cap_elem->mac_cap_info[1] |= + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_MASK; + he_cap_elem->phy_cap_info[4] &= + ~IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK; + he_cap_elem->phy_cap_info[4] &= + ~IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK; + he_cap_elem->phy_cap_info[4] |= (ar->num_tx_chains - 1) << 2; + + he_cap_elem->phy_cap_info[5] &= + ~IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK; + he_cap_elem->phy_cap_info[5] &= + ~IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; + he_cap_elem->phy_cap_info[5] |= ar->num_tx_chains - 1; + + switch (i) { + case NL80211_IFTYPE_AP: + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; + break; + case NL80211_IFTYPE_STATION: + he_cap_elem->mac_cap_info[0] &= + ~IEEE80211_HE_MAC_CAP0_TWT_RES; + he_cap_elem->mac_cap_info[0] |= + IEEE80211_HE_MAC_CAP0_TWT_REQ; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU; + break; + case NL80211_IFTYPE_MESH_POINT: + ath11k_mac_filter_he_cap_mesh(he_cap_elem); + break; + } + + he_cap->he_mcs_nss_supp.rx_mcs_80 = + cpu_to_le16(band_cap->he_mcs & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_80 = + cpu_to_le16(band_cap->he_mcs & 0xffff); + he_cap->he_mcs_nss_supp.rx_mcs_160 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_160 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + he_cap->he_mcs_nss_supp.rx_mcs_80p80 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_80p80 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + + memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); + if (he_cap_elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) + ath11k_gen_ppe_thresh(&band_cap->he_ppet, + he_cap->ppe_thres); + idx++; + } + + return idx; +} + +static void ath11k_mac_setup_he_cap(struct ath11k *ar, + struct ath11k_pdev_cap *cap) +{ + struct ieee80211_supported_band *band; + int count; + + if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) { + count = ath11k_mac_copy_he_cap(ar, cap, + ar->mac.iftype[NL80211_BAND_2GHZ], + NL80211_BAND_2GHZ); + band = &ar->mac.sbands[NL80211_BAND_2GHZ]; + band->iftype_data = ar->mac.iftype[NL80211_BAND_2GHZ]; + band->n_iftype_data = count; + } + + if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) { + count = ath11k_mac_copy_he_cap(ar, cap, + ar->mac.iftype[NL80211_BAND_5GHZ], + NL80211_BAND_5GHZ); + band = &ar->mac.sbands[NL80211_BAND_5GHZ]; + band->iftype_data = ar->mac.iftype[NL80211_BAND_5GHZ]; + band->n_iftype_data = count; + } +} + +static int __ath11k_set_antenna(struct ath11k *ar, u32 tx_ant, u32 rx_ant) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (ath11k_check_chain_mask(ar, tx_ant, true)) + return -EINVAL; + + if (ath11k_check_chain_mask(ar, rx_ant, false)) + return -EINVAL; + + ar->cfg_tx_chainmask = tx_ant; + ar->cfg_rx_chainmask = rx_ant; + + if (ar->state != ATH11K_STATE_ON && + ar->state != ATH11K_STATE_RESTARTED) + return 0; + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_TX_CHAIN_MASK, + tx_ant, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to set tx-chainmask: %d, req 0x%x\n", + ret, tx_ant); + return ret; + } + + ar->num_tx_chains = get_num_chains(tx_ant); + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_RX_CHAIN_MASK, + rx_ant, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to set rx-chainmask: %d, req 0x%x\n", + ret, rx_ant); + return ret; + } + + ar->num_rx_chains = get_num_chains(rx_ant); + + /* Reload HT/VHT/HE capability */ + ath11k_mac_setup_ht_vht_cap(ar, &ar->pdev->cap, NULL); + ath11k_mac_setup_he_cap(ar, &ar->pdev->cap); + + return 0; +} + +int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx) +{ + struct ath11k *ar = ctx; + struct ath11k_base *ab = ar->ab; + struct sk_buff *msdu = skb; + struct ieee80211_tx_info *info; + + spin_lock_bh(&ar->txmgmt_idr_lock); + idr_remove(&ar->txmgmt_idr, buf_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + dma_unmap_single(ab->dev, ATH11K_SKB_CB(msdu)->paddr, msdu->len, + DMA_TO_DEVICE); + + info = IEEE80211_SKB_CB(msdu); + memset(&info->status, 0, sizeof(info->status)); + + ieee80211_free_txskb(ar->hw, msdu); + + return 0; +} + +static int ath11k_mac_vif_txmgmt_idr_remove(int buf_id, void *skb, void *ctx) +{ + struct ieee80211_vif *vif = ctx; + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB((struct sk_buff *)skb); + struct sk_buff *msdu = skb; + struct ath11k *ar = skb_cb->ar; + struct ath11k_base *ab = ar->ab; + + if (skb_cb->vif == vif) { + spin_lock_bh(&ar->txmgmt_idr_lock); + idr_remove(&ar->txmgmt_idr, buf_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, + DMA_TO_DEVICE); + } + + return 0; +} + +static int ath11k_mac_mgmt_tx_wmi(struct ath11k *ar, struct ath11k_vif *arvif, + struct sk_buff *skb) +{ + struct ath11k_base *ab = ar->ab; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + dma_addr_t paddr; + int buf_id; + int ret; + + spin_lock_bh(&ar->txmgmt_idr_lock); + buf_id = idr_alloc(&ar->txmgmt_idr, skb, 0, + ATH11K_TX_MGMT_NUM_PENDING_MAX, GFP_ATOMIC); + spin_unlock_bh(&ar->txmgmt_idr_lock); + if (buf_id < 0) + return -ENOSPC; + + if ((ieee80211_is_action(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) && + ieee80211_has_protected(hdr->frame_control)) { + skb_put(skb, IEEE80211_CCMP_MIC_LEN); + } + + paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(ab->dev, paddr)) { + ath11k_warn(ab, "failed to DMA map mgmt Tx buffer\n"); + ret = -EIO; + goto err_free_idr; + } + + ATH11K_SKB_CB(skb)->paddr = paddr; + + ret = ath11k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb); + if (ret) { + ath11k_warn(ar->ab, "failed to send mgmt frame: %d\n", ret); + goto err_unmap_buf; + } + + return 0; + +err_unmap_buf: + dma_unmap_single(ab->dev, ATH11K_SKB_CB(skb)->paddr, + skb->len, DMA_TO_DEVICE); +err_free_idr: + spin_lock_bh(&ar->txmgmt_idr_lock); + idr_remove(&ar->txmgmt_idr, buf_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + + return ret; +} + +static void ath11k_mgmt_over_wmi_tx_purge(struct ath11k *ar) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&ar->wmi_mgmt_tx_queue)) != NULL) + ieee80211_free_txskb(ar->hw, skb); +} + +static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, wmi_mgmt_tx_work); + struct ieee80211_tx_info *info; + struct ath11k_vif *arvif; + struct sk_buff *skb; + int ret; + + while ((skb = skb_dequeue(&ar->wmi_mgmt_tx_queue)) != NULL) { + info = IEEE80211_SKB_CB(skb); + arvif = ath11k_vif_to_arvif(info->control.vif); + + ret = ath11k_mac_mgmt_tx_wmi(ar, arvif, skb); + if (ret) { + ath11k_warn(ar->ab, "failed to transmit management frame %d\n", + ret); + ieee80211_free_txskb(ar->hw, skb); + } else { + atomic_inc(&ar->num_pending_mgmt_tx); + } + } +} + +static int ath11k_mac_mgmt_tx(struct ath11k *ar, struct sk_buff *skb, + bool is_prb_rsp) +{ + struct sk_buff_head *q = &ar->wmi_mgmt_tx_queue; + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) + return -ESHUTDOWN; + + /* Drop probe response packets when the pending management tx + * count has reached a certain threshold, so as to prioritize + * other mgmt packets like auth and assoc to be sent on time + * for establishing successful connections. + */ + if (is_prb_rsp && + atomic_read(&ar->num_pending_mgmt_tx) > ATH11K_PRB_RSP_DROP_THRESHOLD) { + ath11k_warn(ar->ab, + "dropping probe response as pending queue is almost full\n"); + return -ENOSPC; + } + + if (skb_queue_len(q) == ATH11K_TX_MGMT_NUM_PENDING_MAX) { + ath11k_warn(ar->ab, "mgmt tx queue is full\n"); + return -ENOSPC; + } + + skb_queue_tail(q, skb); + ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work); + + return 0; +} + +static void ath11k_mac_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ath11k *ar = hw->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + bool is_prb_rsp; + int ret; + + if (ieee80211_is_mgmt(hdr->frame_control)) { + is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control); + ret = ath11k_mac_mgmt_tx(ar, skb, is_prb_rsp); + if (ret) { + ath11k_warn(ar->ab, "failed to queue management frame %d\n", + ret); + ieee80211_free_txskb(ar->hw, skb); + } + return; + } + + ret = ath11k_dp_tx(ar, arvif, skb); + if (ret) { + ath11k_warn(ar->ab, "failed to transmit frame %d\n", ret); + ieee80211_free_txskb(ar->hw, skb); + } +} + +void ath11k_mac_drain_tx(struct ath11k *ar) +{ + /* make sure rcu-protected mac80211 tx path itself is drained */ + synchronize_net(); + + cancel_work_sync(&ar->wmi_mgmt_tx_work); + ath11k_mgmt_over_wmi_tx_purge(ar); +} + +static int ath11k_mac_config_mon_status_default(struct ath11k *ar, bool enable) +{ + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 ring_id; + + if (enable) + tlv_filter = ath11k_mac_mon_status_filter_default; + + ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; + + return ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); +} + +static int ath11k_mac_op_start(struct ieee80211_hw *hw) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_pdev *pdev = ar->pdev; + int ret; + + ath11k_mac_drain_tx(ar); + mutex_lock(&ar->conf_mutex); + + switch (ar->state) { + case ATH11K_STATE_OFF: + ar->state = ATH11K_STATE_ON; + break; + case ATH11K_STATE_RESTARTING: + ar->state = ATH11K_STATE_RESTARTED; + break; + case ATH11K_STATE_RESTARTED: + case ATH11K_STATE_WEDGED: + case ATH11K_STATE_ON: + WARN_ON(1); + ret = -EINVAL; + goto err; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, + 1, pdev->pdev_id); + + if (ret) { + ath11k_err(ar->ab, "failed to enable PMF QOS: (%d\n", ret); + goto err; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 1, + pdev->pdev_id); + if (ret) { + ath11k_err(ar->ab, "failed to enable dynamic bw: %d\n", ret); + goto err; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + 0, pdev->pdev_id); + if (ret) { + ath11k_err(ab, "failed to set ac override for ARP: %d\n", + ret); + goto err; + } + + ret = ath11k_wmi_send_dfs_phyerr_offload_enable_cmd(ar, pdev->pdev_id); + if (ret) { + ath11k_err(ab, "failed to offload radar detection: %d\n", + ret); + goto err; + } + + ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar, + HTT_PPDU_STATS_TAG_DEFAULT); + if (ret) { + ath11k_err(ab, "failed to req ppdu stats: %d\n", ret); + goto err; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_MESH_MCAST_ENABLE, + 1, pdev->pdev_id); + + if (ret) { + ath11k_err(ar->ab, "failed to enable MESH MCAST ENABLE: (%d\n", ret); + goto err; + } + + __ath11k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask); + + /* TODO: Do we need to enable ANI? */ + + ath11k_reg_update_chan_list(ar); + + ar->num_started_vdevs = 0; + ar->num_created_vdevs = 0; + ar->num_peers = 0; + + /* Configure monitor status ring with default rx_filter to get rx status + * such as rssi, rx_duration. + */ + ret = ath11k_mac_config_mon_status_default(ar, true); + if (ret) { + ath11k_err(ab, "failed to configure monitor status ring with default rx_filter: (%d)\n", + ret); + goto err; + } + + mutex_unlock(&ar->conf_mutex); + + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], + &ab->pdevs[ar->pdev_idx]); + + return 0; + +err: + ar->state = ATH11K_STATE_OFF; + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static void ath11k_mac_op_stop(struct ieee80211_hw *hw) +{ + struct ath11k *ar = hw->priv; + struct htt_ppdu_stats_info *ppdu_stats, *tmp; + int ret; + + ath11k_mac_drain_tx(ar); + + mutex_lock(&ar->conf_mutex); + ret = ath11k_mac_config_mon_status_default(ar, false); + if (ret) + ath11k_err(ar->ab, "failed to clear rx_filter for monitor status ring: (%d)\n", + ret); + + clear_bit(ATH11K_CAC_RUNNING, &ar->dev_flags); + ar->state = ATH11K_STATE_OFF; + mutex_unlock(&ar->conf_mutex); + + cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->regd_update_work); + + spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { + list_del(&ppdu_stats->list); + kfree(ppdu_stats); + } + spin_unlock_bh(&ar->data_lock); + + rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL); + + synchronize_rcu(); + + atomic_set(&ar->num_pending_mgmt_tx, 0); +} + +static void +ath11k_mac_setup_vdev_create_params(struct ath11k_vif *arvif, + struct vdev_create_params *params) +{ + struct ath11k *ar = arvif->ar; + struct ath11k_pdev *pdev = ar->pdev; + + params->if_id = arvif->vdev_id; + params->type = arvif->vdev_type; + params->subtype = arvif->vdev_subtype; + params->pdev_id = pdev->pdev_id; + + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) { + params->chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains; + params->chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains; + } + if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) { + params->chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains; + params->chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains; + } +} + +static u32 +ath11k_mac_prepare_he_mode(struct ath11k_pdev *pdev, u32 viftype) +{ + struct ath11k_pdev_cap *pdev_cap = &pdev->cap; + struct ath11k_band_cap *cap_band = NULL; + u32 *hecap_phy_ptr = NULL; + u32 hemode = 0; + + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) + cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; + else + cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; + + hecap_phy_ptr = &cap_band->he_cap_phy_info[0]; + + hemode = FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE) | + FIELD_PREP(HE_MODE_SU_TX_BFER, HECAP_PHY_SUBFMR_GET(hecap_phy_ptr)) | + FIELD_PREP(HE_MODE_UL_MUMIMO, HECAP_PHY_ULMUMIMO_GET(hecap_phy_ptr)); + + /* TODO WDS and other modes */ + if (viftype == NL80211_IFTYPE_AP) { + hemode |= FIELD_PREP(HE_MODE_MU_TX_BFER, + HECAP_PHY_MUBFMR_GET(hecap_phy_ptr)) | + FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | + FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); + } else { + hemode |= FIELD_PREP(HE_MODE_MU_TX_BFEE, HE_MU_BFEE_ENABLE); + } + + return hemode; +} + +static int ath11k_set_he_mu_sounding_mode(struct ath11k *ar, + struct ath11k_vif *arvif) +{ + u32 param_id, param_value; + struct ath11k_base *ab = ar->ab; + int ret = 0; + + param_id = WMI_VDEV_PARAM_SET_HEMU_MODE; + param_value = ath11k_mac_prepare_he_mode(ar->pdev, arvif->vif->type); + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d param_value %x\n", + arvif->vdev_id, ret, param_value); + return ret; + } + param_id = WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE; + param_value = + FIELD_PREP(HE_VHT_SOUNDING_MODE, HE_VHT_SOUNDING_MODE_ENABLE) | + FIELD_PREP(HE_TRIG_NONTRIG_SOUNDING_MODE, + HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE); + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d\n", + arvif->vdev_id, ret); + return ret; + } + return ret; +} + +static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct vdev_create_params vdev_param = {0}; + struct peer_create_params peer_param; + u32 param_id, param_value; + u16 nss; + int i; + int ret; + int bit; + + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + + mutex_lock(&ar->conf_mutex); + + if (vif->type == NL80211_IFTYPE_AP && + ar->num_peers > (ar->max_num_peers - 1)) { + ath11k_warn(ab, "failed to create vdev due to insufficient peer entry resource in firmware\n"); + ret = -ENOBUFS; + goto err; + } + + if (ar->num_created_vdevs > (TARGET_NUM_VDEVS - 1)) { + ath11k_warn(ab, "failed to create vdev, reached max vdev limit %d\n", + TARGET_NUM_VDEVS); + ret = -EBUSY; + goto err; + } + + memset(arvif, 0, sizeof(*arvif)); + + arvif->ar = ar; + arvif->vif = vif; + + INIT_LIST_HEAD(&arvif->list); + + /* Should we initialize any worker to handle connection loss indication + * from firmware in sta mode? + */ + + for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { + arvif->bitrate_mask.control[i].legacy = 0xffffffff; + memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].ht_mcs)); + memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].vht_mcs)); + } + + bit = __ffs64(ab->free_vdev_map); + + arvif->vdev_id = bit; + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; + + switch (vif->type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + arvif->vdev_type = WMI_VDEV_TYPE_STA; + break; + case NL80211_IFTYPE_MESH_POINT: + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH_11S; + /* fall through */ + case NL80211_IFTYPE_AP: + arvif->vdev_type = WMI_VDEV_TYPE_AP; + break; + case NL80211_IFTYPE_MONITOR: + arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; + break; + default: + WARN_ON(1); + break; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac add interface id %d type %d subtype %d map %llx\n", + arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype, + ab->free_vdev_map); + + vif->cab_queue = arvif->vdev_id % (ATH11K_HW_MAX_QUEUES - 1); + for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++) + vif->hw_queue[i] = i % (ATH11K_HW_MAX_QUEUES - 1); + + ath11k_mac_setup_vdev_create_params(arvif, &vdev_param); + + ret = ath11k_wmi_vdev_create(ar, vif->addr, &vdev_param); + if (ret) { + ath11k_warn(ab, "failed to create WMI vdev %d: %d\n", + arvif->vdev_id, ret); + goto err; + } + + ar->num_created_vdevs++; + + 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); + + param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE; + param_value = ATH11K_HW_TXRX_NATIVE_WIFI; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ab, "failed to set vdev %d tx encap mode: %d\n", + arvif->vdev_id, ret); + goto err_vdev_del; + } + + nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_NSS, nss); + if (ret) { + ath11k_warn(ab, "failed to set vdev %d chainmask 0x%x, nss %d :%d\n", + arvif->vdev_id, ar->cfg_tx_chainmask, nss, ret); + goto err_vdev_del; + } + + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_AP: + peer_param.vdev_id = arvif->vdev_id; + peer_param.peer_addr = vif->addr; + peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; + ret = ath11k_peer_create(ar, arvif, NULL, &peer_param); + if (ret) { + ath11k_warn(ab, "failed to vdev %d create peer for AP: %d\n", + arvif->vdev_id, ret); + goto err_vdev_del; + } + + ret = ath11k_mac_set_kickout(arvif); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %i kickout parameters: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + break; + case WMI_VDEV_TYPE_STA: + param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY; + param_value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d RX wake policy: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + + param_id = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; + param_value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d TX wake threshold: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + + param_id = WMI_STA_PS_PARAM_PSPOLL_COUNT; + param_value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d pspoll count: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + + ret = ath11k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, false); + if (ret) { + ath11k_warn(ar->ab, "failed to disable vdev %d ps mode: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + break; + default: + break; + } + + arvif->txpower = vif->bss_conf.txpower; + ret = ath11k_mac_txpower_recalc(ar); + if (ret) + goto err_peer_del; + + param_id = WMI_VDEV_PARAM_RTS_THRESHOLD; + param_value = ar->hw->wiphy->rts_threshold; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ar->ab, "failed to set rts threshold for vdev %d: %d\n", + arvif->vdev_id, ret); + } + + ath11k_dp_vdev_tx_attach(ar, arvif); + + mutex_unlock(&ar->conf_mutex); + + return 0; + +err_peer_del: + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ar->num_peers--; + ath11k_wmi_send_peer_delete_cmd(ar, vif->addr, arvif->vdev_id); + } + +err_vdev_del: + ath11k_wmi_vdev_delete(ar, arvif->vdev_id); + ar->num_created_vdevs--; + ab->free_vdev_map |= 1LL << arvif->vdev_id; + spin_lock_bh(&ar->data_lock); + list_del(&arvif->list); + spin_unlock_bh(&ar->data_lock); + +err: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static int ath11k_mac_vif_unref(int buf_id, void *skb, void *ctx) +{ + struct ieee80211_vif *vif = (struct ieee80211_vif *)ctx; + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB((struct sk_buff *)skb); + + if (skb_cb->vif == vif) + skb_cb->vif = NULL; + + return 0; +} + +static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_base *ab = ar->ab; + int ret; + int i; + + mutex_lock(&ar->conf_mutex); + + 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); + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath11k_peer_delete(ar, arvif->vdev_id, vif->addr); + if (ret) + ath11k_warn(ab, "failed to submit AP self-peer removal on vdev %d: %d\n", + arvif->vdev_id, ret); + } + + ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id); + if (ret) + ath11k_warn(ab, "failed to delete WMI vdev %d: %d\n", + arvif->vdev_id, ret); + + ar->num_created_vdevs--; + + ath11k_peer_cleanup(ar, arvif->vdev_id); + + idr_for_each(&ar->txmgmt_idr, + ath11k_mac_vif_txmgmt_idr_remove, vif); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + spin_lock_bh(&ab->dp.tx_ring[i].tx_idr_lock); + idr_for_each(&ab->dp.tx_ring[i].txbuf_idr, + ath11k_mac_vif_unref, vif); + spin_unlock_bh(&ab->dp.tx_ring[i].tx_idr_lock); + } + + /* Recalc txpower for remaining vdev */ + ath11k_mac_txpower_recalc(ar); + clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + + /* TODO: recal traffic pause state based on the available vdevs */ + + mutex_unlock(&ar->conf_mutex); +} + +/* FIXME: Has to be verified. */ +#define SUPPORTED_FILTERS \ + (FIF_ALLMULTI | \ + FIF_CONTROL | \ + FIF_PSPOLL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_PROBE_REQ | \ + FIF_FCSFAIL) + +static void ath11k_mac_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct ath11k *ar = hw->priv; + bool reset_flag = false; + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + ar->filter_flags = *total_flags; + + /* For monitor mode */ + reset_flag = !(ar->filter_flags & FIF_BCN_PRBRESP_PROMISC); + + ret = ath11k_dp_tx_htt_monitor_mode_ring_config(ar, reset_flag); + if (!ret) { + if (!reset_flag) + set_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + else + clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + } else { + ath11k_warn(ar->ab, + "fail to set monitor filter: %d\n", ret); + } + mutex_unlock(&ar->conf_mutex); +} + +static int ath11k_mac_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) +{ + struct ath11k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + *tx_ant = ar->cfg_tx_chainmask; + *rx_ant = ar->cfg_rx_chainmask; + + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int ath11k_mac_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct ath11k *ar = hw->priv; + int ret; + + mutex_lock(&ar->conf_mutex); + ret = __ath11k_set_antenna(ar, tx_ant, rx_ant); + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static int ath11k_mac_op_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + struct ath11k *ar = hw->priv; + int ret = -EINVAL; + + mutex_lock(&ar->conf_mutex); + + switch (params->action) { + case IEEE80211_AMPDU_RX_START: + ret = ath11k_dp_rx_ampdu_start(ar, params); + break; + case IEEE80211_AMPDU_RX_STOP: + ret = ath11k_dp_rx_ampdu_stop(ar, params); + break; + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_OPERATIONAL: + /* Tx A-MPDU aggregation offloaded to hw/fw so deny mac80211 + * Tx aggregation requests. + */ + ret = -EOPNOTSUPP; + break; + } + + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static int ath11k_mac_op_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx add freq %hu width %d ptr %pK\n", + ctx->def.chan->center_freq, ctx->def.width, ctx); + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + /* TODO: In case of multiple channel context, populate rx_channel from + * Rx PPDU desc information. + */ + ar->rx_channel = ctx->def.chan; + spin_unlock_bh(&ar->data_lock); + + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static void ath11k_mac_op_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx remove freq %hu width %d ptr %pK\n", + ctx->def.chan->center_freq, ctx->def.width, ctx); + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + /* TODO: In case of there is one more channel context left, populate + * rx_channel with the channel of that remaining channel context. + */ + ar->rx_channel = NULL; + spin_unlock_bh(&ar->data_lock); + + mutex_unlock(&ar->conf_mutex); +} + +static inline int ath11k_mac_vdev_setup_sync(struct ath11k *ar) +{ + lockdep_assert_held(&ar->conf_mutex); + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) + return -ESHUTDOWN; + + if (!wait_for_completion_timeout(&ar->vdev_setup_done, + ATH11K_VDEV_SETUP_TIMEOUT_HZ)) + return -ETIMEDOUT; + + return ar->last_wmi_vdev_start_status ? -EINVAL : 0; +} + +static int +ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, + const struct cfg80211_chan_def *chandef, + bool restart) +{ + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct wmi_vdev_start_req_arg arg = {}; + int he_support = arvif->vif->bss_conf.he_support; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->vdev_setup_done); + + arg.vdev_id = arvif->vdev_id; + arg.dtim_period = arvif->dtim_period; + arg.bcn_intval = arvif->beacon_interval; + + arg.channel.freq = chandef->chan->center_freq; + arg.channel.band_center_freq1 = chandef->center_freq1; + arg.channel.band_center_freq2 = chandef->center_freq2; + arg.channel.mode = + ath11k_phymodes[chandef->chan->band][chandef->width]; + + arg.channel.min_power = 0; + arg.channel.max_power = chandef->chan->max_power * 2; + arg.channel.max_reg_power = chandef->chan->max_reg_power * 2; + arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2; + + arg.pref_tx_streams = ar->num_tx_chains; + arg.pref_rx_streams = ar->num_rx_chains; + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + arg.ssid = arvif->u.ap.ssid; + arg.ssid_len = arvif->u.ap.ssid_len; + arg.hidden_ssid = arvif->u.ap.hidden_ssid; + + /* For now allow DFS for AP mode */ + arg.channel.chan_radar = + !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); + + arg.channel.passive = arg.channel.chan_radar; + + spin_lock_bh(&ab->base_lock); + arg.regdomain = ar->ab->dfs_region; + spin_unlock_bh(&ab->base_lock); + + /* TODO: Notify if secondary 80Mhz also needs radar detection */ + if (he_support) { + ret = ath11k_set_he_mu_sounding_mode(ar, arvif); + if (ret) { + ath11k_warn(ar->ab, "failed to set he mode vdev %i\n", + arg.vdev_id); + return ret; + } + } + } + + arg.channel.passive |= !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR); + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac vdev %d start center_freq %d phymode %s\n", + arg.vdev_id, arg.channel.freq, + ath11k_wmi_phymode_str(arg.channel.mode)); + + ret = ath11k_wmi_vdev_start(ar, &arg, restart); + if (ret) { + ath11k_warn(ar->ab, "failed to %s WMI vdev %i\n", + restart ? "restart" : "start", arg.vdev_id); + return ret; + } + + ret = ath11k_mac_vdev_setup_sync(ar); + if (ret) { + ath11k_warn(ab, "failed to synchronize setup for vdev %i %s: %d\n", + arg.vdev_id, restart ? "restart" : "start", ret); + return ret; + } + + ar->num_started_vdevs++; + + /* Enable CAC Flag in the driver by checking the channel DFS cac time, + * i.e dfs_cac_ms value which will be valid only for radar channels + * and state as NL80211_DFS_USABLE which indicates CAC needs to be + * done before channel usage. This flags is used to drop rx packets. + * during CAC. + */ + /* TODO Set the flag for other interface types as required */ + if (arvif->vdev_type == WMI_VDEV_TYPE_AP && + chandef->chan->dfs_cac_ms && + chandef->chan->dfs_state == NL80211_DFS_USABLE) { + set_bit(ATH11K_CAC_RUNNING, &ar->dev_flags); + ath11k_dbg(ab, ATH11K_DBG_MAC, + "CAC Started in chan_freq %d for vdev %d\n", + arg.channel.freq, arg.vdev_id); + } + + ret = ath11k_mac_set_txbf_conf(arvif); + if (ret) + ath11k_warn(ab, "failed to set txbf conf for vdev %d: %d\n", + arvif->vdev_id, ret); + + return 0; +} + +static int ath11k_mac_vdev_stop(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->vdev_setup_done); + + spin_lock_bh(&ar->data_lock); + + ar->vdev_stop_status.stop_in_progress = true; + ar->vdev_stop_status.vdev_id = arvif->vdev_id; + + spin_unlock_bh(&ar->data_lock); + + ret = ath11k_wmi_vdev_stop(ar, arvif->vdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to stop WMI vdev %i: %d\n", + arvif->vdev_id, ret); + goto err; + } + + ret = ath11k_mac_vdev_setup_sync(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to synchronize setup for vdev %i: %d\n", + arvif->vdev_id, ret); + goto err; + } + + WARN_ON(ar->num_started_vdevs == 0); + + ar->num_started_vdevs--; + + if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { + clear_bit(ATH11K_CAC_RUNNING, &ar->dev_flags); + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "CAC Stopped for vdev %d\n", + arvif->vdev_id); + } + + return 0; +err: + spin_lock_bh(&ar->data_lock); + ar->vdev_stop_status.stop_in_progress = false; + spin_unlock_bh(&ar->data_lock); + + return ret; +} + +static int ath11k_mac_vdev_start(struct ath11k_vif *arvif, + const struct cfg80211_chan_def *chandef) +{ + return ath11k_mac_vdev_start_restart(arvif, chandef, false); +} + +static int ath11k_mac_vdev_restart(struct ath11k_vif *arvif, + const struct cfg80211_chan_def *chandef) +{ + return ath11k_mac_vdev_start_restart(arvif, chandef, true); +} + +struct ath11k_mac_change_chanctx_arg { + struct ieee80211_chanctx_conf *ctx; + struct ieee80211_vif_chanctx_switch *vifs; + int n_vifs; + int next_vif; +}; + +static void +ath11k_mac_change_chanctx_cnt_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath11k_mac_change_chanctx_arg *arg = data; + + if (rcu_access_pointer(vif->chanctx_conf) != arg->ctx) + return; + + arg->n_vifs++; +} + +static void +ath11k_mac_change_chanctx_fill_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath11k_mac_change_chanctx_arg *arg = data; + struct ieee80211_chanctx_conf *ctx; + + ctx = rcu_access_pointer(vif->chanctx_conf); + if (ctx != arg->ctx) + return; + + if (WARN_ON(arg->next_vif == arg->n_vifs)) + return; + + arg->vifs[arg->next_vif].vif = vif; + arg->vifs[arg->next_vif].old_ctx = ctx; + arg->vifs[arg->next_vif].new_ctx = ctx; + arg->next_vif++; +} + +static void +ath11k_mac_update_vif_chan(struct ath11k *ar, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + for (i = 0; i < n_vifs; i++) { + arvif = (void *)vifs[i].vif->drv_priv; + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n", + arvif->vdev_id, + vifs[i].old_ctx->def.chan->center_freq, + vifs[i].new_ctx->def.chan->center_freq, + vifs[i].old_ctx->def.width, + vifs[i].new_ctx->def.width); + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) { + ath11k_warn(ab, "failed to down vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + /* All relevant vdevs are downed and associated channel resources + * should be available for the channel switch now. + */ + + /* TODO: Update ar->rx_channel */ + + for (i = 0; i < n_vifs; i++) { + arvif = (void *)vifs[i].vif->drv_priv; + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath11k_mac_setup_bcn_tmpl(arvif); + if (ret) + ath11k_warn(ab, "failed to update bcn tmpl during csa: %d\n", + ret); + + ret = ath11k_mac_vdev_restart(arvif, &vifs[i].new_ctx->def); + if (ret) { + ath11k_warn(ab, "failed to restart vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + + ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); + if (ret) { + ath11k_warn(ab, "failed to bring vdev up %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } +} + +static void +ath11k_mac_update_active_vif_chan(struct ath11k *ar, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k_mac_change_chanctx_arg arg = { .ctx = ctx }; + + lockdep_assert_held(&ar->conf_mutex); + + ieee80211_iterate_active_interfaces_atomic(ar->hw, + IEEE80211_IFACE_ITER_NORMAL, + ath11k_mac_change_chanctx_cnt_iter, + &arg); + if (arg.n_vifs == 0) + return; + + arg.vifs = kcalloc(arg.n_vifs, sizeof(arg.vifs[0]), GFP_KERNEL); + if (!arg.vifs) + return; + + ieee80211_iterate_active_interfaces_atomic(ar->hw, + IEEE80211_IFACE_ITER_NORMAL, + ath11k_mac_change_chanctx_fill_iter, + &arg); + + ath11k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs); + + kfree(arg.vifs); +} + +static void ath11k_mac_op_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + + mutex_lock(&ar->conf_mutex); + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx change freq %hu width %d ptr %pK changed %x\n", + ctx->def.chan->center_freq, ctx->def.width, ctx, changed); + + /* This shouldn't really happen because channel switching should use + * switch_vif_chanctx(). + */ + if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) + goto unlock; + + if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) + ath11k_mac_update_active_vif_chan(ar, ctx); + + /* TODO: Recalc radar detection */ + +unlock: + mutex_unlock(&ar->conf_mutex); +} + +static int +ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret; + + mutex_lock(&ar->conf_mutex); + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx assign ptr %pK vdev_id %i\n", + ctx, arvif->vdev_id); + + if (WARN_ON(arvif->is_started)) { + mutex_unlock(&ar->conf_mutex); + return -EBUSY; + } + + ret = ath11k_mac_vdev_start(arvif, &ctx->def); + if (ret) { + ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", + arvif->vdev_id, vif->addr, + ctx->def.chan->center_freq, ret); + goto err; + } + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ret = ath11k_monitor_vdev_up(ar, arvif->vdev_id); + if (ret) + goto err; + } + + arvif->is_started = true; + + /* TODO: Setup ps and cts/rts protection */ + + mutex_unlock(&ar->conf_mutex); + + return 0; + +err: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static void +ath11k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret; + + mutex_lock(&ar->conf_mutex); + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx unassign ptr %pK vdev_id %i\n", + ctx, arvif->vdev_id); + + WARN_ON(!arvif->is_started); + + ret = ath11k_mac_vdev_stop(arvif); + if (ret) + ath11k_warn(ab, "failed to stop vdev %i: %d\n", + arvif->vdev_id, ret); + + arvif->is_started = false; + + mutex_unlock(&ar->conf_mutex); +} + +static int +ath11k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + struct ath11k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac chanctx switch n_vifs %d mode %d\n", + n_vifs, mode); + ath11k_mac_update_vif_chan(ar, vifs, n_vifs); + + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int +ath11k_set_vdev_param_to_all_vifs(struct ath11k *ar, int param, u32 value) +{ + struct ath11k_vif *arvif; + int ret = 0; + + mutex_lock(&ar->conf_mutex); + list_for_each_entry(arvif, &ar->arvifs, list) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "setting mac vdev %d param %d value %d\n", + param, arvif->vdev_id, value); + + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param, value); + if (ret) { + ath11k_warn(ar->ab, "failed to set param %d for vdev %d: %d\n", + param, arvif->vdev_id, ret); + break; + } + } + mutex_unlock(&ar->conf_mutex); + return ret; +} + +/* mac80211 stores device specific RTS/Fragmentation threshold value, + * this is set interface specific to firmware from ath11k driver + */ +static int ath11k_mac_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct ath11k *ar = hw->priv; + int param_id = WMI_VDEV_PARAM_RTS_THRESHOLD; + + return ath11k_set_vdev_param_to_all_vifs(ar, param_id, value); +} + +static int ath11k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) +{ + /* Even though there's a WMI vdev param for fragmentation threshold no + * known firmware actually implements it. Moreover it is not possible to + * rely frame fragmentation to mac80211 because firmware clears the + * "more fragments" bit in frame control making it impossible for remote + * devices to reassemble frames. + * + * Hence implement a dummy callback just to say fragmentation isn't + * supported. This effectively prevents mac80211 from doing frame + * fragmentation in software. + */ + return -EOPNOTSUPP; +} + +static void ath11k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct ath11k *ar = hw->priv; + long time_left; + + if (drop) + return; + + time_left = wait_event_timeout(ar->dp.tx_empty_waitq, + (atomic_read(&ar->dp.num_tx_pending) == 0), + ATH11K_FLUSH_TIMEOUT); + if (time_left == 0) + ath11k_warn(ar->ab, "failed to flush transmit queue %ld\n", time_left); +} + +static int +ath11k_mac_bitrate_mask_num_ht_rates(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + int num_rates = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(mask->control[band].ht_mcs); i++) + num_rates += hweight16(mask->control[band].ht_mcs[i]); + + return num_rates; +} + +static bool +ath11k_mac_has_single_legacy_rate(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + int num_rates = 0; + + num_rates = hweight32(mask->control[band].legacy); + + if (ath11k_mac_bitrate_mask_num_ht_rates(ar, band, mask)) + return false; + + if (ath11k_mac_bitrate_mask_num_vht_rates(ar, band, mask)) + return false; + + return num_rates == 1; +} + +static bool +ath11k_mac_bitrate_mask_get_single_nss(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask, + int *nss) +{ + struct ieee80211_supported_band *sband = &ar->mac.sbands[band]; + u16 vht_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + u8 ht_nss_mask = 0; + u8 vht_nss_mask = 0; + int i; + + /* No need to consider legacy here. Basic rates are always present + * in bitrate mask + */ + + for (i = 0; i < ARRAY_SIZE(mask->control[band].ht_mcs); i++) { + if (mask->control[band].ht_mcs[i] == 0) + continue; + else if (mask->control[band].ht_mcs[i] == + sband->ht_cap.mcs.rx_mask[i]) + ht_nss_mask |= BIT(i); + else + return false; + } + + for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) { + if (mask->control[band].vht_mcs[i] == 0) + continue; + else if (mask->control[band].vht_mcs[i] == + ath11k_mac_get_max_vht_mcs_map(vht_mcs_map, i)) + vht_nss_mask |= BIT(i); + else + return false; + } + + if (ht_nss_mask != vht_nss_mask) + return false; + + if (ht_nss_mask == 0) + return false; + + if (BIT(fls(ht_nss_mask)) - 1 != ht_nss_mask) + return false; + + *nss = fls(ht_nss_mask); + + return true; +} + +static int +ath11k_mac_get_single_legacy_rate(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask, + u32 *rate, u8 *nss) +{ + int rate_idx; + u16 bitrate; + u8 preamble; + u8 hw_rate; + + if (hweight32(mask->control[band].legacy) != 1) + return -EINVAL; + + rate_idx = ffs(mask->control[band].legacy) - 1; + + if (band == NL80211_BAND_5GHZ) + rate_idx += ATH11K_MAC_FIRST_OFDM_RATE_IDX; + + hw_rate = ath11k_legacy_rates[rate_idx].hw_value; + bitrate = ath11k_legacy_rates[rate_idx].bitrate; + + if (ath11k_mac_bitrate_is_cck(bitrate)) + preamble = WMI_RATE_PREAMBLE_CCK; + else + preamble = WMI_RATE_PREAMBLE_OFDM; + + *nss = 1; + *rate = ATH11K_HW_RATE_CODE(hw_rate, 0, preamble); + + return 0; +} + +static int ath11k_mac_set_fixed_rate_params(struct ath11k_vif *arvif, + u32 rate, u8 nss, u8 sgi, u8 ldpc) +{ + struct ath11k *ar = arvif->ar; + u32 vdev_param; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac set fixed rate params vdev %i rate 0x%02hhx nss %hhu sgi %hhu\n", + arvif->vdev_id, rate, nss, sgi); + + vdev_param = WMI_VDEV_PARAM_FIXED_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, rate); + if (ret) { + ath11k_warn(ar->ab, "failed to set fixed rate param 0x%02x: %d\n", + rate, ret); + return ret; + } + + vdev_param = WMI_VDEV_PARAM_NSS; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, nss); + if (ret) { + ath11k_warn(ar->ab, "failed to set nss param %d: %d\n", + nss, ret); + return ret; + } + + vdev_param = WMI_VDEV_PARAM_SGI; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, sgi); + if (ret) { + ath11k_warn(ar->ab, "failed to set sgi param %d: %d\n", + sgi, ret); + return ret; + } + + vdev_param = WMI_VDEV_PARAM_LDPC; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, ldpc); + if (ret) { + ath11k_warn(ar->ab, "failed to set ldpc param %d: %d\n", + ldpc, ret); + return ret; + } + + return 0; +} + +static bool +ath11k_mac_vht_mcs_range_present(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + int i; + u16 vht_mcs; + + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { + vht_mcs = mask->control[band].vht_mcs[i]; + + switch (vht_mcs) { + case 0: + case BIT(8) - 1: + case BIT(9) - 1: + case BIT(10) - 1: + break; + default: + return false; + } + } + + return true; +} + +static void ath11k_mac_set_bitrate_mask_iter(void *data, + struct ieee80211_sta *sta) +{ + struct ath11k_vif *arvif = data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arvif->ar; + + spin_lock_bh(&ar->data_lock); + arsta->changed |= IEEE80211_RC_SUPP_RATES_CHANGED; + spin_unlock_bh(&ar->data_lock); + + ieee80211_queue_work(ar->hw, &arsta->update_wk); +} + +static void ath11k_mac_disable_peer_fixed_rate(void *data, + struct ieee80211_sta *sta) +{ + struct ath11k_vif *arvif = data; + struct ath11k *ar = arvif->ar; + int ret; + + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_PARAM_FIXED_RATE, + WMI_FIXED_RATE_NONE); + if (ret) + ath11k_warn(ar->ab, + "failed to disable peer fixed rate for STA %pM ret %d\n", + sta->addr, ret); +} + +static int +ath11k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + struct ath11k *ar = arvif->ar; + enum nl80211_band band; + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; + u32 rate; + u8 nss; + u8 sgi; + u8 ldpc; + int single_nss; + int ret; + int num_rates; + + if (ath11k_mac_vif_chan(vif, &def)) + return -EPERM; + + band = def.chan->band; + ht_mcs_mask = mask->control[band].ht_mcs; + vht_mcs_mask = mask->control[band].vht_mcs; + ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC); + + sgi = mask->control[band].gi; + if (sgi == NL80211_TXRATE_FORCE_LGI) + return -EINVAL; + + /* mac80211 doesn't support sending a fixed HT/VHT MCS alone, rather it + * requires passing atleast one of used basic rates along with them. + * Fixed rate setting across different preambles(legacy, HT, VHT) is + * not supported by the FW. Hence use of FIXED_RATE vdev param is not + * suitable for setting single HT/VHT rates. + * But, there could be a single basic rate passed from userspace which + * can be done through the FIXED_RATE param. + */ + if (ath11k_mac_has_single_legacy_rate(ar, band, mask)) { + ret = ath11k_mac_get_single_legacy_rate(ar, band, mask, &rate, + &nss); + if (ret) { + ath11k_warn(ar->ab, "failed to get single legacy rate for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + ieee80211_iterate_stations_atomic(ar->hw, + ath11k_mac_disable_peer_fixed_rate, + arvif); + } else if (ath11k_mac_bitrate_mask_get_single_nss(ar, band, mask, + &single_nss)) { + rate = WMI_FIXED_RATE_NONE; + nss = single_nss; + } else { + rate = WMI_FIXED_RATE_NONE; + nss = min_t(u32, ar->num_tx_chains, + max(ath11k_mac_max_ht_nss(ht_mcs_mask), + ath11k_mac_max_vht_nss(vht_mcs_mask))); + + /* If multiple rates across different preambles are given + * we can reconfigure this info with all peers using PEER_ASSOC + * command with the below exception cases. + * - Single VHT Rate : peer_assoc command accommodates only MCS + * range values i.e 0-7, 0-8, 0-9 for VHT. Though mac80211 + * mandates passing basic rates along with HT/VHT rates, FW + * doesn't allow switching from VHT to Legacy. Hence instead of + * setting legacy and VHT rates using RATEMASK_CMD vdev cmd, + * we could set this VHT rate as peer fixed rate param, which + * will override FIXED rate and FW rate control algorithm. + * If single VHT rate is passed along with HT rates, we select + * the VHT rate as fixed rate for vht peers. + * - Multiple VHT Rates : When Multiple VHT rates are given,this + * can be set using RATEMASK CMD which uses FW rate-ctl alg. + * TODO: Setting multiple VHT MCS and replacing peer_assoc with + * RATEMASK_CMDID can cover all use cases of setting rates + * across multiple preambles and rates within same type. + * But requires more validation of the command at this point. + */ + + num_rates = ath11k_mac_bitrate_mask_num_vht_rates(ar, band, + mask); + + if (!ath11k_mac_vht_mcs_range_present(ar, band, mask) && + num_rates > 1) { + /* TODO: Handle multiple VHT MCS values setting using + * RATEMASK CMD + */ + ath11k_warn(ar->ab, + "Setting more than one MCS Value in bitrate mask not supported\n"); + return -EINVAL; + } + + ieee80211_iterate_stations_atomic(ar->hw, + ath11k_mac_disable_peer_fixed_rate, + arvif); + + mutex_lock(&ar->conf_mutex); + + arvif->bitrate_mask = *mask; + ieee80211_iterate_stations_atomic(ar->hw, + ath11k_mac_set_bitrate_mask_iter, + arvif); + + mutex_unlock(&ar->conf_mutex); + } + + mutex_lock(&ar->conf_mutex); + + ret = ath11k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc); + if (ret) { + ath11k_warn(ar->ab, "failed to set fixed rate params on vdev %i: %d\n", + arvif->vdev_id, ret); + } + + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static void +ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type) +{ + struct ath11k *ar = hw->priv; + + if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) + return; + + mutex_lock(&ar->conf_mutex); + + if (ar->state == ATH11K_STATE_RESTARTED) { + ath11k_warn(ar->ab, "pdev %d successfully recovered\n", + ar->pdev->pdev_id); + ar->state = ATH11K_STATE_ON; + ieee80211_wake_queues(ar->hw); + } + + mutex_unlock(&ar->conf_mutex); +} + +static void +ath11k_mac_update_bss_chan_survey(struct ath11k *ar, + struct ieee80211_channel *channel) +{ + int ret; + enum wmi_bss_chan_info_req_type type = WMI_BSS_SURVEY_REQ_TYPE_READ; + + lockdep_assert_held(&ar->conf_mutex); + + if (!test_bit(WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64, ar->ab->wmi_ab.svc_map) || + ar->rx_channel != channel) + return; + + if (ar->scan.state != ATH11K_SCAN_IDLE) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "ignoring bss chan info req while scanning..\n"); + return; + } + + reinit_completion(&ar->bss_survey_done); + + ret = ath11k_wmi_pdev_bss_chan_info_request(ar, type); + if (ret) { + ath11k_warn(ar->ab, "failed to send pdev bss chan info request\n"); + return; + } + + ret = wait_for_completion_timeout(&ar->bss_survey_done, 3 * HZ); + if (ret == 0) + ath11k_warn(ar->ab, "bss channel survey timed out\n"); +} + +static int ath11k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct ath11k *ar = hw->priv; + struct ieee80211_supported_band *sband; + struct survey_info *ar_survey; + int ret = 0; + + if (idx >= ATH11K_NUM_CHANS) + return -ENOENT; + + ar_survey = &ar->survey[idx]; + + mutex_lock(&ar->conf_mutex); + + sband = hw->wiphy->bands[NL80211_BAND_2GHZ]; + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } + + if (!sband) + sband = hw->wiphy->bands[NL80211_BAND_5GHZ]; + + if (!sband || idx >= sband->n_channels) { + ret = -ENOENT; + goto exit; + } + + ath11k_mac_update_bss_chan_survey(ar, &sband->channels[idx]); + + spin_lock_bh(&ar->data_lock); + memcpy(survey, ar_survey, sizeof(*survey)); + spin_unlock_bh(&ar->data_lock); + + survey->channel = &sband->channels[idx]; + + if (ar->rx_channel == survey->channel) + survey->filled |= SURVEY_INFO_IN_USE; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + + sinfo->rx_duration = arsta->rx_duration; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); + + sinfo->tx_duration = arsta->tx_duration; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION); + + if (!arsta->txrate.legacy && !arsta->txrate.nss) + return; + + if (arsta->txrate.legacy) { + sinfo->txrate.legacy = arsta->txrate.legacy; + } else { + sinfo->txrate.mcs = arsta->txrate.mcs; + sinfo->txrate.nss = arsta->txrate.nss; + sinfo->txrate.bw = arsta->txrate.bw; + sinfo->txrate.he_gi = arsta->txrate.he_gi; + sinfo->txrate.he_dcm = arsta->txrate.he_dcm; + sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; + } + sinfo->txrate.flags = arsta->txrate.flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + + /* TODO: Use real NF instead of default one. */ + sinfo->signal = arsta->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; +} + +static const struct ieee80211_ops ath11k_ops = { + .tx = ath11k_mac_op_tx, + .start = ath11k_mac_op_start, + .stop = ath11k_mac_op_stop, + .reconfig_complete = ath11k_mac_op_reconfig_complete, + .add_interface = ath11k_mac_op_add_interface, + .remove_interface = ath11k_mac_op_remove_interface, + .config = ath11k_mac_op_config, + .bss_info_changed = ath11k_mac_op_bss_info_changed, + .configure_filter = ath11k_mac_op_configure_filter, + .hw_scan = ath11k_mac_op_hw_scan, + .cancel_hw_scan = ath11k_mac_op_cancel_hw_scan, + .set_key = ath11k_mac_op_set_key, + .sta_state = ath11k_mac_op_sta_state, + .sta_set_txpwr = ath11k_mac_op_sta_set_txpwr, + .sta_rc_update = ath11k_mac_op_sta_rc_update, + .conf_tx = ath11k_mac_op_conf_tx, + .set_antenna = ath11k_mac_op_set_antenna, + .get_antenna = ath11k_mac_op_get_antenna, + .ampdu_action = ath11k_mac_op_ampdu_action, + .add_chanctx = ath11k_mac_op_add_chanctx, + .remove_chanctx = ath11k_mac_op_remove_chanctx, + .change_chanctx = ath11k_mac_op_change_chanctx, + .assign_vif_chanctx = ath11k_mac_op_assign_vif_chanctx, + .unassign_vif_chanctx = ath11k_mac_op_unassign_vif_chanctx, + .switch_vif_chanctx = ath11k_mac_op_switch_vif_chanctx, + .set_rts_threshold = ath11k_mac_op_set_rts_threshold, + .set_frag_threshold = ath11k_mac_op_set_frag_threshold, + .set_bitrate_mask = ath11k_mac_op_set_bitrate_mask, + .get_survey = ath11k_mac_op_get_survey, + .flush = ath11k_mac_op_flush, + .sta_statistics = ath11k_mac_op_sta_statistics, + CFG80211_TESTMODE_CMD(ath11k_tm_cmd) +#ifdef CONFIG_ATH11K_DEBUGFS + .sta_add_debugfs = ath11k_sta_add_debugfs, +#endif +}; + +static const struct ieee80211_iface_limit ath11k_if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 16, + .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif + }, +}; + +static const struct ieee80211_iface_combination ath11k_if_comb[] = { + { + .limits = ath11k_if_limits, + .n_limits = ARRAY_SIZE(ath11k_if_limits), + .max_interfaces = 16, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .beacon_int_min_gcd = 100, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), + }, +}; + +static void ath11k_mac_update_ch_list(struct ath11k *ar, + struct ieee80211_supported_band *band, + u32 freq_low, u32 freq_high) +{ + int i; + + if (!(freq_low && freq_high)) + return; + + for (i = 0; i < band->n_channels; i++) { + if (band->channels[i].center_freq < freq_low || + band->channels[i].center_freq > freq_high) + band->channels[i].flags |= IEEE80211_CHAN_DISABLED; + } +} + +static int ath11k_mac_setup_channels_rates(struct ath11k *ar, + u32 supported_bands) +{ + struct ieee80211_supported_band *band; + struct ath11k_hal_reg_capabilities_ext *reg_cap; + void *channels; + + BUILD_BUG_ON((ARRAY_SIZE(ath11k_2ghz_channels) + + ARRAY_SIZE(ath11k_5ghz_channels)) != + ATH11K_NUM_CHANS); + + reg_cap = &ar->ab->hal_reg_cap[ar->pdev_idx]; + + if (supported_bands & WMI_HOST_WLAN_2G_CAP) { + channels = kmemdup(ath11k_2ghz_channels, + sizeof(ath11k_2ghz_channels), + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + band = &ar->mac.sbands[NL80211_BAND_2GHZ]; + band->n_channels = ARRAY_SIZE(ath11k_2ghz_channels); + band->channels = channels; + band->n_bitrates = ath11k_g_rates_size; + band->bitrates = ath11k_g_rates; + ar->hw->wiphy->bands[NL80211_BAND_2GHZ] = band; + ath11k_mac_update_ch_list(ar, band, + reg_cap->low_2ghz_chan, + reg_cap->high_2ghz_chan); + } + + if (supported_bands & WMI_HOST_WLAN_5G_CAP) { + channels = kmemdup(ath11k_5ghz_channels, + sizeof(ath11k_5ghz_channels), + GFP_KERNEL); + if (!channels) { + kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + return -ENOMEM; + } + + band = &ar->mac.sbands[NL80211_BAND_5GHZ]; + band->n_channels = ARRAY_SIZE(ath11k_5ghz_channels); + band->channels = channels; + band->n_bitrates = ath11k_a_rates_size; + band->bitrates = ath11k_a_rates; + ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band; + ath11k_mac_update_ch_list(ar, band, + reg_cap->low_5ghz_chan, + reg_cap->high_5ghz_chan); + } + + return 0; +} + +static const u8 ath11k_if_types_ext_capa[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, +}; + +static const u8 ath11k_if_types_ext_capa_sta[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, + [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT, +}; + +static const u8 ath11k_if_types_ext_capa_ap[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, + [9] = WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT, +}; + +static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = { + { + .extended_capabilities = ath11k_if_types_ext_capa, + .extended_capabilities_mask = ath11k_if_types_ext_capa, + .extended_capabilities_len = sizeof(ath11k_if_types_ext_capa), + }, { + .iftype = NL80211_IFTYPE_STATION, + .extended_capabilities = ath11k_if_types_ext_capa_sta, + .extended_capabilities_mask = ath11k_if_types_ext_capa_sta, + .extended_capabilities_len = + sizeof(ath11k_if_types_ext_capa_sta), + }, { + .iftype = NL80211_IFTYPE_AP, + .extended_capabilities = ath11k_if_types_ext_capa_ap, + .extended_capabilities_mask = ath11k_if_types_ext_capa_ap, + .extended_capabilities_len = + sizeof(ath11k_if_types_ext_capa_ap), + }, +}; + +static void __ath11k_mac_unregister(struct ath11k *ar) +{ + cancel_work_sync(&ar->regd_update_work); + + ieee80211_unregister_hw(ar->hw); + + idr_for_each(&ar->txmgmt_idr, ath11k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + + kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); + + SET_IEEE80211_DEV(ar->hw, NULL); +} + +void ath11k_mac_unregister(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar) + continue; + + __ath11k_mac_unregister(ar); + } +} + +static int __ath11k_mac_register(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_pdev_cap *cap = &ar->pdev->cap; + static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, + WLAN_CIPHER_SUITE_BIP_CMAC_256, + WLAN_CIPHER_SUITE_BIP_GMAC_128, + WLAN_CIPHER_SUITE_BIP_GMAC_256, + WLAN_CIPHER_SUITE_GCMP, + WLAN_CIPHER_SUITE_GCMP_256, + WLAN_CIPHER_SUITE_CCMP_256, + }; + int ret; + u32 ht_cap = 0; + + ath11k_pdev_caps_update(ar); + + SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr); + + SET_IEEE80211_DEV(ar->hw, ab->dev); + + ret = ath11k_mac_setup_channels_rates(ar, + cap->supported_bands); + if (ret) + goto err_free; + + ath11k_mac_setup_ht_vht_cap(ar, cap, &ht_cap); + ath11k_mac_setup_he_cap(ar, cap); + + ar->hw->wiphy->available_antennas_rx = cap->rx_chain_mask; + ar->hw->wiphy->available_antennas_tx = cap->tx_chain_mask; + + ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT); + + ieee80211_hw_set(ar->hw, SIGNAL_DBM); + ieee80211_hw_set(ar->hw, SUPPORTS_PS); + ieee80211_hw_set(ar->hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(ar->hw, MFP_CAPABLE); + ieee80211_hw_set(ar->hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(ar->hw, HAS_RATE_CONTROL); + ieee80211_hw_set(ar->hw, AP_LINK_PS); + ieee80211_hw_set(ar->hw, SPECTRUM_MGMT); + ieee80211_hw_set(ar->hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(ar->hw, CONNECTION_MONITOR); + ieee80211_hw_set(ar->hw, SUPPORTS_PER_STA_GTK); + ieee80211_hw_set(ar->hw, WANT_MONITOR_VIF); + ieee80211_hw_set(ar->hw, CHANCTX_STA_CSA); + ieee80211_hw_set(ar->hw, QUEUE_CONTROL); + ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG); + ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK); + if (ht_cap & WMI_HT_CAP_ENABLED) { + ieee80211_hw_set(ar->hw, AMPDU_AGGREGATION); + 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); + } + + ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS; + ar->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + + /* TODO: Check if HT capability advertised from firmware is different + * for each band for a dual band capable radio. It will be tricky to + * handle it when the ht capability different for each band. + */ + if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS) + ar->hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS; + + ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID; + ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN; + + ar->hw->max_listen_interval = ATH11K_MAX_HW_LISTEN_INTERVAL; + + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + ar->hw->wiphy->max_remain_on_channel_duration = 5000; + + ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | + NL80211_FEATURE_AP_SCAN; + + ar->max_num_stations = TARGET_NUM_STATIONS; + ar->max_num_peers = TARGET_NUM_PEERS_PDEV; + + ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations; + + ar->hw->queues = ATH11K_HW_MAX_QUEUES; + ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; + ar->hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; + + ar->hw->vif_data_size = sizeof(struct ath11k_vif); + ar->hw->sta_data_size = sizeof(struct ath11k_sta); + + ar->hw->wiphy->iface_combinations = ath11k_if_comb; + ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath11k_if_comb); + + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_STA_TX_PWR); + + ar->hw->wiphy->cipher_suites = cipher_suites; + ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + ar->hw->wiphy->iftype_ext_capab = ath11k_iftypes_ext_capa; + ar->hw->wiphy->num_iftype_ext_capab = + ARRAY_SIZE(ath11k_iftypes_ext_capa); + + ath11k_reg_init(ar); + + /* advertise HW checksum offload capabilities */ + ar->hw->netdev_features = NETIF_F_HW_CSUM; + + ret = ieee80211_register_hw(ar->hw); + if (ret) { + ath11k_err(ar->ab, "ieee80211 registration failed: %d\n", ret); + goto err_free; + } + + /* Apply the regd received during initialization */ + ret = ath11k_regd_update(ar, true); + if (ret) { + ath11k_err(ar->ab, "ath11k regd update failed: %d\n", ret); + goto err_free; + } + + ret = ath11k_debug_register(ar); + if (ret) { + ath11k_err(ar->ab, "debugfs registration failed: %d\n", ret); + goto err_free; + } + + return 0; + +err_free: + kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); + + SET_IEEE80211_DEV(ar->hw, NULL); + return ret; +} + +int ath11k_mac_register(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i; + int ret; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (ab->pdevs_macaddr_valid) { + ether_addr_copy(ar->mac_addr, pdev->mac_addr); + } else { + ether_addr_copy(ar->mac_addr, ab->mac_addr); + ar->mac_addr[4] += i; + } + + ret = __ath11k_mac_register(ar); + if (ret) + goto err_cleanup; + + idr_init(&ar->txmgmt_idr); + spin_lock_init(&ar->txmgmt_idr_lock); + } + + /* Initialize channel counters frequency value in hertz */ + ab->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; + + return 0; + +err_cleanup: + for (i = i - 1; i >= 0; i--) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + __ath11k_mac_unregister(ar); + } + + return ret; +} + +int ath11k_mac_allocate(struct ath11k_base *ab) +{ + struct ieee80211_hw *hw; + struct ath11k *ar; + struct ath11k_pdev *pdev; + int ret; + int i; + + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) + return 0; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + hw = ieee80211_alloc_hw(sizeof(struct ath11k), &ath11k_ops); + if (!hw) { + ath11k_warn(ab, "failed to allocate mac80211 hw device\n"); + ret = -ENOMEM; + goto err_free_mac; + } + + ar = hw->priv; + ar->hw = hw; + ar->ab = ab; + ar->pdev = pdev; + ar->pdev_idx = i; + ar->lmac_id = ath11k_core_get_hw_mac_id(ab, i); + + ar->wmi = &ab->wmi_ab.wmi[i]; + /* FIXME wmi[0] is already initialized during attach, + * Should we do this again? + */ + ath11k_wmi_pdev_attach(ab, i); + + ar->cfg_tx_chainmask = pdev->cap.tx_chain_mask; + ar->cfg_rx_chainmask = pdev->cap.rx_chain_mask; + ar->num_tx_chains = get_num_chains(pdev->cap.tx_chain_mask); + ar->num_rx_chains = get_num_chains(pdev->cap.rx_chain_mask); + + pdev->ar = ar; + spin_lock_init(&ar->data_lock); + INIT_LIST_HEAD(&ar->arvifs); + INIT_LIST_HEAD(&ar->ppdu_stats_info); + mutex_init(&ar->conf_mutex); + init_completion(&ar->vdev_setup_done); + init_completion(&ar->peer_assoc_done); + init_completion(&ar->install_key_done); + init_completion(&ar->bss_survey_done); + init_completion(&ar->scan.started); + init_completion(&ar->scan.completed); + INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work); + INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work); + + INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + } + + return 0; + +err_free_mac: + ath11k_mac_destroy(ab); + + return ret; +} + +void ath11k_mac_destroy(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar) + continue; + + ieee80211_free_hw(ar->hw); + pdev->ar = NULL; + } +} diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h new file mode 100644 index 000000000000..f286531cdd78 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_MAC_H +#define ATH11K_MAC_H + +#include <net/mac80211.h> +#include <net/cfg80211.h> + +struct ath11k; +struct ath11k_base; + +struct ath11k_generic_iter { + struct ath11k *ar; + int ret; +}; + +/* number of failed packets (20 packets with 16 sw reties each) */ +#define ATH11K_KICKOUT_THRESHOLD (20 * 16) + +/* Use insanely high numbers to make sure that the firmware implementation + * won't start, we have the same functionality already in hostapd. Unit + * is seconds. + */ +#define ATH11K_KEEPALIVE_MIN_IDLE 3747 +#define ATH11K_KEEPALIVE_MAX_IDLE 3895 +#define ATH11K_KEEPALIVE_MAX_UNRESPONSIVE 3900 + +#define WMI_HOST_RC_DS_FLAG 0x01 +#define WMI_HOST_RC_CW40_FLAG 0x02 +#define WMI_HOST_RC_SGI_FLAG 0x04 +#define WMI_HOST_RC_HT_FLAG 0x08 +#define WMI_HOST_RC_RTSCTS_FLAG 0x10 +#define WMI_HOST_RC_TX_STBC_FLAG 0x20 +#define WMI_HOST_RC_RX_STBC_FLAG 0xC0 +#define WMI_HOST_RC_RX_STBC_FLAG_S 6 +#define WMI_HOST_RC_WEP_TKIP_FLAG 0x100 +#define WMI_HOST_RC_TS_FLAG 0x200 +#define WMI_HOST_RC_UAPSD_FLAG 0x400 + +#define WMI_HT_CAP_ENABLED 0x0001 +#define WMI_HT_CAP_HT20_SGI 0x0002 +#define WMI_HT_CAP_DYNAMIC_SMPS 0x0004 +#define WMI_HT_CAP_TX_STBC 0x0008 +#define WMI_HT_CAP_TX_STBC_MASK_SHIFT 3 +#define WMI_HT_CAP_RX_STBC 0x0030 +#define WMI_HT_CAP_RX_STBC_MASK_SHIFT 4 +#define WMI_HT_CAP_LDPC 0x0040 +#define WMI_HT_CAP_L_SIG_TXOP_PROT 0x0080 +#define WMI_HT_CAP_MPDU_DENSITY 0x0700 +#define WMI_HT_CAP_MPDU_DENSITY_MASK_SHIFT 8 +#define WMI_HT_CAP_HT40_SGI 0x0800 +#define WMI_HT_CAP_RX_LDPC 0x1000 +#define WMI_HT_CAP_TX_LDPC 0x2000 +#define WMI_HT_CAP_IBF_BFER 0x4000 + +/* These macros should be used when we wish to advertise STBC support for + * only 1SS or 2SS or 3SS. + */ +#define WMI_HT_CAP_RX_STBC_1SS 0x0010 +#define WMI_HT_CAP_RX_STBC_2SS 0x0020 +#define WMI_HT_CAP_RX_STBC_3SS 0x0030 + +#define WMI_HT_CAP_DEFAULT_ALL (WMI_HT_CAP_ENABLED | \ + WMI_HT_CAP_HT20_SGI | \ + WMI_HT_CAP_HT40_SGI | \ + WMI_HT_CAP_TX_STBC | \ + WMI_HT_CAP_RX_STBC | \ + WMI_HT_CAP_LDPC) + +#define WMI_VHT_CAP_MAX_MPDU_LEN_MASK 0x00000003 +#define WMI_VHT_CAP_RX_LDPC 0x00000010 +#define WMI_VHT_CAP_SGI_80MHZ 0x00000020 +#define WMI_VHT_CAP_SGI_160MHZ 0x00000040 +#define WMI_VHT_CAP_TX_STBC 0x00000080 +#define WMI_VHT_CAP_RX_STBC_MASK 0x00000300 +#define WMI_VHT_CAP_RX_STBC_MASK_SHIFT 8 +#define WMI_VHT_CAP_SU_BFER 0x00000800 +#define WMI_VHT_CAP_SU_BFEE 0x00001000 +#define WMI_VHT_CAP_MAX_CS_ANT_MASK 0x0000E000 +#define WMI_VHT_CAP_MAX_CS_ANT_MASK_SHIFT 13 +#define WMI_VHT_CAP_MAX_SND_DIM_MASK 0x00070000 +#define WMI_VHT_CAP_MAX_SND_DIM_MASK_SHIFT 16 +#define WMI_VHT_CAP_MU_BFER 0x00080000 +#define WMI_VHT_CAP_MU_BFEE 0x00100000 +#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP 0x03800000 +#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIT 23 +#define WMI_VHT_CAP_RX_FIXED_ANT 0x10000000 +#define WMI_VHT_CAP_TX_FIXED_ANT 0x20000000 + +#define WMI_VHT_CAP_MAX_MPDU_LEN_11454 0x00000002 + +/* These macros should be used when we wish to advertise STBC support for + * only 1SS or 2SS or 3SS. + */ +#define WMI_VHT_CAP_RX_STBC_1SS 0x00000100 +#define WMI_VHT_CAP_RX_STBC_2SS 0x00000200 +#define WMI_VHT_CAP_RX_STBC_3SS 0x00000300 + +#define WMI_VHT_CAP_DEFAULT_ALL (WMI_VHT_CAP_MAX_MPDU_LEN_11454 | \ + WMI_VHT_CAP_SGI_80MHZ | \ + WMI_VHT_CAP_TX_STBC | \ + WMI_VHT_CAP_RX_STBC_MASK | \ + WMI_VHT_CAP_RX_LDPC | \ + WMI_VHT_CAP_MAX_AMPDU_LEN_EXP | \ + WMI_VHT_CAP_RX_FIXED_ANT | \ + WMI_VHT_CAP_TX_FIXED_ANT) + +/* FIXME: should these be in ieee80211.h? */ +#define IEEE80211_VHT_MCS_SUPPORT_0_11_MASK GENMASK(23, 16) +#define IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11 BIT(24) + +#define WMI_MAX_SPATIAL_STREAM 3 + +#define ATH11K_CHAN_WIDTH_NUM 8 + +extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default; + +void ath11k_mac_destroy(struct ath11k_base *ab); +void ath11k_mac_unregister(struct ath11k_base *ab); +int ath11k_mac_register(struct ath11k_base *ab); +int ath11k_mac_allocate(struct ath11k_base *ab); +int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx, + u16 *rate); +u8 ath11k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband, + u32 bitrate); +u8 ath11k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, + u8 hw_rate, bool cck); + +void __ath11k_mac_scan_finish(struct ath11k *ar); +void ath11k_mac_scan_finish(struct ath11k *ar); + +struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id); +struct ath11k_vif *ath11k_mac_get_arvif_by_vdev_id(struct ath11k_base *ab, + u32 vdev_id); +struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id); +struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id); +struct ath11k *ath11k_mac_get_ar_vdev_stop_status(struct ath11k_base *ab, + u32 vdev_id); + +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); +#endif diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c new file mode 100644 index 000000000000..4bf1dfa498b6 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "core.h" +#include "peer.h" +#include "debug.h" + +struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, + const u8 *addr) +{ + struct ath11k_peer *peer; + + lockdep_assert_held(&ab->base_lock); + + list_for_each_entry(peer, &ab->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + if (memcmp(peer->addr, addr, ETH_ALEN)) + continue; + + return peer; + } + + return NULL; +} + +struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, + const u8 *addr) +{ + struct ath11k_peer *peer; + + lockdep_assert_held(&ab->base_lock); + + list_for_each_entry(peer, &ab->peers, list) { + if (memcmp(peer->addr, addr, ETH_ALEN)) + continue; + + return peer; + } + + return NULL; +} + +struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, + int peer_id) +{ + struct ath11k_peer *peer; + + lockdep_assert_held(&ab->base_lock); + + list_for_each_entry(peer, &ab->peers, list) + if (peer_id == peer->peer_id) + return peer; + + return NULL; +} + +void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id) +{ + struct ath11k_peer *peer; + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find_by_id(ab, peer_id); + if (!peer) { + ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n", + peer_id); + goto exit; + } + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer unmap vdev %d peer %pM id %d\n", + peer->vdev_id, peer->addr, peer_id); + + list_del(&peer->list); + kfree(peer); + wake_up(&ab->peer_mapping_wq); + +exit: + spin_unlock_bh(&ab->base_lock); +} + +void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash) +{ + struct ath11k_peer *peer; + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, vdev_id, mac_addr); + if (!peer) { + peer = kzalloc(sizeof(*peer), GFP_ATOMIC); + if (!peer) + goto exit; + + peer->vdev_id = vdev_id; + peer->peer_id = peer_id; + peer->ast_hash = ast_hash; + ether_addr_copy(peer->addr, mac_addr); + list_add(&peer->list, &ab->peers); + wake_up(&ab->peer_mapping_wq); + } + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer map vdev %d peer %pM id %d\n", + vdev_id, mac_addr, peer_id); + +exit: + spin_unlock_bh(&ab->base_lock); +} + +static int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id, + const u8 *addr, bool expect_mapped) +{ + int ret; + + ret = wait_event_timeout(ab->peer_mapping_wq, ({ + bool mapped; + + spin_lock_bh(&ab->base_lock); + mapped = !!ath11k_peer_find(ab, vdev_id, addr); + spin_unlock_bh(&ab->base_lock); + + (mapped == expect_mapped || + test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)); + }), 3 * HZ); + + if (ret <= 0) + return -ETIMEDOUT; + + return 0; +} + +void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id) +{ + struct ath11k_peer *peer, *tmp; + struct ath11k_base *ab = ar->ab; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ab->base_lock); + list_for_each_entry_safe(peer, tmp, &ab->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + + ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n", + peer->addr, vdev_id); + + list_del(&peer->list); + kfree(peer); + ar->num_peers--; + } + + spin_unlock_bh(&ab->base_lock); +} + +static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) +{ + return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, false); +} + +int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); + if (ret) { + ath11k_warn(ar->ab, + "failed to delete peer vdev_id %d addr %pM ret %d\n", + vdev_id, addr, ret); + return ret; + } + + ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr); + if (ret) + return ret; + + ar->num_peers--; + + return 0; +} + +static int ath11k_wait_for_peer_created(struct ath11k *ar, int vdev_id, const u8 *addr) +{ + return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, true); +} + +int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, + struct ieee80211_sta *sta, struct peer_create_params *param) +{ + struct ath11k_peer *peer; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (ar->num_peers > (ar->max_num_peers - 1)) { + ath11k_warn(ar->ab, + "failed to create peer due to insufficient peer entry resource in firmware\n"); + return -ENOBUFS; + } + + ret = ath11k_wmi_send_peer_create_cmd(ar, param); + if (ret) { + ath11k_warn(ar->ab, + "failed to send peer create vdev_id %d ret %d\n", + param->vdev_id, ret); + return ret; + } + + ret = ath11k_wait_for_peer_created(ar, param->vdev_id, + param->peer_addr); + if (ret) + return ret; + + spin_lock_bh(&ar->ab->base_lock); + + peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr); + if (!peer) { + spin_unlock_bh(&ar->ab->base_lock); + ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", + param->peer_addr, param->vdev_id); + ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, + param->vdev_id); + return -ENOENT; + } + + peer->sta = sta; + arvif->ast_hash = peer->ast_hash; + + ar->num_peers++; + + spin_unlock_bh(&ar->ab->base_lock); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/peer.h b/drivers/net/wireless/ath/ath11k/peer.h new file mode 100644 index 000000000000..9a40d1f6ccd9 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/peer.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_PEER_H +#define ATH11K_PEER_H + +struct ath11k_peer { + struct list_head list; + struct ieee80211_sta *sta; + int vdev_id; + u8 addr[ETH_ALEN]; + int peer_id; + u16 ast_hash; + + /* 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]; +}; + +void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); +void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash); +struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, int peer_id); +void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id); +int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr); +int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, + struct ieee80211_sta *sta, struct peer_create_params *param); + +#endif /* _PEER_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c new file mode 100644 index 000000000000..2377895a58ec --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -0,0 +1,2433 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "qmi.h" +#include "core.h" +#include "debug.h" +#include <linux/of.h> +#include <linux/firmware.h> + +static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + num_clients_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + num_clients), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + wake_msi_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + wake_msi), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + gpios_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + gpios_len), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = QMI_WLFW_MAX_NUM_GPIO_V01, + .elem_size = sizeof(u32), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + gpios), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + nm_modem_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + nm_modem), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_cache_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_cache_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_cache_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_cache_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_filesys_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_filesys_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_cache_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_cache_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_done_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_done), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_bucket_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_bucket), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1C, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_cfg_mode_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1C, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_cfg_mode), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_host_cap_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_download_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_download_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_update_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_update_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + msa_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + msa_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + pin_connect_result_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + pin_connect_result_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + client_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + client_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + request_mem_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + request_mem_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_mem_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_mem_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_init_done_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_init_done_enable), + }, + + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + rejuvenate_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + rejuvenate_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + xo_cal_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + xo_cal_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + cal_done_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + cal_done_enable), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_ind_register_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_resp_msg_v01, + fw_status_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_resp_msg_v01, + fw_status), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_cfg_s_v01, offset), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_cfg_s_v01, size), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_cfg_s_v01, secure_flag), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, + size), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_mem_type_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, type), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, mem_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_MEM_CFG_V01, + .elem_size = sizeof(struct qmi_wlanfw_mem_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, mem_cfg), + .ei_array = qmi_wlanfw_mem_cfg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_request_mem_ind_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct qmi_wlanfw_mem_seg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_request_mem_ind_msg_v01, + mem_seg), + .ei_array = qmi_wlanfw_mem_seg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, size), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_mem_type_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, type), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, restore), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_respond_mem_req_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct qmi_wlanfw_mem_seg_resp_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_respond_mem_req_msg_v01, + mem_seg), + .ei_array = qmi_wlanfw_mem_seg_resp_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_respond_mem_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_rf_chip_info_s_v01, + chip_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_rf_chip_info_s_v01, + chip_family), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_rf_board_info_s_v01, + board_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_soc_info_s_v01, soc_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_fw_version_info_s_v01, + fw_version), + }, + { + .data_type = QMI_STRING, + .elem_len = ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_fw_version_info_s_v01, + fw_build_timestamp), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + chip_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_rf_chip_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + chip_info), + .ei_array = qmi_wlanfw_rf_chip_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + board_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_rf_board_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + board_info), + .ei_array = qmi_wlanfw_rf_board_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + soc_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_soc_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + soc_info), + .ei_array = qmi_wlanfw_soc_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_version_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_fw_version_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_version_info), + .ei_array = qmi_wlanfw_fw_version_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_build_id_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_build_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + num_macs_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + num_macs), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + valid), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + file_id_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_cal_temp_id_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + file_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + total_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + total_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + seg_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + seg_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLANFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + end_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + end), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + bdf_type_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + bdf_type), + }, + + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_bdf_download_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_m3_info_req_msg_v01, addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_m3_info_req_msg_v01, size), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_m3_info_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + pipe_num), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_pipedir_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + pipe_dir), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + nentries), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + nbytes_max), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + flags), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01, + service_id), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_pipedir_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01, + pipe_dir), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01, + pipe_num), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_shadow_reg_cfg_s_v01, id), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_shadow_reg_cfg_s_v01, + offset), + }, + { + .data_type = QMI_EOTI, + .array_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01, + addr), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_req_msg_v01, + mode), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_req_msg_v01, + hw_debug_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_req_msg_v01, + hw_debug), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + host_version_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_WLANFW_MAX_STR_LEN_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + host_version), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + tgt_cfg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + tgt_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_CE_V01, + .elem_size = sizeof( + struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + tgt_cfg), + .ei_array = qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + svc_cfg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + svc_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_SVC_V01, + .elem_size = sizeof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + svc_cfg), + .ei_array = qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_SHADOW_REG_V01, + .elem_size = sizeof(struct qmi_wlanfw_shadow_reg_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg), + .ei_array = qmi_wlanfw_shadow_reg_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01, + .elem_size = sizeof(struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_v2), + .ei_array = qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + }, +}; + +static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) +{ + struct qmi_wlanfw_host_cap_req_msg_v01 req; + struct qmi_wlanfw_host_cap_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.num_clients_valid = 1; + req.num_clients = 1; + req.mem_cfg_mode = ab->qmi.target_mem_mode; + req.mem_cfg_mode_valid = 1; + req.bdf_support_valid = 1; + req.bdf_support = 1; + + req.m3_support_valid = 0; + req.m3_support = 0; + + req.m3_cache_support_valid = 0; + req.m3_cache_support = 0; + + req.cal_done_valid = 1; + req.cal_done = ab->qmi.cal_done; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_host_cap_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_HOST_CAP_REQ_V01, + QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_host_cap_req_msg_v01_ei, &req); + if (ret < 0) { + ath11k_warn(ab, "Failed to send host capability request,err = %d\n", ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) + goto out; + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "Host capability request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } + +out: + return ret; +} + +static int ath11k_qmi_fw_ind_register_send(struct ath11k_base *ab) +{ + struct qmi_wlanfw_ind_register_req_msg_v01 *req; + struct qmi_wlanfw_ind_register_resp_msg_v01 *resp; + struct qmi_handle *handle = &ab->qmi.handle; + struct qmi_txn txn; + int ret = 0; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) + goto resp_out; + + req->client_id_valid = 1; + req->client_id = QMI_WLANFW_CLIENT_ID; + req->fw_ready_enable_valid = 1; + req->fw_ready_enable = 1; + req->request_mem_enable_valid = 1; + req->request_mem_enable = 1; + req->fw_mem_ready_enable_valid = 1; + req->fw_mem_ready_enable = 1; + req->cal_done_enable_valid = 1; + req->cal_done_enable = 1; + req->fw_init_done_enable_valid = 1; + req->fw_init_done_enable = 1; + + req->pin_connect_result_enable_valid = 0; + req->pin_connect_result_enable = 0; + + ret = qmi_txn_init(handle, &txn, + qmi_wlanfw_ind_register_resp_msg_v01_ei, resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_IND_REGISTER_REQ_V01, + QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_ind_register_req_msg_v01_ei, req); + if (ret < 0) { + ath11k_warn(ab, "Failed to send indication register request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "failed to register fw indication %d\n", ret); + goto out; + } + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "FW Ind register request failed, result: %d, err: %d\n", + resp->resp.result, resp->resp.error); + ret = -EINVAL; + goto out; + } + +out: + kfree(resp); +resp_out: + kfree(req); + return ret; +} + +static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab) +{ + struct qmi_wlanfw_respond_mem_req_msg_v01 *req; + struct qmi_wlanfw_respond_mem_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0, i; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + memset(&resp, 0, sizeof(resp)); + + req->mem_seg_len = ab->qmi.mem_seg_count; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_respond_mem_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + for (i = 0; i < req->mem_seg_len ; i++) { + req->mem_seg[i].addr = ab->qmi.target_mem[i].paddr; + req->mem_seg[i].size = ab->qmi.target_mem[i].size; + req->mem_seg[i].type = ab->qmi.target_mem[i].type; + } + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_RESPOND_MEM_REQ_V01, + QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_respond_mem_req_msg_v01_ei, req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to respond memory request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed memory request, err = %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "Respond mem req failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } +out: + kfree(req); + return ret; +} + +static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) +{ + int i, idx; + + for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { + switch (ab->qmi.target_mem[i].type) { + case BDF_MEM_REGION_TYPE: + ab->qmi.target_mem[idx].paddr = ATH11K_QMI_BDF_ADDRESS; + ab->qmi.target_mem[idx].vaddr = ATH11K_QMI_BDF_ADDRESS; + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + case CALDB_MEM_REGION_TYPE: + if (ab->qmi.target_mem[i].size > ATH11K_QMI_CALDB_SIZE) { + ath11k_warn(ab, "qmi mem size is low to load caldata\n"); + return -EINVAL; + } + /* TODO ath11k does not support cold boot calibration */ + ab->qmi.target_mem[idx].paddr = 0; + ab->qmi.target_mem[idx].vaddr = 0; + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + default: + ath11k_warn(ab, "qmi ignore invalid mem req type %d\n", + ab->qmi.target_mem[i].type); + break; + } + } + ab->qmi.mem_seg_count = idx; + + return 0; +} + +static int ath11k_qmi_request_target_cap(struct ath11k_base *ab) +{ + struct qmi_wlanfw_cap_req_msg_v01 req; + struct qmi_wlanfw_cap_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_cap_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_CAP_REQ_V01, + QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_cap_req_msg_v01_ei, &req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send target cap request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed target cap request %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi targetcap req failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } + + if (resp.chip_info_valid) { + ab->qmi.target.chip_id = resp.chip_info.chip_id; + ab->qmi.target.chip_family = resp.chip_info.chip_family; + } + + if (resp.board_info_valid) + ab->qmi.target.board_id = resp.board_info.board_id; + else + ab->qmi.target.board_id = 0xFF; + + if (resp.soc_info_valid) + ab->qmi.target.soc_id = resp.soc_info.soc_id; + + if (resp.fw_version_info_valid) { + ab->qmi.target.fw_version = resp.fw_version_info.fw_version; + strlcpy(ab->qmi.target.fw_build_timestamp, + resp.fw_version_info.fw_build_timestamp, + sizeof(ab->qmi.target.fw_build_timestamp)); + } + + if (resp.fw_build_id_valid) + strlcpy(ab->qmi.target.fw_build_id, resp.fw_build_id, + sizeof(ab->qmi.target.fw_build_id)); + + ath11k_info(ab, "qmi target: chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x\n", + ab->qmi.target.chip_id, ab->qmi.target.chip_family, + ab->qmi.target.board_id, ab->qmi.target.soc_id); + + ath11k_info(ab, "qmi fw_version: 0x%x fw_build_timestamp: %s fw_build_id: %s", + ab->qmi.target.fw_version, + ab->qmi.target.fw_build_timestamp, + ab->qmi.target.fw_build_id); + +out: + return ret; +} + +static int +ath11k_qmi_prepare_bdf_download(struct ath11k_base *ab, int type, + struct qmi_wlanfw_bdf_download_req_msg_v01 *req, + void __iomem *bdf_addr) +{ + struct device *dev = ab->dev; + char filename[ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE]; + const struct firmware *fw_entry; + struct ath11k_board_data bd; + u32 fw_size; + int ret = 0; + + memset(&bd, 0, sizeof(bd)); + + switch (type) { + case ATH11K_QMI_FILE_TYPE_BDF_GOLDEN: + ret = ath11k_core_fetch_bdf(ab, &bd); + if (ret) { + ath11k_warn(ab, "qmi failed to load BDF\n"); + goto out; + } + + fw_size = min_t(u32, ab->hw_params.fw.board_size, bd.len); + memcpy_toio(bdf_addr, bd.data, fw_size); + ath11k_core_free_bdf(ab, &bd); + break; + case ATH11K_QMI_FILE_TYPE_CALDATA: + snprintf(filename, sizeof(filename), + "%s/%s", ab->hw_params.fw.dir, ATH11K_QMI_DEFAULT_CAL_FILE_NAME); + ret = request_firmware(&fw_entry, filename, dev); + if (ret) { + ath11k_warn(ab, "qmi failed to load CAL: %s\n", filename); + goto out; + } + + fw_size = min_t(u32, ab->hw_params.fw.board_size, + fw_entry->size); + + memcpy_toio(bdf_addr + ATH11K_QMI_CALDATA_OFFSET, + fw_entry->data, fw_size); + ath11k_info(ab, "qmi downloading BDF: %s, size: %zu\n", + filename, fw_entry->size); + + release_firmware(fw_entry); + break; + default: + ret = -EINVAL; + goto out; + } + + req->total_size = fw_size; + +out: + return ret; +} + +static int ath11k_qmi_load_bdf(struct ath11k_base *ab) +{ + struct qmi_wlanfw_bdf_download_req_msg_v01 *req; + struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; + struct qmi_txn txn = {}; + void __iomem *bdf_addr = NULL; + int type, ret; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + memset(&resp, 0, sizeof(resp)); + + bdf_addr = ioremap(ATH11K_QMI_BDF_ADDRESS, ATH11K_QMI_BDF_MAX_SIZE); + if (!bdf_addr) { + ath11k_warn(ab, "qmi ioremap error for BDF\n"); + ret = -EIO; + goto out; + } + + for (type = 0; type < ATH11K_QMI_MAX_FILE_TYPE; type++) { + req->valid = 1; + req->file_id_valid = 1; + req->file_id = ab->qmi.target.board_id; + req->total_size_valid = 1; + req->seg_id_valid = 1; + req->seg_id = type; + req->data_valid = 0; + req->data_len = ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE; + req->bdf_type = 0; + req->bdf_type_valid = 0; + req->end_valid = 1; + req->end = 1; + + ret = ath11k_qmi_prepare_bdf_download(ab, type, req, bdf_addr); + if (ret < 0) + goto out_qmi_bdf; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_bdf_download_resp_msg_v01_ei, + &resp); + if (ret < 0) + goto out_qmi_bdf; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_BDF_DOWNLOAD_REQ_V01, + QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_bdf_download_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + goto out_qmi_bdf; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) + goto out_qmi_bdf; + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi BDF download failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out_qmi_bdf; + } + } + ath11k_info(ab, "qmi BDF downloaded\n"); + +out_qmi_bdf: + iounmap(bdf_addr); +out: + kfree(req); + return ret; +} + +static int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) +{ + struct qmi_wlanfw_m3_info_req_msg_v01 req; + struct qmi_wlanfw_m3_info_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + req.addr = 0; + req.size = 0; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_m3_info_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_M3_INFO_REQ_V01, + QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN, + qmi_wlanfw_m3_info_req_msg_v01_ei, &req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send M3 information request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed M3 information request %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi M3 info request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } +out: + return ret; +} + +static int ath11k_qmi_wlanfw_mode_send(struct ath11k_base *ab, + u32 mode) +{ + struct qmi_wlanfw_wlan_mode_req_msg_v01 req; + struct qmi_wlanfw_wlan_mode_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.mode = mode; + req.hw_debug_valid = 1; + req.hw_debug = 0; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_wlan_mode_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_WLAN_MODE_REQ_V01, + QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_mode_req_msg_v01_ei, &req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send mode request, mode: %d, err = %d\n", + mode, ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + if (mode == ATH11K_FIRMWARE_MODE_OFF && ret == -ENETRESET) { + ath11k_warn(ab, "WLFW service is dis-connected\n"); + return 0; + } + ath11k_warn(ab, "qmi failed set mode request, mode: %d, err = %d\n", + mode, ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "Mode request failed, mode: %d, result: %d err: %d\n", + mode, resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } + +out: + return ret; +} + +static int ath11k_qmi_wlanfw_wlan_cfg_send(struct ath11k_base *ab) +{ + struct qmi_wlanfw_wlan_cfg_req_msg_v01 *req; + struct qmi_wlanfw_wlan_cfg_resp_msg_v01 resp; + struct ce_pipe_config *ce_cfg; + struct service_to_pipe *svc_cfg; + struct qmi_txn txn = {}; + int ret = 0, pipe_num; + + ce_cfg = (struct ce_pipe_config *)ab->qmi.ce_cfg.tgt_ce; + svc_cfg = (struct service_to_pipe *)ab->qmi.ce_cfg.svc_to_ce_map; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + memset(&resp, 0, sizeof(resp)); + + req->host_version_valid = 1; + strlcpy(req->host_version, ATH11K_HOST_VERSION_STRING, + sizeof(req->host_version)); + + req->tgt_cfg_valid = 1; + /* This is number of CE configs */ + req->tgt_cfg_len = ab->qmi.ce_cfg.tgt_ce_len; + for (pipe_num = 0; pipe_num <= req->tgt_cfg_len ; pipe_num++) { + req->tgt_cfg[pipe_num].pipe_num = ce_cfg[pipe_num].pipenum; + req->tgt_cfg[pipe_num].pipe_dir = ce_cfg[pipe_num].pipedir; + req->tgt_cfg[pipe_num].nentries = ce_cfg[pipe_num].nentries; + req->tgt_cfg[pipe_num].nbytes_max = ce_cfg[pipe_num].nbytes_max; + req->tgt_cfg[pipe_num].flags = ce_cfg[pipe_num].flags; + } + + req->svc_cfg_valid = 1; + /* This is number of Service/CE configs */ + req->svc_cfg_len = ab->qmi.ce_cfg.svc_to_ce_map_len; + for (pipe_num = 0; pipe_num < req->svc_cfg_len; pipe_num++) { + req->svc_cfg[pipe_num].service_id = svc_cfg[pipe_num].service_id; + req->svc_cfg[pipe_num].pipe_dir = svc_cfg[pipe_num].pipedir; + req->svc_cfg[pipe_num].pipe_num = svc_cfg[pipe_num].pipenum; + } + req->shadow_reg_valid = 0; + req->shadow_reg_v2_valid = 0; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_wlan_cfg_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_WLAN_CFG_REQ_V01, + QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_cfg_req_msg_v01_ei, req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan config request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed wlan config request, err = %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi wlan config request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } + +out: + kfree(req); + return ret; +} + +void ath11k_qmi_firmware_stop(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_qmi_wlanfw_mode_send(ab, ATH11K_FIRMWARE_MODE_OFF); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan mode off\n"); + return; + } +} + +int ath11k_qmi_firmware_start(struct ath11k_base *ab, + u32 mode) +{ + int ret; + + ret = ath11k_qmi_wlanfw_wlan_cfg_send(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan cfg:%d\n", ret); + return ret; + } + + ret = ath11k_qmi_wlanfw_mode_send(ab, mode); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan fw mode:%d\n", ret); + return ret; + } + + return 0; +} + +static int +ath11k_qmi_driver_event_post(struct ath11k_qmi *qmi, + enum ath11k_qmi_event_type type, + void *data) +{ + struct ath11k_qmi_driver_event *event; + + event = kzalloc(sizeof(*event), GFP_ATOMIC); + if (!event) + return -ENOMEM; + + event->type = type; + event->data = data; + + spin_lock(&qmi->event_lock); + list_add_tail(&event->list, &qmi->event_list); + spin_unlock(&qmi->event_lock); + + queue_work(qmi->event_wq, &qmi->event_work); + + return 0; +} + +static void ath11k_qmi_event_server_arrive(struct ath11k_qmi *qmi) +{ + struct ath11k_base *ab = qmi->ab; + int ret; + + ret = ath11k_qmi_fw_ind_register_send(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send FW indication QMI:%d\n", ret); + return; + } + + ret = ath11k_qmi_host_cap_send(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send host cap QMI:%d\n", ret); + return; + } +} + +static void ath11k_qmi_event_mem_request(struct ath11k_qmi *qmi) +{ + struct ath11k_base *ab = qmi->ab; + int ret; + + ret = ath11k_qmi_respond_fw_mem_request(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to respond fw mem req:%d\n", ret); + return; + } +} + +static void ath11k_qmi_event_load_bdf(struct ath11k_qmi *qmi) +{ + struct ath11k_base *ab = qmi->ab; + int ret; + + ret = ath11k_qmi_request_target_cap(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to req target capabilities:%d\n", ret); + return; + } + + ret = ath11k_qmi_load_bdf(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to load board data file:%d\n", ret); + return; + } + + ret = ath11k_qmi_wlanfw_m3_info_send(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send m3 info req:%d\n", ret); + return; + } +} + +static void ath11k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *data) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + const struct qmi_wlanfw_request_mem_ind_msg_v01 *msg = data; + int i, ret; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi firmware request memory request\n"); + + if (msg->mem_seg_len == 0 || + msg->mem_seg_len > ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01) + ath11k_warn(ab, "Invalid memory segment length: %u\n", + msg->mem_seg_len); + + ab->qmi.mem_seg_count = msg->mem_seg_len; + + for (i = 0; i < qmi->mem_seg_count ; i++) { + ab->qmi.target_mem[i].type = msg->mem_seg[i].type; + ab->qmi.target_mem[i].size = msg->mem_seg[i].size; + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi mem seg type %d size %d\n", + msg->mem_seg[i].type, msg->mem_seg[i].size); + } + + ret = ath11k_qmi_alloc_target_mem_chunk(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to alloc target memory:%d\n", ret); + return; + } + + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_REQUEST_MEM, NULL); +} + +static void ath11k_qmi_msg_mem_ready_cb(struct qmi_handle *qmi_hdl, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi firmware memory ready indication\n"); + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_FW_MEM_READY, NULL); +} + +static void ath11k_qmi_msg_fw_ready_cb(struct qmi_handle *qmi_hdl, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi firmware ready\n"); + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_FW_READY, NULL); +} + +static void ath11k_qmi_msg_cold_boot_cal_done_cb(struct qmi_handle *qmi, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded) +{ +} + +static const struct qmi_msg_handler ath11k_qmi_msg_handlers[] = { + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_REQUEST_MEM_IND_V01, + .ei = qmi_wlanfw_request_mem_ind_msg_v01_ei, + .decoded_size = sizeof(qmi_wlanfw_request_mem_ind_msg_v01_ei), + .fn = ath11k_qmi_msg_mem_request_cb, + }, + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_FW_MEM_READY_IND_V01, + .ei = qmi_wlanfw_mem_ready_ind_msg_v01_ei, + .decoded_size = sizeof(qmi_wlanfw_mem_ready_ind_msg_v01_ei), + .fn = ath11k_qmi_msg_mem_ready_cb, + }, + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_FW_READY_IND_V01, + .ei = qmi_wlanfw_fw_ready_ind_msg_v01_ei, + .decoded_size = sizeof(qmi_wlanfw_fw_ready_ind_msg_v01_ei), + .fn = ath11k_qmi_msg_fw_ready_cb, + }, + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01, + .ei = qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei, + .decoded_size = + sizeof(qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei), + .fn = ath11k_qmi_msg_cold_boot_cal_done_cb, + }, +}; + +static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl, + struct qmi_service *service) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + struct sockaddr_qrtr *sq = &qmi->sq; + int ret; + + sq->sq_family = AF_QIPCRTR; + sq->sq_node = service->node; + sq->sq_port = service->port; + + ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)sq, + sizeof(*sq), 0); + if (ret) { + ath11k_warn(ab, "qmi failed to connect to remote service %d\n", ret); + return ret; + } + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi wifi fw qmi service connected\n"); + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_SERVER_ARRIVE, NULL); + + return 0; +} + +static void ath11k_qmi_ops_del_server(struct qmi_handle *qmi_hdl, + struct qmi_service *service) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi wifi fw del server\n"); + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_SERVER_EXIT, NULL); +} + +static const struct qmi_ops ath11k_qmi_ops = { + .new_server = ath11k_qmi_ops_new_server, + .del_server = ath11k_qmi_ops_del_server, +}; + +static void ath11k_qmi_driver_event_work(struct work_struct *work) +{ + struct ath11k_qmi *qmi = container_of(work, struct ath11k_qmi, + event_work); + struct ath11k_qmi_driver_event *event; + struct ath11k_base *ab = qmi->ab; + + spin_lock(&qmi->event_lock); + while (!list_empty(&qmi->event_list)) { + event = list_first_entry(&qmi->event_list, + struct ath11k_qmi_driver_event, list); + list_del(&event->list); + spin_unlock(&qmi->event_lock); + + if (test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)) + return; + + switch (event->type) { + case ATH11K_QMI_EVENT_SERVER_ARRIVE: + ath11k_qmi_event_server_arrive(qmi); + break; + case ATH11K_QMI_EVENT_SERVER_EXIT: + set_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); + set_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); + break; + case ATH11K_QMI_EVENT_REQUEST_MEM: + ath11k_qmi_event_mem_request(qmi); + break; + case ATH11K_QMI_EVENT_FW_MEM_READY: + ath11k_qmi_event_load_bdf(qmi); + break; + case ATH11K_QMI_EVENT_FW_READY: + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { + queue_work(ab->workqueue, &ab->restart_work); + break; + } + + ath11k_core_qmi_firmware_ready(ab); + ab->qmi.cal_done = 1; + set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); + + break; + case ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE: + break; + default: + ath11k_warn(ab, "invalid event type: %d", event->type); + break; + } + kfree(event); + spin_lock(&qmi->event_lock); + } + spin_unlock(&qmi->event_lock); +} + +int ath11k_qmi_init_service(struct ath11k_base *ab) +{ + int ret; + + memset(&ab->qmi.target, 0, sizeof(struct target_info)); + memset(&ab->qmi.target_mem, 0, sizeof(struct target_mem_chunk)); + ab->qmi.ab = ab; + + ab->qmi.target_mem_mode = ATH11K_QMI_TARGET_MEM_MODE_DEFAULT; + ret = qmi_handle_init(&ab->qmi.handle, ATH11K_QMI_RESP_LEN_MAX, + &ath11k_qmi_ops, ath11k_qmi_msg_handlers); + if (ret < 0) { + ath11k_warn(ab, "failed to initialize qmi handle\n"); + return ret; + } + + ab->qmi.event_wq = alloc_workqueue("ath11k_qmi_driver_event", + WQ_UNBOUND, 1); + if (!ab->qmi.event_wq) { + ath11k_err(ab, "failed to allocate workqueue\n"); + return -EFAULT; + } + + INIT_LIST_HEAD(&ab->qmi.event_list); + spin_lock_init(&ab->qmi.event_lock); + INIT_WORK(&ab->qmi.event_work, ath11k_qmi_driver_event_work); + + ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01, + ATH11K_QMI_WLFW_SERVICE_VERS_V01, + ATH11K_QMI_WLFW_SERVICE_INS_ID_V01); + if (ret < 0) { + ath11k_warn(ab, "failed to add qmi lookup\n"); + return ret; + } + + return ret; +} + +void ath11k_qmi_deinit_service(struct ath11k_base *ab) +{ + qmi_handle_release(&ab->qmi.handle); + cancel_work_sync(&ab->qmi.event_work); + destroy_workqueue(ab->qmi.event_wq); +} + diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h new file mode 100644 index 000000000000..3f7db642d869 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -0,0 +1,445 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_QMI_H +#define ATH11K_QMI_H + +#include <linux/mutex.h> +#include <linux/soc/qcom/qmi.h> + +#define ATH11K_HOST_VERSION_STRING "WIN" +#define ATH11K_QMI_WLANFW_TIMEOUT_MS 5000 +#define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64 +#define ATH11K_QMI_BDF_ADDRESS 0x4B0C0000 +#define ATH11K_QMI_BDF_MAX_SIZE (256 * 1024) +#define ATH11K_QMI_CALDATA_OFFSET (128 * 1024) +#define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 +#define ATH11K_QMI_WLFW_SERVICE_ID_V01 0x45 +#define ATH11K_QMI_WLFW_SERVICE_VERS_V01 0x01 +#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01 0x02 +#define ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32 +#define ATH11K_QMI_RESP_LEN_MAX 8192 +#define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 32 +#define ATH11K_QMI_CALDB_SIZE 0x480000 +#define ATH11K_QMI_DEFAULT_CAL_FILE_NAME "caldata.bin" + +#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 +#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 +#define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x0021 +#define QMI_WLFW_FW_READY_IND_V01 0x0038 + +#define QMI_WLANFW_MAX_DATA_SIZE_V01 6144 +#define ATH11K_FIRMWARE_MODE_OFF 4 +#define ATH11K_QMI_TARGET_MEM_MODE_DEFAULT 0 + +struct ath11k_base; + +enum ath11k_qmi_file_type { + ATH11K_QMI_FILE_TYPE_BDF_GOLDEN, + ATH11K_QMI_FILE_TYPE_CALDATA, + ATH11K_QMI_MAX_FILE_TYPE, +}; + +enum ath11k_qmi_event_type { + ATH11K_QMI_EVENT_SERVER_ARRIVE, + ATH11K_QMI_EVENT_SERVER_EXIT, + ATH11K_QMI_EVENT_REQUEST_MEM, + ATH11K_QMI_EVENT_FW_MEM_READY, + ATH11K_QMI_EVENT_FW_READY, + ATH11K_QMI_EVENT_COLD_BOOT_CAL_START, + ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE, + ATH11K_QMI_EVENT_REGISTER_DRIVER, + ATH11K_QMI_EVENT_UNREGISTER_DRIVER, + ATH11K_QMI_EVENT_RECOVERY, + ATH11K_QMI_EVENT_FORCE_FW_ASSERT, + ATH11K_QMI_EVENT_POWER_UP, + ATH11K_QMI_EVENT_POWER_DOWN, + ATH11K_QMI_EVENT_MAX, +}; + +struct ath11k_qmi_driver_event { + struct list_head list; + enum ath11k_qmi_event_type type; + void *data; +}; + +struct ath11k_qmi_ce_cfg { + const struct ce_pipe_config *tgt_ce; + int tgt_ce_len; + const struct service_to_pipe *svc_to_ce_map; + int svc_to_ce_map_len; + const u8 *shadow_reg; + int shadow_reg_len; + u8 *shadow_reg_v2; + int shadow_reg_v2_len; +}; + +struct ath11k_qmi_event_msg { + struct list_head list; + enum ath11k_qmi_event_type type; +}; + +struct target_mem_chunk { + u32 size; + u32 type; + dma_addr_t paddr; + u32 vaddr; +}; + +struct target_info { + u32 chip_id; + u32 chip_family; + u32 board_id; + u32 soc_id; + u32 fw_version; + char fw_build_timestamp[ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1]; + char fw_build_id[ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1]; +}; + +struct ath11k_qmi { + struct ath11k_base *ab; + struct qmi_handle handle; + struct sockaddr_qrtr sq; + struct work_struct event_work; + struct workqueue_struct *event_wq; + struct list_head event_list; + spinlock_t event_lock; /* spinlock for qmi event list */ + struct ath11k_qmi_ce_cfg ce_cfg; + struct target_mem_chunk target_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; + u32 mem_seg_count; + u32 target_mem_mode; + u8 cal_done; + struct target_info target; +}; + +#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN 189 +#define QMI_WLANFW_HOST_CAP_REQ_V01 0x0034 +#define QMI_WLANFW_HOST_CAP_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034 +#define QMI_WLFW_MAX_NUM_GPIO_V01 32 +#define QMI_IPQ8074_FW_MEM_MODE 0xFF +#define HOST_DDR_REGION_TYPE 0x1 +#define BDF_MEM_REGION_TYPE 0x2 +#define CALDB_MEM_REGION_TYPE 0x4 + +struct qmi_wlanfw_host_cap_req_msg_v01 { + u8 num_clients_valid; + u32 num_clients; + u8 wake_msi_valid; + u32 wake_msi; + u8 gpios_valid; + u32 gpios_len; + u32 gpios[QMI_WLFW_MAX_NUM_GPIO_V01]; + u8 nm_modem_valid; + u8 nm_modem; + u8 bdf_support_valid; + u8 bdf_support; + u8 bdf_cache_support_valid; + u8 bdf_cache_support; + u8 m3_support_valid; + u8 m3_support; + u8 m3_cache_support_valid; + u8 m3_cache_support; + u8 cal_filesys_support_valid; + u8 cal_filesys_support; + u8 cal_cache_support_valid; + u8 cal_cache_support; + u8 cal_done_valid; + u8 cal_done; + u8 mem_bucket_valid; + u32 mem_bucket; + u8 mem_cfg_mode_valid; + u8 mem_cfg_mode; +}; + +struct qmi_wlanfw_host_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN 54 +#define QMI_WLANFW_IND_REGISTER_REQ_V01 0x0020 +#define QMI_WLANFW_IND_REGISTER_RESP_MSG_V01_MAX_LEN 18 +#define QMI_WLANFW_IND_REGISTER_RESP_V01 0x0020 +#define QMI_WLANFW_CLIENT_ID 0x4b4e454c + +struct qmi_wlanfw_ind_register_req_msg_v01 { + u8 fw_ready_enable_valid; + u8 fw_ready_enable; + u8 initiate_cal_download_enable_valid; + u8 initiate_cal_download_enable; + u8 initiate_cal_update_enable_valid; + u8 initiate_cal_update_enable; + u8 msa_ready_enable_valid; + u8 msa_ready_enable; + u8 pin_connect_result_enable_valid; + u8 pin_connect_result_enable; + u8 client_id_valid; + u32 client_id; + u8 request_mem_enable_valid; + u8 request_mem_enable; + u8 fw_mem_ready_enable_valid; + u8 fw_mem_ready_enable; + u8 fw_init_done_enable_valid; + u8 fw_init_done_enable; + u8 rejuvenate_enable_valid; + u32 rejuvenate_enable; + u8 xo_cal_enable_valid; + u8 xo_cal_enable; + u8 cal_done_enable_valid; + u8 cal_done_enable; +}; + +struct qmi_wlanfw_ind_register_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 fw_status_valid; + u64 fw_status; +}; + +#define QMI_WLANFW_REQUEST_MEM_IND_MSG_V01_MAX_LEN 1124 +#define QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN 548 +#define QMI_WLANFW_RESPOND_MEM_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_REQUEST_MEM_IND_V01 0x0035 +#define QMI_WLANFW_RESPOND_MEM_REQ_V01 0x0036 +#define QMI_WLANFW_RESPOND_MEM_RESP_V01 0x0036 +#define QMI_WLANFW_MAX_NUM_MEM_CFG_V01 2 + +struct qmi_wlanfw_mem_cfg_s_v01 { + u64 offset; + u32 size; + u8 secure_flag; +}; + +enum qmi_wlanfw_mem_type_enum_v01 { + WLANFW_MEM_TYPE_ENUM_MIN_VAL_V01 = INT_MIN, + QMI_WLANFW_MEM_TYPE_MSA_V01 = 0, + QMI_WLANFW_MEM_TYPE_DDR_V01 = 1, + QMI_WLANFW_MEM_BDF_V01 = 2, + QMI_WLANFW_MEM_M3_V01 = 3, + QMI_WLANFW_MEM_CAL_V01 = 4, + QMI_WLANFW_MEM_DPD_V01 = 5, + WLANFW_MEM_TYPE_ENUM_MAX_VAL_V01 = INT_MAX, +}; + +struct qmi_wlanfw_mem_seg_s_v01 { + u32 size; + enum qmi_wlanfw_mem_type_enum_v01 type; + u32 mem_cfg_len; + struct qmi_wlanfw_mem_cfg_s_v01 mem_cfg[QMI_WLANFW_MAX_NUM_MEM_CFG_V01]; +}; + +struct qmi_wlanfw_request_mem_ind_msg_v01 { + u32 mem_seg_len; + struct qmi_wlanfw_mem_seg_s_v01 mem_seg[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; +}; + +struct qmi_wlanfw_mem_seg_resp_s_v01 { + u64 addr; + u32 size; + enum qmi_wlanfw_mem_type_enum_v01 type; + u8 restore; +}; + +struct qmi_wlanfw_respond_mem_req_msg_v01 { + u32 mem_seg_len; + struct qmi_wlanfw_mem_seg_resp_s_v01 mem_seg[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; +}; + +struct qmi_wlanfw_respond_mem_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +struct qmi_wlanfw_fw_mem_ready_ind_msg_v01 { + char placeholder; +}; + +#define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0 +#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 207 +#define QMI_WLANFW_CAP_REQ_V01 0x0024 +#define QMI_WLANFW_CAP_RESP_V01 0x0024 + +enum qmi_wlanfw_pipedir_enum_v01 { + QMI_WLFW_PIPEDIR_NONE_V01 = 0, + QMI_WLFW_PIPEDIR_IN_V01 = 1, + QMI_WLFW_PIPEDIR_OUT_V01 = 2, + QMI_WLFW_PIPEDIR_INOUT_V01 = 3, +}; + +struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01 { + __le32 pipe_num; + __le32 pipe_dir; + __le32 nentries; + __le32 nbytes_max; + __le32 flags; +}; + +struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01 { + __le32 service_id; + __le32 pipe_dir; + __le32 pipe_num; +}; + +struct qmi_wlanfw_shadow_reg_cfg_s_v01 { + u16 id; + u16 offset; +}; + +struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01 { + u32 addr; +}; + +struct qmi_wlanfw_memory_region_info_s_v01 { + u64 region_addr; + u32 size; + u8 secure_flag; +}; + +struct qmi_wlanfw_rf_chip_info_s_v01 { + u32 chip_id; + u32 chip_family; +}; + +struct qmi_wlanfw_rf_board_info_s_v01 { + u32 board_id; +}; + +struct qmi_wlanfw_soc_info_s_v01 { + u32 soc_id; +}; + +struct qmi_wlanfw_fw_version_info_s_v01 { + u32 fw_version; + char fw_build_timestamp[ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1]; +}; + +enum qmi_wlanfw_cal_temp_id_enum_v01 { + QMI_WLANFW_CAL_TEMP_IDX_0_V01 = 0, + QMI_WLANFW_CAL_TEMP_IDX_1_V01 = 1, + QMI_WLANFW_CAL_TEMP_IDX_2_V01 = 2, + QMI_WLANFW_CAL_TEMP_IDX_3_V01 = 3, + QMI_WLANFW_CAL_TEMP_IDX_4_V01 = 4, + QMI_WLANFW_CAL_TEMP_ID_MAX_V01 = 0xFF, +}; + +struct qmi_wlanfw_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 chip_info_valid; + struct qmi_wlanfw_rf_chip_info_s_v01 chip_info; + u8 board_info_valid; + struct qmi_wlanfw_rf_board_info_s_v01 board_info; + u8 soc_info_valid; + struct qmi_wlanfw_soc_info_s_v01 soc_info; + u8 fw_version_info_valid; + struct qmi_wlanfw_fw_version_info_s_v01 fw_version_info; + u8 fw_build_id_valid; + char fw_build_id[ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1]; + u8 num_macs_valid; + u8 num_macs; +}; + +struct qmi_wlanfw_cap_req_msg_v01 { + char placeholder; +}; + +#define QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN 6182 +#define QMI_WLANFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_BDF_DOWNLOAD_RESP_V01 0x0025 +#define QMI_WLANFW_BDF_DOWNLOAD_REQ_V01 0x0025 +/* TODO: Need to check with MCL and FW team that data can be pointer and + * can be last element in structure + */ +struct qmi_wlanfw_bdf_download_req_msg_v01 { + u8 valid; + u8 file_id_valid; + enum qmi_wlanfw_cal_temp_id_enum_v01 file_id; + u8 total_size_valid; + u32 total_size; + u8 seg_id_valid; + u32 seg_id; + u8 data_valid; + u32 data_len; + u8 data[QMI_WLANFW_MAX_DATA_SIZE_V01]; + u8 end_valid; + u8 end; + u8 bdf_type_valid; + u8 bdf_type; + +}; + +struct qmi_wlanfw_bdf_download_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 +#define QMI_WLANFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 +#define QMI_WLANFW_M3_INFO_RESP_V01 0x003C +#define QMI_WLANFW_M3_INFO_REQ_V01 0x003C + +struct qmi_wlanfw_m3_info_req_msg_v01 { + u64 addr; + u32 size; +}; + +struct qmi_wlanfw_m3_info_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN 11 +#define QMI_WLANFW_WLAN_MODE_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN 803 +#define QMI_WLANFW_WLAN_CFG_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_WLAN_MODE_REQ_V01 0x0022 +#define QMI_WLANFW_WLAN_MODE_RESP_V01 0x0022 +#define QMI_WLANFW_WLAN_CFG_REQ_V01 0x0023 +#define QMI_WLANFW_WLAN_CFG_RESP_V01 0x0023 +#define QMI_WLANFW_MAX_STR_LEN_V01 16 +#define QMI_WLANFW_MAX_NUM_CE_V01 12 +#define QMI_WLANFW_MAX_NUM_SVC_V01 24 +#define QMI_WLANFW_MAX_NUM_SHADOW_REG_V01 24 +#define QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01 36 + +struct qmi_wlanfw_wlan_mode_req_msg_v01 { + u32 mode; + u8 hw_debug_valid; + u8 hw_debug; +}; + +struct qmi_wlanfw_wlan_mode_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +struct qmi_wlanfw_wlan_cfg_req_msg_v01 { + u8 host_version_valid; + char host_version[QMI_WLANFW_MAX_STR_LEN_V01 + 1]; + u8 tgt_cfg_valid; + u32 tgt_cfg_len; + struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01 + tgt_cfg[QMI_WLANFW_MAX_NUM_CE_V01]; + u8 svc_cfg_valid; + u32 svc_cfg_len; + struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01 + svc_cfg[QMI_WLANFW_MAX_NUM_SVC_V01]; + u8 shadow_reg_valid; + u32 shadow_reg_len; + struct qmi_wlanfw_shadow_reg_cfg_s_v01 + shadow_reg[QMI_WLANFW_MAX_NUM_SHADOW_REG_V01]; + u8 shadow_reg_v2_valid; + u32 shadow_reg_v2_len; + struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01 + shadow_reg_v2[QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01]; +}; + +struct qmi_wlanfw_wlan_cfg_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +int ath11k_qmi_firmware_start(struct ath11k_base *ab, + u32 mode); +void ath11k_qmi_firmware_stop(struct ath11k_base *ab); +void ath11k_qmi_event_work(struct work_struct *work); +void ath11k_qmi_msg_recv_work(struct work_struct *work); +void ath11k_qmi_deinit_service(struct ath11k_base *ab); +int ath11k_qmi_init_service(struct ath11k_base *ab); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c new file mode 100644 index 000000000000..453aa9c06969 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#include "core.h" +#include "debug.h" + +/* World regdom to be used in case default regd from fw is unavailable */ +#define ATH11K_2GHZ_CH01_11 REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0) +#define ATH11K_5GHZ_5150_5350 REG_RULE(5150 - 10, 5350 + 10, 80, 0, 30,\ + NL80211_RRF_NO_IR) +#define ATH11K_5GHZ_5725_5850 REG_RULE(5725 - 10, 5850 + 10, 80, 0, 30,\ + NL80211_RRF_NO_IR) + +#define ETSI_WEATHER_RADAR_BAND_LOW 5590 +#define ETSI_WEATHER_RADAR_BAND_HIGH 5650 +#define ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT 600000 + +static const struct ieee80211_regdomain ath11k_world_regd = { + .n_reg_rules = 3, + .alpha2 = "00", + .reg_rules = { + ATH11K_2GHZ_CH01_11, + ATH11K_5GHZ_5150_5350, + ATH11K_5GHZ_5725_5850, + } +}; + +static bool ath11k_regdom_changes(struct ath11k *ar, char *alpha2) +{ + const struct ieee80211_regdomain *regd; + + regd = rcu_dereference_rtnl(ar->hw->wiphy->regd); + /* This can happen during wiphy registration where the previous + * user request is received before we update the regd received + * from firmware. + */ + if (!regd) + return true; + + return memcmp(regd->alpha2, alpha2, 2) != 0; +} + +static void +ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct wmi_init_country_params init_country_param; + struct ath11k *ar = hw->priv; + int ret; + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "Regulatory Notification received for %s\n", wiphy_name(wiphy)); + + /* Currently supporting only General User Hints. Cell base user + * hints to be handled later. + * Hints from other sources like Core, Beacons are not expected for + * self managed wiphy's + */ + if (!(request->initiator == NL80211_REGDOM_SET_BY_USER && + request->user_reg_hint_type == NL80211_USER_REG_HINT_USER)) { + ath11k_warn(ar->ab, "Unexpected Regulatory event for this wiphy\n"); + return; + } + + if (!IS_ENABLED(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS)) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "Country Setting is not allowed\n"); + return; + } + + if (!ath11k_regdom_changes(ar, request->alpha2)) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, "Country is already set\n"); + return; + } + + /* Set the country code to the firmware and wait for + * the WMI_REG_CHAN_LIST_CC EVENT for updating the + * reg info + */ + init_country_param.flags = ALPHA_IS_SET; + memcpy(&init_country_param.cc_info.alpha2, request->alpha2, 2); + + ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param); + if (ret) + ath11k_warn(ar->ab, + "INIT Country code set to fw failed : %d\n", ret); +} + +int ath11k_reg_update_chan_list(struct ath11k *ar) +{ + struct ieee80211_supported_band **bands; + struct scan_chan_list_params *params; + struct ieee80211_channel *channel; + struct ieee80211_hw *hw = ar->hw; + struct channel_param *ch; + enum nl80211_band band; + int num_channels = 0; + int params_len; + int i, ret; + + bands = hw->wiphy->bands; + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (!bands[band]) + continue; + + for (i = 0; i < bands[band]->n_channels; i++) { + if (bands[band]->channels[i].flags & + IEEE80211_CHAN_DISABLED) + continue; + + num_channels++; + } + } + + if (WARN_ON(!num_channels)) + return -EINVAL; + + params_len = sizeof(struct scan_chan_list_params) + + num_channels * sizeof(struct channel_param); + params = kzalloc(params_len, GFP_KERNEL); + + if (!params) + return -ENOMEM; + + params->pdev_id = ar->pdev->pdev_id; + params->nallchans = num_channels; + + ch = params->ch_param; + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (!bands[band]) + continue; + + for (i = 0; i < bands[band]->n_channels; i++) { + channel = &bands[band]->channels[i]; + + if (channel->flags & IEEE80211_CHAN_DISABLED) + continue; + + /* TODO: Set to true/false based on some condition? */ + ch->allow_ht = true; + ch->allow_vht = true; + ch->allow_he = true; + + ch->dfs_set = + !!(channel->flags & IEEE80211_CHAN_RADAR); + ch->is_chan_passive = !!(channel->flags & + IEEE80211_CHAN_NO_IR); + ch->is_chan_passive |= ch->dfs_set; + ch->mhz = channel->center_freq; + ch->cfreq1 = channel->center_freq; + ch->minpower = 0; + ch->maxpower = channel->max_power * 2; + ch->maxregpower = channel->max_reg_power * 2; + ch->antennamax = channel->max_antenna_gain * 2; + + /* TODO: Use appropriate phymodes */ + if (channel->band == NL80211_BAND_2GHZ) + ch->phy_mode = MODE_11G; + else + ch->phy_mode = MODE_11A; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "mac channel [%d/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", + i, params->nallchans, + ch->mhz, ch->maxpower, ch->maxregpower, + ch->antennamax, ch->phy_mode); + + ch++; + /* TODO: use quarrter/half rate, cfreq12, dfs_cfreq2 + * set_agile, reg_class_idx + */ + } + } + + ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); + kfree(params); + + return ret; +} + +static void ath11k_copy_regd(struct ieee80211_regdomain *regd_orig, + struct ieee80211_regdomain *regd_copy) +{ + u8 i; + + /* The caller should have checked error conditions */ + memcpy(regd_copy, regd_orig, sizeof(*regd_orig)); + + for (i = 0; i < regd_orig->n_reg_rules; i++) + memcpy(®d_copy->reg_rules[i], ®d_orig->reg_rules[i], + sizeof(struct ieee80211_reg_rule)); +} + +int ath11k_regd_update(struct ath11k *ar, bool init) +{ + struct ieee80211_regdomain *regd, *regd_copy = NULL; + int ret, regd_len, pdev_id; + struct ath11k_base *ab; + + ab = ar->ab; + pdev_id = ar->pdev_idx; + + spin_lock(&ab->base_lock); + + if (init) { + /* Apply the regd received during init through + * WMI_REG_CHAN_LIST_CC event. In case of failure to + * receive the regd, initialize with a default world + * regulatory. + */ + if (ab->default_regd[pdev_id]) { + regd = ab->default_regd[pdev_id]; + } else { + ath11k_warn(ab, + "failed to receive default regd during init\n"); + regd = (struct ieee80211_regdomain *)&ath11k_world_regd; + } + } else { + regd = ab->new_regd[pdev_id]; + } + + if (!regd) { + ret = -EINVAL; + spin_unlock(&ab->base_lock); + goto err; + } + + regd_len = sizeof(*regd) + (regd->n_reg_rules * + sizeof(struct ieee80211_reg_rule)); + + regd_copy = kzalloc(regd_len, GFP_ATOMIC); + if (regd_copy) + ath11k_copy_regd(regd, regd_copy); + + spin_unlock(&ab->base_lock); + + if (!regd_copy) { + ret = -ENOMEM; + goto err; + } + + rtnl_lock(); + ret = regulatory_set_wiphy_regd_sync_rtnl(ar->hw->wiphy, regd_copy); + rtnl_unlock(); + + kfree(regd_copy); + + if (ret) + goto err; + + if (ar->state == ATH11K_STATE_ON) { + ret = ath11k_reg_update_chan_list(ar); + if (ret) + goto err; + } + + return 0; +err: + ath11k_warn(ab, "failed to perform regd update : %d\n", ret); + return ret; +} + +static enum nl80211_dfs_regions +ath11k_map_fw_dfs_region(enum ath11k_dfs_region dfs_region) +{ + switch (dfs_region) { + case ATH11K_DFS_REG_FCC: + case ATH11K_DFS_REG_CN: + return NL80211_DFS_FCC; + case ATH11K_DFS_REG_ETSI: + case ATH11K_DFS_REG_KR: + return NL80211_DFS_ETSI; + case ATH11K_DFS_REG_MKK: + return NL80211_DFS_JP; + default: + return NL80211_DFS_UNSET; + } +} + +static u32 ath11k_map_fw_reg_flags(u16 reg_flags) +{ + u32 flags = 0; + + if (reg_flags & REGULATORY_CHAN_NO_IR) + flags = NL80211_RRF_NO_IR; + + if (reg_flags & REGULATORY_CHAN_RADAR) + flags |= NL80211_RRF_DFS; + + if (reg_flags & REGULATORY_CHAN_NO_OFDM) + flags |= NL80211_RRF_NO_OFDM; + + if (reg_flags & REGULATORY_CHAN_INDOOR_ONLY) + flags |= NL80211_RRF_NO_OUTDOOR; + + if (reg_flags & REGULATORY_CHAN_NO_HT40) + flags |= NL80211_RRF_NO_HT40; + + if (reg_flags & REGULATORY_CHAN_NO_80MHZ) + flags |= NL80211_RRF_NO_80MHZ; + + if (reg_flags & REGULATORY_CHAN_NO_160MHZ) + flags |= NL80211_RRF_NO_160MHZ; + + return flags; +} + +static bool +ath11k_reg_can_intersect(struct ieee80211_reg_rule *rule1, + struct ieee80211_reg_rule *rule2) +{ + u32 start_freq1, end_freq1; + u32 start_freq2, end_freq2; + + start_freq1 = rule1->freq_range.start_freq_khz; + start_freq2 = rule2->freq_range.start_freq_khz; + + end_freq1 = rule1->freq_range.end_freq_khz; + end_freq2 = rule2->freq_range.end_freq_khz; + + if ((start_freq1 >= start_freq2 && + start_freq1 < end_freq2) || + (start_freq2 > start_freq1 && + start_freq2 < end_freq1)) + return true; + + /* TODO: Should we restrict intersection feasibility + * based on min bandwidth of the intersected region also, + * say the intersected rule should have a min bandwidth + * of 20MHz? + */ + + return false; +} + +static void ath11k_reg_intersect_rules(struct ieee80211_reg_rule *rule1, + struct ieee80211_reg_rule *rule2, + struct ieee80211_reg_rule *new_rule) +{ + u32 start_freq1, end_freq1; + u32 start_freq2, end_freq2; + u32 freq_diff, max_bw; + + start_freq1 = rule1->freq_range.start_freq_khz; + start_freq2 = rule2->freq_range.start_freq_khz; + + end_freq1 = rule1->freq_range.end_freq_khz; + end_freq2 = rule2->freq_range.end_freq_khz; + + new_rule->freq_range.start_freq_khz = max_t(u32, start_freq1, + start_freq2); + new_rule->freq_range.end_freq_khz = min_t(u32, end_freq1, end_freq2); + + freq_diff = new_rule->freq_range.end_freq_khz - + new_rule->freq_range.start_freq_khz; + max_bw = min_t(u32, rule1->freq_range.max_bandwidth_khz, + rule2->freq_range.max_bandwidth_khz); + new_rule->freq_range.max_bandwidth_khz = min_t(u32, max_bw, freq_diff); + + new_rule->power_rule.max_antenna_gain = + min_t(u32, rule1->power_rule.max_antenna_gain, + rule2->power_rule.max_antenna_gain); + + new_rule->power_rule.max_eirp = min_t(u32, rule1->power_rule.max_eirp, + rule2->power_rule.max_eirp); + + /* Use the flags of both the rules */ + new_rule->flags = rule1->flags | rule2->flags; + + /* To be safe, lts use the max cac timeout of both rules */ + new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms, + rule2->dfs_cac_ms); +} + +static struct ieee80211_regdomain * +ath11k_regd_intersect(struct ieee80211_regdomain *default_regd, + struct ieee80211_regdomain *curr_regd) +{ + u8 num_old_regd_rules, num_curr_regd_rules, num_new_regd_rules; + struct ieee80211_reg_rule *old_rule, *curr_rule, *new_rule; + struct ieee80211_regdomain *new_regd = NULL; + u8 i, j, k; + + num_old_regd_rules = default_regd->n_reg_rules; + num_curr_regd_rules = curr_regd->n_reg_rules; + num_new_regd_rules = 0; + + /* Find the number of intersecting rules to allocate new regd memory */ + for (i = 0; i < num_old_regd_rules; i++) { + old_rule = default_regd->reg_rules + i; + for (j = 0; j < num_curr_regd_rules; j++) { + curr_rule = curr_regd->reg_rules + j; + + if (ath11k_reg_can_intersect(old_rule, curr_rule)) + num_new_regd_rules++; + } + } + + if (!num_new_regd_rules) + return NULL; + + new_regd = kzalloc(sizeof(*new_regd) + (num_new_regd_rules * + sizeof(struct ieee80211_reg_rule)), + GFP_ATOMIC); + + if (!new_regd) + return NULL; + + /* We set the new country and dfs region directly and only trim + * the freq, power, antenna gain by intersecting with the + * default regdomain. Also MAX of the dfs cac timeout is selected. + */ + new_regd->n_reg_rules = num_new_regd_rules; + memcpy(new_regd->alpha2, curr_regd->alpha2, sizeof(new_regd->alpha2)); + new_regd->dfs_region = curr_regd->dfs_region; + new_rule = new_regd->reg_rules; + + for (i = 0, k = 0; i < num_old_regd_rules; i++) { + old_rule = default_regd->reg_rules + i; + for (j = 0; j < num_curr_regd_rules; j++) { + curr_rule = curr_regd->reg_rules + j; + + if (ath11k_reg_can_intersect(old_rule, curr_rule)) + ath11k_reg_intersect_rules(old_rule, curr_rule, + (new_rule + k++)); + } + } + return new_regd; +} + +static const char * +ath11k_reg_get_regdom_str(enum nl80211_dfs_regions dfs_region) +{ + switch (dfs_region) { + case NL80211_DFS_FCC: + return "FCC"; + case NL80211_DFS_ETSI: + return "ETSI"; + case NL80211_DFS_JP: + return "JP"; + default: + return "UNSET"; + } +} + +static u16 +ath11k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw) +{ + u16 bw; + + bw = end_freq - start_freq; + bw = min_t(u16, bw, max_bw); + + if (bw >= 80 && bw < 160) + bw = 80; + else if (bw >= 40 && bw < 80) + bw = 40; + else if (bw < 40) + bw = 20; + + return bw; +} + +static void +ath11k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq, + u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr, + u32 reg_flags) +{ + reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq); + reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq); + reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw); + reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain); + reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr); + reg_rule->flags = reg_flags; +} + +static void +ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, + struct ieee80211_regdomain *regd, + struct cur_reg_rule *reg_rule, + u8 *rule_idx, u32 flags, u16 max_bw) +{ + u32 end_freq; + u16 bw; + u8 i; + + i = *rule_idx; + + bw = ath11k_reg_adjust_bw(reg_rule->start_freq, + ETSI_WEATHER_RADAR_BAND_LOW, max_bw); + + ath11k_reg_update_rule(regd->reg_rules + i, reg_rule->start_freq, + ETSI_WEATHER_RADAR_BAND_LOW, bw, + reg_rule->ant_gain, reg_rule->reg_power, + flags); + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, reg_rule->start_freq, ETSI_WEATHER_RADAR_BAND_LOW, + bw, reg_rule->ant_gain, reg_rule->reg_power, + regd->reg_rules[i].dfs_cac_ms, + flags); + + if (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_HIGH) + end_freq = ETSI_WEATHER_RADAR_BAND_HIGH; + else + end_freq = reg_rule->end_freq; + + bw = ath11k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_LOW, end_freq, + max_bw); + + i++; + + ath11k_reg_update_rule(regd->reg_rules + i, + ETSI_WEATHER_RADAR_BAND_LOW, end_freq, bw, + reg_rule->ant_gain, reg_rule->reg_power, + flags); + + regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT; + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, ETSI_WEATHER_RADAR_BAND_LOW, end_freq, + bw, reg_rule->ant_gain, reg_rule->reg_power, + regd->reg_rules[i].dfs_cac_ms, + flags); + + if (end_freq == reg_rule->end_freq) { + regd->n_reg_rules--; + *rule_idx = i; + return; + } + + bw = ath11k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_HIGH, + reg_rule->end_freq, max_bw); + + i++; + + ath11k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_HIGH, + reg_rule->end_freq, bw, + reg_rule->ant_gain, reg_rule->reg_power, + flags); + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, ETSI_WEATHER_RADAR_BAND_HIGH, reg_rule->end_freq, + bw, reg_rule->ant_gain, reg_rule->reg_power, + regd->reg_rules[i].dfs_cac_ms, + flags); + + *rule_idx = i; +} + +struct ieee80211_regdomain * +ath11k_reg_build_regd(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, bool intersect) +{ + struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL; + struct cur_reg_rule *reg_rule; + u8 i = 0, j = 0; + u8 num_rules; + u16 max_bw; + u32 flags; + char alpha2[3]; + + num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules; + + if (!num_rules) + goto ret; + + /* Add max additional rules to accommodate weather radar band */ + if (reg_info->dfs_region == ATH11K_DFS_REG_ETSI) + num_rules += 2; + + tmp_regd = kzalloc(sizeof(*tmp_regd) + + (num_rules * sizeof(struct ieee80211_reg_rule)), + GFP_ATOMIC); + if (!tmp_regd) + goto ret; + + tmp_regd->n_reg_rules = num_rules; + memcpy(tmp_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); + memcpy(alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); + alpha2[2] = '\0'; + tmp_regd->dfs_region = ath11k_map_fw_dfs_region(reg_info->dfs_region); + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", + alpha2, ath11k_reg_get_regdom_str(tmp_regd->dfs_region), + reg_info->dfs_region, num_rules); + /* Update reg_rules[] below. Firmware is expected to + * send these rules in order(2G rules first and then 5G) + */ + for (; i < tmp_regd->n_reg_rules; i++) { + if (reg_info->num_2g_reg_rules && + (i < reg_info->num_2g_reg_rules)) { + reg_rule = reg_info->reg_rules_2g_ptr + i; + max_bw = min_t(u16, reg_rule->max_bw, + reg_info->max_bw_2g); + flags = 0; + } else if (reg_info->num_5g_reg_rules && + (j < reg_info->num_5g_reg_rules)) { + reg_rule = reg_info->reg_rules_5g_ptr + j++; + max_bw = min_t(u16, reg_rule->max_bw, + reg_info->max_bw_5g); + + /* FW doesn't pass NL80211_RRF_AUTO_BW flag for + * BW Auto correction, we can enable this by default + * for all 5G rules here. The regulatory core performs + * BW correction if required and applies flags as + * per other BW rule flags we pass from here + */ + flags = NL80211_RRF_AUTO_BW; + } else { + break; + } + + flags |= ath11k_map_fw_reg_flags(reg_rule->flags); + + ath11k_reg_update_rule(tmp_regd->reg_rules + i, + reg_rule->start_freq, + reg_rule->end_freq, max_bw, + reg_rule->ant_gain, reg_rule->reg_power, + flags); + + /* Update dfs cac timeout if the dfs domain is ETSI and the + * new rule covers weather radar band. + * Default value of '0' corresponds to 60s timeout, so no + * need to update that for other rules. + */ + if (flags & NL80211_RRF_DFS && + reg_info->dfs_region == ATH11K_DFS_REG_ETSI && + (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_LOW && + reg_rule->start_freq < ETSI_WEATHER_RADAR_BAND_HIGH)){ + ath11k_reg_update_weather_radar_band(ab, tmp_regd, + reg_rule, &i, + flags, max_bw); + continue; + } + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, reg_rule->start_freq, reg_rule->end_freq, + max_bw, reg_rule->ant_gain, reg_rule->reg_power, + tmp_regd->reg_rules[i].dfs_cac_ms, + flags); + } + + if (intersect) { + default_regd = ab->default_regd[reg_info->phy_id]; + + /* Get a new regd by intersecting the received regd with + * our default regd. + */ + new_regd = ath11k_regd_intersect(default_regd, tmp_regd); + kfree(tmp_regd); + if (!new_regd) { + ath11k_warn(ab, "Unable to create intersected regdomain\n"); + goto ret; + } + } else { + new_regd = tmp_regd; + } + +ret: + return new_regd; +} + +void ath11k_regd_update_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, + regd_update_work); + int ret; + + ret = ath11k_regd_update(ar, false); + if (ret) { + /* Firmware has already moved to the new regd. We need + * to maintain channel consistency across FW, Host driver + * and userspace. Hence as a fallback mechanism we can set + * the prev or default country code to the firmware. + */ + /* TODO: Implement Fallback Mechanism */ + } +} + +void ath11k_reg_init(struct ath11k *ar) +{ + ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED; + ar->hw->wiphy->reg_notifier = ath11k_reg_notifier; +} + +void ath11k_reg_free(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < MAX_RADIOS; i++) { + kfree(ab->default_regd[i]); + kfree(ab->new_regd[i]); + } +} diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h new file mode 100644 index 000000000000..39b7fc943541 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_REG_H +#define ATH11K_REG_H + +#include <linux/kernel.h> +#include <net/regulatory.h> + +struct ath11k_base; +struct ath11k; + +/* DFS regdomains supported by Firmware */ +enum ath11k_dfs_region { + ATH11K_DFS_REG_UNSET, + ATH11K_DFS_REG_FCC, + ATH11K_DFS_REG_ETSI, + ATH11K_DFS_REG_MKK, + ATH11K_DFS_REG_CN, + ATH11K_DFS_REG_KR, + ATH11K_DFS_REG_UNDEF, +}; + +/* ATH11K Regulatory API's */ +void ath11k_reg_init(struct ath11k *ar); +void ath11k_reg_free(struct ath11k_base *ab); +void ath11k_regd_update_work(struct work_struct *work); +struct ieee80211_regdomain * +ath11k_reg_build_regd(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, bool intersect); +int ath11k_regd_update(struct ath11k *ar, bool init); +int ath11k_reg_update_chan_list(struct ath11k *ar); +#endif diff --git a/drivers/net/wireless/ath/ath11k/rx_desc.h b/drivers/net/wireless/ath/ath11k/rx_desc.h new file mode 100644 index 000000000000..a5aff801f17f --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/rx_desc.h @@ -0,0 +1,1212 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#ifndef ATH11K_RX_DESC_H +#define ATH11K_RX_DESC_H + +enum rx_desc_rxpcu_filter { + RX_DESC_RXPCU_FILTER_PASS, + RX_DESC_RXPCU_FILTER_MONITOR_CLIENT, + RX_DESC_RXPCU_FILTER_MONITOR_OTHER, +}; + +/* rxpcu_filter_pass + * This MPDU passed the normal frame filter programming of rxpcu. + * + * rxpcu_filter_monitor_client + * This MPDU did not pass the regular frame filter and would + * have been dropped, were it not for the frame fitting into the + * 'monitor_client' category. + * + * rxpcu_filter_monitor_other + * This MPDU did not pass the regular frame filter and also did + * not pass the rxpcu_monitor_client filter. It would have been + * dropped accept that it did pass the 'monitor_other' category. + */ + +#define RX_DESC_INFO0_RXPCU_MPDU_FITLER GENMASK(1, 0) +#define RX_DESC_INFO0_SW_FRAME_GRP_ID GENMASK(8, 2) + +enum rx_desc_sw_frame_grp_id { + RX_DESC_SW_FRAME_GRP_ID_NDP_FRAME, + RX_DESC_SW_FRAME_GRP_ID_MCAST_DATA, + RX_DESC_SW_FRAME_GRP_ID_UCAST_DATA, + RX_DESC_SW_FRAME_GRP_ID_NULL_DATA, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0000, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0001, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0010, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0011, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0100, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0101, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0110, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0111, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1000, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1001, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1010, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1011, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1100, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1101, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1110, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1111, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0000, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0001, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0010, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0011, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0100, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0101, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0110, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0111, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1000, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1001, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1010, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1011, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1100, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1101, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1110, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1111, + RX_DESC_SW_FRAME_GRP_ID_UNSUPPORTED, + RX_DESC_SW_FRAME_GRP_ID_PHY_ERR, +}; + +enum rx_desc_decap_type { + RX_DESC_DECAP_TYPE_RAW, + RX_DESC_DECAP_TYPE_NATIVE_WIFI, + RX_DESC_DECAP_TYPE_ETHERNET2_DIX, + RX_DESC_DECAP_TYPE_8023, +}; + +enum rx_desc_decrypt_status_code { + RX_DESC_DECRYPT_STATUS_CODE_OK, + RX_DESC_DECRYPT_STATUS_CODE_UNPROTECTED_FRAME, + RX_DESC_DECRYPT_STATUS_CODE_DATA_ERR, + RX_DESC_DECRYPT_STATUS_CODE_KEY_INVALID, + RX_DESC_DECRYPT_STATUS_CODE_PEER_ENTRY_INVALID, + RX_DESC_DECRYPT_STATUS_CODE_OTHER, +}; + +#define RX_ATTENTION_INFO1_FIRST_MPDU BIT(0) +#define RX_ATTENTION_INFO1_RSVD_1A BIT(1) +#define RX_ATTENTION_INFO1_MCAST_BCAST BIT(2) +#define RX_ATTENTION_INFO1_AST_IDX_NOT_FOUND BIT(3) +#define RX_ATTENTION_INFO1_AST_IDX_TIMEDOUT BIT(4) +#define RX_ATTENTION_INFO1_POWER_MGMT BIT(5) +#define RX_ATTENTION_INFO1_NON_QOS BIT(6) +#define RX_ATTENTION_INFO1_NULL_DATA BIT(7) +#define RX_ATTENTION_INFO1_MGMT_TYPE BIT(8) +#define RX_ATTENTION_INFO1_CTRL_TYPE BIT(9) +#define RX_ATTENTION_INFO1_MORE_DATA BIT(10) +#define RX_ATTENTION_INFO1_EOSP BIT(11) +#define RX_ATTENTION_INFO1_A_MSDU_ERROR BIT(12) +#define RX_ATTENTION_INFO1_FRAGMENT BIT(13) +#define RX_ATTENTION_INFO1_ORDER BIT(14) +#define RX_ATTENTION_INFO1_CCE_MATCH BIT(15) +#define RX_ATTENTION_INFO1_OVERFLOW_ERR BIT(16) +#define RX_ATTENTION_INFO1_MSDU_LEN_ERR BIT(17) +#define RX_ATTENTION_INFO1_TCP_UDP_CKSUM_FAIL BIT(18) +#define RX_ATTENTION_INFO1_IP_CKSUM_FAIL BIT(19) +#define RX_ATTENTION_INFO1_SA_IDX_INVALID BIT(20) +#define RX_ATTENTION_INFO1_DA_IDX_INVALID BIT(21) +#define RX_ATTENTION_INFO1_RSVD_1B BIT(22) +#define RX_ATTENTION_INFO1_RX_IN_TX_DECRYPT_BYP BIT(23) +#define RX_ATTENTION_INFO1_ENCRYPT_REQUIRED BIT(24) +#define RX_ATTENTION_INFO1_DIRECTED BIT(25) +#define RX_ATTENTION_INFO1_BUFFER_FRAGMENT BIT(26) +#define RX_ATTENTION_INFO1_MPDU_LEN_ERR BIT(27) +#define RX_ATTENTION_INFO1_TKIP_MIC_ERR BIT(28) +#define RX_ATTENTION_INFO1_DECRYPT_ERR BIT(29) +#define RX_ATTENTION_INFO1_UNDECRYPT_FRAME_ERR BIT(30) +#define RX_ATTENTION_INFO1_FCS_ERR BIT(31) + +#define RX_ATTENTION_INFO2_FLOW_IDX_TIMEOUT BIT(0) +#define RX_ATTENTION_INFO2_FLOW_IDX_INVALID BIT(1) +#define RX_ATTENTION_INFO2_WIFI_PARSER_ERR BIT(2) +#define RX_ATTENTION_INFO2_AMSDU_PARSER_ERR BIT(3) +#define RX_ATTENTION_INFO2_SA_IDX_TIMEOUT BIT(4) +#define RX_ATTENTION_INFO2_DA_IDX_TIMEOUT BIT(5) +#define RX_ATTENTION_INFO2_MSDU_LIMIT_ERR BIT(6) +#define RX_ATTENTION_INFO2_DA_IS_VALID BIT(7) +#define RX_ATTENTION_INFO2_DA_IS_MCBC BIT(8) +#define RX_ATTENTION_INFO2_SA_IS_VALID BIT(9) +#define RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE GENMASK(12, 10) +#define RX_ATTENTION_INFO2_RX_BITMAP_NOT_UPDED BIT(13) +#define RX_ATTENTION_INFO2_MSDU_DONE BIT(31) + +struct rx_attention { + __le16 info0; + __le16 phy_ppdu_id; + __le32 info1; + __le32 info2; +} __packed; + +/* rx_attention + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * first_mpdu + * Indicates the first MSDU of the PPDU. If both first_mpdu + * and last_mpdu are set in the MSDU then this is a not an + * A-MPDU frame but a stand alone MPDU. Interior MPDU in an + * A-MPDU shall have both first_mpdu and last_mpdu bits set to + * 0. The PPDU start status will only be valid when this bit + * is set. + * + * mcast_bcast + * Multicast / broadcast indicator. Only set when the MAC + * address 1 bit 0 is set indicating mcast/bcast and the BSSID + * matches one of the 4 BSSID registers. Only set when + * first_msdu is set. + * + * ast_index_not_found + * Only valid when first_msdu is set. Indicates no AST matching + * entries within the the max search count. + * + * ast_index_timeout + * Only valid when first_msdu is set. Indicates an unsuccessful + * search in the address search table due to timeout. + * + * power_mgmt + * Power management bit set in the 802.11 header. Only set + * when first_msdu is set. + * + * non_qos + * Set if packet is not a non-QoS data frame. Only set when + * first_msdu is set. + * + * null_data + * Set if frame type indicates either null data or QoS null + * data format. Only set when first_msdu is set. + * + * mgmt_type + * Set if packet is a management packet. Only set when + * first_msdu is set. + * + * ctrl_type + * Set if packet is a control packet. Only set when first_msdu + * is set. + * + * more_data + * Set if more bit in frame control is set. Only set when + * first_msdu is set. + * + * eosp + * Set if the EOSP (end of service period) bit in the QoS + * control field is set. Only set when first_msdu is set. + * + * a_msdu_error + * Set if number of MSDUs in A-MSDU is above a threshold or if the + * size of the MSDU is invalid. This receive buffer will contain + * all of the remainder of MSDUs in this MPDU w/o decapsulation. + * + * fragment + * Indicates that this is an 802.11 fragment frame. This is + * set when either the more_frag bit is set in the frame + * control or the fragment number is not zero. Only set when + * first_msdu is set. + * + * order + * Set if the order bit in the frame control is set. Only set + * when first_msdu is set. + * + * cce_match + * Indicates that this status has a corresponding MSDU that + * requires FW processing. The OLE will have classification + * ring mask registers which will indicate the ring(s) for + * packets and descriptors which need FW attention. + * + * overflow_err + * PCU Receive FIFO does not have enough space to store the + * full receive packet. Enough space is reserved in the + * receive FIFO for the status is written. This MPDU remaining + * packets in the PPDU will be filtered and no Ack response + * will be transmitted. + * + * msdu_length_err + * Indicates that the MSDU length from the 802.3 encapsulated + * length field extends beyond the MPDU boundary. + * + * tcp_udp_chksum_fail + * Indicates that the computed checksum (tcp_udp_chksum) did + * not match the checksum in the TCP/UDP header. + * + * ip_chksum_fail + * Indicates that the computed checksum did not match the + * checksum in the IP header. + * + * sa_idx_invalid + * Indicates no matching entry was found in the address search + * table for the source MAC address. + * + * da_idx_invalid + * Indicates no matching entry was found in the address search + * table for the destination MAC address. + * + * rx_in_tx_decrypt_byp + * Indicates that RX packet is not decrypted as Crypto is busy + * with TX packet processing. + * + * encrypt_required + * Indicates that this data type frame is not encrypted even if + * the policy for this MPDU requires encryption as indicated in + * the peer table key type. + * + * directed + * MPDU is a directed packet which means that the RA matched + * our STA addresses. In proxySTA it means that the TA matched + * an entry in our address search table with the corresponding + * 'no_ack' bit is the address search entry cleared. + * + * buffer_fragment + * Indicates that at least one of the rx buffers has been + * fragmented. If set the FW should look at the rx_frag_info + * descriptor described below. + * + * mpdu_length_err + * Indicates that the MPDU was pre-maturely terminated + * resulting in a truncated MPDU. Don't trust the MPDU length + * field. + * + * tkip_mic_err + * Indicates that the MPDU Michael integrity check failed + * + * decrypt_err + * Indicates that the MPDU decrypt integrity check failed + * + * fcs_err + * Indicates that the MPDU FCS check failed + * + * flow_idx_timeout + * Indicates an unsuccessful flow search due to the expiring of + * the search timer. + * + * flow_idx_invalid + * flow id is not valid. + * + * amsdu_parser_error + * A-MSDU could not be properly de-agregated. + * + * sa_idx_timeout + * Indicates an unsuccessful search for the source MAC address + * due to the expiring of the search timer. + * + * da_idx_timeout + * Indicates an unsuccessful search for the destination MAC + * address due to the expiring of the search timer. + * + * msdu_limit_error + * Indicates that the MSDU threshold was exceeded and thus + * all the rest of the MSDUs will not be scattered and will not + * be decasulated but will be DMA'ed in RAW format as a single + * MSDU buffer. + * + * da_is_valid + * Indicates that OLE found a valid DA entry. + * + * da_is_mcbc + * Field Only valid if da_is_valid is set. Indicates the DA address + * was a Multicast or Broadcast address. + * + * sa_is_valid + * Indicates that OLE found a valid SA entry. + * + * decrypt_status_code + * Field provides insight into the decryption performed. Values are + * defined in enum %RX_DESC_DECRYPT_STATUS_CODE*. + * + * rx_bitmap_not_updated + * Frame is received, but RXPCU could not update the receive bitmap + * due to (temporary) fifo constraints. + * + * msdu_done + * If set indicates that the RX packet data, RX header data, RX + * PPDU start descriptor, RX MPDU start/end descriptor, RX MSDU + * start/end descriptors and RX Attention descriptor are all + * valid. This bit must be in the last octet of the + * descriptor. + */ + +#define RX_MPDU_START_INFO0_NDP_FRAME BIT(9) +#define RX_MPDU_START_INFO0_PHY_ERR BIT(10) +#define RX_MPDU_START_INFO0_PHY_ERR_MPDU_HDR BIT(11) +#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_DUR_VALID BIT(1) +#define RX_MPDU_START_INFO1_MAC_ADDR1_VALID BIT(2) +#define RX_MPDU_START_INFO1_MAC_ADDR2_VALID BIT(3) +#define RX_MPDU_START_INFO1_MAC_ADDR3_VALID BIT(4) +#define RX_MPDU_START_INFO1_MAC_ADDR4_VALID BIT(5) +#define RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID BIT(6) +#define RX_MPDU_START_INFO1_MPDU_QOS_CTRL_VALID BIT(7) +#define RX_MPDU_START_INFO1_MPDU_HT_CTRL_VALID BIT(8) +#define RX_MPDU_START_INFO1_ENCRYPT_INFO_VALID BIT(9) +#define RX_MPDU_START_INFO1_MPDU_FRAG_NUMBER GENMASK(13, 10) +#define RX_MPDU_START_INFO1_MORE_FRAG_FLAG BIT(14) +#define RX_MPDU_START_INFO1_FROM_DS BIT(16) +#define RX_MPDU_START_INFO1_TO_DS BIT(17) +#define RX_MPDU_START_INFO1_ENCRYPTED BIT(18) +#define RX_MPDU_START_INFO1_MPDU_RETRY BIT(19) +#define RX_MPDU_START_INFO1_MPDU_SEQ_NUM GENMASK(31, 20) + +#define RX_MPDU_START_INFO2_EPD_EN BIT(0) +#define RX_MPDU_START_INFO2_ALL_FRAME_ENCPD BIT(1) +#define RX_MPDU_START_INFO2_ENC_TYPE GENMASK(5, 2) +#define RX_MPDU_START_INFO2_VAR_WEP_KEY_WIDTH GENMASK(7, 6) +#define RX_MPDU_START_INFO2_MESH_STA BIT(8) +#define RX_MPDU_START_INFO2_BSSID_HIT BIT(9) +#define RX_MPDU_START_INFO2_BSSID_NUM GENMASK(13, 10) +#define RX_MPDU_START_INFO2_TID GENMASK(17, 14) + +#define RX_MPDU_START_INFO3_REO_DEST_IND GENMASK(4, 0) +#define RX_MPDU_START_INFO3_FLOW_ID_TOEPLITZ BIT(7) +#define RX_MPDU_START_INFO3_PKT_SEL_FP_UCAST_DATA BIT(8) +#define RX_MPDU_START_INFO3_PKT_SEL_FP_MCAST_DATA BIT(9) +#define RX_MPDU_START_INFO3_PKT_SEL_FP_CTRL_BAR BIT(10) +#define RX_MPDU_START_INFO3_RXDMA0_SRC_RING_SEL GENMASK(12, 11) +#define RX_MPDU_START_INFO3_RXDMA0_DST_RING_SEL GENMASK(14, 13) + +#define RX_MPDU_START_INFO4_REO_QUEUE_DESC_HI GENMASK(7, 0) +#define RX_MPDU_START_INFO4_RECV_QUEUE_NUM GENMASK(23, 8) +#define RX_MPDU_START_INFO4_PRE_DELIM_ERR_WARN BIT(24) +#define RX_MPDU_START_INFO4_FIRST_DELIM_ERR BIT(25) + +#define RX_MPDU_START_INFO5_KEY_ID GENMASK(7, 0) +#define RX_MPDU_START_INFO5_NEW_PEER_ENTRY BIT(8) +#define RX_MPDU_START_INFO5_DECRYPT_NEEDED BIT(9) +#define RX_MPDU_START_INFO5_DECAP_TYPE GENMASK(11, 10) +#define RX_MPDU_START_INFO5_VLAN_TAG_C_PADDING BIT(12) +#define RX_MPDU_START_INFO5_VLAN_TAG_S_PADDING BIT(13) +#define RX_MPDU_START_INFO5_STRIP_VLAN_TAG_C BIT(14) +#define RX_MPDU_START_INFO5_STRIP_VLAN_TAG_S BIT(15) +#define RX_MPDU_START_INFO5_PRE_DELIM_COUNT GENMASK(27, 16) +#define RX_MPDU_START_INFO5_AMPDU_FLAG BIT(28) +#define RX_MPDU_START_INFO5_BAR_FRAME BIT(29) + +#define RX_MPDU_START_INFO6_MPDU_LEN GENMASK(13, 0) +#define RX_MPDU_START_INFO6_FIRST_MPDU BIT(14) +#define RX_MPDU_START_INFO6_MCAST_BCAST BIT(15) +#define RX_MPDU_START_INFO6_AST_IDX_NOT_FOUND BIT(16) +#define RX_MPDU_START_INFO6_AST_IDX_TIMEOUT BIT(17) +#define RX_MPDU_START_INFO6_POWER_MGMT BIT(18) +#define RX_MPDU_START_INFO6_NON_QOS BIT(19) +#define RX_MPDU_START_INFO6_NULL_DATA BIT(20) +#define RX_MPDU_START_INFO6_MGMT_TYPE BIT(21) +#define RX_MPDU_START_INFO6_CTRL_TYPE BIT(22) +#define RX_MPDU_START_INFO6_MORE_DATA BIT(23) +#define RX_MPDU_START_INFO6_EOSP BIT(24) +#define RX_MPDU_START_INFO6_FRAGMENT BIT(25) +#define RX_MPDU_START_INFO6_ORDER BIT(26) +#define RX_MPDU_START_INFO6_UAPSD_TRIGGER BIT(27) +#define RX_MPDU_START_INFO6_ENCRYPT_REQUIRED BIT(28) +#define RX_MPDU_START_INFO6_DIRECTED BIT(29) + +#define RX_MPDU_START_RAW_MPDU BIT(0) + +struct rx_mpdu_start { + __le16 info0; + __le16 phy_ppdu_id; + __le16 ast_index; + __le16 sw_peer_id; + __le32 info1; + __le32 info2; + __le32 pn[4]; + __le32 peer_meta_data; + __le32 info3; + __le32 reo_queue_desc_lo; + __le32 info4; + __le32 info5; + __le32 info6; + __le16 frame_ctrl; + __le16 duration; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctrl; + u8 addr4[ETH_ALEN]; + __le16 qos_ctrl; + __le32 ht_ctrl; + __le32 raw; +} __packed; + +/* rx_mpdu_start + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * Note: for ndp frame, if it was expected because the preceding + * NDPA was filter_pass, the setting rxpcu_filter_pass will be + * used. This setting will also be used for every ndp frame in + * case Promiscuous mode is enabled. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * ndp_frame + * Indicates that the received frame was an NDP frame. + * + * phy_err + * Indicates that PHY error was received before MAC received data. + * + * phy_err_during_mpdu_header + * PHY error was received before MAC received the complete MPDU + * header which was needed for proper decoding. + * + * protocol_version_err + * RXPCU detected a version error in the frame control field. + * + * ast_based_lookup_valid + * AST based lookup for this frame has found a valid result. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * ast_index + * This field indicates the index of the AST entry corresponding + * to this MPDU. It is provided by the GSE module instantiated in + * RXPCU. A value of 0xFFFF indicates an invalid AST index. + * + * sw_peer_id + * This field indicates a unique peer identifier. It is set equal + * to field 'sw_peer_id' from the AST entry. + * + * mpdu_frame_control_valid, mpdu_duration_valid, mpdu_qos_control_valid, + * mpdu_ht_control_valid, frame_encryption_info_valid + * Indicates that each fields have valid entries. + * + * mac_addr_adx_valid + * Corresponding mac_addr_adx_{lo/hi} has valid entries. + * + * from_ds, to_ds + * Valid only when mpdu_frame_control_valid is set. Indicates that + * frame is received from DS and sent to DS. + * + * encrypted + * Protected bit from the frame control. + * + * mpdu_retry + * Retry bit from frame control. Only valid when first_msdu is set. + * + * mpdu_sequence_number + * The sequence number from the 802.11 header. + * + * epd_en + * If set, use EPD instead of LPD. + * + * all_frames_shall_be_encrypted + * If set, all frames (data only?) shall be encrypted. If not, + * RX CRYPTO shall set an error flag. + * + * encrypt_type + * Values are defined in enum %HAL_ENCRYPT_TYPE_. + * + * mesh_sta + * Indicates a Mesh (11s) STA. + * + * bssid_hit + * BSSID of the incoming frame matched one of the 8 BSSID + * register values. + * + * bssid_number + * This number indicates which one out of the 8 BSSID register + * values matched the incoming frame. + * + * tid + * TID field in the QoS control field + * + * pn + * The PN number. + * + * peer_meta_data + * Meta data that SW has programmed in the Peer table entry + * of the transmitting STA. + * + * rx_reo_queue_desc_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * rx_reo_queue_desc_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * receive_queue_number + * Indicates the MPDU queue ID to which this MPDU link + * descriptor belongs. + * + * pre_delim_err_warning + * Indicates that a delimiter FCS error was found in between the + * previous MPDU and this MPDU. Note that this is just a warning, + * and does not mean that this MPDU is corrupted in any way. If + * it is, there will be other errors indicated such as FCS or + * decrypt errors. + * + * first_delim_err + * Indicates that the first delimiter had a FCS failure. + * + * key_id + * The key ID octet from the IV. + * + * new_peer_entry + * Set if new RX_PEER_ENTRY TLV follows. If clear, RX_PEER_ENTRY + * doesn't follow so RX DECRYPTION module either uses old peer + * entry or not decrypt. + * + * decrypt_needed + * When RXPCU sets bit 'ast_index_not_found or ast_index_timeout', + * RXPCU will also ensure that this bit is NOT set. CRYPTO for that + * reason only needs to evaluate this bit and non of the other ones + * + * decap_type + * Used by the OLE during decapsulation. Values are defined in + * enum %MPDU_START_DECAP_TYPE_*. + * + * rx_insert_vlan_c_tag_padding + * rx_insert_vlan_s_tag_padding + * Insert 4 byte of all zeros as VLAN tag or double VLAN tag if + * the rx payload does not have VLAN. + * + * strip_vlan_c_tag_decap + * strip_vlan_s_tag_decap + * Strip VLAN or double VLAN during decapsulation. + * + * pre_delim_count + * The number of delimiters before this MPDU. Note that this + * number is cleared at PPDU start. If this MPDU is the first + * received MPDU in the PPDU and this MPDU gets filtered-in, + * this field will indicate the number of delimiters located + * after the last MPDU in the previous PPDU. + * + * If this MPDU is located after the first received MPDU in + * an PPDU, this field will indicate the number of delimiters + * located between the previous MPDU and this MPDU. + * + * ampdu_flag + * Received frame was part of an A-MPDU. + * + * bar_frame + * Received frame is a BAR frame + * + * mpdu_length + * MPDU length before decapsulation. + * + * first_mpdu..directed + * See definition in RX attention descriptor + * + */ + +enum rx_msdu_start_pkt_type { + RX_MSDU_START_PKT_TYPE_11A, + RX_MSDU_START_PKT_TYPE_11B, + RX_MSDU_START_PKT_TYPE_11N, + RX_MSDU_START_PKT_TYPE_11AC, + RX_MSDU_START_PKT_TYPE_11AX, +}; + +enum rx_msdu_start_sgi { + RX_MSDU_START_SGI_0_8_US, + RX_MSDU_START_SGI_0_4_US, + RX_MSDU_START_SGI_1_6_US, + RX_MSDU_START_SGI_3_2_US, +}; + +enum rx_msdu_start_recv_bw { + RX_MSDU_START_RECV_BW_20MHZ, + RX_MSDU_START_RECV_BW_40MHZ, + RX_MSDU_START_RECV_BW_80MHZ, + RX_MSDU_START_RECV_BW_160MHZ, +}; + +enum rx_msdu_start_reception_type { + RX_MSDU_START_RECEPTION_TYPE_SU, + RX_MSDU_START_RECEPTION_TYPE_DL_MU_MIMO, + RX_MSDU_START_RECEPTION_TYPE_DL_MU_OFDMA, + RX_MSDU_START_RECEPTION_TYPE_DL_MU_OFDMA_MIMO, + RX_MSDU_START_RECEPTION_TYPE_UL_MU_MIMO, + RX_MSDU_START_RECEPTION_TYPE_UL_MU_OFDMA, + RX_MSDU_START_RECEPTION_TYPE_UL_MU_OFDMA_MIMO, +}; + +#define RX_MSDU_START_INFO1_MSDU_LENGTH GENMASK(13, 0) +#define RX_MSDU_START_INFO1_RSVD_1A BIT(14) +#define RX_MSDU_START_INFO1_IPSEC_ESP BIT(15) +#define RX_MSDU_START_INFO1_L3_OFFSET GENMASK(22, 16) +#define RX_MSDU_START_INFO1_IPSEC_AH BIT(23) +#define RX_MSDU_START_INFO1_L4_OFFSET GENMASK(31, 24) + +#define RX_MSDU_START_INFO2_MSDU_NUMBER GENMASK(7, 0) +#define RX_MSDU_START_INFO2_DECAP_TYPE GENMASK(9, 8) +#define RX_MSDU_START_INFO2_IPV4 BIT(10) +#define RX_MSDU_START_INFO2_IPV6 BIT(11) +#define RX_MSDU_START_INFO2_TCP BIT(12) +#define RX_MSDU_START_INFO2_UDP BIT(13) +#define RX_MSDU_START_INFO2_IP_FRAG BIT(14) +#define RX_MSDU_START_INFO2_TCP_ONLY_ACK BIT(15) +#define RX_MSDU_START_INFO2_DA_IS_BCAST_MCAST BIT(16) +#define RX_MSDU_START_INFO2_SELECTED_TOEPLITZ_HASH GENMASK(18, 17) +#define RX_MSDU_START_INFO2_IP_FIXED_HDR_VALID BIT(19) +#define RX_MSDU_START_INFO2_IP_EXTN_HDR_VALID BIT(20) +#define RX_MSDU_START_INFO2_IP_TCP_UDP_HDR_VALID BIT(21) +#define RX_MSDU_START_INFO2_MESH_CTRL_PRESENT BIT(22) +#define RX_MSDU_START_INFO2_LDPC BIT(23) +#define RX_MSDU_START_INFO2_IP4_IP6_NXT_HDR GENMASK(31, 24) +#define RX_MSDU_START_INFO2_DECAP_FORMAT GENMASK(9, 8) + +#define RX_MSDU_START_INFO3_USER_RSSI GENMASK(7, 0) +#define RX_MSDU_START_INFO3_PKT_TYPE GENMASK(11, 8) +#define RX_MSDU_START_INFO3_STBC BIT(12) +#define RX_MSDU_START_INFO3_SGI GENMASK(14, 13) +#define RX_MSDU_START_INFO3_RATE_MCS GENMASK(18, 15) +#define RX_MSDU_START_INFO3_RECV_BW GENMASK(20, 19) +#define RX_MSDU_START_INFO3_RECEPTION_TYPE GENMASK(23, 21) +#define RX_MSDU_START_INFO3_MIMO_SS_BITMAP GENMASK(31, 24) + +struct rx_msdu_start { + __le16 info0; + __le16 phy_ppdu_id; + __le32 info1; + __le32 info2; + __le32 toeplitz_hash; + __le32 flow_id_toeplitz; + __le32 info3; + __le32 ppdu_start_timestamp; + __le32 phy_meta_data; +} __packed; + +/* rx_msdu_start + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * msdu_length + * MSDU length in bytes after decapsulation. + * + * ipsec_esp + * Set if IPv4/v6 packet is using IPsec ESP. + * + * l3_offset + * Depending upon mode bit, this field either indicates the + * L3 offset in bytes from the start of the RX_HEADER or the IP + * offset in bytes from the start of the packet after + * decapsulation. The latter is only valid if ipv4_proto or + * ipv6_proto is set. + * + * ipsec_ah + * Set if IPv4/v6 packet is using IPsec AH + * + * l4_offset + * Depending upon mode bit, this field either indicates the + * L4 offset nin bytes from the start of RX_HEADER (only valid + * if either ipv4_proto or ipv6_proto is set to 1) or indicates + * the offset in bytes to the start of TCP or UDP header from + * the start of the IP header after decapsulation (Only valid if + * tcp_proto or udp_proto is set). The value 0 indicates that + * the offset is longer than 127 bytes. + * + * msdu_number + * Indicates the MSDU number within a MPDU. This value is + * reset to zero at the start of each MPDU. If the number of + * MSDU exceeds 255 this number will wrap using modulo 256. + * + * decap_type + * Indicates the format after decapsulation. Values are defined in + * enum %MPDU_START_DECAP_TYPE_*. + * + * ipv4_proto + * Set if L2 layer indicates IPv4 protocol. + * + * ipv6_proto + * Set if L2 layer indicates IPv6 protocol. + * + * tcp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP protocol + * indicates TCP. + * + * udp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP protocol + * indicates UDP. + * + * ip_frag + * Indicates that either the IP More frag bit is set or IP frag + * number is non-zero. If set indicates that this is a fragmented + * IP packet. + * + * tcp_only_ack + * Set if only the TCP Ack bit is set in the TCP flags and if + * the TCP payload is 0. + * + * da_is_bcast_mcast + * The destination address is broadcast or multicast. + * + * toeplitz_hash + * Actual chosen Hash. + * 0 - Toeplitz hash of 2-tuple (IP source address, IP + * destination address) + * 1 - Toeplitz hash of 4-tuple (IP source address, + * IP destination address, L4 (TCP/UDP) source port, + * L4 (TCP/UDP) destination port) + * 2 - Toeplitz of flow_id + * 3 - Zero is used + * + * ip_fixed_header_valid + * Fixed 20-byte IPv4 header or 40-byte IPv6 header parsed + * fully within first 256 bytes of the packet + * + * ip_extn_header_valid + * IPv6/IPv6 header, including IPv4 options and + * recognizable extension headers parsed fully within first 256 + * bytes of the packet + * + * tcp_udp_header_valid + * Fixed 20-byte TCP (excluding TCP options) or 8-byte UDP + * header parsed fully within first 256 bytes of the packet + * + * mesh_control_present + * When set, this MSDU includes the 'Mesh Control' field + * + * ldpc + * + * ip4_protocol_ip6_next_header + * For IPv4, this is the 8 bit protocol field set). For IPv6 this + * is the 8 bit next_header field. + * + * toeplitz_hash_2_or_4 + * Controlled by RxOLE register - If register bit set to 0, + * Toeplitz hash is computed over 2-tuple IPv4 or IPv6 src/dest + * addresses; otherwise, toeplitz hash is computed over 4-tuple + * IPv4 or IPv6 src/dest addresses and src/dest ports. + * + * flow_id_toeplitz + * Toeplitz hash of 5-tuple + * {IP source address, IP destination address, IP source port, IP + * destination port, L4 protocol} in case of non-IPSec. + * + * In case of IPSec - Toeplitz hash of 4-tuple + * {IP source address, IP destination address, SPI, L4 protocol} + * + * The relevant Toeplitz key registers are provided in RxOLE's + * instance of common parser module. These registers are separate + * from the Toeplitz keys used by ASE/FSE modules inside RxOLE. + * The actual value will be passed on from common parser module + * to RxOLE in one of the WHO_* TLVs. + * + * user_rssi + * RSSI for this user + * + * pkt_type + * Values are defined in enum %RX_MSDU_START_PKT_TYPE_*. + * + * stbc + * When set, use STBC transmission rates. + * + * sgi + * Field only valid when pkt type is HT, VHT or HE. Values are + * defined in enum %RX_MSDU_START_SGI_*. + * + * rate_mcs + * MCS Rate used. + * + * receive_bandwidth + * Full receive Bandwidth. Values are defined in enum + * %RX_MSDU_START_RECV_*. + * + * reception_type + * Indicates what type of reception this is and defined in enum + * %RX_MSDU_START_RECEPTION_TYPE_*. + * + * mimo_ss_bitmap + * Field only valid when + * Reception_type is RX_MSDU_START_RECEPTION_TYPE_DL_MU_MIMO or + * RX_MSDU_START_RECEPTION_TYPE_DL_MU_OFDMA_MIMO. + * + * Bitmap, with each bit indicating if the related spatial + * stream is used for this STA + * + * LSB related to SS 0 + * + * 0 - spatial stream not used for this reception + * 1 - spatial stream used for this reception + * + * ppdu_start_timestamp + * Timestamp that indicates when the PPDU that contained this MPDU + * started on the medium. + * + * phy_meta_data + * SW programmed Meta data provided by the PHY. Can be used for SW + * to indicate the channel the device is on. + */ + +#define RX_MSDU_END_INFO0_RXPCU_MPDU_FITLER GENMASK(1, 0) +#define RX_MSDU_END_INFO0_SW_FRAME_GRP_ID GENMASK(8, 2) + +#define RX_MSDU_END_INFO1_KEY_ID GENMASK(7, 0) +#define RX_MSDU_END_INFO1_CCE_SUPER_RULE GENMASK(13, 8) +#define RX_MSDU_END_INFO1_CCND_TRUNCATE BIT(14) +#define RX_MSDU_END_INFO1_CCND_CCE_DIS BIT(15) +#define RX_MSDU_END_INFO1_EXT_WAPI_PN GENMASK(31, 16) + +#define RX_MSDU_END_INFO2_REPORTED_MPDU_LEN GENMASK(13, 0) +#define RX_MSDU_END_INFO2_FIRST_MSDU BIT(14) +#define RX_MSDU_END_INFO2_LAST_MSDU BIT(15) +#define RX_MSDU_END_INFO2_SA_IDX_TIMEOUT BIT(16) +#define RX_MSDU_END_INFO2_DA_IDX_TIMEOUT BIT(17) +#define RX_MSDU_END_INFO2_MSDU_LIMIT_ERR BIT(18) +#define RX_MSDU_END_INFO2_FLOW_IDX_TIMEOUT BIT(19) +#define RX_MSDU_END_INFO2_FLOW_IDX_INVALID BIT(20) +#define RX_MSDU_END_INFO2_WIFI_PARSER_ERR BIT(21) +#define RX_MSDU_END_INFO2_AMSDU_PARSET_ERR BIT(22) +#define RX_MSDU_END_INFO2_SA_IS_VALID BIT(23) +#define RX_MSDU_END_INFO2_DA_IS_VALID BIT(24) +#define RX_MSDU_END_INFO2_DA_IS_MCBC BIT(25) +#define RX_MSDU_END_INFO2_L3_HDR_PADDING GENMASK(27, 26) + +#define RX_MSDU_END_INFO3_TCP_FLAG GENMASK(8, 0) +#define RX_MSDU_END_INFO3_LRO_ELIGIBLE BIT(9) + +#define RX_MSDU_END_INFO4_DA_OFFSET GENMASK(5, 0) +#define RX_MSDU_END_INFO4_SA_OFFSET GENMASK(11, 6) +#define RX_MSDU_END_INFO4_DA_OFFSET_VALID BIT(12) +#define RX_MSDU_END_INFO4_SA_OFFSET_VALID BIT(13) +#define RX_MSDU_END_INFO4_L3_TYPE GENMASK(31, 16) + +#define RX_MSDU_END_INFO5_MSDU_DROP BIT(0) +#define RX_MSDU_END_INFO5_REO_DEST_IND GENMASK(5, 1) +#define RX_MSDU_END_INFO5_FLOW_IDX GENMASK(25, 6) + +struct rx_msdu_end { + __le16 info0; + __le16 phy_ppdu_id; + __le16 ip_hdr_cksum; + __le16 tcp_udp_cksum; + __le32 info1; + __le32 ext_wapi_pn[2]; + __le32 info2; + __le32 ipv6_options_crc; + __le32 tcp_seq_num; + __le32 tcp_ack_num; + __le16 info3; + __le16 window_size; + __le32 info4; + __le32 rule_indication[2]; + __le16 sa_idx; + __le16 da_idx; + __le32 info5; + __le32 fse_metadata; + __le16 cce_metadata; + __le16 sa_sw_peer_id; +} __packed; + +/* rx_msdu_end + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * ip_hdr_cksum + * This can include the IP header checksum or the pseudo + * header checksum used by TCP/UDP checksum. + * + * tcp_udp_chksum + * The value of the computed TCP/UDP checksum. A mode bit + * selects whether this checksum is the full checksum or the + * partial checksum which does not include the pseudo header. + * + * key_id + * The key ID octet from the IV. Only valid when first_msdu is set. + * + * cce_super_rule + * Indicates the super filter rule. + * + * cce_classify_not_done_truncate + * Classification failed due to truncated frame. + * + * cce_classify_not_done_cce_dis + * Classification failed due to CCE global disable + * + * ext_wapi_pn* + * Extension PN (packet number) which is only used by WAPI. + * + * reported_mpdu_length + * MPDU length before decapsulation. Only valid when first_msdu is + * set. This field is taken directly from the length field of the + * A-MPDU delimiter or the preamble length field for non-A-MPDU + * frames. + * + * first_msdu + * Indicates the first MSDU of A-MSDU. If both first_msdu and + * last_msdu are set in the MSDU then this is a non-aggregated MSDU + * frame: normal MPDU. Interior MSDU in an A-MSDU shall have both + * first_mpdu and last_mpdu bits set to 0. + * + * last_msdu + * Indicates the last MSDU of the A-MSDU. MPDU end status is only + * valid when last_msdu is set. + * + * sa_idx_timeout + * Indicates an unsuccessful MAC source address search due to the + * expiring of the search timer. + * + * da_idx_timeout + * Indicates an unsuccessful MAC destination address search due to + * the expiring of the search timer. + * + * msdu_limit_error + * Indicates that the MSDU threshold was exceeded and thus all the + * rest of the MSDUs will not be scattered and will not be + * decapsulated but will be DMA'ed in RAW format as a single MSDU. + * + * flow_idx_timeout + * Indicates an unsuccessful flow search due to the expiring of + * the search timer. + * + * flow_idx_invalid + * flow id is not valid. + * + * amsdu_parser_error + * A-MSDU could not be properly de-agregated. + * + * sa_is_valid + * Indicates that OLE found a valid SA entry. + * + * da_is_valid + * Indicates that OLE found a valid DA entry. + * + * da_is_mcbc + * Field Only valid if da_is_valid is set. Indicates the DA address + * was a Multicast of Broadcast address. + * + * l3_header_padding + * Number of bytes padded to make sure that the L3 header will + * always start of a Dword boundary. + * + * ipv6_options_crc + * 32 bit CRC computed out of IP v6 extension headers. + * + * tcp_seq_number + * TCP sequence number. + * + * tcp_ack_number + * TCP acknowledge number. + * + * tcp_flag + * TCP flags {NS, CWR, ECE, URG, ACK, PSH, RST, SYN, FIN}. + * + * lro_eligible + * Computed out of TCP and IP fields to indicate that this + * MSDU is eligible for LRO. + * + * window_size + * TCP receive window size. + * + * da_offset + * Offset into MSDU buffer for DA. + * + * sa_offset + * Offset into MSDU buffer for SA. + * + * da_offset_valid + * da_offset field is valid. This will be set to 0 in case + * of a dynamic A-MSDU when DA is compressed. + * + * sa_offset_valid + * sa_offset field is valid. This will be set to 0 in case + * of a dynamic A-MSDU when SA is compressed. + * + * l3_type + * The 16-bit type value indicating the type of L3 later + * extracted from LLC/SNAP, set to zero if SNAP is not + * available. + * + * rule_indication + * Bitmap indicating which of rules have matched. + * + * sa_idx + * The offset in the address table which matches MAC source address + * + * da_idx + * The offset in the address table which matches MAC destination + * address. + * + * msdu_drop + * REO shall drop this MSDU and not forward it to any other ring. + * + * reo_destination_indication + * The id of the reo exit ring where the msdu frame shall push + * after (MPDU level) reordering has finished. Values are defined + * in enum %HAL_RX_MSDU_DESC_REO_DEST_IND_. + * + * flow_idx + * Flow table index. + * + * fse_metadata + * FSE related meta data. + * + * cce_metadata + * CCE related meta data. + * + * sa_sw_peer_id + * sw_peer_id from the address search entry corresponding to the + * source address of the MSDU. + */ + +enum rx_mpdu_end_rxdma_dest_ring { + RX_MPDU_END_RXDMA_DEST_RING_RELEASE, + RX_MPDU_END_RXDMA_DEST_RING_FW, + RX_MPDU_END_RXDMA_DEST_RING_SW, + RX_MPDU_END_RXDMA_DEST_RING_REO, +}; + +#define RX_MPDU_END_INFO1_UNSUP_KTYPE_SHORT_FRAME BIT(11) +#define RX_MPDU_END_INFO1_RX_IN_TX_DECRYPT_BYT BIT(12) +#define RX_MPDU_END_INFO1_OVERFLOW_ERR BIT(13) +#define RX_MPDU_END_INFO1_MPDU_LEN_ERR BIT(14) +#define RX_MPDU_END_INFO1_TKIP_MIC_ERR BIT(15) +#define RX_MPDU_END_INFO1_DECRYPT_ERR BIT(16) +#define RX_MPDU_END_INFO1_UNENCRYPTED_FRAME_ERR BIT(17) +#define RX_MPDU_END_INFO1_PN_FIELDS_VALID BIT(18) +#define RX_MPDU_END_INFO1_FCS_ERR BIT(19) +#define RX_MPDU_END_INFO1_MSDU_LEN_ERR BIT(20) +#define RX_MPDU_END_INFO1_RXDMA0_DEST_RING GENMASK(22, 21) +#define RX_MPDU_END_INFO1_RXDMA1_DEST_RING GENMASK(24, 23) +#define RX_MPDU_END_INFO1_DECRYPT_STATUS_CODE GENMASK(27, 25) +#define RX_MPDU_END_INFO1_RX_BITMAP_NOT_UPD BIT(28) + +struct rx_mpdu_end { + __le16 info0; + __le16 phy_ppdu_id; + __le32 info1; +} __packed; + +/* rx_mpdu_end + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * unsup_ktype_short_frame + * This bit will be '1' when WEP or TKIP or WAPI key type is + * received for 11ah short frame. Crypto will bypass the received + * packet without decryption to RxOLE after setting this bit. + * + * rx_in_tx_decrypt_byp + * Indicates that RX packet is not decrypted as Crypto is + * busy with TX packet processing. + * + * overflow_err + * RXPCU Receive FIFO ran out of space to receive the full MPDU. + * Therefore this MPDU is terminated early and is thus corrupted. + * + * This MPDU will not be ACKed. + * + * RXPCU might still be able to correctly receive the following + * MPDUs in the PPDU if enough fifo space became available in time. + * + * mpdu_length_err + * Set by RXPCU if the expected MPDU length does not correspond + * with the actually received number of bytes in the MPDU. + * + * tkip_mic_err + * Set by Rx crypto when crypto detected a TKIP MIC error for + * this MPDU. + * + * decrypt_err + * Set by RX CRYPTO when CRYPTO detected a decrypt error for this + * MPDU or CRYPTO received an encrypted frame, but did not get a + * valid corresponding key id in the peer entry. + * + * unencrypted_frame_err + * Set by RX CRYPTO when CRYPTO detected an unencrypted frame while + * in the peer entry field 'All_frames_shall_be_encrypted' is set. + * + * pn_fields_contain_valid_info + * Set by RX CRYPTO to indicate that there is a valid PN field + * present in this MPDU. + * + * fcs_err + * Set by RXPCU when there is an FCS error detected for this MPDU. + * + * msdu_length_err + * Set by RXOLE when there is an msdu length error detected + * in at least 1 of the MSDUs embedded within the MPDU. + * + * rxdma0_destination_ring + * rxdma1_destination_ring + * The ring to which RXDMA0/1 shall push the frame, assuming + * no MPDU level errors are detected. In case of MPDU level + * errors, RXDMA0/1 might change the RXDMA0/1 destination. Values + * are defined in %enum RX_MPDU_END_RXDMA_DEST_RING_*. + * + * decrypt_status_code + * Field provides insight into the decryption performed. Values + * are defined in enum %RX_DESC_DECRYPT_STATUS_CODE_*. + * + * rx_bitmap_not_updated + * Frame is received, but RXPCU could not update the receive bitmap + * due to (temporary) fifo constraints. + */ + +/* Padding bytes to avoid TLV's spanning across 128 byte boundary */ +#define HAL_RX_DESC_PADDING0_BYTES 4 +#define HAL_RX_DESC_PADDING1_BYTES 16 + +#define HAL_RX_DESC_HDR_STATUS_LEN 120 + +struct hal_rx_desc { + __le32 msdu_end_tag; + struct rx_msdu_end msdu_end; + __le32 rx_attn_tag; + struct rx_attention attention; + __le32 msdu_start_tag; + struct rx_msdu_start msdu_start; + u8 rx_padding0[HAL_RX_DESC_PADDING0_BYTES]; + __le32 mpdu_start_tag; + struct rx_mpdu_start mpdu_start; + __le32 mpdu_end_tag; + struct rx_mpdu_end mpdu_end; + u8 rx_padding1[HAL_RX_DESC_PADDING1_BYTES]; + __le32 hdr_status_tag; + __le32 phy_ppdu_id; + u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; + u8 msdu_payload[0]; +} __packed; + +#endif /* ATH11K_RX_DESC_H */ diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c new file mode 100644 index 000000000000..d2dc9db01491 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "testmode.h" +#include <net/netlink.h> +#include "debug.h" +#include "wmi.h" +#include "hw.h" +#include "core.h" +#include "testmode_i.h" + +static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = { + [ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH11K_TM_DATA_MAX_LEN }, + [ATH11K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, + [ATH11K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, + [ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, +}; + +/* Returns true if callee consumes the skb and the skb should be discarded. + * Returns false if skb is not used. Does not sleep. + */ +bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb) +{ + struct sk_buff *nl_skb; + bool consumed; + int ret; + + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, + "testmode event wmi cmd_id %d skb %pK skb->len %d\n", + cmd_id, skb, skb->len); + + ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len); + + spin_lock_bh(&ar->data_lock); + + consumed = true; + + nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy, + 2 * sizeof(u32) + skb->len, + GFP_ATOMIC); + if (!nl_skb) { + ath11k_warn(ar->ab, + "failed to allocate skb for testmode wmi event\n"); + goto out; + } + + ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI); + if (ret) { + ath11k_warn(ar->ab, + "failed to to put testmode wmi event cmd attribute: %d\n", + ret); + kfree_skb(nl_skb); + goto out; + } + + ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id); + if (ret) { + ath11k_warn(ar->ab, + "failed to to put testmode wmi even cmd_id: %d\n", + ret); + kfree_skb(nl_skb); + goto out; + } + + ret = nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data); + if (ret) { + ath11k_warn(ar->ab, + "failed to copy skb to testmode wmi event: %d\n", + ret); + kfree_skb(nl_skb); + goto out; + } + + cfg80211_testmode_event(nl_skb, GFP_ATOMIC); + +out: + spin_unlock_bh(&ar->data_lock); + + return consumed; +} + +static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[]) +{ + struct sk_buff *skb; + int ret; + + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, + "testmode cmd get version_major %d version_minor %d\n", + ATH11K_TESTMODE_VERSION_MAJOR, + ATH11K_TESTMODE_VERSION_MINOR); + + skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy, + nla_total_size(sizeof(u32))); + if (!skb) + return -ENOMEM; + + ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR, + ATH11K_TESTMODE_VERSION_MAJOR); + if (ret) { + kfree_skb(skb); + return ret; + } + + ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR, + ATH11K_TESTMODE_VERSION_MINOR); + if (ret) { + kfree_skb(skb); + return ret; + } + + return cfg80211_testmode_reply(skb); +} + +static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[]) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct sk_buff *skb; + u32 cmd_id, buf_len; + int ret; + void *buf; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + if (!tb[ATH11K_TM_ATTR_DATA]) { + ret = -EINVAL; + goto out; + } + + if (!tb[ATH11K_TM_ATTR_WMI_CMDID]) { + ret = -EINVAL; + goto out; + } + + buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]); + + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, + "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n", + cmd_id, buf, buf_len); + + ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, buf_len); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + memcpy(skb->data, buf, buf_len); + + ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + dev_kfree_skb(skb); + ath11k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n", + ret); + goto out; + } + + ret = 0; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct ath11k *ar = hw->priv; + struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1]; + int ret; + + ret = nla_parse(tb, ATH11K_TM_ATTR_MAX, data, len, ath11k_tm_policy, + NULL); + if (ret) + return ret; + + if (!tb[ATH11K_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) { + case ATH11K_TM_CMD_GET_VERSION: + return ath11k_tm_cmd_get_version(ar, tb); + case ATH11K_TM_CMD_WMI: + return ath11k_tm_cmd_wmi(ar, tb); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/wireless/ath/ath11k/testmode.h b/drivers/net/wireless/ath/ath11k/testmode.h new file mode 100644 index 000000000000..aaa122ed9069 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/testmode.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "core.h" + +#ifdef CONFIG_NL80211_TESTMODE + +bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb); +int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); + +#else + +static inline bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, + struct sk_buff *skb) +{ + return false; +} + +static inline int ath11k_tm_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + return 0; +} + +#endif diff --git a/drivers/net/wireless/ath/ath11k/testmode_i.h b/drivers/net/wireless/ath/ath11k/testmode_i.h new file mode 100644 index 000000000000..4bae2a9eeea4 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/testmode_i.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +/* "API" level of the ath11k testmode interface. Bump it after every + * incompatible interface change. + */ +#define ATH11K_TESTMODE_VERSION_MAJOR 1 + +/* Bump this after every _compatible_ interface change, for example + * addition of a new command or an attribute. + */ +#define ATH11K_TESTMODE_VERSION_MINOR 0 + +#define ATH11K_TM_DATA_MAX_LEN 5000 + +enum ath11k_tm_attr { + __ATH11K_TM_ATTR_INVALID = 0, + ATH11K_TM_ATTR_CMD = 1, + ATH11K_TM_ATTR_DATA = 2, + ATH11K_TM_ATTR_WMI_CMDID = 3, + ATH11K_TM_ATTR_VERSION_MAJOR = 4, + ATH11K_TM_ATTR_VERSION_MINOR = 5, + ATH11K_TM_ATTR_WMI_OP_VERSION = 6, + + /* keep last */ + __ATH11K_TM_ATTR_AFTER_LAST, + ATH11K_TM_ATTR_MAX = __ATH11K_TM_ATTR_AFTER_LAST - 1, +}; + +/* All ath11k testmode interface commands specified in + * ATH11K_TM_ATTR_CMD + */ +enum ath11k_tm_cmd { + /* Returns the supported ath11k testmode interface version in + * ATH11K_TM_ATTR_VERSION. Always guaranteed to work. User space + * uses this to verify it's using the correct version of the + * testmode interface + */ + ATH11K_TM_CMD_GET_VERSION = 0, + + /* The command used to transmit a WMI command to the firmware and + * the event to receive WMI events from the firmware. Without + * struct wmi_cmd_hdr header, only the WMI payload. Command id is + * provided with ATH11K_TM_ATTR_WMI_CMDID and payload in + * ATH11K_TM_ATTR_DATA. + */ + ATH11K_TM_CMD_WMI = 1, +}; diff --git a/drivers/net/wireless/ath/ath11k/trace.c b/drivers/net/wireless/ath/ath11k/trace.c new file mode 100644 index 000000000000..f0cc49ba0387 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/trace.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/net/wireless/ath/ath11k/trace.h b/drivers/net/wireless/ath/ath11k/trace.h new file mode 100644 index 000000000000..8700a622be7b --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/trace.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + */ + +#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) + +#include <linux/tracepoint.h> +#include "core.h" + +#define _TRACE_H_ + +/* create empty functions when tracing is disabled */ +#if !defined(CONFIG_ATH11K_TRACING) +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif /* !CONFIG_ATH11K_TRACING || __CHECKER__ */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ath11k + +TRACE_EVENT(ath11k_htt_pktlog, + TP_PROTO(struct ath11k *ar, const void *buf, u16 buf_len), + + TP_ARGS(ar, buf, buf_len), + + TP_STRUCT__entry( + __string(device, dev_name(ar->ab->dev)) + __string(driver, dev_driver_string(ar->ab->dev)) + __field(u16, buf_len) + __dynamic_array(u8, pktlog, buf_len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ar->ab->dev)); + __assign_str(driver, dev_driver_string(ar->ab->dev)); + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(pktlog), buf, buf_len); + ), + + TP_printk( + "%s %s size %hu", + __get_str(driver), + __get_str(device), + __entry->buf_len + ) +); + +TRACE_EVENT(ath11k_htt_ppdu_stats, + TP_PROTO(struct ath11k *ar, const void *data, size_t len), + + TP_ARGS(ar, data, len), + + TP_STRUCT__entry( + __string(device, dev_name(ar->ab->dev)) + __string(driver, dev_driver_string(ar->ab->dev)) + __field(u16, len) + __dynamic_array(u8, ppdu, len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ar->ab->dev)); + __assign_str(driver, dev_driver_string(ar->ab->dev)); + __entry->len = len; + memcpy(__get_dynamic_array(ppdu), data, len); + ), + + TP_printk( + "%s %s ppdu len %d", + __get_str(driver), + __get_str(device), + __entry->len + ) +); + +TRACE_EVENT(ath11k_htt_rxdesc, + TP_PROTO(struct ath11k *ar, const void *data, size_t len), + + TP_ARGS(ar, data, len), + + TP_STRUCT__entry( + __string(device, dev_name(ar->ab->dev)) + __string(driver, dev_driver_string(ar->ab->dev)) + __field(u16, len) + __dynamic_array(u8, rxdesc, len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ar->ab->dev)); + __assign_str(driver, dev_driver_string(ar->ab->dev)); + __entry->len = len; + memcpy(__get_dynamic_array(rxdesc), data, len); + ), + + TP_printk( + "%s %s rxdesc len %d", + __get_str(driver), + __get_str(device), + __entry->len + ) +); + +#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ + +/* we don't want to use include/trace/events */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c new file mode 100644 index 000000000000..a9b301ceb24b --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -0,0 +1,5810 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#include <linux/skbuff.h> +#include <linux/ctype.h> +#include <net/mac80211.h> +#include <net/cfg80211.h> +#include <linux/completion.h> +#include <linux/if_ether.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/uuid.h> +#include <linux/time.h> +#include <linux/of.h> +#include "core.h" +#include "debug.h" +#include "mac.h" +#include "hw.h" +#include "peer.h" + +struct wmi_tlv_policy { + size_t min_len; +}; + +struct wmi_tlv_svc_ready_parse { + bool wmi_svc_bitmap_done; +}; + +struct wmi_tlv_svc_rdy_ext_parse { + struct ath11k_service_ext_param param; + struct wmi_soc_mac_phy_hw_mode_caps *hw_caps; + struct wmi_hw_mode_capabilities *hw_mode_caps; + u32 n_hw_mode_caps; + u32 tot_phy_id; + struct wmi_hw_mode_capabilities pref_hw_mode_caps; + struct wmi_mac_phy_capabilities *mac_phy_caps; + u32 n_mac_phy_caps; + struct wmi_soc_hal_reg_capabilities *soc_hal_reg_caps; + struct wmi_hal_reg_capabilities_ext *ext_hal_reg_caps; + u32 n_ext_hal_reg_caps; + bool hw_mode_done; + bool mac_phy_done; + bool ext_hal_reg_done; +}; + +struct wmi_tlv_rdy_parse { + u32 num_extra_mac_addr; +}; + +static const struct wmi_tlv_policy wmi_tlv_policies[] = { + [WMI_TAG_ARRAY_BYTE] + = { .min_len = 0 }, + [WMI_TAG_ARRAY_UINT32] + = { .min_len = 0 }, + [WMI_TAG_SERVICE_READY_EVENT] + = { .min_len = sizeof(struct wmi_service_ready_event) }, + [WMI_TAG_SERVICE_READY_EXT_EVENT] + = { .min_len = sizeof(struct wmi_service_ready_ext_event) }, + [WMI_TAG_SOC_MAC_PHY_HW_MODE_CAPS] + = { .min_len = sizeof(struct wmi_soc_mac_phy_hw_mode_caps) }, + [WMI_TAG_SOC_HAL_REG_CAPABILITIES] + = { .min_len = sizeof(struct wmi_soc_hal_reg_capabilities) }, + [WMI_TAG_VDEV_START_RESPONSE_EVENT] + = { .min_len = sizeof(struct wmi_vdev_start_resp_event) }, + [WMI_TAG_PEER_DELETE_RESP_EVENT] + = { .min_len = sizeof(struct wmi_peer_delete_resp_event) }, + [WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT] + = { .min_len = sizeof(struct wmi_bcn_tx_status_event) }, + [WMI_TAG_VDEV_STOPPED_EVENT] + = { .min_len = sizeof(struct wmi_vdev_stopped_event) }, + [WMI_TAG_REG_CHAN_LIST_CC_EVENT] + = { .min_len = sizeof(struct wmi_reg_chan_list_cc_event) }, + [WMI_TAG_MGMT_RX_HDR] + = { .min_len = sizeof(struct wmi_mgmt_rx_hdr) }, + [WMI_TAG_MGMT_TX_COMPL_EVENT] + = { .min_len = sizeof(struct wmi_mgmt_tx_compl_event) }, + [WMI_TAG_SCAN_EVENT] + = { .min_len = sizeof(struct wmi_scan_event) }, + [WMI_TAG_PEER_STA_KICKOUT_EVENT] + = { .min_len = sizeof(struct wmi_peer_sta_kickout_event) }, + [WMI_TAG_ROAM_EVENT] + = { .min_len = sizeof(struct wmi_roam_event) }, + [WMI_TAG_CHAN_INFO_EVENT] + = { .min_len = sizeof(struct wmi_chan_info_event) }, + [WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT] + = { .min_len = sizeof(struct wmi_pdev_bss_chan_info_event) }, + [WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT] + = { .min_len = sizeof(struct wmi_vdev_install_key_compl_event) }, + [WMI_TAG_READY_EVENT] + = {.min_len = sizeof(struct wmi_ready_event) }, + [WMI_TAG_SERVICE_AVAILABLE_EVENT] + = {.min_len = sizeof(struct wmi_service_available_event) }, + [WMI_TAG_PEER_ASSOC_CONF_EVENT] + = { .min_len = sizeof(struct wmi_peer_assoc_conf_event) }, + [WMI_TAG_STATS_EVENT] + = { .min_len = sizeof(struct wmi_stats_event) }, + [WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT] + = { .min_len = sizeof(struct wmi_pdev_ctl_failsafe_chk_event) }, +}; + +#define PRIMAP(_hw_mode_) \ + [_hw_mode_] = _hw_mode_##_PRI + +static const int ath11k_hw_mode_pri_map[] = { + PRIMAP(WMI_HOST_HW_MODE_SINGLE), + PRIMAP(WMI_HOST_HW_MODE_DBS), + PRIMAP(WMI_HOST_HW_MODE_SBS_PASSIVE), + PRIMAP(WMI_HOST_HW_MODE_SBS), + PRIMAP(WMI_HOST_HW_MODE_DBS_SBS), + PRIMAP(WMI_HOST_HW_MODE_DBS_OR_SBS), + /* keep last */ + PRIMAP(WMI_HOST_HW_MODE_MAX), +}; + +static int +ath11k_wmi_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath11k_base *ab, u16 tag, u16 len, + const void *ptr, void *data), + void *data) +{ + const void *begin = ptr; + const struct wmi_tlv *tlv; + u16 tlv_tag, tlv_len; + int ret; + + while (len > 0) { + if (len < sizeof(*tlv)) { + ath11k_err(ab, "wmi tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", + ptr - begin, len, sizeof(*tlv)); + return -EINVAL; + } + + tlv = ptr; + tlv_tag = FIELD_GET(WMI_TLV_TAG, tlv->header); + tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header); + ptr += sizeof(*tlv); + len -= sizeof(*tlv); + + if (tlv_len > len) { + ath11k_err(ab, "wmi tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n", + tlv_tag, ptr - begin, len, tlv_len); + return -EINVAL; + } + + if (tlv_tag < ARRAY_SIZE(wmi_tlv_policies) && + wmi_tlv_policies[tlv_tag].min_len && + wmi_tlv_policies[tlv_tag].min_len > tlv_len) { + ath11k_err(ab, "wmi tlv parse failure of tag %hhu at byte %zd (%hhu bytes is less than min length %zu)\n", + tlv_tag, ptr - begin, tlv_len, + wmi_tlv_policies[tlv_tag].min_len); + return -EINVAL; + } + + ret = iter(ab, tlv_tag, tlv_len, ptr, data); + if (ret) + return ret; + + ptr += tlv_len; + len -= tlv_len; + } + + return 0; +} + +static int ath11k_wmi_tlv_iter_parse(struct ath11k_base *ab, u16 tag, u16 len, + const void *ptr, void *data) +{ + const void **tb = data; + + if (tag < WMI_TAG_MAX) + tb[tag] = ptr; + + return 0; +} + +static int ath11k_wmi_tlv_parse(struct ath11k_base *ar, const void **tb, + const void *ptr, size_t len) +{ + return ath11k_wmi_tlv_iter(ar, ptr, len, ath11k_wmi_tlv_iter_parse, + (void *)tb); +} + +static const void ** +ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr, + size_t len, gfp_t gfp) +{ + const void **tb; + int ret; + + tb = kcalloc(WMI_TAG_MAX, sizeof(*tb), gfp); + if (!tb) + return ERR_PTR(-ENOMEM); + + ret = ath11k_wmi_tlv_parse(ab, tb, ptr, len); + if (ret) { + kfree(tb); + return ERR_PTR(ret); + } + + return tb; +} + +static int ath11k_wmi_cmd_send_nowait(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id) +{ + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_cmd_hdr *cmd_hdr; + int ret; + u32 cmd = 0; + + if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return -ENOMEM; + + cmd |= FIELD_PREP(WMI_CMD_HDR_CMD_ID, cmd_id); + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + cmd_hdr->cmd_id = cmd; + + memset(skb_cb, 0, sizeof(*skb_cb)); + ret = ath11k_htc_send(&ab->htc, wmi->eid, skb); + + if (ret) + goto err_pull; + + return 0; + +err_pull: + skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + return ret; +} + +int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id) +{ + struct ath11k_wmi_base *wmi_sc = wmi->wmi_ab; + int ret = -EOPNOTSUPP; + + might_sleep(); + + wait_event_timeout(wmi_sc->tx_credits_wq, ({ + ret = ath11k_wmi_cmd_send_nowait(wmi, skb, cmd_id); + + if (ret && test_bit(ATH11K_FLAG_CRASH_FLUSH, &wmi_sc->ab->dev_flags)) + ret = -ESHUTDOWN; + + (ret != -EAGAIN); + }), WMI_SEND_TIMEOUT_HZ); + + if (ret == -EAGAIN) + ath11k_warn(wmi_sc->ab, "wmi command %d timeout\n", cmd_id); + + return ret; +} + +static int ath11k_pull_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, + const void *ptr, + struct ath11k_service_ext_param *param) +{ + const struct wmi_service_ready_ext_event *ev = ptr; + + if (!ev) + return -EINVAL; + + /* Move this to host based bitmap */ + param->default_conc_scan_config_bits = ev->default_conc_scan_config_bits; + param->default_fw_config_bits = ev->default_fw_config_bits; + param->he_cap_info = ev->he_cap_info; + param->mpdu_density = ev->mpdu_density; + param->max_bssid_rx_filters = ev->max_bssid_rx_filters; + memcpy(¶m->ppet, &ev->ppet, sizeof(param->ppet)); + + return 0; +} + +static int +ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, + struct wmi_soc_mac_phy_hw_mode_caps *hw_caps, + struct wmi_hw_mode_capabilities *wmi_hw_mode_caps, + struct wmi_soc_hal_reg_capabilities *hal_reg_caps, + struct wmi_mac_phy_capabilities *wmi_mac_phy_caps, + u8 hw_mode_id, u8 phy_id, + struct ath11k_pdev *pdev) +{ + struct wmi_mac_phy_capabilities *mac_phy_caps; + struct ath11k_band_cap *cap_band; + struct ath11k_pdev_cap *pdev_cap = &pdev->cap; + u32 phy_map; + u32 hw_idx, phy_idx = 0; + + if (!hw_caps || !wmi_hw_mode_caps || !hal_reg_caps) + return -EINVAL; + + for (hw_idx = 0; hw_idx < hw_caps->num_hw_modes; hw_idx++) { + if (hw_mode_id == wmi_hw_mode_caps[hw_idx].hw_mode_id) + break; + + phy_map = wmi_hw_mode_caps[hw_idx].phy_id_map; + while (phy_map) { + phy_map >>= 1; + phy_idx++; + } + } + + if (hw_idx == hw_caps->num_hw_modes) + return -EINVAL; + + phy_idx += phy_id; + if (phy_id >= hal_reg_caps->num_phy) + return -EINVAL; + + mac_phy_caps = wmi_mac_phy_caps + phy_idx; + + pdev->pdev_id = mac_phy_caps->pdev_id; + pdev_cap->supported_bands = mac_phy_caps->supported_bands; + pdev_cap->ampdu_density = mac_phy_caps->ampdu_density; + + /* Take non-zero tx/rx chainmask. If tx/rx chainmask differs from + * band to band for a single radio, need to see how this should be + * handled. + */ + if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_2G_CAP) { + pdev_cap->tx_chain_mask = mac_phy_caps->tx_chain_mask_2g; + pdev_cap->rx_chain_mask = mac_phy_caps->rx_chain_mask_2g; + } else if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_5G_CAP) { + pdev_cap->vht_cap = mac_phy_caps->vht_cap_info_5g; + pdev_cap->vht_mcs = mac_phy_caps->vht_supp_mcs_5g; + pdev_cap->he_mcs = mac_phy_caps->he_supp_mcs_5g; + pdev_cap->tx_chain_mask = mac_phy_caps->tx_chain_mask_5g; + pdev_cap->rx_chain_mask = mac_phy_caps->rx_chain_mask_5g; + } else { + return -EINVAL; + } + + /* tx/rx chainmask reported from fw depends on the actual hw chains used, + * For example, for 4x4 capable macphys, first 4 chains can be used for first + * mac and the remaing 4 chains can be used for the second mac or vice-versa. + * In this case, tx/rx chainmask 0xf will be advertised for first mac and 0xf0 + * will be advertised for second mac or vice-versa. Compute the shift value for + * for tx/rx chainmask which will be used to advertise supported ht/vht rates to + * mac80211. + */ + pdev_cap->tx_chain_mask_shift = + find_first_bit((unsigned long *)&pdev_cap->tx_chain_mask, 32); + pdev_cap->rx_chain_mask_shift = + find_first_bit((unsigned long *)&pdev_cap->rx_chain_mask, 32); + + cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; + cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_2g; + cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_2g; + cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_2g; + cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_2g_ext; + cap_band->he_mcs = mac_phy_caps->he_supp_mcs_2g; + memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_2g, + sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet2g, + sizeof(struct ath11k_ppe_threshold)); + + cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; + cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g; + cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g; + cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g; + cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext; + cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g; + memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g, + sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g, + sizeof(struct ath11k_ppe_threshold)); + + return 0; +} + +static int +ath11k_pull_reg_cap_svc_rdy_ext(struct ath11k_pdev_wmi *wmi_handle, + struct wmi_soc_hal_reg_capabilities *reg_caps, + struct wmi_hal_reg_capabilities_ext *wmi_ext_reg_cap, + u8 phy_idx, + struct ath11k_hal_reg_capabilities_ext *param) +{ + struct wmi_hal_reg_capabilities_ext *ext_reg_cap; + + if (!reg_caps || !wmi_ext_reg_cap) + return -EINVAL; + + if (phy_idx >= reg_caps->num_phy) + return -EINVAL; + + ext_reg_cap = &wmi_ext_reg_cap[phy_idx]; + + param->phy_id = ext_reg_cap->phy_id; + param->eeprom_reg_domain = ext_reg_cap->eeprom_reg_domain; + param->eeprom_reg_domain_ext = + ext_reg_cap->eeprom_reg_domain_ext; + param->regcap1 = ext_reg_cap->regcap1; + param->regcap2 = ext_reg_cap->regcap2; + /* check if param->wireless_mode is needed */ + param->low_2ghz_chan = ext_reg_cap->low_2ghz_chan; + param->high_2ghz_chan = ext_reg_cap->high_2ghz_chan; + param->low_5ghz_chan = ext_reg_cap->low_5ghz_chan; + param->high_5ghz_chan = ext_reg_cap->high_5ghz_chan; + + return 0; +} + +static int ath11k_pull_service_ready_tlv(struct ath11k_base *ab, + const void *evt_buf, + struct ath11k_targ_cap *cap) +{ + const struct wmi_service_ready_event *ev = evt_buf; + + if (!ev) { + ath11k_err(ab, "%s: failed by NULL param\n", + __func__); + return -EINVAL; + } + + cap->phy_capability = ev->phy_capability; + cap->max_frag_entry = ev->max_frag_entry; + cap->num_rf_chains = ev->num_rf_chains; + cap->ht_cap_info = ev->ht_cap_info; + cap->vht_cap_info = ev->vht_cap_info; + cap->vht_supp_mcs = ev->vht_supp_mcs; + cap->hw_min_tx_power = ev->hw_min_tx_power; + cap->hw_max_tx_power = ev->hw_max_tx_power; + cap->sys_cap_info = ev->sys_cap_info; + cap->min_pkt_size_enable = ev->min_pkt_size_enable; + cap->max_bcn_ie_size = ev->max_bcn_ie_size; + cap->max_num_scan_channels = ev->max_num_scan_channels; + cap->max_supported_macs = ev->max_supported_macs; + cap->wmi_fw_sub_feat_caps = ev->wmi_fw_sub_feat_caps; + cap->txrx_chainmask = ev->txrx_chainmask; + cap->default_dbs_hw_mode_index = ev->default_dbs_hw_mode_index; + cap->num_msdu_desc = ev->num_msdu_desc; + + return 0; +} + +/* Save the wmi_service_bitmap into a linear bitmap. The wmi_services in + * wmi_service ready event are advertised in b0-b3 (LSB 4-bits) of each + * 4-byte word. + */ +static void ath11k_wmi_service_bitmap_copy(struct ath11k_pdev_wmi *wmi, + const u32 *wmi_svc_bm) +{ + int i, j; + + for (i = 0, j = 0; i < WMI_SERVICE_BM_SIZE && j < WMI_MAX_SERVICE; i++) { + do { + if (wmi_svc_bm[i] & BIT(j % WMI_SERVICE_BITS_IN_SIZE32)) + set_bit(j, wmi->wmi_ab->svc_map); + } while (++j % WMI_SERVICE_BITS_IN_SIZE32); + } +} + +static int ath11k_wmi_tlv_svc_rdy_parse(struct ath11k_base *ab, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_ready_parse *svc_ready = data; + struct ath11k_pdev_wmi *wmi_handle = &ab->wmi_ab.wmi[0]; + u16 expect_len; + + switch (tag) { + case WMI_TAG_SERVICE_READY_EVENT: + if (ath11k_pull_service_ready_tlv(ab, ptr, &ab->target_caps)) + return -EINVAL; + break; + + case WMI_TAG_ARRAY_UINT32: + if (!svc_ready->wmi_svc_bitmap_done) { + expect_len = WMI_SERVICE_BM_SIZE * sizeof(u32); + if (len < expect_len) { + ath11k_warn(ab, "invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + ath11k_wmi_service_bitmap_copy(wmi_handle, ptr); + + svc_ready->wmi_svc_bitmap_done = true; + } + break; + default: + break; + } + + return 0; +} + +static int ath11k_service_ready_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_tlv_svc_ready_parse svc_ready = { }; + int ret; + + ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len, + ath11k_wmi_tlv_svc_rdy_parse, + &svc_ready); + if (ret) { + ath11k_warn(ab, "failed to parse tlv %d\n", ret); + return ret; + } + + return 0; +} + +struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len) +{ + struct sk_buff *skb; + struct ath11k_base *ab = wmi_sc->ab; + u32 round_len = roundup(len, 4); + + skb = ath11k_htc_alloc_skb(ab, WMI_SKB_HEADROOM + round_len); + if (!skb) + return NULL; + + skb_reserve(skb, WMI_SKB_HEADROOM); + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath11k_warn(ab, "unaligned WMI skb data\n"); + + skb_put(skb, round_len); + memset(skb->data, 0, round_len); + + return skb; +} + +int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, + struct sk_buff *frame) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_mgmt_send_cmd *cmd; + struct wmi_tlv *frame_tlv; + struct sk_buff *skb; + u32 buf_len; + int ret, len; + + buf_len = frame->len < WMI_MGMT_SEND_DOWNLD_LEN ? + frame->len : WMI_MGMT_SEND_DOWNLD_LEN; + + len = sizeof(*cmd) + sizeof(*frame_tlv) + roundup(buf_len, 4); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_mgmt_send_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_MGMT_TX_SEND_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->desc_id = buf_id; + cmd->chanfreq = 0; + cmd->paddr_lo = lower_32_bits(ATH11K_SKB_CB(frame)->paddr); + cmd->paddr_hi = upper_32_bits(ATH11K_SKB_CB(frame)->paddr); + cmd->frame_len = frame->len; + cmd->buf_len = buf_len; + cmd->tx_params_valid = 0; + + frame_tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); + frame_tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, buf_len); + + memcpy(frame_tlv->value, frame->data, buf_len); + + ath11k_ce_byte_swap(frame_tlv->value, buf_len); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_MGMT_TX_SEND_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to submit WMI_MGMT_TX_SEND_CMDID cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_vdev_create(struct ath11k *ar, u8 *macaddr, + struct vdev_create_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_create_cmd *cmd; + struct sk_buff *skb; + struct wmi_vdev_txrx_streams *txrx_streams; + struct wmi_tlv *tlv; + int ret, len; + void *ptr; + + /* It can be optimized my sending tx/rx chain configuration + * only for supported bands instead of always sending it for + * both the bands. + */ + len = sizeof(*cmd) + TLV_HDR_SIZE + + (WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams)); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_create_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_CREATE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->if_id; + cmd->vdev_type = param->type; + cmd->vdev_subtype = param->subtype; + cmd->num_cfg_txrx_streams = WMI_NUM_SUPPORTED_BAND_MAX; + cmd->pdev_id = param->pdev_id; + ether_addr_copy(cmd->vdev_macaddr.addr, macaddr); + + ptr = skb->data + sizeof(*cmd); + len = WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + txrx_streams = ptr; + len = sizeof(*txrx_streams); + txrx_streams->tlv_header = + FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_TXRX_STREAMS) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + txrx_streams->band = WMI_TPC_CHAINMASK_CONFIG_BAND_2G; + txrx_streams->supported_tx_streams = + param->chains[NL80211_BAND_2GHZ].tx; + txrx_streams->supported_rx_streams = + param->chains[NL80211_BAND_2GHZ].rx; + + txrx_streams++; + txrx_streams->tlv_header = + FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_TXRX_STREAMS) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + txrx_streams->band = WMI_TPC_CHAINMASK_CONFIG_BAND_5G; + txrx_streams->supported_tx_streams = + param->chains[NL80211_BAND_5GHZ].tx; + txrx_streams->supported_rx_streams = + param->chains[NL80211_BAND_5GHZ].rx; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_CREATE_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to submit WMI_VDEV_CREATE_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev create: id %d type %d subtype %d macaddr %pM pdevid %d\n", + param->if_id, param->type, param->subtype, + macaddr, param->pdev_id); + + return ret; +} + +int ath11k_wmi_vdev_delete(struct ath11k *ar, u8 vdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_delete_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_vdev_delete_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_DELETE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_DELETE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_VDEV_DELETE_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "WMI vdev delete id %d\n", vdev_id); + + return ret; +} + +int ath11k_wmi_vdev_stop(struct ath11k *ar, u8 vdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_stop_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_vdev_stop_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_STOP_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_STOP_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_VDEV_STOP cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "WMI vdev stop id 0x%x\n", vdev_id); + + return ret; +} + +int ath11k_wmi_vdev_down(struct ath11k *ar, u8 vdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_down_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_vdev_down_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_DOWN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_DOWN_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_VDEV_DOWN cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "WMI vdev down id 0x%x\n", vdev_id); + + return ret; +} + +static void ath11k_wmi_put_wmi_channel(struct wmi_channel *chan, + struct wmi_vdev_start_req_arg *arg) +{ + memset(chan, 0, sizeof(*chan)); + + chan->mhz = arg->channel.freq; + chan->band_center_freq1 = arg->channel.band_center_freq1; + if (arg->channel.mode == MODE_11AC_VHT80_80) + chan->band_center_freq2 = arg->channel.band_center_freq2; + else + chan->band_center_freq2 = 0; + + chan->info |= FIELD_PREP(WMI_CHAN_INFO_MODE, arg->channel.mode); + if (arg->channel.passive) + chan->info |= WMI_CHAN_INFO_PASSIVE; + if (arg->channel.allow_ibss) + chan->info |= WMI_CHAN_INFO_ADHOC_ALLOWED; + if (arg->channel.allow_ht) + chan->info |= WMI_CHAN_INFO_ALLOW_HT; + if (arg->channel.allow_vht) + chan->info |= WMI_CHAN_INFO_ALLOW_VHT; + if (arg->channel.allow_he) + chan->info |= WMI_CHAN_INFO_ALLOW_HE; + if (arg->channel.ht40plus) + chan->info |= WMI_CHAN_INFO_HT40_PLUS; + if (arg->channel.chan_radar) + chan->info |= WMI_CHAN_INFO_DFS; + if (arg->channel.freq2_radar) + chan->info |= WMI_CHAN_INFO_DFS_FREQ2; + + chan->reg_info_1 = FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR, + arg->channel.max_power) | + FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR, + arg->channel.max_reg_power); + + chan->reg_info_2 = FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX, + arg->channel.max_antenna_gain) | + FIELD_PREP(WMI_CHAN_REG_INFO2_MAX_TX_PWR, + arg->channel.max_power); +} + +int ath11k_wmi_vdev_start(struct ath11k *ar, struct wmi_vdev_start_req_arg *arg, + bool restart) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_start_request_cmd *cmd; + struct sk_buff *skb; + struct wmi_channel *chan; + struct wmi_tlv *tlv; + void *ptr; + int ret, len; + + if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) + return -EINVAL; + + len = sizeof(*cmd) + sizeof(*chan) + TLV_HDR_SIZE; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_start_request_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_START_REQUEST_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = arg->vdev_id; + cmd->beacon_interval = arg->bcn_intval; + cmd->bcn_tx_rate = arg->bcn_tx_rate; + cmd->dtim_period = arg->dtim_period; + cmd->num_noa_descriptors = arg->num_noa_descriptors; + cmd->preferred_rx_streams = arg->pref_rx_streams; + cmd->preferred_tx_streams = arg->pref_tx_streams; + cmd->cac_duration_ms = arg->cac_duration_ms; + cmd->regdomain = arg->regdomain; + cmd->he_ops = arg->he_ops; + + if (!restart) { + if (arg->ssid) { + cmd->ssid.ssid_len = arg->ssid_len; + memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len); + } + if (arg->hidden_ssid) + cmd->flags |= WMI_VDEV_START_HIDDEN_SSID; + if (arg->pmf_enabled) + cmd->flags |= WMI_VDEV_START_PMF_ENABLED; + } + + cmd->flags |= WMI_VDEV_START_LDPC_RX_ENABLED; + + ptr = skb->data + sizeof(*cmd); + chan = ptr; + + ath11k_wmi_put_wmi_channel(chan, arg); + + chan->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_CHANNEL) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*chan) - TLV_HDR_SIZE); + ptr += sizeof(*chan); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, 0); + + /* Note: This is a nested TLV containing: + * [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv].. + */ + + ptr += sizeof(*tlv); + + if (restart) + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_VDEV_RESTART_REQUEST_CMDID); + else + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_VDEV_START_REQUEST_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit vdev_%s cmd\n", + restart ? "restart" : "start"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "vdev %s id 0x%x freq 0x%x mode 0x%x\n", + restart ? "restart" : "start", arg->vdev_id, + arg->channel.freq, arg->channel.mode); + + return ret; +} + +int ath11k_wmi_vdev_up(struct ath11k *ar, u32 vdev_id, u32 aid, const u8 *bssid) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_up_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_vdev_up_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_UP_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->vdev_assoc_id = aid; + + ether_addr_copy(cmd->vdev_bssid.addr, bssid); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_UP_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_VDEV_UP cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI mgmt vdev up id 0x%x assoc id %d bssid %pM\n", + vdev_id, aid, bssid); + + return ret; +} + +int ath11k_wmi_send_peer_create_cmd(struct ath11k *ar, + struct peer_create_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_create_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_peer_create_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_CREATE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, param->peer_addr); + cmd->peer_type = param->peer_type; + cmd->vdev_id = param->vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_CREATE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_PEER_CREATE cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI peer create vdev_id %d peer_addr %pM\n", + param->vdev_id, param->peer_addr); + + return ret; +} + +int ath11k_wmi_send_peer_delete_cmd(struct ath11k *ar, + const u8 *peer_addr, u8 vdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_delete_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_peer_delete_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_DELETE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + cmd->vdev_id = vdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI peer delete vdev_id %d peer_addr %pM\n", + vdev_id, peer_addr); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_DELETE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PEER_DELETE cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_pdev_set_regdomain(struct ath11k *ar, + struct pdev_set_regdomain_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_set_regdomain_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_pdev_set_regdomain_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_SET_REGDOMAIN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->reg_domain = param->current_rd_in_use; + cmd->reg_domain_2g = param->current_rd_2g; + cmd->reg_domain_5g = param->current_rd_5g; + cmd->conformance_test_limit_2g = param->ctl_2g; + cmd->conformance_test_limit_5g = param->ctl_5g; + cmd->dfs_domain = param->dfs_domain; + cmd->pdev_id = param->pdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI pdev regd rd %d rd2g %d rd5g %d domain %d pdev id %d\n", + param->current_rd_in_use, param->current_rd_2g, + param->current_rd_5g, param->dfs_domain, param->pdev_id); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_REGDOMAIN_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PDEV_SET_REGDOMAIN cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_set_peer_param(struct ath11k *ar, const u8 *peer_addr, + u32 vdev_id, u32 param_id, u32 param_val) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_set_param_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_peer_set_param_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_SET_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + cmd->vdev_id = vdev_id; + cmd->param_id = param_id; + cmd->param_value = param_val; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_SET_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PEER_SET_PARAM cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev %d peer 0x%pM set param %d value %d\n", + vdev_id, peer_addr, param_id, param_val); + + return ret; +} + +int ath11k_wmi_send_peer_flush_tids_cmd(struct ath11k *ar, + u8 peer_addr[ETH_ALEN], + struct peer_flush_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_flush_tids_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_peer_flush_tids_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_FLUSH_TIDS_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + cmd->peer_tid_bitmap = param->peer_tid_bitmap; + cmd->vdev_id = param->vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_FLUSH_TIDS_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PEER_FLUSH_TIDS cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI peer flush vdev_id %d peer_addr %pM tids %08x\n", + param->vdev_id, peer_addr, param->peer_tid_bitmap); + + return ret; +} + +int ath11k_wmi_peer_rx_reorder_queue_setup(struct ath11k *ar, + int vdev_id, const u8 *addr, + dma_addr_t paddr, u8 tid, + u8 ba_window_size_valid, + u32 ba_window_size) +{ + struct wmi_peer_reorder_queue_setup_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 wmi_peer_reorder_queue_setup_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_REORDER_QUEUE_SETUP_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, addr); + cmd->vdev_id = vdev_id; + cmd->tid = tid; + cmd->queue_ptr_lo = lower_32_bits(paddr); + cmd->queue_ptr_hi = upper_32_bits(paddr); + cmd->queue_no = tid; + cmd->ba_window_size_valid = ba_window_size_valid; + cmd->ba_window_size = ba_window_size; + + ret = ath11k_wmi_cmd_send(ar->wmi, skb, + WMI_PEER_REORDER_QUEUE_SETUP_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PEER_REORDER_QUEUE_SETUP\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi rx reorder queue setup addr %pM vdev_id %d tid %d\n", + addr, vdev_id, tid); + + return ret; +} + +int +ath11k_wmi_rx_reord_queue_remove(struct ath11k *ar, + struct rx_reorder_queue_remove_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_reorder_queue_remove_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_peer_reorder_queue_remove_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_REORDER_QUEUE_REMOVE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, param->peer_macaddr); + cmd->vdev_id = param->vdev_id; + cmd->tid_mask = param->peer_tid_bitmap; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "%s: peer_macaddr %pM vdev_id %d, tid_map %d", __func__, + param->peer_macaddr, param->vdev_id, param->peer_tid_bitmap); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PEER_REORDER_QUEUE_REMOVE_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PEER_REORDER_QUEUE_REMOVE_CMDID"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id, + u32 param_value, u8 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_set_param_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_pdev_set_param_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + cmd->param_id = param_id; + cmd->param_value = param_value; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_PARAM cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI pdev set param %d pdev id %d value %d\n", + param_id, pdev_id, param_value); + + return ret; +} + +int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_set_ps_mode_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_pdev_set_ps_mode_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_STA_POWERSAVE_MODE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->sta_ps_mode = enable; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_POWERSAVE_MODE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_PARAM cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev set psmode %d vdev id %d\n", + enable, vdev_id); + + return ret; +} + +int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt, + u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_suspend_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_pdev_suspend_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SUSPEND_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->suspend_opt = suspend_opt; + cmd->pdev_id = pdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SUSPEND_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_SUSPEND cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI pdev suspend pdev_id %d\n", pdev_id); + + return ret; +} + +int ath11k_wmi_pdev_resume(struct ath11k *ar, u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_resume_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_pdev_resume_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_RESUME_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI pdev resume pdev id %d\n", pdev_id); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_RESUME_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_RESUME cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +/* TODO FW Support for the cmd is not available yet. + * Can be tested once the command and corresponding + * event is implemented in FW + */ +int ath11k_wmi_pdev_bss_chan_info_request(struct ath11k *ar, + enum wmi_bss_chan_info_req_type type) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_bss_chan_info_req_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_pdev_bss_chan_info_req_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_BSS_CHAN_INFO_REQUEST) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->req_type = type; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI bss chan info req type %d\n", type); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_BSS_CHAN_INFO_REQUEST_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PDEV_BSS_CHAN_INFO_REQUEST cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_set_ap_ps_param_cmd(struct ath11k *ar, u8 *peer_addr, + struct ap_ps_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_ap_ps_peer_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_ap_ps_peer_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_AP_PS_PEER_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->vdev_id; + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + cmd->param = param->param; + cmd->value = param->value; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_AP_PS_PEER_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_AP_PS_PEER_PARAM_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI set ap ps vdev id %d peer %pM param %d value %d\n", + param->vdev_id, peer_addr, param->param, param->value); + + return ret; +} + +int ath11k_wmi_set_sta_ps_param(struct ath11k *ar, u32 vdev_id, + u32 param, u32 param_value) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_sta_powersave_param_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_sta_powersave_param_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_STA_POWERSAVE_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->param = param; + cmd->value = param_value; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI set sta ps vdev_id %d param %d value %d\n", + vdev_id, param, param_value); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_POWERSAVE_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_STA_POWERSAVE_PARAM_CMDID"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_force_fw_hang_cmd(struct ath11k *ar, u32 type, u32 delay_time_ms) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_force_fw_hang_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_force_fw_hang_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_FORCE_FW_HANG_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + + cmd->type = type; + cmd->delay_time_ms = delay_time_ms; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_FORCE_FW_HANG_CMDID); + + if (ret) { + ath11k_warn(ar->ab, "Failed to send WMI_FORCE_FW_HANG_CMDID"); + dev_kfree_skb(skb); + } + return ret; +} + +int ath11k_wmi_vdev_set_param_cmd(struct ath11k *ar, u32 vdev_id, + u32 param_id, u32 param_value) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_set_param_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_vdev_set_param_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_SET_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->param_id = param_id; + cmd->param_value = param_value; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_SET_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_VDEV_SET_PARAM_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev id 0x%x set param %d value %d\n", + vdev_id, param_id, param_value); + + return ret; +} + +int ath11k_wmi_send_stats_request_cmd(struct ath11k *ar, + struct stats_request_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_request_stats_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_request_stats_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_REQUEST_STATS_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->stats_id = param->stats_id; + cmd->vdev_id = param->vdev_id; + cmd->pdev_id = param->pdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_REQUEST_STATS_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_REQUEST_STATS cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI request stats 0x%x vdev id %d pdev id %d\n", + param->stats_id, param->vdev_id, param->pdev_id); + + return ret; +} + +int ath11k_wmi_send_bcn_offload_control_cmd(struct ath11k *ar, + u32 vdev_id, u32 bcn_ctrl_op) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_bcn_offload_ctrl_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_bcn_offload_ctrl_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_BCN_OFFLOAD_CTRL_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->bcn_ctrl_op = bcn_ctrl_op; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI bcn ctrl offload vdev id %d ctrl_op %d\n", + vdev_id, bcn_ctrl_op); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_BCN_OFFLOAD_CTRL_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_BCN_OFFLOAD_CTRL_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id, + struct ieee80211_mutable_offsets *offs, + struct sk_buff *bcn) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_bcn_tmpl_cmd *cmd; + struct wmi_bcn_prb_info *bcn_prb_info; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + int ret, len; + size_t aligned_len = roundup(bcn->len, 4); + + len = sizeof(*cmd) + sizeof(*bcn_prb_info) + TLV_HDR_SIZE + aligned_len; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_bcn_tmpl_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_BCN_TMPL_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->tim_ie_offset = offs->tim_offset; + cmd->csa_switch_count_offset = offs->csa_counter_offs[0]; + cmd->ext_csa_switch_count_offset = offs->csa_counter_offs[1]; + cmd->buf_len = bcn->len; + + ptr = skb->data + sizeof(*cmd); + + bcn_prb_info = ptr; + len = sizeof(*bcn_prb_info); + bcn_prb_info->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_BCN_PRB_INFO) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + bcn_prb_info->caps = 0; + bcn_prb_info->erp = 0; + + ptr += sizeof(*bcn_prb_info); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, aligned_len); + memcpy(tlv->value, bcn->data, bcn->len); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_BCN_TMPL_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_BCN_TMPL_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_vdev_install_key(struct ath11k *ar, + struct wmi_vdev_install_key_arg *arg) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_install_key_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int ret, len; + int key_len_aligned = roundup(arg->key_len, sizeof(uint32_t)); + + len = sizeof(*cmd) + TLV_HDR_SIZE + key_len_aligned; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_install_key_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_INSTALL_KEY_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = arg->vdev_id; + ether_addr_copy(cmd->peer_macaddr.addr, arg->macaddr); + cmd->key_idx = arg->key_idx; + cmd->key_flags = arg->key_flags; + cmd->key_cipher = arg->key_cipher; + cmd->key_len = arg->key_len; + cmd->key_txmic_len = arg->key_txmic_len; + cmd->key_rxmic_len = arg->key_rxmic_len; + + if (arg->key_rsc_counter) + memcpy(&cmd->key_rsc_counter, &arg->key_rsc_counter, + sizeof(struct wmi_key_seq_counter)); + + tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, key_len_aligned); + memcpy(tlv->value, (u8 *)arg->key_data, key_len_aligned); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_INSTALL_KEY_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_VDEV_INSTALL_KEY cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev install key idx %d cipher %d len %d\n", + arg->key_idx, arg->key_cipher, arg->key_len); + + return ret; +} + +static inline void +ath11k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd, + struct peer_assoc_params *param) +{ + cmd->peer_flags = 0; + + if (param->is_wme_set) { + if (param->qos_flag) + cmd->peer_flags |= WMI_PEER_QOS; + if (param->apsd_flag) + cmd->peer_flags |= WMI_PEER_APSD; + if (param->ht_flag) + cmd->peer_flags |= WMI_PEER_HT; + if (param->bw_40) + cmd->peer_flags |= WMI_PEER_40MHZ; + if (param->bw_80) + cmd->peer_flags |= WMI_PEER_80MHZ; + if (param->bw_160) + cmd->peer_flags |= WMI_PEER_160MHZ; + + /* Typically if STBC is enabled for VHT it should be enabled + * for HT as well + **/ + if (param->stbc_flag) + cmd->peer_flags |= WMI_PEER_STBC; + + /* Typically if LDPC is enabled for VHT it should be enabled + * for HT as well + **/ + if (param->ldpc_flag) + cmd->peer_flags |= WMI_PEER_LDPC; + + if (param->static_mimops_flag) + cmd->peer_flags |= WMI_PEER_STATIC_MIMOPS; + if (param->dynamic_mimops_flag) + cmd->peer_flags |= WMI_PEER_DYN_MIMOPS; + if (param->spatial_mux_flag) + cmd->peer_flags |= WMI_PEER_SPATIAL_MUX; + if (param->vht_flag) + cmd->peer_flags |= WMI_PEER_VHT; + if (param->he_flag) + cmd->peer_flags |= WMI_PEER_HE; + if (param->twt_requester) + cmd->peer_flags |= WMI_PEER_TWT_REQ; + if (param->twt_responder) + cmd->peer_flags |= WMI_PEER_TWT_RESP; + } + + /* Suppress authorization for all AUTH modes that need 4-way handshake + * (during re-association). + * Authorization will be done for these modes on key installation. + */ + if (param->auth_flag) + cmd->peer_flags |= WMI_PEER_AUTH; + if (param->need_ptk_4_way) + cmd->peer_flags |= WMI_PEER_NEED_PTK_4_WAY; + else + cmd->peer_flags &= ~WMI_PEER_NEED_PTK_4_WAY; + if (param->need_gtk_2_way) + cmd->peer_flags |= WMI_PEER_NEED_GTK_2_WAY; + /* safe mode bypass the 4-way handshake */ + if (param->safe_mode_enabled) + cmd->peer_flags &= ~(WMI_PEER_NEED_PTK_4_WAY | + WMI_PEER_NEED_GTK_2_WAY); + + if (param->is_pmf_enabled) + cmd->peer_flags |= WMI_PEER_PMF; + + /* Disable AMSDU for station transmit, if user configures it */ + /* Disable AMSDU for AP transmit to 11n Stations, if user configures + * it + * if (param->amsdu_disable) Add after FW support + **/ + + /* Target asserts if node is marked HT and all MCS is set to 0. + * Mark the node as non-HT if all the mcs rates are disabled through + * iwpriv + **/ + if (param->peer_ht_rates.num_rates == 0) + cmd->peer_flags &= ~WMI_PEER_HT; +} + +int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar, + struct peer_assoc_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_assoc_complete_cmd *cmd; + struct wmi_vht_rate_set *mcs; + struct wmi_he_rate_set *he_mcs; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + u32 peer_legacy_rates_align; + u32 peer_ht_rates_align; + int i, ret, len; + + peer_legacy_rates_align = roundup(param->peer_legacy_rates.num_rates, + sizeof(u32)); + peer_ht_rates_align = roundup(param->peer_ht_rates.num_rates, + sizeof(u32)); + + len = sizeof(*cmd) + + TLV_HDR_SIZE + (peer_legacy_rates_align * sizeof(u8)) + + TLV_HDR_SIZE + (peer_ht_rates_align * sizeof(u8)) + + sizeof(*mcs) + TLV_HDR_SIZE + + (sizeof(*he_mcs) * param->peer_he_mcs_count); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + ptr = skb->data; + + cmd = ptr; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PEER_ASSOC_COMPLETE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->vdev_id; + + cmd->peer_new_assoc = param->peer_new_assoc; + cmd->peer_associd = param->peer_associd; + + ath11k_wmi_copy_peer_flags(cmd, param); + + ether_addr_copy(cmd->peer_macaddr.addr, param->peer_mac); + + cmd->peer_rate_caps = param->peer_rate_caps; + cmd->peer_caps = param->peer_caps; + cmd->peer_listen_intval = param->peer_listen_intval; + cmd->peer_ht_caps = param->peer_ht_caps; + cmd->peer_max_mpdu = param->peer_max_mpdu; + cmd->peer_mpdu_density = param->peer_mpdu_density; + cmd->peer_vht_caps = param->peer_vht_caps; + cmd->peer_phymode = param->peer_phymode; + + /* Update 11ax capabilities */ + cmd->peer_he_cap_info = param->peer_he_cap_macinfo[0]; + cmd->peer_he_cap_info_ext = param->peer_he_cap_macinfo[1]; + cmd->peer_he_cap_info_internal = param->peer_he_cap_macinfo_internal; + cmd->peer_he_ops = param->peer_he_ops; + memcpy(&cmd->peer_he_cap_phy, ¶m->peer_he_cap_phyinfo, + sizeof(param->peer_he_cap_phyinfo)); + memcpy(&cmd->peer_ppet, ¶m->peer_ppet, + sizeof(param->peer_ppet)); + + /* Update peer legacy rate information */ + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, peer_legacy_rates_align); + + ptr += TLV_HDR_SIZE; + + cmd->num_peer_legacy_rates = param->peer_legacy_rates.num_rates; + memcpy(ptr, param->peer_legacy_rates.rates, + param->peer_legacy_rates.num_rates); + + /* Update peer HT rate information */ + ptr += peer_legacy_rates_align; + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, peer_ht_rates_align); + ptr += TLV_HDR_SIZE; + cmd->num_peer_ht_rates = param->peer_ht_rates.num_rates; + memcpy(ptr, param->peer_ht_rates.rates, + param->peer_ht_rates.num_rates); + + /* VHT Rates */ + ptr += peer_ht_rates_align; + + mcs = ptr; + + mcs->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VHT_RATE_SET) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*mcs) - TLV_HDR_SIZE); + + cmd->peer_nss = param->peer_nss; + + /* Update bandwidth-NSS mapping */ + cmd->peer_bw_rxnss_override = 0; + cmd->peer_bw_rxnss_override |= param->peer_bw_rxnss_override; + + if (param->vht_capable) { + mcs->rx_max_rate = param->rx_max_rate; + mcs->rx_mcs_set = param->rx_mcs_set; + mcs->tx_max_rate = param->tx_max_rate; + mcs->tx_mcs_set = param->tx_mcs_set; + } + + /* HE Rates */ + cmd->peer_he_mcs = param->peer_he_mcs_count; + + ptr += sizeof(*mcs); + + len = param->peer_he_mcs_count * sizeof(*he_mcs); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + + /* Loop through the HE rate set */ + for (i = 0; i < param->peer_he_mcs_count; i++) { + he_mcs = ptr; + he_mcs->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_HE_RATE_SET) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*he_mcs) - TLV_HDR_SIZE); + + he_mcs->rx_mcs_set = param->peer_he_rx_mcs_set[i]; + he_mcs->tx_mcs_set = param->peer_he_tx_mcs_set[i]; + ptr += sizeof(*he_mcs); + } + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_ASSOC_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PEER_ASSOC_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi peer assoc vdev id %d assoc id %d peer mac %pM peer_flags %x rate_caps %x peer_caps %x listen_intval %d ht_caps %x max_mpdu %d nss %d phymode %d peer_mpdu_density %d vht_caps %x he cap_info %x he ops %x he cap_info_ext %x he phy %x %x %x peer_bw_rxnss_override %x\n", + cmd->vdev_id, cmd->peer_associd, param->peer_mac, + cmd->peer_flags, cmd->peer_rate_caps, cmd->peer_caps, + cmd->peer_listen_intval, cmd->peer_ht_caps, + cmd->peer_max_mpdu, cmd->peer_nss, cmd->peer_phymode, + cmd->peer_mpdu_density, + cmd->peer_vht_caps, cmd->peer_he_cap_info, + cmd->peer_he_ops, cmd->peer_he_cap_info_ext, + cmd->peer_he_cap_phy[0], cmd->peer_he_cap_phy[1], + cmd->peer_he_cap_phy[2], + cmd->peer_bw_rxnss_override); + + return ret; +} + +void ath11k_wmi_start_scan_init(struct ath11k *ar, + struct scan_req_params *arg) +{ + /* setup commonly used values */ + arg->scan_req_id = 1; + arg->scan_priority = WMI_SCAN_PRIORITY_LOW; + arg->dwell_time_active = 50; + arg->dwell_time_active_2g = 0; + arg->dwell_time_passive = 150; + arg->min_rest_time = 50; + arg->max_rest_time = 500; + arg->repeat_probe_time = 0; + arg->probe_spacing_time = 0; + arg->idle_time = 0; + arg->max_scan_time = 20000; + arg->probe_delay = 5; + arg->notify_scan_events = WMI_SCAN_EVENT_STARTED | + WMI_SCAN_EVENT_COMPLETED | + WMI_SCAN_EVENT_BSS_CHANNEL | + WMI_SCAN_EVENT_FOREIGN_CHAN | + WMI_SCAN_EVENT_DEQUEUED; + arg->scan_flags |= WMI_SCAN_CHAN_STAT_EVENT; + arg->num_bssid = 1; +} + +static inline void +ath11k_wmi_copy_scan_event_cntrl_flags(struct wmi_start_scan_cmd *cmd, + struct scan_req_params *param) +{ + /* Scan events subscription */ + if (param->scan_ev_started) + cmd->notify_scan_events |= WMI_SCAN_EVENT_STARTED; + if (param->scan_ev_completed) + cmd->notify_scan_events |= WMI_SCAN_EVENT_COMPLETED; + if (param->scan_ev_bss_chan) + cmd->notify_scan_events |= WMI_SCAN_EVENT_BSS_CHANNEL; + if (param->scan_ev_foreign_chan) + cmd->notify_scan_events |= WMI_SCAN_EVENT_FOREIGN_CHAN; + if (param->scan_ev_dequeued) + cmd->notify_scan_events |= WMI_SCAN_EVENT_DEQUEUED; + if (param->scan_ev_preempted) + cmd->notify_scan_events |= WMI_SCAN_EVENT_PREEMPTED; + if (param->scan_ev_start_failed) + cmd->notify_scan_events |= WMI_SCAN_EVENT_START_FAILED; + if (param->scan_ev_restarted) + cmd->notify_scan_events |= WMI_SCAN_EVENT_RESTARTED; + if (param->scan_ev_foreign_chn_exit) + cmd->notify_scan_events |= WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT; + if (param->scan_ev_suspended) + cmd->notify_scan_events |= WMI_SCAN_EVENT_SUSPENDED; + if (param->scan_ev_resumed) + cmd->notify_scan_events |= WMI_SCAN_EVENT_RESUMED; + + /** Set scan control flags */ + cmd->scan_ctrl_flags = 0; + if (param->scan_f_passive) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; + if (param->scan_f_strict_passive_pch) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_STRICT_PASSIVE_ON_PCHN; + if (param->scan_f_promisc_mode) + cmd->scan_ctrl_flags |= WMI_SCAN_FILTER_PROMISCUOS; + if (param->scan_f_capture_phy_err) + cmd->scan_ctrl_flags |= WMI_SCAN_CAPTURE_PHY_ERROR; + if (param->scan_f_half_rate) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_HALF_RATE_SUPPORT; + if (param->scan_f_quarter_rate) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_QUARTER_RATE_SUPPORT; + if (param->scan_f_cck_rates) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES; + if (param->scan_f_ofdm_rates) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_OFDM_RATES; + if (param->scan_f_chan_stat_evnt) + cmd->scan_ctrl_flags |= WMI_SCAN_CHAN_STAT_EVENT; + if (param->scan_f_filter_prb_req) + cmd->scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ; + if (param->scan_f_bcast_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_BCAST_PROBE_REQ; + if (param->scan_f_offchan_mgmt_tx) + cmd->scan_ctrl_flags |= WMI_SCAN_OFFCHAN_MGMT_TX; + if (param->scan_f_offchan_data_tx) + cmd->scan_ctrl_flags |= WMI_SCAN_OFFCHAN_DATA_TX; + if (param->scan_f_force_active_dfs_chn) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_FORCE_ACTIVE_ON_DFS; + if (param->scan_f_add_tpc_ie_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_TPC_IE_IN_PROBE_REQ; + if (param->scan_f_add_ds_ie_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_DS_IE_IN_PROBE_REQ; + if (param->scan_f_add_spoofed_mac_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_SPOOF_MAC_IN_PROBE_REQ; + if (param->scan_f_add_rand_seq_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_RANDOM_SEQ_NO_IN_PROBE_REQ; + if (param->scan_f_en_ie_whitelist_in_probe) + cmd->scan_ctrl_flags |= + WMI_SCAN_ENABLE_IE_WHTELIST_IN_PROBE_REQ; + + /* for adaptive scan mode using 3 bits (21 - 23 bits) */ + WMI_SCAN_SET_DWELL_MODE(cmd->scan_ctrl_flags, + param->adaptive_dwell_time_mode); +} + +int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, + struct scan_req_params *params) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_start_scan_cmd *cmd; + struct wmi_ssid *ssid = NULL; + struct wmi_mac_addr *bssid; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + int i, ret, len; + u32 *tmp_ptr; + u8 extraie_len_with_pad = 0; + + len = sizeof(*cmd); + + len += TLV_HDR_SIZE; + if (params->num_chan) + len += params->num_chan * sizeof(u32); + + len += TLV_HDR_SIZE; + if (params->num_ssids) + len += params->num_ssids * sizeof(*ssid); + + len += TLV_HDR_SIZE; + if (params->num_bssid) + len += sizeof(*bssid) * params->num_bssid; + + len += TLV_HDR_SIZE; + if (params->extraie.len) + extraie_len_with_pad = + roundup(params->extraie.len, sizeof(u32)); + len += extraie_len_with_pad; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + ptr = skb->data; + + cmd = ptr; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_START_SCAN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->scan_id = params->scan_id; + cmd->scan_req_id = params->scan_req_id; + cmd->vdev_id = params->vdev_id; + cmd->scan_priority = params->scan_priority; + cmd->notify_scan_events = params->notify_scan_events; + + ath11k_wmi_copy_scan_event_cntrl_flags(cmd, params); + + cmd->dwell_time_active = params->dwell_time_active; + cmd->dwell_time_active_2g = params->dwell_time_active_2g; + cmd->dwell_time_passive = params->dwell_time_passive; + cmd->min_rest_time = params->min_rest_time; + cmd->max_rest_time = params->max_rest_time; + cmd->repeat_probe_time = params->repeat_probe_time; + cmd->probe_spacing_time = params->probe_spacing_time; + cmd->idle_time = params->idle_time; + cmd->max_scan_time = params->max_scan_time; + cmd->probe_delay = params->probe_delay; + cmd->burst_duration = params->burst_duration; + cmd->num_chan = params->num_chan; + cmd->num_bssid = params->num_bssid; + cmd->num_ssids = params->num_ssids; + cmd->ie_len = params->extraie.len; + cmd->n_probes = params->n_probes; + + ptr += sizeof(*cmd); + + len = params->num_chan * sizeof(u32); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + tmp_ptr = (u32 *)ptr; + + for (i = 0; i < params->num_chan; ++i) + tmp_ptr[i] = params->chan_list[i]; + + ptr += len; + + len = params->num_ssids * sizeof(*ssid); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + + if (params->num_ssids) { + ssid = ptr; + for (i = 0; i < params->num_ssids; ++i) { + ssid->ssid_len = params->ssid[i].length; + memcpy(ssid->ssid, params->ssid[i].ssid, + params->ssid[i].length); + ssid++; + } + } + + ptr += (params->num_ssids * sizeof(*ssid)); + len = params->num_bssid * sizeof(*bssid); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + bssid = ptr; + + if (params->num_bssid) { + for (i = 0; i < params->num_bssid; ++i) { + ether_addr_copy(bssid->addr, + params->bssid_list[i].addr); + bssid++; + } + } + + ptr += params->num_bssid * sizeof(*bssid); + + len = extraie_len_with_pad; + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + + if (params->extraie.len) + memcpy(ptr, params->extraie.ptr, + params->extraie.len); + + ptr += extraie_len_with_pad; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_START_SCAN_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_START_SCAN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, + struct scan_cancel_param *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_stop_scan_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_stop_scan_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_STOP_SCAN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->vdev_id; + cmd->requestor = param->requester; + cmd->scan_id = param->scan_id; + cmd->pdev_id = param->pdev_id; + /* stop the scan with the corresponding scan_id */ + if (param->req_type == WLAN_SCAN_CANCEL_PDEV_ALL) { + /* Cancelling all scans */ + cmd->req_type = WMI_SCAN_STOP_ALL; + } else if (param->req_type == WLAN_SCAN_CANCEL_VDEV_ALL) { + /* Cancelling VAP scans */ + cmd->req_type = WMI_SCN_STOP_VAP_ALL; + } else if (param->req_type == WLAN_SCAN_CANCEL_SINGLE) { + /* Cancelling specific scan */ + cmd->req_type = WMI_SCAN_STOP_ONE; + } else { + ath11k_warn(ar->ab, "invalid scan cancel param %d", + param->req_type); + dev_kfree_skb(skb); + return -EINVAL; + } + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_STOP_SCAN_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_STOP_SCAN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, + struct scan_chan_list_params *chan_list) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_scan_chan_list_cmd *cmd; + struct sk_buff *skb; + struct wmi_channel *chan_info; + struct channel_param *tchan_info; + struct wmi_tlv *tlv; + void *ptr; + int i, ret, len; + u32 *reg1, *reg2; + + len = sizeof(*cmd) + TLV_HDR_SIZE + + sizeof(*chan_info) * chan_list->nallchans; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_scan_chan_list_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_SCAN_CHAN_LIST_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI no.of chan = %d len = %d\n", chan_list->nallchans, len); + cmd->pdev_id = chan_list->pdev_id; + cmd->num_scan_chans = chan_list->nallchans; + + ptr = skb->data + sizeof(*cmd); + + len = sizeof(*chan_info) * chan_list->nallchans; + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + ptr += TLV_HDR_SIZE; + + tchan_info = &chan_list->ch_param[0]; + + for (i = 0; i < chan_list->nallchans; ++i) { + chan_info = ptr; + memset(chan_info, 0, sizeof(*chan_info)); + len = sizeof(*chan_info); + chan_info->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_CHANNEL) | + FIELD_PREP(WMI_TLV_LEN, + len - TLV_HDR_SIZE); + + reg1 = &chan_info->reg_info_1; + reg2 = &chan_info->reg_info_2; + chan_info->mhz = tchan_info->mhz; + chan_info->band_center_freq1 = tchan_info->cfreq1; + chan_info->band_center_freq2 = tchan_info->cfreq2; + + if (tchan_info->is_chan_passive) + chan_info->info |= WMI_CHAN_INFO_PASSIVE; + if (tchan_info->allow_he) + chan_info->info |= WMI_CHAN_INFO_ALLOW_HE; + else if (tchan_info->allow_vht) + chan_info->info |= WMI_CHAN_INFO_ALLOW_VHT; + else if (tchan_info->allow_ht) + chan_info->info |= WMI_CHAN_INFO_ALLOW_HT; + if (tchan_info->half_rate) + chan_info->info |= WMI_CHAN_INFO_HALF_RATE; + if (tchan_info->quarter_rate) + chan_info->info |= WMI_CHAN_INFO_QUARTER_RATE; + + chan_info->info |= FIELD_PREP(WMI_CHAN_INFO_MODE, + tchan_info->phy_mode); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MIN_PWR, + tchan_info->minpower); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR, + tchan_info->maxpower); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR, + tchan_info->maxregpower); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_REG_CLS, + tchan_info->reg_class_id); + *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX, + tchan_info->antennamax); + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI chan scan list chan[%d] = %u\n", + i, chan_info->mhz); + + ptr += sizeof(*chan_info); + + tchan_info++; + } + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_SCAN_CHAN_LIST_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_SCAN_CHAN_LIST cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, + struct wmi_wmm_params_all_arg *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_set_wmm_params_cmd *cmd; + struct wmi_wmm_params *wmm_param; + struct wmi_wmm_params_arg *wmi_wmm_arg; + struct sk_buff *skb; + int ret, ac; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_set_wmm_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_SET_WMM_PARAMS_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->wmm_param_type = 0; + + for (ac = 0; ac < WME_NUM_AC; ac++) { + switch (ac) { + case WME_AC_BE: + wmi_wmm_arg = ¶m->ac_be; + break; + case WME_AC_BK: + wmi_wmm_arg = ¶m->ac_bk; + break; + case WME_AC_VI: + wmi_wmm_arg = ¶m->ac_vi; + break; + case WME_AC_VO: + wmi_wmm_arg = ¶m->ac_vo; + break; + } + + wmm_param = (struct wmi_wmm_params *)&cmd->wmm_params[ac]; + wmm_param->tlv_header = + FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_SET_WMM_PARAMS_CMD) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*wmm_param) - TLV_HDR_SIZE); + + wmm_param->aifs = wmi_wmm_arg->aifs; + wmm_param->cwmin = wmi_wmm_arg->cwmin; + wmm_param->cwmax = wmi_wmm_arg->cwmax; + wmm_param->txoplimit = wmi_wmm_arg->txop; + wmm_param->acm = wmi_wmm_arg->acm; + wmm_param->no_ack = wmi_wmm_arg->no_ack; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi wmm set ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n", + ac, wmm_param->aifs, wmm_param->cwmin, + wmm_param->cwmax, wmm_param->txoplimit, + wmm_param->acm, wmm_param->no_ack); + } + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_VDEV_SET_WMM_PARAMS_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_VDEV_SET_WMM_PARAMS_CMDID"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_dfs_phyerr_offload_enable_cmd(struct ath11k *ar, + u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_dfs_phyerr_offload_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_dfs_phyerr_offload_cmd *)skb->data; + cmd->tlv_header = + FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = pdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI dfs phy err offload enable pdev id %d\n", pdev_id); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_pdev_peer_pktlog_filter(struct ath11k *ar, u8 *addr, u8 enable) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_pktlog_filter_cmd *cmd; + struct wmi_pdev_pktlog_filter_info *info; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + int ret, len; + + len = sizeof(*cmd) + sizeof(*info) + TLV_HDR_SIZE; + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_pktlog_filter_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PEER_PKTLOG_FILTER_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = DP_HW2SW_MACID(ar->pdev->pdev_id); + cmd->num_mac = 1; + cmd->enable = enable; + + ptr = skb->data + sizeof(*cmd); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*info)); + + ptr += TLV_HDR_SIZE; + info = ptr; + + ether_addr_copy(info->peer_macaddr.addr, addr); + info->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PEER_PKTLOG_FILTER_INFO) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*info) - TLV_HDR_SIZE); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_PKTLOG_FILTER_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_PKTLOG_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int +ath11k_wmi_send_init_country_cmd(struct ath11k *ar, + struct wmi_init_country_params init_cc_params) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_init_country_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_init_country_cmd *)skb->data; + cmd->tlv_header = + FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_SET_INIT_COUNTRY_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = ar->pdev->pdev_id; + + switch (init_cc_params.flags) { + case ALPHA_IS_SET: + cmd->init_cc_type = WMI_COUNTRY_INFO_TYPE_ALPHA; + memcpy((u8 *)&cmd->cc_info.alpha2, + init_cc_params.cc_info.alpha2, 3); + break; + case CC_IS_SET: + cmd->init_cc_type = WMI_COUNTRY_INFO_TYPE_COUNTRY_CODE; + cmd->cc_info.country_code = init_cc_params.cc_info.country_code; + break; + case REGDMN_IS_SET: + cmd->init_cc_type = WMI_COUNTRY_INFO_TYPE_REGDOMAIN; + cmd->cc_info.regdom_id = init_cc_params.cc_info.regdom_id; + break; + default: + ret = -EINVAL; + goto out; + } + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_SET_INIT_COUNTRY_CMDID); + +out: + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_SET_INIT_COUNTRY CMD :%d\n", + ret); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pktlog_enable_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_pktlog_enable_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PKTLOG_ENABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = DP_HW2SW_MACID(ar->pdev->pdev_id); + cmd->evlist = pktlog_filter; + cmd->enable = ATH11K_WMI_PKTLOG_ENABLE_FORCE; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_PKTLOG_ENABLE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_PKTLOG_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_pdev_pktlog_disable(struct ath11k *ar) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pktlog_disable_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_pktlog_disable_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PKTLOG_DISABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = DP_HW2SW_MACID(ar->pdev->pdev_id); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_PKTLOG_DISABLE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_PKTLOG_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int +ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_twt_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_twt_enable_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_ENABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + cmd->sta_cong_timer_ms = ATH11K_TWT_DEF_STA_CONG_TIMER_MS; + cmd->default_slot_size = ATH11K_TWT_DEF_DEFAULT_SLOT_SIZE; + cmd->congestion_thresh_setup = ATH11K_TWT_DEF_CONGESTION_THRESH_SETUP; + cmd->congestion_thresh_teardown = + ATH11K_TWT_DEF_CONGESTION_THRESH_TEARDOWN; + cmd->congestion_thresh_critical = + ATH11K_TWT_DEF_CONGESTION_THRESH_CRITICAL; + cmd->interference_thresh_teardown = + ATH11K_TWT_DEF_INTERFERENCE_THRESH_TEARDOWN; + cmd->interference_thresh_setup = + ATH11K_TWT_DEF_INTERFERENCE_THRESH_SETUP; + cmd->min_no_sta_setup = ATH11K_TWT_DEF_MIN_NO_STA_SETUP; + cmd->min_no_sta_teardown = ATH11K_TWT_DEF_MIN_NO_STA_TEARDOWN; + cmd->no_of_bcast_mcast_slots = ATH11K_TWT_DEF_NO_OF_BCAST_MCAST_SLOTS; + cmd->min_no_twt_slots = ATH11K_TWT_DEF_MIN_NO_TWT_SLOTS; + cmd->max_no_sta_twt = ATH11K_TWT_DEF_MAX_NO_STA_TWT; + cmd->mode_check_interval = ATH11K_TWT_DEF_MODE_CHECK_INTERVAL; + cmd->add_sta_slot_interval = ATH11K_TWT_DEF_ADD_STA_SLOT_INTERVAL; + cmd->remove_sta_slot_interval = + ATH11K_TWT_DEF_REMOVE_STA_SLOT_INTERVAL; + /* TODO add MBSSID support */ + cmd->mbss_support = 0; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_TWT_ENABLE_CMDID); + if (ret) { + ath11k_warn(ab, "Failed to send WMI_TWT_ENABLE_CMDID"); + dev_kfree_skb(skb); + } + return ret; +} + +int +ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_twt_disable_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_twt_disable_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_DISABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_TWT_DISABLE_CMDID); + if (ret) { + ath11k_warn(ab, "Failed to send WMI_TWT_DISABLE_CMDID"); + dev_kfree_skb(skb); + } + return ret; +} + +int +ath11k_wmi_send_obss_spr_cmd(struct ath11k *ar, u32 vdev_id, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct wmi_obss_spatial_reuse_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_spatial_reuse_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_OBSS_SPATIAL_REUSE_SET_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->enable = he_obss_pd->enable; + cmd->obss_min = he_obss_pd->min_offset; + cmd->obss_max = he_obss_pd->max_offset; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_OBSS_PD_SPATIAL_REUSE_CMDID); + if (ret) { + ath11k_warn(ab, + "Failed to send WMI_PDEV_OBSS_PD_SPATIAL_REUSE_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) +{ + u8 i; + struct ath11k_hal_reg_capabilities_ext *hal_reg_cap; + struct ath11k_pdev *pdev; + + for (i = 0; i < soc->num_radios; i++) { + pdev = &soc->pdevs[i]; + hal_reg_cap = &soc->hal_reg_cap[i]; + band_to_mac[i].pdev_id = pdev->pdev_id; + + switch (pdev->cap.supported_bands) { + case WMI_HOST_WLAN_2G_5G_CAP: + band_to_mac[i].start_freq = hal_reg_cap->low_2ghz_chan; + band_to_mac[i].end_freq = hal_reg_cap->high_5ghz_chan; + break; + case WMI_HOST_WLAN_2G_CAP: + band_to_mac[i].start_freq = hal_reg_cap->low_2ghz_chan; + band_to_mac[i].end_freq = hal_reg_cap->high_2ghz_chan; + break; + case WMI_HOST_WLAN_5G_CAP: + band_to_mac[i].start_freq = hal_reg_cap->low_5ghz_chan; + band_to_mac[i].end_freq = hal_reg_cap->high_5ghz_chan; + break; + default: + break; + } + } +} + +static void +ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg, + struct target_resource_config *tg_cfg) +{ + wmi_cfg->num_vdevs = tg_cfg->num_vdevs; + wmi_cfg->num_peers = tg_cfg->num_peers; + wmi_cfg->num_offload_peers = tg_cfg->num_offload_peers; + wmi_cfg->num_offload_reorder_buffs = tg_cfg->num_offload_reorder_buffs; + wmi_cfg->num_peer_keys = tg_cfg->num_peer_keys; + wmi_cfg->num_tids = tg_cfg->num_tids; + wmi_cfg->ast_skid_limit = tg_cfg->ast_skid_limit; + wmi_cfg->tx_chain_mask = tg_cfg->tx_chain_mask; + wmi_cfg->rx_chain_mask = tg_cfg->rx_chain_mask; + wmi_cfg->rx_timeout_pri[0] = tg_cfg->rx_timeout_pri[0]; + wmi_cfg->rx_timeout_pri[1] = tg_cfg->rx_timeout_pri[1]; + wmi_cfg->rx_timeout_pri[2] = tg_cfg->rx_timeout_pri[2]; + wmi_cfg->rx_timeout_pri[3] = tg_cfg->rx_timeout_pri[3]; + wmi_cfg->rx_decap_mode = tg_cfg->rx_decap_mode; + wmi_cfg->scan_max_pending_req = tg_cfg->scan_max_pending_req; + wmi_cfg->bmiss_offload_max_vdev = tg_cfg->bmiss_offload_max_vdev; + wmi_cfg->roam_offload_max_vdev = tg_cfg->roam_offload_max_vdev; + wmi_cfg->roam_offload_max_ap_profiles = + tg_cfg->roam_offload_max_ap_profiles; + wmi_cfg->num_mcast_groups = tg_cfg->num_mcast_groups; + wmi_cfg->num_mcast_table_elems = tg_cfg->num_mcast_table_elems; + wmi_cfg->mcast2ucast_mode = tg_cfg->mcast2ucast_mode; + wmi_cfg->tx_dbg_log_size = tg_cfg->tx_dbg_log_size; + wmi_cfg->num_wds_entries = tg_cfg->num_wds_entries; + wmi_cfg->dma_burst_size = tg_cfg->dma_burst_size; + wmi_cfg->mac_aggr_delim = tg_cfg->mac_aggr_delim; + wmi_cfg->rx_skip_defrag_timeout_dup_detection_check = + tg_cfg->rx_skip_defrag_timeout_dup_detection_check; + wmi_cfg->vow_config = tg_cfg->vow_config; + wmi_cfg->gtk_offload_max_vdev = tg_cfg->gtk_offload_max_vdev; + wmi_cfg->num_msdu_desc = tg_cfg->num_msdu_desc; + wmi_cfg->max_frag_entries = tg_cfg->max_frag_entries; + wmi_cfg->num_tdls_vdevs = tg_cfg->num_tdls_vdevs; + wmi_cfg->num_tdls_conn_table_entries = + tg_cfg->num_tdls_conn_table_entries; + wmi_cfg->beacon_tx_offload_max_vdev = + tg_cfg->beacon_tx_offload_max_vdev; + wmi_cfg->num_multicast_filter_entries = + tg_cfg->num_multicast_filter_entries; + wmi_cfg->num_wow_filters = tg_cfg->num_wow_filters; + wmi_cfg->num_keep_alive_pattern = tg_cfg->num_keep_alive_pattern; + wmi_cfg->keep_alive_pattern_size = tg_cfg->keep_alive_pattern_size; + wmi_cfg->max_tdls_concurrent_sleep_sta = + tg_cfg->max_tdls_concurrent_sleep_sta; + wmi_cfg->max_tdls_concurrent_buffer_sta = + tg_cfg->max_tdls_concurrent_buffer_sta; + wmi_cfg->wmi_send_separate = tg_cfg->wmi_send_separate; + wmi_cfg->num_ocb_vdevs = tg_cfg->num_ocb_vdevs; + wmi_cfg->num_ocb_channels = tg_cfg->num_ocb_channels; + wmi_cfg->num_ocb_schedules = tg_cfg->num_ocb_schedules; + wmi_cfg->bpf_instruction_size = tg_cfg->bpf_instruction_size; + wmi_cfg->max_bssid_rx_filters = tg_cfg->max_bssid_rx_filters; + wmi_cfg->use_pdev_id = tg_cfg->use_pdev_id; + wmi_cfg->flag1 = tg_cfg->atf_config; + wmi_cfg->peer_map_unmap_v2_support = tg_cfg->peer_map_unmap_v2_support; + wmi_cfg->sched_params = tg_cfg->sched_params; + wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count; + wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count; +} + +static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, + struct wmi_init_cmd_param *param) +{ + struct ath11k_base *ab = wmi->wmi_ab->ab; + struct sk_buff *skb; + struct wmi_init_cmd *cmd; + struct wmi_resource_config *cfg; + struct wmi_pdev_set_hw_mode_cmd_param *hw_mode; + struct wmi_pdev_band_to_mac *band_to_mac; + struct wlan_host_mem_chunk *host_mem_chunks; + struct wmi_tlv *tlv; + size_t ret, len; + void *ptr; + u32 hw_mode_len = 0; + u16 idx; + + if (param->hw_mode_id != WMI_HOST_HW_MODE_MAX) + hw_mode_len = sizeof(*hw_mode) + TLV_HDR_SIZE + + (param->num_band_to_mac * sizeof(*band_to_mac)); + + len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(*cfg) + hw_mode_len + + (sizeof(*host_mem_chunks) * WMI_MAX_MEM_REQS); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_init_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_INIT_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ptr = skb->data + sizeof(*cmd); + cfg = ptr; + + ath11k_wmi_copy_resource_config(cfg, param->res_cfg); + + cfg->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_RESOURCE_CONFIG) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cfg) - TLV_HDR_SIZE); + + ptr += sizeof(*cfg); + host_mem_chunks = ptr + TLV_HDR_SIZE; + len = sizeof(struct wlan_host_mem_chunk); + + for (idx = 0; idx < param->num_mem_chunks; ++idx) { + host_mem_chunks[idx].tlv_header = + FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_WLAN_HOST_MEMORY_CHUNK) | + FIELD_PREP(WMI_TLV_LEN, len); + + host_mem_chunks[idx].ptr = param->mem_chunks[idx].paddr; + host_mem_chunks[idx].size = param->mem_chunks[idx].len; + host_mem_chunks[idx].req_id = param->mem_chunks[idx].req_id; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "WMI host mem chunk req_id %d paddr 0x%llx len %d\n", + param->mem_chunks[idx].req_id, + (u64)param->mem_chunks[idx].paddr, + param->mem_chunks[idx].len); + } + cmd->num_host_mem_chunks = param->num_mem_chunks; + len = sizeof(struct wlan_host_mem_chunk) * param->num_mem_chunks; + + /* num_mem_chunks is zero */ + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE + len; + + if (param->hw_mode_id != WMI_HOST_HW_MODE_MAX) { + hw_mode = (struct wmi_pdev_set_hw_mode_cmd_param *)ptr; + hw_mode->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_SET_HW_MODE_CMD) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*hw_mode) - TLV_HDR_SIZE); + + hw_mode->hw_mode_index = param->hw_mode_id; + hw_mode->num_band_to_mac = param->num_band_to_mac; + + ptr += sizeof(*hw_mode); + + len = param->num_band_to_mac * sizeof(*band_to_mac); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + len = sizeof(*band_to_mac); + + for (idx = 0; idx < param->num_band_to_mac; idx++) { + band_to_mac = (void *)ptr; + + band_to_mac->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_BAND_TO_MAC) | + FIELD_PREP(WMI_TLV_LEN, + len - TLV_HDR_SIZE); + band_to_mac->pdev_id = param->band_to_mac[idx].pdev_id; + band_to_mac->start_freq = + param->band_to_mac[idx].start_freq; + band_to_mac->end_freq = + param->band_to_mac[idx].end_freq; + ptr += sizeof(*band_to_mac); + } + } + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_INIT_CMDID); + if (ret) { + ath11k_warn(ab, "failed to send WMI_INIT_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_wait_for_service_ready(struct ath11k_base *ab) +{ + unsigned long time_left; + + time_left = wait_for_completion_timeout(&ab->wmi_ab.service_ready, + WMI_SERVICE_READY_TIMEOUT_HZ); + if (!time_left) + return -ETIMEDOUT; + + return 0; +} + +int ath11k_wmi_wait_for_unified_ready(struct ath11k_base *ab) +{ + unsigned long time_left; + + time_left = wait_for_completion_timeout(&ab->wmi_ab.unified_ready, + WMI_SERVICE_READY_TIMEOUT_HZ); + if (!time_left) + return -ETIMEDOUT; + + return 0; +} + +int ath11k_wmi_cmd_init(struct ath11k_base *ab) +{ + struct ath11k_wmi_base *wmi_sc = &ab->wmi_ab; + struct wmi_init_cmd_param init_param; + struct target_resource_config config; + + memset(&init_param, 0, sizeof(init_param)); + memset(&config, 0, sizeof(config)); + + config.num_vdevs = ab->num_radios * TARGET_NUM_VDEVS; + + if (ab->num_radios == 2) { + config.num_peers = TARGET_NUM_PEERS(DBS); + config.num_tids = TARGET_NUM_TIDS(DBS); + } else if (ab->num_radios == 3) { + config.num_peers = TARGET_NUM_PEERS(DBS_SBS); + config.num_tids = TARGET_NUM_TIDS(DBS_SBS); + } else { + /* Control should not reach here */ + config.num_peers = TARGET_NUM_PEERS(SINGLE); + config.num_tids = TARGET_NUM_TIDS(SINGLE); + } + config.num_offload_peers = TARGET_NUM_OFFLD_PEERS; + config.num_offload_reorder_buffs = TARGET_NUM_OFFLD_REORDER_BUFFS; + config.num_peer_keys = TARGET_NUM_PEER_KEYS; + config.ast_skid_limit = TARGET_AST_SKID_LIMIT; + config.tx_chain_mask = (1 << ab->target_caps.num_rf_chains) - 1; + config.rx_chain_mask = (1 << ab->target_caps.num_rf_chains) - 1; + config.rx_timeout_pri[0] = TARGET_RX_TIMEOUT_LO_PRI; + config.rx_timeout_pri[1] = TARGET_RX_TIMEOUT_LO_PRI; + config.rx_timeout_pri[2] = TARGET_RX_TIMEOUT_LO_PRI; + config.rx_timeout_pri[3] = TARGET_RX_TIMEOUT_HI_PRI; + config.rx_decap_mode = TARGET_DECAP_MODE_NATIVE_WIFI; + config.scan_max_pending_req = TARGET_SCAN_MAX_PENDING_REQS; + config.bmiss_offload_max_vdev = TARGET_BMISS_OFFLOAD_MAX_VDEV; + config.roam_offload_max_vdev = TARGET_ROAM_OFFLOAD_MAX_VDEV; + config.roam_offload_max_ap_profiles = TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES; + config.num_mcast_groups = TARGET_NUM_MCAST_GROUPS; + config.num_mcast_table_elems = TARGET_NUM_MCAST_TABLE_ELEMS; + config.mcast2ucast_mode = TARGET_MCAST2UCAST_MODE; + config.tx_dbg_log_size = TARGET_TX_DBG_LOG_SIZE; + config.num_wds_entries = TARGET_NUM_WDS_ENTRIES; + config.dma_burst_size = TARGET_DMA_BURST_SIZE; + config.rx_skip_defrag_timeout_dup_detection_check = + TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config.vow_config = TARGET_VOW_CONFIG; + config.gtk_offload_max_vdev = TARGET_GTK_OFFLOAD_MAX_VDEV; + config.num_msdu_desc = TARGET_NUM_MSDU_DESC; + config.beacon_tx_offload_max_vdev = ab->num_radios * TARGET_MAX_BCN_OFFLD; + config.rx_batchmode = TARGET_RX_BATCHMODE; + config.peer_map_unmap_v2_support = 1; + config.twt_ap_pdev_count = 2; + config.twt_ap_sta_count = 1000; + + memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config)); + + init_param.res_cfg = &wmi_sc->wlan_resource_config; + init_param.num_mem_chunks = wmi_sc->num_mem_chunks; + init_param.hw_mode_id = wmi_sc->preferred_hw_mode; + init_param.mem_chunks = wmi_sc->mem_chunks; + + if (wmi_sc->preferred_hw_mode == WMI_HOST_HW_MODE_SINGLE) + init_param.hw_mode_id = WMI_HOST_HW_MODE_MAX; + + init_param.num_band_to_mac = ab->num_radios; + + ath11k_fill_band_to_mac_param(ab, init_param.band_to_mac); + + return ath11k_init_cmd_send(&wmi_sc->wmi[0], &init_param); +} + +static int ath11k_wmi_tlv_hw_mode_caps_parse(struct ath11k_base *soc, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + struct wmi_hw_mode_capabilities *hw_mode_cap; + u32 phy_map = 0; + + if (tag != WMI_TAG_HW_MODE_CAPABILITIES) + return -EPROTO; + + if (svc_rdy_ext->n_hw_mode_caps >= svc_rdy_ext->param.num_hw_modes) + return -ENOBUFS; + + hw_mode_cap = container_of(ptr, struct wmi_hw_mode_capabilities, + hw_mode_id); + svc_rdy_ext->n_hw_mode_caps++; + + phy_map = hw_mode_cap->phy_id_map; + while (phy_map) { + svc_rdy_ext->tot_phy_id++; + phy_map = phy_map >> 1; + } + + return 0; +} + +static int ath11k_wmi_tlv_hw_mode_caps(struct ath11k_base *soc, + u16 len, const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + struct wmi_hw_mode_capabilities *hw_mode_caps; + enum wmi_host_hw_mode_config_type mode, pref; + u32 i; + int ret; + + svc_rdy_ext->n_hw_mode_caps = 0; + svc_rdy_ext->hw_mode_caps = (struct wmi_hw_mode_capabilities *)ptr; + + ret = ath11k_wmi_tlv_iter(soc, ptr, len, + ath11k_wmi_tlv_hw_mode_caps_parse, + svc_rdy_ext); + if (ret) { + ath11k_warn(soc, "failed to parse tlv %d\n", ret); + return ret; + } + + i = 0; + while (i < svc_rdy_ext->n_hw_mode_caps) { + hw_mode_caps = &svc_rdy_ext->hw_mode_caps[i]; + mode = hw_mode_caps->hw_mode_id; + pref = soc->wmi_ab.preferred_hw_mode; + + if (ath11k_hw_mode_pri_map[mode] < ath11k_hw_mode_pri_map[pref]) { + svc_rdy_ext->pref_hw_mode_caps = *hw_mode_caps; + soc->wmi_ab.preferred_hw_mode = mode; + } + i++; + } + + if (soc->wmi_ab.preferred_hw_mode == WMI_HOST_HW_MODE_MAX) + return -EINVAL; + + return 0; +} + +static int ath11k_wmi_tlv_mac_phy_caps_parse(struct ath11k_base *soc, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + + if (tag != WMI_TAG_MAC_PHY_CAPABILITIES) + return -EPROTO; + + if (svc_rdy_ext->n_mac_phy_caps >= svc_rdy_ext->tot_phy_id) + return -ENOBUFS; + + len = min_t(u16, len, sizeof(struct wmi_mac_phy_capabilities)); + if (!svc_rdy_ext->n_mac_phy_caps) { + svc_rdy_ext->mac_phy_caps = kzalloc((svc_rdy_ext->tot_phy_id) * len, + GFP_ATOMIC); + if (!svc_rdy_ext->mac_phy_caps) + return -ENOMEM; + } + + memcpy(svc_rdy_ext->mac_phy_caps + svc_rdy_ext->n_mac_phy_caps, ptr, len); + svc_rdy_ext->n_mac_phy_caps++; + return 0; +} + +static int ath11k_wmi_tlv_ext_hal_reg_caps_parse(struct ath11k_base *soc, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + + if (tag != WMI_TAG_HAL_REG_CAPABILITIES_EXT) + return -EPROTO; + + if (svc_rdy_ext->n_ext_hal_reg_caps >= svc_rdy_ext->param.num_phy) + return -ENOBUFS; + + svc_rdy_ext->n_ext_hal_reg_caps++; + return 0; +} + +static int ath11k_wmi_tlv_ext_hal_reg_caps(struct ath11k_base *soc, + u16 len, const void *ptr, void *data) +{ + struct ath11k_pdev_wmi *wmi_handle = &soc->wmi_ab.wmi[0]; + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + struct ath11k_hal_reg_capabilities_ext reg_cap; + int ret; + u32 i; + + svc_rdy_ext->n_ext_hal_reg_caps = 0; + svc_rdy_ext->ext_hal_reg_caps = (struct wmi_hal_reg_capabilities_ext *)ptr; + ret = ath11k_wmi_tlv_iter(soc, ptr, len, + ath11k_wmi_tlv_ext_hal_reg_caps_parse, + svc_rdy_ext); + if (ret) { + ath11k_warn(soc, "failed to parse tlv %d\n", ret); + return ret; + } + + for (i = 0; i < svc_rdy_ext->param.num_phy; i++) { + ret = ath11k_pull_reg_cap_svc_rdy_ext(wmi_handle, + svc_rdy_ext->soc_hal_reg_caps, + svc_rdy_ext->ext_hal_reg_caps, i, + ®_cap); + if (ret) { + ath11k_warn(soc, "failed to extract reg cap %d\n", i); + return ret; + } + + memcpy(&soc->hal_reg_cap[reg_cap.phy_id], + ®_cap, sizeof(reg_cap)); + } + return 0; +} + +static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc, + u16 len, const void *ptr, + void *data) +{ + struct ath11k_pdev_wmi *wmi_handle = &soc->wmi_ab.wmi[0]; + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + u8 hw_mode_id = svc_rdy_ext->pref_hw_mode_caps.hw_mode_id; + u32 phy_id_map; + int ret; + + svc_rdy_ext->soc_hal_reg_caps = (struct wmi_soc_hal_reg_capabilities *)ptr; + svc_rdy_ext->param.num_phy = svc_rdy_ext->soc_hal_reg_caps->num_phy; + + soc->num_radios = 0; + phy_id_map = svc_rdy_ext->pref_hw_mode_caps.phy_id_map; + + while (phy_id_map && soc->num_radios < MAX_RADIOS) { + ret = ath11k_pull_mac_phy_cap_svc_ready_ext(wmi_handle, + svc_rdy_ext->hw_caps, + svc_rdy_ext->hw_mode_caps, + svc_rdy_ext->soc_hal_reg_caps, + svc_rdy_ext->mac_phy_caps, + hw_mode_id, soc->num_radios, + &soc->pdevs[soc->num_radios]); + if (ret) { + ath11k_warn(soc, "failed to extract mac caps, idx :%d\n", + soc->num_radios); + return ret; + } + + soc->num_radios++; + + /* TODO: mac_phy_cap prints */ + phy_id_map >>= 1; + } + return 0; +} + +static int ath11k_wmi_tlv_svc_rdy_ext_parse(struct ath11k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct ath11k_pdev_wmi *wmi_handle = &ab->wmi_ab.wmi[0]; + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + int ret; + + switch (tag) { + case WMI_TAG_SERVICE_READY_EXT_EVENT: + ret = ath11k_pull_svc_ready_ext(wmi_handle, ptr, + &svc_rdy_ext->param); + if (ret) { + ath11k_warn(ab, "unable to extract ext params\n"); + return ret; + } + break; + + case WMI_TAG_SOC_MAC_PHY_HW_MODE_CAPS: + svc_rdy_ext->hw_caps = (struct wmi_soc_mac_phy_hw_mode_caps *)ptr; + svc_rdy_ext->param.num_hw_modes = svc_rdy_ext->hw_caps->num_hw_modes; + break; + + case WMI_TAG_SOC_HAL_REG_CAPABILITIES: + ret = ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(ab, len, ptr, + svc_rdy_ext); + if (ret) + return ret; + break; + + case WMI_TAG_ARRAY_STRUCT: + if (!svc_rdy_ext->hw_mode_done) { + ret = ath11k_wmi_tlv_hw_mode_caps(ab, len, ptr, + svc_rdy_ext); + if (ret) + return ret; + + svc_rdy_ext->hw_mode_done = true; + } else if (!svc_rdy_ext->mac_phy_done) { + svc_rdy_ext->n_mac_phy_caps = 0; + ret = ath11k_wmi_tlv_iter(ab, ptr, len, + ath11k_wmi_tlv_mac_phy_caps_parse, + svc_rdy_ext); + if (ret) { + ath11k_warn(ab, "failed to parse tlv %d\n", ret); + return ret; + } + + svc_rdy_ext->mac_phy_done = true; + } else if (!svc_rdy_ext->ext_hal_reg_done) { + ret = ath11k_wmi_tlv_ext_hal_reg_caps(ab, len, ptr, + svc_rdy_ext); + if (ret) + return ret; + + svc_rdy_ext->ext_hal_reg_done = true; + complete(&ab->wmi_ab.service_ready); + } + break; + + default: + break; + } + return 0; +} + +static int ath11k_service_ready_ext_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct wmi_tlv_svc_rdy_ext_parse svc_rdy_ext = { }; + int ret; + + ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len, + ath11k_wmi_tlv_svc_rdy_ext_parse, + &svc_rdy_ext); + if (ret) { + ath11k_warn(ab, "failed to parse tlv %d\n", ret); + return ret; + } + + kfree(svc_rdy_ext.mac_phy_caps); + return 0; +} + +static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_vdev_start_resp_event *vdev_rsp) +{ + const void **tb; + const struct wmi_vdev_start_resp_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_VDEV_START_RESPONSE_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch vdev start resp ev"); + kfree(tb); + return -EPROTO; + } + + memset(vdev_rsp, 0, sizeof(*vdev_rsp)); + + vdev_rsp->vdev_id = ev->vdev_id; + vdev_rsp->requestor_id = ev->requestor_id; + vdev_rsp->resp_type = ev->resp_type; + vdev_rsp->status = ev->status; + vdev_rsp->chain_mask = ev->chain_mask; + vdev_rsp->smps_mode = ev->smps_mode; + vdev_rsp->mac_id = ev->mac_id; + vdev_rsp->cfgd_tx_streams = ev->cfgd_tx_streams; + vdev_rsp->cfgd_rx_streams = ev->cfgd_rx_streams; + + kfree(tb); + return 0; +} + +static struct cur_reg_rule +*create_reg_rules_from_wmi(u32 num_reg_rules, + struct wmi_regulatory_rule_struct *wmi_reg_rule) +{ + struct cur_reg_rule *reg_rule_ptr; + u32 count; + + reg_rule_ptr = kzalloc((num_reg_rules * sizeof(*reg_rule_ptr)), + GFP_ATOMIC); + + if (!reg_rule_ptr) + return NULL; + + for (count = 0; count < num_reg_rules; count++) { + reg_rule_ptr[count].start_freq = + FIELD_GET(REG_RULE_START_FREQ, + wmi_reg_rule[count].freq_info); + reg_rule_ptr[count].end_freq = + FIELD_GET(REG_RULE_END_FREQ, + wmi_reg_rule[count].freq_info); + reg_rule_ptr[count].max_bw = + FIELD_GET(REG_RULE_MAX_BW, + wmi_reg_rule[count].bw_pwr_info); + reg_rule_ptr[count].reg_power = + FIELD_GET(REG_RULE_REG_PWR, + wmi_reg_rule[count].bw_pwr_info); + reg_rule_ptr[count].ant_gain = + FIELD_GET(REG_RULE_ANT_GAIN, + wmi_reg_rule[count].bw_pwr_info); + reg_rule_ptr[count].flags = + FIELD_GET(REG_RULE_FLAGS, + wmi_reg_rule[count].flag_info); + } + + return reg_rule_ptr; +} + +static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, + struct sk_buff *skb, + struct cur_regulatory_info *reg_info) +{ + const void **tb; + const struct wmi_reg_chan_list_cc_event *chan_list_event_hdr; + struct wmi_regulatory_rule_struct *wmi_reg_rule; + u32 num_2g_reg_rules, num_5g_reg_rules; + int ret; + + ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory channel list\n"); + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + chan_list_event_hdr = tb[WMI_TAG_REG_CHAN_LIST_CC_EVENT]; + if (!chan_list_event_hdr) { + ath11k_warn(ab, "failed to fetch reg chan list update ev\n"); + kfree(tb); + return -EPROTO; + } + + reg_info->num_2g_reg_rules = chan_list_event_hdr->num_2g_reg_rules; + reg_info->num_5g_reg_rules = chan_list_event_hdr->num_5g_reg_rules; + + if (!(reg_info->num_2g_reg_rules + reg_info->num_5g_reg_rules)) { + ath11k_warn(ab, "No regulatory rules available in the event info\n"); + kfree(tb); + return -EINVAL; + } + + memcpy(reg_info->alpha2, &chan_list_event_hdr->alpha2, + REG_ALPHA2_LEN); + reg_info->dfs_region = chan_list_event_hdr->dfs_region; + reg_info->phybitmap = chan_list_event_hdr->phybitmap; + reg_info->num_phy = chan_list_event_hdr->num_phy; + reg_info->phy_id = chan_list_event_hdr->phy_id; + reg_info->ctry_code = chan_list_event_hdr->country_id; + reg_info->reg_dmn_pair = chan_list_event_hdr->domain_code; + if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_PASS) + reg_info->status_code = REG_SET_CC_STATUS_PASS; + else if (chan_list_event_hdr->status_code == WMI_REG_CURRENT_ALPHA2_NOT_FOUND) + reg_info->status_code = REG_CURRENT_ALPHA2_NOT_FOUND; + else if (chan_list_event_hdr->status_code == WMI_REG_INIT_ALPHA2_NOT_FOUND) + reg_info->status_code = REG_INIT_ALPHA2_NOT_FOUND; + else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_CHANGE_NOT_ALLOWED) + reg_info->status_code = REG_SET_CC_CHANGE_NOT_ALLOWED; + else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_NO_MEMORY) + reg_info->status_code = REG_SET_CC_STATUS_NO_MEMORY; + else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_FAIL) + reg_info->status_code = REG_SET_CC_STATUS_FAIL; + + reg_info->min_bw_2g = chan_list_event_hdr->min_bw_2g; + reg_info->max_bw_2g = chan_list_event_hdr->max_bw_2g; + reg_info->min_bw_5g = chan_list_event_hdr->min_bw_5g; + reg_info->max_bw_5g = chan_list_event_hdr->max_bw_5g; + + num_2g_reg_rules = reg_info->num_2g_reg_rules; + num_5g_reg_rules = reg_info->num_5g_reg_rules; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "%s:cc %s dsf %d BW: min_2g %d max_2g %d min_5g %d max_5g %d", + __func__, reg_info->alpha2, reg_info->dfs_region, + reg_info->min_bw_2g, reg_info->max_bw_2g, + reg_info->min_bw_5g, reg_info->max_bw_5g); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "%s: num_2g_reg_rules %d num_5g_reg_rules %d", __func__, + num_2g_reg_rules, num_5g_reg_rules); + + wmi_reg_rule = + (struct wmi_regulatory_rule_struct *)((u8 *)chan_list_event_hdr + + sizeof(*chan_list_event_hdr) + + sizeof(struct wmi_tlv)); + + if (num_2g_reg_rules) { + reg_info->reg_rules_2g_ptr = create_reg_rules_from_wmi(num_2g_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_2g_ptr) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 2g rules\n"); + return -ENOMEM; + } + } + + if (num_5g_reg_rules) { + wmi_reg_rule += num_2g_reg_rules; + reg_info->reg_rules_5g_ptr = create_reg_rules_from_wmi(num_5g_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_5g_ptr) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 5g rules\n"); + return -ENOMEM; + } + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory channel list\n"); + + kfree(tb); + return 0; +} + +static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_peer_delete_resp_event *peer_del_resp) +{ + const void **tb; + const struct wmi_peer_delete_resp_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_PEER_DELETE_RESP_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch peer delete resp ev"); + kfree(tb); + return -EPROTO; + } + + memset(peer_del_resp, 0, sizeof(*peer_del_resp)); + + peer_del_resp->vdev_id = ev->vdev_id; + ether_addr_copy(peer_del_resp->peer_macaddr.addr, + ev->peer_macaddr.addr); + + kfree(tb); + return 0; +} + +static int ath11k_pull_bcn_tx_status_ev(struct ath11k_base *ab, void *evt_buf, + u32 len, u32 *vdev_id, + u32 *tx_status) +{ + const void **tb; + const struct wmi_bcn_tx_status_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch bcn tx status ev"); + kfree(tb); + return -EPROTO; + } + + *vdev_id = ev->vdev_id; + *tx_status = ev->tx_status; + + kfree(tb); + return 0; +} + +static int ath11k_pull_vdev_stopped_param_tlv(struct ath11k_base *ab, struct sk_buff *skb, + u32 *vdev_id) +{ + const void **tb; + const struct wmi_vdev_stopped_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_VDEV_STOPPED_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch vdev stop ev"); + kfree(tb); + return -EPROTO; + } + + *vdev_id = ev->vdev_id; + + kfree(tb); + return 0; +} + +static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab, + struct sk_buff *skb, + struct mgmt_rx_event_params *hdr) +{ + const void **tb; + const struct wmi_mgmt_rx_hdr *ev; + const u8 *frame; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_MGMT_RX_HDR]; + frame = tb[WMI_TAG_ARRAY_BYTE]; + + if (!ev || !frame) { + ath11k_warn(ab, "failed to fetch mgmt rx hdr"); + kfree(tb); + return -EPROTO; + } + + hdr->pdev_id = ev->pdev_id; + hdr->channel = ev->channel; + hdr->snr = ev->snr; + hdr->rate = ev->rate; + hdr->phy_mode = ev->phy_mode; + hdr->buf_len = ev->buf_len; + hdr->status = ev->status; + hdr->flags = ev->flags; + hdr->rssi = ev->rssi; + hdr->tsf_delta = ev->tsf_delta; + memcpy(hdr->rssi_ctl, ev->rssi_ctl, sizeof(hdr->rssi_ctl)); + + if (skb->len < (frame - skb->data) + hdr->buf_len) { + ath11k_warn(ab, "invalid length in mgmt rx hdr ev"); + kfree(tb); + return -EPROTO; + } + + /* shift the sk_buff to point to `frame` */ + skb_trim(skb, 0); + skb_put(skb, frame - skb->data); + skb_pull(skb, frame - skb->data); + skb_put(skb, hdr->buf_len); + + ath11k_ce_byte_swap(skb->data, hdr->buf_len); + + kfree(tb); + return 0; +} + +static int wmi_process_mgmt_tx_comp(struct ath11k *ar, u32 desc_id, + u32 status) +{ + struct sk_buff *msdu; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; + + spin_lock_bh(&ar->txmgmt_idr_lock); + msdu = idr_find(&ar->txmgmt_idr, desc_id); + + if (!msdu) { + ath11k_warn(ar->ab, "received mgmt tx compl for invalid msdu_id: %d\n", + desc_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + return -ENOENT; + } + + idr_remove(&ar->txmgmt_idr, desc_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + + skb_cb = ATH11K_SKB_CB(msdu); + dma_unmap_single(ar->ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + + info = IEEE80211_SKB_CB(msdu); + if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && !status) + info->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(ar->hw, msdu); + + WARN_ON_ONCE(atomic_read(&ar->num_pending_mgmt_tx) == 0); + atomic_dec(&ar->num_pending_mgmt_tx); + + return 0; +} + +static int ath11k_pull_mgmt_tx_compl_param_tlv(struct ath11k_base *ab, + struct sk_buff *skb, + struct wmi_mgmt_tx_compl_event *param) +{ + const void **tb; + const struct wmi_mgmt_tx_compl_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_MGMT_TX_COMPL_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch mgmt tx compl ev"); + kfree(tb); + return -EPROTO; + } + + param->pdev_id = ev->pdev_id; + param->desc_id = ev->desc_id; + param->status = ev->status; + + kfree(tb); + return 0; +} + +static void ath11k_wmi_event_scan_started(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ath11k_warn(ar->ab, "received scan started event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_STARTING: + ar->scan.state = ATH11K_SCAN_RUNNING; + complete(&ar->scan.started); + break; + } +} + +static void ath11k_wmi_event_scan_start_failed(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ath11k_warn(ar->ab, "received scan start failed event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_STARTING: + complete(&ar->scan.started); + __ath11k_mac_scan_finish(ar); + break; + } +} + +static void ath11k_wmi_event_scan_completed(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_STARTING: + /* One suspected reason scan can be completed while starting is + * if firmware fails to deliver all scan events to the host, + * e.g. when transport pipe is full. This has been observed + * with spectral scan phyerr events starving wmi transport + * pipe. In such case the "scan completed" event should be (and + * is) ignored by the host as it may be just firmware's scan + * state machine recovering. + */ + ath11k_warn(ar->ab, "received scan completed event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + __ath11k_mac_scan_finish(ar); + break; + } +} + +static void ath11k_wmi_event_scan_bss_chan(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_STARTING: + ath11k_warn(ar->ab, "received scan bss chan event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ar->scan_channel = NULL; + break; + } +} + +static void ath11k_wmi_event_scan_foreign_chan(struct ath11k *ar, u32 freq) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_STARTING: + ath11k_warn(ar->ab, "received scan foreign chan event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq); + break; + } +} + +static const char * +ath11k_wmi_event_scan_type_str(enum wmi_scan_event_type type, + enum wmi_scan_completion_reason reason) +{ + switch (type) { + case WMI_SCAN_EVENT_STARTED: + return "started"; + case WMI_SCAN_EVENT_COMPLETED: + switch (reason) { + case WMI_SCAN_REASON_COMPLETED: + return "completed"; + case WMI_SCAN_REASON_CANCELLED: + return "completed [cancelled]"; + case WMI_SCAN_REASON_PREEMPTED: + return "completed [preempted]"; + case WMI_SCAN_REASON_TIMEDOUT: + return "completed [timedout]"; + case WMI_SCAN_REASON_INTERNAL_FAILURE: + return "completed [internal err]"; + case WMI_SCAN_REASON_MAX: + break; + } + return "completed [unknown]"; + case WMI_SCAN_EVENT_BSS_CHANNEL: + return "bss channel"; + case WMI_SCAN_EVENT_FOREIGN_CHAN: + return "foreign channel"; + case WMI_SCAN_EVENT_DEQUEUED: + return "dequeued"; + case WMI_SCAN_EVENT_PREEMPTED: + return "preempted"; + case WMI_SCAN_EVENT_START_FAILED: + return "start failed"; + case WMI_SCAN_EVENT_RESTARTED: + return "restarted"; + case WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT: + return "foreign channel exit"; + default: + return "unknown"; + } +} + +static int ath11k_pull_scan_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_scan_event *scan_evt_param) +{ + const void **tb; + const struct wmi_scan_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_SCAN_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch scan ev"); + kfree(tb); + return -EPROTO; + } + + scan_evt_param->event_type = ev->event_type; + scan_evt_param->reason = ev->reason; + scan_evt_param->channel_freq = ev->channel_freq; + scan_evt_param->scan_req_id = ev->scan_req_id; + scan_evt_param->scan_id = ev->scan_id; + scan_evt_param->vdev_id = ev->vdev_id; + scan_evt_param->tsf_timestamp = ev->tsf_timestamp; + + kfree(tb); + return 0; +} + +static int ath11k_pull_peer_sta_kickout_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_peer_sta_kickout_arg *arg) +{ + const void **tb; + const struct wmi_peer_sta_kickout_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_PEER_STA_KICKOUT_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch peer sta kickout ev"); + kfree(tb); + return -EPROTO; + } + + arg->mac_addr = ev->peer_macaddr.addr; + + kfree(tb); + return 0; +} + +static int ath11k_pull_roam_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_roam_event *roam_ev) +{ + const void **tb; + const struct wmi_roam_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_ROAM_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch roam ev"); + kfree(tb); + return -EPROTO; + } + + roam_ev->vdev_id = ev->vdev_id; + roam_ev->reason = ev->reason; + roam_ev->rssi = ev->rssi; + + kfree(tb); + return 0; +} + +static int freq_to_idx(struct ath11k *ar, int freq) +{ + struct ieee80211_supported_band *sband; + int band, ch, idx = 0; + + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { + sband = ar->hw->wiphy->bands[band]; + if (!sband) + continue; + + for (ch = 0; ch < sband->n_channels; ch++, idx++) + if (sband->channels[ch].center_freq == freq) + goto exit; + } + +exit: + return idx; +} + +static int ath11k_pull_chan_info_ev(struct ath11k_base *ab, u8 *evt_buf, + u32 len, struct wmi_chan_info_event *ch_info_ev) +{ + const void **tb; + const struct wmi_chan_info_event *ev; + 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_CHAN_INFO_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch chan info ev"); + kfree(tb); + return -EPROTO; + } + + ch_info_ev->err_code = ev->err_code; + ch_info_ev->freq = ev->freq; + ch_info_ev->cmd_flags = ev->cmd_flags; + ch_info_ev->noise_floor = ev->noise_floor; + ch_info_ev->rx_clear_count = ev->rx_clear_count; + ch_info_ev->cycle_count = ev->cycle_count; + ch_info_ev->chan_tx_pwr_range = ev->chan_tx_pwr_range; + ch_info_ev->chan_tx_pwr_tp = ev->chan_tx_pwr_tp; + ch_info_ev->rx_frame_count = ev->rx_frame_count; + ch_info_ev->tx_frame_cnt = ev->tx_frame_cnt; + ch_info_ev->mac_clk_mhz = ev->mac_clk_mhz; + ch_info_ev->vdev_id = ev->vdev_id; + + kfree(tb); + return 0; +} + +static int +ath11k_pull_pdev_bss_chan_info_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_pdev_bss_chan_info_event *bss_ch_info_ev) +{ + const void **tb; + const struct wmi_pdev_bss_chan_info_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_BSS_CHAN_INFO_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch pdev bss chan info ev"); + kfree(tb); + return -EPROTO; + } + + bss_ch_info_ev->pdev_id = ev->pdev_id; + bss_ch_info_ev->freq = ev->freq; + bss_ch_info_ev->noise_floor = ev->noise_floor; + bss_ch_info_ev->rx_clear_count_low = ev->rx_clear_count_low; + bss_ch_info_ev->rx_clear_count_high = ev->rx_clear_count_high; + bss_ch_info_ev->cycle_count_low = ev->cycle_count_low; + bss_ch_info_ev->cycle_count_high = ev->cycle_count_high; + bss_ch_info_ev->tx_cycle_count_low = ev->tx_cycle_count_low; + bss_ch_info_ev->tx_cycle_count_high = ev->tx_cycle_count_high; + bss_ch_info_ev->rx_cycle_count_low = ev->rx_cycle_count_low; + bss_ch_info_ev->rx_cycle_count_high = ev->rx_cycle_count_high; + bss_ch_info_ev->rx_bss_cycle_count_low = ev->rx_bss_cycle_count_low; + bss_ch_info_ev->rx_bss_cycle_count_high = ev->rx_bss_cycle_count_high; + + kfree(tb); + return 0; +} + +static int +ath11k_pull_vdev_install_key_compl_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_vdev_install_key_complete_arg *arg) +{ + const void **tb; + const struct wmi_vdev_install_key_compl_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_VDEV_INSTALL_KEY_COMPLETE_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch vdev install key compl ev"); + kfree(tb); + return -EPROTO; + } + + arg->vdev_id = ev->vdev_id; + arg->macaddr = ev->peer_macaddr.addr; + arg->key_idx = ev->key_idx; + arg->key_flags = ev->key_flags; + arg->status = ev->status; + + kfree(tb); + return 0; +} + +static int ath11k_pull_peer_assoc_conf_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_peer_assoc_conf_arg *peer_assoc_conf) +{ + const void **tb; + const struct wmi_peer_assoc_conf_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->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_PEER_ASSOC_CONF_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch peer assoc conf ev"); + kfree(tb); + return -EPROTO; + } + + peer_assoc_conf->vdev_id = ev->vdev_id; + peer_assoc_conf->macaddr = ev->peer_macaddr.addr; + + kfree(tb); + return 0; +} + +static void ath11k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src, + struct ath11k_fw_stats_pdev *dst) +{ + dst->ch_noise_floor = src->chan_nf; + dst->tx_frame_count = src->tx_frame_count; + dst->rx_frame_count = src->rx_frame_count; + dst->rx_clear_count = src->rx_clear_count; + dst->cycle_count = src->cycle_count; + dst->phy_err_count = src->phy_err_count; + dst->chan_tx_power = src->chan_tx_pwr; +} + +static void +ath11k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src, + struct ath11k_fw_stats_pdev *dst) +{ + dst->comp_queued = src->comp_queued; + dst->comp_delivered = src->comp_delivered; + dst->msdu_enqued = src->msdu_enqued; + dst->mpdu_enqued = src->mpdu_enqued; + dst->wmm_drop = src->wmm_drop; + dst->local_enqued = src->local_enqued; + dst->local_freed = src->local_freed; + dst->hw_queued = src->hw_queued; + dst->hw_reaped = src->hw_reaped; + dst->underrun = src->underrun; + dst->tx_abort = src->tx_abort; + dst->mpdus_requed = src->mpdus_requed; + dst->tx_ko = src->tx_ko; + dst->data_rc = src->data_rc; + dst->self_triggers = src->self_triggers; + dst->sw_retry_failure = src->sw_retry_failure; + dst->illgl_rate_phy_err = src->illgl_rate_phy_err; + dst->pdev_cont_xretry = src->pdev_cont_xretry; + dst->pdev_tx_timeout = src->pdev_tx_timeout; + dst->pdev_resets = src->pdev_resets; + dst->stateless_tid_alloc_failure = src->stateless_tid_alloc_failure; + dst->phy_underrun = src->phy_underrun; + dst->txop_ovf = src->txop_ovf; +} + +static void ath11k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src, + struct ath11k_fw_stats_pdev *dst) +{ + dst->mid_ppdu_route_change = src->mid_ppdu_route_change; + dst->status_rcvd = src->status_rcvd; + dst->r0_frags = src->r0_frags; + dst->r1_frags = src->r1_frags; + dst->r2_frags = src->r2_frags; + dst->r3_frags = src->r3_frags; + dst->htt_msdus = src->htt_msdus; + dst->htt_mpdus = src->htt_mpdus; + dst->loc_msdus = src->loc_msdus; + dst->loc_mpdus = src->loc_mpdus; + dst->oversize_amsdu = src->oversize_amsdu; + dst->phy_errs = src->phy_errs; + dst->phy_err_drop = src->phy_err_drop; + dst->mpdu_errs = src->mpdu_errs; +} + +static void +ath11k_wmi_pull_vdev_stats(const struct wmi_vdev_stats *src, + struct ath11k_fw_stats_vdev *dst) +{ + int i; + + dst->vdev_id = src->vdev_id; + dst->beacon_snr = src->beacon_snr; + dst->data_snr = src->data_snr; + dst->num_rx_frames = src->num_rx_frames; + dst->num_rts_fail = src->num_rts_fail; + dst->num_rts_success = src->num_rts_success; + dst->num_rx_err = src->num_rx_err; + dst->num_rx_discard = src->num_rx_discard; + dst->num_tx_not_acked = src->num_tx_not_acked; + + for (i = 0; i < ARRAY_SIZE(src->num_tx_frames); i++) + dst->num_tx_frames[i] = src->num_tx_frames[i]; + + for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_retries); i++) + dst->num_tx_frames_retries[i] = src->num_tx_frames_retries[i]; + + for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_failures); i++) + dst->num_tx_frames_failures[i] = src->num_tx_frames_failures[i]; + + for (i = 0; i < ARRAY_SIZE(src->tx_rate_history); i++) + dst->tx_rate_history[i] = src->tx_rate_history[i]; + + for (i = 0; i < ARRAY_SIZE(src->beacon_rssi_history); i++) + dst->beacon_rssi_history[i] = src->beacon_rssi_history[i]; +} + +static void +ath11k_wmi_pull_bcn_stats(const struct wmi_bcn_stats *src, + struct ath11k_fw_stats_bcn *dst) +{ + dst->vdev_id = src->vdev_id; + dst->tx_bcn_succ_cnt = src->tx_bcn_succ_cnt; + dst->tx_bcn_outage_cnt = src->tx_bcn_outage_cnt; +} + +int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, + struct ath11k_fw_stats *stats) +{ + const void **tb; + const struct wmi_stats_event *ev; + const void *data; + int i, ret; + u32 len = skb->len; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, 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_STATS_EVENT]; + data = tb[WMI_TAG_ARRAY_BYTE]; + if (!ev || !data) { + ath11k_warn(ab, "failed to fetch update stats ev"); + kfree(tb); + return -EPROTO; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats update ev pdev_id %d pdev %i vdev %i bcn %i\n", + ev->pdev_id, + ev->num_pdev_stats, ev->num_vdev_stats, + ev->num_bcn_stats); + + stats->pdev_id = ev->pdev_id; + stats->stats_id = 0; + + for (i = 0; i < ev->num_pdev_stats; i++) { + const struct wmi_pdev_stats *src; + struct ath11k_fw_stats_pdev *dst; + + src = data; + if (len < sizeof(*src)) { + kfree(tb); + return -EPROTO; + } + + stats->stats_id = WMI_REQUEST_PDEV_STAT; + + data += sizeof(*src); + len -= sizeof(*src); + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath11k_wmi_pull_pdev_stats_base(&src->base, dst); + ath11k_wmi_pull_pdev_stats_tx(&src->tx, dst); + ath11k_wmi_pull_pdev_stats_rx(&src->rx, dst); + list_add_tail(&dst->list, &stats->pdevs); + } + + for (i = 0; i < ev->num_vdev_stats; i++) { + const struct wmi_vdev_stats *src; + struct ath11k_fw_stats_vdev *dst; + + src = data; + if (len < sizeof(*src)) { + kfree(tb); + return -EPROTO; + } + + stats->stats_id = WMI_REQUEST_VDEV_STAT; + + data += sizeof(*src); + len -= sizeof(*src); + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath11k_wmi_pull_vdev_stats(src, dst); + list_add_tail(&dst->list, &stats->vdevs); + } + + for (i = 0; i < ev->num_bcn_stats; i++) { + const struct wmi_bcn_stats *src; + struct ath11k_fw_stats_bcn *dst; + + src = data; + if (len < sizeof(*src)) { + kfree(tb); + return -EPROTO; + } + + stats->stats_id = WMI_REQUEST_BCN_STAT; + + data += sizeof(*src); + len -= sizeof(*src); + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath11k_wmi_pull_bcn_stats(src, dst); + list_add_tail(&dst->list, &stats->bcn); + } + + kfree(tb); + return 0; +} + +size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head) +{ + struct ath11k_fw_stats_vdev *i; + size_t num = 0; + + list_for_each_entry(i, head, list) + ++num; + + return num; +} + +static size_t ath11k_wmi_fw_stats_num_bcn(struct list_head *head) +{ + struct ath11k_fw_stats_bcn *i; + size_t num = 0; + + list_for_each_entry(i, head, list) + ++num; + + return num; +} + +static void +ath11k_wmi_fw_pdev_base_stats_fill(const struct ath11k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath11k PDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Channel noise floor", pdev->ch_noise_floor); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Channel TX power", pdev->chan_tx_power); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX frame count", pdev->tx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX frame count", pdev->rx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX clear count", pdev->rx_clear_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Cycle count", pdev->cycle_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY error count", pdev->phy_err_count); + + *length = len; +} + +static void +ath11k_wmi_fw_pdev_tx_stats_fill(const struct ath11k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath11k PDEV TX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies queued", pdev->comp_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies disp.", pdev->comp_delivered); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDU queued", pdev->msdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU queued", pdev->mpdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs dropped", pdev->wmm_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local enqued", pdev->local_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local freed", pdev->local_freed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW queued", pdev->hw_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs reaped", pdev->hw_reaped); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num underruns", pdev->underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs cleaned", pdev->tx_abort); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs requed", pdev->mpdus_requed); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Excessive retries", pdev->tx_ko); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "HW rate", pdev->data_rc); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Sched self triggers", pdev->self_triggers); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Dropped due to SW retries", + pdev->sw_retry_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Illegal rate phy errors", + pdev->illgl_rate_phy_err); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV continuous xretry", pdev->pdev_cont_xretry); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX timeout", pdev->pdev_tx_timeout); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV resets", pdev->pdev_resets); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Stateless TIDs alloc failures", + pdev->stateless_tid_alloc_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY underrun", pdev->phy_underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "MPDU is more than txop limit", pdev->txop_ovf); + *length = len; +} + +static void +ath11k_wmi_fw_pdev_rx_stats_fill(const struct ath11k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath11k PDEV RX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Mid PPDU route change", + pdev->mid_ppdu_route_change); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Tot. number of statuses", pdev->status_rcvd); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 0", pdev->r0_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 1", pdev->r1_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 2", pdev->r2_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 3", pdev->r3_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to HTT", pdev->htt_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to HTT", pdev->htt_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to stack", pdev->loc_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to stack", pdev->loc_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Oversized AMSUs", pdev->oversize_amsdu); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors", pdev->phy_errs); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors drops", pdev->phy_err_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs); + *length = len; +} + +static void +ath11k_wmi_fw_vdev_stats_fill(struct ath11k *ar, + const struct ath11k_fw_stats_vdev *vdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + struct ath11k_vif *arvif = ath11k_mac_get_arvif(ar, vdev->vdev_id); + u8 *vif_macaddr; + int i; + + /* VDEV stats has all the active VDEVs of other PDEVs as well, + * ignoring those not part of requested PDEV + */ + if (!arvif) + return; + + vif_macaddr = arvif->vif->addr; + + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", vdev->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", vif_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "beacon snr", vdev->beacon_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "data snr", vdev->data_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx frames", vdev->num_rx_frames); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts fail", vdev->num_rts_fail); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts success", vdev->num_rts_success); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx err", vdev->num_rx_err); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx discard", vdev->num_rx_discard); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num tx not acked", vdev->num_tx_not_acked); + + for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames", i, + vdev->num_tx_frames[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames retries", i, + vdev->num_tx_frames_retries[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames failures", i, + vdev->num_tx_frames_failures[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] 0x%08x\n", + "tx rate history", i, + vdev->tx_rate_history[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "beacon rssi history", i, + vdev->beacon_rssi_history[i]); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; +} + +static void +ath11k_wmi_fw_bcn_stats_fill(struct ath11k *ar, + const struct ath11k_fw_stats_bcn *bcn, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + struct ath11k_vif *arvif = ath11k_mac_get_arvif(ar, bcn->vdev_id); + u8 *vdev_macaddr; + + if (!arvif) { + ath11k_warn(ar->ab, "invalid vdev id %d in bcn stats", + bcn->vdev_id); + return; + } + + vdev_macaddr = arvif->vif->addr; + + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", bcn->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", vdev_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================"); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx success", bcn->tx_bcn_succ_cnt); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx failures", bcn->tx_bcn_outage_cnt); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; +} + +void ath11k_wmi_fw_stats_fill(struct ath11k *ar, + struct ath11k_fw_stats *fw_stats, + u32 stats_id, char *buf) +{ + u32 len = 0; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + const struct ath11k_fw_stats_pdev *pdev; + const struct ath11k_fw_stats_vdev *vdev; + const struct ath11k_fw_stats_bcn *bcn; + size_t num_bcn; + + spin_lock_bh(&ar->data_lock); + + if (stats_id == WMI_REQUEST_PDEV_STAT) { + pdev = list_first_entry_or_null(&fw_stats->pdevs, + struct ath11k_fw_stats_pdev, list); + if (!pdev) { + ath11k_warn(ar->ab, "failed to get pdev stats\n"); + goto unlock; + } + + ath11k_wmi_fw_pdev_base_stats_fill(pdev, buf, &len); + ath11k_wmi_fw_pdev_tx_stats_fill(pdev, buf, &len); + ath11k_wmi_fw_pdev_rx_stats_fill(pdev, buf, &len); + } + + if (stats_id == WMI_REQUEST_VDEV_STAT) { + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath11k VDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + list_for_each_entry(vdev, &fw_stats->vdevs, list) + ath11k_wmi_fw_vdev_stats_fill(ar, vdev, buf, &len); + } + + if (stats_id == WMI_REQUEST_BCN_STAT) { + num_bcn = ath11k_wmi_fw_stats_num_bcn(&fw_stats->bcn); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n", + "ath11k Beacon stats", num_bcn); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "==================="); + + list_for_each_entry(bcn, &fw_stats->bcn, list) + ath11k_wmi_fw_bcn_stats_fill(ar, bcn, buf, &len); + } + +unlock: + spin_unlock_bh(&ar->data_lock); + + if (len >= buf_len) + buf[len - 1] = 0; + else + buf[len] = 0; +} + +static void ath11k_wmi_op_ep_tx_credits(struct ath11k_base *ab) +{ + /* try to send pending beacons first. they take priority */ + wake_up(&ab->wmi_ab.tx_credits_wq); +} + +static void ath11k_wmi_htc_tx_complete(struct ath11k_base *ab, + struct sk_buff *skb) +{ + dev_kfree_skb(skb); +} + +static bool ath11k_reg_is_world_alpha(char *alpha) +{ + return alpha[0] == '0' && alpha[1] == '0'; +} + +static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct cur_regulatory_info *reg_info = NULL; + struct ieee80211_regdomain *regd = NULL; + bool intersect = false; + int ret = 0, pdev_idx; + struct ath11k *ar; + + reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC); + if (!reg_info) { + ret = -ENOMEM; + goto fallback; + } + + ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info); + if (ret) { + ath11k_warn(ab, "failed to extract regulatory info from received event\n"); + goto fallback; + } + + if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { + /* In case of failure to set the requested ctry, + * fw retains the current regd. We print a failure info + * and return from here. + */ + ath11k_warn(ab, "Failed to set the requested Country regulatory setting\n"); + goto mem_free; + } + + pdev_idx = reg_info->phy_id; + + if (pdev_idx >= ab->num_radios) + goto fallback; + + /* Avoid multiple overwrites to default regd, during core + * stop-start after mac registration. + */ + if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && + !memcmp((char *)ab->default_regd[pdev_idx]->alpha2, + (char *)reg_info->alpha2, 2)) + goto mem_free; + + /* Intersect new rules with default regd if a new country setting was + * requested, i.e a default regd was already set during initialization + * and the regd coming from this event has a valid country info. + */ + if (ab->default_regd[pdev_idx] && + !ath11k_reg_is_world_alpha((char *) + ab->default_regd[pdev_idx]->alpha2) && + !ath11k_reg_is_world_alpha((char *)reg_info->alpha2)) + intersect = true; + + regd = ath11k_reg_build_regd(ab, reg_info, intersect); + if (!regd) { + ath11k_warn(ab, "failed to build regd from reg_info\n"); + goto fallback; + } + + spin_lock(&ab->base_lock); + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { + /* Once mac is registered, ar is valid and all CC events from + * fw is considered to be received due to user requests + * currently. + * Free previously built regd before assigning the newly + * generated regd to ar. NULL pointer handling will be + * taken care by kfree itself. + */ + ar = ab->pdevs[pdev_idx].ar; + kfree(ab->new_regd[pdev_idx]); + ab->new_regd[pdev_idx] = regd; + ieee80211_queue_work(ar->hw, &ar->regd_update_work); + } else { + /* Multiple events for the same *ar is not expected. But we + * can still clear any previously stored default_regd if we + * are receiving this event for the same radio by mistake. + * NULL pointer handling will be taken care by kfree itself. + */ + kfree(ab->default_regd[pdev_idx]); + /* This regd would be applied during mac registration */ + ab->default_regd[pdev_idx] = regd; + } + ab->dfs_region = reg_info->dfs_region; + spin_unlock(&ab->base_lock); + + goto mem_free; + +fallback: + /* Fallback to older reg (by sending previous country setting + * again if fw has succeded and we failed to process here. + * The Regdomain should be uniform across driver and fw. Since the + * FW has processed the command and sent a success status, we expect + * this function to succeed as well. If it doesn't, CTRY needs to be + * reverted at the fw and the old SCAN_CHAN_LIST cmd needs to be sent. + */ + /* TODO: This is rare, but still should also be handled */ + WARN_ON(1); +mem_free: + if (reg_info) { + kfree(reg_info->reg_rules_2g_ptr); + kfree(reg_info->reg_rules_5g_ptr); + kfree(reg_info); + } + return ret; +} + +static int ath11k_wmi_tlv_rdy_parse(struct ath11k_base *ab, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_rdy_parse *rdy_parse = data; + struct wmi_ready_event *fixed_param; + struct wmi_mac_addr *addr_list; + struct ath11k_pdev *pdev; + u32 num_mac_addr; + int i; + + switch (tag) { + case WMI_TAG_READY_EVENT: + fixed_param = (struct wmi_ready_event *)ptr; + ab->wlan_init_status = fixed_param->status; + rdy_parse->num_extra_mac_addr = fixed_param->num_extra_mac_addr; + + ether_addr_copy(ab->mac_addr, fixed_param->mac_addr.addr); + ab->wmi_ready = true; + break; + case WMI_TAG_ARRAY_FIXED_STRUCT: + addr_list = (struct wmi_mac_addr *)ptr; + num_mac_addr = rdy_parse->num_extra_mac_addr; + + if (!(ab->num_radios > 1 && num_mac_addr >= ab->num_radios)) + break; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ether_addr_copy(pdev->mac_addr, addr_list[i].addr); + } + ab->pdevs_macaddr_valid = true; + break; + default: + break; + } + + return 0; +} + +static int ath11k_ready_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_tlv_rdy_parse rdy_parse = { }; + int ret; + + ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len, + ath11k_wmi_tlv_rdy_parse, &rdy_parse); + if (ret) { + ath11k_warn(ab, "failed to parse tlv %d\n", ret); + return ret; + } + + complete(&ab->wmi_ab.unified_ready); + return 0; +} + +static void ath11k_peer_delete_resp_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_peer_delete_resp_event peer_del_resp; + + if (ath11k_pull_peer_del_resp_ev(ab, skb, &peer_del_resp) != 0) { + ath11k_warn(ab, "failed to extract peer delete resp"); + return; + } + + /* TODO: Do we need to validate whether ath11k_peer_find() return NULL + * Why this is needed when there is HTT event for peer delete + */ +} + +static inline const char *ath11k_wmi_vdev_resp_print(u32 vdev_resp_status) +{ + switch (vdev_resp_status) { + case WMI_VDEV_START_RESPONSE_INVALID_VDEVID: + return "invalid vdev id"; + case WMI_VDEV_START_RESPONSE_NOT_SUPPORTED: + return "not supported"; + case WMI_VDEV_START_RESPONSE_DFS_VIOLATION: + return "dfs violation"; + case WMI_VDEV_START_RESPONSE_INVALID_REGDOMAIN: + return "invalid regdomain"; + default: + return "unknown"; + } +} + +static void ath11k_vdev_start_resp_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_vdev_start_resp_event vdev_start_resp; + struct ath11k *ar; + u32 status; + + if (ath11k_pull_vdev_start_resp_tlv(ab, skb, &vdev_start_resp) != 0) { + ath11k_warn(ab, "failed to extract vdev start resp"); + return; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_start_resp.vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in vdev start resp ev %d", + vdev_start_resp.vdev_id); + rcu_read_unlock(); + return; + } + + ar->last_wmi_vdev_start_status = 0; + + status = vdev_start_resp.status; + + if (WARN_ON_ONCE(status)) { + ath11k_warn(ab, "vdev start resp error status %d (%s)\n", + status, ath11k_wmi_vdev_resp_print(status)); + ar->last_wmi_vdev_start_status = status; + } + + complete(&ar->vdev_setup_done); + + rcu_read_unlock(); + + ath11k_dbg(ab, ATH11K_DBG_WMI, "vdev start resp for vdev id %d", + vdev_start_resp.vdev_id); +} + +static void ath11k_bcn_tx_status_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + u32 vdev_id, tx_status; + + if (ath11k_pull_bcn_tx_status_ev(ab, skb->data, skb->len, + &vdev_id, &tx_status) != 0) { + ath11k_warn(ab, "failed to extract bcn tx status"); + return; + } +} + +static void ath11k_vdev_stopped_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct ath11k *ar; + u32 vdev_id = 0; + + if (ath11k_pull_vdev_stopped_param_tlv(ab, skb, &vdev_id) != 0) { + ath11k_warn(ab, "failed to extract vdev stopped event"); + return; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_vdev_stop_status(ab, vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in vdev stopped ev %d", + vdev_id); + rcu_read_unlock(); + return; + } + + complete(&ar->vdev_setup_done); + + rcu_read_unlock(); + + ath11k_dbg(ab, ATH11K_DBG_WMI, "vdev stopped for vdev id %d", vdev_id); +} + +static void ath11k_mgmt_rx_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct mgmt_rx_event_params rx_ev = {0}; + struct ath11k *ar; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *hdr; + u16 fc; + struct ieee80211_supported_band *sband; + + if (ath11k_pull_mgmt_rx_params_tlv(ab, skb, &rx_ev) != 0) { + ath11k_warn(ab, "failed to extract mgmt rx event"); + dev_kfree_skb(skb); + return; + } + + memset(status, 0, sizeof(*status)); + + ath11k_dbg(ab, ATH11K_DBG_MGMT, "mgmt rx event status %08x\n", + rx_ev.status); + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, rx_ev.pdev_id); + + if (!ar) { + ath11k_warn(ab, "invalid pdev_id %d in mgmt_rx_event\n", + rx_ev.pdev_id); + dev_kfree_skb(skb); + goto exit; + } + + if ((test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) || + (rx_ev.status & (WMI_RX_STATUS_ERR_DECRYPT | + WMI_RX_STATUS_ERR_KEY_CACHE_MISS | WMI_RX_STATUS_ERR_CRC))) { + dev_kfree_skb(skb); + goto exit; + } + + if (rx_ev.status & WMI_RX_STATUS_ERR_MIC) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (rx_ev.channel >= 1 && rx_ev.channel <= 14) { + status->band = NL80211_BAND_2GHZ; + } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH11K_MAX_5G_CHAN) { + status->band = NL80211_BAND_5GHZ; + } else { + /* Shouldn't happen unless list of advertised channels to + * mac80211 has been changed. + */ + WARN_ON_ONCE(1); + dev_kfree_skb(skb); + goto exit; + } + + if (rx_ev.phy_mode == MODE_11B && status->band == NL80211_BAND_5GHZ) + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi mgmt rx 11b (CCK) on 5GHz\n"); + + sband = &ar->mac.sbands[status->band]; + + status->freq = ieee80211_channel_to_frequency(rx_ev.channel, + status->band); + status->signal = rx_ev.snr + ATH11K_DEFAULT_NOISE_FLOOR; + status->rate_idx = ath11k_mac_bitrate_to_idx(sband, rx_ev.rate / 100); + + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_control); + + /* Firmware is guaranteed to report all essential management frames via + * WMI while it can deliver some extra via HTT. Since there can be + * duplicates split the reporting wrt monitor/sniffing. + */ + status->flag |= RX_FLAG_SKIP_MONITOR; + + /* In case of PMF, FW delivers decrypted frames with Protected Bit set. + * Don't clear that. Also, FW delivers broadcast management frames + * (ex: group privacy action frames in mesh) as encrypted payload. + */ + if (ieee80211_has_protected(hdr->frame_control) && + !is_multicast_ether_addr(ieee80211_get_DA(hdr))) { + status->flag |= RX_FLAG_DECRYPTED; + + if (!ieee80211_is_robust_mgmt_frame(skb)) { + status->flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16(fc & + ~IEEE80211_FCTL_PROTECTED); + } + } + + /* TODO: Pending handle beacon implementation + *if (ieee80211_is_beacon(hdr->frame_control)) + * ath11k_mac_handle_beacon(ar, skb); + */ + + ath11k_dbg(ab, ATH11K_DBG_MGMT, + "event mgmt rx skb %pK len %d ftype %02x stype %02x\n", + skb, skb->len, + fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); + + ath11k_dbg(ab, ATH11K_DBG_MGMT, + "event mgmt rx freq %d band %d snr %d, rate_idx %d\n", + status->freq, status->band, status->signal, + status->rate_idx); + + ieee80211_rx_ni(ar->hw, skb); + +exit: + rcu_read_unlock(); +} + +static void ath11k_mgmt_tx_compl_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_mgmt_tx_compl_event tx_compl_param = {0}; + struct ath11k *ar; + + if (ath11k_pull_mgmt_tx_compl_param_tlv(ab, skb, &tx_compl_param) != 0) { + ath11k_warn(ab, "failed to extract mgmt tx compl event"); + return; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, tx_compl_param.pdev_id); + if (!ar) { + ath11k_warn(ab, "invalid pdev id %d in mgmt_tx_compl_event\n", + tx_compl_param.pdev_id); + goto exit; + } + + wmi_process_mgmt_tx_comp(ar, tx_compl_param.desc_id, + tx_compl_param.status); + + ath11k_dbg(ab, ATH11K_DBG_MGMT, + "mgmt tx compl ev pdev_id %d, desc_id %d, status %d", + tx_compl_param.pdev_id, tx_compl_param.desc_id, + tx_compl_param.status); + +exit: + rcu_read_unlock(); +} + +static struct ath11k *ath11k_get_ar_on_scan_abort(struct ath11k_base *ab, + u32 vdev_id) +{ + int i; + struct ath11k_pdev *pdev; + struct ath11k *ar; + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) { + ar = pdev->ar; + + spin_lock_bh(&ar->data_lock); + if (ar->scan.state == ATH11K_SCAN_ABORTING && + ar->scan.vdev_id == vdev_id) { + spin_unlock_bh(&ar->data_lock); + return ar; + } + spin_unlock_bh(&ar->data_lock); + } + } + return NULL; +} + +static void ath11k_scan_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct ath11k *ar; + struct wmi_scan_event scan_ev = {0}; + + if (ath11k_pull_scan_ev(ab, skb, &scan_ev) != 0) { + ath11k_warn(ab, "failed to extract scan event"); + return; + } + + rcu_read_lock(); + + /* In case the scan was cancelled, ex. during interface teardown, + * the interface will not be found in active interfaces. + * Rather, in such scenarios, iterate over the active pdev's to + * search 'ar' if the corresponding 'ar' scan is ABORTING and the + * aborting scan's vdev id matches this event info. + */ + if (scan_ev.event_type == WMI_SCAN_EVENT_COMPLETED && + scan_ev.reason == WMI_SCAN_REASON_CANCELLED) + ar = ath11k_get_ar_on_scan_abort(ab, scan_ev.vdev_id); + else + ar = ath11k_mac_get_ar_by_vdev_id(ab, scan_ev.vdev_id); + + if (!ar) { + ath11k_warn(ab, "Received scan event for unknown vdev"); + rcu_read_unlock(); + return; + } + + spin_lock_bh(&ar->data_lock); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "scan event %s type %d reason %d freq %d req_id %d scan_id %d vdev_id %d state %s (%d)\n", + ath11k_wmi_event_scan_type_str(scan_ev.event_type, scan_ev.reason), + scan_ev.event_type, scan_ev.reason, scan_ev.channel_freq, + scan_ev.scan_req_id, scan_ev.scan_id, scan_ev.vdev_id, + ath11k_scan_state_str(ar->scan.state), ar->scan.state); + + switch (scan_ev.event_type) { + case WMI_SCAN_EVENT_STARTED: + ath11k_wmi_event_scan_started(ar); + break; + case WMI_SCAN_EVENT_COMPLETED: + ath11k_wmi_event_scan_completed(ar); + break; + case WMI_SCAN_EVENT_BSS_CHANNEL: + ath11k_wmi_event_scan_bss_chan(ar); + break; + case WMI_SCAN_EVENT_FOREIGN_CHAN: + ath11k_wmi_event_scan_foreign_chan(ar, scan_ev.channel_freq); + break; + case WMI_SCAN_EVENT_START_FAILED: + ath11k_warn(ab, "received scan start failure event\n"); + ath11k_wmi_event_scan_start_failed(ar); + break; + case WMI_SCAN_EVENT_DEQUEUED: + case WMI_SCAN_EVENT_PREEMPTED: + case WMI_SCAN_EVENT_RESTARTED: + case WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT: + default: + break; + } + + spin_unlock_bh(&ar->data_lock); + + rcu_read_unlock(); +} + +static void ath11k_peer_sta_kickout_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_peer_sta_kickout_arg arg = {}; + struct ieee80211_sta *sta; + struct ath11k_peer *peer; + struct ath11k *ar; + + if (ath11k_pull_peer_sta_kickout_ev(ab, skb, &arg) != 0) { + ath11k_warn(ab, "failed to extract peer sta kickout event"); + return; + } + + rcu_read_lock(); + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find_by_addr(ab, arg.mac_addr); + + if (!peer) { + ath11k_warn(ab, "peer not found %pM\n", + arg.mac_addr); + goto exit; + } + + ar = ath11k_mac_get_ar_by_vdev_id(ab, peer->vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in peer sta kickout ev %d", + peer->vdev_id); + goto exit; + } + + sta = ieee80211_find_sta_by_ifaddr(ar->hw, + arg.mac_addr, NULL); + if (!sta) { + ath11k_warn(ab, "Spurious quick kickout for STA %pM\n", + arg.mac_addr); + goto exit; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, "peer sta kickout event %pM", + arg.mac_addr); + + ieee80211_report_low_ack(sta, 10); + +exit: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +} + +static void ath11k_roam_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_roam_event roam_ev = {}; + struct ath11k *ar; + + if (ath11k_pull_roam_ev(ab, skb, &roam_ev) != 0) { + ath11k_warn(ab, "failed to extract roam event"); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi roam event vdev %u reason 0x%08x rssi %d\n", + roam_ev.vdev_id, roam_ev.reason, roam_ev.rssi); + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, roam_ev.vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in roam ev %d", + roam_ev.vdev_id); + rcu_read_unlock(); + return; + } + + if (roam_ev.reason >= WMI_ROAM_REASON_MAX) + ath11k_warn(ab, "ignoring unknown roam event reason %d on vdev %i\n", + roam_ev.reason, roam_ev.vdev_id); + + switch (roam_ev.reason) { + case WMI_ROAM_REASON_BEACON_MISS: + /* TODO: Pending beacon miss and connection_loss_work + * implementation + * ath11k_mac_handle_beacon_miss(ar, vdev_id); + */ + break; + case WMI_ROAM_REASON_BETTER_AP: + case WMI_ROAM_REASON_LOW_RSSI: + case WMI_ROAM_REASON_SUITABLE_AP_FOUND: + case WMI_ROAM_REASON_HO_FAILED: + ath11k_warn(ab, "ignoring not implemented roam event reason %d on vdev %i\n", + roam_ev.reason, roam_ev.vdev_id); + break; + } + + rcu_read_unlock(); +} + +static void ath11k_chan_info_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_chan_info_event ch_info_ev = {0}; + struct ath11k *ar; + struct survey_info *survey; + int idx; + /* HW channel counters frequency value in hertz */ + u32 cc_freq_hz = ab->cc_freq_hz; + + if (ath11k_pull_chan_info_ev(ab, skb->data, skb->len, &ch_info_ev) != 0) { + ath11k_warn(ab, "failed to extract chan info event"); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "chan info vdev_id %d err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d mac_clk_mhz %d\n", + ch_info_ev.vdev_id, ch_info_ev.err_code, ch_info_ev.freq, + ch_info_ev.cmd_flags, ch_info_ev.noise_floor, + ch_info_ev.rx_clear_count, ch_info_ev.cycle_count, + ch_info_ev.mac_clk_mhz); + + if (ch_info_ev.cmd_flags == WMI_CHAN_INFO_END_RESP) { + ath11k_dbg(ab, ATH11K_DBG_WMI, "chan info report completed\n"); + return; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, ch_info_ev.vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in chan info ev %d", + ch_info_ev.vdev_id); + rcu_read_unlock(); + return; + } + spin_lock_bh(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_STARTING: + ath11k_warn(ab, "received chan info event without a scan request, ignoring\n"); + goto exit; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + break; + } + + idx = freq_to_idx(ar, ch_info_ev.freq); + if (idx >= ARRAY_SIZE(ar->survey)) { + ath11k_warn(ab, "chan info: invalid frequency %d (idx %d out of bounds)\n", + ch_info_ev.freq, idx); + goto exit; + } + + /* If FW provides MAC clock frequency in Mhz, overriding the initialized + * HW channel counters frequency value + */ + if (ch_info_ev.mac_clk_mhz) + cc_freq_hz = (ch_info_ev.mac_clk_mhz * 1000); + + if (ch_info_ev.cmd_flags == WMI_CHAN_INFO_START_RESP) { + survey = &ar->survey[idx]; + memset(survey, 0, sizeof(*survey)); + survey->noise = ch_info_ev.noise_floor; + survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; + survey->time = div_u64(ch_info_ev.cycle_count, cc_freq_hz); + survey->time_busy = div_u64(ch_info_ev.rx_clear_count, cc_freq_hz); + } +exit: + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); +} + +static void +ath11k_pdev_bss_chan_info_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_pdev_bss_chan_info_event bss_ch_info_ev = {}; + struct survey_info *survey; + struct ath11k *ar; + u32 cc_freq_hz = ab->cc_freq_hz; + u64 busy, total, tx, rx, rx_bss; + int idx; + + if (ath11k_pull_pdev_bss_chan_info_ev(ab, skb, &bss_ch_info_ev) != 0) { + ath11k_warn(ab, "failed to extract pdev bss chan info event"); + return; + } + + busy = (u64)(bss_ch_info_ev.rx_clear_count_high) << 32 | + bss_ch_info_ev.rx_clear_count_low; + + total = (u64)(bss_ch_info_ev.cycle_count_high) << 32 | + bss_ch_info_ev.cycle_count_low; + + tx = (u64)(bss_ch_info_ev.tx_cycle_count_high) << 32 | + bss_ch_info_ev.tx_cycle_count_low; + + rx = (u64)(bss_ch_info_ev.rx_cycle_count_high) << 32 | + bss_ch_info_ev.rx_cycle_count_low; + + rx_bss = (u64)(bss_ch_info_ev.rx_bss_cycle_count_high) << 32 | + bss_ch_info_ev.rx_bss_cycle_count_low; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "pdev bss chan info:\n pdev_id: %d freq: %d noise: %d cycle: busy %llu total %llu tx %llu rx %llu rx_bss %llu\n", + bss_ch_info_ev.pdev_id, bss_ch_info_ev.freq, + bss_ch_info_ev.noise_floor, busy, total, + tx, rx, rx_bss); + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, bss_ch_info_ev.pdev_id); + + if (!ar) { + ath11k_warn(ab, "invalid pdev id %d in bss_chan_info event\n", + bss_ch_info_ev.pdev_id); + rcu_read_unlock(); + return; + } + + spin_lock_bh(&ar->data_lock); + idx = freq_to_idx(ar, bss_ch_info_ev.freq); + if (idx >= ARRAY_SIZE(ar->survey)) { + ath11k_warn(ab, "bss chan info: invalid frequency %d (idx %d out of bounds)\n", + bss_ch_info_ev.freq, idx); + goto exit; + } + + survey = &ar->survey[idx]; + + survey->noise = bss_ch_info_ev.noise_floor; + survey->time = div_u64(total, cc_freq_hz); + survey->time_busy = div_u64(busy, cc_freq_hz); + survey->time_rx = div_u64(rx_bss, cc_freq_hz); + survey->time_tx = div_u64(tx, cc_freq_hz); + survey->filled |= (SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX); +exit: + spin_unlock_bh(&ar->data_lock); + complete(&ar->bss_survey_done); + + rcu_read_unlock(); +} + +static void ath11k_vdev_install_key_compl_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct wmi_vdev_install_key_complete_arg install_key_compl = {0}; + struct ath11k *ar; + + if (ath11k_pull_vdev_install_key_compl_ev(ab, skb, &install_key_compl) != 0) { + ath11k_warn(ab, "failed to extract install key compl event"); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "vdev install key ev idx %d flags %08x macaddr %pM status %d\n", + install_key_compl.key_idx, install_key_compl.key_flags, + install_key_compl.macaddr, install_key_compl.status); + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, install_key_compl.vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in install key compl ev %d", + install_key_compl.vdev_id); + rcu_read_unlock(); + return; + } + + ar->install_key_status = 0; + + if (install_key_compl.status != WMI_VDEV_INSTALL_KEY_COMPL_STATUS_SUCCESS) { + ath11k_warn(ab, "install key failed for %pM status %d\n", + install_key_compl.macaddr, install_key_compl.status); + ar->install_key_status = install_key_compl.status; + } + + complete(&ar->install_key_done); + rcu_read_unlock(); +} + +static void ath11k_service_available_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + const void **tb; + const struct wmi_service_available_event *ev; + int ret; + int i, j; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_SERVICE_AVAILABLE_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch svc available ev"); + kfree(tb); + return; + } + + /* TODO: Use wmi_service_segment_offset information to get the service + * especially when more services are advertised in multiple sevice + * available events. + */ + for (i = 0, j = WMI_MAX_SERVICE; + i < WMI_SERVICE_SEGMENT_BM_SIZE32 && j < WMI_MAX_EXT_SERVICE; + i++) { + do { + if (ev->wmi_service_segment_bitmap[i] & + BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32)) + set_bit(j, ab->wmi_ab.svc_map); + } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32); + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi_ext_service_bitmap 0:0x%x, 1:0x%x, 2:0x%x, 3:0x%x", + ev->wmi_service_segment_bitmap[0], ev->wmi_service_segment_bitmap[1], + ev->wmi_service_segment_bitmap[2], ev->wmi_service_segment_bitmap[3]); + + kfree(tb); +} + +static void ath11k_peer_assoc_conf_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_peer_assoc_conf_arg peer_assoc_conf = {0}; + struct ath11k *ar; + + if (ath11k_pull_peer_assoc_conf_ev(ab, skb, &peer_assoc_conf) != 0) { + ath11k_warn(ab, "failed to extract peer assoc conf event"); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "peer assoc conf ev vdev id %d macaddr %pM\n", + peer_assoc_conf.vdev_id, peer_assoc_conf.macaddr); + + 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); + return; + } + + complete(&ar->peer_assoc_done); +} + +static void ath11k_update_stats_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + ath11k_debug_fw_stats_process(ab, skb); +} + +/* PDEV_CTL_FAILSAFE_CHECK_EVENT is received from FW when the frequency scanned + * is not part of BDF CTL(Conformance test limits) table entries. + */ +static void ath11k_pdev_ctl_failsafe_check_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + const void **tb; + const struct wmi_pdev_ctl_failsafe_chk_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch pdev ctl failsafe check ev"); + kfree(tb); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "pdev ctl failsafe check ev status %d\n", + ev->ctl_failsafe_status); + + /* If ctl_failsafe_status is set to 1 FW will max out the Transmit power + * to 10 dBm else the CTL power entry in the BDF would be picked up. + */ + if (ev->ctl_failsafe_status != 0) + ath11k_warn(ab, "pdev ctl failsafe failure status %d", + ev->ctl_failsafe_status); + + kfree(tb); +} + +static void +ath11k_wmi_process_csa_switch_count_event(struct ath11k_base *ab, + const struct wmi_pdev_csa_switch_ev *ev, + const u32 *vdev_ids) +{ + int i; + struct ath11k_vif *arvif; + + /* Finish CSA once the switch count becomes NULL */ + if (ev->current_switch_count) + return; + + rcu_read_lock(); + for (i = 0; i < ev->num_vdevs; i++) { + arvif = ath11k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]); + + if (!arvif) { + ath11k_warn(ab, "Recvd csa status for unknown vdev %d", + vdev_ids[i]); + continue; + } + + if (arvif->is_up && arvif->vif->csa_active) + ieee80211_csa_finish(arvif->vif); + } + rcu_read_unlock(); +} + +static void +ath11k_wmi_pdev_csa_switch_count_status_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + const void **tb; + const struct wmi_pdev_csa_switch_ev *ev; + const u32 *vdev_ids; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT]; + vdev_ids = tb[WMI_TAG_ARRAY_UINT32]; + + if (!ev || !vdev_ids) { + ath11k_warn(ab, "failed to fetch pdev csa switch count ev"); + kfree(tb); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "pdev csa switch count %d for pdev %d, num_vdevs %d", + ev->current_switch_count, ev->pdev_id, + ev->num_vdevs); + + ath11k_wmi_process_csa_switch_count_event(ab, ev, vdev_ids); + + kfree(tb); +} + +static void +ath11k_wmi_pdev_dfs_radar_detected_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + const void **tb; + const struct wmi_pdev_radar_ev *ev; + struct ath11k *ar; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_PDEV_DFS_RADAR_DETECTION_EVENT]; + + if (!ev) { + ath11k_warn(ab, "failed to fetch pdev dfs radar detected ev"); + kfree(tb); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "pdev dfs radar detected on pdev %d, detection mode %d, chan freq %d, chan_width %d, detector id %d, seg id %d, timestamp %d, chirp %d, freq offset %d, sidx %d", + ev->pdev_id, ev->detection_mode, ev->chan_freq, ev->chan_width, + ev->detector_id, ev->segment_id, ev->timestamp, ev->is_chirp, + ev->freq_offset, ev->sidx); + + ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); + + if (!ar) { + ath11k_warn(ab, "radar detected in invalid pdev %d\n", + ev->pdev_id); + goto exit; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, "DFS Radar Detected in pdev %d\n", + ev->pdev_id); + + if (ar->dfs_block_radar_events) + ath11k_info(ab, "DFS Radar detected, but ignored as requested\n"); + else + ieee80211_radar_detected(ar->hw); + +exit: + kfree(tb); +} + +static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_tlv_event_id id; + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = FIELD_GET(WMI_CMD_HDR_CMD_ID, (cmd_hdr->cmd_id)); + + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + goto out; + + switch (id) { + /* Process all the WMI events here */ + case WMI_SERVICE_READY_EVENTID: + ath11k_service_ready_event(ab, skb); + break; + case WMI_SERVICE_READY_EXT_EVENTID: + ath11k_service_ready_ext_event(ab, skb); + break; + case WMI_REG_CHAN_LIST_CC_EVENTID: + ath11k_reg_chan_list_event(ab, skb); + break; + case WMI_READY_EVENTID: + ath11k_ready_event(ab, skb); + break; + case WMI_PEER_DELETE_RESP_EVENTID: + ath11k_peer_delete_resp_event(ab, skb); + break; + case WMI_VDEV_START_RESP_EVENTID: + ath11k_vdev_start_resp_event(ab, skb); + break; + case WMI_OFFLOAD_BCN_TX_STATUS_EVENTID: + ath11k_bcn_tx_status_event(ab, skb); + break; + case WMI_VDEV_STOPPED_EVENTID: + ath11k_vdev_stopped_event(ab, skb); + break; + case WMI_MGMT_RX_EVENTID: + ath11k_mgmt_rx_event(ab, skb); + /* mgmt_rx_event() owns the skb now! */ + return; + case WMI_MGMT_TX_COMPLETION_EVENTID: + ath11k_mgmt_tx_compl_event(ab, skb); + break; + case WMI_SCAN_EVENTID: + ath11k_scan_event(ab, skb); + break; + case WMI_PEER_STA_KICKOUT_EVENTID: + ath11k_peer_sta_kickout_event(ab, skb); + break; + case WMI_ROAM_EVENTID: + ath11k_roam_event(ab, skb); + break; + case WMI_CHAN_INFO_EVENTID: + ath11k_chan_info_event(ab, skb); + break; + case WMI_PDEV_BSS_CHAN_INFO_EVENTID: + ath11k_pdev_bss_chan_info_event(ab, skb); + break; + case WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID: + ath11k_vdev_install_key_compl_event(ab, skb); + break; + case WMI_SERVICE_AVAILABLE_EVENTID: + ath11k_service_available_event(ab, skb); + break; + case WMI_PEER_ASSOC_CONF_EVENTID: + ath11k_peer_assoc_conf_event(ab, skb); + break; + case WMI_UPDATE_STATS_EVENTID: + ath11k_update_stats_event(ab, skb); + break; + case WMI_PDEV_CTL_FAILSAFE_CHECK_EVENTID: + ath11k_pdev_ctl_failsafe_check_event(ab, skb); + break; + case WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID: + ath11k_wmi_pdev_csa_switch_count_status_event(ab, skb); + break; + /* add Unsupported events here */ + case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: + case WMI_VDEV_DELETE_RESP_EVENTID: + case WMI_PEER_OPER_MODE_CHANGE_EVENTID: + case WMI_TWT_ENABLE_EVENTID: + case WMI_TWT_DISABLE_EVENTID: + ath11k_dbg(ab, ATH11K_DBG_WMI, + "ignoring unsupported event 0x%x\n", id); + break; + case WMI_PDEV_DFS_RADAR_DETECTION_EVENTID: + ath11k_wmi_pdev_dfs_radar_detected_event(ab, skb); + break; + /* TODO: Add remaining events */ + default: + ath11k_warn(ab, "Unknown eventid: 0x%x\n", id); + break; + } + +out: + dev_kfree_skb(skb); +} + +static int ath11k_connect_pdev_htc_service(struct ath11k_base *ab, + u32 pdev_idx) +{ + int status; + u32 svc_id[] = { ATH11K_HTC_SVC_ID_WMI_CONTROL, + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1, + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2 }; + + struct ath11k_htc_svc_conn_req conn_req; + struct ath11k_htc_svc_conn_resp conn_resp; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + /* these fields are the same for all service endpoints */ + conn_req.ep_ops.ep_tx_complete = ath11k_wmi_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath11k_wmi_tlv_op_rx; + conn_req.ep_ops.ep_tx_credits = ath11k_wmi_op_ep_tx_credits; + + /* connect to control service */ + conn_req.service_id = svc_id[pdev_idx]; + + status = ath11k_htc_connect_service(&ab->htc, &conn_req, &conn_resp); + if (status) { + ath11k_warn(ab, "failed to connect to WMI CONTROL service status: %d\n", + status); + return status; + } + + ab->wmi_ab.wmi_endpoint_id[pdev_idx] = conn_resp.eid; + ab->wmi_ab.wmi[pdev_idx].eid = conn_resp.eid; + ab->wmi_ab.max_msg_len[pdev_idx] = conn_resp.max_msg_len; + + return 0; +} + +static int +ath11k_wmi_send_unit_test_cmd(struct ath11k *ar, + struct wmi_unit_test_cmd ut_cmd, + u32 *test_args) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_unit_test_cmd *cmd; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + u32 *ut_cmd_args; + int buf_len, arg_len; + int ret; + int i; + + arg_len = sizeof(u32) * ut_cmd.num_args; + buf_len = sizeof(ut_cmd) + arg_len + TLV_HDR_SIZE; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, buf_len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_unit_test_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_UNIT_TEST_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(ut_cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = ut_cmd.vdev_id; + cmd->module_id = ut_cmd.module_id; + cmd->num_args = ut_cmd.num_args; + cmd->diag_token = ut_cmd.diag_token; + + ptr = skb->data + sizeof(ut_cmd); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) | + FIELD_PREP(WMI_TLV_LEN, arg_len); + + ptr += TLV_HDR_SIZE; + + ut_cmd_args = ptr; + for (i = 0; i < ut_cmd.num_args; i++) + ut_cmd_args[i] = test_args[i]; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_UNIT_TEST_CMDID); + + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_UNIT_TEST CMD :%d\n", + ret); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI unit test : module %d vdev %d n_args %d token %d\n", + cmd->module_id, cmd->vdev_id, cmd->num_args, + cmd->diag_token); + + return ret; +} + +int ath11k_wmi_simulate_radar(struct ath11k *ar) +{ + struct ath11k_vif *arvif; + u32 dfs_args[DFS_MAX_TEST_ARGS]; + struct wmi_unit_test_cmd wmi_ut; + bool arvif_found = false; + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_started && arvif->vdev_type == WMI_VDEV_TYPE_AP) { + arvif_found = true; + break; + } + } + + if (!arvif_found) + return -EINVAL; + + dfs_args[DFS_TEST_CMDID] = 0; + dfs_args[DFS_TEST_PDEV_ID] = ar->pdev->pdev_id; + /* Currently we could pass segment_id(b0 - b1), chirp(b2) + * freq offset (b3 - b10) to unit test. For simulation + * purpose this can be set to 0 which is valid. + */ + dfs_args[DFS_TEST_RADAR_PARAM] = 0; + + wmi_ut.vdev_id = arvif->vdev_id; + wmi_ut.module_id = DFS_UNIT_TEST_MODULE; + wmi_ut.num_args = DFS_MAX_TEST_ARGS; + wmi_ut.diag_token = DFS_UNIT_TEST_TOKEN; + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, "Triggering Radar Simulation\n"); + + return ath11k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); +} + +int ath11k_wmi_connect(struct ath11k_base *ab) +{ + u32 i; + u8 wmi_ep_count; + + wmi_ep_count = ab->htc.wmi_ep_count; + if (wmi_ep_count > MAX_RADIOS) + return -1; + + for (i = 0; i < wmi_ep_count; i++) + ath11k_connect_pdev_htc_service(ab, i); + + return 0; +} + +static void ath11k_wmi_pdev_detach(struct ath11k_base *ab, u8 pdev_id) +{ + if (WARN_ON(pdev_id >= MAX_RADIOS)) + return; + + /* TODO: Deinit any pdev specific wmi resource */ +} + +int ath11k_wmi_pdev_attach(struct ath11k_base *ab, + u8 pdev_id) +{ + struct ath11k_pdev_wmi *wmi_handle; + + if (pdev_id >= MAX_RADIOS) + return -EINVAL; + + wmi_handle = &ab->wmi_ab.wmi[pdev_id]; + + wmi_handle->wmi_ab = &ab->wmi_ab; + + ab->wmi_ab.ab = ab; + /* TODO: Init remaining resource specific to pdev */ + + return 0; +} + +int ath11k_wmi_attach(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_wmi_pdev_attach(ab, 0); + if (ret) + return ret; + + ab->wmi_ab.ab = ab; + ab->wmi_ab.preferred_hw_mode = WMI_HOST_HW_MODE_MAX; + + /* TODO: Init remaining wmi soc resources required */ + init_completion(&ab->wmi_ab.service_ready); + init_completion(&ab->wmi_ab.unified_ready); + + return 0; +} + +void ath11k_wmi_detach(struct ath11k_base *ab) +{ + int i; + + /* TODO: Deinit wmi resource specific to SOC as required */ + + for (i = 0; i < ab->htc.wmi_ep_count; i++) + ath11k_wmi_pdev_detach(ab, i); +} diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h new file mode 100644 index 000000000000..1fde15c762ad --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -0,0 +1,4764 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_WMI_H +#define ATH11K_WMI_H + +#include <net/mac80211.h> +#include "htc.h" + +struct ath11k_base; +struct ath11k; +struct ath11k_fw_stats; + +#define PSOC_HOST_MAX_NUM_SS (8) + +/* defines to set Packet extension values whic can be 0 us, 8 usec or 16 usec */ +#define MAX_HE_NSS 8 +#define MAX_HE_MODULATION 8 +#define MAX_HE_RU 4 +#define HE_MODULATION_NONE 7 +#define HE_PET_0_USEC 0 +#define HE_PET_8_USEC 1 +#define HE_PET_16_USEC 2 + +#define WMI_MAX_NUM_SS MAX_HE_NSS +#define WMI_MAX_NUM_RU MAX_HE_RU + +#define WMI_TLV_CMD(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_EV(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_CMD_UNSUPPORTED 0 +#define WMI_TLV_PDEV_PARAM_UNSUPPORTED 0 +#define WMI_TLV_VDEV_PARAM_UNSUPPORTED 0 + +struct wmi_cmd_hdr { + u32 cmd_id; +} __packed; + +struct wmi_tlv { + u32 header; + u8 value[0]; +} __packed; + +#define WMI_TLV_LEN GENMASK(15, 0) +#define WMI_TLV_TAG GENMASK(31, 16) +#define TLV_HDR_SIZE sizeof_field(struct wmi_tlv, header) + +#define WMI_CMD_HDR_CMD_ID GENMASK(23, 0) +#define WMI_MAX_MEM_REQS 32 +#define ATH11K_MAX_HW_LISTEN_INTERVAL 5 + +#define WLAN_SCAN_PARAMS_MAX_SSID 16 +#define WLAN_SCAN_PARAMS_MAX_BSSID 4 +#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 + +#define WMI_BA_MODE_BUFFER_SIZE_256 3 +/* + * HW mode config type replicated from FW header + * @WMI_HOST_HW_MODE_SINGLE: Only one PHY is active. + * @WMI_HOST_HW_MODE_DBS: Both PHYs are active in different bands, + * one in 2G and another in 5G. + * @WMI_HOST_HW_MODE_SBS_PASSIVE: Both PHYs are in passive mode (only rx) in + * same band; no tx allowed. + * @WMI_HOST_HW_MODE_SBS: Both PHYs are active in the same band. + * Support for both PHYs within one band is planned + * for 5G only(as indicated in WMI_MAC_PHY_CAPABILITIES), + * but could be extended to other bands in the future. + * The separation of the band between the two PHYs needs + * to be communicated separately. + * @WMI_HOST_HW_MODE_DBS_SBS: 3 PHYs, with 2 on the same band doing SBS + * as in WMI_HW_MODE_SBS, and 3rd on the other band + * @WMI_HOST_HW_MODE_DBS_OR_SBS: Two PHY with one PHY capabale of both 2G and + * 5G. It can support SBS (5G + 5G) OR DBS (5G + 2G). + * @WMI_HOST_HW_MODE_MAX: Max hw_mode_id. Used to indicate invalid mode. + */ +enum wmi_host_hw_mode_config_type { + WMI_HOST_HW_MODE_SINGLE = 0, + WMI_HOST_HW_MODE_DBS = 1, + WMI_HOST_HW_MODE_SBS_PASSIVE = 2, + WMI_HOST_HW_MODE_SBS = 3, + WMI_HOST_HW_MODE_DBS_SBS = 4, + WMI_HOST_HW_MODE_DBS_OR_SBS = 5, + + /* keep last */ + WMI_HOST_HW_MODE_MAX +}; + +/* HW mode priority values used to detect the preferred HW mode + * on the available modes. + */ +enum wmi_host_hw_mode_priority { + WMI_HOST_HW_MODE_DBS_SBS_PRI, + WMI_HOST_HW_MODE_DBS_PRI, + WMI_HOST_HW_MODE_DBS_OR_SBS_PRI, + WMI_HOST_HW_MODE_SBS_PRI, + WMI_HOST_HW_MODE_SBS_PASSIVE_PRI, + WMI_HOST_HW_MODE_SINGLE_PRI, + + /* keep last the lowest priority */ + WMI_HOST_HW_MODE_MAX_PRI +}; + +enum { + WMI_HOST_WLAN_2G_CAP = 0x1, + WMI_HOST_WLAN_5G_CAP = 0x2, + WMI_HOST_WLAN_2G_5G_CAP = 0x3, +}; + +/* + * wmi command groups. + */ +enum wmi_cmd_group { + /* 0 to 2 are reserved */ + WMI_GRP_START = 0x3, + WMI_GRP_SCAN = WMI_GRP_START, + WMI_GRP_PDEV = 0x4, + WMI_GRP_VDEV = 0x5, + WMI_GRP_PEER = 0x6, + WMI_GRP_MGMT = 0x7, + WMI_GRP_BA_NEG = 0x8, + WMI_GRP_STA_PS = 0x9, + WMI_GRP_DFS = 0xa, + WMI_GRP_ROAM = 0xb, + WMI_GRP_OFL_SCAN = 0xc, + WMI_GRP_P2P = 0xd, + WMI_GRP_AP_PS = 0xe, + WMI_GRP_RATE_CTRL = 0xf, + WMI_GRP_PROFILE = 0x10, + WMI_GRP_SUSPEND = 0x11, + WMI_GRP_BCN_FILTER = 0x12, + WMI_GRP_WOW = 0x13, + WMI_GRP_RTT = 0x14, + WMI_GRP_SPECTRAL = 0x15, + WMI_GRP_STATS = 0x16, + WMI_GRP_ARP_NS_OFL = 0x17, + WMI_GRP_NLO_OFL = 0x18, + WMI_GRP_GTK_OFL = 0x19, + WMI_GRP_CSA_OFL = 0x1a, + WMI_GRP_CHATTER = 0x1b, + WMI_GRP_TID_ADDBA = 0x1c, + WMI_GRP_MISC = 0x1d, + WMI_GRP_GPIO = 0x1e, + WMI_GRP_FWTEST = 0x1f, + WMI_GRP_TDLS = 0x20, + WMI_GRP_RESMGR = 0x21, + WMI_GRP_STA_SMPS = 0x22, + WMI_GRP_WLAN_HB = 0x23, + WMI_GRP_RMC = 0x24, + WMI_GRP_MHF_OFL = 0x25, + WMI_GRP_LOCATION_SCAN = 0x26, + WMI_GRP_OEM = 0x27, + WMI_GRP_NAN = 0x28, + WMI_GRP_COEX = 0x29, + WMI_GRP_OBSS_OFL = 0x2a, + WMI_GRP_LPI = 0x2b, + WMI_GRP_EXTSCAN = 0x2c, + WMI_GRP_DHCP_OFL = 0x2d, + WMI_GRP_IPA = 0x2e, + WMI_GRP_MDNS_OFL = 0x2f, + WMI_GRP_SAP_OFL = 0x30, + WMI_GRP_OCB = 0x31, + WMI_GRP_SOC = 0x32, + WMI_GRP_PKT_FILTER = 0x33, + WMI_GRP_MAWC = 0x34, + WMI_GRP_PMF_OFFLOAD = 0x35, + WMI_GRP_BPF_OFFLOAD = 0x36, + WMI_GRP_NAN_DATA = 0x37, + WMI_GRP_PROTOTYPE = 0x38, + WMI_GRP_MONITOR = 0x39, + WMI_GRP_REGULATORY = 0x3a, + WMI_GRP_HW_DATA_FILTER = 0x3b, + WMI_GRP_WLM = 0x3c, + WMI_GRP_11K_OFFLOAD = 0x3d, + WMI_GRP_TWT = 0x3e, + WMI_GRP_MOTION_DET = 0x3f, + WMI_GRP_SPATIAL_REUSE = 0x40, +}; + +#define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) + +#define WMI_CMD_UNSUPPORTED 0 + +enum wmi_tlv_cmd_id { + WMI_INIT_CMDID = 0x1, + WMI_START_SCAN_CMDID = WMI_TLV_CMD(WMI_GRP_SCAN), + WMI_STOP_SCAN_CMDID, + WMI_SCAN_CHAN_LIST_CMDID, + WMI_SCAN_SCH_PRIO_TBL_CMDID, + WMI_SCAN_UPDATE_REQUEST_CMDID, + WMI_SCAN_PROB_REQ_OUI_CMDID, + WMI_SCAN_ADAPTIVE_DWELL_CONFIG_CMDID, + WMI_PDEV_SET_REGDOMAIN_CMDID = WMI_TLV_CMD(WMI_GRP_PDEV), + WMI_PDEV_SET_CHANNEL_CMDID, + WMI_PDEV_SET_PARAM_CMDID, + WMI_PDEV_PKTLOG_ENABLE_CMDID, + WMI_PDEV_PKTLOG_DISABLE_CMDID, + WMI_PDEV_SET_WMM_PARAMS_CMDID, + WMI_PDEV_SET_HT_CAP_IE_CMDID, + WMI_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_PDEV_SET_QUIET_MODE_CMDID, + WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_PDEV_GET_TPC_CONFIG_CMDID, + WMI_PDEV_SET_BASE_MACADDR_CMDID, + WMI_PDEV_DUMP_CMDID, + WMI_PDEV_SET_LED_CONFIG_CMDID, + WMI_PDEV_GET_TEMPERATURE_CMDID, + WMI_PDEV_SET_LED_FLASHING_CMDID, + WMI_PDEV_SMART_ANT_ENABLE_CMDID, + WMI_PDEV_SMART_ANT_SET_RX_ANTENNA_CMDID, + WMI_PDEV_SET_ANTENNA_SWITCH_TABLE_CMDID, + WMI_PDEV_SET_CTL_TABLE_CMDID, + WMI_PDEV_SET_MIMOGAIN_TABLE_CMDID, + WMI_PDEV_FIPS_CMDID, + WMI_PDEV_GET_ANI_CCK_CONFIG_CMDID, + WMI_PDEV_GET_ANI_OFDM_CONFIG_CMDID, + WMI_PDEV_GET_NFCAL_POWER_CMDID, + WMI_PDEV_GET_TPC_CMDID, + WMI_MIB_STATS_ENABLE_CMDID, + WMI_PDEV_SET_PCL_CMDID, + WMI_PDEV_SET_HW_MODE_CMDID, + WMI_PDEV_SET_MAC_CONFIG_CMDID, + WMI_PDEV_SET_ANTENNA_MODE_CMDID, + WMI_SET_PERIODIC_CHANNEL_STATS_CONFIG_CMDID, + WMI_PDEV_WAL_POWER_DEBUG_CMDID, + WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID, + WMI_PDEV_SET_WAKEUP_CONFIG_CMDID, + WMI_PDEV_GET_ANTDIV_STATUS_CMDID, + WMI_PDEV_GET_CHIP_POWER_STATS_CMDID, + WMI_PDEV_SET_STATS_THRESHOLD_CMDID, + WMI_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMDID, + WMI_PDEV_UPDATE_PKT_ROUTING_CMDID, + WMI_PDEV_CHECK_CAL_VERSION_CMDID, + WMI_PDEV_SET_DIVERSITY_GAIN_CMDID, + WMI_PDEV_DIV_GET_RSSI_ANTID_CMDID, + WMI_PDEV_BSS_CHAN_INFO_REQUEST_CMDID, + WMI_PDEV_UPDATE_PMK_CACHE_CMDID, + WMI_PDEV_UPDATE_FILS_HLP_PKT_CMDID, + WMI_PDEV_UPDATE_CTLTABLE_REQUEST_CMDID, + WMI_PDEV_CONFIG_VENDOR_OUI_ACTION_CMDID, + WMI_PDEV_SET_AC_TX_QUEUE_OPTIMIZED_CMDID, + WMI_PDEV_SET_RX_FILTER_PROMISCUOUS_CMDID, + WMI_PDEV_DMA_RING_CFG_REQ_CMDID, + WMI_PDEV_HE_TB_ACTION_FRM_CMDID, + WMI_PDEV_PKTLOG_FILTER_CMDID, + WMI_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_VDEV), + WMI_VDEV_DELETE_CMDID, + WMI_VDEV_START_REQUEST_CMDID, + WMI_VDEV_RESTART_REQUEST_CMDID, + WMI_VDEV_UP_CMDID, + WMI_VDEV_STOP_CMDID, + WMI_VDEV_DOWN_CMDID, + WMI_VDEV_SET_PARAM_CMDID, + WMI_VDEV_INSTALL_KEY_CMDID, + WMI_VDEV_WNM_SLEEPMODE_CMDID, + WMI_VDEV_WMM_ADDTS_CMDID, + WMI_VDEV_WMM_DELTS_CMDID, + WMI_VDEV_SET_WMM_PARAMS_CMDID, + WMI_VDEV_SET_GTX_PARAMS_CMDID, + WMI_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMDID, + WMI_VDEV_PLMREQ_START_CMDID, + WMI_VDEV_PLMREQ_STOP_CMDID, + WMI_VDEV_TSF_TSTAMP_ACTION_CMDID, + WMI_VDEV_SET_IE_CMDID, + WMI_VDEV_RATEMASK_CMDID, + WMI_VDEV_ATF_REQUEST_CMDID, + WMI_VDEV_SET_DSCP_TID_MAP_CMDID, + WMI_VDEV_FILTER_NEIGHBOR_RX_PACKETS_CMDID, + WMI_VDEV_SET_QUIET_MODE_CMDID, + WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID, + WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID, + WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID, + WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER), + WMI_PEER_DELETE_CMDID, + WMI_PEER_FLUSH_TIDS_CMDID, + WMI_PEER_SET_PARAM_CMDID, + WMI_PEER_ASSOC_CMDID, + WMI_PEER_ADD_WDS_ENTRY_CMDID, + WMI_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_PEER_MCAST_GROUP_CMDID, + WMI_PEER_INFO_REQ_CMDID, + WMI_PEER_GET_ESTIMATED_LINKSPEED_CMDID, + WMI_PEER_SET_RATE_REPORT_CONDITION_CMDID, + WMI_PEER_UPDATE_WDS_ENTRY_CMDID, + WMI_PEER_ADD_PROXY_STA_ENTRY_CMDID, + WMI_PEER_SMART_ANT_SET_TX_ANTENNA_CMDID, + WMI_PEER_SMART_ANT_SET_TRAIN_INFO_CMDID, + WMI_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMDID, + WMI_PEER_ATF_REQUEST_CMDID, + WMI_PEER_BWF_REQUEST_CMDID, + WMI_PEER_REORDER_QUEUE_SETUP_CMDID, + WMI_PEER_REORDER_QUEUE_REMOVE_CMDID, + WMI_PEER_SET_RX_BLOCKSIZE_CMDID, + WMI_PEER_ANTDIV_INFO_REQ_CMDID, + WMI_BCN_TX_CMDID = WMI_TLV_CMD(WMI_GRP_MGMT), + WMI_PDEV_SEND_BCN_CMDID, + WMI_BCN_TMPL_CMDID, + WMI_BCN_FILTER_RX_CMDID, + WMI_PRB_REQ_FILTER_RX_CMDID, + WMI_MGMT_TX_CMDID, + WMI_PRB_TMPL_CMDID, + WMI_MGMT_TX_SEND_CMDID, + WMI_OFFCHAN_DATA_TX_SEND_CMDID, + WMI_PDEV_SEND_FD_CMDID, + WMI_BCN_OFFLOAD_CTRL_CMDID, + WMI_BSS_COLOR_CHANGE_ENABLE_CMDID, + WMI_VDEV_BCN_OFFLOAD_QUIET_CONFIG_CMDID, + WMI_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_GRP_BA_NEG), + WMI_ADDBA_SEND_CMDID, + WMI_ADDBA_STATUS_CMDID, + WMI_DELBA_SEND_CMDID, + WMI_ADDBA_SET_RESP_CMDID, + WMI_SEND_SINGLEAMSDU_CMDID, + WMI_STA_POWERSAVE_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_STA_PS), + WMI_STA_POWERSAVE_PARAM_CMDID, + WMI_STA_MIMO_PS_MODE_CMDID, + WMI_PDEV_DFS_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_DFS), + WMI_PDEV_DFS_DISABLE_CMDID, + WMI_DFS_PHYERR_FILTER_ENA_CMDID, + WMI_DFS_PHYERR_FILTER_DIS_CMDID, + WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID, + WMI_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMDID, + WMI_VDEV_ADFS_CH_CFG_CMDID, + WMI_VDEV_ADFS_OCAC_ABORT_CMDID, + WMI_ROAM_SCAN_MODE = WMI_TLV_CMD(WMI_GRP_ROAM), + WMI_ROAM_SCAN_RSSI_THRESHOLD, + WMI_ROAM_SCAN_PERIOD, + WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_ROAM_AP_PROFILE, + WMI_ROAM_CHAN_LIST, + WMI_ROAM_SCAN_CMD, + WMI_ROAM_SYNCH_COMPLETE, + WMI_ROAM_SET_RIC_REQUEST_CMDID, + WMI_ROAM_INVOKE_CMDID, + WMI_ROAM_FILTER_CMDID, + WMI_ROAM_SUBNET_CHANGE_CONFIG_CMDID, + WMI_ROAM_CONFIGURE_MAWC_CMDID, + WMI_ROAM_SET_MBO_PARAM_CMDID, + WMI_ROAM_PER_CONFIG_CMDID, + WMI_OFL_SCAN_ADD_AP_PROFILE = WMI_TLV_CMD(WMI_GRP_OFL_SCAN), + WMI_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_OFL_SCAN_PERIOD, + WMI_P2P_DEV_SET_DEVICE_INFO = WMI_TLV_CMD(WMI_GRP_P2P), + WMI_P2P_DEV_SET_DISCOVERABILITY, + WMI_P2P_GO_SET_BEACON_IE, + WMI_P2P_GO_SET_PROBE_RESP_IE, + WMI_P2P_SET_VENDOR_IE_DATA_CMDID, + WMI_P2P_DISC_OFFLOAD_CONFIG_CMDID, + WMI_P2P_DISC_OFFLOAD_APPIE_CMDID, + WMI_P2P_DISC_OFFLOAD_PATTERN_CMDID, + WMI_P2P_SET_OPPPS_PARAM_CMDID, + WMI_P2P_LISTEN_OFFLOAD_START_CMDID, + WMI_P2P_LISTEN_OFFLOAD_STOP_CMDID, + WMI_AP_PS_PEER_PARAM_CMDID = WMI_TLV_CMD(WMI_GRP_AP_PS), + WMI_AP_PS_PEER_UAPSD_COEX_CMDID, + WMI_AP_PS_EGAP_PARAM_CMDID, + WMI_PEER_RATE_RETRY_SCHED_CMDID = WMI_TLV_CMD(WMI_GRP_RATE_CTRL), + WMI_WLAN_PROFILE_TRIGGER_CMDID = WMI_TLV_CMD(WMI_GRP_PROFILE), + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + WMI_PDEV_SUSPEND_CMDID = WMI_TLV_CMD(WMI_GRP_SUSPEND), + WMI_PDEV_RESUME_CMDID, + WMI_ADD_BCN_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_BCN_FILTER), + WMI_RMV_BCN_FILTER_CMDID, + WMI_WOW_ADD_WAKE_PATTERN_CMDID = WMI_TLV_CMD(WMI_GRP_WOW), + WMI_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_WOW_ENABLE_CMDID, + WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + WMI_WOW_IOAC_ADD_KEEPALIVE_CMDID, + WMI_WOW_IOAC_DEL_KEEPALIVE_CMDID, + WMI_WOW_IOAC_ADD_WAKE_PATTERN_CMDID, + WMI_WOW_IOAC_DEL_WAKE_PATTERN_CMDID, + WMI_D0_WOW_ENABLE_DISABLE_CMDID, + WMI_EXTWOW_ENABLE_CMDID, + WMI_EXTWOW_SET_APP_TYPE1_PARAMS_CMDID, + WMI_EXTWOW_SET_APP_TYPE2_PARAMS_CMDID, + WMI_WOW_ENABLE_ICMPV6_NA_FLT_CMDID, + WMI_WOW_UDP_SVC_OFLD_CMDID, + WMI_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMDID, + WMI_WOW_SET_ACTION_WAKE_UP_CMDID, + WMI_RTT_MEASREQ_CMDID = WMI_TLV_CMD(WMI_GRP_RTT), + WMI_RTT_TSF_CMDID, + WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID = WMI_TLV_CMD(WMI_GRP_SPECTRAL), + WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + WMI_REQUEST_STATS_CMDID = WMI_TLV_CMD(WMI_GRP_STATS), + WMI_MCC_SCHED_TRAFFIC_STATS_CMDID, + WMI_REQUEST_STATS_EXT_CMDID, + WMI_REQUEST_LINK_STATS_CMDID, + WMI_START_LINK_STATS_CMDID, + WMI_CLEAR_LINK_STATS_CMDID, + WMI_GET_FW_MEM_DUMP_CMDID, + WMI_DEBUG_MESG_FLUSH_CMDID, + WMI_DIAG_EVENT_LOG_CONFIG_CMDID, + WMI_REQUEST_WLAN_STATS_CMDID, + WMI_REQUEST_RCPI_CMDID, + WMI_REQUEST_PEER_STATS_INFO_CMDID, + WMI_REQUEST_RADIO_CHAN_STATS_CMDID, + WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL), + WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), + WMI_APFIND_CMDID, + WMI_PASSPOINT_LIST_CONFIG_CMDID, + WMI_NLO_CONFIGURE_MAWC_CMDID, + WMI_GTK_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_GTK_OFL), + WMI_CSA_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_CSA_OFL), + WMI_CSA_OFFLOAD_CHANSWITCH_CMDID, + WMI_CHATTER_SET_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_CHATTER), + WMI_CHATTER_ADD_COALESCING_FILTER_CMDID, + WMI_CHATTER_DELETE_COALESCING_FILTER_CMDID, + WMI_CHATTER_COALESCING_QUERY_CMDID, + WMI_PEER_TID_ADDBA_CMDID = WMI_TLV_CMD(WMI_GRP_TID_ADDBA), + WMI_PEER_TID_DELBA_CMDID, + WMI_STA_DTIM_PS_METHOD_CMDID, + WMI_STA_UAPSD_AUTO_TRIG_CMDID, + WMI_STA_KEEPALIVE_CMDID, + WMI_BA_REQ_SSN_CMDID, + WMI_ECHO_CMDID = WMI_TLV_CMD(WMI_GRP_MISC), + WMI_PDEV_UTF_CMDID, + WMI_DBGLOG_CFG_CMDID, + WMI_PDEV_QVIT_CMDID, + WMI_PDEV_FTM_INTG_CMDID, + WMI_VDEV_SET_KEEPALIVE_CMDID, + WMI_VDEV_GET_KEEPALIVE_CMDID, + WMI_FORCE_FW_HANG_CMDID, + WMI_SET_MCASTBCAST_FILTER_CMDID, + WMI_THERMAL_MGMT_CMDID, + WMI_HOST_AUTO_SHUTDOWN_CFG_CMDID, + WMI_TPC_CHAINMASK_CONFIG_CMDID, + WMI_SET_ANTENNA_DIVERSITY_CMDID, + WMI_OCB_SET_SCHED_CMDID, + WMI_RSSI_BREACH_MONITOR_CONFIG_CMDID, + WMI_LRO_CONFIG_CMDID, + WMI_TRANSFER_DATA_TO_FLASH_CMDID, + WMI_CONFIG_ENHANCED_MCAST_FILTER_CMDID, + WMI_VDEV_WISA_CMDID, + WMI_DBGLOG_TIME_STAMP_SYNC_CMDID, + WMI_SET_MULTIPLE_MCAST_FILTER_CMDID, + WMI_READ_DATA_FROM_FLASH_CMDID, + WMI_GPIO_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_GPIO), + WMI_GPIO_OUTPUT_CMDID, + WMI_TXBF_CMDID, + WMI_FWTEST_VDEV_MCC_SET_TBTT_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_FWTEST), + WMI_FWTEST_P2P_SET_NOA_PARAM_CMDID, + WMI_UNIT_TEST_CMDID, + WMI_FWTEST_CMDID, + WMI_QBOOST_CFG_CMDID, + WMI_TDLS_SET_STATE_CMDID = WMI_TLV_CMD(WMI_GRP_TDLS), + WMI_TDLS_PEER_UPDATE_CMDID, + WMI_TDLS_SET_OFFCHAN_MODE_CMDID, + WMI_RESMGR_ADAPTIVE_OCS_EN_DIS_CMDID = WMI_TLV_CMD(WMI_GRP_RESMGR), + WMI_RESMGR_SET_CHAN_TIME_QUOTA_CMDID, + WMI_RESMGR_SET_CHAN_LATENCY_CMDID, + WMI_STA_SMPS_FORCE_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_STA_SMPS), + WMI_STA_SMPS_PARAM_CMDID, + WMI_HB_SET_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_WLAN_HB), + WMI_HB_SET_TCP_PARAMS_CMDID, + WMI_HB_SET_TCP_PKT_FILTER_CMDID, + WMI_HB_SET_UDP_PARAMS_CMDID, + WMI_HB_SET_UDP_PKT_FILTER_CMDID, + WMI_RMC_SET_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_RMC), + WMI_RMC_SET_ACTION_PERIOD_CMDID, + WMI_RMC_CONFIG_CMDID, + WMI_RMC_SET_MANUAL_LEADER_CMDID, + WMI_MHF_OFFLOAD_SET_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_MHF_OFL), + WMI_MHF_OFFLOAD_PLUMB_ROUTING_TBL_CMDID, + WMI_BATCH_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_LOCATION_SCAN), + WMI_BATCH_SCAN_DISABLE_CMDID, + WMI_BATCH_SCAN_TRIGGER_RESULT_CMDID, + WMI_OEM_REQ_CMDID = WMI_TLV_CMD(WMI_GRP_OEM), + WMI_OEM_REQUEST_CMDID, + WMI_LPI_OEM_REQ_CMDID, + WMI_NAN_CMDID = WMI_TLV_CMD(WMI_GRP_NAN), + WMI_MODEM_POWER_STATE_CMDID = WMI_TLV_CMD(WMI_GRP_COEX), + WMI_CHAN_AVOID_UPDATE_CMDID, + WMI_COEX_CONFIG_CMDID, + WMI_CHAN_AVOID_RPT_ALLOW_CMDID, + WMI_COEX_GET_ANTENNA_ISOLATION_CMDID, + WMI_SAR_LIMITS_CMDID, + WMI_OBSS_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_OBSS_OFL), + WMI_OBSS_SCAN_DISABLE_CMDID, + WMI_LPI_MGMT_SNOOPING_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_LPI), + WMI_LPI_START_SCAN_CMDID, + WMI_LPI_STOP_SCAN_CMDID, + WMI_EXTSCAN_START_CMDID = WMI_TLV_CMD(WMI_GRP_EXTSCAN), + WMI_EXTSCAN_STOP_CMDID, + WMI_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMDID, + WMI_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMDID, + WMI_EXTSCAN_GET_CACHED_RESULTS_CMDID, + WMI_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMDID, + WMI_EXTSCAN_SET_CAPABILITIES_CMDID, + WMI_EXTSCAN_GET_CAPABILITIES_CMDID, + WMI_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMDID, + WMI_EXTSCAN_CONFIGURE_MAWC_CMDID, + WMI_SET_DHCP_SERVER_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_DHCP_OFL), + WMI_IPA_OFFLOAD_ENABLE_DISABLE_CMDID = WMI_TLV_CMD(WMI_GRP_IPA), + WMI_MDNS_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_MDNS_OFL), + WMI_MDNS_SET_FQDN_CMDID, + WMI_MDNS_SET_RESPONSE_CMDID, + WMI_MDNS_GET_STATS_CMDID, + WMI_SAP_OFL_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_SAP_OFL), + WMI_SAP_SET_BLACKLIST_PARAM_CMDID, + WMI_OCB_SET_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_OCB), + WMI_OCB_SET_UTC_TIME_CMDID, + WMI_OCB_START_TIMING_ADVERT_CMDID, + WMI_OCB_STOP_TIMING_ADVERT_CMDID, + WMI_OCB_GET_TSF_TIMER_CMDID, + WMI_DCC_GET_STATS_CMDID, + WMI_DCC_CLEAR_STATS_CMDID, + WMI_DCC_UPDATE_NDL_CMDID, + WMI_SOC_SET_PCL_CMDID = WMI_TLV_CMD(WMI_GRP_SOC), + WMI_SOC_SET_HW_MODE_CMDID, + WMI_SOC_SET_DUAL_MAC_CONFIG_CMDID, + WMI_SOC_SET_ANTENNA_MODE_CMDID, + WMI_PACKET_FILTER_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_PKT_FILTER), + WMI_PACKET_FILTER_ENABLE_CMDID, + WMI_MAWC_SENSOR_REPORT_IND_CMDID = WMI_TLV_CMD(WMI_GRP_MAWC), + WMI_PMF_OFFLOAD_SET_SA_QUERY_CMDID = WMI_TLV_CMD(WMI_GRP_PMF_OFFLOAD), + WMI_BPF_GET_CAPABILITY_CMDID = WMI_TLV_CMD(WMI_GRP_BPF_OFFLOAD), + WMI_BPF_GET_VDEV_STATS_CMDID, + WMI_BPF_SET_VDEV_INSTRUCTIONS_CMDID, + WMI_BPF_DEL_VDEV_INSTRUCTIONS_CMDID, + WMI_BPF_SET_VDEV_ACTIVE_MODE_CMDID, + WMI_MNT_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_MONITOR), + WMI_SET_CURRENT_COUNTRY_CMDID = WMI_TLV_CMD(WMI_GRP_REGULATORY), + WMI_11D_SCAN_START_CMDID, + WMI_11D_SCAN_STOP_CMDID, + WMI_SET_INIT_COUNTRY_CMDID, + WMI_NDI_GET_CAP_REQ_CMDID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE), + WMI_NDP_INITIATOR_REQ_CMDID, + WMI_NDP_RESPONDER_REQ_CMDID, + WMI_NDP_END_REQ_CMDID, + WMI_HW_DATA_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_HW_DATA_FILTER), + WMI_TWT_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_TWT), + WMI_TWT_DISABLE_CMDID, + WMI_TWT_ADD_DIALOG_CMDID, + WMI_TWT_DEL_DIALOG_CMDID, + WMI_TWT_PAUSE_DIALOG_CMDID, + WMI_TWT_RESUME_DIALOG_CMDID, + WMI_PDEV_OBSS_PD_SPATIAL_REUSE_CMDID = + WMI_TLV_CMD(WMI_GRP_SPATIAL_REUSE), + WMI_PDEV_OBSS_PD_SPATIAL_REUSE_SET_DEF_OBSS_THRESH_CMDID, +}; + +enum wmi_tlv_event_id { + WMI_SERVICE_READY_EVENTID = 0x1, + WMI_READY_EVENTID, + WMI_SERVICE_AVAILABLE_EVENTID, + WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN), + WMI_PDEV_TPC_CONFIG_EVENTID = WMI_TLV_CMD(WMI_GRP_PDEV), + WMI_CHAN_INFO_EVENTID, + WMI_PHYERR_EVENTID, + WMI_PDEV_DUMP_EVENTID, + WMI_TX_PAUSE_EVENTID, + WMI_DFS_RADAR_EVENTID, + WMI_PDEV_L1SS_TRACK_EVENTID, + WMI_PDEV_TEMPERATURE_EVENTID, + WMI_SERVICE_READY_EXT_EVENTID, + WMI_PDEV_FIPS_EVENTID, + WMI_PDEV_CHANNEL_HOPPING_EVENTID, + WMI_PDEV_ANI_CCK_LEVEL_EVENTID, + WMI_PDEV_ANI_OFDM_LEVEL_EVENTID, + WMI_PDEV_TPC_EVENTID, + WMI_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENTID, + WMI_PDEV_SET_HW_MODE_RESP_EVENTID, + WMI_PDEV_HW_MODE_TRANSITION_EVENTID, + WMI_PDEV_SET_MAC_CONFIG_RESP_EVENTID, + WMI_PDEV_ANTDIV_STATUS_EVENTID, + WMI_PDEV_CHIP_POWER_STATS_EVENTID, + WMI_PDEV_CHIP_POWER_SAVE_FAILURE_DETECTED_EVENTID, + WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID, + WMI_PDEV_CHECK_CAL_VERSION_EVENTID, + WMI_PDEV_DIV_RSSI_ANTID_EVENTID, + WMI_PDEV_BSS_CHAN_INFO_EVENTID, + WMI_PDEV_UPDATE_CTLTABLE_EVENTID, + WMI_PDEV_DMA_RING_CFG_RSP_EVENTID, + WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID, + WMI_PDEV_CTL_FAILSAFE_CHECK_EVENTID, + WMI_VDEV_START_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_VDEV), + WMI_VDEV_STOPPED_EVENTID, + WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID, + WMI_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID, + WMI_VDEV_TSF_REPORT_EVENTID, + WMI_VDEV_DELETE_RESP_EVENTID, + WMI_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENTID, + WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID, + WMI_PEER_STA_KICKOUT_EVENTID = WMI_TLV_CMD(WMI_GRP_PEER), + WMI_PEER_INFO_EVENTID, + WMI_PEER_TX_FAIL_CNT_THR_EVENTID, + WMI_PEER_ESTIMATED_LINKSPEED_EVENTID, + WMI_PEER_STATE_EVENTID, + WMI_PEER_ASSOC_CONF_EVENTID, + WMI_PEER_DELETE_RESP_EVENTID, + WMI_PEER_RATECODE_LIST_EVENTID, + WMI_WDS_PEER_EVENTID, + WMI_PEER_STA_PS_STATECHG_EVENTID, + WMI_PEER_ANTDIV_INFO_EVENTID, + WMI_PEER_RESERVED0_EVENTID, + WMI_PEER_RESERVED1_EVENTID, + WMI_PEER_RESERVED2_EVENTID, + WMI_PEER_RESERVED3_EVENTID, + WMI_PEER_RESERVED4_EVENTID, + WMI_PEER_RESERVED5_EVENTID, + WMI_PEER_RESERVED6_EVENTID, + WMI_PEER_RESERVED7_EVENTID, + WMI_PEER_RESERVED8_EVENTID, + WMI_PEER_RESERVED9_EVENTID, + WMI_PEER_RESERVED10_EVENTID, + WMI_PEER_OPER_MODE_CHANGE_EVENTID, + WMI_MGMT_RX_EVENTID = WMI_TLV_CMD(WMI_GRP_MGMT), + WMI_HOST_SWBA_EVENTID, + WMI_TBTTOFFSET_UPDATE_EVENTID, + WMI_OFFLOAD_BCN_TX_STATUS_EVENTID, + WMI_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID, + WMI_MGMT_TX_COMPLETION_EVENTID, + WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID, + WMI_TBTTOFFSET_EXT_UPDATE_EVENTID, + WMI_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_CMD(WMI_GRP_BA_NEG), + WMI_TX_ADDBA_COMPLETE_EVENTID, + WMI_BA_RSP_SSN_EVENTID, + WMI_AGGR_STATE_TRIG_EVENTID, + WMI_ROAM_EVENTID = WMI_TLV_CMD(WMI_GRP_ROAM), + WMI_PROFILE_MATCH, + WMI_ROAM_SYNCH_EVENTID, + WMI_P2P_DISC_EVENTID = WMI_TLV_CMD(WMI_GRP_P2P), + WMI_P2P_NOA_EVENTID, + WMI_P2P_LISTEN_OFFLOAD_STOPPED_EVENTID, + WMI_AP_PS_EGAP_INFO_EVENTID = WMI_TLV_CMD(WMI_GRP_AP_PS), + WMI_PDEV_RESUME_EVENTID = WMI_TLV_CMD(WMI_GRP_SUSPEND), + WMI_WOW_WAKEUP_HOST_EVENTID = WMI_TLV_CMD(WMI_GRP_WOW), + WMI_D0_WOW_DISABLE_ACK_EVENTID, + WMI_WOW_INITIAL_WAKEUP_EVENTID, + WMI_RTT_MEASUREMENT_REPORT_EVENTID = WMI_TLV_CMD(WMI_GRP_RTT), + WMI_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_RTT_ERROR_REPORT_EVENTID, + WMI_STATS_EXT_EVENTID = WMI_TLV_CMD(WMI_GRP_STATS), + WMI_IFACE_LINK_STATS_EVENTID, + WMI_PEER_LINK_STATS_EVENTID, + WMI_RADIO_LINK_STATS_EVENTID, + WMI_UPDATE_FW_MEM_DUMP_EVENTID, + WMI_DIAG_EVENT_LOG_SUPPORTED_EVENTID, + WMI_INST_RSSI_STATS_EVENTID, + WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID, + WMI_REPORT_STATS_EVENTID, + WMI_UPDATE_RCPI_EVENTID, + WMI_PEER_STATS_INFO_EVENTID, + WMI_RADIO_CHAN_STATS_EVENTID, + WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), + WMI_NLO_SCAN_COMPLETE_EVENTID, + WMI_APFIND_EVENTID, + WMI_PASSPOINT_MATCH_EVENTID, + WMI_GTK_OFFLOAD_STATUS_EVENTID = WMI_TLV_CMD(WMI_GRP_GTK_OFL), + WMI_GTK_REKEY_FAIL_EVENTID, + WMI_CSA_HANDLING_EVENTID = WMI_TLV_CMD(WMI_GRP_CSA_OFL), + WMI_CHATTER_PC_QUERY_EVENTID = WMI_TLV_CMD(WMI_GRP_CHATTER), + WMI_PDEV_DFS_RADAR_DETECTION_EVENTID = WMI_TLV_CMD(WMI_GRP_DFS), + WMI_VDEV_DFS_CAC_COMPLETE_EVENTID, + WMI_VDEV_ADFS_OCAC_COMPLETE_EVENTID, + WMI_ECHO_EVENTID = WMI_TLV_CMD(WMI_GRP_MISC), + WMI_PDEV_UTF_EVENTID, + WMI_DEBUG_MESG_EVENTID, + WMI_UPDATE_STATS_EVENTID, + WMI_DEBUG_PRINT_EVENTID, + WMI_DCS_INTERFERENCE_EVENTID, + WMI_PDEV_QVIT_EVENTID, + WMI_WLAN_PROFILE_DATA_EVENTID, + WMI_PDEV_FTM_INTG_EVENTID, + WMI_WLAN_FREQ_AVOID_EVENTID, + WMI_VDEV_GET_KEEPALIVE_EVENTID, + WMI_THERMAL_MGMT_EVENTID, + WMI_DIAG_DATA_CONTAINER_EVENTID, + WMI_HOST_AUTO_SHUTDOWN_EVENTID, + WMI_UPDATE_WHAL_MIB_STATS_EVENTID, + WMI_UPDATE_VDEV_RATE_STATS_EVENTID, + WMI_DIAG_EVENTID, + WMI_OCB_SET_SCHED_EVENTID, + WMI_DEBUG_MESG_FLUSH_COMPLETE_EVENTID, + WMI_RSSI_BREACH_EVENTID, + WMI_TRANSFER_DATA_TO_FLASH_COMPLETE_EVENTID, + WMI_PDEV_UTF_SCPC_EVENTID, + WMI_READ_DATA_FROM_FLASH_EVENTID, + WMI_REPORT_RX_AGGR_FAILURE_EVENTID, + WMI_PKGID_EVENTID, + WMI_GPIO_INPUT_EVENTID = WMI_TLV_CMD(WMI_GRP_GPIO), + WMI_UPLOADH_EVENTID, + WMI_CAPTUREH_EVENTID, + WMI_RFKILL_STATE_CHANGE_EVENTID, + WMI_TDLS_PEER_EVENTID = WMI_TLV_CMD(WMI_GRP_TDLS), + WMI_STA_SMPS_FORCE_MODE_COMPL_EVENTID = WMI_TLV_CMD(WMI_GRP_STA_SMPS), + WMI_BATCH_SCAN_ENABLED_EVENTID = WMI_TLV_CMD(WMI_GRP_LOCATION_SCAN), + WMI_BATCH_SCAN_RESULT_EVENTID, + WMI_OEM_CAPABILITY_EVENTID = WMI_TLV_CMD(WMI_GRP_OEM), + WMI_OEM_MEASUREMENT_REPORT_EVENTID, + WMI_OEM_ERROR_REPORT_EVENTID, + WMI_OEM_RESPONSE_EVENTID, + WMI_NAN_EVENTID = WMI_TLV_CMD(WMI_GRP_NAN), + WMI_NAN_DISC_IFACE_CREATED_EVENTID, + WMI_NAN_DISC_IFACE_DELETED_EVENTID, + WMI_NAN_STARTED_CLUSTER_EVENTID, + WMI_NAN_JOINED_CLUSTER_EVENTID, + WMI_COEX_REPORT_ANTENNA_ISOLATION_EVENTID = WMI_TLV_CMD(WMI_GRP_COEX), + WMI_LPI_RESULT_EVENTID = WMI_TLV_CMD(WMI_GRP_LPI), + WMI_LPI_STATUS_EVENTID, + WMI_LPI_HANDOFF_EVENTID, + WMI_EXTSCAN_START_STOP_EVENTID = WMI_TLV_CMD(WMI_GRP_EXTSCAN), + WMI_EXTSCAN_OPERATION_EVENTID, + WMI_EXTSCAN_TABLE_USAGE_EVENTID, + WMI_EXTSCAN_CACHED_RESULTS_EVENTID, + WMI_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID, + WMI_EXTSCAN_HOTLIST_MATCH_EVENTID, + WMI_EXTSCAN_CAPABILITIES_EVENTID, + WMI_EXTSCAN_HOTLIST_SSID_MATCH_EVENTID, + WMI_MDNS_STATS_EVENTID = WMI_TLV_CMD(WMI_GRP_MDNS_OFL), + WMI_SAP_OFL_ADD_STA_EVENTID = WMI_TLV_CMD(WMI_GRP_SAP_OFL), + WMI_SAP_OFL_DEL_STA_EVENTID, + WMI_OCB_SET_CONFIG_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_OCB), + WMI_OCB_GET_TSF_TIMER_RESP_EVENTID, + WMI_DCC_GET_STATS_RESP_EVENTID, + WMI_DCC_UPDATE_NDL_RESP_EVENTID, + WMI_DCC_STATS_EVENTID, + WMI_SOC_SET_HW_MODE_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_SOC), + WMI_SOC_HW_MODE_TRANSITION_EVENTID, + WMI_SOC_SET_DUAL_MAC_CONFIG_RESP_EVENTID, + WMI_MAWC_ENABLE_SENSOR_EVENTID = WMI_TLV_CMD(WMI_GRP_MAWC), + WMI_BPF_CAPABILIY_INFO_EVENTID = WMI_TLV_CMD(WMI_GRP_BPF_OFFLOAD), + WMI_BPF_VDEV_STATS_INFO_EVENTID, + WMI_RMC_NEW_LEADER_EVENTID = WMI_TLV_CMD(WMI_GRP_RMC), + WMI_REG_CHAN_LIST_CC_EVENTID = WMI_TLV_CMD(WMI_GRP_REGULATORY), + WMI_11D_NEW_COUNTRY_EVENTID, + WMI_NDI_CAP_RSP_EVENTID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE), + WMI_NDP_INITIATOR_RSP_EVENTID, + WMI_NDP_RESPONDER_RSP_EVENTID, + WMI_NDP_END_RSP_EVENTID, + WMI_NDP_INDICATION_EVENTID, + WMI_NDP_CONFIRM_EVENTID, + WMI_NDP_END_INDICATION_EVENTID, + + WMI_TWT_ENABLE_EVENTID = WMI_TLV_CMD(WMI_GRP_TWT), + WMI_TWT_DISABLE_EVENTID, + WMI_TWT_ADD_DIALOG_EVENTID, + WMI_TWT_DEL_DIALOG_EVENTID, + WMI_TWT_PAUSE_DIALOG_EVENTID, + WMI_TWT_RESUME_DIALOG_EVENTID, +}; + +enum wmi_tlv_pdev_param { + WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + WMI_PDEV_PARAM_RX_CHAIN_MASK, + WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + WMI_PDEV_PARAM_TXPOWER_SCALE, + WMI_PDEV_PARAM_BEACON_GEN_MODE, + WMI_PDEV_PARAM_BEACON_TX_MODE, + WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + WMI_PDEV_PARAM_PROTECTION_MODE, + WMI_PDEV_PARAM_DYNAMIC_BW, + WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + WMI_PDEV_PARAM_AGG_SW_RETRY_TH, + WMI_PDEV_PARAM_STA_KICKOUT_TH, + WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING, + WMI_PDEV_PARAM_LTR_ENABLE, + WMI_PDEV_PARAM_LTR_AC_LATENCY_BE, + WMI_PDEV_PARAM_LTR_AC_LATENCY_BK, + WMI_PDEV_PARAM_LTR_AC_LATENCY_VI, + WMI_PDEV_PARAM_LTR_AC_LATENCY_VO, + WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + WMI_PDEV_PARAM_LTR_RX_OVERRIDE, + WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + WMI_PDEV_PARAM_L1SS_ENABLE, + WMI_PDEV_PARAM_DSLEEP_ENABLE, + WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + WMI_PDEV_PARAM_PCIELP_TXBUF_WATERMARK, + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_PMF_QOS, + WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + WMI_PDEV_PARAM_DCS, + WMI_PDEV_PARAM_ANI_ENABLE, + WMI_PDEV_PARAM_ANI_POLL_PERIOD, + WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + WMI_PDEV_PARAM_ANI_CCK_LEVEL, + WMI_PDEV_PARAM_DYNTXCHAIN, + WMI_PDEV_PARAM_PROXY_STA, + WMI_PDEV_PARAM_IDLE_PS_CONFIG, + WMI_PDEV_PARAM_POWER_GATING_SLEEP, + WMI_PDEV_PARAM_RFKILL_ENABLE, + WMI_PDEV_PARAM_BURST_DUR, + WMI_PDEV_PARAM_BURST_ENABLE, + WMI_PDEV_PARAM_HW_RFKILL_CONFIG, + WMI_PDEV_PARAM_LOW_POWER_RF_ENABLE, + WMI_PDEV_PARAM_L1SS_TRACK, + WMI_PDEV_PARAM_HYST_EN, + WMI_PDEV_PARAM_POWER_COLLAPSE_ENABLE, + WMI_PDEV_PARAM_LED_SYS_STATE, + WMI_PDEV_PARAM_LED_ENABLE, + WMI_PDEV_PARAM_AUDIO_OVER_WLAN_LATENCY, + WMI_PDEV_PARAM_AUDIO_OVER_WLAN_ENABLE, + WMI_PDEV_PARAM_WHAL_MIB_STATS_UPDATE_ENABLE, + WMI_PDEV_PARAM_VDEV_RATE_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_CTS_CBW, + WMI_PDEV_PARAM_WNTS_CONFIG, + WMI_PDEV_PARAM_ADAPTIVE_EARLY_RX_ENABLE, + WMI_PDEV_PARAM_ADAPTIVE_EARLY_RX_MIN_SLEEP_SLOP, + WMI_PDEV_PARAM_ADAPTIVE_EARLY_RX_INC_DEC_STEP, + WMI_PDEV_PARAM_EARLY_RX_FIX_SLEEP_SLOP, + WMI_PDEV_PARAM_BMISS_BASED_ADAPTIVE_BTO_ENABLE, + WMI_PDEV_PARAM_BMISS_BTO_MIN_BCN_TIMEOUT, + WMI_PDEV_PARAM_BMISS_BTO_INC_DEC_STEP, + WMI_PDEV_PARAM_BTO_FIX_BCN_TIMEOUT, + WMI_PDEV_PARAM_CE_BASED_ADAPTIVE_BTO_ENABLE, + WMI_PDEV_PARAM_CE_BTO_COMBO_CE_VALUE, + WMI_PDEV_PARAM_TX_CHAIN_MASK_2G, + WMI_PDEV_PARAM_RX_CHAIN_MASK_2G, + WMI_PDEV_PARAM_TX_CHAIN_MASK_5G, + WMI_PDEV_PARAM_RX_CHAIN_MASK_5G, + WMI_PDEV_PARAM_TX_CHAIN_MASK_CCK, + WMI_PDEV_PARAM_TX_CHAIN_MASK_1SS, + WMI_PDEV_PARAM_CTS2SELF_FOR_P2P_GO_CONFIG, + WMI_PDEV_PARAM_TXPOWER_DECR_DB, + WMI_PDEV_PARAM_AGGR_BURST, + WMI_PDEV_PARAM_RX_DECAP_MODE, + WMI_PDEV_PARAM_FAST_CHANNEL_RESET, + WMI_PDEV_PARAM_SMART_ANTENNA_DEFAULT_ANTENNA, + WMI_PDEV_PARAM_ANTENNA_GAIN, + WMI_PDEV_PARAM_RX_FILTER, + WMI_PDEV_SET_MCAST_TO_UCAST_TID, + WMI_PDEV_PARAM_PROXY_STA_MODE, + WMI_PDEV_PARAM_SET_MCAST2UCAST_MODE, + WMI_PDEV_PARAM_SET_MCAST2UCAST_BUFFER, + WMI_PDEV_PARAM_REMOVE_MCAST2UCAST_BUFFER, + WMI_PDEV_PEER_STA_PS_STATECHG_ENABLE, + WMI_PDEV_PARAM_IGMPMLD_AC_OVERRIDE, + WMI_PDEV_PARAM_BLOCK_INTERBSS, + WMI_PDEV_PARAM_SET_DISABLE_RESET_CMDID, + WMI_PDEV_PARAM_SET_MSDU_TTL_CMDID, + WMI_PDEV_PARAM_SET_PPDU_DURATION_CMDID, + WMI_PDEV_PARAM_TXBF_SOUND_PERIOD_CMDID, + WMI_PDEV_PARAM_SET_PROMISC_MODE_CMDID, + WMI_PDEV_PARAM_SET_BURST_MODE_CMDID, + WMI_PDEV_PARAM_EN_STATS, + WMI_PDEV_PARAM_MU_GROUP_POLICY, + WMI_PDEV_PARAM_NOISE_DETECTION, + WMI_PDEV_PARAM_NOISE_THRESHOLD, + WMI_PDEV_PARAM_DPD_ENABLE, + WMI_PDEV_PARAM_SET_MCAST_BCAST_ECHO, + WMI_PDEV_PARAM_ATF_STRICT_SCH, + WMI_PDEV_PARAM_ATF_SCHED_DURATION, + WMI_PDEV_PARAM_ANT_PLZN, + WMI_PDEV_PARAM_MGMT_RETRY_LIMIT, + WMI_PDEV_PARAM_SENSITIVITY_LEVEL, + WMI_PDEV_PARAM_SIGNED_TXPOWER_2G, + WMI_PDEV_PARAM_SIGNED_TXPOWER_5G, + WMI_PDEV_PARAM_ENABLE_PER_TID_AMSDU, + WMI_PDEV_PARAM_ENABLE_PER_TID_AMPDU, + WMI_PDEV_PARAM_CCA_THRESHOLD, + WMI_PDEV_PARAM_RTS_FIXED_RATE, + WMI_PDEV_PARAM_PDEV_RESET, + WMI_PDEV_PARAM_WAPI_MBSSID_OFFSET, + WMI_PDEV_PARAM_ARP_DBG_SRCADDR, + WMI_PDEV_PARAM_ARP_DBG_DSTADDR, + WMI_PDEV_PARAM_ATF_OBSS_NOISE_SCH, + WMI_PDEV_PARAM_ATF_OBSS_NOISE_SCALING_FACTOR, + WMI_PDEV_PARAM_CUST_TXPOWER_SCALE, + WMI_PDEV_PARAM_ATF_DYNAMIC_ENABLE, + WMI_PDEV_PARAM_CTRL_RETRY_LIMIT, + WMI_PDEV_PARAM_PROPAGATION_DELAY, + WMI_PDEV_PARAM_ENA_ANT_DIV, + WMI_PDEV_PARAM_FORCE_CHAIN_ANT, + WMI_PDEV_PARAM_ANT_DIV_SELFTEST, + WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL, + WMI_PDEV_PARAM_STATS_OBSERVATION_PERIOD, + WMI_PDEV_PARAM_TX_PPDU_DELAY_BIN_SIZE_MS, + WMI_PDEV_PARAM_TX_PPDU_DELAY_ARRAY_LEN, + WMI_PDEV_PARAM_TX_MPDU_AGGR_ARRAY_LEN, + WMI_PDEV_PARAM_RX_MPDU_AGGR_ARRAY_LEN, + WMI_PDEV_PARAM_TX_SCH_DELAY, + WMI_PDEV_PARAM_ENABLE_RTS_SIFS_BURSTING, + WMI_PDEV_PARAM_MAX_MPDUS_IN_AMPDU, + WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE, + WMI_PDEV_PARAM_FAST_PWR_TRANSITION, + WMI_PDEV_PARAM_RADIO_CHAN_STATS_ENABLE, + WMI_PDEV_PARAM_RADIO_DIAGNOSIS_ENABLE, + WMI_PDEV_PARAM_MESH_MCAST_ENABLE, +}; + +enum wmi_tlv_vdev_param { + WMI_VDEV_PARAM_RTS_THRESHOLD = 0x1, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + WMI_VDEV_PARAM_BEACON_INTERVAL, + WMI_VDEV_PARAM_LISTEN_INTERVAL, + WMI_VDEV_PARAM_MULTICAST_RATE, + WMI_VDEV_PARAM_MGMT_TX_RATE, + WMI_VDEV_PARAM_SLOT_TIME, + WMI_VDEV_PARAM_PREAMBLE, + WMI_VDEV_PARAM_SWBA_TIME, + WMI_VDEV_STATS_UPDATE_PERIOD, + WMI_VDEV_PWRSAVE_AGEOUT_TIME, + WMI_VDEV_HOST_SWBA_INTERVAL, + WMI_VDEV_PARAM_DTIM_PERIOD, + WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + WMI_VDEV_PARAM_WDS, + WMI_VDEV_PARAM_ATIM_WINDOW, + WMI_VDEV_PARAM_BMISS_COUNT_MAX, + WMI_VDEV_PARAM_BMISS_FIRST_BCNT, + WMI_VDEV_PARAM_BMISS_FINAL_BCNT, + WMI_VDEV_PARAM_FEATURE_WMM, + WMI_VDEV_PARAM_CHWIDTH, + WMI_VDEV_PARAM_CHEXTOFFSET, + WMI_VDEV_PARAM_DISABLE_HTPROTECTION, + WMI_VDEV_PARAM_STA_QUICKKICKOUT, + WMI_VDEV_PARAM_MGMT_RATE, + WMI_VDEV_PARAM_PROTECTION_MODE, + WMI_VDEV_PARAM_FIXED_RATE, + WMI_VDEV_PARAM_SGI, + WMI_VDEV_PARAM_LDPC, + WMI_VDEV_PARAM_TX_STBC, + WMI_VDEV_PARAM_RX_STBC, + WMI_VDEV_PARAM_INTRA_BSS_FWD, + WMI_VDEV_PARAM_DEF_KEYID, + WMI_VDEV_PARAM_NSS, + WMI_VDEV_PARAM_BCAST_DATA_RATE, + WMI_VDEV_PARAM_MCAST_DATA_RATE, + WMI_VDEV_PARAM_MCAST_INDICATE, + WMI_VDEV_PARAM_DHCP_INDICATE, + WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + WMI_VDEV_PARAM_TXBF, + WMI_VDEV_PARAM_PACKET_POWERSAVE, + WMI_VDEV_PARAM_DROP_UNENCRY, + WMI_VDEV_PARAM_TX_ENCAP_TYPE, + WMI_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, + WMI_VDEV_PARAM_EARLY_RX_ADJUST_ENABLE, + WMI_VDEV_PARAM_EARLY_RX_TGT_BMISS_NUM, + WMI_VDEV_PARAM_EARLY_RX_BMISS_SAMPLE_CYCLE, + WMI_VDEV_PARAM_EARLY_RX_SLOP_STEP, + WMI_VDEV_PARAM_EARLY_RX_INIT_SLOP, + WMI_VDEV_PARAM_EARLY_RX_ADJUST_PAUSE, + WMI_VDEV_PARAM_TX_PWRLIMIT, + WMI_VDEV_PARAM_SNR_NUM_FOR_CAL, + WMI_VDEV_PARAM_ROAM_FW_OFFLOAD, + WMI_VDEV_PARAM_ENABLE_RMC, + WMI_VDEV_PARAM_IBSS_MAX_BCN_LOST_MS, + WMI_VDEV_PARAM_MAX_RATE, + WMI_VDEV_PARAM_EARLY_RX_DRIFT_SAMPLE, + WMI_VDEV_PARAM_SET_IBSS_TX_FAIL_CNT_THR, + WMI_VDEV_PARAM_EBT_RESYNC_TIMEOUT, + WMI_VDEV_PARAM_AGGR_TRIG_EVENT_ENABLE, + WMI_VDEV_PARAM_IS_IBSS_POWER_SAVE_ALLOWED, + WMI_VDEV_PARAM_IS_POWER_COLLAPSE_ALLOWED, + WMI_VDEV_PARAM_IS_AWAKE_ON_TXRX_ENABLED, + WMI_VDEV_PARAM_INACTIVITY_CNT, + WMI_VDEV_PARAM_TXSP_END_INACTIVITY_TIME_MS, + WMI_VDEV_PARAM_DTIM_POLICY, + WMI_VDEV_PARAM_IBSS_PS_WARMUP_TIME_SECS, + WMI_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE, + WMI_VDEV_PARAM_RX_LEAK_WINDOW, + WMI_VDEV_PARAM_STATS_AVG_FACTOR, + WMI_VDEV_PARAM_DISCONNECT_TH, + WMI_VDEV_PARAM_RTSCTS_RATE, + WMI_VDEV_PARAM_MCC_RTSCTS_PROTECTION_ENABLE, + WMI_VDEV_PARAM_MCC_BROADCAST_PROBE_ENABLE, + WMI_VDEV_PARAM_TXPOWER_SCALE, + WMI_VDEV_PARAM_TXPOWER_SCALE_DECR_DB, + WMI_VDEV_PARAM_MCAST2UCAST_SET, + WMI_VDEV_PARAM_RC_NUM_RETRIES, + WMI_VDEV_PARAM_CABQ_MAXDUR, + WMI_VDEV_PARAM_MFPTEST_SET, + WMI_VDEV_PARAM_RTS_FIXED_RATE, + WMI_VDEV_PARAM_VHT_SGIMASK, + WMI_VDEV_PARAM_VHT80_RATEMASK, + WMI_VDEV_PARAM_PROXY_STA, + WMI_VDEV_PARAM_VIRTUAL_CELL_MODE, + WMI_VDEV_PARAM_RX_DECAP_TYPE, + WMI_VDEV_PARAM_BW_NSS_RATEMASK, + WMI_VDEV_PARAM_SENSOR_AP, + WMI_VDEV_PARAM_BEACON_RATE, + WMI_VDEV_PARAM_DTIM_ENABLE_CTS, + WMI_VDEV_PARAM_STA_KICKOUT, + WMI_VDEV_PARAM_CAPABILITIES, + WMI_VDEV_PARAM_TSF_INCREMENT, + WMI_VDEV_PARAM_AMPDU_PER_AC, + WMI_VDEV_PARAM_RX_FILTER, + WMI_VDEV_PARAM_MGMT_TX_POWER, + WMI_VDEV_PARAM_NON_AGG_SW_RETRY_TH, + WMI_VDEV_PARAM_AGG_SW_RETRY_TH, + WMI_VDEV_PARAM_DISABLE_DYN_BW_RTS, + WMI_VDEV_PARAM_ATF_SSID_SCHED_POLICY, + WMI_VDEV_PARAM_HE_DCM, + WMI_VDEV_PARAM_HE_RANGE_EXT, + WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE, + WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME, + WMI_VDEV_PARAM_BA_MODE = 0x7e, + WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE = 0x87, + WMI_VDEV_PARAM_PROTOTYPE = 0x8000, + WMI_VDEV_PARAM_BSS_COLOR, + WMI_VDEV_PARAM_SET_HEMU_MODE, + WMI_VDEV_PARAM_TX_OFDMA_CPLEN, +}; + +enum wmi_tlv_peer_flags { + WMI_TLV_PEER_AUTH = 0x00000001, + WMI_TLV_PEER_QOS = 0x00000002, + WMI_TLV_PEER_NEED_PTK_4_WAY = 0x00000004, + WMI_TLV_PEER_NEED_GTK_2_WAY = 0x00000010, + WMI_TLV_PEER_APSD = 0x00000800, + WMI_TLV_PEER_HT = 0x00001000, + WMI_TLV_PEER_40MHZ = 0x00002000, + WMI_TLV_PEER_STBC = 0x00008000, + WMI_TLV_PEER_LDPC = 0x00010000, + WMI_TLV_PEER_DYN_MIMOPS = 0x00020000, + WMI_TLV_PEER_STATIC_MIMOPS = 0x00040000, + WMI_TLV_PEER_SPATIAL_MUX = 0x00200000, + WMI_TLV_PEER_VHT = 0x02000000, + WMI_TLV_PEER_80MHZ = 0x04000000, + WMI_TLV_PEER_PMF = 0x08000000, + WMI_PEER_IS_P2P_CAPABLE = 0x20000000, + WMI_PEER_160MHZ = 0x40000000, + WMI_PEER_SAFEMODE_EN = 0x80000000, + +}; + +/** Enum list of TLV Tags for each parameter structure type. */ +enum wmi_tlv_tag { + WMI_TAG_LAST_RESERVED = 15, + WMI_TAG_FIRST_ARRAY_ENUM, + WMI_TAG_ARRAY_UINT32 = WMI_TAG_FIRST_ARRAY_ENUM, + WMI_TAG_ARRAY_BYTE, + WMI_TAG_ARRAY_STRUCT, + WMI_TAG_ARRAY_FIXED_STRUCT, + WMI_TAG_LAST_ARRAY_ENUM = 31, + WMI_TAG_SERVICE_READY_EVENT, + WMI_TAG_HAL_REG_CAPABILITIES, + WMI_TAG_WLAN_HOST_MEM_REQ, + WMI_TAG_READY_EVENT, + WMI_TAG_SCAN_EVENT, + WMI_TAG_PDEV_TPC_CONFIG_EVENT, + WMI_TAG_CHAN_INFO_EVENT, + WMI_TAG_COMB_PHYERR_RX_HDR, + WMI_TAG_VDEV_START_RESPONSE_EVENT, + WMI_TAG_VDEV_STOPPED_EVENT, + WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT, + WMI_TAG_PEER_STA_KICKOUT_EVENT, + WMI_TAG_MGMT_RX_HDR, + WMI_TAG_TBTT_OFFSET_EVENT, + WMI_TAG_TX_DELBA_COMPLETE_EVENT, + WMI_TAG_TX_ADDBA_COMPLETE_EVENT, + WMI_TAG_ROAM_EVENT, + WMI_TAG_WOW_EVENT_INFO, + WMI_TAG_WOW_EVENT_INFO_SECTION_BITMAP, + WMI_TAG_RTT_EVENT_HEADER, + WMI_TAG_RTT_ERROR_REPORT_EVENT, + WMI_TAG_RTT_MEAS_EVENT, + WMI_TAG_ECHO_EVENT, + WMI_TAG_FTM_INTG_EVENT, + WMI_TAG_VDEV_GET_KEEPALIVE_EVENT, + WMI_TAG_GPIO_INPUT_EVENT, + WMI_TAG_CSA_EVENT, + WMI_TAG_GTK_OFFLOAD_STATUS_EVENT, + WMI_TAG_IGTK_INFO, + WMI_TAG_DCS_INTERFERENCE_EVENT, + WMI_TAG_ATH_DCS_CW_INT, + WMI_TAG_WLAN_DCS_CW_INT = /* ALIAS */ + WMI_TAG_ATH_DCS_CW_INT, + WMI_TAG_ATH_DCS_WLAN_INT_STAT, + WMI_TAG_WLAN_DCS_IM_TGT_STATS_T = /* ALIAS */ + WMI_TAG_ATH_DCS_WLAN_INT_STAT, + WMI_TAG_WLAN_PROFILE_CTX_T, + WMI_TAG_WLAN_PROFILE_T, + WMI_TAG_PDEV_QVIT_EVENT, + WMI_TAG_HOST_SWBA_EVENT, + WMI_TAG_TIM_INFO, + WMI_TAG_P2P_NOA_INFO, + WMI_TAG_STATS_EVENT, + WMI_TAG_AVOID_FREQ_RANGES_EVENT, + WMI_TAG_AVOID_FREQ_RANGE_DESC, + WMI_TAG_GTK_REKEY_FAIL_EVENT, + WMI_TAG_INIT_CMD, + WMI_TAG_RESOURCE_CONFIG, + WMI_TAG_WLAN_HOST_MEMORY_CHUNK, + WMI_TAG_START_SCAN_CMD, + WMI_TAG_STOP_SCAN_CMD, + WMI_TAG_SCAN_CHAN_LIST_CMD, + WMI_TAG_CHANNEL, + WMI_TAG_PDEV_SET_REGDOMAIN_CMD, + WMI_TAG_PDEV_SET_PARAM_CMD, + WMI_TAG_PDEV_SET_WMM_PARAMS_CMD, + WMI_TAG_WMM_PARAMS, + WMI_TAG_PDEV_SET_QUIET_CMD, + WMI_TAG_VDEV_CREATE_CMD, + WMI_TAG_VDEV_DELETE_CMD, + WMI_TAG_VDEV_START_REQUEST_CMD, + WMI_TAG_P2P_NOA_DESCRIPTOR, + WMI_TAG_P2P_GO_SET_BEACON_IE, + WMI_TAG_GTK_OFFLOAD_CMD, + WMI_TAG_VDEV_UP_CMD, + WMI_TAG_VDEV_STOP_CMD, + WMI_TAG_VDEV_DOWN_CMD, + WMI_TAG_VDEV_SET_PARAM_CMD, + WMI_TAG_VDEV_INSTALL_KEY_CMD, + WMI_TAG_PEER_CREATE_CMD, + WMI_TAG_PEER_DELETE_CMD, + WMI_TAG_PEER_FLUSH_TIDS_CMD, + WMI_TAG_PEER_SET_PARAM_CMD, + WMI_TAG_PEER_ASSOC_COMPLETE_CMD, + WMI_TAG_VHT_RATE_SET, + WMI_TAG_BCN_TMPL_CMD, + WMI_TAG_PRB_TMPL_CMD, + WMI_TAG_BCN_PRB_INFO, + WMI_TAG_PEER_TID_ADDBA_CMD, + WMI_TAG_PEER_TID_DELBA_CMD, + WMI_TAG_STA_POWERSAVE_MODE_CMD, + WMI_TAG_STA_POWERSAVE_PARAM_CMD, + WMI_TAG_STA_DTIM_PS_METHOD_CMD, + WMI_TAG_ROAM_SCAN_MODE, + WMI_TAG_ROAM_SCAN_RSSI_THRESHOLD, + WMI_TAG_ROAM_SCAN_PERIOD, + WMI_TAG_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_TAG_PDEV_SUSPEND_CMD, + WMI_TAG_PDEV_RESUME_CMD, + WMI_TAG_ADD_BCN_FILTER_CMD, + WMI_TAG_RMV_BCN_FILTER_CMD, + WMI_TAG_WOW_ENABLE_CMD, + WMI_TAG_WOW_HOSTWAKEUP_FROM_SLEEP_CMD, + WMI_TAG_STA_UAPSD_AUTO_TRIG_CMD, + WMI_TAG_STA_UAPSD_AUTO_TRIG_PARAM, + WMI_TAG_SET_ARP_NS_OFFLOAD_CMD, + WMI_TAG_ARP_OFFLOAD_TUPLE, + WMI_TAG_NS_OFFLOAD_TUPLE, + WMI_TAG_FTM_INTG_CMD, + WMI_TAG_STA_KEEPALIVE_CMD, + WMI_TAG_STA_KEEPALVE_ARP_RESPONSE, + WMI_TAG_P2P_SET_VENDOR_IE_DATA_CMD, + WMI_TAG_AP_PS_PEER_CMD, + WMI_TAG_PEER_RATE_RETRY_SCHED_CMD, + WMI_TAG_WLAN_PROFILE_TRIGGER_CMD, + WMI_TAG_WLAN_PROFILE_SET_HIST_INTVL_CMD, + WMI_TAG_WLAN_PROFILE_GET_PROF_DATA_CMD, + WMI_TAG_WLAN_PROFILE_ENABLE_PROFILE_ID_CMD, + WMI_TAG_WOW_DEL_PATTERN_CMD, + WMI_TAG_WOW_ADD_DEL_EVT_CMD, + WMI_TAG_RTT_MEASREQ_HEAD, + WMI_TAG_RTT_MEASREQ_BODY, + WMI_TAG_RTT_TSF_CMD, + WMI_TAG_VDEV_SPECTRAL_CONFIGURE_CMD, + WMI_TAG_VDEV_SPECTRAL_ENABLE_CMD, + WMI_TAG_REQUEST_STATS_CMD, + WMI_TAG_NLO_CONFIG_CMD, + WMI_TAG_NLO_CONFIGURED_PARAMETERS, + WMI_TAG_CSA_OFFLOAD_ENABLE_CMD, + WMI_TAG_CSA_OFFLOAD_CHANSWITCH_CMD, + WMI_TAG_CHATTER_SET_MODE_CMD, + WMI_TAG_ECHO_CMD, + WMI_TAG_VDEV_SET_KEEPALIVE_CMD, + WMI_TAG_VDEV_GET_KEEPALIVE_CMD, + WMI_TAG_FORCE_FW_HANG_CMD, + WMI_TAG_GPIO_CONFIG_CMD, + WMI_TAG_GPIO_OUTPUT_CMD, + WMI_TAG_PEER_ADD_WDS_ENTRY_CMD, + WMI_TAG_PEER_REMOVE_WDS_ENTRY_CMD, + WMI_TAG_BCN_TX_HDR, + WMI_TAG_BCN_SEND_FROM_HOST_CMD, + WMI_TAG_MGMT_TX_HDR, + WMI_TAG_ADDBA_CLEAR_RESP_CMD, + WMI_TAG_ADDBA_SEND_CMD, + WMI_TAG_DELBA_SEND_CMD, + WMI_TAG_ADDBA_SETRESPONSE_CMD, + WMI_TAG_SEND_SINGLEAMSDU_CMD, + WMI_TAG_PDEV_PKTLOG_ENABLE_CMD, + WMI_TAG_PDEV_PKTLOG_DISABLE_CMD, + WMI_TAG_PDEV_SET_HT_IE_CMD, + WMI_TAG_PDEV_SET_VHT_IE_CMD, + WMI_TAG_PDEV_SET_DSCP_TID_MAP_CMD, + WMI_TAG_PDEV_GREEN_AP_PS_ENABLE_CMD, + WMI_TAG_PDEV_GET_TPC_CONFIG_CMD, + WMI_TAG_PDEV_SET_BASE_MACADDR_CMD, + WMI_TAG_PEER_MCAST_GROUP_CMD, + WMI_TAG_ROAM_AP_PROFILE, + WMI_TAG_AP_PROFILE, + WMI_TAG_SCAN_SCH_PRIORITY_TABLE_CMD, + WMI_TAG_PDEV_DFS_ENABLE_CMD, + WMI_TAG_PDEV_DFS_DISABLE_CMD, + WMI_TAG_WOW_ADD_PATTERN_CMD, + WMI_TAG_WOW_BITMAP_PATTERN_T, + WMI_TAG_WOW_IPV4_SYNC_PATTERN_T, + WMI_TAG_WOW_IPV6_SYNC_PATTERN_T, + WMI_TAG_WOW_MAGIC_PATTERN_CMD, + WMI_TAG_SCAN_UPDATE_REQUEST_CMD, + WMI_TAG_CHATTER_PKT_COALESCING_FILTER, + WMI_TAG_CHATTER_COALESCING_ADD_FILTER_CMD, + WMI_TAG_CHATTER_COALESCING_DELETE_FILTER_CMD, + WMI_TAG_CHATTER_COALESCING_QUERY_CMD, + WMI_TAG_TXBF_CMD, + WMI_TAG_DEBUG_LOG_CONFIG_CMD, + WMI_TAG_NLO_EVENT, + WMI_TAG_CHATTER_QUERY_REPLY_EVENT, + WMI_TAG_UPLOAD_H_HDR, + WMI_TAG_CAPTURE_H_EVENT_HDR, + WMI_TAG_VDEV_WNM_SLEEPMODE_CMD, + WMI_TAG_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMD, + WMI_TAG_VDEV_WMM_ADDTS_CMD, + WMI_TAG_VDEV_WMM_DELTS_CMD, + WMI_TAG_VDEV_SET_WMM_PARAMS_CMD, + WMI_TAG_TDLS_SET_STATE_CMD, + WMI_TAG_TDLS_PEER_UPDATE_CMD, + WMI_TAG_TDLS_PEER_EVENT, + WMI_TAG_TDLS_PEER_CAPABILITIES, + WMI_TAG_VDEV_MCC_SET_TBTT_MODE_CMD, + WMI_TAG_ROAM_CHAN_LIST, + WMI_TAG_VDEV_MCC_BCN_INTVL_CHANGE_EVENT, + WMI_TAG_RESMGR_ADAPTIVE_OCS_ENABLE_DISABLE_CMD, + WMI_TAG_RESMGR_SET_CHAN_TIME_QUOTA_CMD, + WMI_TAG_RESMGR_SET_CHAN_LATENCY_CMD, + WMI_TAG_BA_REQ_SSN_CMD, + WMI_TAG_BA_RSP_SSN_EVENT, + WMI_TAG_STA_SMPS_FORCE_MODE_CMD, + WMI_TAG_SET_MCASTBCAST_FILTER_CMD, + WMI_TAG_P2P_SET_OPPPS_CMD, + WMI_TAG_P2P_SET_NOA_CMD, + WMI_TAG_BA_REQ_SSN_CMD_SUB_STRUCT_PARAM, + WMI_TAG_BA_REQ_SSN_EVENT_SUB_STRUCT_PARAM, + WMI_TAG_STA_SMPS_PARAM_CMD, + WMI_TAG_VDEV_SET_GTX_PARAMS_CMD, + WMI_TAG_MCC_SCHED_TRAFFIC_STATS_CMD, + WMI_TAG_MCC_SCHED_STA_TRAFFIC_STATS, + WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT, + WMI_TAG_P2P_NOA_EVENT, + WMI_TAG_HB_SET_ENABLE_CMD, + WMI_TAG_HB_SET_TCP_PARAMS_CMD, + WMI_TAG_HB_SET_TCP_PKT_FILTER_CMD, + WMI_TAG_HB_SET_UDP_PARAMS_CMD, + WMI_TAG_HB_SET_UDP_PKT_FILTER_CMD, + WMI_TAG_HB_IND_EVENT, + WMI_TAG_TX_PAUSE_EVENT, + WMI_TAG_RFKILL_EVENT, + WMI_TAG_DFS_RADAR_EVENT, + WMI_TAG_DFS_PHYERR_FILTER_ENA_CMD, + WMI_TAG_DFS_PHYERR_FILTER_DIS_CMD, + WMI_TAG_BATCH_SCAN_RESULT_SCAN_LIST, + WMI_TAG_BATCH_SCAN_RESULT_NETWORK_INFO, + WMI_TAG_BATCH_SCAN_ENABLE_CMD, + WMI_TAG_BATCH_SCAN_DISABLE_CMD, + WMI_TAG_BATCH_SCAN_TRIGGER_RESULT_CMD, + WMI_TAG_BATCH_SCAN_ENABLED_EVENT, + WMI_TAG_BATCH_SCAN_RESULT_EVENT, + WMI_TAG_VDEV_PLMREQ_START_CMD, + WMI_TAG_VDEV_PLMREQ_STOP_CMD, + WMI_TAG_THERMAL_MGMT_CMD, + WMI_TAG_THERMAL_MGMT_EVENT, + WMI_TAG_PEER_INFO_REQ_CMD, + WMI_TAG_PEER_INFO_EVENT, + WMI_TAG_PEER_INFO, + WMI_TAG_PEER_TX_FAIL_CNT_THR_EVENT, + WMI_TAG_RMC_SET_MODE_CMD, + WMI_TAG_RMC_SET_ACTION_PERIOD_CMD, + WMI_TAG_RMC_CONFIG_CMD, + WMI_TAG_MHF_OFFLOAD_SET_MODE_CMD, + WMI_TAG_MHF_OFFLOAD_PLUMB_ROUTING_TABLE_CMD, + WMI_TAG_ADD_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TAG_DEL_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TAG_NAN_CMD_PARAM, + WMI_TAG_NAN_EVENT_HDR, + WMI_TAG_PDEV_L1SS_TRACK_EVENT, + WMI_TAG_DIAG_DATA_CONTAINER_EVENT, + WMI_TAG_MODEM_POWER_STATE_CMD_PARAM, + WMI_TAG_PEER_GET_ESTIMATED_LINKSPEED_CMD, + WMI_TAG_PEER_ESTIMATED_LINKSPEED_EVENT, + WMI_TAG_AGGR_STATE_TRIG_EVENT, + WMI_TAG_MHF_OFFLOAD_ROUTING_TABLE_ENTRY, + WMI_TAG_ROAM_SCAN_CMD, + WMI_TAG_REQ_STATS_EXT_CMD, + WMI_TAG_STATS_EXT_EVENT, + WMI_TAG_OBSS_SCAN_ENABLE_CMD, + WMI_TAG_OBSS_SCAN_DISABLE_CMD, + WMI_TAG_OFFLOAD_PRB_RSP_TX_STATUS_EVENT, + WMI_TAG_PDEV_SET_LED_CONFIG_CMD, + WMI_TAG_HOST_AUTO_SHUTDOWN_CFG_CMD, + WMI_TAG_HOST_AUTO_SHUTDOWN_EVENT, + WMI_TAG_UPDATE_WHAL_MIB_STATS_EVENT, + WMI_TAG_CHAN_AVOID_UPDATE_CMD_PARAM, + WMI_TAG_WOW_IOAC_PKT_PATTERN_T, + WMI_TAG_WOW_IOAC_TMR_PATTERN_T, + WMI_TAG_WOW_IOAC_ADD_KEEPALIVE_CMD, + WMI_TAG_WOW_IOAC_DEL_KEEPALIVE_CMD, + WMI_TAG_WOW_IOAC_KEEPALIVE_T, + WMI_TAG_WOW_IOAC_ADD_PATTERN_CMD, + WMI_TAG_WOW_IOAC_DEL_PATTERN_CMD, + WMI_TAG_START_LINK_STATS_CMD, + WMI_TAG_CLEAR_LINK_STATS_CMD, + WMI_TAG_REQUEST_LINK_STATS_CMD, + WMI_TAG_IFACE_LINK_STATS_EVENT, + WMI_TAG_RADIO_LINK_STATS_EVENT, + WMI_TAG_PEER_STATS_EVENT, + WMI_TAG_CHANNEL_STATS, + WMI_TAG_RADIO_LINK_STATS, + WMI_TAG_RATE_STATS, + WMI_TAG_PEER_LINK_STATS, + WMI_TAG_WMM_AC_STATS, + WMI_TAG_IFACE_LINK_STATS, + WMI_TAG_LPI_MGMT_SNOOPING_CONFIG_CMD, + WMI_TAG_LPI_START_SCAN_CMD, + WMI_TAG_LPI_STOP_SCAN_CMD, + WMI_TAG_LPI_RESULT_EVENT, + WMI_TAG_PEER_STATE_EVENT, + WMI_TAG_EXTSCAN_BUCKET_CMD, + WMI_TAG_EXTSCAN_BUCKET_CHANNEL_EVENT, + WMI_TAG_EXTSCAN_START_CMD, + WMI_TAG_EXTSCAN_STOP_CMD, + WMI_TAG_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMD, + WMI_TAG_EXTSCAN_WLAN_CHANGE_BSSID_PARAM_CMD, + WMI_TAG_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMD, + WMI_TAG_EXTSCAN_GET_CACHED_RESULTS_CMD, + WMI_TAG_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMD, + WMI_TAG_EXTSCAN_SET_CAPABILITIES_CMD, + WMI_TAG_EXTSCAN_GET_CAPABILITIES_CMD, + WMI_TAG_EXTSCAN_OPERATION_EVENT, + WMI_TAG_EXTSCAN_START_STOP_EVENT, + WMI_TAG_EXTSCAN_TABLE_USAGE_EVENT, + WMI_TAG_EXTSCAN_WLAN_DESCRIPTOR_EVENT, + WMI_TAG_EXTSCAN_RSSI_INFO_EVENT, + WMI_TAG_EXTSCAN_CACHED_RESULTS_EVENT, + WMI_TAG_EXTSCAN_WLAN_CHANGE_RESULTS_EVENT, + WMI_TAG_EXTSCAN_WLAN_CHANGE_RESULT_BSSID_EVENT, + WMI_TAG_EXTSCAN_HOTLIST_MATCH_EVENT, + WMI_TAG_EXTSCAN_CAPABILITIES_EVENT, + WMI_TAG_EXTSCAN_CACHE_CAPABILITIES_EVENT, + WMI_TAG_EXTSCAN_WLAN_CHANGE_MONITOR_CAPABILITIES_EVENT, + WMI_TAG_EXTSCAN_HOTLIST_MONITOR_CAPABILITIES_EVENT, + WMI_TAG_D0_WOW_ENABLE_DISABLE_CMD, + WMI_TAG_D0_WOW_DISABLE_ACK_EVENT, + WMI_TAG_UNIT_TEST_CMD, + WMI_TAG_ROAM_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_11I_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_11R_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_ESE_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_SYNCH_EVENT, + WMI_TAG_ROAM_SYNCH_COMPLETE, + WMI_TAG_EXTWOW_ENABLE_CMD, + WMI_TAG_EXTWOW_SET_APP_TYPE1_PARAMS_CMD, + WMI_TAG_EXTWOW_SET_APP_TYPE2_PARAMS_CMD, + WMI_TAG_LPI_STATUS_EVENT, + WMI_TAG_LPI_HANDOFF_EVENT, + WMI_TAG_VDEV_RATE_STATS_EVENT, + WMI_TAG_VDEV_RATE_HT_INFO, + WMI_TAG_RIC_REQUEST, + WMI_TAG_PDEV_GET_TEMPERATURE_CMD, + WMI_TAG_PDEV_TEMPERATURE_EVENT, + WMI_TAG_SET_DHCP_SERVER_OFFLOAD_CMD, + WMI_TAG_TPC_CHAINMASK_CONFIG_CMD, + WMI_TAG_RIC_TSPEC, + WMI_TAG_TPC_CHAINMASK_CONFIG, + WMI_TAG_IPA_OFFLOAD_ENABLE_DISABLE_CMD, + WMI_TAG_SCAN_PROB_REQ_OUI_CMD, + WMI_TAG_KEY_MATERIAL, + WMI_TAG_TDLS_SET_OFFCHAN_MODE_CMD, + WMI_TAG_SET_LED_FLASHING_CMD, + WMI_TAG_MDNS_OFFLOAD_CMD, + WMI_TAG_MDNS_SET_FQDN_CMD, + WMI_TAG_MDNS_SET_RESP_CMD, + WMI_TAG_MDNS_GET_STATS_CMD, + WMI_TAG_MDNS_STATS_EVENT, + WMI_TAG_ROAM_INVOKE_CMD, + WMI_TAG_PDEV_RESUME_EVENT, + WMI_TAG_PDEV_SET_ANTENNA_DIVERSITY_CMD, + WMI_TAG_SAP_OFL_ENABLE_CMD, + WMI_TAG_SAP_OFL_ADD_STA_EVENT, + WMI_TAG_SAP_OFL_DEL_STA_EVENT, + WMI_TAG_APFIND_CMD_PARAM, + WMI_TAG_APFIND_EVENT_HDR, + WMI_TAG_OCB_SET_SCHED_CMD, + WMI_TAG_OCB_SET_SCHED_EVENT, + WMI_TAG_OCB_SET_CONFIG_CMD, + WMI_TAG_OCB_SET_CONFIG_RESP_EVENT, + WMI_TAG_OCB_SET_UTC_TIME_CMD, + WMI_TAG_OCB_START_TIMING_ADVERT_CMD, + WMI_TAG_OCB_STOP_TIMING_ADVERT_CMD, + WMI_TAG_OCB_GET_TSF_TIMER_CMD, + WMI_TAG_OCB_GET_TSF_TIMER_RESP_EVENT, + WMI_TAG_DCC_GET_STATS_CMD, + WMI_TAG_DCC_CHANNEL_STATS_REQUEST, + WMI_TAG_DCC_GET_STATS_RESP_EVENT, + WMI_TAG_DCC_CLEAR_STATS_CMD, + WMI_TAG_DCC_UPDATE_NDL_CMD, + WMI_TAG_DCC_UPDATE_NDL_RESP_EVENT, + WMI_TAG_DCC_STATS_EVENT, + WMI_TAG_OCB_CHANNEL, + WMI_TAG_OCB_SCHEDULE_ELEMENT, + WMI_TAG_DCC_NDL_STATS_PER_CHANNEL, + WMI_TAG_DCC_NDL_CHAN, + WMI_TAG_QOS_PARAMETER, + WMI_TAG_DCC_NDL_ACTIVE_STATE_CONFIG, + WMI_TAG_ROAM_SCAN_EXTENDED_THRESHOLD_PARAM, + WMI_TAG_ROAM_FILTER, + WMI_TAG_PASSPOINT_CONFIG_CMD, + WMI_TAG_PASSPOINT_EVENT_HDR, + WMI_TAG_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMD, + WMI_TAG_EXTSCAN_HOTLIST_SSID_MATCH_EVENT, + WMI_TAG_VDEV_TSF_TSTAMP_ACTION_CMD, + WMI_TAG_VDEV_TSF_REPORT_EVENT, + WMI_TAG_GET_FW_MEM_DUMP, + WMI_TAG_UPDATE_FW_MEM_DUMP, + WMI_TAG_FW_MEM_DUMP_PARAMS, + WMI_TAG_DEBUG_MESG_FLUSH, + WMI_TAG_DEBUG_MESG_FLUSH_COMPLETE, + WMI_TAG_PEER_SET_RATE_REPORT_CONDITION, + WMI_TAG_ROAM_SUBNET_CHANGE_CONFIG, + WMI_TAG_VDEV_SET_IE_CMD, + WMI_TAG_RSSI_BREACH_MONITOR_CONFIG, + WMI_TAG_RSSI_BREACH_EVENT, + WMI_TAG_WOW_EVENT_INITIAL_WAKEUP, + WMI_TAG_SOC_SET_PCL_CMD, + WMI_TAG_SOC_SET_HW_MODE_CMD, + WMI_TAG_SOC_SET_HW_MODE_RESPONSE_EVENT, + WMI_TAG_SOC_HW_MODE_TRANSITION_EVENT, + WMI_TAG_VDEV_TXRX_STREAMS, + WMI_TAG_SOC_SET_HW_MODE_RESPONSE_VDEV_MAC_ENTRY, + WMI_TAG_SOC_SET_DUAL_MAC_CONFIG_CMD, + WMI_TAG_SOC_SET_DUAL_MAC_CONFIG_RESPONSE_EVENT, + WMI_TAG_WOW_IOAC_SOCK_PATTERN_T, + WMI_TAG_WOW_ENABLE_ICMPV6_NA_FLT_CMD, + WMI_TAG_DIAG_EVENT_LOG_CONFIG, + WMI_TAG_DIAG_EVENT_LOG_SUPPORTED_EVENT_FIXED_PARAMS, + WMI_TAG_PACKET_FILTER_CONFIG, + WMI_TAG_PACKET_FILTER_ENABLE, + WMI_TAG_SAP_SET_BLACKLIST_PARAM_CMD, + WMI_TAG_MGMT_TX_SEND_CMD, + WMI_TAG_MGMT_TX_COMPL_EVENT, + WMI_TAG_SOC_SET_ANTENNA_MODE_CMD, + WMI_TAG_WOW_UDP_SVC_OFLD_CMD, + WMI_TAG_LRO_INFO_CMD, + WMI_TAG_ROAM_EARLYSTOP_RSSI_THRES_PARAM, + WMI_TAG_SERVICE_READY_EXT_EVENT, + WMI_TAG_MAWC_SENSOR_REPORT_IND_CMD, + WMI_TAG_MAWC_ENABLE_SENSOR_EVENT, + WMI_TAG_ROAM_CONFIGURE_MAWC_CMD, + WMI_TAG_NLO_CONFIGURE_MAWC_CMD, + WMI_TAG_EXTSCAN_CONFIGURE_MAWC_CMD, + WMI_TAG_PEER_ASSOC_CONF_EVENT, + WMI_TAG_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMD, + WMI_TAG_AP_PS_EGAP_PARAM_CMD, + WMI_TAG_AP_PS_EGAP_INFO_EVENT, + WMI_TAG_PMF_OFFLOAD_SET_SA_QUERY_CMD, + WMI_TAG_TRANSFER_DATA_TO_FLASH_CMD, + WMI_TAG_TRANSFER_DATA_TO_FLASH_COMPLETE_EVENT, + WMI_TAG_SCPC_EVENT, + WMI_TAG_AP_PS_EGAP_INFO_CHAINMASK_LIST, + WMI_TAG_STA_SMPS_FORCE_MODE_COMPLETE_EVENT, + WMI_TAG_BPF_GET_CAPABILITY_CMD, + WMI_TAG_BPF_CAPABILITY_INFO_EVT, + WMI_TAG_BPF_GET_VDEV_STATS_CMD, + WMI_TAG_BPF_VDEV_STATS_INFO_EVT, + WMI_TAG_BPF_SET_VDEV_INSTRUCTIONS_CMD, + WMI_TAG_BPF_DEL_VDEV_INSTRUCTIONS_CMD, + WMI_TAG_VDEV_DELETE_RESP_EVENT, + WMI_TAG_PEER_DELETE_RESP_EVENT, + WMI_TAG_ROAM_DENSE_THRES_PARAM, + WMI_TAG_ENLO_CANDIDATE_SCORE_PARAM, + WMI_TAG_PEER_UPDATE_WDS_ENTRY_CMD, + WMI_TAG_VDEV_CONFIG_RATEMASK, + WMI_TAG_PDEV_FIPS_CMD, + WMI_TAG_PDEV_SMART_ANT_ENABLE_CMD, + WMI_TAG_PDEV_SMART_ANT_SET_RX_ANTENNA_CMD, + WMI_TAG_PEER_SMART_ANT_SET_TX_ANTENNA_CMD, + WMI_TAG_PEER_SMART_ANT_SET_TRAIN_ANTENNA_CMD, + WMI_TAG_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMD, + WMI_TAG_PDEV_SET_ANT_SWITCH_TBL_CMD, + WMI_TAG_PDEV_SET_CTL_TABLE_CMD, + WMI_TAG_PDEV_SET_MIMOGAIN_TABLE_CMD, + WMI_TAG_FWTEST_SET_PARAM_CMD, + WMI_TAG_PEER_ATF_REQUEST, + WMI_TAG_VDEV_ATF_REQUEST, + WMI_TAG_PDEV_GET_ANI_CCK_CONFIG_CMD, + WMI_TAG_PDEV_GET_ANI_OFDM_CONFIG_CMD, + WMI_TAG_INST_RSSI_STATS_RESP, + WMI_TAG_MED_UTIL_REPORT_EVENT, + WMI_TAG_PEER_STA_PS_STATECHANGE_EVENT, + WMI_TAG_WDS_ADDR_EVENT, + WMI_TAG_PEER_RATECODE_LIST_EVENT, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENT, + WMI_TAG_PDEV_TPC_EVENT, + WMI_TAG_ANI_OFDM_EVENT, + WMI_TAG_ANI_CCK_EVENT, + WMI_TAG_PDEV_CHANNEL_HOPPING_EVENT, + WMI_TAG_PDEV_FIPS_EVENT, + WMI_TAG_ATF_PEER_INFO, + WMI_TAG_PDEV_GET_TPC_CMD, + WMI_TAG_VDEV_FILTER_NRP_CONFIG_CMD, + WMI_TAG_QBOOST_CFG_CMD, + WMI_TAG_PDEV_SMART_ANT_GPIO_HANDLE, + WMI_TAG_PEER_SMART_ANT_SET_TX_ANTENNA_SERIES, + WMI_TAG_PEER_SMART_ANT_SET_TRAIN_ANTENNA_PARAM, + WMI_TAG_PDEV_SET_ANT_CTRL_CHAIN, + WMI_TAG_PEER_CCK_OFDM_RATE_INFO, + WMI_TAG_PEER_MCS_RATE_INFO, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_NFDBR, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_NFDBM, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_FREQNUM, + WMI_TAG_MU_REPORT_TOTAL_MU, + WMI_TAG_VDEV_SET_DSCP_TID_MAP_CMD, + WMI_TAG_ROAM_SET_MBO, + WMI_TAG_MIB_STATS_ENABLE_CMD, + WMI_TAG_NAN_DISC_IFACE_CREATED_EVENT, + WMI_TAG_NAN_DISC_IFACE_DELETED_EVENT, + WMI_TAG_NAN_STARTED_CLUSTER_EVENT, + WMI_TAG_NAN_JOINED_CLUSTER_EVENT, + WMI_TAG_NDI_GET_CAP_REQ, + WMI_TAG_NDP_INITIATOR_REQ, + WMI_TAG_NDP_RESPONDER_REQ, + WMI_TAG_NDP_END_REQ, + WMI_TAG_NDI_CAP_RSP_EVENT, + WMI_TAG_NDP_INITIATOR_RSP_EVENT, + WMI_TAG_NDP_RESPONDER_RSP_EVENT, + WMI_TAG_NDP_END_RSP_EVENT, + WMI_TAG_NDP_INDICATION_EVENT, + WMI_TAG_NDP_CONFIRM_EVENT, + WMI_TAG_NDP_END_INDICATION_EVENT, + WMI_TAG_VDEV_SET_QUIET_CMD, + WMI_TAG_PDEV_SET_PCL_CMD, + WMI_TAG_PDEV_SET_HW_MODE_CMD, + WMI_TAG_PDEV_SET_MAC_CONFIG_CMD, + WMI_TAG_PDEV_SET_ANTENNA_MODE_CMD, + WMI_TAG_PDEV_SET_HW_MODE_RESPONSE_EVENT, + WMI_TAG_PDEV_HW_MODE_TRANSITION_EVENT, + WMI_TAG_PDEV_SET_HW_MODE_RESPONSE_VDEV_MAC_ENTRY, + WMI_TAG_PDEV_SET_MAC_CONFIG_RESPONSE_EVENT, + WMI_TAG_COEX_CONFIG_CMD, + WMI_TAG_CONFIG_ENHANCED_MCAST_FILTER, + WMI_TAG_CHAN_AVOID_RPT_ALLOW_CMD, + WMI_TAG_SET_PERIODIC_CHANNEL_STATS_CONFIG, + WMI_TAG_VDEV_SET_CUSTOM_AGGR_SIZE_CMD, + WMI_TAG_PDEV_WAL_POWER_DEBUG_CMD, + WMI_TAG_MAC_PHY_CAPABILITIES, + WMI_TAG_HW_MODE_CAPABILITIES, + WMI_TAG_SOC_MAC_PHY_HW_MODE_CAPS, + WMI_TAG_HAL_REG_CAPABILITIES_EXT, + WMI_TAG_SOC_HAL_REG_CAPABILITIES, + WMI_TAG_VDEV_WISA_CMD, + WMI_TAG_TX_POWER_LEVEL_STATS_EVT, + WMI_TAG_SCAN_ADAPTIVE_DWELL_PARAMETERS_TLV, + WMI_TAG_SCAN_ADAPTIVE_DWELL_CONFIG, + WMI_TAG_WOW_SET_ACTION_WAKE_UP_CMD, + WMI_TAG_NDP_END_RSP_PER_NDI, + WMI_TAG_PEER_BWF_REQUEST, + WMI_TAG_BWF_PEER_INFO, + WMI_TAG_DBGLOG_TIME_STAMP_SYNC_CMD, + WMI_TAG_RMC_SET_LEADER_CMD, + WMI_TAG_RMC_MANUAL_LEADER_EVENT, + WMI_TAG_PER_CHAIN_RSSI_STATS, + WMI_TAG_RSSI_STATS, + WMI_TAG_P2P_LO_START_CMD, + WMI_TAG_P2P_LO_STOP_CMD, + WMI_TAG_P2P_LO_STOPPED_EVENT, + WMI_TAG_REORDER_QUEUE_SETUP_CMD, + WMI_TAG_REORDER_QUEUE_REMOVE_CMD, + WMI_TAG_SET_MULTIPLE_MCAST_FILTER_CMD, + WMI_TAG_MGMT_TX_COMPL_BUNDLE_EVENT, + WMI_TAG_READ_DATA_FROM_FLASH_CMD, + WMI_TAG_READ_DATA_FROM_FLASH_EVENT, + WMI_TAG_PDEV_SET_REORDER_TIMEOUT_VAL_CMD, + WMI_TAG_PEER_SET_RX_BLOCKSIZE_CMD, + WMI_TAG_PDEV_SET_WAKEUP_CONFIG_CMDID, + WMI_TAG_TLV_BUF_LEN_PARAM, + WMI_TAG_SERVICE_AVAILABLE_EVENT, + WMI_TAG_PEER_ANTDIV_INFO_REQ_CMD, + WMI_TAG_PEER_ANTDIV_INFO_EVENT, + WMI_TAG_PEER_ANTDIV_INFO, + WMI_TAG_PDEV_GET_ANTDIV_STATUS_CMD, + WMI_TAG_PDEV_ANTDIV_STATUS_EVENT, + WMI_TAG_MNT_FILTER_CMD, + WMI_TAG_GET_CHIP_POWER_STATS_CMD, + WMI_TAG_PDEV_CHIP_POWER_STATS_EVENT, + WMI_TAG_COEX_GET_ANTENNA_ISOLATION_CMD, + WMI_TAG_COEX_REPORT_ISOLATION_EVENT, + WMI_TAG_CHAN_CCA_STATS, + WMI_TAG_PEER_SIGNAL_STATS, + WMI_TAG_TX_STATS, + WMI_TAG_PEER_AC_TX_STATS, + WMI_TAG_RX_STATS, + WMI_TAG_PEER_AC_RX_STATS, + WMI_TAG_REPORT_STATS_EVENT, + WMI_TAG_CHAN_CCA_STATS_THRESH, + WMI_TAG_PEER_SIGNAL_STATS_THRESH, + WMI_TAG_TX_STATS_THRESH, + WMI_TAG_RX_STATS_THRESH, + WMI_TAG_PDEV_SET_STATS_THRESHOLD_CMD, + WMI_TAG_REQUEST_WLAN_STATS_CMD, + WMI_TAG_RX_AGGR_FAILURE_EVENT, + WMI_TAG_RX_AGGR_FAILURE_INFO, + WMI_TAG_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMD, + WMI_TAG_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENT, + WMI_TAG_PDEV_BAND_TO_MAC, + WMI_TAG_TBTT_OFFSET_INFO, + WMI_TAG_TBTT_OFFSET_EXT_EVENT, + WMI_TAG_SAR_LIMITS_CMD, + WMI_TAG_SAR_LIMIT_CMD_ROW, + WMI_TAG_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMD, + WMI_TAG_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMD, + WMI_TAG_VDEV_ADFS_CH_CFG_CMD, + WMI_TAG_VDEV_ADFS_OCAC_ABORT_CMD, + WMI_TAG_PDEV_DFS_RADAR_DETECTION_EVENT, + WMI_TAG_VDEV_ADFS_OCAC_COMPLETE_EVENT, + WMI_TAG_VDEV_DFS_CAC_COMPLETE_EVENT, + WMI_TAG_VENDOR_OUI, + WMI_TAG_REQUEST_RCPI_CMD, + WMI_TAG_UPDATE_RCPI_EVENT, + WMI_TAG_REQUEST_PEER_STATS_INFO_CMD, + WMI_TAG_PEER_STATS_INFO, + WMI_TAG_PEER_STATS_INFO_EVENT, + WMI_TAG_PKGID_EVENT, + WMI_TAG_CONNECTED_NLO_RSSI_PARAMS, + WMI_TAG_SET_CURRENT_COUNTRY_CMD, + WMI_TAG_REGULATORY_RULE_STRUCT, + WMI_TAG_REG_CHAN_LIST_CC_EVENT, + WMI_TAG_11D_SCAN_START_CMD, + WMI_TAG_11D_SCAN_STOP_CMD, + WMI_TAG_11D_NEW_COUNTRY_EVENT, + WMI_TAG_REQUEST_RADIO_CHAN_STATS_CMD, + WMI_TAG_RADIO_CHAN_STATS, + WMI_TAG_RADIO_CHAN_STATS_EVENT, + WMI_TAG_ROAM_PER_CONFIG, + WMI_TAG_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMD, + WMI_TAG_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENT, + WMI_TAG_BPF_SET_VDEV_ACTIVE_MODE_CMD, + WMI_TAG_HW_DATA_FILTER_CMD, + WMI_TAG_CONNECTED_NLO_BSS_BAND_RSSI_PREF, + WMI_TAG_PEER_OPER_MODE_CHANGE_EVENT, + WMI_TAG_CHIP_POWER_SAVE_FAILURE_DETECTED, + WMI_TAG_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMD, + WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT, + WMI_TAG_PDEV_UPDATE_PKT_ROUTING_CMD, + WMI_TAG_PDEV_CHECK_CAL_VERSION_CMD, + WMI_TAG_PDEV_CHECK_CAL_VERSION_EVENT, + WMI_TAG_PDEV_SET_DIVERSITY_GAIN_CMD, + WMI_TAG_MAC_PHY_CHAINMASK_COMBO, + WMI_TAG_MAC_PHY_CHAINMASK_CAPABILITY, + WMI_TAG_VDEV_SET_ARP_STATS_CMD, + WMI_TAG_VDEV_GET_ARP_STATS_CMD, + WMI_TAG_VDEV_GET_ARP_STATS_EVENT, + WMI_TAG_IFACE_OFFLOAD_STATS, + WMI_TAG_REQUEST_STATS_CMD_SUB_STRUCT_PARAM, + WMI_TAG_RSSI_CTL_EXT, + WMI_TAG_SINGLE_PHYERR_EXT_RX_HDR, + WMI_TAG_COEX_BT_ACTIVITY_EVENT, + WMI_TAG_VDEV_GET_TX_POWER_CMD, + WMI_TAG_VDEV_TX_POWER_EVENT, + WMI_TAG_OFFCHAN_DATA_TX_COMPL_EVENT, + WMI_TAG_OFFCHAN_DATA_TX_SEND_CMD, + WMI_TAG_TX_SEND_PARAMS, + WMI_TAG_HE_RATE_SET, + WMI_TAG_CONGESTION_STATS, + WMI_TAG_SET_INIT_COUNTRY_CMD, + WMI_TAG_SCAN_DBS_DUTY_CYCLE, + WMI_TAG_SCAN_DBS_DUTY_CYCLE_PARAM_TLV, + WMI_TAG_PDEV_DIV_GET_RSSI_ANTID, + WMI_TAG_THERM_THROT_CONFIG_REQUEST, + WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO, + WMI_TAG_THERM_THROT_STATS_EVENT, + WMI_TAG_THERM_THROT_LEVEL_STATS_INFO, + WMI_TAG_PDEV_DIV_RSSI_ANTID_EVENT, + WMI_TAG_OEM_DMA_RING_CAPABILITIES, + WMI_TAG_OEM_DMA_RING_CFG_REQ, + WMI_TAG_OEM_DMA_RING_CFG_RSP, + WMI_TAG_OEM_INDIRECT_DATA, + WMI_TAG_OEM_DMA_BUF_RELEASE, + WMI_TAG_OEM_DMA_BUF_RELEASE_ENTRY, + WMI_TAG_PDEV_BSS_CHAN_INFO_REQUEST, + WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT, + WMI_TAG_ROAM_LCA_DISALLOW_CONFIG, + WMI_TAG_VDEV_LIMIT_OFFCHAN_CMD, + WMI_TAG_ROAM_RSSI_REJECTION_OCE_CONFIG, + WMI_TAG_UNIT_TEST_EVENT, + WMI_TAG_ROAM_FILS_OFFLOAD, + WMI_TAG_PDEV_UPDATE_PMK_CACHE_CMD, + WMI_TAG_PMK_CACHE, + WMI_TAG_PDEV_UPDATE_FILS_HLP_PKT_CMD, + WMI_TAG_ROAM_FILS_SYNCH, + WMI_TAG_GTK_OFFLOAD_EXTENDED, + WMI_TAG_ROAM_BG_SCAN_ROAMING, + WMI_TAG_OIC_PING_OFFLOAD_PARAMS_CMD, + WMI_TAG_OIC_PING_OFFLOAD_SET_ENABLE_CMD, + WMI_TAG_OIC_PING_HANDOFF_EVENT, + WMI_TAG_DHCP_LEASE_RENEW_OFFLOAD_CMD, + WMI_TAG_DHCP_LEASE_RENEW_EVENT, + WMI_TAG_BTM_CONFIG, + WMI_TAG_DEBUG_MESG_FW_DATA_STALL, + WMI_TAG_WLM_CONFIG_CMD, + WMI_TAG_PDEV_UPDATE_CTLTABLE_REQUEST, + WMI_TAG_PDEV_UPDATE_CTLTABLE_EVENT, + WMI_TAG_ROAM_CND_SCORING_PARAM, + WMI_TAG_PDEV_CONFIG_VENDOR_OUI_ACTION, + WMI_TAG_VENDOR_OUI_EXT, + WMI_TAG_ROAM_SYNCH_FRAME_EVENT, + WMI_TAG_FD_SEND_FROM_HOST_CMD, + WMI_TAG_ENABLE_FILS_CMD, + WMI_TAG_HOST_SWFDA_EVENT, + WMI_TAG_BCN_OFFLOAD_CTRL_CMD, + WMI_TAG_PDEV_SET_AC_TX_QUEUE_OPTIMIZED_CMD, + WMI_TAG_STATS_PERIOD, + WMI_TAG_NDL_SCHEDULE_UPDATE, + WMI_TAG_PEER_TID_MSDUQ_QDEPTH_THRESH_UPDATE_CMD, + WMI_TAG_MSDUQ_QDEPTH_THRESH_UPDATE, + WMI_TAG_PDEV_SET_RX_FILTER_PROMISCUOUS_CMD, + WMI_TAG_SAR2_RESULT_EVENT, + WMI_TAG_SAR_CAPABILITIES, + WMI_TAG_SAP_OBSS_DETECTION_CFG_CMD, + WMI_TAG_SAP_OBSS_DETECTION_INFO_EVT, + WMI_TAG_DMA_RING_CAPABILITIES, + WMI_TAG_DMA_RING_CFG_REQ, + WMI_TAG_DMA_RING_CFG_RSP, + WMI_TAG_DMA_BUF_RELEASE, + WMI_TAG_DMA_BUF_RELEASE_ENTRY, + WMI_TAG_SAR_GET_LIMITS_CMD, + WMI_TAG_SAR_GET_LIMITS_EVENT, + WMI_TAG_SAR_GET_LIMITS_EVENT_ROW, + WMI_TAG_OFFLOAD_11K_REPORT, + WMI_TAG_INVOKE_NEIGHBOR_REPORT, + WMI_TAG_NEIGHBOR_REPORT_OFFLOAD, + WMI_TAG_VDEV_SET_CONNECTIVITY_CHECK_STATS, + WMI_TAG_VDEV_GET_CONNECTIVITY_CHECK_STATS, + WMI_TAG_BPF_SET_VDEV_ENABLE_CMD, + WMI_TAG_BPF_SET_VDEV_WORK_MEMORY_CMD, + WMI_TAG_BPF_GET_VDEV_WORK_MEMORY_CMD, + WMI_TAG_BPF_GET_VDEV_WORK_MEMORY_RESP_EVT, + WMI_TAG_PDEV_GET_NFCAL_POWER, + WMI_TAG_BSS_COLOR_CHANGE_ENABLE, + WMI_TAG_OBSS_COLOR_COLLISION_DET_CONFIG, + WMI_TAG_OBSS_COLOR_COLLISION_EVT, + WMI_TAG_RUNTIME_DPD_RECAL_CMD, + WMI_TAG_TWT_ENABLE_CMD, + WMI_TAG_TWT_DISABLE_CMD, + WMI_TAG_TWT_ADD_DIALOG_CMD, + WMI_TAG_TWT_DEL_DIALOG_CMD, + WMI_TAG_TWT_PAUSE_DIALOG_CMD, + WMI_TAG_TWT_RESUME_DIALOG_CMD, + WMI_TAG_TWT_ENABLE_COMPLETE_EVENT, + WMI_TAG_TWT_DISABLE_COMPLETE_EVENT, + WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT, + WMI_TAG_TWT_DEL_DIALOG_COMPLETE_EVENT, + WMI_TAG_TWT_PAUSE_DIALOG_COMPLETE_EVENT, + WMI_TAG_TWT_RESUME_DIALOG_COMPLETE_EVENT, + WMI_TAG_REQUEST_ROAM_SCAN_STATS_CMD, + WMI_TAG_ROAM_SCAN_STATS_EVENT, + WMI_TAG_PEER_TID_CONFIGURATIONS_CMD, + WMI_TAG_VDEV_SET_CUSTOM_SW_RETRY_TH_CMD, + WMI_TAG_GET_TPC_POWER_CMD, + WMI_TAG_GET_TPC_POWER_EVENT, + WMI_TAG_DMA_BUF_RELEASE_SPECTRAL_META_DATA, + WMI_TAG_MOTION_DET_CONFIG_PARAMS_CMD, + WMI_TAG_MOTION_DET_BASE_LINE_CONFIG_PARAMS_CMD, + WMI_TAG_MOTION_DET_START_STOP_CMD, + WMI_TAG_MOTION_DET_BASE_LINE_START_STOP_CMD, + WMI_TAG_MOTION_DET_EVENT, + WMI_TAG_MOTION_DET_BASE_LINE_EVENT, + WMI_TAG_NDP_TRANSPORT_IP, + WMI_TAG_OBSS_SPATIAL_REUSE_SET_CMD, + WMI_TAG_ESP_ESTIMATE_EVENT, + WMI_TAG_NAN_HOST_CONFIG, + WMI_TAG_SPECTRAL_BIN_SCALING_PARAMS, + WMI_TAG_PEER_CFR_CAPTURE_CMD, + WMI_TAG_PEER_CHAN_WIDTH_SWITCH_CMD, + WMI_TAG_CHAN_WIDTH_PEER_LIST, + WMI_TAG_OBSS_SPATIAL_REUSE_SET_DEF_OBSS_THRESH_CMD, + WMI_TAG_PDEV_HE_TB_ACTION_FRM_CMD, + WMI_TAG_PEER_EXTD2_STATS, + WMI_TAG_HPCS_PULSE_START_CMD, + WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT, + WMI_TAG_VDEV_CHAINMASK_CONFIG_CMD, + WMI_TAG_VDEV_BCN_OFFLOAD_QUIET_CONFIG_CMD, + WMI_TAG_NAN_EVENT_INFO, + WMI_TAG_NDP_CHANNEL_INFO, + WMI_TAG_NDP_CMD, + WMI_TAG_NDP_EVENT, + /* TODO add all the missing cmds */ + WMI_TAG_PDEV_PEER_PKTLOG_FILTER_CMD = 0x301, + WMI_TAG_PDEV_PEER_PKTLOG_FILTER_INFO, + WMI_TAG_MAX +}; + +enum wmi_tlv_service { + WMI_TLV_SERVICE_BEACON_OFFLOAD = 0, + WMI_TLV_SERVICE_SCAN_OFFLOAD = 1, + WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD = 2, + WMI_TLV_SERVICE_BCN_MISS_OFFLOAD = 3, + WMI_TLV_SERVICE_STA_PWRSAVE = 4, + WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE = 5, + WMI_TLV_SERVICE_AP_UAPSD = 6, + WMI_TLV_SERVICE_AP_DFS = 7, + WMI_TLV_SERVICE_11AC = 8, + WMI_TLV_SERVICE_BLOCKACK = 9, + WMI_TLV_SERVICE_PHYERR = 10, + WMI_TLV_SERVICE_BCN_FILTER = 11, + WMI_TLV_SERVICE_RTT = 12, + WMI_TLV_SERVICE_WOW = 13, + WMI_TLV_SERVICE_RATECTRL_CACHE = 14, + WMI_TLV_SERVICE_IRAM_TIDS = 15, + WMI_TLV_SERVICE_ARPNS_OFFLOAD = 16, + WMI_TLV_SERVICE_NLO = 17, + WMI_TLV_SERVICE_GTK_OFFLOAD = 18, + WMI_TLV_SERVICE_SCAN_SCH = 19, + WMI_TLV_SERVICE_CSA_OFFLOAD = 20, + WMI_TLV_SERVICE_CHATTER = 21, + WMI_TLV_SERVICE_COEX_FREQAVOID = 22, + WMI_TLV_SERVICE_PACKET_POWER_SAVE = 23, + WMI_TLV_SERVICE_FORCE_FW_HANG = 24, + WMI_TLV_SERVICE_GPIO = 25, + WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM = 26, + WMI_STA_UAPSD_BASIC_AUTO_TRIG = 27, + WMI_STA_UAPSD_VAR_AUTO_TRIG = 28, + WMI_TLV_SERVICE_STA_KEEP_ALIVE = 29, + WMI_TLV_SERVICE_TX_ENCAP = 30, + WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC = 31, + WMI_TLV_SERVICE_EARLY_RX = 32, + WMI_TLV_SERVICE_STA_SMPS = 33, + WMI_TLV_SERVICE_FWTEST = 34, + WMI_TLV_SERVICE_STA_WMMAC = 35, + WMI_TLV_SERVICE_TDLS = 36, + WMI_TLV_SERVICE_BURST = 37, + WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE = 38, + WMI_TLV_SERVICE_ADAPTIVE_OCS = 39, + WMI_TLV_SERVICE_BA_SSN_SUPPORT = 40, + WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE = 41, + WMI_TLV_SERVICE_WLAN_HB = 42, + WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT = 43, + WMI_TLV_SERVICE_BATCH_SCAN = 44, + WMI_TLV_SERVICE_QPOWER = 45, + WMI_TLV_SERVICE_PLMREQ = 46, + WMI_TLV_SERVICE_THERMAL_MGMT = 47, + WMI_TLV_SERVICE_RMC = 48, + WMI_TLV_SERVICE_MHF_OFFLOAD = 49, + WMI_TLV_SERVICE_COEX_SAR = 50, + WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE = 51, + WMI_TLV_SERVICE_NAN = 52, + WMI_TLV_SERVICE_L1SS_STAT = 53, + WMI_TLV_SERVICE_ESTIMATE_LINKSPEED = 54, + WMI_TLV_SERVICE_OBSS_SCAN = 55, + WMI_TLV_SERVICE_TDLS_OFFCHAN = 56, + WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA = 57, + WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA = 58, + WMI_TLV_SERVICE_IBSS_PWRSAVE = 59, + WMI_TLV_SERVICE_LPASS = 60, + WMI_TLV_SERVICE_EXTSCAN = 61, + WMI_TLV_SERVICE_D0WOW = 62, + WMI_TLV_SERVICE_HSOFFLOAD = 63, + WMI_TLV_SERVICE_ROAM_HO_OFFLOAD = 64, + WMI_TLV_SERVICE_RX_FULL_REORDER = 65, + WMI_TLV_SERVICE_DHCP_OFFLOAD = 66, + WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT = 67, + WMI_TLV_SERVICE_MDNS_OFFLOAD = 68, + WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD = 69, + WMI_TLV_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT = 70, + WMI_TLV_SERVICE_OCB = 71, + WMI_TLV_SERVICE_AP_ARPNS_OFFLOAD = 72, + WMI_TLV_SERVICE_PER_BAND_CHAINMASK_SUPPORT = 73, + WMI_TLV_SERVICE_PACKET_FILTER_OFFLOAD = 74, + WMI_TLV_SERVICE_MGMT_TX_HTT = 75, + WMI_TLV_SERVICE_MGMT_TX_WMI = 76, + WMI_TLV_SERVICE_EXT_MSG = 77, + WMI_TLV_SERVICE_MAWC = 78, + WMI_TLV_SERVICE_PEER_ASSOC_CONF = 79, + WMI_TLV_SERVICE_EGAP = 80, + WMI_TLV_SERVICE_STA_PMF_OFFLOAD = 81, + WMI_TLV_SERVICE_UNIFIED_WOW_CAPABILITY = 82, + WMI_TLV_SERVICE_ENHANCED_PROXY_STA = 83, + WMI_TLV_SERVICE_ATF = 84, + WMI_TLV_SERVICE_COEX_GPIO = 85, + WMI_TLV_SERVICE_AUX_SPECTRAL_INTF = 86, + WMI_TLV_SERVICE_AUX_CHAN_LOAD_INTF = 87, + WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64 = 88, + WMI_TLV_SERVICE_ENTERPRISE_MESH = 89, + WMI_TLV_SERVICE_RESTRT_CHNL_SUPPORT = 90, + WMI_TLV_SERVICE_BPF_OFFLOAD = 91, + WMI_TLV_SERVICE_SYNC_DELETE_CMDS = 92, + WMI_TLV_SERVICE_SMART_ANTENNA_SW_SUPPORT = 93, + WMI_TLV_SERVICE_SMART_ANTENNA_HW_SUPPORT = 94, + WMI_TLV_SERVICE_RATECTRL_LIMIT_MAX_MIN_RATES = 95, + WMI_TLV_SERVICE_NAN_DATA = 96, + WMI_TLV_SERVICE_NAN_RTT = 97, + WMI_TLV_SERVICE_11AX = 98, + WMI_TLV_SERVICE_DEPRECATED_REPLACE = 99, + WMI_TLV_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE = 100, + WMI_TLV_SERVICE_ENHANCED_MCAST_FILTER = 101, + WMI_TLV_SERVICE_PERIODIC_CHAN_STAT_SUPPORT = 102, + WMI_TLV_SERVICE_MESH_11S = 103, + WMI_TLV_SERVICE_HALF_RATE_QUARTER_RATE_SUPPORT = 104, + WMI_TLV_SERVICE_VDEV_RX_FILTER = 105, + WMI_TLV_SERVICE_P2P_LISTEN_OFFLOAD_SUPPORT = 106, + WMI_TLV_SERVICE_MARK_FIRST_WAKEUP_PACKET = 107, + WMI_TLV_SERVICE_MULTIPLE_MCAST_FILTER_SET = 108, + WMI_TLV_SERVICE_HOST_MANAGED_RX_REORDER = 109, + WMI_TLV_SERVICE_FLASH_RDWR_SUPPORT = 110, + WMI_TLV_SERVICE_WLAN_STATS_REPORT = 111, + WMI_TLV_SERVICE_TX_MSDU_ID_NEW_PARTITION_SUPPORT = 112, + WMI_TLV_SERVICE_DFS_PHYERR_OFFLOAD = 113, + WMI_TLV_SERVICE_RCPI_SUPPORT = 114, + WMI_TLV_SERVICE_FW_MEM_DUMP_SUPPORT = 115, + WMI_TLV_SERVICE_PEER_STATS_INFO = 116, + WMI_TLV_SERVICE_REGULATORY_DB = 117, + WMI_TLV_SERVICE_11D_OFFLOAD = 118, + WMI_TLV_SERVICE_HW_DATA_FILTERING = 119, + WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART = 120, + WMI_TLV_SERVICE_PKT_ROUTING = 121, + WMI_TLV_SERVICE_CHECK_CAL_VERSION = 122, + WMI_TLV_SERVICE_OFFCHAN_TX_WMI = 123, + WMI_TLV_SERVICE_8SS_TX_BFEE = 124, + WMI_TLV_SERVICE_EXTENDED_NSS_SUPPORT = 125, + WMI_TLV_SERVICE_ACK_TIMEOUT = 126, + WMI_TLV_SERVICE_PDEV_BSS_CHANNEL_INFO_64 = 127, + + WMI_MAX_SERVICE = 128, + + WMI_TLV_SERVICE_CHAN_LOAD_INFO = 128, + WMI_TLV_SERVICE_TX_PPDU_INFO_STATS_SUPPORT = 129, + WMI_TLV_SERVICE_VDEV_LIMIT_OFFCHAN_SUPPORT = 130, + WMI_TLV_SERVICE_FILS_SUPPORT = 131, + WMI_TLV_SERVICE_WLAN_OIC_PING_OFFLOAD = 132, + WMI_TLV_SERVICE_WLAN_DHCP_RENEW = 133, + WMI_TLV_SERVICE_MAWC_SUPPORT = 134, + WMI_TLV_SERVICE_VDEV_LATENCY_CONFIG = 135, + WMI_TLV_SERVICE_PDEV_UPDATE_CTLTABLE_SUPPORT = 136, + WMI_TLV_SERVICE_PKTLOG_SUPPORT_OVER_HTT = 137, + WMI_TLV_SERVICE_VDEV_MULTI_GROUP_KEY_SUPPORT = 138, + WMI_TLV_SERVICE_SCAN_PHYMODE_SUPPORT = 139, + WMI_TLV_SERVICE_THERM_THROT = 140, + WMI_TLV_SERVICE_BCN_OFFLOAD_START_STOP_SUPPORT = 141, + WMI_TLV_SERVICE_WOW_WAKEUP_BY_TIMER_PATTERN = 142, + WMI_TLV_SERVICE_PEER_MAP_UNMAP_V2_SUPPORT = 143, + WMI_TLV_SERVICE_OFFCHAN_DATA_TID_SUPPORT = 144, + WMI_TLV_SERVICE_RX_PROMISC_ENABLE_SUPPORT = 145, + WMI_TLV_SERVICE_SUPPORT_DIRECT_DMA = 146, + WMI_TLV_SERVICE_AP_OBSS_DETECTION_OFFLOAD = 147, + WMI_TLV_SERVICE_11K_NEIGHBOUR_REPORT_SUPPORT = 148, + WMI_TLV_SERVICE_LISTEN_INTERVAL_OFFLOAD_SUPPORT = 149, + WMI_TLV_SERVICE_BSS_COLOR_OFFLOAD = 150, + WMI_TLV_SERVICE_RUNTIME_DPD_RECAL = 151, + WMI_TLV_SERVICE_STA_TWT = 152, + WMI_TLV_SERVICE_AP_TWT = 153, + WMI_TLV_SERVICE_GMAC_OFFLOAD_SUPPORT = 154, + WMI_TLV_SERVICE_SPOOF_MAC_SUPPORT = 155, + WMI_TLV_SERVICE_PEER_TID_CONFIGS_SUPPORT = 156, + WMI_TLV_SERVICE_VDEV_SWRETRY_PER_AC_CONFIG_SUPPORT = 157, + WMI_TLV_SERVICE_DUAL_BEACON_ON_SINGLE_MAC_SCC_SUPPORT = 158, + WMI_TLV_SERVICE_DUAL_BEACON_ON_SINGLE_MAC_MCC_SUPPORT = 159, + WMI_TLV_SERVICE_MOTION_DET = 160, + WMI_TLV_SERVICE_INFRA_MBSSID = 161, + WMI_TLV_SERVICE_OBSS_SPATIAL_REUSE = 162, + WMI_TLV_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT = 163, + WMI_TLV_SERVICE_NAN_DBS_SUPPORT = 164, + WMI_TLV_SERVICE_NDI_DBS_SUPPORT = 165, + WMI_TLV_SERVICE_NAN_SAP_SUPPORT = 166, + WMI_TLV_SERVICE_NDI_SAP_SUPPORT = 167, + WMI_TLV_SERVICE_CFR_CAPTURE_SUPPORT = 168, + WMI_TLV_SERVICE_CFR_CAPTURE_IND_MSG_TYPE_1 = 169, + WMI_TLV_SERVICE_ESP_SUPPORT = 170, + WMI_TLV_SERVICE_PEER_CHWIDTH_CHANGE = 171, + WMI_TLV_SERVICE_WLAN_HPCS_PULSE = 172, + WMI_TLV_SERVICE_PER_VDEV_CHAINMASK_CONFIG_SUPPORT = 173, + WMI_TLV_SERVICE_TX_DATA_MGMT_ACK_RSSI = 174, + WMI_TLV_SERVICE_NAN_DISABLE_SUPPORT = 175, + WMI_TLV_SERVICE_HTT_H2T_NO_HTC_HDR_LEN_IN_MSG_LEN = 176, + + WMI_MAX_EXT_SERVICE + +}; + +enum { + WMI_SMPS_FORCED_MODE_NONE = 0, + WMI_SMPS_FORCED_MODE_DISABLED, + WMI_SMPS_FORCED_MODE_STATIC, + WMI_SMPS_FORCED_MODE_DYNAMIC +}; + +#define WMI_TPC_CHAINMASK_CONFIG_BAND_2G 0 +#define WMI_TPC_CHAINMASK_CONFIG_BAND_5G 1 +#define WMI_NUM_SUPPORTED_BAND_MAX 2 + +#define WMI_PEER_MIMO_PS_STATE 0x1 +#define WMI_PEER_AMPDU 0x2 +#define WMI_PEER_AUTHORIZE 0x3 +#define WMI_PEER_CHWIDTH 0x4 +#define WMI_PEER_NSS 0x5 +#define WMI_PEER_USE_4ADDR 0x6 +#define WMI_PEER_MEMBERSHIP 0x7 +#define WMI_PEER_USERPOS 0x8 +#define WMI_PEER_CRIT_PROTO_HINT_ENABLED 0x9 +#define WMI_PEER_TX_FAIL_CNT_THR 0xA +#define WMI_PEER_SET_HW_RETRY_CTS2S 0xB +#define WMI_PEER_IBSS_ATIM_WINDOW_LENGTH 0xC +#define WMI_PEER_PHYMODE 0xD +#define WMI_PEER_USE_FIXED_PWR 0xE +#define WMI_PEER_PARAM_FIXED_RATE 0xF +#define WMI_PEER_SET_MU_WHITELIST 0x10 +#define WMI_PEER_SET_MAX_TX_RATE 0x11 +#define WMI_PEER_SET_MIN_TX_RATE 0x12 +#define WMI_PEER_SET_DEFAULT_ROUTING 0x13 + +/* slot time long */ +#define WMI_VDEV_SLOT_TIME_LONG 0x1 +/* slot time short */ +#define WMI_VDEV_SLOT_TIME_SHORT 0x2 +/* preablbe long */ +#define WMI_VDEV_PREAMBLE_LONG 0x1 +/* preablbe short */ +#define WMI_VDEV_PREAMBLE_SHORT 0x2 + +enum wmi_peer_smps_state { + WMI_PEER_SMPS_PS_NONE = 0x0, + WMI_PEER_SMPS_STATIC = 0x1, + WMI_PEER_SMPS_DYNAMIC = 0x2 +}; + +enum wmi_peer_chwidth { + WMI_PEER_CHWIDTH_20MHZ = 0, + WMI_PEER_CHWIDTH_40MHZ = 1, + WMI_PEER_CHWIDTH_80MHZ = 2, + WMI_PEER_CHWIDTH_160MHZ = 3, +}; + +enum wmi_beacon_gen_mode { + WMI_BEACON_STAGGERED_MODE = 0, + WMI_BEACON_BURST_MODE = 1 +}; + +struct wmi_host_pdev_band_to_mac { + u32 pdev_id; + u32 start_freq; + u32 end_freq; +}; + +struct ath11k_ppe_threshold { + u32 numss_m1; + u32 ru_bit_mask; + u32 ppet16_ppet8_ru3_ru0[PSOC_HOST_MAX_NUM_SS]; +}; + +struct ath11k_service_ext_param { + u32 default_conc_scan_config_bits; + u32 default_fw_config_bits; + struct ath11k_ppe_threshold ppet; + u32 he_cap_info; + u32 mpdu_density; + u32 max_bssid_rx_filters; + u32 num_hw_modes; + u32 num_phy; +}; + +struct ath11k_hw_mode_caps { + u32 hw_mode_id; + u32 phy_id_map; + u32 hw_mode_config_type; +}; + +#define PSOC_HOST_MAX_PHY_SIZE (3) +#define ATH11K_11B_SUPPORT BIT(0) +#define ATH11K_11G_SUPPORT BIT(1) +#define ATH11K_11A_SUPPORT BIT(2) +#define ATH11K_11N_SUPPORT BIT(3) +#define ATH11K_11AC_SUPPORT BIT(4) +#define ATH11K_11AX_SUPPORT BIT(5) + +struct ath11k_hal_reg_capabilities_ext { + u32 phy_id; + u32 eeprom_reg_domain; + u32 eeprom_reg_domain_ext; + u32 regcap1; + u32 regcap2; + u32 wireless_modes; + u32 low_2ghz_chan; + u32 high_2ghz_chan; + u32 low_5ghz_chan; + u32 high_5ghz_chan; +}; + +#define WMI_HOST_MAX_PDEV 3 + +struct wlan_host_mem_chunk { + u32 tlv_header; + u32 req_id; + u32 ptr; + u32 size; +} __packed; + +struct wmi_host_mem_chunk { + void *vaddr; + dma_addr_t paddr; + u32 len; + u32 req_id; +}; + +struct wmi_init_cmd_param { + u32 tlv_header; + struct target_resource_config *res_cfg; + u8 num_mem_chunks; + struct wmi_host_mem_chunk *mem_chunks; + u32 hw_mode_id; + u32 num_band_to_mac; + struct wmi_host_pdev_band_to_mac band_to_mac[WMI_HOST_MAX_PDEV]; +}; + +struct wmi_pdev_band_to_mac { + u32 tlv_header; + u32 pdev_id; + u32 start_freq; + u32 end_freq; +} __packed; + +struct wmi_pdev_set_hw_mode_cmd_param { + u32 tlv_header; + u32 pdev_id; + u32 hw_mode_index; + u32 num_band_to_mac; +} __packed; + +struct wmi_ppe_threshold { + u32 numss_m1; /** NSS - 1*/ + union { + u32 ru_count; + u32 ru_mask; + } __packed; + u32 ppet16_ppet8_ru3_ru0[WMI_MAX_NUM_SS]; +} __packed; + +#define HW_BD_INFO_SIZE 5 + +struct wmi_abi_version { + u32 abi_version_0; + u32 abi_version_1; + u32 abi_version_ns_0; + u32 abi_version_ns_1; + u32 abi_version_ns_2; + u32 abi_version_ns_3; +} __packed; + +struct wmi_init_cmd { + u32 tlv_header; + struct wmi_abi_version host_abi_vers; + u32 num_host_mem_chunks; +} __packed; + +struct wmi_resource_config { + u32 tlv_header; + u32 num_vdevs; + u32 num_peers; + u32 num_offload_peers; + u32 num_offload_reorder_buffs; + u32 num_peer_keys; + u32 num_tids; + u32 ast_skid_limit; + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 rx_timeout_pri[4]; + u32 rx_decap_mode; + u32 scan_max_pending_req; + u32 bmiss_offload_max_vdev; + u32 roam_offload_max_vdev; + u32 roam_offload_max_ap_profiles; + u32 num_mcast_groups; + u32 num_mcast_table_elems; + u32 mcast2ucast_mode; + u32 tx_dbg_log_size; + u32 num_wds_entries; + u32 dma_burst_size; + u32 mac_aggr_delim; + u32 rx_skip_defrag_timeout_dup_detection_check; + u32 vow_config; + u32 gtk_offload_max_vdev; + u32 num_msdu_desc; + u32 max_frag_entries; + u32 num_tdls_vdevs; + u32 num_tdls_conn_table_entries; + u32 beacon_tx_offload_max_vdev; + u32 num_multicast_filter_entries; + u32 num_wow_filters; + u32 num_keep_alive_pattern; + u32 keep_alive_pattern_size; + u32 max_tdls_concurrent_sleep_sta; + u32 max_tdls_concurrent_buffer_sta; + u32 wmi_send_separate; + u32 num_ocb_vdevs; + u32 num_ocb_channels; + u32 num_ocb_schedules; + u32 flag1; + u32 smart_ant_cap; + u32 bk_minfree; + u32 be_minfree; + u32 vi_minfree; + u32 vo_minfree; + u32 alloc_frag_desc_for_data_pkt; + u32 num_ns_ext_tuples_cfg; + u32 bpf_instruction_size; + u32 max_bssid_rx_filters; + u32 use_pdev_id; + u32 max_num_dbs_scan_duty_cycle; + u32 max_num_group_keys; + u32 peer_map_unmap_v2_support; + u32 sched_params; + u32 twt_ap_pdev_count; + u32 twt_ap_sta_count; +} __packed; + +struct wmi_service_ready_event { + u32 fw_build_vers; + struct wmi_abi_version fw_abi_vers; + u32 phy_capability; + u32 max_frag_entry; + u32 num_rf_chains; + u32 ht_cap_info; + u32 vht_cap_info; + u32 vht_supp_mcs; + u32 hw_min_tx_power; + u32 hw_max_tx_power; + u32 sys_cap_info; + u32 min_pkt_size_enable; + u32 max_bcn_ie_size; + u32 num_mem_reqs; + u32 max_num_scan_channels; + u32 hw_bd_id; + u32 hw_bd_info[HW_BD_INFO_SIZE]; + u32 max_supported_macs; + u32 wmi_fw_sub_feat_caps; + u32 num_dbs_hw_modes; + /* txrx_chainmask + * [7:0] - 2G band tx chain mask + * [15:8] - 2G band rx chain mask + * [23:16] - 5G band tx chain mask + * [31:24] - 5G band rx chain mask + */ + u32 txrx_chainmask; + u32 default_dbs_hw_mode_index; + u32 num_msdu_desc; +} __packed; + +#define WMI_SERVICE_BM_SIZE ((WMI_MAX_SERVICE + sizeof(u32) - 1) / sizeof(u32)) + +#define WMI_SERVICE_SEGMENT_BM_SIZE32 4 /* 4x u32 = 128 bits */ +#define WMI_SERVICE_EXT_BM_SIZE (WMI_SERVICE_SEGMENT_BM_SIZE32 * sizeof(u32)) +#define WMI_AVAIL_SERVICE_BITS_IN_SIZE32 32 +#define WMI_SERVICE_BITS_IN_SIZE32 4 + +struct wmi_service_ready_ext_event { + u32 default_conc_scan_config_bits; + u32 default_fw_config_bits; + struct wmi_ppe_threshold ppet; + u32 he_cap_info; + u32 mpdu_density; + u32 max_bssid_rx_filters; + u32 fw_build_vers_ext; + u32 max_nlo_ssids; + u32 max_bssid_indicator; + u32 he_cap_info_ext; +} __packed; + +struct wmi_soc_mac_phy_hw_mode_caps { + u32 num_hw_modes; + u32 num_chainmask_tables; +} __packed; + +struct wmi_hw_mode_capabilities { + u32 tlv_header; + u32 hw_mode_id; + u32 phy_id_map; + u32 hw_mode_config_type; +} __packed; + +#define WMI_MAX_HECAP_PHY_SIZE (3) + +struct wmi_mac_phy_capabilities { + u32 hw_mode_id; + u32 pdev_id; + u32 phy_id; + u32 supported_flags; + u32 supported_bands; + u32 ampdu_density; + u32 max_bw_supported_2g; + u32 ht_cap_info_2g; + u32 vht_cap_info_2g; + u32 vht_supp_mcs_2g; + u32 he_cap_info_2g; + u32 he_supp_mcs_2g; + u32 tx_chain_mask_2g; + u32 rx_chain_mask_2g; + u32 max_bw_supported_5g; + u32 ht_cap_info_5g; + u32 vht_cap_info_5g; + u32 vht_supp_mcs_5g; + u32 he_cap_info_5g; + u32 he_supp_mcs_5g; + u32 tx_chain_mask_5g; + u32 rx_chain_mask_5g; + u32 he_cap_phy_info_2g[WMI_MAX_HECAP_PHY_SIZE]; + u32 he_cap_phy_info_5g[WMI_MAX_HECAP_PHY_SIZE]; + struct wmi_ppe_threshold he_ppet2g; + struct wmi_ppe_threshold he_ppet5g; + u32 chainmask_table_id; + u32 lmac_id; + u32 he_cap_info_2g_ext; + u32 he_cap_info_5g_ext; + u32 he_cap_info_internal; +} __packed; + +struct wmi_hal_reg_capabilities_ext { + u32 tlv_header; + u32 phy_id; + u32 eeprom_reg_domain; + u32 eeprom_reg_domain_ext; + u32 regcap1; + u32 regcap2; + u32 wireless_modes; + u32 low_2ghz_chan; + u32 high_2ghz_chan; + u32 low_5ghz_chan; + u32 high_5ghz_chan; +} __packed; + +struct wmi_soc_hal_reg_capabilities { + u32 num_phy; +} __packed; + +/* 2 word representation of MAC addr */ +struct wmi_mac_addr { + union { + u8 addr[6]; + struct { + u32 word0; + u32 word1; + } __packed; + } __packed; +} __packed; + +struct wmi_ready_event { + struct wmi_abi_version fw_abi_vers; + struct wmi_mac_addr mac_addr; + u32 status; + u32 num_dscp_table; + u32 num_extra_mac_addr; + u32 num_total_peers; + u32 num_extra_peers; +} __packed; + +struct wmi_service_available_event { + u32 wmi_service_segment_offset; + u32 wmi_service_segment_bitmap[WMI_SERVICE_SEGMENT_BM_SIZE32]; +} __packed; + +struct ath11k_pdev_wmi { + struct ath11k_wmi_base *wmi_ab; + enum ath11k_htc_ep_id eid; + const struct wmi_peer_flags_map *peer_flags; + u32 rx_decap_mode; +}; + +struct vdev_create_params { + u8 if_id; + u32 type; + u32 subtype; + struct { + u8 tx; + u8 rx; + } chains[NUM_NL80211_BANDS]; + u32 pdev_id; +}; + +struct wmi_vdev_create_cmd { + u32 tlv_header; + u32 vdev_id; + u32 vdev_type; + u32 vdev_subtype; + struct wmi_mac_addr vdev_macaddr; + u32 num_cfg_txrx_streams; + u32 pdev_id; +} __packed; + +struct wmi_vdev_txrx_streams { + u32 tlv_header; + u32 band; + u32 supported_tx_streams; + u32 supported_rx_streams; +} __packed; + +struct wmi_vdev_delete_cmd { + u32 tlv_header; + u32 vdev_id; +} __packed; + +struct wmi_vdev_up_cmd { + u32 tlv_header; + u32 vdev_id; + u32 vdev_assoc_id; + struct wmi_mac_addr vdev_bssid; + struct wmi_mac_addr trans_bssid; + u32 profile_idx; + u32 profile_num; +} __packed; + +struct wmi_vdev_stop_cmd { + u32 tlv_header; + u32 vdev_id; +} __packed; + +struct wmi_vdev_down_cmd { + u32 tlv_header; + u32 vdev_id; +} __packed; + +#define WMI_VDEV_START_HIDDEN_SSID BIT(0) +#define WMI_VDEV_START_PMF_ENABLED BIT(1) +#define WMI_VDEV_START_LDPC_RX_ENABLED BIT(3) + +struct wmi_ssid { + u32 ssid_len; + u32 ssid[8]; +} __packed; + +#define ATH11K_VDEV_SETUP_TIMEOUT_HZ (1 * HZ) + +struct wmi_vdev_start_request_cmd { + u32 tlv_header; + u32 vdev_id; + u32 requestor_id; + u32 beacon_interval; + u32 dtim_period; + u32 flags; + struct wmi_ssid ssid; + u32 bcn_tx_rate; + u32 bcn_txpower; + u32 num_noa_descriptors; + u32 disable_hw_ack; + u32 preferred_tx_streams; + u32 preferred_rx_streams; + u32 he_ops; + u32 cac_duration_ms; + u32 regdomain; +} __packed; + +#define MGMT_TX_DL_FRM_LEN 64 +#define WMI_MAC_MAX_SSID_LENGTH 32 +struct mac_ssid { + u8 length; + u8 mac_ssid[WMI_MAC_MAX_SSID_LENGTH]; +} __packed; + +struct wmi_p2p_noa_descriptor { + u32 type_count; + u32 duration; + u32 interval; + u32 start_time; +}; + +struct channel_param { + u8 chan_id; + u8 pwr; + u32 mhz; + u32 half_rate:1, + quarter_rate:1, + dfs_set:1, + dfs_set_cfreq2:1, + is_chan_passive:1, + allow_ht:1, + allow_vht:1, + allow_he:1, + set_agile:1; + u32 phy_mode; + u32 cfreq1; + u32 cfreq2; + char maxpower; + char minpower; + char maxregpower; + u8 antennamax; + u8 reg_class_id; +} __packed; + +enum wmi_phy_mode { + MODE_11A = 0, + MODE_11G = 1, /* 11b/g Mode */ + MODE_11B = 2, /* 11b Mode */ + MODE_11GONLY = 3, /* 11g only Mode */ + MODE_11NA_HT20 = 4, + MODE_11NG_HT20 = 5, + MODE_11NA_HT40 = 6, + MODE_11NG_HT40 = 7, + MODE_11AC_VHT20 = 8, + MODE_11AC_VHT40 = 9, + MODE_11AC_VHT80 = 10, + MODE_11AC_VHT20_2G = 11, + MODE_11AC_VHT40_2G = 12, + MODE_11AC_VHT80_2G = 13, + MODE_11AC_VHT80_80 = 14, + MODE_11AC_VHT160 = 15, + MODE_11AX_HE20 = 16, + MODE_11AX_HE40 = 17, + MODE_11AX_HE80 = 18, + MODE_11AX_HE80_80 = 19, + MODE_11AX_HE160 = 20, + MODE_11AX_HE20_2G = 21, + MODE_11AX_HE40_2G = 22, + MODE_11AX_HE80_2G = 23, + MODE_UNKNOWN = 24, + MODE_MAX = 24 +}; + +static inline const char *ath11k_wmi_phymode_str(enum wmi_phy_mode mode) +{ + switch (mode) { + case MODE_11A: + return "11a"; + case MODE_11G: + return "11g"; + case MODE_11B: + return "11b"; + case MODE_11GONLY: + return "11gonly"; + case MODE_11NA_HT20: + return "11na-ht20"; + case MODE_11NG_HT20: + return "11ng-ht20"; + case MODE_11NA_HT40: + return "11na-ht40"; + case MODE_11NG_HT40: + return "11ng-ht40"; + case MODE_11AC_VHT20: + return "11ac-vht20"; + case MODE_11AC_VHT40: + return "11ac-vht40"; + case MODE_11AC_VHT80: + return "11ac-vht80"; + case MODE_11AC_VHT160: + return "11ac-vht160"; + case MODE_11AC_VHT80_80: + return "11ac-vht80+80"; + case MODE_11AC_VHT20_2G: + return "11ac-vht20-2g"; + case MODE_11AC_VHT40_2G: + return "11ac-vht40-2g"; + case MODE_11AC_VHT80_2G: + return "11ac-vht80-2g"; + case MODE_11AX_HE20: + return "11ax-he20"; + case MODE_11AX_HE40: + return "11ax-he40"; + case MODE_11AX_HE80: + return "11ax-he80"; + case MODE_11AX_HE80_80: + return "11ax-he80+80"; + case MODE_11AX_HE160: + return "11ax-he160"; + case MODE_11AX_HE20_2G: + return "11ax-he20-2g"; + case MODE_11AX_HE40_2G: + return "11ax-he40-2g"; + case MODE_11AX_HE80_2G: + return "11ax-he80-2g"; + case MODE_UNKNOWN: + /* skip */ + break; + + /* no default handler to allow compiler to check that the + * enum is fully handled + */ + } + + return "<unknown>"; +} + +struct wmi_channel_arg { + u32 freq; + u32 band_center_freq1; + u32 band_center_freq2; + bool passive; + bool allow_ibss; + bool allow_ht; + bool allow_vht; + bool ht40plus; + bool chan_radar; + bool freq2_radar; + bool allow_he; + u32 min_power; + u32 max_power; + u32 max_reg_power; + u32 max_antenna_gain; + enum wmi_phy_mode mode; +}; + +struct wmi_vdev_start_req_arg { + u32 vdev_id; + struct wmi_channel_arg channel; + u32 bcn_intval; + u32 dtim_period; + u8 *ssid; + u32 ssid_len; + u32 bcn_tx_rate; + u32 bcn_tx_power; + bool disable_hw_ack; + bool hidden_ssid; + bool pmf_enabled; + u32 he_ops; + u32 cac_duration_ms; + u32 regdomain; + u32 pref_rx_streams; + u32 pref_tx_streams; + u32 num_noa_descriptors; +}; + +struct peer_create_params { + const u8 *peer_addr; + u32 peer_type; + u32 vdev_id; +}; + +struct peer_delete_params { + u8 vdev_id; +}; + +struct peer_flush_params { + u32 peer_tid_bitmap; + u8 vdev_id; +}; + +struct pdev_set_regdomain_params { + u16 current_rd_in_use; + u16 current_rd_2g; + u16 current_rd_5g; + u32 ctl_2g; + u32 ctl_5g; + u8 dfs_domain; + u32 pdev_id; +}; + +struct rx_reorder_queue_remove_params { + u8 *peer_macaddr; + u16 vdev_id; + u32 peer_tid_bitmap; +}; + +#define WMI_HOST_PDEV_ID_SOC 0xFF +#define WMI_HOST_PDEV_ID_0 0 +#define WMI_HOST_PDEV_ID_1 1 +#define WMI_HOST_PDEV_ID_2 2 + +#define WMI_PDEV_ID_SOC 0 +#define WMI_PDEV_ID_1ST 1 +#define WMI_PDEV_ID_2ND 2 +#define WMI_PDEV_ID_3RD 3 + +/* Freq units in MHz */ +#define REG_RULE_START_FREQ 0x0000ffff +#define REG_RULE_END_FREQ 0xffff0000 +#define REG_RULE_FLAGS 0x0000ffff +#define REG_RULE_MAX_BW 0x0000ffff +#define REG_RULE_REG_PWR 0x00ff0000 +#define REG_RULE_ANT_GAIN 0xff000000 + +#define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0) +#define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1) +#define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2) +#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3) + +#define HECAP_PHYDWORD_0 0 +#define HECAP_PHYDWORD_1 1 +#define HECAP_PHYDWORD_2 2 + +#define HECAP_PHY_SU_BFER BIT(31) +#define HECAP_PHY_SU_BFEE BIT(0) +#define HECAP_PHY_MU_BFER BIT(1) +#define HECAP_PHY_UL_MUMIMO BIT(22) +#define HECAP_PHY_UL_MUOFDMA BIT(23) + +#define HECAP_PHY_SUBFMR_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_SU_BFER, hecap_phy[HECAP_PHYDWORD_0]) + +#define HECAP_PHY_SUBFME_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_SU_BFEE, hecap_phy[HECAP_PHYDWORD_1]) + +#define HECAP_PHY_MUBFMR_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_MU_BFER, hecap_phy[HECAP_PHYDWORD_1]) + +#define HECAP_PHY_ULMUMIMO_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_UL_MUMIMO, hecap_phy[HECAP_PHYDWORD_0]) + +#define HECAP_PHY_ULOFDMA_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_UL_MUOFDMA, hecap_phy[HECAP_PHYDWORD_0]) + +#define HE_MODE_SU_TX_BFEE BIT(0) +#define HE_MODE_SU_TX_BFER BIT(1) +#define HE_MODE_MU_TX_BFEE BIT(2) +#define HE_MODE_MU_TX_BFER BIT(3) +#define HE_MODE_DL_OFDMA BIT(4) +#define HE_MODE_UL_OFDMA BIT(5) +#define HE_MODE_UL_MUMIMO BIT(6) + +#define HE_DL_MUOFDMA_ENABLE 1 +#define HE_UL_MUOFDMA_ENABLE 1 +#define HE_DL_MUMIMO_ENABLE 1 +#define HE_MU_BFEE_ENABLE 1 +#define HE_SU_BFEE_ENABLE 1 + +#define HE_VHT_SOUNDING_MODE_ENABLE 1 +#define HE_SU_MU_SOUNDING_MODE_ENABLE 1 +#define HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE 1 + +/* HE or VHT Sounding */ +#define HE_VHT_SOUNDING_MODE BIT(0) +/* SU or MU Sounding */ +#define HE_SU_MU_SOUNDING_MODE BIT(2) +/* Trig or Non-Trig Sounding */ +#define HE_TRIG_NONTRIG_SOUNDING_MODE BIT(3) + +#define WMI_TXBF_STS_CAP_OFFSET_LSB 4 +#define WMI_TXBF_STS_CAP_OFFSET_MASK 0x70 +#define WMI_BF_SOUND_DIM_OFFSET_LSB 8 +#define WMI_BF_SOUND_DIM_OFFSET_MASK 0x700 + +struct pdev_params { + u32 param_id; + u32 param_value; +}; + +enum wmi_peer_type { + WMI_PEER_TYPE_DEFAULT = 0, + WMI_PEER_TYPE_BSS = 1, + WMI_PEER_TYPE_TDLS = 2, +}; + +struct wmi_peer_create_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 peer_type; +} __packed; + +struct wmi_peer_delete_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_reorder_queue_setup_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 tid; + u32 queue_ptr_lo; + u32 queue_ptr_hi; + u32 queue_no; + u32 ba_window_size_valid; + u32 ba_window_size; +} __packed; + +struct wmi_peer_reorder_queue_remove_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 tid_mask; +} __packed; + +struct gpio_config_params { + u32 gpio_num; + u32 input; + u32 pull_type; + u32 intr_mode; +}; + +enum wmi_gpio_type { + WMI_GPIO_PULL_NONE, + WMI_GPIO_PULL_UP, + WMI_GPIO_PULL_DOWN +}; + +enum wmi_gpio_intr_type { + WMI_GPIO_INTTYPE_DISABLE, + WMI_GPIO_INTTYPE_RISING_EDGE, + WMI_GPIO_INTTYPE_FALLING_EDGE, + WMI_GPIO_INTTYPE_BOTH_EDGE, + WMI_GPIO_INTTYPE_LEVEL_LOW, + WMI_GPIO_INTTYPE_LEVEL_HIGH +}; + +enum wmi_bss_chan_info_req_type { + WMI_BSS_SURVEY_REQ_TYPE_READ = 1, + WMI_BSS_SURVEY_REQ_TYPE_READ_CLEAR, +}; + +struct wmi_gpio_config_cmd_param { + u32 tlv_header; + u32 gpio_num; + u32 input; + u32 pull_type; + u32 intr_mode; +}; + +struct gpio_output_params { + u32 gpio_num; + u32 set; +}; + +struct wmi_gpio_output_cmd_param { + u32 tlv_header; + u32 gpio_num; + u32 set; +}; + +struct set_fwtest_params { + u32 arg; + u32 value; +}; + +struct wmi_fwtest_set_param_cmd_param { + u32 tlv_header; + u32 param_id; + u32 param_value; +}; + +struct wmi_pdev_set_param_cmd { + u32 tlv_header; + u32 pdev_id; + u32 param_id; + u32 param_value; +} __packed; + +struct wmi_pdev_set_ps_mode_cmd { + u32 tlv_header; + u32 vdev_id; + u32 sta_ps_mode; +} __packed; + +struct wmi_pdev_suspend_cmd { + u32 tlv_header; + u32 pdev_id; + u32 suspend_opt; +} __packed; + +struct wmi_pdev_resume_cmd { + u32 tlv_header; + u32 pdev_id; +} __packed; + +struct wmi_pdev_bss_chan_info_req_cmd { + u32 tlv_header; + /* ref wmi_bss_chan_info_req_type */ + u32 req_type; +} __packed; + +struct wmi_ap_ps_peer_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 param; + u32 value; +} __packed; + +struct wmi_sta_powersave_param_cmd { + u32 tlv_header; + u32 vdev_id; + u32 param; + u32 value; +} __packed; + +struct wmi_pdev_set_regdomain_cmd { + u32 tlv_header; + u32 pdev_id; + u32 reg_domain; + u32 reg_domain_2g; + u32 reg_domain_5g; + u32 conformance_test_limit_2g; + u32 conformance_test_limit_5g; + u32 dfs_domain; +} __packed; + +struct wmi_peer_set_param_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 param_id; + u32 param_value; +} __packed; + +struct wmi_peer_flush_tids_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 peer_tid_bitmap; +} __packed; + +struct wmi_dfs_phyerr_offload_cmd { + u32 tlv_header; + u32 pdev_id; +} __packed; + +struct wmi_bcn_offload_ctrl_cmd { + u32 tlv_header; + u32 vdev_id; + u32 bcn_ctrl_op; +} __packed; + +enum scan_dwelltime_adaptive_mode { + SCAN_DWELL_MODE_DEFAULT = 0, + SCAN_DWELL_MODE_CONSERVATIVE = 1, + SCAN_DWELL_MODE_MODERATE = 2, + SCAN_DWELL_MODE_AGGRESSIVE = 3, + SCAN_DWELL_MODE_STATIC = 4 +}; + +#define WLAN_SCAN_MAX_NUM_SSID 10 +#define WLAN_SCAN_MAX_NUM_BSSID 10 +#define WLAN_SCAN_MAX_NUM_CHANNELS 40 + +#define WLAN_SSID_MAX_LEN 32 + +struct element_info { + u32 len; + u8 *ptr; +}; + +struct wlan_ssid { + u8 length; + u8 ssid[WLAN_SSID_MAX_LEN]; +}; + +#define WMI_IE_BITMAP_SIZE 8 + +#define WMI_SCAN_MAX_NUM_SSID 0x0A +/* prefix used by scan requestor ids on the host */ +#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000 + +/* prefix used by scan request ids generated on the host */ +/* host cycles through the lower 12 bits to generate ids */ +#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000 + +#define WLAN_SCAN_PARAMS_MAX_SSID 16 +#define WLAN_SCAN_PARAMS_MAX_BSSID 4 +#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 + +/* Values lower than this may be refused by some firmware revisions with a scan + * completion with a timedout reason. + */ +#define WMI_SCAN_CHAN_MIN_TIME_MSEC 40 + +/* Scan priority numbers must be sequential, starting with 0 */ +enum wmi_scan_priority { + WMI_SCAN_PRIORITY_VERY_LOW = 0, + WMI_SCAN_PRIORITY_LOW, + WMI_SCAN_PRIORITY_MEDIUM, + WMI_SCAN_PRIORITY_HIGH, + WMI_SCAN_PRIORITY_VERY_HIGH, + WMI_SCAN_PRIORITY_COUNT /* number of priorities supported */ +}; + +enum wmi_scan_event_type { + WMI_SCAN_EVENT_STARTED = BIT(0), + WMI_SCAN_EVENT_COMPLETED = BIT(1), + WMI_SCAN_EVENT_BSS_CHANNEL = BIT(2), + WMI_SCAN_EVENT_FOREIGN_CHAN = BIT(3), + WMI_SCAN_EVENT_DEQUEUED = BIT(4), + /* possibly by high-prio scan */ + WMI_SCAN_EVENT_PREEMPTED = BIT(5), + WMI_SCAN_EVENT_START_FAILED = BIT(6), + WMI_SCAN_EVENT_RESTARTED = BIT(7), + WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT = BIT(8), + WMI_SCAN_EVENT_SUSPENDED = BIT(9), + WMI_SCAN_EVENT_RESUMED = BIT(10), + WMI_SCAN_EVENT_MAX = BIT(15), +}; + +enum wmi_scan_completion_reason { + WMI_SCAN_REASON_COMPLETED, + WMI_SCAN_REASON_CANCELLED, + WMI_SCAN_REASON_PREEMPTED, + WMI_SCAN_REASON_TIMEDOUT, + WMI_SCAN_REASON_INTERNAL_FAILURE, + WMI_SCAN_REASON_MAX, +}; + +struct wmi_start_scan_cmd { + u32 tlv_header; + u32 scan_id; + u32 scan_req_id; + u32 vdev_id; + u32 scan_priority; + u32 notify_scan_events; + u32 dwell_time_active; + u32 dwell_time_passive; + u32 min_rest_time; + u32 max_rest_time; + u32 repeat_probe_time; + u32 probe_spacing_time; + u32 idle_time; + u32 max_scan_time; + u32 probe_delay; + u32 scan_ctrl_flags; + u32 burst_duration; + u32 num_chan; + u32 num_bssid; + u32 num_ssids; + u32 ie_len; + u32 n_probes; + struct wmi_mac_addr mac_addr; + struct wmi_mac_addr mac_mask; + u32 ie_bitmap[WMI_IE_BITMAP_SIZE]; + u32 num_vendor_oui; + u32 scan_ctrl_flags_ext; + u32 dwell_time_active_2g; +} __packed; + +#define WMI_SCAN_FLAG_PASSIVE 0x1 +#define WMI_SCAN_ADD_BCAST_PROBE_REQ 0x2 +#define WMI_SCAN_ADD_CCK_RATES 0x4 +#define WMI_SCAN_ADD_OFDM_RATES 0x8 +#define WMI_SCAN_CHAN_STAT_EVENT 0x10 +#define WMI_SCAN_FILTER_PROBE_REQ 0x20 +#define WMI_SCAN_BYPASS_DFS_CHN 0x40 +#define WMI_SCAN_CONTINUE_ON_ERROR 0x80 +#define WMI_SCAN_FILTER_PROMISCUOS 0x100 +#define WMI_SCAN_FLAG_FORCE_ACTIVE_ON_DFS 0x200 +#define WMI_SCAN_ADD_TPC_IE_IN_PROBE_REQ 0x400 +#define WMI_SCAN_ADD_DS_IE_IN_PROBE_REQ 0x800 +#define WMI_SCAN_ADD_SPOOF_MAC_IN_PROBE_REQ 0x1000 +#define WMI_SCAN_OFFCHAN_MGMT_TX 0x2000 +#define WMI_SCAN_OFFCHAN_DATA_TX 0x4000 +#define WMI_SCAN_CAPTURE_PHY_ERROR 0x8000 +#define WMI_SCAN_FLAG_STRICT_PASSIVE_ON_PCHN 0x10000 +#define WMI_SCAN_FLAG_HALF_RATE_SUPPORT 0x20000 +#define WMI_SCAN_FLAG_QUARTER_RATE_SUPPORT 0x40000 +#define WMI_SCAN_RANDOM_SEQ_NO_IN_PROBE_REQ 0x80000 +#define WMI_SCAN_ENABLE_IE_WHTELIST_IN_PROBE_REQ 0x100000 + +#define WMI_SCAN_DWELL_MODE_MASK 0x00E00000 +#define WMI_SCAN_DWELL_MODE_SHIFT 21 + +enum { + WMI_SCAN_DWELL_MODE_DEFAULT = 0, + WMI_SCAN_DWELL_MODE_CONSERVATIVE = 1, + WMI_SCAN_DWELL_MODE_MODERATE = 2, + WMI_SCAN_DWELL_MODE_AGGRESSIVE = 3, + WMI_SCAN_DWELL_MODE_STATIC = 4, +}; + +#define WMI_SCAN_SET_DWELL_MODE(flag, mode) \ + ((flag) |= (((mode) << WMI_SCAN_DWELL_MODE_SHIFT) & \ + WMI_SCAN_DWELL_MODE_MASK)) + +struct scan_req_params { + u32 scan_id; + u32 scan_req_id; + u32 vdev_id; + u32 pdev_id; + enum wmi_scan_priority scan_priority; + union { + struct { + u32 scan_ev_started:1, + scan_ev_completed:1, + scan_ev_bss_chan:1, + scan_ev_foreign_chan:1, + scan_ev_dequeued:1, + scan_ev_preempted:1, + scan_ev_start_failed:1, + scan_ev_restarted:1, + scan_ev_foreign_chn_exit:1, + scan_ev_invalid:1, + scan_ev_gpio_timeout:1, + scan_ev_suspended:1, + scan_ev_resumed:1; + }; + u32 scan_events; + }; + u32 dwell_time_active; + u32 dwell_time_active_2g; + u32 dwell_time_passive; + u32 min_rest_time; + u32 max_rest_time; + u32 repeat_probe_time; + u32 probe_spacing_time; + u32 idle_time; + u32 max_scan_time; + u32 probe_delay; + union { + struct { + u32 scan_f_passive:1, + scan_f_bcast_probe:1, + scan_f_cck_rates:1, + scan_f_ofdm_rates:1, + scan_f_chan_stat_evnt:1, + scan_f_filter_prb_req:1, + scan_f_bypass_dfs_chn:1, + scan_f_continue_on_err:1, + scan_f_offchan_mgmt_tx:1, + scan_f_offchan_data_tx:1, + scan_f_promisc_mode:1, + scan_f_capture_phy_err:1, + scan_f_strict_passive_pch:1, + scan_f_half_rate:1, + scan_f_quarter_rate:1, + scan_f_force_active_dfs_chn:1, + scan_f_add_tpc_ie_in_probe:1, + scan_f_add_ds_ie_in_probe:1, + scan_f_add_spoofed_mac_in_probe:1, + scan_f_add_rand_seq_in_probe:1, + scan_f_en_ie_whitelist_in_probe:1, + scan_f_forced:1, + scan_f_2ghz:1, + scan_f_5ghz:1, + scan_f_80mhz:1; + }; + u32 scan_flags; + }; + enum scan_dwelltime_adaptive_mode adaptive_dwell_time_mode; + u32 burst_duration; + u32 num_chan; + u32 num_bssid; + u32 num_ssids; + u32 n_probes; + u32 chan_list[WLAN_SCAN_MAX_NUM_CHANNELS]; + u32 notify_scan_events; + struct wlan_ssid ssid[WLAN_SCAN_MAX_NUM_SSID]; + struct wmi_mac_addr bssid_list[WLAN_SCAN_MAX_NUM_BSSID]; + struct element_info extraie; + struct element_info htcap; + struct element_info vhtcap; +}; + +struct wmi_ssid_arg { + int len; + const u8 *ssid; +}; + +struct wmi_bssid_arg { + const u8 *bssid; +}; + +struct wmi_start_scan_arg { + u32 scan_id; + u32 scan_req_id; + u32 vdev_id; + u32 scan_priority; + u32 notify_scan_events; + u32 dwell_time_active; + u32 dwell_time_passive; + u32 min_rest_time; + u32 max_rest_time; + u32 repeat_probe_time; + u32 probe_spacing_time; + u32 idle_time; + u32 max_scan_time; + u32 probe_delay; + u32 scan_ctrl_flags; + + u32 ie_len; + u32 n_channels; + u32 n_ssids; + u32 n_bssids; + + u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN]; + u32 channels[64]; + struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID]; + struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID]; +}; + +#define WMI_SCAN_STOP_ONE 0x00000000 +#define WMI_SCN_STOP_VAP_ALL 0x01000000 +#define WMI_SCAN_STOP_ALL 0x04000000 + +/* Prefix 0xA000 indicates that the scan request + * is trigger by HOST + */ +#define ATH11K_SCAN_ID 0xA000 + +enum scan_cancel_req_type { + WLAN_SCAN_CANCEL_SINGLE = 1, + WLAN_SCAN_CANCEL_VDEV_ALL, + WLAN_SCAN_CANCEL_PDEV_ALL, +}; + +struct scan_cancel_param { + u32 requester; + u32 scan_id; + enum scan_cancel_req_type req_type; + u32 vdev_id; + u32 pdev_id; +}; + +struct wmi_bcn_send_from_host_cmd { + u32 tlv_header; + u32 vdev_id; + u32 data_len; + union { + u32 frag_ptr; + u32 frag_ptr_lo; + }; + u32 frame_ctrl; + u32 dtim_flag; + u32 bcn_antenna; + u32 frag_ptr_hi; +}; + +#define WMI_CHAN_INFO_MODE GENMASK(5, 0) +#define WMI_CHAN_INFO_HT40_PLUS BIT(6) +#define WMI_CHAN_INFO_PASSIVE BIT(7) +#define WMI_CHAN_INFO_ADHOC_ALLOWED BIT(8) +#define WMI_CHAN_INFO_AP_DISABLED BIT(9) +#define WMI_CHAN_INFO_DFS BIT(10) +#define WMI_CHAN_INFO_ALLOW_HT BIT(11) +#define WMI_CHAN_INFO_ALLOW_VHT BIT(12) +#define WMI_CHAN_INFO_CHAN_CHANGE_CAUSE_CSA BIT(13) +#define WMI_CHAN_INFO_HALF_RATE BIT(14) +#define WMI_CHAN_INFO_QUARTER_RATE BIT(15) +#define WMI_CHAN_INFO_DFS_FREQ2 BIT(16) +#define WMI_CHAN_INFO_ALLOW_HE BIT(17) + +#define WMI_CHAN_REG_INFO1_MIN_PWR GENMASK(7, 0) +#define WMI_CHAN_REG_INFO1_MAX_PWR GENMASK(15, 8) +#define WMI_CHAN_REG_INFO1_MAX_REG_PWR GENMASK(23, 16) +#define WMI_CHAN_REG_INFO1_REG_CLS GENMASK(31, 24) + +#define WMI_CHAN_REG_INFO2_ANT_MAX GENMASK(7, 0) +#define WMI_CHAN_REG_INFO2_MAX_TX_PWR GENMASK(15, 8) + +struct wmi_channel { + u32 tlv_header; + u32 mhz; + u32 band_center_freq1; + u32 band_center_freq2; + u32 info; + u32 reg_info_1; + u32 reg_info_2; +} __packed; + +struct wmi_mgmt_params { + void *tx_frame; + u16 frm_len; + u8 vdev_id; + u16 chanfreq; + void *pdata; + u16 desc_id; + u8 *macaddr; + void *qdf_ctx; +}; + +enum wmi_sta_ps_mode { + WMI_STA_PS_MODE_DISABLED = 0, + WMI_STA_PS_MODE_ENABLED = 1, +}; + +#define WMI_SMPS_MASK_LOWER_16BITS 0xFF +#define WMI_SMPS_MASK_UPPER_3BITS 0x7 +#define WMI_SMPS_PARAM_VALUE_SHIFT 29 + +#define ATH11K_WMI_FW_HANG_ASSERT_TYPE 1 +#define ATH11K_WMI_FW_HANG_DELAY 0 + +/* type, 0:unused 1: ASSERT 2: not respond detect command + * delay_time_ms, the simulate will delay time + */ + +struct wmi_force_fw_hang_cmd { + u32 tlv_header; + u32 type; + u32 delay_time_ms; +}; + +struct wmi_vdev_set_param_cmd { + u32 tlv_header; + u32 vdev_id; + u32 param_id; + u32 param_value; +} __packed; + +enum wmi_stats_id { + WMI_REQUEST_PEER_STAT = BIT(0), + WMI_REQUEST_AP_STAT = BIT(1), + WMI_REQUEST_PDEV_STAT = BIT(2), + WMI_REQUEST_VDEV_STAT = BIT(3), + WMI_REQUEST_BCNFLT_STAT = BIT(4), + WMI_REQUEST_VDEV_RATE_STAT = BIT(5), + WMI_REQUEST_INST_STAT = BIT(6), + WMI_REQUEST_MIB_STAT = BIT(7), + WMI_REQUEST_RSSI_PER_CHAIN_STAT = BIT(8), + WMI_REQUEST_CONGESTION_STAT = BIT(9), + WMI_REQUEST_PEER_EXTD_STAT = BIT(10), + WMI_REQUEST_BCN_STAT = BIT(11), + WMI_REQUEST_BCN_STAT_RESET = BIT(12), + WMI_REQUEST_PEER_EXTD2_STAT = BIT(13), +}; + +struct wmi_request_stats_cmd { + u32 tlv_header; + enum wmi_stats_id stats_id; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 pdev_id; +} __packed; + +#define WMI_BEACON_TX_BUFFER_SIZE 512 + +struct wmi_bcn_tmpl_cmd { + u32 tlv_header; + u32 vdev_id; + u32 tim_ie_offset; + u32 buf_len; + u32 csa_switch_count_offset; + u32 ext_csa_switch_count_offset; + u32 csa_event_bitmap; + u32 mbssid_ie_offset; + u32 esp_ie_offset; +} __packed; + +struct wmi_key_seq_counter { + u32 key_seq_counter_l; + u32 key_seq_counter_h; +} __packed; + +struct wmi_vdev_install_key_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 key_idx; + u32 key_flags; + u32 key_cipher; + struct wmi_key_seq_counter key_rsc_counter; + struct wmi_key_seq_counter key_global_rsc_counter; + struct wmi_key_seq_counter key_tsc_counter; + u8 wpi_key_rsc_counter[16]; + u8 wpi_key_tsc_counter[16]; + u32 key_len; + u32 key_txmic_len; + u32 key_rxmic_len; + u32 is_group_key_id_valid; + u32 group_key_id; + + /* Followed by key_data containing key followed by + * tx mic and then rx mic + */ +} __packed; + +struct wmi_vdev_install_key_arg { + u32 vdev_id; + const u8 *macaddr; + u32 key_idx; + u32 key_flags; + u32 key_cipher; + u32 key_len; + u32 key_txmic_len; + u32 key_rxmic_len; + u64 key_rsc_counter; + const void *key_data; +}; + +#define WMI_MAX_SUPPORTED_RATES 128 +#define WMI_HOST_MAX_HECAP_PHY_SIZE 3 +#define WMI_HOST_MAX_HE_RATE_SET 3 +#define WMI_HECAP_TXRX_MCS_NSS_IDX_80 0 +#define WMI_HECAP_TXRX_MCS_NSS_IDX_160 1 +#define WMI_HECAP_TXRX_MCS_NSS_IDX_80_80 2 + +struct wmi_rate_set_arg { + u32 num_rates; + u8 rates[WMI_MAX_SUPPORTED_RATES]; +}; + +struct peer_assoc_params { + struct wmi_mac_addr peer_macaddr; + u32 vdev_id; + u32 peer_new_assoc; + u32 peer_associd; + u32 peer_flags; + u32 peer_caps; + u32 peer_listen_intval; + u32 peer_ht_caps; + u32 peer_max_mpdu; + u32 peer_mpdu_density; + u32 peer_rate_caps; + u32 peer_nss; + u32 peer_vht_caps; + u32 peer_phymode; + u32 peer_ht_info[2]; + struct wmi_rate_set_arg peer_legacy_rates; + struct wmi_rate_set_arg peer_ht_rates; + u32 rx_max_rate; + u32 rx_mcs_set; + u32 tx_max_rate; + u32 tx_mcs_set; + u8 vht_capable; + u32 tx_max_mcs_nss; + u32 peer_bw_rxnss_override; + bool is_pmf_enabled; + bool is_wme_set; + bool qos_flag; + bool apsd_flag; + bool ht_flag; + bool bw_40; + bool bw_80; + bool bw_160; + bool stbc_flag; + bool ldpc_flag; + bool static_mimops_flag; + bool dynamic_mimops_flag; + bool spatial_mux_flag; + bool vht_flag; + bool vht_ng_flag; + bool need_ptk_4_way; + bool need_gtk_2_way; + bool auth_flag; + bool safe_mode_enabled; + bool amsdu_disable; + /* Use common structure */ + u8 peer_mac[ETH_ALEN]; + + bool he_flag; + u32 peer_he_cap_macinfo[2]; + u32 peer_he_cap_macinfo_internal; + u32 peer_he_ops; + u32 peer_he_cap_phyinfo[WMI_HOST_MAX_HECAP_PHY_SIZE]; + u32 peer_he_mcs_count; + u32 peer_he_rx_mcs_set[WMI_HOST_MAX_HE_RATE_SET]; + u32 peer_he_tx_mcs_set[WMI_HOST_MAX_HE_RATE_SET]; + bool twt_responder; + bool twt_requester; + struct ath11k_ppe_threshold peer_ppet; +}; + +struct wmi_peer_assoc_complete_cmd { + u32 tlv_header; + struct wmi_mac_addr peer_macaddr; + u32 vdev_id; + u32 peer_new_assoc; + u32 peer_associd; + u32 peer_flags; + u32 peer_caps; + u32 peer_listen_intval; + u32 peer_ht_caps; + u32 peer_max_mpdu; + u32 peer_mpdu_density; + u32 peer_rate_caps; + u32 peer_nss; + u32 peer_vht_caps; + u32 peer_phymode; + u32 peer_ht_info[2]; + u32 num_peer_legacy_rates; + u32 num_peer_ht_rates; + u32 peer_bw_rxnss_override; + struct wmi_ppe_threshold peer_ppet; + u32 peer_he_cap_info; + u32 peer_he_ops; + u32 peer_he_cap_phy[WMI_MAX_HECAP_PHY_SIZE]; + u32 peer_he_mcs; + u32 peer_he_cap_info_ext; + u32 peer_he_cap_info_internal; +} __packed; + +struct wmi_stop_scan_cmd { + u32 tlv_header; + u32 requestor; + u32 scan_id; + u32 req_type; + u32 vdev_id; + u32 pdev_id; +}; + +struct scan_chan_list_params { + u32 pdev_id; + u16 nallchans; + struct channel_param ch_param[1]; +}; + +struct wmi_scan_chan_list_cmd { + u32 tlv_header; + u32 num_scan_chans; + u32 flags; + u32 pdev_id; +} __packed; + +#define WMI_MGMT_SEND_DOWNLD_LEN 64 + +#define WMI_TX_PARAMS_DWORD0_POWER GENMASK(7, 0) +#define WMI_TX_PARAMS_DWORD0_MCS_MASK GENMASK(19, 8) +#define WMI_TX_PARAMS_DWORD0_NSS_MASK GENMASK(27, 20) +#define WMI_TX_PARAMS_DWORD0_RETRY_LIMIT GENMASK(31, 28) + +#define WMI_TX_PARAMS_DWORD1_CHAIN_MASK GENMASK(7, 0) +#define WMI_TX_PARAMS_DWORD1_BW_MASK GENMASK(14, 8) +#define WMI_TX_PARAMS_DWORD1_PREAMBLE_TYPE GENMASK(19, 15) +#define WMI_TX_PARAMS_DWORD1_FRAME_TYPE BIT(20) +#define WMI_TX_PARAMS_DWORD1_RSVD GENMASK(31, 21) + +struct wmi_mgmt_send_params { + u32 tlv_header; + u32 tx_params_dword0; + u32 tx_params_dword1; +}; + +struct wmi_mgmt_send_cmd { + u32 tlv_header; + u32 vdev_id; + u32 desc_id; + u32 chanfreq; + u32 paddr_lo; + u32 paddr_hi; + u32 frame_len; + u32 buf_len; + u32 tx_params_valid; + + /* This TLV is followed by struct wmi_mgmt_frame */ + + /* Followed by struct wmi_mgmt_send_params */ +} __packed; + +struct wmi_sta_powersave_mode_cmd { + u32 tlv_header; + u32 vdev_id; + u32 sta_ps_mode; +}; + +struct wmi_sta_smps_force_mode_cmd { + u32 tlv_header; + u32 vdev_id; + u32 forced_mode; +}; + +struct wmi_sta_smps_param_cmd { + u32 tlv_header; + u32 vdev_id; + u32 param; + u32 value; +}; + +struct wmi_bcn_prb_info { + u32 tlv_header; + u32 caps; + u32 erp; +} __packed; + +enum { + WMI_PDEV_SUSPEND, + WMI_PDEV_SUSPEND_AND_DISABLE_INTR, +}; + +struct green_ap_ps_params { + u32 value; +}; + +struct wmi_pdev_green_ap_ps_enable_cmd_param { + u32 tlv_header; + u32 pdev_id; + u32 enable; +}; + +struct ap_ps_params { + u32 vdev_id; + u32 param; + u32 value; +}; + +struct vdev_set_params { + u32 if_id; + u32 param_id; + u32 param_value; +}; + +struct stats_request_params { + u32 stats_id; + u32 vdev_id; + u32 pdev_id; +}; + +enum set_init_cc_type { + WMI_COUNTRY_INFO_TYPE_ALPHA, + WMI_COUNTRY_INFO_TYPE_COUNTRY_CODE, + WMI_COUNTRY_INFO_TYPE_REGDOMAIN, +}; + +enum set_init_cc_flags { + INVALID_CC, + CC_IS_SET, + REGDMN_IS_SET, + ALPHA_IS_SET, +}; + +struct wmi_init_country_params { + union { + u16 country_code; + u16 regdom_id; + u8 alpha2[3]; + } cc_info; + enum set_init_cc_flags flags; +}; + +struct wmi_init_country_cmd { + u32 tlv_header; + u32 pdev_id; + u32 init_cc_type; + union { + u32 country_code; + u32 regdom_id; + u32 alpha2; + } cc_info; +} __packed; + +struct wmi_pdev_pktlog_filter_info { + u32 tlv_header; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_pdev_pktlog_filter_cmd { + u32 tlv_header; + u32 pdev_id; + u32 enable; + u32 filter_type; + u32 num_mac; +} __packed; + +enum ath11k_wmi_pktlog_enable { + ATH11K_WMI_PKTLOG_ENABLE_AUTO = 0, + ATH11K_WMI_PKTLOG_ENABLE_FORCE = 1, +}; + +struct wmi_pktlog_enable_cmd { + u32 tlv_header; + u32 pdev_id; + u32 evlist; /* WMI_PKTLOG_EVENT */ + u32 enable; +} __packed; + +struct wmi_pktlog_disable_cmd { + u32 tlv_header; + u32 pdev_id; +} __packed; + +#define DFS_PHYERR_UNIT_TEST_CMD 0 +#define DFS_UNIT_TEST_MODULE 0x2b +#define DFS_UNIT_TEST_TOKEN 0xAA + +enum dfs_test_args_idx { + DFS_TEST_CMDID = 0, + DFS_TEST_PDEV_ID, + DFS_TEST_RADAR_PARAM, + DFS_MAX_TEST_ARGS, +}; + +struct wmi_dfs_unit_test_arg { + u32 cmd_id; + u32 pdev_id; + u32 radar_param; +}; + +struct wmi_unit_test_cmd { + u32 tlv_header; + u32 vdev_id; + u32 module_id; + u32 num_args; + u32 diag_token; + /* Followed by test args*/ +} __packed; + +#define MAX_SUPPORTED_RATES 128 + +#define WMI_PEER_AUTH 0x00000001 +#define WMI_PEER_QOS 0x00000002 +#define WMI_PEER_NEED_PTK_4_WAY 0x00000004 +#define WMI_PEER_NEED_GTK_2_WAY 0x00000010 +#define WMI_PEER_HE 0x00000400 +#define WMI_PEER_APSD 0x00000800 +#define WMI_PEER_HT 0x00001000 +#define WMI_PEER_40MHZ 0x00002000 +#define WMI_PEER_STBC 0x00008000 +#define WMI_PEER_LDPC 0x00010000 +#define WMI_PEER_DYN_MIMOPS 0x00020000 +#define WMI_PEER_STATIC_MIMOPS 0x00040000 +#define WMI_PEER_SPATIAL_MUX 0x00200000 +#define WMI_PEER_TWT_REQ 0x00400000 +#define WMI_PEER_TWT_RESP 0x00800000 +#define WMI_PEER_VHT 0x02000000 +#define WMI_PEER_80MHZ 0x04000000 +#define WMI_PEER_PMF 0x08000000 +/* TODO: Place holder for WLAN_PEER_F_PS_PRESEND_REQUIRED = 0x10000000. + * Need to be cleaned up + */ +#define WMI_PEER_IS_P2P_CAPABLE 0x20000000 +#define WMI_PEER_160MHZ 0x40000000 +#define WMI_PEER_SAFEMODE_EN 0x80000000 + +struct beacon_tmpl_params { + u8 vdev_id; + u32 tim_ie_offset; + u32 tmpl_len; + u32 tmpl_len_aligned; + u32 csa_switch_count_offset; + u32 ext_csa_switch_count_offset; + u8 *frm; +}; + +struct wmi_rate_set { + u32 num_rates; + u32 rates[(MAX_SUPPORTED_RATES / 4) + 1]; +}; + +struct wmi_vht_rate_set { + u32 tlv_header; + u32 rx_max_rate; + u32 rx_mcs_set; + u32 tx_max_rate; + u32 tx_mcs_set; + u32 tx_max_mcs_nss; +} __packed; + +struct wmi_he_rate_set { + u32 tlv_header; + u32 rx_mcs_set; + u32 tx_mcs_set; +} __packed; + +#define MAX_REG_RULES 10 +#define REG_ALPHA2_LEN 2 + +enum wmi_start_event_param { + WMI_VDEV_START_RESP_EVENT = 0, + WMI_VDEV_RESTART_RESP_EVENT, +}; + +struct wmi_vdev_start_resp_event { + u32 vdev_id; + u32 requestor_id; + enum wmi_start_event_param resp_type; + u32 status; + u32 chain_mask; + u32 smps_mode; + union { + u32 mac_id; + u32 pdev_id; + }; + u32 cfgd_tx_streams; + u32 cfgd_rx_streams; +} __packed; + +/* VDEV start response status codes */ +enum wmi_vdev_start_resp_status_code { + WMI_VDEV_START_RESPONSE_STATUS_SUCCESS = 0, + WMI_VDEV_START_RESPONSE_INVALID_VDEVID = 1, + WMI_VDEV_START_RESPONSE_NOT_SUPPORTED = 2, + WMI_VDEV_START_RESPONSE_DFS_VIOLATION = 3, + WMI_VDEV_START_RESPONSE_INVALID_REGDOMAIN = 4, +}; + +; +enum cc_setting_code { + REG_SET_CC_STATUS_PASS = 0, + REG_CURRENT_ALPHA2_NOT_FOUND = 1, + REG_INIT_ALPHA2_NOT_FOUND = 2, + REG_SET_CC_CHANGE_NOT_ALLOWED = 3, + REG_SET_CC_STATUS_NO_MEMORY = 4, + REG_SET_CC_STATUS_FAIL = 5, +}; + +/* Regaulatory Rule Flags Passed by FW */ +#define REGULATORY_CHAN_DISABLED BIT(0) +#define REGULATORY_CHAN_NO_IR BIT(1) +#define REGULATORY_CHAN_RADAR BIT(3) +#define REGULATORY_CHAN_NO_OFDM BIT(6) +#define REGULATORY_CHAN_INDOOR_ONLY BIT(9) + +#define REGULATORY_CHAN_NO_HT40 BIT(4) +#define REGULATORY_CHAN_NO_80MHZ BIT(7) +#define REGULATORY_CHAN_NO_160MHZ BIT(8) +#define REGULATORY_CHAN_NO_20MHZ BIT(11) +#define REGULATORY_CHAN_NO_10MHZ BIT(12) + +enum { + WMI_REG_SET_CC_STATUS_PASS = 0, + WMI_REG_CURRENT_ALPHA2_NOT_FOUND = 1, + WMI_REG_INIT_ALPHA2_NOT_FOUND = 2, + WMI_REG_SET_CC_CHANGE_NOT_ALLOWED = 3, + WMI_REG_SET_CC_STATUS_NO_MEMORY = 4, + WMI_REG_SET_CC_STATUS_FAIL = 5, +}; + +struct cur_reg_rule { + u16 start_freq; + u16 end_freq; + u16 max_bw; + u8 reg_power; + u8 ant_gain; + u16 flags; +}; + +struct cur_regulatory_info { + enum cc_setting_code status_code; + u8 num_phy; + u8 phy_id; + u16 reg_dmn_pair; + u16 ctry_code; + u8 alpha2[REG_ALPHA2_LEN + 1]; + u32 dfs_region; + u32 phybitmap; + u32 min_bw_2g; + u32 max_bw_2g; + u32 min_bw_5g; + u32 max_bw_5g; + u32 num_2g_reg_rules; + u32 num_5g_reg_rules; + struct cur_reg_rule *reg_rules_2g_ptr; + struct cur_reg_rule *reg_rules_5g_ptr; +}; + +struct wmi_reg_chan_list_cc_event { + u32 status_code; + u32 phy_id; + u32 alpha2; + u32 num_phy; + u32 country_id; + u32 domain_code; + u32 dfs_region; + u32 phybitmap; + u32 min_bw_2g; + u32 max_bw_2g; + u32 min_bw_5g; + u32 max_bw_5g; + u32 num_2g_reg_rules; + u32 num_5g_reg_rules; +} __packed; + +struct wmi_regulatory_rule_struct { + u32 tlv_header; + u32 freq_info; + u32 bw_pwr_info; + u32 flag_info; +}; + +struct wmi_peer_delete_resp_event { + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_bcn_tx_status_event { + u32 vdev_id; + u32 tx_status; +} __packed; + +struct wmi_vdev_stopped_event { + u32 vdev_id; +} __packed; + +struct wmi_pdev_bss_chan_info_event { + u32 pdev_id; + u32 freq; /* Units in MHz */ + u32 noise_floor; /* units are dBm */ + /* rx clear - how often the channel was unused */ + u32 rx_clear_count_low; + u32 rx_clear_count_high; + /* cycle count - elapsed time during measured period, in clock ticks */ + u32 cycle_count_low; + u32 cycle_count_high; + /* tx cycle count - elapsed time spent in tx, in clock ticks */ + u32 tx_cycle_count_low; + u32 tx_cycle_count_high; + /* rx cycle count - elapsed time spent in rx, in clock ticks */ + u32 rx_cycle_count_low; + u32 rx_cycle_count_high; + /*rx_cycle cnt for my bss in 64bits format */ + u32 rx_bss_cycle_count_low; + u32 rx_bss_cycle_count_high; +} __packed; + +#define WMI_VDEV_INSTALL_KEY_COMPL_STATUS_SUCCESS 0 + +struct wmi_vdev_install_key_compl_event { + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 key_idx; + u32 key_flags; + u32 status; +} __packed; + +struct wmi_vdev_install_key_complete_arg { + u32 vdev_id; + const u8 *macaddr; + u32 key_idx; + u32 key_flags; + u32 status; +}; + +struct wmi_peer_assoc_conf_event { + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_assoc_conf_arg { + u32 vdev_id; + const u8 *macaddr; +}; + +/* + * PDEV statistics + */ +struct wmi_pdev_stats_base { + s32 chan_nf; + u32 tx_frame_count; /* Cycles spent transmitting frames */ + u32 rx_frame_count; /* Cycles spent receiving frames */ + u32 rx_clear_count; /* Total channel busy time, evidently */ + u32 cycle_count; /* Total on-channel time */ + u32 phy_err_count; + u32 chan_tx_pwr; +} __packed; + +struct wmi_pdev_stats_extra { + u32 ack_rx_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 no_beacons; + u32 mib_int_count; +} __packed; + +struct wmi_pdev_stats_tx { + /* Num HTT cookies queued to dispatch list */ + s32 comp_queued; + + /* Num HTT cookies dispatched */ + s32 comp_delivered; + + /* Num MSDU queued to WAL */ + s32 msdu_enqued; + + /* Num MPDU queue to WAL */ + s32 mpdu_enqued; + + /* Num MSDUs dropped by WMM limit */ + s32 wmm_drop; + + /* Num Local frames queued */ + s32 local_enqued; + + /* Num Local frames done */ + s32 local_freed; + + /* Num queued to HW */ + s32 hw_queued; + + /* Num PPDU reaped from HW */ + s32 hw_reaped; + + /* Num underruns */ + s32 underrun; + + /* Num PPDUs cleaned up in TX abort */ + s32 tx_abort; + + /* Num MPDUs requed by SW */ + s32 mpdus_requed; + + /* excessive retries */ + u32 tx_ko; + + /* data hw rate code */ + u32 data_rc; + + /* Scheduler self triggers */ + u32 self_triggers; + + /* frames dropped due to excessive sw retries */ + u32 sw_retry_failure; + + /* illegal rate phy errors */ + u32 illgl_rate_phy_err; + + /* wal pdev continuous xretry */ + u32 pdev_cont_xretry; + + /* wal pdev tx timeouts */ + u32 pdev_tx_timeout; + + /* wal pdev resets */ + u32 pdev_resets; + + /* frames dropped due to non-availability of stateless TIDs */ + u32 stateless_tid_alloc_failure; + + /* PhY/BB underrun */ + u32 phy_underrun; + + /* MPDU is more than txop limit */ + u32 txop_ovf; +} __packed; + +struct wmi_pdev_stats_rx { + /* Cnts any change in ring routing mid-ppdu */ + s32 mid_ppdu_route_change; + + /* Total number of statuses processed */ + s32 status_rcvd; + + /* Extra frags on rings 0-3 */ + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + + /* MSDUs / MPDUs delivered to HTT */ + s32 htt_msdus; + s32 htt_mpdus; + + /* MSDUs / MPDUs delivered to local stack */ + s32 loc_msdus; + s32 loc_mpdus; + + /* AMSDUs that have more MSDUs than the status ring size */ + s32 oversize_amsdu; + + /* Number of PHY errors */ + s32 phy_errs; + + /* Number of PHY errors drops */ + s32 phy_err_drop; + + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + s32 mpdu_errs; +} __packed; + +struct wmi_pdev_stats { + struct wmi_pdev_stats_base base; + struct wmi_pdev_stats_tx tx; + struct wmi_pdev_stats_rx rx; +} __packed; + +#define WLAN_MAX_AC 4 +#define MAX_TX_RATE_VALUES 10 +#define MAX_TX_RATE_VALUES 10 + +struct wmi_vdev_stats { + u32 vdev_id; + u32 beacon_snr; + u32 data_snr; + u32 num_tx_frames[WLAN_MAX_AC]; + u32 num_rx_frames; + u32 num_tx_frames_retries[WLAN_MAX_AC]; + u32 num_tx_frames_failures[WLAN_MAX_AC]; + u32 num_rts_fail; + u32 num_rts_success; + u32 num_rx_err; + u32 num_rx_discard; + u32 num_tx_not_acked; + u32 tx_rate_history[MAX_TX_RATE_VALUES]; + u32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +} __packed; + +struct wmi_bcn_stats { + u32 vdev_id; + u32 tx_bcn_succ_cnt; + u32 tx_bcn_outage_cnt; +} __packed; + +struct wmi_stats_event { + u32 stats_id; + u32 num_pdev_stats; + u32 num_vdev_stats; + u32 num_peer_stats; + u32 num_bcnflt_stats; + u32 num_chan_stats; + u32 num_mib_stats; + u32 pdev_id; + u32 num_bcn_stats; + u32 num_peer_extd_stats; + u32 num_peer_extd2_stats; +} __packed; + +struct wmi_pdev_ctl_failsafe_chk_event { + u32 pdev_id; + u32 ctl_failsafe_status; +} __packed; + +struct wmi_pdev_csa_switch_ev { + u32 pdev_id; + u32 current_switch_count; + u32 num_vdevs; +} __packed; + +struct wmi_pdev_radar_ev { + u32 pdev_id; + u32 detection_mode; + u32 chan_freq; + u32 chan_width; + u32 detector_id; + u32 segment_id; + u32 timestamp; + u32 is_chirp; + s32 freq_offset; + s32 sidx; +} __packed; + +#define WMI_RX_STATUS_OK 0x00 +#define WMI_RX_STATUS_ERR_CRC 0x01 +#define WMI_RX_STATUS_ERR_DECRYPT 0x08 +#define WMI_RX_STATUS_ERR_MIC 0x10 +#define WMI_RX_STATUS_ERR_KEY_CACHE_MISS 0x20 + +#define WLAN_MGMT_TXRX_HOST_MAX_ANTENNA 4 + +struct mgmt_rx_event_params { + u32 channel; + u32 snr; + u8 rssi_ctl[WLAN_MGMT_TXRX_HOST_MAX_ANTENNA]; + u32 rate; + enum wmi_phy_mode phy_mode; + u32 buf_len; + int status; + u32 flags; + int rssi; + u32 tsf_delta; + u8 pdev_id; +}; + +#define ATH_MAX_ANTENNA 4 + +struct wmi_mgmt_rx_hdr { + u32 channel; + u32 snr; + u32 rate; + u32 phy_mode; + u32 buf_len; + u32 status; + u32 rssi_ctl[ATH_MAX_ANTENNA]; + u32 flags; + int rssi; + u32 tsf_delta; + u32 rx_tsf_l32; + u32 rx_tsf_u32; + u32 pdev_id; +} __packed; + +#define MAX_ANTENNA_EIGHT 8 + +struct wmi_rssi_ctl_ext { + u32 tlv_header; + u32 rssi_ctl_ext[MAX_ANTENNA_EIGHT - ATH_MAX_ANTENNA]; +}; + +struct wmi_mgmt_tx_compl_event { + u32 desc_id; + u32 status; + u32 pdev_id; +} __packed; + +struct wmi_scan_event { + u32 event_type; /* %WMI_SCAN_EVENT_ */ + u32 reason; /* %WMI_SCAN_REASON_ */ + u32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */ + u32 scan_req_id; + u32 scan_id; + u32 vdev_id; + /* TSF Timestamp when the scan event (%WMI_SCAN_EVENT_) is completed + * In case of AP it is TSF of the AP vdev + * In case of STA connected state, this is the TSF of the AP + * In case of STA not connected, it will be the free running HW timer + */ + u32 tsf_timestamp; +} __packed; + +struct wmi_peer_sta_kickout_arg { + const u8 *mac_addr; +}; + +struct wmi_peer_sta_kickout_event { + struct wmi_mac_addr peer_macaddr; +} __packed; + +enum wmi_roam_reason { + WMI_ROAM_REASON_BETTER_AP = 1, + WMI_ROAM_REASON_BEACON_MISS = 2, + WMI_ROAM_REASON_LOW_RSSI = 3, + WMI_ROAM_REASON_SUITABLE_AP_FOUND = 4, + WMI_ROAM_REASON_HO_FAILED = 5, + + /* keep last */ + WMI_ROAM_REASON_MAX, +}; + +struct wmi_roam_event { + u32 vdev_id; + u32 reason; + u32 rssi; +} __packed; + +#define WMI_CHAN_INFO_START_RESP 0 +#define WMI_CHAN_INFO_END_RESP 1 + +struct wmi_chan_info_event { + u32 err_code; + u32 freq; + u32 cmd_flags; + u32 noise_floor; + u32 rx_clear_count; + u32 cycle_count; + u32 chan_tx_pwr_range; + u32 chan_tx_pwr_tp; + u32 rx_frame_count; + u32 my_bss_rx_cycle_count; + u32 rx_11b_mode_data_duration; + u32 tx_frame_cnt; + u32 mac_clk_mhz; + u32 vdev_id; +} __packed; + +struct ath11k_targ_cap { + u32 phy_capability; + u32 max_frag_entry; + u32 num_rf_chains; + u32 ht_cap_info; + u32 vht_cap_info; + u32 vht_supp_mcs; + u32 hw_min_tx_power; + u32 hw_max_tx_power; + u32 sys_cap_info; + u32 min_pkt_size_enable; + u32 max_bcn_ie_size; + u32 max_num_scan_channels; + u32 max_supported_macs; + u32 wmi_fw_sub_feat_caps; + u32 txrx_chainmask; + u32 default_dbs_hw_mode_index; + u32 num_msdu_desc; +}; + +enum wmi_vdev_type { + WMI_VDEV_TYPE_AP = 1, + WMI_VDEV_TYPE_STA = 2, + WMI_VDEV_TYPE_IBSS = 3, + WMI_VDEV_TYPE_MONITOR = 4, +}; + +enum wmi_vdev_subtype { + WMI_VDEV_SUBTYPE_NONE, + WMI_VDEV_SUBTYPE_P2P_DEVICE, + WMI_VDEV_SUBTYPE_P2P_CLIENT, + WMI_VDEV_SUBTYPE_P2P_GO, + WMI_VDEV_SUBTYPE_PROXY_STA, + WMI_VDEV_SUBTYPE_MESH_NON_11S, + WMI_VDEV_SUBTYPE_MESH_11S, +}; + +enum wmi_sta_powersave_param { + WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0, + WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1, + WMI_STA_PS_PARAM_PSPOLL_COUNT = 2, + WMI_STA_PS_PARAM_INACTIVITY_TIME = 3, + WMI_STA_PS_PARAM_UAPSD = 4, +}; + +#define WMI_UAPSD_AC_TYPE_DELI 0 +#define WMI_UAPSD_AC_TYPE_TRIG 1 + +#define WMI_UAPSD_AC_BIT_MASK(ac, type) \ + ((type == WMI_UAPSD_AC_TYPE_DELI) ? \ + (1 << (ac << 1)) : (1 << ((ac << 1) + 1))) + +enum wmi_sta_ps_param_uapsd { + WMI_STA_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_STA_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_STA_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_STA_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +#define WMI_STA_UAPSD_MAX_INTERVAL_MSEC UINT_MAX + +struct wmi_sta_uapsd_auto_trig_param { + u32 wmm_ac; + u32 user_priority; + u32 service_interval; + u32 suspend_interval; + u32 delay_interval; +}; + +struct wmi_sta_uapsd_auto_trig_cmd_fixed_param { + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 num_ac; +}; + +struct wmi_sta_uapsd_auto_trig_arg { + u32 wmm_ac; + u32 user_priority; + u32 service_interval; + u32 suspend_interval; + u32 delay_interval; +}; + +enum wmi_sta_ps_param_tx_wake_threshold { + WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER = 0, + WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS = 1, + + /* Values greater than one indicate that many TX attempts per beacon + * interval before the STA will wake up + */ +}; + +/* The maximum number of PS-Poll frames the FW will send in response to + * traffic advertised in TIM before waking up (by sending a null frame with PS + * = 0). Value 0 has a special meaning: there is no maximum count and the FW + * will send as many PS-Poll as are necessary to retrieve buffered BU. This + * parameter is used when the RX wake policy is + * WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD and ignored when the RX wake + * policy is WMI_STA_PS_RX_WAKE_POLICY_WAKE. + */ +enum wmi_sta_ps_param_pspoll_count { + WMI_STA_PS_PSPOLL_COUNT_NO_MAX = 0, + /* Values greater than 0 indicate the maximum numer of PS-Poll frames + * FW will send before waking up. + */ +}; + +/* U-APSD configuration of peer station from (re)assoc request and TSPECs */ +enum wmi_ap_ps_param_uapsd { + WMI_AP_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_AP_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_AP_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_AP_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +/* U-APSD maximum service period of peer station */ +enum wmi_ap_ps_peer_param_max_sp { + WMI_AP_PS_PEER_PARAM_MAX_SP_UNLIMITED = 0, + WMI_AP_PS_PEER_PARAM_MAX_SP_2 = 1, + WMI_AP_PS_PEER_PARAM_MAX_SP_4 = 2, + WMI_AP_PS_PEER_PARAM_MAX_SP_6 = 3, + MAX_WMI_AP_PS_PEER_PARAM_MAX_SP, +}; + +enum wmi_ap_ps_peer_param { + /** Set uapsd configuration for a given peer. + * + * This include the delivery and trigger enabled state for each AC. + * The host MLME needs to set this based on AP capability and stations + * request Set in the association request received from the station. + * + * Lower 8 bits of the value specify the UAPSD configuration. + * + * (see enum wmi_ap_ps_param_uapsd) + * The default value is 0. + */ + WMI_AP_PS_PEER_PARAM_UAPSD = 0, + + /** + * Set the service period for a UAPSD capable station + * + * The service period from wme ie in the (re)assoc request frame. + * + * (see enum wmi_ap_ps_peer_param_max_sp) + */ + WMI_AP_PS_PEER_PARAM_MAX_SP = 1, + + /** Time in seconds for aging out buffered frames + * for STA in power save + */ + WMI_AP_PS_PEER_PARAM_AGEOUT_TIME = 2, + + /** Specify frame types that are considered SIFS + * RESP trigger frame + */ + WMI_AP_PS_PEER_PARAM_SIFS_RESP_FRMTYPE = 3, + + /** Specifies the trigger state of TID. + * Valid only for UAPSD frame type + */ + WMI_AP_PS_PEER_PARAM_SIFS_RESP_UAPSD = 4, + + /* Specifies the WNM sleep state of a STA */ + WMI_AP_PS_PEER_PARAM_WNM_SLEEP = 5, +}; + +#define DISABLE_SIFS_RESPONSE_TRIGGER 0 + +#define WMI_MAX_KEY_INDEX 3 +#define WMI_MAX_KEY_LEN 32 + +#define WMI_KEY_PAIRWISE 0x00 +#define WMI_KEY_GROUP 0x01 + +#define WMI_CIPHER_NONE 0x0 /* clear key */ +#define WMI_CIPHER_WEP 0x1 +#define WMI_CIPHER_TKIP 0x2 +#define WMI_CIPHER_AES_OCB 0x3 +#define WMI_CIPHER_AES_CCM 0x4 +#define WMI_CIPHER_WAPI 0x5 +#define WMI_CIPHER_CKIP 0x6 +#define WMI_CIPHER_AES_CMAC 0x7 +#define WMI_CIPHER_ANY 0x8 +#define WMI_CIPHER_AES_GCM 0x9 +#define WMI_CIPHER_AES_GMAC 0xa + +/* Value to disable fixed rate setting */ +#define WMI_FIXED_RATE_NONE (0xffff) + +#define ATH11K_RC_VERSION_OFFSET 28 +#define ATH11K_RC_PREAMBLE_OFFSET 8 +#define ATH11K_RC_NSS_OFFSET 5 + +#define ATH11K_HW_RATE_CODE(rate, nss, preamble) \ + ((1 << ATH11K_RC_VERSION_OFFSET) | \ + ((nss) << ATH11K_RC_NSS_OFFSET) | \ + ((preamble) << ATH11K_RC_PREAMBLE_OFFSET) | \ + (rate)) + +/* Preamble types to be used with VDEV fixed rate configuration */ +enum wmi_rate_preamble { + WMI_RATE_PREAMBLE_OFDM, + WMI_RATE_PREAMBLE_CCK, + WMI_RATE_PREAMBLE_HT, + WMI_RATE_PREAMBLE_VHT, + WMI_RATE_PREAMBLE_HE, +}; + +/** + * enum wmi_rtscts_prot_mode - Enable/Disable RTS/CTS and CTS2Self Protection. + * @WMI_RTS_CTS_DISABLED : RTS/CTS protection is disabled. + * @WMI_USE_RTS_CTS : RTS/CTS Enabled. + * @WMI_USE_CTS2SELF : CTS to self protection Enabled. + */ +enum wmi_rtscts_prot_mode { + WMI_RTS_CTS_DISABLED = 0, + WMI_USE_RTS_CTS = 1, + WMI_USE_CTS2SELF = 2, +}; + +/** + * enum wmi_rtscts_profile - Selection of RTS CTS profile along with enabling + * protection mode. + * @WMI_RTSCTS_FOR_NO_RATESERIES - Neither of rate-series should use RTS-CTS + * @WMI_RTSCTS_FOR_SECOND_RATESERIES - Only second rate-series will use RTS-CTS + * @WMI_RTSCTS_ACROSS_SW_RETRIES - Only the second rate-series will use RTS-CTS, + * but if there's a sw retry, both the rate + * series will use RTS-CTS. + * @WMI_RTSCTS_ERP - RTS/CTS used for ERP protection for every PPDU. + * @WMI_RTSCTS_FOR_ALL_RATESERIES - Enable RTS-CTS for all rate series. + */ +enum wmi_rtscts_profile { + WMI_RTSCTS_FOR_NO_RATESERIES = 0, + WMI_RTSCTS_FOR_SECOND_RATESERIES = 1, + WMI_RTSCTS_ACROSS_SW_RETRIES = 2, + WMI_RTSCTS_ERP = 3, + WMI_RTSCTS_FOR_ALL_RATESERIES = 4, +}; + +struct ath11k_hal_reg_cap { + u32 eeprom_rd; + u32 eeprom_rd_ext; + u32 regcap1; + u32 regcap2; + u32 wireless_modes; + u32 low_2ghz_chan; + u32 high_2ghz_chan; + u32 low_5ghz_chan; + u32 high_5ghz_chan; +}; + +struct ath11k_mem_chunk { + void *vaddr; + dma_addr_t paddr; + u32 len; + u32 req_id; +}; + +#define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr) + +enum wmi_sta_ps_param_rx_wake_policy { + WMI_STA_PS_RX_WAKE_POLICY_WAKE = 0, + WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD = 1, +}; + +enum ath11k_hw_txrx_mode { + ATH11K_HW_TXRX_RAW = 0, + ATH11K_HW_TXRX_NATIVE_WIFI = 1, + ATH11K_HW_TXRX_ETHERNET = 2, +}; + +struct wmi_wmm_params { + u32 tlv_header; + u32 cwmin; + u32 cwmax; + u32 aifs; + u32 txoplimit; + u32 acm; + u32 no_ack; +} __packed; + +struct wmi_wmm_params_arg { + u8 acm; + u8 aifs; + u16 cwmin; + u16 cwmax; + u16 txop; + u8 no_ack; +}; + +struct wmi_vdev_set_wmm_params_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_wmm_params wmm_params[4]; + u32 wmm_param_type; +} __packed; + +struct wmi_wmm_params_all_arg { + struct wmi_wmm_params_arg ac_be; + struct wmi_wmm_params_arg ac_bk; + struct wmi_wmm_params_arg ac_vi; + struct wmi_wmm_params_arg ac_vo; +}; + +#define ATH11K_TWT_DEF_STA_CONG_TIMER_MS 5000 +#define ATH11K_TWT_DEF_DEFAULT_SLOT_SIZE 10 +#define ATH11K_TWT_DEF_CONGESTION_THRESH_SETUP 50 +#define ATH11K_TWT_DEF_CONGESTION_THRESH_TEARDOWN 20 +#define ATH11K_TWT_DEF_CONGESTION_THRESH_CRITICAL 100 +#define ATH11K_TWT_DEF_INTERFERENCE_THRESH_TEARDOWN 80 +#define ATH11K_TWT_DEF_INTERFERENCE_THRESH_SETUP 50 +#define ATH11K_TWT_DEF_MIN_NO_STA_SETUP 10 +#define ATH11K_TWT_DEF_MIN_NO_STA_TEARDOWN 2 +#define ATH11K_TWT_DEF_NO_OF_BCAST_MCAST_SLOTS 2 +#define ATH11K_TWT_DEF_MIN_NO_TWT_SLOTS 2 +#define ATH11K_TWT_DEF_MAX_NO_STA_TWT 500 +#define ATH11K_TWT_DEF_MODE_CHECK_INTERVAL 10000 +#define ATH11K_TWT_DEF_ADD_STA_SLOT_INTERVAL 1000 +#define ATH11K_TWT_DEF_REMOVE_STA_SLOT_INTERVAL 5000 + +struct wmi_twt_enable_params_cmd { + u32 tlv_header; + u32 pdev_id; + u32 sta_cong_timer_ms; + u32 mbss_support; + u32 default_slot_size; + u32 congestion_thresh_setup; + u32 congestion_thresh_teardown; + u32 congestion_thresh_critical; + u32 interference_thresh_teardown; + u32 interference_thresh_setup; + u32 min_no_sta_setup; + u32 min_no_sta_teardown; + u32 no_of_bcast_mcast_slots; + u32 min_no_twt_slots; + u32 max_no_sta_twt; + u32 mode_check_interval; + u32 add_sta_slot_interval; + u32 remove_sta_slot_interval; +} __packed; + +struct wmi_twt_disable_params_cmd { + u32 tlv_header; + u32 pdev_id; +} __packed; + +struct wmi_obss_spatial_reuse_params_cmd { + u32 tlv_header; + u32 pdev_id; + u32 enable; + s32 obss_min; + s32 obss_max; + u32 vdev_id; +} __packed; + +struct target_resource_config { + u32 num_vdevs; + u32 num_peers; + u32 num_active_peers; + u32 num_offload_peers; + u32 num_offload_reorder_buffs; + u32 num_peer_keys; + u32 num_tids; + u32 ast_skid_limit; + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 rx_timeout_pri[4]; + u32 rx_decap_mode; + u32 scan_max_pending_req; + u32 bmiss_offload_max_vdev; + u32 roam_offload_max_vdev; + u32 roam_offload_max_ap_profiles; + u32 num_mcast_groups; + u32 num_mcast_table_elems; + u32 mcast2ucast_mode; + u32 tx_dbg_log_size; + u32 num_wds_entries; + u32 dma_burst_size; + u32 mac_aggr_delim; + u32 rx_skip_defrag_timeout_dup_detection_check; + u32 vow_config; + u32 gtk_offload_max_vdev; + u32 num_msdu_desc; + u32 max_frag_entries; + u32 max_peer_ext_stats; + u32 smart_ant_cap; + u32 bk_minfree; + u32 be_minfree; + u32 vi_minfree; + u32 vo_minfree; + u32 rx_batchmode; + u32 tt_support; + u32 atf_config; + u32 iphdr_pad_config; + u32 qwrap_config:16, + alloc_frag_desc_for_data_pkt:16; + u32 num_tdls_vdevs; + u32 num_tdls_conn_table_entries; + u32 beacon_tx_offload_max_vdev; + u32 num_multicast_filter_entries; + u32 num_wow_filters; + u32 num_keep_alive_pattern; + u32 keep_alive_pattern_size; + u32 max_tdls_concurrent_sleep_sta; + u32 max_tdls_concurrent_buffer_sta; + u32 wmi_send_separate; + u32 num_ocb_vdevs; + u32 num_ocb_channels; + u32 num_ocb_schedules; + u32 num_ns_ext_tuples_cfg; + u32 bpf_instruction_size; + u32 max_bssid_rx_filters; + u32 use_pdev_id; + u32 peer_map_unmap_v2_support; + u32 sched_params; + u32 twt_ap_pdev_count; + u32 twt_ap_sta_count; +}; + +#define WMI_MAX_MEM_REQS 32 + +#define MAX_RADIOS 3 + +#define WMI_SERVICE_READY_TIMEOUT_HZ (5 * HZ) +#define WMI_SEND_TIMEOUT_HZ (3 * HZ) + +struct ath11k_wmi_base { + struct ath11k_base *ab; + struct ath11k_pdev_wmi wmi[MAX_RADIOS]; + enum ath11k_htc_ep_id wmi_endpoint_id[MAX_RADIOS]; + u32 max_msg_len[MAX_RADIOS]; + + struct completion service_ready; + struct completion unified_ready; + DECLARE_BITMAP(svc_map, WMI_MAX_EXT_SERVICE); + wait_queue_head_t tx_credits_wq; + const struct wmi_peer_flags_map *peer_flags; + u32 num_mem_chunks; + u32 rx_decap_mode; + struct wmi_host_mem_chunk mem_chunks[WMI_MAX_MEM_REQS]; + + enum wmi_host_hw_mode_config_type preferred_hw_mode; + struct target_resource_config wlan_resource_config; + + struct ath11k_targ_cap *targ_cap; +}; + +int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id); +struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); +int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, + struct sk_buff *frame); +int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id, + struct ieee80211_mutable_offsets *offs, + struct sk_buff *bcn); +int ath11k_wmi_vdev_down(struct ath11k *ar, u8 vdev_id); +int ath11k_wmi_vdev_up(struct ath11k *ar, u32 vdev_id, u32 aid, + const u8 *bssid); +int ath11k_wmi_vdev_stop(struct ath11k *ar, u8 vdev_id); +int ath11k_wmi_vdev_start(struct ath11k *ar, struct wmi_vdev_start_req_arg *arg, + bool restart); +int ath11k_wmi_set_peer_param(struct ath11k *ar, const u8 *peer_addr, + u32 vdev_id, u32 param_id, u32 param_val); +int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id, + u32 param_value, u8 pdev_id); +int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable); +int ath11k_wmi_wait_for_unified_ready(struct ath11k_base *ab); +int ath11k_wmi_cmd_init(struct ath11k_base *ab); +int ath11k_wmi_wait_for_service_ready(struct ath11k_base *ab); +int ath11k_wmi_connect(struct ath11k_base *ab); +int ath11k_wmi_pdev_attach(struct ath11k_base *ab, + u8 pdev_id); +int ath11k_wmi_attach(struct ath11k_base *ab); +void ath11k_wmi_detach(struct ath11k_base *ab); +int ath11k_wmi_vdev_create(struct ath11k *ar, u8 *macaddr, + struct vdev_create_params *param); +int ath11k_wmi_peer_rx_reorder_queue_setup(struct ath11k *ar, int vdev_id, + const u8 *addr, dma_addr_t paddr, + u8 tid, u8 ba_window_size_valid, + u32 ba_window_size); +int ath11k_wmi_send_peer_create_cmd(struct ath11k *ar, + struct peer_create_params *param); +int ath11k_wmi_vdev_set_param_cmd(struct ath11k *ar, u32 vdev_id, + u32 param_id, u32 param_value); + +int ath11k_wmi_set_sta_ps_param(struct ath11k *ar, u32 vdev_id, + u32 param, u32 param_value); +int ath11k_wmi_force_fw_hang_cmd(struct ath11k *ar, u32 type, u32 delay_time_ms); +int ath11k_wmi_send_peer_delete_cmd(struct ath11k *ar, + const u8 *peer_addr, u8 vdev_id); +int ath11k_wmi_vdev_delete(struct ath11k *ar, u8 vdev_id); +void ath11k_wmi_start_scan_init(struct ath11k *ar, struct scan_req_params *arg); +int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, + struct scan_req_params *params); +int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, + struct scan_cancel_param *param); +int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, + struct wmi_wmm_params_all_arg *param); +int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt, + u32 pdev_id); +int ath11k_wmi_pdev_resume(struct ath11k *ar, u32 pdev_id); + +int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar, + struct peer_assoc_params *param); +int ath11k_wmi_vdev_install_key(struct ath11k *ar, + struct wmi_vdev_install_key_arg *arg); +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_peer_flush_tids_cmd(struct ath11k *ar, + u8 peer_addr[ETH_ALEN], + struct peer_flush_params *param); +int ath11k_wmi_send_set_ap_ps_param_cmd(struct ath11k *ar, u8 *peer_addr, + struct ap_ps_params *param); +int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, + struct scan_chan_list_params *chan_list); +int ath11k_wmi_send_dfs_phyerr_offload_enable_cmd(struct ath11k *ar, + u32 pdev_id); +int ath11k_wmi_send_bcn_offload_control_cmd(struct ath11k *ar, + u32 vdev_id, u32 bcn_ctrl_op); +int +ath11k_wmi_send_init_country_cmd(struct ath11k *ar, + struct wmi_init_country_params init_cc_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); +int +ath11k_wmi_rx_reord_queue_remove(struct ath11k *ar, + struct rx_reorder_queue_remove_params *param); +int ath11k_wmi_send_pdev_set_regdomain(struct ath11k *ar, + struct pdev_set_regdomain_params *param); +int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, + struct ath11k_fw_stats *stats); +size_t ath11k_wmi_fw_stats_num_peers(struct list_head *head); +size_t ath11k_wmi_fw_stats_num_peers_extd(struct list_head *head); +size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head); +void ath11k_wmi_fw_stats_fill(struct ath11k *ar, + struct ath11k_fw_stats *fw_stats, u32 stats_id, + char *buf); +int ath11k_wmi_simulate_radar(struct ath11k *ar); +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); +#endif diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.c b/drivers/net/wireless/ath/ath9k/ar9003_aic.c index 547cd46da260..d0f1e8bcc846 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_aic.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.c @@ -406,7 +406,7 @@ static bool ar9003_aic_cal_post_process(struct ath_hw *ah) sram.com_att_6db = ar9003_aic_find_index(1, fixed_com_att_db); - sram.valid = 1; + sram.valid = true; sram.rot_dir_att_db = min(max(rot_dir_path_att_db, diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index fb649d85b8fc..dd0c32379375 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1216,7 +1216,7 @@ err_fw: static int send_eject_command(struct usb_interface *interface) { struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc = &interface->altsetting[0]; + struct usb_host_interface *iface_desc = interface->cur_altsetting; struct usb_endpoint_descriptor *endpoint; unsigned char *cmd; u8 bulk_out_ep; diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 20f4f8ea9f89..bee9110b91f3 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -666,14 +666,14 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, /* * Some users have reported their EEPROM programmed with - * 0x8000 set, this is not a supported regulatory domain - * but since we have more than one user with it we need - * a solution for them. We default to 0x64, which is the - * default Atheros world regulatory domain. + * 0x8000 or 0x0 set, this is not a supported regulatory + * domain but since we have more than one user with it we + * need a solution for them. We default to 0x64, which is + * the default Atheros world regulatory domain. */ static void ath_regd_sanitize(struct ath_regulatory *reg) { - if (reg->current_rd != COUNTRY_ERD_FLAG) + if (reg->current_rd != COUNTRY_ERD_FLAG && reg->current_rd != 0) return; printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n"); reg->current_rd = 0x64; diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index c30fdd0cbf1e..e49c306e0eef 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1169,7 +1169,6 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY); ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION); - ieee80211_hw_set(wcn->hw, CONNECTION_MONITOR); ieee80211_hw_set(wcn->hw, SUPPORTS_PS); ieee80211_hw_set(wcn->hw, SIGNAL_DBM); ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL); diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 523550f94a3f..77269ac7f352 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -1620,7 +1620,7 @@ int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif, msg_body.beacon_length6 = msg_body.beacon_length + 6; if (msg_body.beacon_length > BEACON_TEMPLATE_SIZE) { - wcn36xx_err("Beacon is to big: beacon size=%d\n", + wcn36xx_err("Beacon is too big: beacon size=%d\n", msg_body.beacon_length); ret = -ENOMEM; goto out; diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 7d6f14420855..0851d2bede89 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2579,6 +2579,38 @@ wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, return rc; } +static int wil_cfg80211_set_multicast_to_unicast(struct wiphy *wiphy, + struct net_device *dev, + const bool enabled) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + if (wil->multicast_to_unicast == enabled) + return 0; + + wil_info(wil, "set multicast to unicast, enabled=%d\n", enabled); + wil->multicast_to_unicast = enabled; + + return 0; +} + +static int wil_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + + wil->cqm_rssi_thold = rssi_thold; + + rc = wmi_set_cqm_rssi_config(wil, rssi_thold, rssi_hyst); + if (rc) + /* reset stored value upon failure */ + wil->cqm_rssi_thold = 0; + + return rc; +} + static const struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface, @@ -2610,11 +2642,13 @@ static const struct cfg80211_ops wil_cfg80211_ops = { .start_p2p_device = wil_cfg80211_start_p2p_device, .stop_p2p_device = wil_cfg80211_stop_p2p_device, .set_power_mgmt = wil_cfg80211_set_power_mgmt, + .set_cqm_rssi_config = wil_cfg80211_set_cqm_rssi_config, .suspend = wil_cfg80211_suspend, .resume = wil_cfg80211_resume, .sched_scan_start = wil_cfg80211_sched_scan_start, .sched_scan_stop = wil_cfg80211_sched_scan_stop, .update_ft_ies = wil_cfg80211_update_ft_ies, + .set_multicast_to_unicast = wil_cfg80211_set_multicast_to_unicast, }; static void wil_wiphy_init(struct wiphy *wiphy) diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c index 912c4eaf017b..fef10886ca4a 100644 --- a/drivers/net/wireless/ath/wil6210/ethtool.c +++ b/drivers/net/wireless/ath/wil6210/ethtool.c @@ -11,26 +11,6 @@ #include "wil6210.h" -static int wil_ethtoolops_begin(struct net_device *ndev) -{ - struct wil6210_priv *wil = ndev_to_wil(ndev); - - mutex_lock(&wil->mutex); - - wil_dbg_misc(wil, "ethtoolops_begin\n"); - - return 0; -} - -static void wil_ethtoolops_complete(struct net_device *ndev) -{ - struct wil6210_priv *wil = ndev_to_wil(ndev); - - wil_dbg_misc(wil, "ethtoolops_complete\n"); - - mutex_unlock(&wil->mutex); -} - static int wil_ethtoolops_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *cp) { @@ -39,11 +19,12 @@ static int wil_ethtoolops_get_coalesce(struct net_device *ndev, u32 rx_itr_en, rx_itr_val = 0; int ret; + mutex_lock(&wil->mutex); wil_dbg_misc(wil, "ethtoolops_get_coalesce\n"); ret = wil_pm_runtime_get(wil); if (ret < 0) - return ret; + goto out; tx_itr_en = wil_r(wil, RGF_DMA_ITR_TX_CNT_CTL); if (tx_itr_en & BIT_DMA_ITR_TX_CNT_CTL_EN) @@ -57,7 +38,11 @@ static int wil_ethtoolops_get_coalesce(struct net_device *ndev, cp->tx_coalesce_usecs = tx_itr_val; cp->rx_coalesce_usecs = rx_itr_val; - return 0; + ret = 0; + +out: + mutex_unlock(&wil->mutex); + return ret; } static int wil_ethtoolops_set_coalesce(struct net_device *ndev, @@ -67,12 +52,14 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev, struct wireless_dev *wdev = ndev->ieee80211_ptr; int ret; + mutex_lock(&wil->mutex); wil_dbg_misc(wil, "ethtoolops_set_coalesce: rx %d usec, tx %d usec\n", cp->rx_coalesce_usecs, cp->tx_coalesce_usecs); if (wdev->iftype == NL80211_IFTYPE_MONITOR) { wil_dbg_misc(wil, "No IRQ coalescing in monitor mode\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } /* only @rx_coalesce_usecs and @tx_coalesce_usecs supported, @@ -88,24 +75,26 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev, ret = wil_pm_runtime_get(wil); if (ret < 0) - return ret; + goto out; wil->txrx_ops.configure_interrupt_moderation(wil); wil_pm_runtime_put(wil); + ret = 0; - return 0; +out: + mutex_unlock(&wil->mutex); + return ret; out_bad: wil_dbg_misc(wil, "Unsupported coalescing params. Raw command:\n"); print_hex_dump_debug("DBG[MISC] coal ", DUMP_PREFIX_OFFSET, 16, 4, cp, sizeof(*cp), false); + mutex_unlock(&wil->mutex); return -EINVAL; } static const struct ethtool_ops wil_ethtool_ops = { - .begin = wil_ethtoolops_begin, - .complete = wil_ethtoolops_complete, .get_drvinfo = cfg80211_get_drvinfo, .get_coalesce = wil_ethtoolops_get_coalesce, .set_coalesce = wil_ethtoolops_set_coalesce, diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 06091d8a9e23..3ba5b2550a8c 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -762,7 +762,7 @@ int wil_priv_init(struct wil6210_priv *wil) */ wil->rx_buff_id_count = WIL_RX_BUFF_ARR_SIZE_DEFAULT; - wil->amsdu_en = 1; + wil->amsdu_en = true; return 0; @@ -1654,6 +1654,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) /* Disable device led before reset*/ wmi_led_cfg(wil, false); + down_write(&wil->mem_lock); + /* prevent NAPI from being scheduled and prevent wmi commands */ mutex_lock(&wil->wmi_mutex); if (test_bit(wil_status_suspending, wil->status)) @@ -1702,6 +1704,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) if (wil->secured_boot) { wil_err(wil, "secured boot is not supported\n"); + up_write(&wil->mem_lock); return -ENOTSUPP; } @@ -1737,6 +1740,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) clear_bit(wil_status_resetting, wil->status); + up_write(&wil->mem_lock); + if (load_fw) { wil_unmask_irq(wil); @@ -1786,6 +1791,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; out: + up_write(&wil->mem_lock); clear_bit(wil_status_resetting, wil->status); return rc; } @@ -1811,9 +1817,7 @@ int __wil_up(struct wil6210_priv *wil) WARN_ON(!mutex_is_locked(&wil->mutex)); - down_write(&wil->mem_lock); rc = wil_reset(wil, true); - up_write(&wil->mem_lock); if (rc) return rc; @@ -1905,9 +1909,7 @@ int __wil_down(struct wil6210_priv *wil) wil_abort_scan_all_vifs(wil, false); mutex_unlock(&wil->vif_mutex); - down_write(&wil->mem_lock); rc = wil_reset(wil, false); - up_write(&wil->mem_lock); return rc; } diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 8ebc6d59aa74..bc8c15fb609d 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -10,6 +10,7 @@ #include <linux/moduleparam.h> #include <linux/ip.h> #include <linux/ipv6.h> +#include <linux/if_vlan.h> #include <net/ipv6.h> #include <linux/prefetch.h> @@ -1139,7 +1140,7 @@ static int wil_tx_desc_map(union wil_tx_desc *desc, dma_addr_t pa, void wil_tx_data_init(struct wil_ring_tx_data *txdata) { spin_lock_bh(&txdata->lock); - txdata->dot1x_open = 0; + txdata->dot1x_open = false; txdata->enabled = 0; txdata->idle = 0; txdata->last_idle = 0; @@ -1529,6 +1530,35 @@ static struct wil_ring *wil_find_tx_bcast_1(struct wil6210_priv *wil, return v; } +/* apply multicast to unicast only for ARP and IP packets + * (see NL80211_CMD_SET_MULTICAST_TO_UNICAST for more info) + */ +static bool wil_check_multicast_to_unicast(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + const struct ethhdr *eth = (void *)skb->data; + const struct vlan_ethhdr *ethvlan = (void *)skb->data; + __be16 ethertype; + + if (!wil->multicast_to_unicast) + return false; + + /* multicast to unicast conversion only for some payload */ + ethertype = eth->h_proto; + if (ethertype == htons(ETH_P_8021Q) && skb->len >= VLAN_ETH_HLEN) + ethertype = ethvlan->h_vlan_encapsulated_proto; + switch (ethertype) { + case htons(ETH_P_ARP): + case htons(ETH_P_IP): + case htons(ETH_P_IPV6): + break; + default: + return false; + } + + return true; +} + static void wil_set_da_for_vring(struct wil6210_priv *wil, struct sk_buff *skb, int vring_index) { @@ -2336,7 +2366,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) /* in STA mode (ESS), all to same VRING (to AP) */ ring = wil_find_tx_ring_sta(wil, vif, skb); } else if (bcast) { - if (vif->pbss) + if (vif->pbss || wil_check_multicast_to_unicast(wil, skb)) /* in pbss, no bcast VRING - duplicate skb in * all stations VRINGs */ diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index 778b63be6a9a..7bfe867c7509 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -869,6 +869,7 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil, u8 data_offset; struct wil_rx_status_extended *s; u16 sring_idx = sring - wil->srings; + int invalid_buff_id_retry; BUILD_BUG_ON(sizeof(struct wil_rx_status_extended) > sizeof(skb->cb)); @@ -882,9 +883,9 @@ again: /* Extract the buffer ID from the status message */ buff_id = le16_to_cpu(wil_rx_status_get_buff_id(msg)); + invalid_buff_id_retry = 0; while (!buff_id) { struct wil_rx_status_extended *s; - int invalid_buff_id_retry = 0; wil_dbg_txrx(wil, "buff_id is not updated yet by HW, (swhead 0x%x)\n", @@ -902,6 +903,11 @@ again: if (unlikely(!wil_val_in_range(buff_id, 1, wil->rx_buff_mgmt.size))) { wil_err(wil, "Corrupt buff_id=%d, sring->swhead=%d\n", buff_id, sring->swhead); + print_hex_dump(KERN_ERR, "RxS ", DUMP_PREFIX_OFFSET, 16, 1, + msg, wil->use_compressed_rx_status ? + sizeof(struct wil_rx_status_compressed) : + sizeof(struct wil_rx_status_extended), false); + wil_rx_status_reset_buff_id(sring); wil_sring_advance_swhead(sring); sring->invalid_buff_id_cnt++; @@ -962,6 +968,11 @@ again: if (unlikely(dmalen > sz)) { wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); + print_hex_dump(KERN_ERR, "RxS ", DUMP_PREFIX_OFFSET, 16, 1, + msg, wil->use_compressed_rx_status ? + sizeof(struct wil_rx_status_compressed) : + sizeof(struct wil_rx_status_extended), false); + stats->rx_large_frame++; rxdata->skipping = true; } diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h index c744c65225da..c736f7413a35 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.h +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h @@ -46,7 +46,7 @@ #define WIL_RX_EDMA_DLPF_LU_MISS_TID_POS 5 -#define WIL_RX_EDMA_MID_VALID_BIT BIT(22) +#define WIL_RX_EDMA_MID_VALID_BIT BIT(20) #define WIL_EDMA_DESC_TX_MAC_CFG_0_QID_POS 16 #define WIL_EDMA_DESC_TX_MAC_CFG_0_QID_LEN 6 @@ -244,8 +244,8 @@ struct wil_ring_tx_status { * calculated, Bit1- L4Err - TCP/UDP Checksum Error * bit 7 : Reserved:1 * bit 8..19 : Flow ID:12 - MSDU flow ID - * bit 20..21 : MID:2 - The MAC ID - * bit 22 : MID_V:1 - The MAC ID field is valid + * bit 20 : MID_V:1 - The MAC ID field is valid + * bit 21..22 : MID:2 - The MAC ID * bit 23 : L3T:1 - IP types: 0-IPv6, 1-IPv4 * bit 24 : L4T:1 - Layer 4 Type: 0-UDP, 1-TCP * bit 25 : BC:1 - The received MPDU is broadcast @@ -479,7 +479,7 @@ static inline int wil_rx_status_get_mid(void *msg) return 0; /* use the default MID */ return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, - 20, 21); + 21, 22); } static inline int wil_rx_status_get_error(void *msg) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 97626bfd4dac..5dc881d3c057 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1059,6 +1059,8 @@ struct wil6210_priv { u32 max_agg_wsize; u32 max_ampdu_size; + u8 multicast_to_unicast; + s32 cqm_rssi_thold; }; #define wil_to_wiphy(i) (i->wiphy) @@ -1148,7 +1150,7 @@ static inline void wil_c(struct wil6210_priv *wil, u32 reg, u32 val) */ static inline bool wil_cid_valid(struct wil6210_priv *wil, int cid) { - return (cid >= 0 && cid < wil->max_assoc_sta); + return (cid >= 0 && cid < wil->max_assoc_sta && cid < WIL6210_MAX_CID); } void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len); @@ -1440,4 +1442,6 @@ int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, void update_supported_bands(struct wil6210_priv *wil); void wil_clear_fw_log_addr(struct wil6210_priv *wil); +int wmi_set_cqm_rssi_config(struct wil6210_priv *wil, + s32 rssi_thold, u32 rssi_hyst); #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c index 1332eb8c831f..89c12cb2aaab 100644 --- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c +++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c @@ -46,7 +46,7 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil, int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) { - int i, rc; + int i; const struct fw_map *map; void *data; u32 host_min, dump_size, offset, len; @@ -62,9 +62,15 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) return -EINVAL; } - rc = wil_mem_access_lock(wil); - if (rc) - return rc; + down_write(&wil->mem_lock); + + if (test_bit(wil_status_suspending, wil->status) || + test_bit(wil_status_suspended, wil->status)) { + wil_err(wil, + "suspend/resume in progress. cannot copy crash dump\n"); + up_write(&wil->mem_lock); + return -EBUSY; + } /* copy to crash dump area */ for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { @@ -84,7 +90,8 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) wil_memcpy_fromio_32((void * __force)(dest + offset), (const void __iomem * __force)data, len); } - wil_mem_access_unlock(wil); + + up_write(&wil->mem_lock); return 0; } diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 7a0d934eb271..23e1ed6a9d6d 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -196,8 +196,8 @@ const struct fw_map talyn_mb_fw_mapping[] = { {0x8c0000, 0x8c0210, 0x8c0000, "dum_user_rgf", true, true}, /* DMA OFU 296b */ {0x8c2000, 0x8c2128, 0x8c2000, "dma_ofu", true, true}, - /* ucode debug 4k */ - {0x8c3000, 0x8c4000, 0x8c3000, "ucode_debug", true, true}, + /* ucode debug 256b */ + {0x8c3000, 0x8c3100, 0x8c3000, "ucode_debug", true, true}, /* upper area 1536k */ {0x900000, 0xa80000, 0x900000, "upper", true, true}, /* UCODE areas - accessible by debugfs blobs but not by @@ -476,6 +476,8 @@ static const char *cmdid2name(u16 cmdid) return "WMI_RBUFCAP_CFG_CMD"; case WMI_TEMP_SENSE_ALL_CMDID: return "WMI_TEMP_SENSE_ALL_CMDID"; + case WMI_SET_LINK_MONITOR_CMDID: + return "WMI_SET_LINK_MONITOR_CMD"; default: return "Untracked CMD"; } @@ -624,6 +626,10 @@ static const char *eventid2name(u16 eventid) return "WMI_RBUFCAP_CFG_EVENT"; case WMI_TEMP_SENSE_ALL_DONE_EVENTID: return "WMI_TEMP_SENSE_ALL_DONE_EVENTID"; + case WMI_SET_LINK_MONITOR_EVENTID: + return "WMI_SET_LINK_MONITOR_EVENT"; + case WMI_LINK_MONITOR_EVENTID: + return "WMI_LINK_MONITOR_EVENT"; default: return "Untracked EVENT"; } @@ -1507,14 +1513,14 @@ static void wmi_link_stats_parse(struct wil6210_vif *vif, u64 tsf, if (vif->fw_stats_ready) { /* clean old statistics */ vif->fw_stats_tsf = 0; - vif->fw_stats_ready = 0; + vif->fw_stats_ready = false; } wil_link_stats_store_basic(vif, payload + hdr_size); if (!has_next) { vif->fw_stats_tsf = tsf; - vif->fw_stats_ready = 1; + vif->fw_stats_ready = true; } break; @@ -1529,14 +1535,14 @@ static void wmi_link_stats_parse(struct wil6210_vif *vif, u64 tsf, if (wil->fw_stats_global.ready) { /* clean old statistics */ wil->fw_stats_global.tsf = 0; - wil->fw_stats_global.ready = 0; + wil->fw_stats_global.ready = false; } wil_link_stats_store_global(vif, payload + hdr_size); if (!has_next) { wil->fw_stats_global.tsf = tsf; - wil->fw_stats_global.ready = 1; + wil->fw_stats_global.ready = true; } break; @@ -1836,6 +1842,32 @@ fail: wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID); } +static void +wmi_evt_link_monitor(struct wil6210_vif *vif, int id, void *d, int len) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct net_device *ndev = vif_to_ndev(vif); + struct wmi_link_monitor_event *evt = d; + enum nl80211_cqm_rssi_threshold_event event_type; + + if (len < sizeof(*evt)) { + wil_err(wil, "link monitor event too short %d\n", len); + return; + } + + wil_dbg_wmi(wil, "link monitor event, type %d rssi %d (stored %d)\n", + evt->type, evt->rssi_level, wil->cqm_rssi_thold); + + if (evt->type != WMI_LINK_MONITOR_NOTIF_RSSI_THRESHOLD_EVT) + /* ignore */ + return; + + event_type = (evt->rssi_level > wil->cqm_rssi_thold ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH : + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW); + cfg80211_cqm_rssi_notify(ndev, event_type, evt->rssi_level, GFP_KERNEL); +} + /** * Some events are ignored for purpose; and need not be interpreted as * "unhandled events" @@ -1869,6 +1901,7 @@ static const struct { {WMI_LINK_STATS_EVENTID, wmi_evt_link_stats}, {WMI_FT_AUTH_STATUS_EVENTID, wmi_evt_auth_status}, {WMI_FT_REASSOC_STATUS_EVENTID, wmi_evt_reassoc_status}, + {WMI_LINK_MONITOR_EVENTID, wmi_evt_link_monitor}, }; /* @@ -3981,3 +4014,46 @@ int wmi_link_stats_cfg(struct wil6210_vif *vif, u32 type, u8 cid, u32 interval) return 0; } + +int wmi_set_cqm_rssi_config(struct wil6210_priv *wil, + s32 rssi_thold, u32 rssi_hyst) +{ + struct net_device *ndev = wil->main_ndev; + struct wil6210_vif *vif = ndev_to_vif(ndev); + int rc; + struct { + struct wmi_set_link_monitor_cmd cmd; + s8 rssi_thold; + } __packed cmd = { + .cmd = { + .rssi_hyst = rssi_hyst, + .rssi_thresholds_list_size = 1, + }, + .rssi_thold = rssi_thold, + }; + struct { + struct wmi_cmd_hdr hdr; + struct wmi_set_link_monitor_event evt; + } __packed reply = { + .evt = {.status = WMI_FW_STATUS_FAILURE}, + }; + + if (rssi_thold > S8_MAX || rssi_thold < S8_MIN || rssi_hyst > U8_MAX) + return -EINVAL; + + rc = wmi_call(wil, WMI_SET_LINK_MONITOR_CMDID, vif->mid, &cmd, + sizeof(cmd), WMI_SET_LINK_MONITOR_EVENTID, + &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) { + wil_err(wil, "WMI_SET_LINK_MONITOR_CMDID failed, rc %d\n", rc); + return rc; + } + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "WMI_SET_LINK_MONITOR_CMDID failed, status %d\n", + reply.evt.status); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 6bd4ccee28ab..e3558136e0c4 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -192,6 +192,7 @@ enum wmi_command_id { WMI_RCP_ADDBA_RESP_EDMA_CMDID = 0x83B, WMI_LINK_MAINTAIN_CFG_WRITE_CMDID = 0x842, WMI_LINK_MAINTAIN_CFG_READ_CMDID = 0x843, + WMI_SET_LINK_MONITOR_CMDID = 0x845, WMI_SET_SECTORS_CMDID = 0x849, WMI_MAINTAIN_PAUSE_CMDID = 0x850, WMI_MAINTAIN_RESUME_CMDID = 0x851, @@ -1973,6 +1974,7 @@ enum wmi_event_id { WMI_REPORT_STATISTICS_EVENTID = 0x100B, WMI_FT_AUTH_STATUS_EVENTID = 0x100C, WMI_FT_REASSOC_STATUS_EVENTID = 0x100D, + WMI_LINK_MONITOR_EVENTID = 0x100E, WMI_RADAR_GENERAL_CONFIG_EVENTID = 0x1100, WMI_RADAR_CONFIG_SELECT_EVENTID = 0x1101, WMI_RADAR_PARAMS_CONFIG_EVENTID = 0x1102, @@ -2024,6 +2026,7 @@ enum wmi_event_id { WMI_TX_MGMT_PACKET_EVENTID = 0x1841, WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID = 0x1842, WMI_LINK_MAINTAIN_CFG_READ_DONE_EVENTID = 0x1843, + WMI_SET_LINK_MONITOR_EVENTID = 0x1845, WMI_RF_XPM_READ_RESULT_EVENTID = 0x1856, WMI_RF_XPM_WRITE_RESULT_EVENTID = 0x1857, WMI_LED_CFG_DONE_EVENTID = 0x1858, @@ -3312,6 +3315,36 @@ struct wmi_link_maintain_cfg_read_cmd { __le32 cid; } __packed; +/* WMI_SET_LINK_MONITOR_CMDID */ +struct wmi_set_link_monitor_cmd { + u8 rssi_hyst; + u8 reserved[12]; + u8 rssi_thresholds_list_size; + s8 rssi_thresholds_list[0]; +} __packed; + +/* wmi_link_monitor_event_type */ +enum wmi_link_monitor_event_type { + WMI_LINK_MONITOR_NOTIF_RSSI_THRESHOLD_EVT = 0x00, + WMI_LINK_MONITOR_NOTIF_TX_ERR_EVT = 0x01, + WMI_LINK_MONITOR_NOTIF_THERMAL_EVT = 0x02, +}; + +/* WMI_SET_LINK_MONITOR_EVENTID */ +struct wmi_set_link_monitor_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_LINK_MONITOR_EVENTID */ +struct wmi_link_monitor_event { + /* link_monitor_event_type */ + u8 type; + s8 rssi_level; + u8 reserved[2]; +} __packed; + /* WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID */ struct wmi_link_maintain_cfg_write_done_event { /* requested connection ID */ |