mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-19 10:23:03 +00:00
2716 lines
74 KiB
Diff
2716 lines
74 KiB
Diff
From 0fa55ca418c8afd6da242407a184c23548c553dc Mon Sep 17 00:00:00 2001
|
|
From: Sriram R <srirrama@codeaurora.org>
|
|
Date: Fri, 5 Jun 2020 12:21:15 +0530
|
|
Subject: [PATCH 2/3] ath11k: Add nss driver interface
|
|
|
|
This patch adds interface support for accessing nss driver with
|
|
support for initialization, teardown, vap up/down, peer create/delete,
|
|
tx/rx. NSS Stats addition is not part of this version.
|
|
|
|
Signed-off-by: Sriram R <srirrama@codeaurora.org>
|
|
---
|
|
drivers/net/wireless/ath/ath11k/Kconfig | 9 +
|
|
drivers/net/wireless/ath/ath11k/Makefile | 1 +
|
|
drivers/net/wireless/ath/ath11k/nss.c | 1762 ++++++++++++++++++++++++++++++
|
|
drivers/net/wireless/ath/ath11k/nss.h | 217 ++++
|
|
4 files changed, 1989 insertions(+)
|
|
create mode 100644 drivers/net/wireless/ath/ath11k/nss.c
|
|
create mode 100644 drivers/net/wireless/ath/ath11k/nss.h
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/Kconfig
|
|
+++ b/drivers/net/wireless/ath/ath11k/Kconfig
|
|
@@ -11,6 +11,15 @@ config ATH11K
|
|
|
|
If you choose to build a module, it'll be called ath11k.
|
|
|
|
+config ATH11K_NSS_SUPPORT
|
|
+ bool "QCA ath11k nss support"
|
|
+ depends on ATH11K
|
|
+ default y
|
|
+ ---help---
|
|
+ Enables NSS offload support for ATH11K driver
|
|
+
|
|
+ If unsure, say Y to enable NSS offload support.
|
|
+
|
|
config ATH11K_AHB
|
|
tristate "Atheros ath11k AHB support"
|
|
depends on m
|
|
--- a/drivers/net/wireless/ath/ath11k/Makefile
|
|
+++ b/drivers/net/wireless/ath/ath11k/Makefile
|
|
@@ -26,6 +26,7 @@ ath11k-$(CONFIG_THERMAL) += thermal.o
|
|
ath11k-$(CONFIG_WANT_DEV_COREDUMP) += coredump.o
|
|
ath11k-$(CPTCFG_ATH11K_SPECTRAL) += spectral.o
|
|
ath11k-$(CPTCFG_ATH11K_PKTLOG) += pktlog.o
|
|
+ath11k-$(CPTCFG_ATH11K_NSS_SUPPORT) += nss.o
|
|
|
|
obj-$(CPTCFG_ATH11K_AHB) += ath11k_ahb.o
|
|
ath11k_ahb-y += ahb.o
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/ath/ath11k/nss.c
|
|
@@ -0,0 +1,2339 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
+/*
|
|
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
|
+ */
|
|
+
|
|
+#include "mac.h"
|
|
+#include "nss.h"
|
|
+#include "core.h"
|
|
+#include "peer.h"
|
|
+#include "dp_rx.h"
|
|
+#include "dp_tx.h"
|
|
+#include "hif.h"
|
|
+#include "../../../../../net/mac80211/sta_info.h"
|
|
+
|
|
+/*-----------------------------ATH11K-NSS Helpers--------------------------*/
|
|
+
|
|
+static enum ath11k_nss_opmode
|
|
+ath11k_nss_get_vdev_opmode(struct ath11k_vif *arvif)
|
|
+{
|
|
+ switch (arvif->vdev_type) {
|
|
+ case WMI_VDEV_TYPE_AP:
|
|
+ return ATH11K_NSS_OPMODE_AP;
|
|
+ case WMI_VDEV_TYPE_STA:
|
|
+ return ATH11K_NSS_OPMODE_STA;
|
|
+ default:
|
|
+ ath11k_warn(arvif->ar->ab, "unsupported nss vdev type %d\n",
|
|
+ arvif->vdev_type);
|
|
+ }
|
|
+
|
|
+ return ATH11K_NSS_OPMODE_UNKNOWN;
|
|
+}
|
|
+
|
|
+static void ath11k_nss_wifili_stats_sync(struct ath11k_base *ab,
|
|
+ struct nss_wifili_stats_sync_msg *wlsoc_stats)
|
|
+{
|
|
+ struct nss_wifili_device_stats *devstats = &wlsoc_stats->stats;
|
|
+ struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats;
|
|
+ int i;
|
|
+
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+
|
|
+ soc_stats->err_ring_pkts += devstats->rxwbm_stats.err_src_rxdma;
|
|
+ soc_stats->invalid_rbm += devstats->rxwbm_stats.invalid_buf_mgr;
|
|
+
|
|
+ for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++)
|
|
+ soc_stats->rxdma_error[i] += devstats->rxwbm_stats.err_dma_codes[i];
|
|
+
|
|
+ for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++)
|
|
+ soc_stats->reo_error[i] += devstats->rxwbm_stats.err_reo_codes[i];
|
|
+
|
|
+ for (i = 0; i < DP_REO_DST_RING_MAX; i++)
|
|
+ soc_stats->hal_reo_error[i] += devstats->rxreo_stats[i].ring_error;
|
|
+
|
|
+ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++)
|
|
+ soc_stats->tx_err.desc_na[i] += devstats->tcl_stats[i].tcl_ring_full;
|
|
+
|
|
+
|
|
+ for (i = 0; i < NSS_WIFILI_MAX_TCL_DATA_RINGS_MSG; i++)
|
|
+ atomic_add(devstats->txcomp_stats[i].invalid_bufsrc
|
|
+ + devstats->txcomp_stats[i].invalid_cookie
|
|
+ + devstats->tx_sw_pool_stats[i].desc_alloc_fail
|
|
+ + devstats->tx_ext_sw_pool_stats[i].desc_alloc_fail,
|
|
+ &soc_stats->tx_err.misc_fail);
|
|
+
|
|
+ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++)
|
|
+ atomic_add(devstats->tx_data_stats[i].tx_send_fail_cnt,
|
|
+ &soc_stats->tx_err.misc_fail);
|
|
+
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_get_peer_tx_tid_stats(struct ath11k_base *ab, int npeers, struct nss_wifili_sojourn_peer_stats *stats)
|
|
+{
|
|
+ struct ath11k_peer *peer;
|
|
+ int peer_idx, tid_idx;
|
|
+
|
|
+ for (peer_idx = 0; peer_idx < npeers; peer_idx++) {
|
|
+
|
|
+ rcu_read_lock();
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+
|
|
+ peer = ath11k_peer_find_by_id(ab, (&stats[peer_idx])->peer_id);
|
|
+ if (!peer || !peer->sta) {
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "unable to find peer %d\n", (&stats[peer_idx])->peer_id);
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ rcu_read_unlock();
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (!peer->nss.nss_stats) {
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ rcu_read_unlock();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (tid_idx = 0; tid_idx < NSS_TX_TID_MAX; tid_idx++)
|
|
+ peer->nss.nss_stats->tx_tid_msdu[tid_idx] += (&stats[peer_idx])->stats[tid_idx].num_msdus;
|
|
+
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ rcu_read_unlock();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ath11k_nss_get_peer_stats(struct ath11k_base *ab, struct nss_wifili_peer_stats *stats)
|
|
+{
|
|
+ struct ath11k_peer *peer;
|
|
+ struct nss_wifili_peer_ctrl_stats *pstats = NULL;
|
|
+ int i, j;
|
|
+
|
|
+ for (i = 0; i < stats->npeers; i++) {
|
|
+ pstats = &stats->wpcs[i];
|
|
+
|
|
+ rcu_read_lock();
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+
|
|
+ peer = ath11k_peer_find_by_id(ab, pstats->peer_id);
|
|
+ if (!peer || !peer->sta) {
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "unable to find peer %d\n", pstats->peer_id);
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ rcu_read_unlock();
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (!peer->nss.nss_stats) {
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ rcu_read_unlock();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (pstats->tx.tx_success_cnt)
|
|
+ peer->nss.nss_stats->last_ack = jiffies;
|
|
+
|
|
+ if (pstats->rx.rx_recvd) {
|
|
+ peer->nss.nss_stats->last_rx = jiffies;
|
|
+ }
|
|
+
|
|
+ peer->nss.nss_stats->tx_packets += pstats->tx.tx_mcast_cnt +
|
|
+ pstats->tx.tx_ucast_cnt +
|
|
+ pstats->tx.tx_bcast_cnt;
|
|
+ peer->nss.nss_stats->tx_bytes += pstats->tx.tx_mcast_bytes +
|
|
+ pstats->tx.tx_ucast_bytes +
|
|
+ pstats->tx.tx_bcast_bytes;
|
|
+ peer->nss.nss_stats->tx_retries += pstats->tx.retries;
|
|
+
|
|
+ for (j = 0; j < NSS_WIFILI_TQM_RR_MAX; j++)
|
|
+ peer->nss.nss_stats->tx_failed += pstats->tx.dropped.drop_stats[j];
|
|
+
|
|
+ peer->nss.nss_stats->rx_packets += pstats->rx.rx_recvd;
|
|
+ peer->nss.nss_stats->rx_bytes += pstats->rx.rx_recvd_bytes;
|
|
+ peer->nss.nss_stats->rx_dropped += pstats->rx.err.mic_err +
|
|
+ pstats->rx.err.decrypt_err;
|
|
+
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ rcu_read_unlock();
|
|
+ }
|
|
+}
|
|
+
|
|
+void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, struct htt_rx_ring_tlv_filter *tlv_filter)
|
|
+{
|
|
+ if (ab->nss.enabled)
|
|
+ tlv_filter->rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS;
|
|
+}
|
|
+
|
|
+static u32 ath11k_nss_cipher_type(struct ath11k_base *ab, u32 cipher)
|
|
+{
|
|
+ switch (cipher) {
|
|
+ case WLAN_CIPHER_SUITE_CCMP:
|
|
+ return PEER_SEC_TYPE_AES_CCMP;
|
|
+ case WLAN_CIPHER_SUITE_TKIP:
|
|
+ return PEER_SEC_TYPE_TKIP;
|
|
+ case WLAN_CIPHER_SUITE_CCMP_256:
|
|
+ return PEER_SEC_TYPE_AES_CCMP_256;
|
|
+ case WLAN_CIPHER_SUITE_GCMP:
|
|
+ return PEER_SEC_TYPE_AES_GCMP;
|
|
+ case WLAN_CIPHER_SUITE_GCMP_256:
|
|
+ return PEER_SEC_TYPE_AES_GCMP_256;
|
|
+ default:
|
|
+ ath11k_warn(ab, "unknown cipher type %d\n", cipher);
|
|
+ return PEER_SEC_TYPE_NONE;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ath11k_nss_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 void ath11k_nss_tx_encap_raw(struct sk_buff *skb)
|
|
+{
|
|
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
+ struct ieee80211_hdr *hdr = (void *)skb->data;
|
|
+ u32 cipher;
|
|
+
|
|
+ if (!ieee80211_has_protected(hdr->frame_control))
|
|
+ return;
|
|
+
|
|
+ /* Include length for MIC */
|
|
+ skb_put(skb, IEEE80211_CCMP_MIC_LEN);
|
|
+
|
|
+ /* Include length for ICV if TKIP is used */
|
|
+ cipher = info->control.hw_key->cipher;
|
|
+ if (cipher == WLAN_CIPHER_SUITE_TKIP)
|
|
+ skb_put(skb, IEEE80211_TKIP_ICV_LEN);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_peer_mem_free(struct ath11k_base *ab, u32 peer_id)
|
|
+{
|
|
+ struct ath11k_peer *peer;
|
|
+
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+
|
|
+ peer = ath11k_peer_find_by_id(ab, peer_id);
|
|
+ if (!peer) {
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ ath11k_warn(ab, "ath11k_nss: unable to free peer mem%d\n",
|
|
+ peer_id);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dma_unmap_single(ab->dev, peer->nss.paddr,
|
|
+ WIFILI_NSS_PEER_BYTE_SIZE, DMA_TO_DEVICE);
|
|
+
|
|
+ kfree(peer->nss.vaddr);
|
|
+ if (peer->nss.nss_stats) {
|
|
+ kfree(peer->nss.nss_stats);
|
|
+ peer->nss.nss_stats = NULL;
|
|
+ }
|
|
+
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss peer %d mem freed\n", peer_id);
|
|
+}
|
|
+
|
|
+/*-----------------------------Events/Callbacks------------------------------*/
|
|
+
|
|
+void ath11k_nss_wifili_event_receive(struct ath11k_base *ab, struct nss_wifili_msg *msg)
|
|
+{
|
|
+ u32 msg_type = msg->cm.type;
|
|
+ enum nss_cmn_response response = msg->cm.response;
|
|
+ u32 error = msg->cm.error;
|
|
+ u32 peer_id;
|
|
+ struct nss_wifili_peer_stats *peer_stats;
|
|
+ struct nss_wifili_sojourn_stats_msg *stats_msg;
|
|
+
|
|
+ if (!ab)
|
|
+ return;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss wifili event received %d response %d error %d\n",
|
|
+ msg_type, response, error);
|
|
+
|
|
+ switch (msg_type) {
|
|
+ case NSS_WIFILI_INIT_MSG:
|
|
+ case NSS_WIFILI_PDEV_INIT_MSG:
|
|
+ case NSS_WIFILI_START_MSG:
|
|
+ case NSS_WIFILI_SOC_RESET_MSG:
|
|
+ case NSS_WIFILI_STOP_MSG:
|
|
+ case NSS_WIFILI_PDEV_DEINIT_MSG:
|
|
+ ab->nss.response = response;
|
|
+ complete(&ab->nss.complete);
|
|
+ break;
|
|
+
|
|
+ case NSS_WIFILI_PEER_CREATE_MSG:
|
|
+ if (response != NSS_CMN_RESPONSE_EMSG)
|
|
+ break;
|
|
+
|
|
+ peer_id = (&msg->msg.peermsg)->peer_id;
|
|
+
|
|
+ /* free peer memory allocated during peer create due to failure */
|
|
+ ath11k_nss_peer_mem_free(ab, peer_id);
|
|
+ break;
|
|
+ case NSS_WIFILI_PEER_DELETE_MSG:
|
|
+ peer_id = (&msg->msg.peermsg)->peer_id;
|
|
+
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG)
|
|
+ ath11k_warn(ab, "ath11k_nss: peer delete failed%d\n",
|
|
+ peer_id);
|
|
+
|
|
+ /* free peer memory allocated during peer create irrespective of
|
|
+ * delete status
|
|
+ */
|
|
+ ath11k_nss_peer_mem_free(ab, peer_id);
|
|
+ break;
|
|
+ case NSS_WIFILI_PEER_SECURITY_TYPE_MSG:
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG)
|
|
+ ath11k_warn(ab, "peer securty config failed\n");
|
|
+
|
|
+ break;
|
|
+ case NSS_WIFILI_STATS_MSG:
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG) {
|
|
+ ath11k_warn(ab, "soc_dp_stats failed to get updated\n");
|
|
+ break;
|
|
+ }
|
|
+ ath11k_nss_wifili_stats_sync(ab, &msg->msg.wlsoc_stats);
|
|
+ break;
|
|
+ case NSS_WIFILI_PEER_STATS_MSG:
|
|
+ peer_stats = &msg->msg.peer_stats.stats;
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG) {
|
|
+ ath11k_warn(ab, "peer stats msg failed with error = %u\n", error);
|
|
+ break;
|
|
+ }
|
|
+ ath11k_nss_get_peer_stats(ab, peer_stats);
|
|
+ break;
|
|
+ case NSS_WIFILI_SOJOURN_STATS_MSG:
|
|
+ stats_msg = &msg->msg.sj_stats_msg;
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG) {
|
|
+ ath11k_warn(ab, "tid peer stats msg failed with error = %u\n", error);
|
|
+ break;
|
|
+ }
|
|
+ ath11k_nss_get_peer_tx_tid_stats(ab, stats_msg->npeers, &stats_msg->sj_peer_stats[0]);
|
|
+ break;
|
|
+ case NSS_WIFILI_TID_REOQ_SETUP_MSG:
|
|
+ /* TODO setup tidq */
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "unhandled event %d\n", msg_type);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void ath11k_nss_process_mic_error(struct ath11k_base *ab, struct sk_buff *skb)
|
|
+{
|
|
+ struct ath11k_vif *arvif;
|
|
+ struct ath11k_peer *peer = NULL;
|
|
+ struct hal_rx_desc *desc = (struct hal_rx_desc *)skb->data;
|
|
+ struct wireless_dev *wdev;
|
|
+ u16 peer_id;
|
|
+ u8 peer_addr[ETH_ALEN];
|
|
+ u8 ucast_keyidx, mcast_keyidx;
|
|
+ bool is_mcbc;
|
|
+
|
|
+ if (!ath11k_dp_rx_h_msdu_end_first_msdu(ab, desc))
|
|
+ goto fail;
|
|
+
|
|
+ is_mcbc = ath11k_dp_rx_h_attn_is_mcbc(ab, desc);
|
|
+ peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(ab, desc);
|
|
+
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+ peer = ath11k_peer_find_by_id(ab, peer_id);
|
|
+ if (!peer) {
|
|
+ ath11k_info(ab, "ath11k_nss:peer not found");
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if (!peer->vif) {
|
|
+ ath11k_warn(ab, "ath11k_nss:vif not found");
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ ether_addr_copy(peer_addr, peer->addr);
|
|
+ mcast_keyidx = peer->mcast_keyidx;
|
|
+ ucast_keyidx = peer->ucast_keyidx;
|
|
+ arvif = ath11k_vif_to_arvif(peer->vif);
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+
|
|
+ if (!arvif->is_started) {
|
|
+ ath11k_warn(ab, "ath11k_nss:arvif not started");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ wdev = ieee80211_vif_to_wdev_relaxed(arvif->vif);
|
|
+ if (!wdev) {
|
|
+ ath11k_warn(ab, "ath11k_nss: wdev is null\n");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if (!wdev->netdev) {
|
|
+ ath11k_warn(ab, "ath11k_nss: netdev is null\n");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ cfg80211_michael_mic_failure(wdev->netdev, peer_addr,
|
|
+ is_mcbc ? NL80211_KEYTYPE_GROUP :
|
|
+ NL80211_KEYTYPE_PAIRWISE,
|
|
+ is_mcbc ? mcast_keyidx : ucast_keyidx,
|
|
+ NULL, GFP_ATOMIC);
|
|
+ kfree(skb);
|
|
+ return;
|
|
+
|
|
+fail:
|
|
+ kfree(skb);
|
|
+ ath11k_warn(ab, "ath11k_nss: Failed to handle mic error\n");
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void
|
|
+ath11k_nss_wifili_ext_callback_fn(struct ath11k_base *ab, struct sk_buff *skb,
|
|
+ __attribute__((unused)) struct napi_struct *napi)
|
|
+{
|
|
+ struct nss_wifili_soc_per_packet_metadata *wepm;
|
|
+
|
|
+ wepm = (struct nss_wifili_soc_per_packet_metadata *)(skb->head +
|
|
+ NSS_WIFILI_SOC_PER_PACKET_METADATA_OFFSET);
|
|
+
|
|
+ switch (wepm->pkt_type) {
|
|
+ case NSS_WIFILI_SOC_EXT_DATA_PKT_MIC_ERROR:
|
|
+ ath11k_nss_process_mic_error(ab, skb);
|
|
+ break;
|
|
+ default:
|
|
+ kfree(skb);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void ath11k_nss_vdev_cfg_cb(void *app_data, struct nss_cmn_msg *msg)
|
|
+{
|
|
+ struct ath11k_vif *arvif = (struct ath11k_vif *)app_data;
|
|
+
|
|
+ if (!arvif)
|
|
+ return;
|
|
+
|
|
+ ath11k_dbg(arvif->ar->ab, ATH11K_DBG_NSS, "vdev cfg msg callback received msg:%d rsp:%d\n",
|
|
+ msg->type, msg->response);
|
|
+
|
|
+ complete(&arvif->nss.complete);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_vdev_event_receive(void *dev, struct nss_cmn_msg *vdev_msg)
|
|
+{
|
|
+ /*TODO*/
|
|
+}
|
|
+
|
|
+static void
|
|
+ath11k_nss_vdev_special_data_receive(struct net_device *dev, struct sk_buff *skb,
|
|
+ __attribute__((unused)) struct napi_struct *napi)
|
|
+{
|
|
+ /* TODO */
|
|
+}
|
|
+
|
|
+/* TODO: move to mac80211 after cleanups/refactoring required after feature completion */
|
|
+static int ath11k_nss_deliver_rx(struct ieee80211_vif *vif, struct sk_buff *skb,
|
|
+ bool eth, int data_offs, struct napi_struct *napi)
|
|
+{
|
|
+ struct sk_buff_head subframe_list;
|
|
+ struct ieee80211_hdr *hdr;
|
|
+ struct sk_buff *subframe;
|
|
+ struct net_device *dev;
|
|
+ int hdr_len;
|
|
+ u8 *qc;
|
|
+
|
|
+ dev = skb->dev;
|
|
+
|
|
+ if (eth)
|
|
+ goto deliver_msdu;
|
|
+
|
|
+ hdr = (struct ieee80211_hdr *)skb->data;
|
|
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
|
|
+
|
|
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
|
|
+ qc = ieee80211_get_qos_ctl(hdr);
|
|
+ if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
|
|
+ goto deliver_amsdu;
|
|
+ }
|
|
+
|
|
+ if (ieee80211_data_to_8023_exthdr(skb, NULL, vif->addr, vif->type,
|
|
+ data_offs - hdr_len)) {
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+deliver_msdu:
|
|
+ skb->protocol = eth_type_trans(skb, dev);
|
|
+ napi_gro_receive(napi, skb);
|
|
+ return 0;
|
|
+
|
|
+deliver_amsdu:
|
|
+ /* Move to the start of the first subframe */
|
|
+ skb_pull(skb, data_offs);
|
|
+
|
|
+ __skb_queue_head_init(&subframe_list);
|
|
+
|
|
+ /* create list containing all the subframes */
|
|
+ ieee80211_amsdu_to_8023s(skb, &subframe_list, NULL,
|
|
+ vif->type, 0, NULL, NULL);
|
|
+
|
|
+ /* This shouldn't happen, indicating error during defragmentation */
|
|
+ if (skb_queue_empty(&subframe_list))
|
|
+ return -EINVAL;
|
|
+
|
|
+ while (!skb_queue_empty(&subframe_list)) {
|
|
+ subframe = __skb_dequeue(&subframe_list);
|
|
+ subframe->protocol = eth_type_trans(subframe, dev);
|
|
+ napi_gro_receive(napi, subframe);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_undecap_raw(struct ath11k_vif *arvif, struct sk_buff *skb,
|
|
+ int *data_offset)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ enum hal_encrypt_type enctype;
|
|
+ struct ath11k_peer *peer = NULL;
|
|
+ struct ieee80211_hdr *hdr;
|
|
+ int hdr_len;
|
|
+
|
|
+ hdr = (struct ieee80211_hdr *)skb->data;
|
|
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
|
|
+
|
|
+ *data_offset = hdr_len;
|
|
+
|
|
+ /* FCS is included in the raw mode skb, we can trim it, fcs error
|
|
+ * packets are not expected to be received in this path
|
|
+ */
|
|
+ skb_trim(skb, skb->len - FCS_LEN);
|
|
+
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+
|
|
+ peer = ath11k_peer_find_by_addr(ab, hdr->addr2);
|
|
+ if (!peer) {
|
|
+ ath11k_warn(ab, "peer not found for raw/nwifi undecap, drop this packet\n");
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ enctype = peer->sec_type;
|
|
+
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+
|
|
+ *data_offset += ath11k_dp_rx_crypto_param_len(ar, enctype);
|
|
+
|
|
+ /* Strip ICV, MIC, MMIC */
|
|
+ skb_trim(skb, skb->len -
|
|
+ ath11k_dp_rx_crypto_mic_len(ar, enctype));
|
|
+
|
|
+ skb_trim(skb, skb->len -
|
|
+ ath11k_dp_rx_crypto_icv_len(ar, enctype));
|
|
+
|
|
+ if (enctype == HAL_ENCRYPT_TYPE_TKIP_MIC)
|
|
+ skb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_undecap_nwifi(struct ath11k_vif *arvif, struct sk_buff *skb,
|
|
+ int *data_offset)
|
|
+{
|
|
+ struct ieee80211_hdr *hdr;
|
|
+ int hdr_len;
|
|
+
|
|
+ hdr = (struct ieee80211_hdr *)skb->data;
|
|
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
|
|
+
|
|
+ *data_offset = hdr_len;
|
|
+
|
|
+ /* We dont receive the IV from nss host on slow path
|
|
+ * hence we can return only the header length as offset.
|
|
+ **/
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+ath11k_nss_vdev_data_receive(struct net_device *dev, struct sk_buff *skb,
|
|
+ __attribute__((unused)) struct napi_struct *napi)
|
|
+{
|
|
+ enum ath11k_hw_txrx_mode decap_type;
|
|
+ struct wireless_dev *wdev;
|
|
+ struct ieee80211_vif *vif;
|
|
+ struct ath11k_vif *arvif;
|
|
+ struct ath11k_base *ab;
|
|
+ bool eth_decap = false;
|
|
+ int data_offs = 0;
|
|
+ int ret;
|
|
+
|
|
+ if (!dev) {
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ wdev = dev->ieee80211_ptr;
|
|
+ if (!wdev) {
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ vif = wdev_to_ieee80211_vif(wdev);
|
|
+ if (!vif) {
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ arvif = (struct ath11k_vif *)vif->drv_priv;
|
|
+ if (!arvif) {
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ab = arvif->ar->ab;
|
|
+
|
|
+ skb->dev = dev;
|
|
+
|
|
+ /* log the original skb received from nss */
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "dp rx msdu from nss: ",
|
|
+ skb->data, skb->len);
|
|
+
|
|
+ decap_type = arvif->nss.decap;
|
|
+
|
|
+ switch (decap_type) {
|
|
+ case ATH11K_HW_TXRX_RAW:
|
|
+ ret = ath11k_nss_undecap_raw(arvif, skb, &data_offs);
|
|
+ break;
|
|
+ case ATH11K_HW_TXRX_NATIVE_WIFI:
|
|
+ ret = ath11k_nss_undecap_nwifi(arvif, skb, &data_offs);
|
|
+ break;
|
|
+ case ATH11K_HW_TXRX_ETHERNET:
|
|
+ /* no changes required for ethernet decap */
|
|
+ ret = 0;
|
|
+ eth_decap = true;
|
|
+ break;
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "error in nss rx undecap, type %d err %d\n", decap_type,
|
|
+ ret);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi);
|
|
+}
|
|
+
|
|
+int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb)
|
|
+{
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+ int encap_type = ath11k_dp_tx_get_encap_type(arvif, skb);
|
|
+ struct ath11k_soc_dp_stats *soc_stats = &ar->ab->soc_stats;
|
|
+
|
|
+ if(encap_type != arvif->nss.encap) {
|
|
+ ath11k_warn(ar->ab, "encap mismatch in nss tx skb encap type %d" \
|
|
+ "vif encap type %d\n", encap_type, arvif->nss.encap);
|
|
+ goto drop;
|
|
+ }
|
|
+
|
|
+ if (encap_type == HAL_TCL_ENCAP_TYPE_ETHERNET)
|
|
+ goto send;
|
|
+
|
|
+ if (encap_type == HAL_TCL_ENCAP_TYPE_RAW)
|
|
+ ath11k_nss_tx_encap_raw(skb);
|
|
+ else
|
|
+ ath11k_nss_tx_encap_nwifi(skb);
|
|
+
|
|
+send:
|
|
+ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "", "nss tx msdu: ",
|
|
+ skb->data, skb->len);
|
|
+
|
|
+ status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb, arvif->nss.if_num);
|
|
+
|
|
+ if (status != NSS_TX_SUCCESS)
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss tx failure\n");
|
|
+
|
|
+ return status;
|
|
+drop:
|
|
+ atomic_inc(&soc_stats->tx_err.misc_fail);
|
|
+ WARN_ON_ONCE(1);
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val)
|
|
+{
|
|
+ struct nss_wifi_vdev_msg *vdev_msg = NULL;
|
|
+ struct nss_wifi_vdev_cmd_msg *vdev_cmd;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+
|
|
+ if (!ar->ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ /* Monitor interface is not offloaded to nss */
|
|
+ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
|
|
+ return 0;
|
|
+
|
|
+ vdev_msg = kzalloc(sizeof(*vdev_msg), GFP_ATOMIC);
|
|
+ if (!vdev_msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* TODO: Convert to function for conversion in case of many
|
|
+ * such commands
|
|
+ */
|
|
+ if (cmd == NSS_WIFI_VDEV_SECURITY_TYPE_CMD)
|
|
+ val = ath11k_nss_cipher_type(ar->ab, val);
|
|
+
|
|
+ if (cmd == NSS_WIFI_VDEV_ENCAP_TYPE_CMD)
|
|
+ arvif->nss.encap = val;
|
|
+ else if (cmd == NSS_WIFI_VDEV_DECAP_TYPE_CMD)
|
|
+ arvif->nss.decap = val;
|
|
+
|
|
+ vdev_cmd = &vdev_msg->msg.vdev_cmd;
|
|
+ vdev_cmd->cmd = cmd;
|
|
+ vdev_cmd->value = val;
|
|
+
|
|
+ nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num,
|
|
+ NSS_WIFI_VDEV_INTERFACE_CMD_MSG,
|
|
+ sizeof(struct nss_wifi_vdev_cmd_msg),
|
|
+ NULL, NULL);
|
|
+
|
|
+ status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab, "nss vdev set cmd failure cmd:%d val:%d",
|
|
+ cmd, val);
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev set cmd success cmd:%d val:%d\n",
|
|
+ cmd, val);
|
|
+free:
|
|
+ kfree(vdev_msg);
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_vdev_configure(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ struct nss_wifi_vdev_msg *vdev_msg;
|
|
+ struct nss_wifi_vdev_config_msg *vdev_cfg;
|
|
+ nss_tx_status_t status;
|
|
+ int ret;
|
|
+
|
|
+ vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC);
|
|
+ if (!vdev_msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ vdev_cfg = &vdev_msg->msg.vdev_config;
|
|
+
|
|
+ vdev_cfg->radio_ifnum = ar->nss.if_num;
|
|
+ vdev_cfg->vdev_id = arvif->vdev_id;
|
|
+
|
|
+ vdev_cfg->opmode = ath11k_nss_get_vdev_opmode(arvif);
|
|
+
|
|
+ ether_addr_copy(vdev_cfg->mac_addr, arvif->vif->addr);
|
|
+
|
|
+ reinit_completion(&arvif->nss.complete);
|
|
+
|
|
+ nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num,
|
|
+ NSS_WIFI_VDEV_INTERFACE_CONFIGURE_MSG,
|
|
+ sizeof(struct nss_wifi_vdev_config_msg),
|
|
+ (nss_wifi_vdev_msg_callback_t *)ath11k_nss_vdev_cfg_cb,
|
|
+ arvif);
|
|
+
|
|
+ status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab, "failed to configure nss vdev nss_err:%d\n",
|
|
+ status);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ ret = wait_for_completion_timeout(&arvif->nss.complete,
|
|
+ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
|
|
+ if (!ret) {
|
|
+ ath11k_warn(ar->ab, "timeout in receiving nss vdev cfg response\n");
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+free:
|
|
+ kfree(vdev_msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void ath11k_nss_vdev_unregister(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+
|
|
+ switch (arvif->vif->type) {
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ nss_unregister_wifi_vdev_if(arvif->nss.if_num);
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "unregistered nss vdev %d \n",
|
|
+ arvif->nss.if_num);
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_warn(ab, "unsupported interface type %d for nss vdev unregister\n",
|
|
+ arvif->vif->type);
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ath11k_nss_vdev_register(struct ath11k_vif *arvif,
|
|
+ struct net_device *netdev)
|
|
+{
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ struct ath11k_base *ab = ar->ab;
|
|
+ nss_tx_status_t status;
|
|
+ u32 features = 0;
|
|
+
|
|
+ switch (arvif->vif->type) {
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ status = nss_register_wifi_vdev_if(ar->nss.ctx,
|
|
+ arvif->nss.if_num,
|
|
+ ath11k_nss_vdev_data_receive,
|
|
+ ath11k_nss_vdev_special_data_receive,
|
|
+ ath11k_nss_vdev_event_receive,
|
|
+ netdev, features);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "failed to register nss vdev if_num %d nss_err:%d\n",
|
|
+ arvif->nss.if_num, status);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "registered nss vdev if_num %d\n",
|
|
+ arvif->nss.if_num);
|
|
+
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_warn(ab, "unsupported interface type %d for nss vdev register\n",
|
|
+ arvif->vif->type);
|
|
+ return -ENOTSUPP;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ath11k_nss_vdev_free(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ nss_tx_status_t status;
|
|
+
|
|
+ switch (arvif->vif->type) {
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ status = nss_dynamic_interface_dealloc_node(
|
|
+ arvif->nss.if_num,
|
|
+ NSS_DYNAMIC_INTERFACE_TYPE_VAP);
|
|
+ if (status != NSS_TX_SUCCESS)
|
|
+ ath11k_warn(ab, "failed to free nss vdev nss_err:%d\n",
|
|
+ status);
|
|
+ else
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "nss vdev interface deallocated\n");
|
|
+
|
|
+ return;
|
|
+ default:
|
|
+ ath11k_warn(ab, "unsupported interface type %d for nss vdev dealloc\n",
|
|
+ arvif->vif->type);
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ath11k_nss_vdev_alloc(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ enum nss_dynamic_interface_type if_type;
|
|
+ int if_num;
|
|
+
|
|
+ /* Initialize completion for verifying NSS message response */
|
|
+ init_completion(&arvif->nss.complete);
|
|
+
|
|
+ switch (arvif->vif->type) {
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ if_type = NSS_DYNAMIC_INTERFACE_TYPE_VAP;
|
|
+ /* allocate interface context with NSS driver for the new vdev */
|
|
+ if_num = nss_dynamic_interface_alloc_node(if_type);
|
|
+ if (if_num < 0) {
|
|
+ ath11k_warn(ab, "failed to allocate nss vdev\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ arvif->nss.if_num = if_num;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "allocated nss vdev if_num %d\n",
|
|
+ arvif->nss.if_num);
|
|
+
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_warn(ab, "unsupported interface type %d for nss vdev alloc\n",
|
|
+ arvif->vif->type);
|
|
+ return -ENOTSUPP;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ath11k_nss_vdev_create(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ struct ath11k_base *ab = ar->ab;
|
|
+ struct wireless_dev *wdev;
|
|
+ int ret;
|
|
+
|
|
+ if (!ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ /* Monitor interface is not offloaded to nss */
|
|
+ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
|
|
+ return 0;
|
|
+
|
|
+ if (arvif->nss.created)
|
|
+ return 0;
|
|
+
|
|
+ wdev = ieee80211_vif_to_wdev_relaxed(arvif->vif);
|
|
+ if (!wdev) {
|
|
+ ath11k_warn(ab, "ath11k_nss: wdev is null\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (!wdev->netdev) {
|
|
+ ath11k_warn(ab, "ath11k_nss: netdev is null\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = ath11k_nss_vdev_alloc(arvif);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = ath11k_nss_vdev_register(arvif, wdev->netdev);
|
|
+ if (ret)
|
|
+ goto free_vdev;
|
|
+
|
|
+ switch (arvif->vif->type) {
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ ret = ath11k_nss_vdev_configure(arvif);
|
|
+ if (ret)
|
|
+ goto unregister_vdev;
|
|
+
|
|
+ break;
|
|
+ default:
|
|
+ ret = -ENOTSUPP;
|
|
+ goto unregister_vdev;
|
|
+ }
|
|
+
|
|
+ arvif->nss.created = true;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "nss vdev interface created ctx %pK, ifnum %d\n",
|
|
+ ar->nss.ctx, arvif->nss.if_num);
|
|
+
|
|
+ return ret;
|
|
+
|
|
+unregister_vdev:
|
|
+ ath11k_nss_vdev_unregister(arvif);
|
|
+free_vdev:
|
|
+ ath11k_nss_vdev_free(arvif);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void ath11k_nss_vdev_delete(struct ath11k_vif *arvif)
|
|
+{
|
|
+ if (!arvif->ar->ab->nss.enabled)
|
|
+ return;
|
|
+
|
|
+ /* Monitor interface is not offloaded to nss */
|
|
+ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
|
|
+ return;
|
|
+
|
|
+ if (!arvif->nss.created)
|
|
+ return;
|
|
+
|
|
+ ath11k_nss_vdev_unregister(arvif);
|
|
+
|
|
+ ath11k_nss_vdev_free(arvif);
|
|
+
|
|
+ arvif->nss.created = false;
|
|
+}
|
|
+
|
|
+int ath11k_nss_vdev_up(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct nss_wifi_vdev_msg *vdev_msg = NULL;
|
|
+ struct nss_wifi_vdev_enable_msg *vdev_en;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (!ar->ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ /* Monitor interface is not offloaded to nss */
|
|
+ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
|
|
+ return 0;
|
|
+
|
|
+ vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC);
|
|
+ if (!vdev_msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ vdev_en = &vdev_msg->msg.vdev_enable;
|
|
+
|
|
+ ether_addr_copy(vdev_en->mac_addr, arvif->vif->addr);
|
|
+
|
|
+ nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num,
|
|
+ NSS_WIFI_VDEV_INTERFACE_UP_MSG,
|
|
+ sizeof(struct nss_wifi_vdev_enable_msg),
|
|
+ NULL, NULL);
|
|
+
|
|
+ status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab, "nss vdev up tx msg error %d\n", status);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev up tx msg success\n");
|
|
+free:
|
|
+ kfree(vdev_msg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ath11k_nss_vdev_down(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct nss_wifi_vdev_msg *vdev_msg = NULL;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (!ar->ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ /* Monitor interface is not offloaded to nss */
|
|
+ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
|
|
+ return 0;
|
|
+
|
|
+ vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC);
|
|
+ if (!vdev_msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num,
|
|
+ NSS_WIFI_VDEV_INTERFACE_DOWN_MSG,
|
|
+ sizeof(struct nss_wifi_vdev_disable_msg),
|
|
+ NULL, NULL);
|
|
+
|
|
+ status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab, "nss vdev down tx msg error %d\n", status);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev down tx msg success\n");
|
|
+free:
|
|
+ kfree(vdev_msg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*----------------------------Peer Setup/Config -----------------------------*/
|
|
+
|
|
+int ath11k_nss_set_peer_sec_type(struct ath11k *ar,
|
|
+ struct ath11k_peer *peer,
|
|
+ struct ieee80211_key_conf *key_conf)
|
|
+{
|
|
+ struct nss_wifili_peer_security_type_msg *sec_msg;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ struct nss_wifili_msg *wlmsg;
|
|
+ nss_tx_status_t status;
|
|
+ u8 *mic_key;
|
|
+
|
|
+ if (!ar->ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ sec_msg = &wlmsg->msg.securitymsg;
|
|
+ sec_msg->peer_id = peer->peer_id;
|
|
+
|
|
+ /* 0 -unicast , 1 - mcast/unicast */
|
|
+ sec_msg->pkt_type = !(key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
|
|
+
|
|
+ sec_msg->security_type = ath11k_nss_cipher_type(ar->ab,
|
|
+ key_conf->cipher);
|
|
+
|
|
+ if (sec_msg->security_type == PEER_SEC_TYPE_TKIP) {
|
|
+ mic_key = &key_conf->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY];
|
|
+ memcpy(&sec_msg->mic_key[0], mic_key, NSS_WIFILI_MIC_KEY_LEN);
|
|
+ }
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num,
|
|
+ NSS_WIFILI_PEER_SECURITY_TYPE_MSG,
|
|
+ sizeof(struct nss_wifili_peer_security_type_msg),
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab, "nss peer %d security cfg fail %d\n",
|
|
+ peer->peer_id, status);
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss peer id %d security cfg complete\n",
|
|
+ peer->peer_id);
|
|
+free:
|
|
+ kfree(wlmsg);
|
|
+ return status;
|
|
+}
|
|
+
|
|
+void ath11k_nss_update_sta_stats(struct station_info *sinfo,
|
|
+ struct ieee80211_sta *sta,
|
|
+ struct ath11k_sta *arsta)
|
|
+{
|
|
+ struct sta_info *stainfo;
|
|
+ struct ath11k_peer *peer;
|
|
+ int tid_idx;
|
|
+ struct ath11k *ar = arsta->arvif->ar;
|
|
+ struct ath11k_base *ab = ar->ab;
|
|
+
|
|
+ if (!ab->nss.enabled)
|
|
+ return;
|
|
+
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+ peer = ath11k_peer_find_by_addr(arsta->arvif->ar->ab, sta->addr);
|
|
+ if (!peer) {
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "unable to find peer %pM\n",
|
|
+ sta->addr);
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ if (!peer->nss.nss_stats)
|
|
+ goto exit;
|
|
+
|
|
+ stainfo = container_of(sta, struct sta_info, sta);
|
|
+ if (peer->nss.nss_stats->last_rx &&
|
|
+ time_after((unsigned long)peer->nss.nss_stats->last_rx, stainfo->rx_stats.last_rx))
|
|
+ stainfo->rx_stats.last_rx = peer->nss.nss_stats->last_rx;
|
|
+
|
|
+ if (peer->nss.nss_stats->last_ack &&
|
|
+ time_after((unsigned long)peer->nss.nss_stats->last_ack, stainfo->status_stats.last_ack))
|
|
+ stainfo->status_stats.last_ack = peer->nss.nss_stats->last_ack;
|
|
+
|
|
+ stainfo->rx_stats.dropped += peer->nss.nss_stats->rx_dropped -
|
|
+ peer->nss.nss_stats->last_rxdrop;
|
|
+ peer->nss.nss_stats->last_rxdrop = peer->nss.nss_stats->rx_dropped;
|
|
+
|
|
+ sinfo->tx_packets = 0;
|
|
+ /* Add only ac-0 count as mgmt packets uses WME_AC_BE */
|
|
+ sinfo->tx_packets += stainfo->tx_stats.packets[WME_AC_BE];
|
|
+ sinfo->tx_packets += peer->nss.nss_stats->tx_packets;
|
|
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
|
|
+ sinfo->tx_bytes = 0;
|
|
+
|
|
+ /* Add only ac-0 count as mgmt packets uses WME_AC_BE */
|
|
+ sinfo->tx_bytes += stainfo->tx_stats.bytes[WME_AC_BE];
|
|
+ sinfo->tx_bytes += peer->nss.nss_stats->tx_bytes;
|
|
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);
|
|
+
|
|
+ sinfo->tx_failed = stainfo->status_stats.retry_failed +
|
|
+ peer->nss.nss_stats->tx_failed;
|
|
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
|
|
+
|
|
+ sinfo->tx_retries = stainfo->status_stats.retry_count +
|
|
+ peer->nss.nss_stats->tx_retries;
|
|
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
|
|
+
|
|
+ sinfo->rx_packets = stainfo->rx_stats.packets +
|
|
+ peer->nss.nss_stats->rx_packets;
|
|
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
|
|
+
|
|
+ sinfo->rx_bytes = stainfo->rx_stats.bytes +
|
|
+ peer->nss.nss_stats->rx_bytes;
|
|
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);
|
|
+
|
|
+ if (peer->nss.nss_stats->rxrate.legacy || peer->nss.nss_stats->rxrate.nss) {
|
|
+ if (peer->nss.nss_stats->rxrate.legacy) {
|
|
+ sinfo->rxrate.legacy = peer->nss.nss_stats->rxrate.legacy;
|
|
+ } else {
|
|
+ sinfo->rxrate.mcs = peer->nss.nss_stats->rxrate.mcs;
|
|
+ sinfo->rxrate.nss = peer->nss.nss_stats->rxrate.nss;
|
|
+ sinfo->rxrate.bw = peer->nss.nss_stats->rxrate.bw;
|
|
+ sinfo->rxrate.he_gi = peer->nss.nss_stats->rxrate.he_gi;
|
|
+ sinfo->rxrate.he_dcm = peer->nss.nss_stats->rxrate.he_dcm;
|
|
+ sinfo->rxrate.he_ru_alloc = peer->nss.nss_stats->rxrate.he_ru_alloc;
|
|
+ }
|
|
+ sinfo->rxrate.flags = peer->nss.nss_stats->rxrate.flags;
|
|
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
|
|
+ }
|
|
+
|
|
+ if (!sinfo->pertid && cfg80211_sinfo_alloc_tid_stats(sinfo, GFP_KERNEL))
|
|
+ goto exit;
|
|
+
|
|
+ for (tid_idx = 0; tid_idx < NSS_TX_TID_MAX; tid_idx++) {
|
|
+ sinfo->pertid[tid_idx].tx_msdu = peer->nss.nss_stats->tx_tid_msdu[tid_idx];
|
|
+ sinfo->pertid[tid_idx].filled |= BIT_ULL(NL80211_TID_STATS_TX_MSDU);
|
|
+ }
|
|
+
|
|
+ for (tid_idx = 0; tid_idx < IEEE80211_NUM_TIDS; tid_idx++) {
|
|
+ sinfo->pertid[tid_idx].rx_msdu = peer->nss.nss_stats->rx_tid_msdu[tid_idx];
|
|
+ sinfo->pertid[tid_idx].filled |= BIT_ULL(NL80211_TID_STATS_RX_MSDU);
|
|
+ }
|
|
+
|
|
+exit:
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+}
|
|
+
|
|
+void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info,
|
|
+ struct ath11k_peer *peer)
|
|
+{
|
|
+ struct ath11k_sta *arsta = (struct ath11k_sta *)peer->sta->drv_priv;
|
|
+ u16 ath11k_hal_rx_legacy_rates[] =
|
|
+ { 10, 20, 55, 60, 90, 110, 120, 180, 240, 360, 480, 540 };
|
|
+ u16 rate = 0;
|
|
+ u16 num_msdu;
|
|
+ struct ath11k *ar = arsta->arvif->ar;
|
|
+ struct ath11k_base *ab = ar->ab;
|
|
+
|
|
+ if (!ab->nss.enabled)
|
|
+ return;
|
|
+
|
|
+ if (!peer->nss.nss_stats)
|
|
+ return;
|
|
+
|
|
+ if ((ppdu_info->preamble_type == WMI_RATE_PREAMBLE_CCK ||
|
|
+ ppdu_info->preamble_type == WMI_RATE_PREAMBLE_OFDM) &&
|
|
+ (ppdu_info->rate < ATH11K_LEGACY_NUM)) {
|
|
+ rate = ath11k_hal_rx_legacy_rates[ppdu_info->rate];
|
|
+ }
|
|
+
|
|
+ memset(&peer->nss.nss_stats->rxrate, 0, sizeof(peer->nss.nss_stats->rxrate));
|
|
+
|
|
+ switch (ppdu_info->preamble_type) {
|
|
+ case WMI_RATE_PREAMBLE_OFDM:
|
|
+ peer->nss.nss_stats->rxrate.legacy = rate;
|
|
+ break;
|
|
+ case WMI_RATE_PREAMBLE_CCK:
|
|
+ peer->nss.nss_stats->rxrate.legacy = rate;
|
|
+ break;
|
|
+ case WMI_RATE_PREAMBLE_HT:
|
|
+ peer->nss.nss_stats->rxrate.mcs = ppdu_info->mcs + 8 * (ppdu_info->nss - 1);
|
|
+ peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_MCS;
|
|
+ if (ppdu_info->gi)
|
|
+ peer->nss.nss_stats->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
|
|
+ break;
|
|
+ case WMI_RATE_PREAMBLE_VHT:
|
|
+ peer->nss.nss_stats->rxrate.mcs = ppdu_info->mcs;
|
|
+ peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
|
|
+ if (ppdu_info->gi)
|
|
+ peer->nss.nss_stats->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
|
|
+ break;
|
|
+ case WMI_RATE_PREAMBLE_HE:
|
|
+ peer->nss.nss_stats->rxrate.mcs = ppdu_info->mcs;
|
|
+ peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_HE_MCS;
|
|
+ peer->nss.nss_stats->rxrate.he_dcm = ppdu_info->dcm;
|
|
+ peer->nss.nss_stats->rxrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(ppdu_info->gi);
|
|
+ peer->nss.nss_stats->rxrate.he_ru_alloc = ppdu_info->ru_alloc;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ peer->nss.nss_stats->rxrate.nss = ppdu_info->nss;
|
|
+ peer->nss.nss_stats->rxrate.bw = ath11k_mac_bw_to_mac80211_bw(ppdu_info->bw);
|
|
+
|
|
+ num_msdu = ppdu_info->tcp_msdu_count + ppdu_info->tcp_ack_msdu_count +
|
|
+ ppdu_info->udp_msdu_count + ppdu_info->other_msdu_count;
|
|
+ if (ppdu_info->tid <= IEEE80211_NUM_TIDS)
|
|
+ peer->nss.nss_stats->rx_tid_msdu[ppdu_info->tid] += num_msdu;
|
|
+}
|
|
+
|
|
+int ath11k_nss_peer_delete(struct ath11k_base *ab, u8 *addr)
|
|
+{
|
|
+ struct nss_wifili_peer_msg *peer_msg;
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ struct ath11k_peer *peer;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ int ret;
|
|
+
|
|
+ if (!ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+
|
|
+ peer = ath11k_peer_find_by_addr(ab, addr);
|
|
+ if (!peer) {
|
|
+ ath11k_warn(ab, "peer not found for nss peer delete\n");
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg) {
|
|
+ ath11k_warn(ab, "nss send peer delete msg alloc failure\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto free_peer;
|
|
+ }
|
|
+
|
|
+ peer_msg = &wlmsg->msg.peermsg;
|
|
+
|
|
+ peer_msg->vdev_id = peer->vdev_id;
|
|
+ peer_msg->peer_id = peer->peer_id;
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ NSS_WIFILI_PEER_DELETE_MSG,
|
|
+ sizeof(struct nss_wifili_peer_msg),
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "nss send peer delete msg tx error %d\n", status);
|
|
+ ret = -EINVAL;
|
|
+ kfree(wlmsg);
|
|
+ goto free_peer;
|
|
+ } else {
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss peer delete message success : peer_id %d\n",
|
|
+ peer->peer_id);
|
|
+ ret = 0;
|
|
+ }
|
|
+
|
|
+ kfree(wlmsg);
|
|
+ if (peer->nss.nss_stats) {
|
|
+ kfree(peer->nss.nss_stats);
|
|
+ peer->nss.nss_stats = NULL;
|
|
+ }
|
|
+
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+
|
|
+ return ret;
|
|
+
|
|
+free_peer:
|
|
+ dma_unmap_single(ab->dev, peer->nss.paddr,
|
|
+ WIFILI_NSS_PEER_BYTE_SIZE, DMA_TO_DEVICE);
|
|
+ kfree(peer->nss.vaddr);
|
|
+ if (peer->nss.nss_stats) {
|
|
+ kfree(peer->nss.nss_stats);
|
|
+ peer->nss.nss_stats = NULL;
|
|
+ }
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer)
|
|
+{
|
|
+ struct nss_wifili_peer_msg *peer_msg;
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ int ret;
|
|
+
|
|
+ if (!ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ peer_msg = &wlmsg->msg.peermsg;
|
|
+
|
|
+ peer_msg->vdev_id = peer->vdev_id;
|
|
+ peer_msg->peer_id = peer->peer_id;
|
|
+ peer_msg->hw_ast_idx = peer->hw_peer_id;
|
|
+ peer_msg->tx_ast_hash = peer->ast_hash;
|
|
+ ether_addr_copy(peer_msg->peer_mac_addr, peer->addr);
|
|
+
|
|
+ peer->nss.vaddr = kzalloc(WIFILI_NSS_PEER_BYTE_SIZE, GFP_ATOMIC);
|
|
+
|
|
+ if (!peer->nss.vaddr) {
|
|
+ ath11k_warn(ab, "failed to allocate memory for nss peer info\n");
|
|
+ kfree(wlmsg);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ peer->nss.paddr = dma_map_single(ab->dev, peer->nss.vaddr,
|
|
+ WIFILI_NSS_PEER_BYTE_SIZE, DMA_TO_DEVICE);
|
|
+
|
|
+ ret = dma_mapping_error(ab->dev, peer->nss.paddr);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "error during nss peer info memalloc\n");
|
|
+ kfree(peer->nss.vaddr);
|
|
+ ret = -ENOMEM;
|
|
+ goto msg_free;
|
|
+ }
|
|
+
|
|
+ peer_msg->nss_peer_mem = peer->nss.paddr;
|
|
+ peer_msg->psta_vdev_id = peer->vdev_id;
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ NSS_WIFILI_PEER_CREATE_MSG,
|
|
+ sizeof(struct nss_wifili_peer_msg),
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ret = -EINVAL;
|
|
+ ath11k_warn(ab, "nss send peer create msg tx error\n");
|
|
+ goto peer_mem_free;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "nss peer_create msg success mac:%pM vdev:%d peer_id:%d hw_ast_idx:%d ast_hash:%d\n",
|
|
+ peer_msg->peer_mac_addr, peer_msg->vdev_id, peer_msg->peer_id,
|
|
+ peer_msg->hw_ast_idx, peer_msg->tx_ast_hash);
|
|
+
|
|
+ peer->nss.nss_stats = kzalloc(sizeof(*peer->nss.nss_stats), GFP_ATOMIC);
|
|
+ if (!peer->nss.nss_stats)
|
|
+ ath11k_warn(ab, "Unable to create nss stats memory\n");
|
|
+
|
|
+ goto msg_free;
|
|
+
|
|
+peer_mem_free:
|
|
+ dma_unmap_single(ab->dev, peer->nss.paddr,
|
|
+ WIFILI_NSS_PEER_BYTE_SIZE, DMA_TO_DEVICE);
|
|
+ kfree(peer->nss.vaddr);
|
|
+msg_free:
|
|
+ kfree(wlmsg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*-------------------------------INIT/DEINIT---------------------------------*/
|
|
+
|
|
+static int ath11k_nss_radio_buf_cfg(struct ath11k *ar, int range, int buf_sz)
|
|
+{
|
|
+ struct nss_wifili_radio_buf_cfg_msg *buf_cfg;
|
|
+ struct nss_wifili_radio_cfg_msg *radio_buf_cfg_msg;
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ radio_buf_cfg_msg = &wlmsg->msg.radiocfgmsg;
|
|
+
|
|
+ radio_buf_cfg_msg->radio_if_num = ar->nss.if_num;
|
|
+ buf_cfg = &wlmsg->msg.radiocfgmsg.radiomsg.radiobufcfgmsg;
|
|
+ buf_cfg->range = range;
|
|
+ buf_cfg->buf_cnt = buf_sz;
|
|
+
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num,
|
|
+ NSS_WIFILI_RADIO_BUF_CFG,
|
|
+ sizeof(struct nss_wifili_radio_buf_cfg_msg),
|
|
+ NULL, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab, "nss radio buf cfg send failed %d\n", status);
|
|
+ ret = -EINVAL;
|
|
+ } else {
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS,
|
|
+ "nss radio cfg message range:%d buf_sz:%d if_num:%d ctx:%p\n",
|
|
+ range, buf_sz, ar->nss.if_num, ar->nss.ctx);
|
|
+ }
|
|
+
|
|
+ kfree(wlmsg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void ath11k_nss_fill_srng_info(struct ath11k_base *ab, int ring_id,
|
|
+ struct nss_wifili_hal_srng_info *hsi)
|
|
+{
|
|
+ struct ath11k_hal *hal = &ab->hal;
|
|
+ struct hal_srng *srng;
|
|
+ u32 offset;
|
|
+ int i;
|
|
+
|
|
+ if (ring_id < 0) {
|
|
+ ath11k_warn(ab, "Invalid ring id used for nss init\n");
|
|
+ WARN_ON(1);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ srng = &hal->srng_list[ring_id];
|
|
+
|
|
+ hsi->ring_id = srng->ring_id;
|
|
+ hsi->ring_dir = srng->ring_dir;
|
|
+ hsi->ring_base_paddr = srng->ring_base_paddr;
|
|
+ hsi->entry_size = srng->entry_size;
|
|
+ hsi->num_entries = srng->num_entries;
|
|
+ hsi->flags = srng->flags;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "Ring info to send to nss - ring_id:%d ring_dir:%d ring_paddr:%d entry_size:%d num_entries:%d flags:%d\n",
|
|
+ hsi->ring_id, hsi->ring_dir, hsi->ring_base_paddr,
|
|
+ hsi->entry_size, hsi->num_entries, hsi->flags);
|
|
+
|
|
+ for (i = 0; i < HAL_SRNG_NUM_REG_GRP; i++) {
|
|
+ offset = srng->hwreg_base[i];
|
|
+
|
|
+ /* For PCI based devices, get the umac ring base address offset
|
|
+ * based on window register configuration.
|
|
+ */
|
|
+ if (!(srng->flags & HAL_SRNG_FLAGS_LMAC_RING))
|
|
+ offset = ath11k_hif_get_window_offset(ab, srng->hwreg_base[i]);
|
|
+
|
|
+ hsi->hwreg_base[i] = (u32)ab->mem_pa + offset;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "SRNG Register %d address %d\n",
|
|
+ i, hsi->hwreg_base[i]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ath11k_nss_tx_desc_mem_free(struct ath11k_base *ab)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ATH11K_NSS_MAX_NUMBER_OF_PAGE; i++) {
|
|
+ if (!ab->nss.tx_desc_paddr[i])
|
|
+ continue;
|
|
+
|
|
+ dma_free_coherent(ab->dev,
|
|
+ ab->nss.tx_desc_size[i],
|
|
+ ab->nss.tx_desc_vaddr[i],
|
|
+ ab->nss.tx_desc_paddr[i]);
|
|
+ ab->nss.tx_desc_vaddr[i] = NULL;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "allocated tx desc mem freed\n");
|
|
+}
|
|
+
|
|
+static int ath11k_nss_tx_desc_mem_alloc(struct ath11k_base *ab, u32 required_size, u32 *page_idx)
|
|
+{
|
|
+ int i, alloc_size;
|
|
+ int curr_page_idx;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "tx desc mem alloc size: %d\n", required_size);
|
|
+
|
|
+ curr_page_idx = *page_idx;
|
|
+
|
|
+ for (i = 0, alloc_size = 0; i < required_size; i += alloc_size) {
|
|
+ alloc_size = required_size - i;
|
|
+
|
|
+ if (alloc_size > WIFILI_NSS_MAX_MEM_PAGE_SIZE)
|
|
+ alloc_size = WIFILI_NSS_MAX_MEM_PAGE_SIZE;
|
|
+
|
|
+ ab->nss.tx_desc_vaddr[curr_page_idx] =
|
|
+ dma_alloc_coherent(ab->dev, alloc_size,
|
|
+ &ab->nss.tx_desc_paddr[curr_page_idx],
|
|
+ GFP_KERNEL);
|
|
+
|
|
+ if (!ab->nss.tx_desc_vaddr[curr_page_idx])
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ab->nss.tx_desc_size[curr_page_idx] = alloc_size;
|
|
+ curr_page_idx++;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "curr page %d, allocated %d, total allocated %d\n",
|
|
+ curr_page_idx, alloc_size, i + alloc_size);
|
|
+
|
|
+ if (curr_page_idx == ATH11K_NSS_MAX_NUMBER_OF_PAGE) {
|
|
+ ath11k_warn(ab, "max page number reached while tx desc mem allocation\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ *page_idx = curr_page_idx;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_fill_tx_desc_info(struct ath11k_base *ab,
|
|
+ struct nss_wifili_init_msg *wim)
|
|
+{
|
|
+ struct nss_wifili_tx_desc_addtnl_mem_msg *dam;
|
|
+ u32 required_size, required_size_ext;
|
|
+ struct nss_wifili_tx_desc_init_msg *dim;
|
|
+ u32 tx_desc_limit_0 = 0;
|
|
+ u32 tx_desc_limit_1 = 0;
|
|
+ u32 tx_desc_limit_2 = 0;
|
|
+ u32 dam_page_idx = 0;
|
|
+ int page_idx = 0;
|
|
+ int i;
|
|
+
|
|
+ wim->tx_sw_internode_queue_size = ATH11K_WIFIILI_MAX_TX_PROCESSQ;
|
|
+
|
|
+ dim = &wim->wtdim;
|
|
+ dam = &wim->wtdam;
|
|
+
|
|
+ dim->num_pool = ab->num_radios;
|
|
+ dim->num_tx_device_limit = ATH11K_WIFILI_MAX_TX_DESC;
|
|
+
|
|
+ //TODO Revisit below calc based on platform/mem cfg
|
|
+ switch (dim->num_pool) {
|
|
+ case 1:
|
|
+ tx_desc_limit_0 = ATH11K_WIFILI_DBDC_NUM_TX_DESC;
|
|
+ break;
|
|
+ case 2:
|
|
+ tx_desc_limit_0 = ATH11K_WIFILI_DBDC_NUM_TX_DESC;
|
|
+ tx_desc_limit_1 = ATH11K_WIFILI_DBDC_NUM_TX_DESC;
|
|
+ break;
|
|
+ case 3:
|
|
+ tx_desc_limit_0 = ATH11K_WIFILI_DBTC_NUM_TX_DESC;
|
|
+ tx_desc_limit_1 = ATH11K_WIFILI_DBTC_NUM_TX_DESC;
|
|
+ tx_desc_limit_2 = ATH11K_WIFILI_DBTC_NUM_TX_DESC;
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_warn(ab, "unexpected num radios during tx desc alloc\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dim->num_tx_desc = tx_desc_limit_0;
|
|
+ dim->num_tx_desc_ext = tx_desc_limit_0;
|
|
+ dim->num_tx_desc_2 = tx_desc_limit_1;
|
|
+ dim->num_tx_desc_ext_2 = tx_desc_limit_1;
|
|
+ dim->num_tx_desc_3 = tx_desc_limit_2;
|
|
+ dim->num_tx_desc_ext_3 = tx_desc_limit_2;
|
|
+
|
|
+ required_size = (dim->num_tx_desc + dim->num_tx_desc_2 +
|
|
+ dim->num_tx_desc_3 +
|
|
+ dim->num_pool) * WIFILI_NSS_TX_DESC_SIZE;
|
|
+
|
|
+ required_size_ext = (dim->num_tx_desc_ext + dim->num_tx_desc_ext_2 +
|
|
+ dim->num_tx_desc_ext_3 +
|
|
+ dim->num_pool) * WIFILI_NSS_TX_EXT_DESC_SIZE;
|
|
+
|
|
+ if (ath11k_nss_tx_desc_mem_alloc(ab, required_size, &page_idx)) {
|
|
+ ath11k_warn(ab, "memory allocation for tx desc of size %d failed\n",
|
|
+ required_size);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* Fill the page number from where extension tx descriptor is available */
|
|
+ dim->ext_desc_page_num = page_idx;
|
|
+
|
|
+ if (ath11k_nss_tx_desc_mem_alloc(ab, required_size_ext, &page_idx)) {
|
|
+ ath11k_warn(ab, "memory allocation for extension tx desc of size %d failed\n",
|
|
+ required_size_ext);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < page_idx; i++) {
|
|
+ if (i < NSS_WIFILI_MAX_NUMBER_OF_PAGE_MSG) {
|
|
+ dim->memory_addr[i] = (u32)ab->nss.tx_desc_paddr[i];
|
|
+ dim->memory_size[i] = (u32)ab->nss.tx_desc_size[i];
|
|
+ dim->num_memaddr++;
|
|
+ } else {
|
|
+ dam_page_idx = i - NSS_WIFILI_MAX_NUMBER_OF_PAGE_MSG;
|
|
+ dam->addtnl_memory_addr[dam_page_idx] = (u32)ab->nss.tx_desc_paddr[i];
|
|
+ dam->addtnl_memory_size[dam_page_idx] = (u32)ab->nss.tx_desc_size[i];
|
|
+ dam->num_addtnl_addr++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (i > NSS_WIFILI_MAX_NUMBER_OF_PAGE_MSG)
|
|
+ wim->flags |= WIFILI_ADDTL_MEM_SEG_SET;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_get_target_type(struct ath11k_base *ab)
|
|
+{
|
|
+ switch (ab->hw_rev) {
|
|
+ case ATH11K_HW_IPQ8074:
|
|
+ return ATH11K_WIFILI_TARGET_TYPE_QCA8074V2;
|
|
+ case ATH11K_HW_IPQ6018:
|
|
+ return ATH11K_WIFILI_TARGET_TYPE_QCA6018;
|
|
+ case ATH11K_HW_QCN9000:
|
|
+ return ATH11K_WIFILI_TARGET_TYPE_QCN9000;
|
|
+ case ATH11K_HW_IPQ5018:
|
|
+ return ATH11K_WIFILI_TARGET_TYPE_QCA5018;
|
|
+ default:
|
|
+ ath11k_warn(ab, "NSS Offload not supported for this HW\n");
|
|
+ return ATH11K_WIFILI_TARGET_TYPE_UNKNOWN;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ath11k_nss_get_interface_type(struct ath11k_base *ab)
|
|
+{
|
|
+ switch (ab->hw_rev) {
|
|
+ case ATH11K_HW_IPQ8074:
|
|
+ case ATH11K_HW_IPQ6018:
|
|
+ case ATH11K_HW_IPQ5018:
|
|
+ return NSS_WIFILI_INTERNAL_INTERFACE;
|
|
+ case ATH11K_HW_QCN9000:
|
|
+ return nss_get_available_wifili_external_if();
|
|
+ default:
|
|
+ /* This can't happen since we validated target type earlier */
|
|
+ WARN_ON(1);
|
|
+ return NSS_MAX_NET_INTERFACES;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ath11k_nss_get_dynamic_interface_type(struct ath11k_base *ab)
|
|
+{
|
|
+ switch (ab->nss.if_num) {
|
|
+ case NSS_WIFILI_INTERNAL_INTERFACE:
|
|
+ return NSS_DYNAMIC_INTERFACE_TYPE_WIFILI_INTERNAL;
|
|
+ case NSS_WIFILI_EXTERNAL_INTERFACE0:
|
|
+ return NSS_DYNAMIC_INTERFACE_TYPE_WIFILI_EXTERNAL0;
|
|
+ case NSS_WIFILI_EXTERNAL_INTERFACE1:
|
|
+ return NSS_DYNAMIC_INTERFACE_TYPE_WIFILI_EXTERNAL1;
|
|
+ default:
|
|
+ ath11k_warn(ab, "NSS Offload invalid interface\n");
|
|
+ return NSS_DYNAMIC_INTERFACE_TYPE_NONE;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ath11k_nss_init(struct ath11k_base *ab)
|
|
+{
|
|
+ struct nss_wifili_init_msg *wim = NULL;
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ struct nss_ctx_instance *nss_contex;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ u32 target_type;
|
|
+ u32 features = 0;
|
|
+ nss_tx_status_t status;
|
|
+ struct ath11k_dp *dp;
|
|
+ int i, ret;
|
|
+
|
|
+ dp = &ab->dp;
|
|
+
|
|
+ target_type = ath11k_nss_get_target_type(ab);
|
|
+
|
|
+ if (target_type == ATH11K_WIFILI_TARGET_TYPE_UNKNOWN)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ wim = &wlmsg->msg.init;
|
|
+
|
|
+ wim->target_type = target_type;
|
|
+
|
|
+ /* fill rx parameters to initialize rx context */
|
|
+ wim->wrip.tlv_size = sizeof(struct hal_rx_desc);
|
|
+ wim->wrip.rx_buf_len = DP_RX_BUFFER_SIZE;
|
|
+
|
|
+ /* fill hal srng message */
|
|
+ wim->hssm.dev_base_addr = (u32)ab->mem_pa;
|
|
+ wim->hssm.shadow_rdptr_mem_addr = (u32)ab->hal.rdp.paddr;
|
|
+ wim->hssm.shadow_wrptr_mem_addr = (u32)ab->hal.wrp.paddr;
|
|
+
|
|
+ /* fill TCL data/completion ring info */
|
|
+ wim->num_tcl_data_rings = DP_TCL_NUM_RING_MAX;
|
|
+ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) {
|
|
+ ath11k_nss_fill_srng_info(ab, dp->tx_ring[i].tcl_data_ring.ring_id,
|
|
+ &wim->tcl_ring_info[i]);
|
|
+ ath11k_nss_fill_srng_info(ab, dp->tx_ring[i].tcl_comp_ring.ring_id,
|
|
+ &wim->tx_comp_ring[i]);
|
|
+ }
|
|
+
|
|
+ /* allocate tx desc memory for NSS and fill corresponding info */
|
|
+ ret = ath11k_nss_fill_tx_desc_info(ab, wim);
|
|
+ if (ret)
|
|
+ goto free;
|
|
+
|
|
+ /* fill reo dest ring info */
|
|
+ wim->num_reo_dest_rings = DP_REO_DST_RING_MAX;
|
|
+ for (i = 0; i < DP_REO_DST_RING_MAX; i++) {
|
|
+ ath11k_nss_fill_srng_info(ab, dp->reo_dst_ring[i].ring_id,
|
|
+ &wim->reo_dest_ring[i]);
|
|
+ }
|
|
+
|
|
+ /* fill reo reinject ring info */
|
|
+ ath11k_nss_fill_srng_info(ab, dp->reo_reinject_ring.ring_id,
|
|
+ &wim->reo_reinject_ring);
|
|
+
|
|
+ /* fill reo release ring info */
|
|
+ ath11k_nss_fill_srng_info(ab, dp->rx_rel_ring.ring_id,
|
|
+ &wim->rx_rel_ring);
|
|
+
|
|
+ /* fill reo exception ring info */
|
|
+ ath11k_nss_fill_srng_info(ab, dp->reo_except_ring.ring_id,
|
|
+ &wim->reo_exception_ring);
|
|
+
|
|
+ wim->flags |= WIFILI_NSS_CCE_DISABLED;
|
|
+
|
|
+ ab->nss.if_num = ath11k_nss_get_interface_type(ab);
|
|
+
|
|
+ if (ab->nss.if_num >= NSS_MAX_NET_INTERFACES) {
|
|
+ ath11k_warn(ab, "NSS invalid interface\n");
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* register callbacks for events and exceptions with nss */
|
|
+ nss_contex = nss_register_wifili_if(ab->nss.if_num, NULL,
|
|
+ (nss_wifili_callback_t)ath11k_nss_wifili_ext_callback_fn,
|
|
+ (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive,
|
|
+ (struct net_device *)ab, features);
|
|
+
|
|
+ if (!nss_contex) {
|
|
+ ath11k_warn(ab, "nss wifili register failure\n");
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ if (nss_cmn_get_state(nss_contex) != NSS_STATE_INITIALIZED) {
|
|
+ ath11k_warn(ab, "nss state in default init state\n");
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* The registered soc context is stored in ab, and will be used for
|
|
+ * all soc related messages with nss
|
|
+ */
|
|
+ ab->nss.ctx = nss_contex;
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ /* Initialize the common part of the wlmsg */
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ NSS_WIFILI_INIT_MSG,
|
|
+ sizeof(struct nss_wifili_init_msg),
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(nss_contex, wlmsg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "failure to send nss init msg\n");
|
|
+ goto unregister;
|
|
+ }
|
|
+
|
|
+ reinit_completion(&ab->nss.complete);
|
|
+
|
|
+ /* Note: response is contention free during init sequence */
|
|
+ ab->nss.response = ATH11K_NSS_MSG_ACK;
|
|
+
|
|
+ ret = wait_for_completion_timeout(&ab->nss.complete,
|
|
+ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
|
|
+ if (!ret) {
|
|
+ ath11k_warn(ab, "timeout while waiting for nss init msg response\n");
|
|
+ goto unregister;
|
|
+ }
|
|
+
|
|
+ /* Check if the response is success from the callback */
|
|
+ if (ab->nss.response != ATH11K_NSS_MSG_ACK)
|
|
+ goto unregister;
|
|
+
|
|
+ kfree(wlmsg);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Init Message TX Success %p %d\n",
|
|
+ ab->nss.ctx, ab->nss.if_num);
|
|
+ return 0;
|
|
+
|
|
+unregister:
|
|
+ nss_unregister_wifili_if(ab->nss.if_num);
|
|
+free:
|
|
+ ath11k_nss_tx_desc_mem_free(ab);
|
|
+ kfree(wlmsg);
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_stats_cfg(struct ath11k *ar, int nss_msg, int enable)
|
|
+{
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ struct nss_wifili_stats_cfg_msg *stats_cfg;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ struct ath11k_base *ab = ar->ab;
|
|
+ int ret = 0;
|
|
+
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ stats_cfg = &wlmsg->msg.scm;
|
|
+ stats_cfg->cfg = enable;
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ nss_msg,
|
|
+ sizeof(struct nss_wifili_stats_cfg_msg),
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "nss stats cfg %d msg tx failure\n", nss_msg);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss stats %d enable %d\n", nss_msg, enable);
|
|
+
|
|
+free:
|
|
+ kfree(wlmsg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void ath11k_nss_stats_disable(struct ath11k *ar)
|
|
+{
|
|
+ /*TODO Revisit if this required */
|
|
+ ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_CFG_MSG,
|
|
+ ATH11K_NSS_STATS_DISABLE);
|
|
+
|
|
+ ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_V2_CFG_MSG,
|
|
+ ATH11K_NSS_STATS_DISABLE);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_stats_enable(struct ath11k *ar)
|
|
+{
|
|
+ /*TODO Revisit if we need to enable both these stats */
|
|
+ ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_CFG_MSG,
|
|
+ ATH11K_NSS_STATS_ENABLE);
|
|
+
|
|
+ ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_V2_CFG_MSG,
|
|
+ ATH11K_NSS_STATS_ENABLE);
|
|
+}
|
|
+
|
|
+int ath11k_nss_pdev_init(struct ath11k_base *ab, int radio_id)
|
|
+{
|
|
+ struct ath11k *ar = ab->pdevs[radio_id].ar;
|
|
+ struct nss_wifili_pdev_init_msg *pdevmsg;
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ int radio_if_num = -1;
|
|
+ int refill_ring_id;
|
|
+ int features = 0;
|
|
+ int dyn_if_type;
|
|
+ int ret, i;
|
|
+
|
|
+ dyn_if_type = ath11k_nss_get_dynamic_interface_type(ab);
|
|
+
|
|
+ /* Allocate a node for dynamic interface */
|
|
+ radio_if_num = nss_dynamic_interface_alloc_node(dyn_if_type);
|
|
+
|
|
+ if (radio_if_num < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* The ifnum and registered radio context is stored in ar and used
|
|
+ * for messages related to vdev/radio
|
|
+ */
|
|
+ ar->nss.if_num = radio_if_num;
|
|
+
|
|
+ /* No callbacks are registered for radio specific events/data */
|
|
+ ar->nss.ctx = nss_register_wifili_radio_if((u32)radio_if_num, NULL,
|
|
+ NULL, NULL, (struct net_device *)ar,
|
|
+ features);
|
|
+
|
|
+ if (!ar->nss.ctx) {
|
|
+ ath11k_warn(ab, "failure during nss pdev register\n");
|
|
+ ret = -EINVAL;
|
|
+ goto dealloc;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss pdev init - id:%d init ctxt:%p ifnum:%d\n",
|
|
+ ar->pdev->pdev_id, ar->nss.ctx, ar->nss.if_num);
|
|
+
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg) {
|
|
+ ret = -ENOMEM;
|
|
+ goto unregister;
|
|
+ }
|
|
+
|
|
+ pdevmsg = &wlmsg->msg.pdevmsg;
|
|
+
|
|
+ pdevmsg->radio_id = radio_id;
|
|
+ pdevmsg->lmac_id = ar->lmac_id;
|
|
+ pdevmsg->target_pdev_id = ar->pdev->pdev_id;
|
|
+ pdevmsg->num_rx_swdesc = WIFILI_RX_DESC_POOL_WEIGHT * DP_RXDMA_BUF_RING_SIZE;
|
|
+
|
|
+ /* Store rxdma ring info to the message */
|
|
+ refill_ring_id = ar->dp.rx_refill_buf_ring.refill_buf_ring.ring_id;
|
|
+ ath11k_nss_fill_srng_info(ab, refill_ring_id, &pdevmsg->rxdma_ring);
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ NSS_WIFILI_PDEV_INIT_MSG,
|
|
+ sizeof(struct nss_wifili_pdev_init_msg),
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
|
|
+
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "nss send pdev msg tx error : %d\n", status);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ reinit_completion(&ab->nss.complete);
|
|
+
|
|
+ /* Note: response is contention free during init sequence */
|
|
+ ab->nss.response = ATH11K_NSS_MSG_ACK;
|
|
+
|
|
+ ret = wait_for_completion_timeout(&ab->nss.complete,
|
|
+ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
|
|
+ if (!ret) {
|
|
+ ath11k_warn(ab, "timeout while waiting for pdev init msg response\n");
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* Check if the response is success from the callback */
|
|
+ if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ kfree(wlmsg);
|
|
+
|
|
+ /* Enable nss stats */
|
|
+ ath11k_nss_stats_enable(ar);
|
|
+
|
|
+ /*TODO CFG Tx buffer limit as per no clients range per radio
|
|
+ * this needs to be based on target/mem cfg
|
|
+ * similar to tx desc cfg at soc init per radio
|
|
+ */
|
|
+
|
|
+ for (i = 0; i < ATH11K_NSS_RADIO_TX_LIMIT_RANGE; i++)
|
|
+ ath11k_nss_radio_buf_cfg(ar, i, ATH11K_NSS_RADIO_TX_LIMIT_RANGE3);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+free:
|
|
+ kfree(wlmsg);
|
|
+unregister:
|
|
+ nss_unregister_wifili_radio_if(ar->nss.if_num);
|
|
+dealloc:
|
|
+ nss_dynamic_interface_dealloc_node(ar->nss.if_num, dyn_if_type);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* TODO : Check if start, reset and stop messages can be done using single function as
|
|
+ * body is similar, having it now for clarity */
|
|
+
|
|
+int ath11k_nss_start(struct ath11k_base *ab)
|
|
+{
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ int ret;
|
|
+
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ /* Empty message for NSS Start message */
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ NSS_WIFILI_START_MSG,
|
|
+ 0,
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
|
|
+
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "nss send start msg tx error %d\n", status);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ reinit_completion(&ab->nss.complete);
|
|
+
|
|
+ /* Note: response is contention free during init sequence */
|
|
+ ab->nss.response = ATH11K_NSS_MSG_ACK;
|
|
+
|
|
+ ret = wait_for_completion_timeout(&ab->nss.complete,
|
|
+ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
|
|
+ if (!ret) {
|
|
+ ath11k_warn(ab, "timeout while waiting for response for nss start msg\n");
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* Check if the response is success from the callback */
|
|
+ if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* NSS Start success */
|
|
+ ret = 0;
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss start success\n");
|
|
+
|
|
+free:
|
|
+ kfree(wlmsg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void ath11k_nss_reset(struct ath11k_base *ab)
|
|
+{
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ int ret;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS reset\n");
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg) {
|
|
+ ath11k_warn(ab, "mem allocation failure during nss reset\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ /* Empty message for NSS Reset message */
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ NSS_WIFILI_SOC_RESET_MSG,
|
|
+ 0,
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
|
|
+
|
|
+ /* Add a retry mechanism to reset nss until success */
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "nss send reset msg tx error %d\n", status);
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ reinit_completion(&ab->nss.complete);
|
|
+
|
|
+ /* Note: response is contention free during deinit sequence */
|
|
+ ab->nss.response = ATH11K_NSS_MSG_ACK;
|
|
+
|
|
+ ret = wait_for_completion_timeout(&ab->nss.complete,
|
|
+ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
|
|
+ if (!ret) {
|
|
+ ath11k_warn(ab, "timeout while waiting for response for nss reset msg\n");
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* Check if the response is success from the callback */
|
|
+ if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
|
|
+ ath11k_warn(ab, "failure response during nss reset %d\n", ab->nss.response);
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* Unregister wifili interface */
|
|
+ nss_unregister_wifili_if(ab->nss.if_num);
|
|
+
|
|
+free:
|
|
+ kfree(wlmsg);
|
|
+}
|
|
+
|
|
+static int ath11k_nss_stop(struct ath11k_base *ab)
|
|
+{
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ int ret;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS stop\n");
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ /* Empty message for Stop command */
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ NSS_WIFILI_STOP_MSG,
|
|
+ 0,
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
|
|
+
|
|
+ /* Add a retry mechanism to stop nss until success */
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "nss send stop msg tx error %d\n", status);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ reinit_completion(&ab->nss.complete);
|
|
+
|
|
+ /* Note: response is contention free during deinit sequence */
|
|
+ ab->nss.response = ATH11K_NSS_MSG_ACK;
|
|
+
|
|
+ ret = wait_for_completion_timeout(&ab->nss.complete,
|
|
+ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
|
|
+ if (!ret) {
|
|
+ ath11k_warn(ab, "timeout while waiting for response for nss stop msg\n");
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* Check if the response is success from the callback */
|
|
+ if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* NSS Stop success */
|
|
+ ret = 0;
|
|
+free:
|
|
+ kfree(wlmsg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ath11k_nss_pdev_deinit(struct ath11k_base *ab, int radio_id)
|
|
+{
|
|
+ struct ath11k *ar = ab->pdevs[radio_id].ar;
|
|
+ struct nss_wifili_pdev_deinit_msg *deinit;
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ int dyn_if_type;
|
|
+ int ret;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS pdev %d deinit\n", radio_id);
|
|
+ dyn_if_type = ath11k_nss_get_dynamic_interface_type(ab);
|
|
+
|
|
+ /* Disable stats before teardown */
|
|
+ ath11k_nss_stats_disable(ar);
|
|
+
|
|
+ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC);
|
|
+ if (!wlmsg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ deinit = &wlmsg->msg.pdevdeinit;
|
|
+ deinit->ifnum = radio_id;
|
|
+
|
|
+ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
|
|
+
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ NSS_WIFILI_PDEV_DEINIT_MSG,
|
|
+ sizeof(struct nss_wifili_pdev_deinit_msg),
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "nss send pdev deinit msg tx error %d\n", status);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ reinit_completion(&ab->nss.complete);
|
|
+
|
|
+ /* Note: response is contention free during deinit sequence */
|
|
+ ab->nss.response = ATH11K_NSS_MSG_ACK;
|
|
+
|
|
+ ret = wait_for_completion_timeout(&ab->nss.complete,
|
|
+ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS));
|
|
+ if (!ret) {
|
|
+ ath11k_warn(ab, "timeout while waiting for pdev deinit msg response\n");
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* Check if the response is success from the callback */
|
|
+ if (ab->nss.response != ATH11K_NSS_MSG_ACK) {
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ /* pdev deinit msg success, dealloc, deregister and return */
|
|
+ ret = 0;
|
|
+
|
|
+ nss_dynamic_interface_dealloc_node(ar->nss.if_num, dyn_if_type);
|
|
+ nss_unregister_wifili_radio_if(ar->nss.if_num);
|
|
+free:
|
|
+ kfree(wlmsg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ath11k_nss_teardown(struct ath11k_base *ab)
|
|
+{
|
|
+ int i, ret;
|
|
+
|
|
+ if (!ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ ath11k_nss_stop(ab);
|
|
+
|
|
+ for (i = 0; i < ab->num_radios ; i++) {
|
|
+ ret = ath11k_nss_pdev_deinit(ab, i);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failure during pdev%d deinit\n", i);
|
|
+ }
|
|
+
|
|
+ ath11k_nss_reset(ab);
|
|
+ ath11k_nss_tx_desc_mem_free(ab);
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Teardown Complete\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ath11k_nss_setup(struct ath11k_base *ab)
|
|
+{
|
|
+ int i;
|
|
+ int ret = 0;
|
|
+ u32 target_type;
|
|
+
|
|
+ if (!ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ target_type = ath11k_nss_get_target_type(ab);
|
|
+
|
|
+ if (target_type == ATH11K_WIFILI_TARGET_TYPE_UNKNOWN)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ /* Verify NSS support is enabled */
|
|
+ if (nss_cmn_get_nss_enabled() == false) {
|
|
+ ath11k_warn(ab, "NSS offload support disabled, falling back to default mode\n");
|
|
+ return -ENOTSUPP;
|
|
+ }
|
|
+
|
|
+ /* Initialize completion for verifying NSS message response */
|
|
+ init_completion(&ab->nss.complete);
|
|
+
|
|
+ /* Setup common resources for NSS */
|
|
+ ret = ath11k_nss_init(ab);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "NSS SOC Initialization Failed :%d\n", ret);
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ /* Setup pdev related resources for NSS */
|
|
+ for (i = 0; i < ab->num_radios; i++) {
|
|
+ ret = ath11k_nss_pdev_init(ab, i);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "NSS PDEV %d Initialization Failed :%d\n", i, ret);
|
|
+ goto pdev_deinit;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Set the NSS statemachine to start */
|
|
+ ret = ath11k_nss_start(ab);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "NSS Start Failed : %d\n", ret);
|
|
+ goto pdev_deinit;
|
|
+ }
|
|
+
|
|
+ /* Default nexthop interface is set to ETH RX */
|
|
+ ret = nss_wifi_vdev_base_set_next_hop(ab->nss.ctx, NSS_ETH_RX_INTERFACE);
|
|
+ if (ret != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "Failure to set default next hop : %d\n", ret);
|
|
+ goto stop;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Setup Complete\n");
|
|
+ return ret;
|
|
+
|
|
+stop:
|
|
+ ath11k_nss_stop(ab);
|
|
+
|
|
+pdev_deinit:
|
|
+ for (; i >= 0; i--)
|
|
+ ath11k_nss_pdev_init(ab, i);
|
|
+
|
|
+ ath11k_nss_reset(ab);
|
|
+ ath11k_nss_tx_desc_mem_free(ab);
|
|
+fail:
|
|
+ return ret;
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/ath/ath11k/nss.h
|
|
@@ -0,0 +1,267 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
|
|
+/*
|
|
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
|
+ */
|
|
+
|
|
+#ifndef ATH11K_NSS_H
|
|
+#define ATH11K_NSS_H
|
|
+
|
|
+#include <nss_api_if.h>
|
|
+#include <nss_cmn.h>
|
|
+
|
|
+struct ath11k;
|
|
+struct ath11k_base;
|
|
+struct ath11k_vif;
|
|
+struct ath11k_peer;
|
|
+struct ath11k_sta;
|
|
+struct hal_rx_mon_ppdu_info;
|
|
+
|
|
+/* NSS DBG macro is not included as part of debug enum to avoid
|
|
+ * frequent changes during upgrade*/
|
|
+#define ATH11K_DBG_NSS 0x80000000
|
|
+
|
|
+/* WIFILI Supported Target Types */
|
|
+#define ATH11K_WIFILI_TARGET_TYPE_UNKNOWN 0xFF
|
|
+#define ATH11K_WIFILI_TARGET_TYPE_QCA8074 20
|
|
+#define ATH11K_WIFILI_TARGET_TYPE_QCA8074V2 24
|
|
+#define ATH11K_WIFILI_TARGET_TYPE_QCA6018 25
|
|
+#define ATH11K_WIFILI_TARGET_TYPE_QCN9000 26
|
|
+#define ATH11K_WIFILI_TARGET_TYPE_QCA5018 29
|
|
+
|
|
+/* Max limit for NSS Queue */
|
|
+#define ATH11K_WIFIILI_MAX_TX_PROCESSQ 1024
|
|
+
|
|
+/* Max TX Desc limit */
|
|
+#define ATH11K_WIFILI_MAX_TX_DESC 65536
|
|
+
|
|
+/* TX Desc related info */
|
|
+/*TODO : Check this again during experiments for lowmem or
|
|
+ changes for platforms based on num radios supported */
|
|
+#define ATH11K_WIFILI_DBDC_NUM_TX_DESC (1024 * 8)
|
|
+#define ATH11K_WIFILI_DBTC_NUM_TX_DESC (1024 * 8)
|
|
+
|
|
+// TODO Revisit these page size calc
|
|
+#define WIFILI_NSS_TX_DESC_SIZE 20*4
|
|
+#define WIFILI_NSS_TX_EXT_DESC_SIZE 40*4
|
|
+/* Number of desc per page(12bit) should be<4096, page limit per 1024 byte is 80*3=240 */
|
|
+#define WIFILI_NSS_TX_DESC_PAGE_LIMIT 240
|
|
+#define WIFILI_NSS_MAX_MEM_PAGE_SIZE (WIFILI_NSS_TX_DESC_PAGE_LIMIT * 1024)
|
|
+#define WIFILI_NSS_MAX_EXT_MEM_PAGE_SIZE (WIFILI_NSS_TX_DESC_PAGE_LIMIT * 1024)
|
|
+#define WIFILI_RX_DESC_POOL_WEIGHT 3
|
|
+
|
|
+/* Status of the NSS messages sent from driver */
|
|
+#define ATH11K_NSS_MSG_ACK 0
|
|
+/* Timeout for waiting for response from NSS on TX msg */
|
|
+#define ATH11K_NSS_MSG_TIMEOUT_MS 5000
|
|
+
|
|
+/* Init Flags */
|
|
+#define WIFILI_NSS_CCE_DISABLED 0x1
|
|
+#define WIFILI_ADDTL_MEM_SEG_SET 0x000000002
|
|
+
|
|
+/* ATH11K NSS PEER Info */
|
|
+/* Host memory allocated for peer info storage in nss */
|
|
+#define WIFILI_NSS_PEER_BYTE_SIZE 1152
|
|
+
|
|
+/* ATH11K NSS Stats */
|
|
+#define ATH11K_NSS_STATS_ENABLE 1
|
|
+#define ATH11K_NSS_STATS_DISABLE 0
|
|
+
|
|
+/* TX Buf cfg range */
|
|
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE 4
|
|
+
|
|
+/* TODO : Analysis based on platform */
|
|
+/* TX Limit till 64 clients */
|
|
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE0 8192
|
|
+/* TX Limit till 128 clients */
|
|
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE1 8192
|
|
+/* TX Limit till 256 clients */
|
|
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE2 8192
|
|
+/* TX Limit > 256 clients */
|
|
+#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE3 8192
|
|
+
|
|
+#define ATH11K_NSS_MAX_NUMBER_OF_PAGE 96
|
|
+
|
|
+#define NSS_TX_TID_MAX 8
|
|
+
|
|
+enum ath11k_nss_opmode {
|
|
+ ATH11K_NSS_OPMODE_UNKNOWN,
|
|
+ ATH11K_NSS_OPMODE_AP,
|
|
+ ATH11K_NSS_OPMODE_IBSS,
|
|
+ ATH11K_NSS_OPMODE_STA,
|
|
+ ATH11K_NSS_OPMODE_MONITOR,
|
|
+};
|
|
+
|
|
+struct peer_stats {
|
|
+ u64 last_rx;
|
|
+ u64 last_ack;
|
|
+ u32 tx_packets;
|
|
+ u32 tx_bytes;
|
|
+ u32 tx_retries;
|
|
+ u32 tx_failed;
|
|
+ u32 rx_packets;
|
|
+ u32 rx_bytes;
|
|
+ u32 rx_dropped;
|
|
+ u32 last_rxdrop;
|
|
+ struct rate_info rxrate;
|
|
+ u32 tx_tid_msdu[NSS_TX_TID_MAX];
|
|
+ u32 rx_tid_msdu[IEEE80211_NUM_TIDS + 1];
|
|
+};
|
|
+
|
|
+enum ath11k_nss_peer_sec_type {
|
|
+ PEER_SEC_TYPE_NONE,
|
|
+ PEER_SEC_TYPE_WEP128,
|
|
+ PEER_SEC_TYPE_WEP104,
|
|
+ PEER_SEC_TYPE_WEP40,
|
|
+ PEER_SEC_TYPE_TKIP,
|
|
+ PEER_SEC_TYPE_TKIP_NOMIC,
|
|
+ PEER_SEC_TYPE_AES_CCMP,
|
|
+ PEER_SEC_TYPE_WAPI,
|
|
+ PEER_SEC_TYPE_AES_CCMP_256,
|
|
+ PEER_SEC_TYPE_AES_GCMP,
|
|
+ PEER_SEC_TYPE_AES_GCMP_256,
|
|
+ PEER_SEC_TYPES_MAX
|
|
+};
|
|
+
|
|
+/* this holds the memory allocated for nss managed peer info */
|
|
+struct ath11k_nss_peer {
|
|
+ uint32_t *vaddr;
|
|
+ dma_addr_t paddr;
|
|
+ struct peer_stats *nss_stats;
|
|
+};
|
|
+
|
|
+/* Structure to hold the vif related info for nss offload support */
|
|
+struct arvif_nss {
|
|
+ /* dynamic ifnum allocated by nss driver for vif */
|
|
+ int if_num;
|
|
+ /* Used for completion status for vdev config nss messages */
|
|
+ struct completion complete;
|
|
+ /* Keep the copy of encap type for nss */
|
|
+ int encap;
|
|
+ /* Keep the copy of decap type for nss */
|
|
+ int decap;
|
|
+ bool created;
|
|
+};
|
|
+
|
|
+/* Structure to hold the pdev/radio related info for nss offload support */
|
|
+struct ath11k_nss {
|
|
+ /* dynamic ifnum allocated by nss driver for pdev */
|
|
+ int if_num;
|
|
+ /* Radio/pdev Context obtained on pdev register */
|
|
+ void* ctx;
|
|
+};
|
|
+
|
|
+/* Structure to hold the soc related info for nss offload support */
|
|
+struct ath11k_soc_nss {
|
|
+ /* turn on/off nss offload support in ath11k */
|
|
+ bool enabled;
|
|
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
|
|
+ /* soc nss ctx */
|
|
+ void* ctx;
|
|
+ /* if_num to be used for soc related nss messages */
|
|
+ int if_num;
|
|
+ /* Completion to nss message response */
|
|
+ struct completion complete;
|
|
+ /* Response to nss messages are stored here on msg callback
|
|
+ * used only in contention free messages during init */
|
|
+ int response;
|
|
+ /* Below is used for identifying allocated tx descriptors */
|
|
+ dma_addr_t tx_desc_paddr[ATH11K_NSS_MAX_NUMBER_OF_PAGE];
|
|
+ uint32_t * tx_desc_vaddr[ATH11K_NSS_MAX_NUMBER_OF_PAGE];
|
|
+ uint32_t tx_desc_size[ATH11K_NSS_MAX_NUMBER_OF_PAGE];
|
|
+#endif
|
|
+};
|
|
+
|
|
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
|
|
+int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb);
|
|
+int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val);
|
|
+int ath11k_nss_vdev_create(struct ath11k_vif *arvif);
|
|
+void ath11k_nss_vdev_delete(struct ath11k_vif *arvif);
|
|
+int ath11k_nss_vdev_up(struct ath11k_vif *arvif);
|
|
+int ath11k_nss_vdev_down(struct ath11k_vif *arvif);
|
|
+int ath11k_nss_peer_delete(struct ath11k_base *ab, u8 *addr);
|
|
+int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer);
|
|
+int ath11k_nss_set_peer_sec_type(struct ath11k *ar, struct ath11k_peer *peer,
|
|
+ struct ieee80211_key_conf *key_conf);
|
|
+void ath11k_nss_update_sta_stats(struct station_info *sinfo,
|
|
+ struct ieee80211_sta *sta,
|
|
+ struct ath11k_sta *arsta);
|
|
+void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info,
|
|
+ struct ath11k_peer *peer);
|
|
+int ath11k_nss_setup(struct ath11k_base *ab);
|
|
+int ath11k_nss_teardown(struct ath11k_base *ab);
|
|
+void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, struct htt_rx_ring_tlv_filter *tlv_filter);
|
|
+#else
|
|
+static inline int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int ath11k_nss_vdev_create(struct ath11k_vif *arvif)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void ath11k_nss_vdev_delete(struct ath11k_vif *arvif)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath11k_nss_update_sta_stats(struct station_info *sinfo,
|
|
+ struct ieee80211_sta *sta,
|
|
+ struct ath11k_sta *arsta)
|
|
+{
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info,
|
|
+ struct ath11k_peer *peer)
|
|
+{
|
|
+ return;
|
|
+}
|
|
+
|
|
+static inline int ath11k_nss_vdev_up(struct ath11k_vif *arvif)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int ath11k_nss_vdev_down(struct ath11k_vif *arvif)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int ath11k_nss_peer_delete(struct ath11k_base *ab, u8 *addr)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int ath11k_nss_set_peer_sec_type(struct ath11k *ar, struct ath11k_peer *peer,
|
|
+ struct ieee80211_key_conf *key_conf)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int ath11k_nss_setup(struct ath11k_base *ab)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int ath11k_nss_teardown(struct ath11k_base *ab)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, struct htt_rx_ring_tlv_filter *tlv_filter)
|
|
+{
|
|
+ return;
|
|
+}
|
|
+#endif /* CPTCFG_ATH11K_NSS_SUPPORT */
|
|
+#endif
|
|
--- a/drivers/net/wireless/ath/ath11k/hif.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/hif.h
|
|
@@ -24,6 +24,7 @@ struct ath11k_hif_ops {
|
|
u32 *base_vector);
|
|
void (*get_msi_address)(struct ath11k_base *ab, u32 *msi_addr_lo,
|
|
u32 *msi_addr_hi);
|
|
+ u32 (*get_window_offset)(struct ath11k_base *ab, u32 offset);
|
|
void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);
|
|
};
|
|
|
|
@@ -94,6 +95,14 @@ static inline void ath11k_get_msi_addres
|
|
ab->hif.ops->get_msi_address(ab, msi_addr_lo, msi_addr_hi);
|
|
}
|
|
|
|
+static inline u32 ath11k_hif_get_window_offset(struct ath11k_base *ab, u32 offset)
|
|
+{
|
|
+ if (ab->hif.ops->get_window_offset)
|
|
+ return ab->hif.ops->get_window_offset(ab, offset);
|
|
+
|
|
+ return offset;
|
|
+}
|
|
+
|
|
static inline void ath11k_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id,
|
|
u32 *msi_data_idx)
|
|
{
|
|
--- a/drivers/net/wireless/ath/ath11k/pci.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/pci.c
|
|
@@ -160,6 +160,20 @@ static inline u32 ath11k_pci_get_window_
|
|
return window_start;
|
|
}
|
|
|
|
+static inline u32 ath11k_pci_get_window_offset(struct ath11k_base *ab,
|
|
+ u32 offset)
|
|
+{
|
|
+ u32 window_start;
|
|
+
|
|
+ if (ab->bus_params.static_window_map) {
|
|
+ window_start = ath11k_pci_get_window_start(ab, offset);
|
|
+
|
|
+ if (window_start)
|
|
+ offset = window_start + (offset & WINDOW_RANGE_MASK);
|
|
+ }
|
|
+ return offset;
|
|
+}
|
|
+
|
|
void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value)
|
|
{
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
|
@@ -939,6 +953,7 @@ static const struct ath11k_hif_ops ath11
|
|
.get_msi_address = ath11k_pci_get_msi_address,
|
|
.get_user_msi_vector = ath11k_get_user_msi_assignment,
|
|
.map_service_to_pipe = ath11k_pci_map_service_to_pipe,
|
|
+ .get_window_offset = ath11k_pci_get_window_offset,
|
|
.get_ce_msi_idx = ath11k_pci_get_ce_msi_idx,
|
|
};
|
|
|