mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-19 18:31:33 +00:00
4568 lines
132 KiB
Diff
4568 lines
132 KiB
Diff
From fbe5a76d8c9ff1cf3f906a3c863928fc1adcbc95 Mon Sep 17 00:00:00 2001
|
|
From: Karthikeyan Kathirvel <kathirve@codeaurora.org>
|
|
Date: Tue, 16 Feb 2021 13:44:39 +0530
|
|
Subject: [PATCH] ath11k: Add mesh nss offload support
|
|
|
|
- New capability advertising nss offload support for mesh type
|
|
- Mesh obj vap and link vap registration/clean up
|
|
- Command/event handling
|
|
- New .ch files in ath11k for nss mesh offload related debugs
|
|
- Tx/Rx data path on mesh link vap uses native wifi format
|
|
- Mesh obj vap handls packets in ether format. No Tx on Mesh
|
|
obj vap is expected as packets transmitted in slow path is
|
|
supposed to be encapsulated in 802.11 format.
|
|
- New mac80211-driver callbacks for mesh vap, mpath and mpp
|
|
configurations.
|
|
|
|
Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@codeaurora.org>
|
|
|
|
Change-Id: Ib6950344286ba18fab43586262c62dcd09557614
|
|
Co-developed-by: Karthikeyan Kathirvel <kathirve@codeaurora.org>
|
|
Signed-off-by: Karthikeyan Kathirvel <kathirve@codeaurora.org>
|
|
Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@codeaurora.org>
|
|
---
|
|
drivers/net/wireless/ath/ath11k/Makefile | 2 +-
|
|
drivers/net/wireless/ath/ath11k/debug_nss.c | 343 +++++++
|
|
drivers/net/wireless/ath/ath11k/debug_nss.h | 24 +
|
|
drivers/net/wireless/ath/ath11k/dp.h | 16 +-
|
|
drivers/net/wireless/ath/ath11k/dp_rx.c | 170 ++-
|
|
drivers/net/wireless/ath/ath11k/dp_rx.h | 2 +
|
|
drivers/net/wireless/ath/ath11k/mac.c | 36 +-
|
|
drivers/net/wireless/ath/ath11k/nss.c | 1482 ++++++++++++++++++++++++---
|
|
drivers/net/wireless/ath/ath11k/nss.h | 63 +-
|
|
include/net/mac80211.h | 106 ++
|
|
net/mac80211/cfg.c | 19 +-
|
|
net/mac80211/debug.h | 10 +
|
|
net/mac80211/debugfs.c | 1 +
|
|
net/mac80211/driver-ops.c | 20 +
|
|
net/mac80211/driver-ops.h | 7 +
|
|
net/mac80211/mesh.h | 5 +
|
|
net/mac80211/mesh_hwmp.c | 273 +++++
|
|
net/mac80211/mesh_pathtbl.c | 167 ++-
|
|
net/mac80211/rx.c | 8 +-
|
|
19 files changed, 2548 insertions(+), 206 deletions(-)
|
|
create mode 100644 drivers/net/wireless/ath/ath11k/debug_nss.c
|
|
create mode 100644 drivers/net/wireless/ath/ath11k/debug_nss.h
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/Makefile
|
|
+++ b/drivers/net/wireless/ath/ath11k/Makefile
|
|
@@ -20,7 +20,7 @@ ath11k-y += core.o \
|
|
wow.o \
|
|
vendor.o
|
|
|
|
-ath11k-$(CPTCFG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o
|
|
+ath11k-$(CPTCFG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o debug_nss.o
|
|
ath11k-$(CPTCFG_NL80211_TESTMODE) += testmode.o
|
|
ath11k-$(CPTCFG_ATH11K_TRACING) += trace.o
|
|
ath11k-$(CONFIG_THERMAL) += thermal.o
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/ath/ath11k/debug_nss.c
|
|
@@ -0,0 +1,924 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
+/*
|
|
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
|
+ */
|
|
+
|
|
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
|
|
+
|
|
+#include <net/mac80211.h>
|
|
+#include "dp_rx.h"
|
|
+#include "nss.h"
|
|
+#include "debug.h"
|
|
+#include "debug_nss.h"
|
|
+
|
|
+static unsigned int
|
|
+debug_nss_fill_mpp_dump(struct ath11k_vif *arvif, char *buf, ssize_t size)
|
|
+{
|
|
+ struct arvif_nss *nss = &arvif->nss;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ struct ath11k_nss_mpp_entry *entry, *tmp;
|
|
+ LIST_HEAD(local_entry);
|
|
+ unsigned int len = 0;
|
|
+ int i;
|
|
+
|
|
+ len += scnprintf(buf + len, size - len, "\nProxy path table\n");
|
|
+ len += scnprintf(buf + len, size - len, "dest_mac_addr\t\tmesh_dest_mac\t\tflags\n");
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_splice_tail_init(&nss->mpp_dump, &local_entry);
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+
|
|
+ list_for_each_entry_safe(entry, tmp, &local_entry, list) {
|
|
+ for (i = 0; i < entry->num_entries; i++)
|
|
+ len += scnprintf(buf + len, size - len, "%pM\t%pM\t0x%x\n",
|
|
+ entry->mpp[i].dest_mac_addr,
|
|
+ entry->mpp[i].mesh_dest_mac, entry->mpp[i].flags);
|
|
+ list_del(&entry->list);
|
|
+ kfree(entry);
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_dump_mpp_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct ath11k_vif *arvif = inode->i_private;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ unsigned long time_left;
|
|
+ struct ath11k_nss_dbg_priv_data *priv_data;
|
|
+ int ret;
|
|
+ ssize_t size = 100;
|
|
+ char *buf;
|
|
+
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+
|
|
+ reinit_completion(&arvif->nss.dump_mpp_complete);
|
|
+
|
|
+ priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
|
|
+ if (!priv_data) {
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ priv_data->arvif = arvif;
|
|
+ ret = ath11k_nss_dump_mpp_request(arvif);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ar->ab, "failed to send dump mpp command %d\n", ret);
|
|
+ goto err_unlock;
|
|
+ }
|
|
+
|
|
+ time_left = wait_for_completion_timeout(&arvif->nss.dump_mpp_complete,
|
|
+ ATH11K_NSS_MPATH_DUMP_TIMEOUT);
|
|
+ if (time_left == 0) {
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto err_unlock;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+
|
|
+ size += (arvif->nss.mpp_dump_num_entries * 200 + 10 * 100);
|
|
+ buf = kmalloc(size, GFP_KERNEL);
|
|
+ if (!buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ priv_data->buf = buf;
|
|
+
|
|
+ priv_data->len= debug_nss_fill_mpp_dump(arvif, buf, size);
|
|
+
|
|
+ file->private_data = priv_data;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_unlock:
|
|
+ kfree(priv_data);
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_dump_mpp_release(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct ath11k_nss_dbg_priv_data *priv_data = file->private_data;
|
|
+
|
|
+ kfree(priv_data->buf);
|
|
+ kfree(priv_data);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t ath11k_nss_dump_mpp_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_nss_dbg_priv_data *priv_data = file->private_data;
|
|
+ struct ath11k_vif *arvif = priv_data->arvif;
|
|
+ char *buf = priv_data->buf;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+
|
|
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, priv_data->len);
|
|
+
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_dump_mpp_table = {
|
|
+ .open = ath11k_nss_dump_mpp_open,
|
|
+ .read = ath11k_nss_dump_mpp_read,
|
|
+ .release = ath11k_nss_dump_mpp_release,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static unsigned int
|
|
+debug_nss_fill_mpath_dump(struct ath11k_vif *arvif, char *buf, ssize_t size)
|
|
+{
|
|
+ struct arvif_nss *nss = &arvif->nss;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ struct ath11k_nss_mpath_entry *entry, *tmp;
|
|
+ LIST_HEAD(local_entry);
|
|
+ unsigned int len = 0;
|
|
+ u64 expiry_time;
|
|
+ int i;
|
|
+
|
|
+ len += scnprintf(buf + len, size - len, "\nmpath table\n");
|
|
+ len += scnprintf(buf + len, size - len, "dest_mac_addr\t\tnext_hop_mac\t\tmetric\t"
|
|
+ "expiry_time\thop_count\tflags\tlink_vap_id\n");
|
|
+
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_splice_tail_init(&nss->mpath_dump, &local_entry);
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+
|
|
+ list_for_each_entry_safe(entry, tmp, &local_entry, list) {
|
|
+ for (i = 0; i < entry->num_entries; i++) {
|
|
+ memcpy(&expiry_time, entry->mpath[i].expiry_time, sizeof(u64));
|
|
+ len += scnprintf(buf + len, size - len, "%pM\t%pM\t%u\t%llu\t\t\t%d\t0x%x\t%d\n",
|
|
+ entry->mpath[i].dest_mac_addr,
|
|
+ entry->mpath[i].next_hop_mac_addr, entry->mpath[i].metric,
|
|
+ expiry_time, entry->mpath[i].hop_count,
|
|
+ entry->mpath[i].flags, entry->mpath[i].link_vap_id);
|
|
+ }
|
|
+ kfree(entry);
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_dump_mpath_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct ath11k_vif *arvif = inode->i_private;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ unsigned long time_left;
|
|
+ struct ath11k_nss_dbg_priv_data *priv_data;
|
|
+ ssize_t size = 200;
|
|
+ char *buf;
|
|
+ int ret;
|
|
+
|
|
+ reinit_completion(&arvif->nss.dump_mpath_complete);
|
|
+
|
|
+ priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
|
|
+ if (!priv_data)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+
|
|
+ priv_data->arvif = arvif;
|
|
+ ret = ath11k_nss_dump_mpath_request(arvif);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ar->ab, "failed to send dump mpath command %d\n", ret);
|
|
+ goto err_unlock;
|
|
+ }
|
|
+
|
|
+ time_left = wait_for_completion_timeout(&arvif->nss.dump_mpath_complete,
|
|
+ ATH11K_NSS_MPATH_DUMP_TIMEOUT);
|
|
+ if (time_left == 0) {
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto err_unlock;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+
|
|
+ size += (arvif->nss.mpath_dump_num_entries * 200 + 10 * 100);
|
|
+ buf = kmalloc(size, GFP_KERNEL);
|
|
+ if (!buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ priv_data->buf = buf;
|
|
+
|
|
+ priv_data->len = debug_nss_fill_mpath_dump(arvif, buf, size);
|
|
+
|
|
+ file->private_data = priv_data;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_unlock:
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_dump_mpath_release(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct ath11k_nss_dbg_priv_data *priv_data = file->private_data;
|
|
+
|
|
+ kfree(priv_data->buf);
|
|
+ kfree(priv_data);
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+static ssize_t ath11k_nss_dump_mpath_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_nss_dbg_priv_data *priv_data = file->private_data;
|
|
+ struct ath11k_vif *arvif = priv_data->arvif;
|
|
+ char *buf = priv_data->buf;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+
|
|
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, priv_data->len);
|
|
+
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_dump_mpath_table = {
|
|
+ .open = ath11k_nss_dump_mpath_open,
|
|
+ .read = ath11k_nss_dump_mpath_read,
|
|
+ .release = ath11k_nss_dump_mpath_release,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_mpath_add(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+ ret = sscanf(buf, "%u %hhu %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %hhu %hhu",
|
|
+ &path.metric,
|
|
+ &path.hop_count,
|
|
+ &path.mesh_da[0],
|
|
+ &path.mesh_da[1],
|
|
+ &path.mesh_da[2],
|
|
+ &path.mesh_da[3],
|
|
+ &path.mesh_da[4],
|
|
+ &path.mesh_da[5],
|
|
+ &path.next_hop[0],
|
|
+ &path.next_hop[1],
|
|
+ &path.next_hop[2],
|
|
+ &path.next_hop[3],
|
|
+ &path.next_hop[4],
|
|
+ &path.next_hop[5],
|
|
+ &path.block_mesh_fwd,
|
|
+ &path.metadata_type);
|
|
+
|
|
+
|
|
+ path.flags |= IEEE80211_MESH_PATH_ACTIVE | IEEE80211_MESH_PATH_RESOLVED;
|
|
+
|
|
+ if (ret != 16)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Configure the mpath */
|
|
+ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPATH,
|
|
+ &path);
|
|
+ if(ret) {
|
|
+ ath11k_warn(arvif->ar->ab, "failed to configure mpath ret %d\n", ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret ? ret : count;
|
|
+
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_mpath_add = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_mpath_add,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_mpp_add(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+
|
|
+ if (!arvif->ar->ab->nss.debug_mode) {
|
|
+ ret = -EPERM;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
|
|
+ &path.da[0],
|
|
+ &path.da[1],
|
|
+ &path.da[2],
|
|
+ &path.da[3],
|
|
+ &path.da[4],
|
|
+ &path.da[5],
|
|
+ &path.mesh_da[0],
|
|
+ &path.mesh_da[1],
|
|
+ &path.mesh_da[2],
|
|
+ &path.mesh_da[3],
|
|
+ &path.mesh_da[4],
|
|
+ &path.mesh_da[5]);
|
|
+
|
|
+ path.flags |= IEEE80211_MESH_PATH_ACTIVE | IEEE80211_MESH_PATH_RESOLVED;
|
|
+
|
|
+ if (ret != 12)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Configure the mpp */
|
|
+ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPP,
|
|
+ &path);
|
|
+ if(ret) {
|
|
+ ath11k_warn(arvif->ar->ab, "failed to configure mpp ret %d\n", ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret ? ret : count;
|
|
+
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_mpp_add = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_mpp_add,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_mpath_update(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+ ret = sscanf(buf, "%u %hhu %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %hhu %lu %hhu %hhu",
|
|
+ &path.metric,
|
|
+ &path.hop_count,
|
|
+ &path.mesh_da[0],
|
|
+ &path.mesh_da[1],
|
|
+ &path.mesh_da[2],
|
|
+ &path.mesh_da[3],
|
|
+ &path.mesh_da[4],
|
|
+ &path.mesh_da[5],
|
|
+ &path.next_hop[0],
|
|
+ &path.next_hop[1],
|
|
+ &path.next_hop[2],
|
|
+ &path.next_hop[3],
|
|
+ &path.next_hop[4],
|
|
+ &path.next_hop[5],
|
|
+ &path.old_next_hop[0],
|
|
+ &path.old_next_hop[1],
|
|
+ &path.old_next_hop[2],
|
|
+ &path.old_next_hop[3],
|
|
+ &path.old_next_hop[4],
|
|
+ &path.old_next_hop[5],
|
|
+ &path.mesh_gate,
|
|
+ &path.exp_time,
|
|
+ &path.block_mesh_fwd,
|
|
+ &path.metadata_type);
|
|
+
|
|
+
|
|
+ path.flags |= IEEE80211_MESH_PATH_ACTIVE | IEEE80211_MESH_PATH_RESOLVED;
|
|
+
|
|
+ if (ret != 24)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Configure the mpath */
|
|
+ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPATH,
|
|
+ &path);
|
|
+ if(ret) {
|
|
+ ath11k_warn(arvif->ar->ab, "failed to configure mpath ret %d\n", ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret ? ret : count;
|
|
+
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_mpath_update = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_mpath_update,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_mpp_update(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+
|
|
+ if (!arvif->ar->ab->nss.debug_mode) {
|
|
+ ret = -EPERM;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
|
|
+ &path.da[0],
|
|
+ &path.da[1],
|
|
+ &path.da[2],
|
|
+ &path.da[3],
|
|
+ &path.da[4],
|
|
+ &path.da[5],
|
|
+ &path.mesh_da[0],
|
|
+ &path.mesh_da[1],
|
|
+ &path.mesh_da[2],
|
|
+ &path.mesh_da[3],
|
|
+ &path.mesh_da[4],
|
|
+ &path.mesh_da[5]);
|
|
+
|
|
+ path.flags |= IEEE80211_MESH_PATH_ACTIVE | IEEE80211_MESH_PATH_RESOLVED;
|
|
+
|
|
+ if (ret != 12)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Configure the mpp */
|
|
+ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPP,
|
|
+ &path);
|
|
+ if(ret) {
|
|
+ ath11k_warn(arvif->ar->ab, "failed to configure mpp ret %d\n", ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret ? ret : count;
|
|
+
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_mpp_update = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_mpp_update,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+
|
|
+static ssize_t ath11k_nss_mpath_delete(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+
|
|
+ if (!arvif->ar->ab->nss.debug_mode) {
|
|
+ ret = -EPERM;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
|
|
+ &path.mesh_da[0],
|
|
+ &path.mesh_da[1],
|
|
+ &path.mesh_da[2],
|
|
+ &path.mesh_da[3],
|
|
+ &path.mesh_da[4],
|
|
+ &path.mesh_da[5],
|
|
+ &path.next_hop[0],
|
|
+ &path.next_hop[1],
|
|
+ &path.next_hop[2],
|
|
+ &path.next_hop[3],
|
|
+ &path.next_hop[4],
|
|
+ &path.next_hop[5]);
|
|
+
|
|
+ path.flags |= IEEE80211_MESH_PATH_DELETED;
|
|
+
|
|
+ if (ret != 12)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Configure the mpath */
|
|
+ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPATH,
|
|
+ &path);
|
|
+ if(ret) {
|
|
+ ath11k_warn(arvif->ar->ab, "failed to configure mpath ret %d\n", ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret ? ret : count;
|
|
+
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_mpath_del = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_mpath_delete,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_mpp_delete(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+
|
|
+ if (!arvif->ar->ab->nss.debug_mode) {
|
|
+ ret = -EPERM;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
|
|
+ &path.da[0],
|
|
+ &path.da[1],
|
|
+ &path.da[2],
|
|
+ &path.da[3],
|
|
+ &path.da[4],
|
|
+ &path.da[5],
|
|
+ &path.mesh_da[0],
|
|
+ &path.mesh_da[1],
|
|
+ &path.mesh_da[2],
|
|
+ &path.mesh_da[3],
|
|
+ &path.mesh_da[4],
|
|
+ &path.mesh_da[5]);
|
|
+
|
|
+ path.flags |= IEEE80211_MESH_PATH_DELETED;
|
|
+
|
|
+ if (ret != 12)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Configure the mpp */
|
|
+ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPP,
|
|
+ &path);
|
|
+ if(ret) {
|
|
+ ath11k_warn(arvif->ar->ab, "failed to configure mpp ret %d\n", ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret ? ret : count;
|
|
+
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_mpp_del = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_mpp_delete,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+
|
|
+static ssize_t ath11k_nss_assoc_link(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+ u32 assoc_link = 0;
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+ ret = sscanf(buf, "%u", &assoc_link);
|
|
+
|
|
+ if (ret != 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ arvif->ar->ab->nss.debug_mode = true;
|
|
+ arvif->vif->driver_flags |= IEEE80211_VIF_NSS_OFFLOAD_DEBUG_MODE;
|
|
+
|
|
+ ret = ath11k_nss_assoc_link_arvif_to_ifnum(arvif, assoc_link);
|
|
+
|
|
+ return ret ? ret : count;
|
|
+
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_assoc_link = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_assoc_link,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_links(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ char buf[512] = {0};
|
|
+ struct arvif_nss *nss;
|
|
+ int len = 0;
|
|
+
|
|
+ list_for_each_entry(nss, &mesh_vaps, list)
|
|
+ len += scnprintf(buf + len, sizeof(buf) - len, "link id %d\n",
|
|
+ nss->if_num);
|
|
+
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_links = {
|
|
+ .open = simple_open,
|
|
+ .read = ath11k_nss_links,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_vap_link_id(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ struct arvif_nss *nss = &arvif->nss;
|
|
+ char buf[512] = {0};
|
|
+ int len = 0;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf) - len, "link id %d\n",
|
|
+ nss->if_num);
|
|
+
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_vap_link_id = {
|
|
+ .open = simple_open,
|
|
+ .read = ath11k_nss_vap_link_id,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_read_mpp_mode(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ char buf[512] = {0};
|
|
+ int len = 0;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf) - len, "%s\n",mpp_mode ?
|
|
+ "Host Assisted Learning" : "NSS Independent Learning");
|
|
+
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static ssize_t ath11k_nss_write_mpp_mode(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+ u32 mppath_mode = 0;
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+ ret = sscanf(buf, "%u", &mppath_mode);
|
|
+
|
|
+ if (ret != 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mpp_mode = mppath_mode;
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+ return ret ? ret : count;
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_mpp_mode = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_write_mpp_mode,
|
|
+ .read = ath11k_nss_read_mpp_mode,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_write_excep_flags(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+ struct nss_wifi_mesh_exception_flag_msg msg;
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+
|
|
+ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %hhu",
|
|
+ &msg.dest_mac_addr[0],
|
|
+ &msg.dest_mac_addr[1],
|
|
+ &msg.dest_mac_addr[2],
|
|
+ &msg.dest_mac_addr[3],
|
|
+ &msg.dest_mac_addr[4],
|
|
+ &msg.dest_mac_addr[5],
|
|
+ &msg.exception);
|
|
+
|
|
+ if (ret != 7)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = ath11k_nss_mesh_exception_flags(arvif, &msg);
|
|
+
|
|
+ return ret ? ret : count;
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_excep_flags = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_write_excep_flags,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_write_metadata_type(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+ u8 pkt_type;
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+
|
|
+ ret = sscanf(buf, "%hhu", &pkt_type);
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+ arvif->nss.metadata_type = pkt_type ? NSS_WIFI_MESH_PRE_HEADER_80211 : NSS_WIFI_MESH_PRE_HEADER_NONE;
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+
|
|
+ if (ret != 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+ return ret ? ret : count;
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_metadata_type = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_write_metadata_type,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static ssize_t ath11k_nss_write_exc_rate_limit(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_vif *arvif = file->private_data;
|
|
+ u8 buf[128] = {0};
|
|
+ int ret;
|
|
+ struct nss_wifi_mesh_rate_limit_config nss_exc_cfg;
|
|
+
|
|
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ buf[ret] = '\0';
|
|
+
|
|
+ ret = sscanf(buf, "%u %u %u",
|
|
+ &nss_exc_cfg.exception_num,
|
|
+ &nss_exc_cfg.enable,
|
|
+ &nss_exc_cfg.rate_limit);
|
|
+
|
|
+ if (ret != 3)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = ath11k_nss_exc_rate_config(arvif, &nss_exc_cfg);
|
|
+
|
|
+ return ret ? ret : count;
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_nss_exc_rate_limit = {
|
|
+ .open = simple_open,
|
|
+ .write = ath11k_nss_write_exc_rate_limit,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+void ath11k_debugfs_nss_mesh_vap_create(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct dentry *debugfs_nss_mesh_dir, *debugfs_dbg_infra;
|
|
+
|
|
+ debugfs_nss_mesh_dir = debugfs_create_dir("nss_mesh", arvif->vif->debugfs_dir);
|
|
+ debugfs_dbg_infra = debugfs_create_dir("dbg_infra", debugfs_nss_mesh_dir);
|
|
+
|
|
+ debugfs_create_file("dump_nss_mpath_table", 0600,
|
|
+ debugfs_nss_mesh_dir, arvif,
|
|
+ &fops_nss_dump_mpath_table);
|
|
+
|
|
+ debugfs_create_file("dump_nss_mpp_table", 0600,
|
|
+ debugfs_nss_mesh_dir, arvif,
|
|
+ &fops_nss_dump_mpp_table);
|
|
+
|
|
+ debugfs_create_file("mpath_add", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_mpath_add);
|
|
+
|
|
+ debugfs_create_file("mpath_update", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_mpath_update);
|
|
+
|
|
+ debugfs_create_file("mpath_del", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_mpath_del);
|
|
+
|
|
+ debugfs_create_file("mpp_add", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_mpp_add);
|
|
+
|
|
+ debugfs_create_file("mpp_update", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_mpp_update);
|
|
+
|
|
+ debugfs_create_file("mpp_del", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_mpp_del);
|
|
+
|
|
+ debugfs_create_file("assoc_link", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_assoc_link);
|
|
+
|
|
+ debugfs_create_file("vap_linkid", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_vap_link_id);
|
|
+
|
|
+ debugfs_create_file("excep_flags", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_excep_flags);
|
|
+
|
|
+ debugfs_create_file("metadata_type", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_metadata_type);
|
|
+
|
|
+ debugfs_create_file("exc_rate_limit", 0200,
|
|
+ debugfs_dbg_infra, arvif,
|
|
+ &fops_nss_exc_rate_limit);
|
|
+}
|
|
+
|
|
+void ath11k_debugfs_nss_soc_create(struct ath11k_base *ab)
|
|
+{
|
|
+ struct dentry *debugfs_dbg_infra;
|
|
+
|
|
+ debugfs_dbg_infra = debugfs_create_dir("dbg_infra", debugfs_ath11k);
|
|
+
|
|
+ debugfs_create_file("links", 0200,
|
|
+ debugfs_dbg_infra, ab,
|
|
+ &fops_nss_links);
|
|
+
|
|
+ debugfs_create_file("mpp_mode", 0600,
|
|
+ debugfs_dbg_infra, ab,
|
|
+ &fops_nss_mpp_mode);
|
|
+}
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/ath/ath11k/debug_nss.h
|
|
@@ -0,0 +1,35 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
|
|
+/*
|
|
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
|
+ */
|
|
+
|
|
+#ifndef ATH11K_DEBUG_NSS_H
|
|
+#define ATH11K_DEBUG_NSS_H
|
|
+
|
|
+#include <nss_api_if.h>
|
|
+#include <nss_cmn.h>
|
|
+
|
|
+#define ATH11K_NSS_MPATH_DUMP_TIMEOUT (2 * HZ)
|
|
+
|
|
+struct ath11k_vif;
|
|
+extern enum nss_wifi_mesh_mpp_learning_mode mpp_mode;
|
|
+extern struct list_head mesh_vaps;
|
|
+struct ath11k_nss_dbg_priv_data {
|
|
+ struct ath11k_vif *arvif;
|
|
+ char *buf;
|
|
+ unsigned int len;
|
|
+};
|
|
+
|
|
+#ifdef CPTCFG_MAC80211_DEBUGFS
|
|
+void ath11k_debugfs_nss_mesh_vap_create(struct ath11k_vif *arvif);
|
|
+void ath11k_debugfs_nss_soc_create(struct ath11k_base *ab);
|
|
+#else
|
|
+static inline void ath11k_debugfs_nss_mesh_vap_create(struct ath11k_vif *arvif)
|
|
+{
|
|
+}
|
|
+static inline void ath11k_debugfs_nss_soc_create(struct ath11k_base *ab)
|
|
+{
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
--- a/drivers/net/wireless/ath/ath11k/dp.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/dp.h
|
|
@@ -1575,15 +1575,29 @@ struct htt_ppdu_stats_usr_cmn_array {
|
|
struct htt_tx_ppdu_stats_info tx_ppdu_info[0];
|
|
} __packed;
|
|
|
|
+#define HTT_PPDU_STATS_CMPLTN_FLUSH_INFO_FLOW_TYPE GENMASK(7, 0)
|
|
+#define HTT_PPDU_STATS_CMPLTN_FLUSH_INFO_NUM_MPDU GENMASK(16, 8)
|
|
+#define HTT_PPDU_STATS_CMPLTN_FLUSH_INFO_NUM_MSDU GENMASK(30, 17)
|
|
+
|
|
+struct htt_ppdu_stats_cmpltn_flush {
|
|
+ u32 drop_reason;
|
|
+ u32 info;
|
|
+ u8 tid_num;
|
|
+ u8 queue_type;
|
|
+ u16 sw_peer_id;
|
|
+} __packed;
|
|
+
|
|
struct htt_ppdu_user_stats {
|
|
u16 peer_id;
|
|
u16 delay_ba;
|
|
u32 tlv_flags;
|
|
bool is_valid_peer_id;
|
|
+ bool rate_stats_updated;
|
|
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;
|
|
struct htt_ppdu_stats_user_common common;
|
|
+ struct htt_ppdu_stats_cmpltn_flush cmpltn_flush;
|
|
};
|
|
|
|
#define HTT_PPDU_STATS_MAX_USERS 37
|
|
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
|
|
@@ -1405,6 +1405,68 @@ static int ath11k_htt_tlv_ppdu_stats_par
|
|
return 0;
|
|
}
|
|
|
|
+static void ath11k_dp_ppdu_stats_flush_tlv_parse(struct ath11k_base *ab,
|
|
+ struct htt_ppdu_stats_cmpltn_flush *msg)
|
|
+{
|
|
+ struct ath11k *ar;
|
|
+ struct ieee80211_sta *sta;
|
|
+ struct ath11k_sta *arsta;
|
|
+ struct ath11k_peer *peer = NULL;
|
|
+ struct ieee80211_tx_status status;
|
|
+
|
|
+ if (!ab->nss.mesh_nss_offload_enabled)
|
|
+ return;
|
|
+
|
|
+ rcu_read_lock();
|
|
+
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+ peer = ath11k_peer_find_by_id(ab, msg->sw_peer_id);
|
|
+ if (!peer)
|
|
+ goto exit;
|
|
+
|
|
+ if (peer->vif->type != NL80211_IFTYPE_MESH_POINT)
|
|
+ goto exit;
|
|
+
|
|
+ if (ether_addr_equal(peer->addr, peer->vif->addr))
|
|
+ goto exit;
|
|
+
|
|
+ sta = peer->sta;
|
|
+ arsta = (struct ath11k_sta *)sta->drv_priv;
|
|
+
|
|
+ memset(&status, 0, sizeof(status));
|
|
+
|
|
+ status.sta = sta;
|
|
+ status.rate = &arsta->last_txrate;
|
|
+ status.mpdu_fail = FIELD_GET(HTT_PPDU_STATS_CMPLTN_FLUSH_INFO_NUM_MPDU,
|
|
+ msg->info);
|
|
+ ar = arsta->arvif->ar;
|
|
+ ieee80211s_update_metric_ppdu(ar->hw, &status);
|
|
+
|
|
+exit:
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ rcu_read_unlock();
|
|
+}
|
|
+
|
|
+static int ath11k_htt_tlv_ppdu_soc_stats_parse(struct ath11k_base *ab,
|
|
+ u16 tag, u16 len, const void *ptr,
|
|
+ void *data)
|
|
+{
|
|
+ switch (tag) {
|
|
+ case HTT_PPDU_STATS_TAG_USR_COMPLTN_FLUSH:
|
|
+ if (len < sizeof(struct htt_ppdu_stats_cmpltn_flush)) {
|
|
+ ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n",
|
|
+ len, tag);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ath11k_dp_ppdu_stats_flush_tlv_parse(ab, ptr);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static void
|
|
ath11k_update_per_peer_tx_stats(struct ath11k *ar,
|
|
struct htt_ppdu_stats *ppdu_stats, u8 user)
|
|
@@ -1431,6 +1493,9 @@ ath11k_update_per_peer_tx_stats(struct a
|
|
if (!(usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_RATE)))
|
|
return;
|
|
|
|
+ if (usr_stats->rate_stats_updated)
|
|
+ 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);
|
|
@@ -1568,6 +1633,8 @@ ath11k_update_per_peer_tx_stats(struct a
|
|
ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx);
|
|
}
|
|
|
|
+ usr_stats->rate_stats_updated = true;
|
|
+
|
|
spin_unlock_bh(&ab->base_lock);
|
|
rcu_read_unlock();
|
|
}
|
|
@@ -1655,8 +1722,10 @@ int ath11k_dp_htt_tlv_iter(struct ath11k
|
|
int ret = -EINVAL;
|
|
struct htt_ppdu_stats_info * ppdu_info = NULL;
|
|
|
|
- ppdu_info = (struct htt_ppdu_stats_info *)data;
|
|
- ppdu_info->tlv_bitmap = 0;
|
|
+ if (data) {
|
|
+ ppdu_info = (struct htt_ppdu_stats_info *)data;
|
|
+ ppdu_info->tlv_bitmap = 0;
|
|
+ }
|
|
while (len > 0) {
|
|
if (len < sizeof(*tlv)) {
|
|
ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n",
|
|
@@ -1685,6 +1754,66 @@ int ath11k_dp_htt_tlv_iter(struct ath11k
|
|
return 0;
|
|
}
|
|
|
|
+static void
|
|
+ath11k_dp_rx_ppdu_stats_update_tx_comp_status(struct ath11k *ar,
|
|
+ struct htt_ppdu_stats_info *ppdu_info)
|
|
+{
|
|
+ struct ath11k_base *ab = ar->ab;
|
|
+ struct ieee80211_sta *sta;
|
|
+ struct ath11k_sta *arsta;
|
|
+ struct ath11k_peer *peer = NULL;
|
|
+ struct htt_ppdu_user_stats* usr_stats = NULL;
|
|
+ struct ieee80211_tx_status status;
|
|
+ u32 peer_id = 0;
|
|
+ int i;
|
|
+
|
|
+ lockdep_assert_held(&ar->data_lock);
|
|
+
|
|
+ if (!ar->ab->nss.mesh_nss_offload_enabled)
|
|
+ return;
|
|
+
|
|
+ ath11k_htt_update_ppdu_stats(ar, &ppdu_info->ppdu_stats);
|
|
+
|
|
+ rcu_read_lock();
|
|
+
|
|
+ for (i = 0; i < ppdu_info->ppdu_stats.common.num_users; i++) {
|
|
+ usr_stats = &ppdu_info->ppdu_stats.user_stats[i];
|
|
+ peer_id = usr_stats->peer_id;
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
+ peer = ath11k_peer_find_by_id(ab, peer_id);
|
|
+ if (!peer) {
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (peer->vif->type != NL80211_IFTYPE_MESH_POINT) {
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ if (ether_addr_equal(peer->addr, peer->vif->addr)) {
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ sta = peer->sta;
|
|
+ arsta = (struct ath11k_sta *)sta->drv_priv;
|
|
+
|
|
+ memset(&status, 0, sizeof(status));
|
|
+
|
|
+ status.sta = sta;
|
|
+ status.rate = &arsta->last_txrate;
|
|
+ status.mpdu_succ = usr_stats->cmpltn_cmn.mpdu_success;
|
|
+
|
|
+ ieee80211s_update_metric_ppdu(ar->hw, &status);
|
|
+
|
|
+ spin_unlock_bh(&ab->base_lock);
|
|
+ }
|
|
+
|
|
+exit:
|
|
+ rcu_read_unlock();
|
|
+}
|
|
+
|
|
static int ath11k_htt_pull_ppdu_stats(struct ath11k_base *ab,
|
|
struct sk_buff *skb)
|
|
{
|
|
@@ -1703,6 +1832,15 @@ static int ath11k_htt_pull_ppdu_stats(st
|
|
pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, msg->info);
|
|
ppdu_id = msg->ppdu_id;
|
|
|
|
+ if (pdev_id == 0) {
|
|
+ ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len,
|
|
+ ath11k_htt_tlv_ppdu_soc_stats_parse,
|
|
+ NULL);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failed to parse tlv %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
rcu_read_lock();
|
|
ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id);
|
|
if (!ar) {
|
|
@@ -1774,6 +1912,12 @@ static int ath11k_htt_pull_ppdu_stats(st
|
|
}
|
|
}
|
|
|
|
+ /* Stats update for mesh interface used when nss-offload in mesh is enabled */
|
|
+ if ((ppdu_info->frame_type == HTT_STATS_PPDU_FTYPE_DATA &&
|
|
+ (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_RATE)) &&
|
|
+ ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)))
|
|
+ ath11k_dp_rx_ppdu_stats_update_tx_comp_status(ar, ppdu_info);
|
|
+
|
|
spin_unlock_bh(&ar->data_lock);
|
|
exit:
|
|
rcu_read_unlock();
|
|
@@ -3031,6 +3175,23 @@ static void ath11k_dp_rx_process_receive
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
+void ath11k_dp_rx_from_nss(struct ath11k *ar, struct sk_buff *msdu,
|
|
+ struct napi_struct *napi)
|
|
+{
|
|
+ struct ieee80211_rx_status rx_status = {0};
|
|
+ struct ath11k_skb_rxcb *rxcb;
|
|
+ bool fast_rx = false;
|
|
+
|
|
+ rxcb = ATH11K_SKB_RXCB(msdu);
|
|
+
|
|
+ ath11k_dp_rx_h_ppdu(ar, rxcb->rx_desc, &rx_status);
|
|
+ ath11k_dp_rx_h_mpdu(ar, msdu, rxcb->rx_desc, &rx_status, &fast_rx);
|
|
+
|
|
+ rx_status.flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED;
|
|
+
|
|
+ ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status);
|
|
+}
|
|
+
|
|
int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id,
|
|
struct napi_struct *napi, int budget)
|
|
{
|
|
--- a/drivers/net/wireless/ath/ath11k/dp_rx.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.h
|
|
@@ -159,4 +159,6 @@ bool ath11k_dp_rx_h_attn_is_mcbc(struct
|
|
struct hal_rx_desc *desc);
|
|
u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab,
|
|
struct hal_rx_desc *desc);
|
|
+void ath11k_dp_rx_from_nss(struct ath11k *ar, struct sk_buff *msdu,
|
|
+ struct napi_struct *napi);
|
|
#endif /* ATH11K_DP_RX_H */
|
|
--- a/drivers/net/wireless/ath/ath11k/mac.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/mac.c
|
|
@@ -3092,6 +3092,16 @@ static void ath11k_mac_op_nss_bss_info_c
|
|
ath11k_warn(ar->ab, "failed to set ap_isolate in nss %d\n", ret);
|
|
}
|
|
|
|
+ if (changed & (BSS_CHANGED_NSS_MESH_TTL |
|
|
+ BSS_CHANGED_NSS_MESH_REFRESH_TIME |
|
|
+ BSS_CHANGED_NSS_MESH_FWD_ENABLED)) {
|
|
+ ret = ath11k_nss_mesh_config_update(vif, changed);
|
|
+ if (ret)
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to update mesh nss offload configuration %d\n",
|
|
+ ret);
|
|
+ }
|
|
+
|
|
mutex_unlock(&ar->conf_mutex);
|
|
}
|
|
|
|
@@ -6056,7 +6066,7 @@ static void ath11k_mac_op_tx(struct ieee
|
|
skb_cb->flags |= ATH11K_SKB_TX_STATUS;
|
|
|
|
if (ar->ab->nss.enabled)
|
|
- ret = ath11k_nss_tx(arvif,skb);
|
|
+ ret = ath11k_nss_tx(arvif, skb);
|
|
else
|
|
ret = ath11k_dp_tx(ar, arvif, skb,
|
|
(control->sta) ? control->sta->drv_priv : NULL);
|
|
@@ -8449,6 +8459,28 @@ static void ath11k_mac_op_sta_statistics
|
|
ath11k_nss_update_sta_stats(arvif, sinfo, sta);
|
|
}
|
|
|
|
+#ifdef CPTCFG_MAC80211_MESH
|
|
+static void
|
|
+ath11k_mac_op_config_mesh_offload_path(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_vif *vif,
|
|
+ enum ieee80211_mesh_path_offld_cmd cmd,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ struct ath11k *ar = hw->priv;
|
|
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
|
|
+ int ret;
|
|
+
|
|
+ if (arvif->ar->ab->nss.debug_mode) {
|
|
+ ret = 0;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ret = ath11k_nss_mesh_config_path(ar, arvif, cmd, path);
|
|
+ if (ret)
|
|
+ ath11k_warn(ar->ab, "failed to configure path entry to mesh table %d\n", ret);
|
|
+}
|
|
+#endif
|
|
+
|
|
#define ATH11K_WLAN_PRIO_MAX 0x63
|
|
#define ATH11K_WLAN_PRIO_WEIGHT 0xff
|
|
|
|
@@ -8573,6 +8605,9 @@ static const struct ieee80211_ops ath11k
|
|
#ifdef CPTCFG_ATH11K_DEBUGFS
|
|
.sta_add_debugfs = ath11k_debugfs_sta_op_add,
|
|
#endif
|
|
+#ifdef CPTCFG_MAC80211_MESH
|
|
+ .config_mesh_offload_path = ath11k_mac_op_config_mesh_offload_path,
|
|
+#endif
|
|
};
|
|
|
|
static void ath11k_mac_update_ch_list(struct ath11k *ar,
|
|
@@ -9068,6 +9103,8 @@ static int __ath11k_mac_register(struct
|
|
ieee80211_hw_set(ar->hw, SUPPORTS_NSS_OFFLOAD);
|
|
wiphy_ext_feature_set(ar->hw->wiphy,
|
|
NL80211_EXT_FEATURE_VLAN_OFFLOAD);
|
|
+ if (ab->nss.mesh_nss_offload_enabled)
|
|
+ ieee80211_hw_set(ar->hw, SUPPORTS_MESH_NSS_OFFLOAD);
|
|
}
|
|
|
|
ret = ieee80211_register_hw(ar->hw);
|
|
--- a/drivers/net/wireless/ath/ath11k/nss.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/nss.c
|
|
@@ -5,6 +5,7 @@
|
|
|
|
#include "mac.h"
|
|
#include "nss.h"
|
|
+#include "debug_nss.h"
|
|
#include "core.h"
|
|
#include "peer.h"
|
|
#include "dp_tx.h"
|
|
@@ -14,6 +15,9 @@
|
|
#include "wmi.h"
|
|
#include "../../../../../net/mac80211/sta_info.h"
|
|
|
|
+enum nss_wifi_mesh_mpp_learning_mode mpp_mode = NSS_WIFI_MESH_MPP_LEARNING_MODE_INDEPENDENT_NSS;
|
|
+LIST_HEAD(mesh_vaps);
|
|
+
|
|
/*-----------------------------ATH11K-NSS Helpers--------------------------*/
|
|
|
|
static enum ath11k_nss_opmode
|
|
@@ -32,6 +36,30 @@ ath11k_nss_get_vdev_opmode(struct ath11k
|
|
return ATH11K_NSS_OPMODE_UNKNOWN;
|
|
}
|
|
|
|
+static struct ath11k_vif *ath11k_nss_get_arvif_from_dev(struct net_device *dev)
|
|
+{
|
|
+ struct wireless_dev *wdev;
|
|
+ struct ieee80211_vif *vif;
|
|
+ struct ath11k_vif *arvif;
|
|
+
|
|
+ if (!dev)
|
|
+ return NULL;
|
|
+
|
|
+ wdev = dev->ieee80211_ptr;
|
|
+ if (!wdev)
|
|
+ return NULL;
|
|
+
|
|
+ vif = wdev_to_ieee80211_vif(wdev);
|
|
+ if (!vif)
|
|
+ return NULL;
|
|
+
|
|
+ arvif = (struct ath11k_vif *)vif->drv_priv;
|
|
+ if (!arvif)
|
|
+ return NULL;
|
|
+
|
|
+ return arvif;
|
|
+}
|
|
+
|
|
static void ath11k_nss_wifili_stats_sync(struct ath11k_base *ab,
|
|
struct nss_wifili_stats_sync_msg *wlsoc_stats)
|
|
{
|
|
@@ -248,6 +276,9 @@ void ath11k_nss_wifili_event_receive(str
|
|
|
|
switch (msg_type) {
|
|
case NSS_WIFILI_INIT_MSG:
|
|
+ ab->nss.response = response;
|
|
+ complete(&ab->nss.complete);
|
|
+ break;
|
|
case NSS_WIFILI_PDEV_INIT_MSG:
|
|
case NSS_WIFILI_START_MSG:
|
|
case NSS_WIFILI_SOC_RESET_MSG:
|
|
@@ -256,7 +287,6 @@ void ath11k_nss_wifili_event_receive(str
|
|
ab->nss.response = response;
|
|
complete(&ab->nss.complete);
|
|
break;
|
|
-
|
|
case NSS_WIFILI_PEER_CREATE_MSG:
|
|
if (response != NSS_CMN_RESPONSE_EMSG)
|
|
break;
|
|
@@ -326,6 +356,13 @@ void ath11k_nss_wifili_event_receive(str
|
|
ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "nss wifili peer 4addr event received %d response %d error %d\n",
|
|
msg_type, response, error);
|
|
break;
|
|
+ case NSS_WIFILI_SEND_MESH_CAPABILITY_INFO:
|
|
+ complete(&ab->nss.complete);
|
|
+ if (response != NSS_CMN_RESPONSE_EMSG)
|
|
+ ab->nss.mesh_nss_offload_enabled = true;
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "nss wifili mesh capability response %d\n",
|
|
+ ab->nss.mesh_nss_offload_enabled);
|
|
+ break;
|
|
default:
|
|
ath11k_dbg(ab, ATH11K_DBG_NSS, "unhandled event %d\n", msg_type);
|
|
break;
|
|
@@ -413,7 +450,9 @@ ath11k_nss_wifili_ext_callback_fn(struct
|
|
ath11k_nss_process_mic_error(ab, skb);
|
|
break;
|
|
default:
|
|
- kfree(skb);
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "unknown packet type received in wifili ext cb %d",
|
|
+ wepm->pkt_type);
|
|
+ dev_kfree_skb_any(skb);
|
|
break;
|
|
}
|
|
}
|
|
@@ -728,8 +767,6 @@ ath11k_nss_vdev_special_data_receive(str
|
|
{
|
|
struct nss_wifi_vdev_per_packet_metadata *wifi_metadata = NULL;
|
|
struct nss_wifi_vdev_wds_per_packet_metadata *wds_metadata = NULL;
|
|
- struct wireless_dev *wdev;
|
|
- struct ieee80211_vif *vif;
|
|
struct ath11k_vif *arvif;
|
|
struct ath11k_base *ab;
|
|
bool drop = false;
|
|
@@ -737,24 +774,7 @@ ath11k_nss_vdev_special_data_receive(str
|
|
int data_offs = 0;
|
|
int ret = 0;
|
|
|
|
- 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;
|
|
+ arvif = ath11k_nss_get_arvif_from_dev(dev);
|
|
if (!arvif) {
|
|
dev_kfree_skb_any(skb);
|
|
return;
|
|
@@ -875,17 +895,1071 @@ ath11k_nss_ext_vdev_data_receive(struct
|
|
int data_offs = 0;
|
|
int ret;
|
|
|
|
- if (!dev) {
|
|
+ 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 ext : ",
|
|
+ skb->data, skb->len);
|
|
+
|
|
+ ret = ath11k_nss_undecap(arvif, skb, &data_offs, ð_decap);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "error in nss ext rx undecap, type %d err %d\n",
|
|
+ arvif->nss.decap, ret);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi);
|
|
+}
|
|
+
|
|
+/*------Mesh offload------*/
|
|
+
|
|
+void ath11k_nss_mesh_wifili_event_receive(void *app_data,
|
|
+ struct nss_cmn_msg *cmn_msg)
|
|
+{
|
|
+ struct nss_wifi_mesh_msg *msg = (struct nss_wifi_mesh_msg *)cmn_msg;
|
|
+ struct ath11k_base *ab = app_data;
|
|
+ u32 msg_type = msg->cm.type;
|
|
+ enum nss_cmn_response response = msg->cm.response;
|
|
+ u32 error = msg->cm.error;
|
|
+
|
|
+ if (!ab)
|
|
+ return;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "nss mesh event received %d response %d error %d\n",
|
|
+ msg_type, response, error);
|
|
+
|
|
+ switch (msg_type) {
|
|
+ case NSS_WIFI_MESH_MSG_MPATH_ADD:
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG)
|
|
+ ath11k_warn(ab,"failed to add an entry to mpath table mesh_da %pM vdev_id %d\n",
|
|
+ (&msg->msg.mpath_add)->dest_mac_addr,
|
|
+ (&msg->msg.mpath_add)->link_vap_id);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_MPATH_UPDATE:
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG)
|
|
+ ath11k_warn(ab, "failed to update mpath entry mesh_da %pM vdev_id %d"
|
|
+ "next_hop %pM metric %d flags 0x%u hop_count %d"
|
|
+ "exp_time %u mesh_gate %u\n",
|
|
+ (&msg->msg.mpath_update)->dest_mac_addr,
|
|
+ (&msg->msg.mpath_update)->link_vap_id,
|
|
+ (&msg->msg.mpath_update)->next_hop_mac_addr,
|
|
+ (&msg->msg.mpath_update)->metric,
|
|
+ (&msg->msg.mpath_update)->path_flags,
|
|
+ (&msg->msg.mpath_update)->hop_count,
|
|
+ (&msg->msg.mpath_update)->expiry_time,
|
|
+ (&msg->msg.mpath_update)->is_mesh_gate);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_MPATH_DELETE:
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG)
|
|
+ ath11k_warn(ab,"failed to remove mpath entry mesh_da %pM"
|
|
+ "vdev_id %d\n",
|
|
+ (&msg->msg.mpath_del)->mesh_dest_mac_addr,
|
|
+ (&msg->msg.mpath_del)->link_vap_id);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PROXY_PATH_ADD:
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG)
|
|
+ ath11k_warn(ab,"failed to add proxy entry da %pM mesh_da %pM \n",
|
|
+ (&msg->msg.proxy_add_msg)->dest_mac_addr,
|
|
+ (&msg->msg.proxy_add_msg)->mesh_dest_mac);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PROXY_PATH_UPDATE:
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG)
|
|
+ ath11k_warn(ab,"failed to update proxy path da %pM mesh_da %pM\n",
|
|
+ (&msg->msg.proxy_update_msg)->dest_mac_addr,
|
|
+ (&msg->msg.proxy_update_msg)->mesh_dest_mac);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PROXY_PATH_DELETE:
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG)
|
|
+ ath11k_warn(ab,"failed to remove proxy path entry da %pM mesh_da %pM\n",
|
|
+ (&msg->msg.proxy_del_msg)->dest_mac_addr,
|
|
+ (&msg->msg.proxy_del_msg)->mesh_dest_mac_addr);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_EXCEPTION_FLAG:
|
|
+ if (response == NSS_CMN_RESPONSE_EMSG)
|
|
+ ath11k_warn(ab,"failed to add the exception da %pM\n",
|
|
+ (&msg->msg.exception_msg)->dest_mac_addr);
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "unhandled event %d\n", msg_type);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void nss_mesh_convert_path_flags(u16 *dest, u16 *src, bool to_nss)
|
|
+{
|
|
+ if (to_nss) {
|
|
+ if (*src & IEEE80211_MESH_PATH_ACTIVE)
|
|
+ *dest |= NSS_WIFI_MESH_PATH_FLAG_ACTIVE;
|
|
+ if (*src & IEEE80211_MESH_PATH_RESOLVING)
|
|
+ *dest |= NSS_WIFI_MESH_PATH_FLAG_RESOLVING;
|
|
+ if (*src & IEEE80211_MESH_PATH_RESOLVED)
|
|
+ *dest |= NSS_WIFI_MESH_PATH_FLAG_RESOLVED;
|
|
+ if (*src & IEEE80211_MESH_PATH_FIXED)
|
|
+ *dest |= NSS_WIFI_MESH_PATH_FLAG_FIXED;
|
|
+ } else {
|
|
+ if (*src & NSS_WIFI_MESH_PATH_FLAG_ACTIVE)
|
|
+ *dest |= IEEE80211_MESH_PATH_ACTIVE;
|
|
+ if (*src & NSS_WIFI_MESH_PATH_FLAG_RESOLVING)
|
|
+ *dest |= IEEE80211_MESH_PATH_RESOLVING;
|
|
+ if (*src & NSS_WIFI_MESH_PATH_FLAG_RESOLVED)
|
|
+ *dest |= IEEE80211_MESH_PATH_RESOLVED;
|
|
+ if (*src & NSS_WIFI_MESH_PATH_FLAG_FIXED)
|
|
+ *dest |= IEEE80211_MESH_PATH_FIXED;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ath11k_nss_mesh_mpath_refresh(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct nss_wifi_mesh_path_refresh_msg *refresh_msg;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ int ret;
|
|
+
|
|
+ refresh_msg = &msg->msg.path_refresh_msg;
|
|
+ ether_addr_copy(path.mesh_da, refresh_msg->dest_mac_addr);
|
|
+ ether_addr_copy(path.next_hop, refresh_msg->next_hop_mac_addr);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "Mesh path refresh event from nss, mDA %pM next_hop %pM link_vdev %d\n",
|
|
+ refresh_msg->dest_mac_addr, refresh_msg->next_hop_mac_addr,
|
|
+ refresh_msg->link_vap_id);
|
|
+
|
|
+
|
|
+ if (ab->nss.debug_mode)
|
|
+ return;
|
|
+
|
|
+ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path,
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_REFRESH);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failed to notify mpath refresh nss event %d\n", ret);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_mesh_path_not_found(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct nss_wifi_mesh_mpath_not_found_msg *err_msg;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ int ret;
|
|
+
|
|
+ err_msg = &msg->msg.mpath_not_found_msg;
|
|
+ ether_addr_copy(path.da, err_msg->dest_mac_addr);
|
|
+ if (err_msg->is_mesh_forward_path)
|
|
+ ether_addr_copy(path.ta, err_msg->transmitter_mac_addr);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "Mesh path not found event from nss, (m)DA %pM ta %pM link vap %d\n",
|
|
+ err_msg->dest_mac_addr, err_msg->transmitter_mac_addr, err_msg->link_vap_id);
|
|
+
|
|
+
|
|
+ if (ab->nss.debug_mode)
|
|
+ return;
|
|
+
|
|
+ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path,
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_PATH_NOT_FOUND);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failed to notify mpath not found nss event %d\n", ret);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_mesh_path_delete(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct nss_wifi_mesh_mpath_del_msg *del_msg = &msg->msg.mpath_del;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ int ret;
|
|
+
|
|
+ ether_addr_copy(path.mesh_da, del_msg->mesh_dest_mac_addr);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "Mesh path delete event from nss, mDA %pM vap_id %d\n",
|
|
+ del_msg->mesh_dest_mac_addr, del_msg->link_vap_id);
|
|
+
|
|
+ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path,
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_DEL);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failed to notify mpath delete nss event %d\n", ret);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_mesh_path_expiry(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct nss_wifi_mesh_path_expiry_msg *exp_msg = &msg->msg.path_expiry_msg;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ int ret;
|
|
+
|
|
+ ether_addr_copy(path.mesh_da, exp_msg->mesh_dest_mac_addr);
|
|
+ ether_addr_copy(path.next_hop, exp_msg->next_hop_mac_addr);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "Mesh path delete event from nss, mDA %pM next_hop %pM if_num %d\n",
|
|
+ exp_msg->mesh_dest_mac_addr, exp_msg->next_hop_mac_addr,
|
|
+ arvif->nss.if_num);
|
|
+
|
|
+ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path,
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_EXP);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failed to notify mpath expiry nss event %d\n", ret);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_mesh_mpp_learn(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+ {
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct nss_wifi_mesh_proxy_path_learn_msg *learn_msg;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ int ret;
|
|
+
|
|
+ learn_msg = &msg->msg.proxy_learn_msg;
|
|
+
|
|
+ ether_addr_copy(path.mesh_da, learn_msg->mesh_dest_mac);
|
|
+ ether_addr_copy(path.da, learn_msg->dest_mac_addr);
|
|
+ nss_mesh_convert_path_flags(&path.flags, &learn_msg->path_flags, false);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "Mesh proxy learn event from nss, mDA %pM da %pM flags 0x%x if_num %d\n",
|
|
+ learn_msg->mesh_dest_mac, learn_msg->dest_mac_addr,
|
|
+ learn_msg->path_flags, arvif->nss.if_num);
|
|
+
|
|
+ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path,
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_LEARN);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failed to notify proxy learn event %d\n", ret);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_mesh_mpp_add(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct nss_wifi_mesh_proxy_path_add_msg *add_msg = &msg->msg.proxy_add_msg;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ int ret;
|
|
+
|
|
+ ether_addr_copy(path.mesh_da, add_msg->mesh_dest_mac);
|
|
+ ether_addr_copy(path.da, add_msg->dest_mac_addr);
|
|
+ nss_mesh_convert_path_flags(&path.flags, &add_msg->path_flags, false);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "Mesh proxy add event from nss, mDA %pM da %pM flags 0x%x if_num %d\n",
|
|
+ add_msg->mesh_dest_mac, add_msg->dest_mac_addr, add_msg->path_flags,
|
|
+ arvif->nss.if_num);
|
|
+
|
|
+ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path,
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_ADD);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failed to notify proxy add event %d\n", ret);
|
|
+}
|
|
+
|
|
+static void ath11k_nss_mesh_mpp_update(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct nss_wifi_mesh_proxy_path_update_msg *umsg;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ int ret;
|
|
+
|
|
+ umsg = &msg->msg.proxy_update_msg;
|
|
+ ether_addr_copy(path.mesh_da, umsg->mesh_dest_mac);
|
|
+ ether_addr_copy(path.da, umsg->dest_mac_addr);
|
|
+ nss_mesh_convert_path_flags(&path.flags, &umsg->path_flags, false);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "Mesh proxy update event from nss, mDA %pM da %pM flags 0x%x if_num %d\n",
|
|
+ umsg->mesh_dest_mac, umsg->dest_mac_addr, umsg->path_flags, arvif->nss.if_num);
|
|
+
|
|
+ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path,
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_UPDATE);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failed to notify proxy update event %d\n", ret);
|
|
+}
|
|
+
|
|
+static int
|
|
+ath11k_nss_mesh_process_path_table_dump_msg(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+{
|
|
+ struct nss_wifi_mesh_path_table_dump *mpath_dump = &msg->msg.mpath_table_dump;
|
|
+ struct ath11k_nss_mpath_entry *entry;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ ssize_t len;
|
|
+
|
|
+ len = sizeof(struct nss_wifi_mesh_path_dump_entry) * mpath_dump->num_entries;
|
|
+ entry = kzalloc(sizeof(*entry) + len, GFP_ATOMIC);
|
|
+ if (!entry)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memcpy(entry->mpath, mpath_dump->path_entry, len);
|
|
+ entry->num_entries = mpath_dump->num_entries;
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_add_tail(&entry->list, &arvif->nss.mpath_dump);
|
|
+ arvif->nss.mpath_dump_num_entries += mpath_dump->num_entries;
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+
|
|
+ if (!mpath_dump->more_events)
|
|
+ complete(&arvif->nss.dump_mpath_complete);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+ath11k_nss_mesh_process_mpp_table_dump_msg(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+{
|
|
+ struct nss_wifi_mesh_proxy_path_table_dump *mpp_dump;
|
|
+ struct ath11k_nss_mpp_entry *entry, *tmp;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ struct arvif_nss *nss = &arvif->nss;
|
|
+ ssize_t len;
|
|
+ LIST_HEAD(local_entry_exp_update);
|
|
+
|
|
+ mpp_dump = &msg->msg.proxy_path_table_dump;
|
|
+
|
|
+ len = sizeof(struct nss_wifi_mesh_proxy_path_dump_entry) * mpp_dump->num_entries;
|
|
+ entry = kzalloc(sizeof(*entry) + len, GFP_ATOMIC);
|
|
+ if (!entry)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memcpy(entry->mpp, mpp_dump->path_entry, len);
|
|
+ entry->num_entries = mpp_dump->num_entries;
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_add_tail(&entry->list, &arvif->nss.mpp_dump);
|
|
+ arvif->nss.mpp_dump_num_entries += mpp_dump->num_entries;
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+
|
|
+ if (!mpp_dump->more_events) {
|
|
+ if (arvif->nss.mpp_aging) {
|
|
+ arvif->nss.mpp_aging = false;
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_splice_tail_init(&nss->mpp_dump, &local_entry_exp_update);
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+
|
|
+ list_for_each_entry_safe(entry, tmp, &local_entry_exp_update, list) {
|
|
+ if (entry->mpp->time_diff > ATH11K_MPP_EXPIRY_TIMER_INTERVAL_MS)
|
|
+ continue;
|
|
+ mesh_nss_offld_proxy_path_exp_update(arvif->vif,
|
|
+ entry->mpp->dest_mac_addr,
|
|
+ entry->mpp->mesh_dest_mac,
|
|
+ entry->mpp->time_diff);
|
|
+ }
|
|
+ /* If mpp_dump_req is true dont free the entry
|
|
+ * since it will get freed in debug_nss_fill_mpp_dump
|
|
+ * both mpp_aging and mpp_dump_req will be true during
|
|
+ * simultaneous accessing of mpp dump entry. So this will
|
|
+ * gain the reuse of same dump result for both mpp_aging
|
|
+ * and mpp_dump_req */
|
|
+ if (!arvif->nss.mpp_dump_req) {
|
|
+ list_for_each_entry_safe(entry, tmp, &local_entry_exp_update, list)
|
|
+ kfree(entry);
|
|
+ } else {
|
|
+ /* Adding back to global nss dump tbl to reuse the same
|
|
+ * tbl for mpp dump request
|
|
+ */
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_splice_tail_init(&local_entry_exp_update, &nss->mpp_dump);
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (arvif->nss.mpp_dump_req) {
|
|
+ complete(&arvif->nss.dump_mpp_complete);
|
|
+ arvif->nss.mpp_dump_req = false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ath11k_nss_mesh_exception_flags(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_exception_flag_msg *nss_msg)
|
|
+{
|
|
+ nss_wifi_mesh_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive;
|
|
+
|
|
+ status = nss_wifi_meshmgr_mesh_path_exception(arvif->nss.mesh_handle, nss_msg,
|
|
+ msg_cb, arvif->ar->ab);
|
|
+
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(arvif->ar->ab, "failed to set the exception flags\n");
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ath11k_nss_exc_rate_config(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_rate_limit_config *nss_exc_cfg)
|
|
+{
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ status = nss_wifi_meshmgr_config_mesh_exception_sync(arvif->nss.mesh_handle, nss_exc_cfg);
|
|
+
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(arvif->ar->ab, "failed to set the exception rate ctrl\n");
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void ath11k_nss_mesh_obj_vdev_event_receive(struct net_device *dev,
|
|
+ struct nss_wifi_mesh_msg *msg)
|
|
+{
|
|
+ struct ath11k_base *ab;
|
|
+ struct ath11k_vif *arvif;
|
|
+ int ret;
|
|
+
|
|
+ arvif = ath11k_nss_get_arvif_from_dev(dev);
|
|
+ if (!arvif)
|
|
+ return;
|
|
+
|
|
+ ab = arvif->ar->ab;
|
|
+
|
|
+ switch (msg->cm.type) {
|
|
+ case NSS_WIFI_MESH_MSG_PATH_REFRESH:
|
|
+ ath11k_nss_mesh_mpath_refresh(arvif, msg);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PATH_NOT_FOUND:
|
|
+ ath11k_nss_mesh_path_not_found(arvif, msg);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_MPATH_DELETE:
|
|
+ ath11k_nss_mesh_path_delete(arvif, msg);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PATH_EXPIRY:
|
|
+ ath11k_nss_mesh_path_expiry(arvif, msg);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PROXY_PATH_LEARN:
|
|
+ ath11k_nss_mesh_mpp_learn(arvif, msg);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PROXY_PATH_ADD:
|
|
+ ath11k_nss_mesh_mpp_add(arvif, msg);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PROXY_PATH_UPDATE:
|
|
+ ath11k_nss_mesh_mpp_update(arvif, msg);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PATH_TABLE_DUMP:
|
|
+ ret = ath11k_nss_mesh_process_path_table_dump_msg(arvif, msg);
|
|
+ if (ret)
|
|
+ ath11k_warn(arvif->ar->ab, "failed mpath table dump message %d\n",
|
|
+ ret);
|
|
+ break;
|
|
+ case NSS_WIFI_MESH_MSG_PROXY_PATH_TABLE_DUMP:
|
|
+ ret = ath11k_nss_mesh_process_mpp_table_dump_msg(arvif, msg);
|
|
+ if (ret)
|
|
+ ath11k_warn(arvif->ar->ab, "failed mpp table dump message %d\n",
|
|
+ ret);
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "unknown message type on mesh obj vap %d\n",
|
|
+ msg->cm.type);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ath11k_nss_mesh_mpath_add(struct ath11k_vif *arvif,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ nss_wifi_mesh_msg_callback_t msg_cb;
|
|
+ struct nss_wifi_mesh_mpath_add_msg *msg;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "add mpath for mesh_da %pM on radio %d\n",
|
|
+ path->mesh_da, ar->pdev->pdev_id);
|
|
+
|
|
+ msg = kzalloc(sizeof(struct nss_wifi_mesh_mpath_add_msg), GFP_ATOMIC);
|
|
+ if (!msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive;
|
|
+
|
|
+ ether_addr_copy(msg->dest_mac_addr, path->mesh_da);
|
|
+ ether_addr_copy(msg->next_hop_mac_addr, path->next_hop);
|
|
+ msg->hop_count = path->hop_count;
|
|
+ msg->metric = path->metric;
|
|
+ nss_mesh_convert_path_flags(&msg->path_flags, &path->flags, true);
|
|
+ msg->link_vap_id = arvif->nss.if_num;
|
|
+ msg->block_mesh_fwd = path->block_mesh_fwd;
|
|
+ msg->metadata_type = path->metadata_type ? NSS_WIFI_MESH_PRE_HEADER_80211: NSS_WIFI_MESH_PRE_HEADER_NONE;
|
|
+
|
|
+ status = nss_wifi_meshmgr_mesh_path_add(arvif->nss.mesh_handle, msg,
|
|
+ msg_cb, ar->ab);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to add mpath entry mesh_da %pM radio_id %d status %d\n",
|
|
+ path->mesh_da, arvif->nss.if_num, status);
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ kfree(msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_mesh_mpath_update(struct ath11k_vif *arvif,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ nss_wifi_mesh_msg_callback_t msg_cb;
|
|
+ struct nss_wifi_mesh_mpath_update_msg *msg;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH,
|
|
+ "update mpath mesh_da %pM radio %d next_hop %pM old_next_hop %pM "
|
|
+ "metric %d flags 0x%x hop_count %d "
|
|
+ "exp_time %lu mesh_gate %d\n",
|
|
+ path->mesh_da, ar->pdev->pdev_id, path->next_hop, path->old_next_hop,
|
|
+ path->metric, path->flags, path->hop_count, path->exp_time,
|
|
+ path->mesh_gate);
|
|
+
|
|
+ msg = kzalloc(sizeof(struct nss_wifi_mesh_mpath_update_msg), GFP_ATOMIC);
|
|
+ if (!msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive;
|
|
+
|
|
+ ether_addr_copy(msg->dest_mac_addr, path->mesh_da);
|
|
+ ether_addr_copy(msg->next_hop_mac_addr, path->next_hop);
|
|
+ ether_addr_copy(msg->old_next_hop_mac_addr, path->old_next_hop);
|
|
+ msg->hop_count = path->hop_count;
|
|
+ msg->metric = path->metric;
|
|
+ nss_mesh_convert_path_flags(&msg->path_flags, &path->flags, true);
|
|
+ msg->link_vap_id = arvif->nss.if_num;
|
|
+ msg->is_mesh_gate = path->mesh_gate;
|
|
+ msg->expiry_time = path->exp_time;
|
|
+ msg->block_mesh_fwd = path->block_mesh_fwd;
|
|
+ msg->metadata_type = path->metadata_type ? NSS_WIFI_MESH_PRE_HEADER_80211: NSS_WIFI_MESH_PRE_HEADER_NONE;
|
|
+
|
|
+ msg->update_flags = NSS_WIFI_MESH_PATH_UPDATE_FLAG_NEXTHOP |
|
|
+ NSS_WIFI_MESH_PATH_UPDATE_FLAG_HOPCOUNT |
|
|
+ NSS_WIFI_MESH_PATH_UPDATE_FLAG_METRIC |
|
|
+ NSS_WIFI_MESH_PATH_UPDATE_FLAG_MESH_FLAGS |
|
|
+ NSS_WIFI_MESH_PATH_UPDATE_FLAG_BLOCK_MESH_FWD |
|
|
+ NSS_WIFI_MESH_PATH_UPDATE_FLAG_METADATA_ENABLE_VALID;
|
|
+
|
|
+ status = nss_wifi_meshmgr_mesh_path_update(arvif->nss.mesh_handle, msg,
|
|
+ msg_cb, ar->ab);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to update mpath entry mesh_da %pM radio_id %d status %d\n",
|
|
+ path->mesh_da, arvif->nss.if_num, status);
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ kfree(msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_mesh_mpath_del(struct ath11k_vif *arvif,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ nss_wifi_mesh_msg_callback_t msg_cb;
|
|
+ struct nss_wifi_mesh_mpath_del_msg *msg;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "del mpath for mesh_da %pM on radio %d\n",
|
|
+ path->mesh_da, ar->pdev->pdev_id);
|
|
+
|
|
+ msg = kzalloc(sizeof(struct nss_wifi_mesh_mpath_del_msg), GFP_ATOMIC);
|
|
+ if (!msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive;
|
|
+
|
|
+ ether_addr_copy(msg->mesh_dest_mac_addr, path->mesh_da);
|
|
+ ether_addr_copy(msg->next_hop_mac_addr, path->next_hop);
|
|
+ msg->link_vap_id = arvif->nss.if_num;
|
|
+
|
|
+ status = nss_wifi_meshmgr_mesh_path_delete(arvif->nss.mesh_handle,
|
|
+ msg, msg_cb, ar->ab);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to del mpath entry mesh_da %pM radio_id %d status %d\n",
|
|
+ path->mesh_da, arvif->nss.if_num, status);
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ kfree(msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_mesh_mpp_add_cmd(struct ath11k_vif *arvif,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ nss_wifi_mesh_msg_callback_t msg_cb;
|
|
+ struct nss_wifi_mesh_proxy_path_add_msg *msg;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "add mpp mesh_da %pM da %pM\n",
|
|
+ path->mesh_da, path->da);
|
|
+
|
|
+ msg = kzalloc(sizeof(struct nss_wifi_mesh_proxy_path_add_msg), GFP_ATOMIC);
|
|
+ if (!msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive;
|
|
+
|
|
+ ether_addr_copy(msg->dest_mac_addr, path->da);
|
|
+ ether_addr_copy(msg->mesh_dest_mac, path->mesh_da);
|
|
+ nss_mesh_convert_path_flags(&msg->path_flags, &path->flags, true);
|
|
+
|
|
+ status = nss_wifi_meshmgr_mesh_proxy_path_add(arvif->nss.mesh_handle,
|
|
+ msg, msg_cb, ar->ab);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to add mpp entry da %pM mesh_da %pM status %d\n",
|
|
+ path->da, path->mesh_da, status);
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ kfree(msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_mesh_mpp_update_cmd(struct ath11k_vif *arvif,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ nss_wifi_mesh_msg_callback_t msg_cb;
|
|
+ struct nss_wifi_mesh_proxy_path_update_msg *msg;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "update mpp da %pM mesh_da %pM on vap_id %d\n",
|
|
+ path->da, path->mesh_da, arvif->nss.if_num);
|
|
+
|
|
+ msg = kzalloc(sizeof(struct nss_wifi_mesh_proxy_path_update_msg), GFP_ATOMIC);
|
|
+ if (!msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive;
|
|
+
|
|
+ ether_addr_copy(msg->dest_mac_addr, path->da);
|
|
+ ether_addr_copy(msg->mesh_dest_mac, path->mesh_da);
|
|
+ nss_mesh_convert_path_flags(&msg->path_flags, &path->flags, true);
|
|
+ msg->bitmap = NSS_WIFI_MESH_PATH_UPDATE_FLAG_NEXTHOP |
|
|
+ NSS_WIFI_MESH_PATH_UPDATE_FLAG_HOPCOUNT;
|
|
+
|
|
+ status = nss_wifi_meshmgr_mesh_proxy_path_update(arvif->nss.mesh_handle,
|
|
+ msg, msg_cb, ar->ab);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to update mpp da %pM mesh_da %pM status %d\n",
|
|
+ path->da, path->mesh_da, status);
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ kfree(msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_mesh_mpp_del_cmd(struct ath11k_vif *arvif,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ nss_wifi_mesh_msg_callback_t msg_cb;
|
|
+ struct nss_wifi_mesh_proxy_path_del_msg *msg;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "del mpath for mesh_da %pM\n",
|
|
+ path->mesh_da);
|
|
+
|
|
+ msg = kzalloc(sizeof(struct nss_wifi_mesh_proxy_path_del_msg), GFP_ATOMIC);
|
|
+ if (!msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive;
|
|
+
|
|
+ ether_addr_copy(msg->dest_mac_addr, path->da);
|
|
+ ether_addr_copy(msg->mesh_dest_mac_addr, path->mesh_da);
|
|
+
|
|
+ status = nss_wifi_meshmgr_mesh_proxy_path_delete(arvif->nss.mesh_handle, msg,
|
|
+ msg_cb, ar->ab);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to add mpath entry mesh_da %pM status %d\n",
|
|
+ path->mesh_da, status);
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ kfree(msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ath11k_nss_mesh_config_path(struct ath11k *ar, struct ath11k_vif *arvif,
|
|
+ enum ieee80211_mesh_path_offld_cmd cmd,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+
|
|
+ if (!ar->ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ switch (cmd) {
|
|
+ case IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPATH:
|
|
+ ret = ath11k_nss_mesh_mpath_add(arvif, path);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPATH:
|
|
+ ret = ath11k_nss_mesh_mpath_update(arvif, path);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPATH:
|
|
+ ret = ath11k_nss_mesh_mpath_del(arvif, path);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPP:
|
|
+ ret = ath11k_nss_mesh_mpp_add_cmd(arvif, path);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPP:
|
|
+ ret = ath11k_nss_mesh_mpp_update_cmd(arvif, path);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPP:
|
|
+ ret = ath11k_nss_mesh_mpp_del_cmd(arvif, path);
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_warn(ar->ab, "unknown mesh path table command type %d\n", cmd);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ath11k_nss_mesh_config_update(struct ieee80211_vif *vif, int changed)
|
|
+{
|
|
+ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct nss_wifi_mesh_config_msg *nss_msg;
|
|
+ struct arvif_nss *nss = &arvif->nss;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (!ab->nss.enabled)
|
|
+ return 0;
|
|
+
|
|
+ if (!ab->nss.mesh_nss_offload_enabled)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ if (!changed)
|
|
+ return 0;
|
|
+
|
|
+ nss_msg = kzalloc(sizeof(*nss_msg), GFP_KERNEL);
|
|
+ if (!nss_msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (changed & BSS_CHANGED_NSS_MESH_TTL) {
|
|
+ nss_msg->ttl = vif->bss_conf.nss_offld_ttl;
|
|
+ nss->mesh_ttl = vif->bss_conf.nss_offld_ttl;
|
|
+ nss_msg->config_flags |= NSS_WIFI_MESH_CONFIG_FLAG_TTL_VALID;
|
|
+ }
|
|
+
|
|
+ if (changed & BSS_CHANGED_NSS_MESH_REFRESH_TIME) {
|
|
+ nss_msg->mesh_path_refresh_time =
|
|
+ vif->bss_conf.nss_offld_mpath_refresh_time;
|
|
+ nss->mpath_refresh_time =
|
|
+ vif->bss_conf.nss_offld_mpath_refresh_time;
|
|
+ nss_msg->config_flags |= NSS_WIFI_MESH_CONFIG_FLAG_MPATH_REFRESH_VALID;
|
|
+ }
|
|
+
|
|
+ if (changed & BSS_CHANGED_NSS_MESH_FWD_ENABLED) {
|
|
+ nss_msg->block_mesh_forwarding =
|
|
+ vif->bss_conf.nss_offld_mesh_forward_enabled;
|
|
+ nss->mesh_forward_enabled =
|
|
+ vif->bss_conf.nss_offld_mesh_forward_enabled;
|
|
+ nss_msg->config_flags |= NSS_WIFI_MESH_CONFIG_FLAG_BLOCK_MESH_FWD_VALID;
|
|
+ nss_msg->metadata_type = arvif->nss.metadata_type;
|
|
+ nss_msg->config_flags |= NSS_WIFI_MESH_CONFIG_FLAG_METADATA_ENABLE_VALID;
|
|
+ }
|
|
+
|
|
+ status = nss_wifi_meshmgr_mesh_config_update_sync(arvif->nss.mesh_handle,
|
|
+ nss_msg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "failed to configure nss mesh obj vdev nss_err:%d\n",
|
|
+ status);
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ kfree(nss_msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ath11k_nss_dump_mpath_request(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ struct arvif_nss *nss = &arvif->nss;
|
|
+ struct ath11k_nss_mpath_entry *entry, *tmp;
|
|
+ LIST_HEAD(local_entry);
|
|
+ nss_tx_status_t status;
|
|
+
|
|
+ /* Clean up any stale entries from old events */
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_splice_tail(&nss->mpath_dump, &local_entry);
|
|
+ arvif->nss.mpath_dump_num_entries = 0;
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+
|
|
+ list_for_each_entry_safe(entry, tmp, &local_entry, list)
|
|
+ kfree(entry);
|
|
+
|
|
+ status = nss_wifi_meshmgr_dump_mesh_path_sync(arvif->nss.mesh_handle);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "failed to send mpath dump command on mesh obj vdev nss_err:%d\n",
|
|
+ status);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ath11k_nss_dump_mpp_request(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ struct arvif_nss *nss = &arvif->nss;
|
|
+ struct ath11k_nss_mpp_entry *entry, *tmp;
|
|
+ LIST_HEAD(local_entry);
|
|
+ nss_wifi_meshmgr_status_t status;
|
|
+
|
|
+ if (!arvif->nss.mpp_aging) {
|
|
+ /* Clean up any stale entries from old events */
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_splice_tail_init(&nss->mpp_dump, &local_entry);
|
|
+ arvif->nss.mpp_dump_num_entries = 0;
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+
|
|
+ list_for_each_entry_safe(entry, tmp, &local_entry, list) {
|
|
+ list_del(&entry->list);
|
|
+ kfree(entry);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ arvif->nss.mpp_dump_req = true;
|
|
+
|
|
+ status = nss_wifi_meshmgr_dump_mesh_proxy_path_sync(arvif->nss.mesh_handle);
|
|
+ if (status != NSS_WIFI_MESHMGR_SUCCESS) {
|
|
+ if (status == NSS_WIFI_MESHMGR_FAILURE_ONESHOT_ALREADY_ATTACHED)
|
|
+ return 0;
|
|
+ ath11k_warn(ab, "failed to send mpp dump command on mesh obj vdev nss_err:%d\n",
|
|
+ status);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ath11k_nss_mpp_timer_cb(struct timer_list *timer)
|
|
+{
|
|
+ nss_wifi_mesh_msg_callback_t msg_cb;
|
|
+ struct arvif_nss *nss = from_timer(nss, timer,mpp_expiry_timer);
|
|
+ struct ath11k_vif *arvif = container_of(nss, struct ath11k_vif, nss);
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ LIST_HEAD(local_entry);
|
|
+ nss_tx_status_t status;
|
|
+
|
|
+ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive;
|
|
+
|
|
+ if (!arvif->nss.mpp_dump_req)
|
|
+ arvif->nss.mpp_dump_num_entries = 0;
|
|
+ arvif->nss.mpp_aging = true;
|
|
+
|
|
+ status = nss_wifi_meshmgr_dump_mesh_proxy_path(arvif->nss.mesh_handle, msg_cb, ab);
|
|
+ if (status != NSS_TX_SUCCESS)
|
|
+ ath11k_warn(ab, "failed to send mpp dump command from timer nss_err:%d\n",
|
|
+ status);
|
|
+
|
|
+ mod_timer(&nss->mpp_expiry_timer,
|
|
+ jiffies + msecs_to_jiffies(ATH11K_MPP_EXPIRY_TIMER_INTERVAL_MS));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+ath11k_nss_mesh_obj_vdev_data_receive(struct net_device *dev, struct sk_buff *skb,
|
|
+ struct napi_struct *napi)
|
|
+{
|
|
+ struct ath11k_vif *arvif;
|
|
+ struct ath11k_base *ab;
|
|
+ char dump_msg[100] = {0};
|
|
+ struct nss_wifi_mesh_per_packet_metadata *wifi_metadata = NULL;
|
|
+
|
|
+ arvif = ath11k_nss_get_arvif_from_dev(dev);
|
|
+ if (!arvif) {
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ab = arvif->ar->ab;
|
|
+
|
|
+ skb->dev = dev;
|
|
+
|
|
+ snprintf(dump_msg, sizeof(dump_msg), "nss mesh obj vdev: link id %d ",
|
|
+ arvif->nss.if_num);
|
|
+
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "dp rx msdu from nss", dump_msg,
|
|
+ skb->data, skb->len);
|
|
+
|
|
+ if (arvif->nss.metadata_type == NSS_WIFI_MESH_PRE_HEADER_80211) {
|
|
+ wifi_metadata = (struct nss_wifi_mesh_per_packet_metadata *)(skb->data -
|
|
+ (sizeof(struct nss_wifi_mesh_per_packet_metadata)));
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH,
|
|
+ "exception from nss on mesh obj vap: pkt_type %d\n",
|
|
+ wifi_metadata->pkt_type);
|
|
+ switch (wifi_metadata->pkt_type) {
|
|
+ case NSS_WIFI_MESH_PRE_HEADER_80211:
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "",
|
|
+ "wifi header from nss on mesh obj vdev: ",
|
|
+ skb->data - sizeof(*wifi_metadata), sizeof(*wifi_metadata) + skb->len);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+ default:
|
|
+ dev_kfree_skb_any(skb);
|
|
+ }
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ath11k_nss_deliver_rx(arvif->vif, skb, true, 0, napi);
|
|
+}
|
|
+
|
|
+static void
|
|
+ath11k_nss_mesh_obj_ext_data_callback(struct net_device *dev, struct sk_buff *skb,
|
|
+ __attribute__((unused)) struct napi_struct *napi)
|
|
+{
|
|
+ struct ath11k_vif *arvif;
|
|
+ struct ath11k_base *ab;
|
|
+ struct nss_wifi_mesh_encap_ext_pkt_metadata *wifi_metadata = NULL;
|
|
+ int metadata_len;
|
|
+
|
|
+ arvif = ath11k_nss_get_arvif_from_dev(dev);
|
|
+ if (!arvif) {
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ab = arvif->ar->ab;
|
|
+
|
|
+ skb->dev = dev;
|
|
+
|
|
+ metadata_len = NSS_WIFI_MESH_ENCAP_METADATA_OFFSET_TYPE +
|
|
+ sizeof(struct nss_wifi_mesh_encap_ext_pkt_metadata);
|
|
+
|
|
+ /* msdu from nss should contain metadata in headroom
|
|
+ * any msdu which has invalid or not contains metadata
|
|
+ * will be treated as invalid msdu and dropping it.
|
|
+ */
|
|
+ if (!(metadata_len < skb_headroom(skb))) {
|
|
+ ath11k_warn(ab, "msdu from nss is having invalid headroom %d\n", skb_headroom(skb));
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dma_unmap_single(ab->dev, virt_to_phys(skb->head),
|
|
+ metadata_len,
|
|
+ DMA_FROM_DEVICE);
|
|
+
|
|
+ wifi_metadata = (struct nss_wifi_mesh_encap_ext_pkt_metadata *)(skb->head +
|
|
+ NSS_WIFI_MESH_ENCAP_METADATA_OFFSET_TYPE);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "msdu from nss ext_data _cb on mesh obj vdev");
|
|
+
|
|
+ switch (wifi_metadata->pkt_type) {
|
|
+ case NSS_WIFI_MESH_ENCAP_EXT_DATA_PKT_TYPE_MPATH_NOT_FOUND_EXC:
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "msdu from nss ext_data for mpath not found : ",
|
|
+ skb->data, skb->len);
|
|
+ skb->protocol = eth_type_trans(skb, dev);
|
|
+ skb_reset_network_header(skb);
|
|
+ dev_queue_xmit(skb);
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_warn(ab, "unknown packet type received in mesh obj ext data %d",
|
|
+ wifi_metadata->pkt_type);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+ath11k_nss_mesh_link_vdev_data_receive(void *dev,
|
|
+ struct sk_buff *skb,
|
|
+ struct napi_struct *napi)
|
|
+{
|
|
+ struct ieee80211_vif *vif;
|
|
+ struct ath11k_vif *arvif;
|
|
+ struct ath11k_base *ab;
|
|
+ struct wireless_dev *wdev = (struct wireless_dev *)dev;
|
|
+
|
|
+ vif = wdev_to_ieee80211_vif(wdev);
|
|
+ if (!vif) {
|
|
dev_kfree_skb_any(skb);
|
|
return;
|
|
}
|
|
|
|
- wdev = dev->ieee80211_ptr;
|
|
- if (!wdev) {
|
|
+ arvif = (struct ath11k_vif *)vif->drv_priv;
|
|
+ if (!arvif) {
|
|
dev_kfree_skb_any(skb);
|
|
return;
|
|
}
|
|
|
|
+ ab = arvif->ar->ab;
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "msdu from nss data_receive_cb on mesh link vdev: ",
|
|
+ skb->data, skb->len);
|
|
+ /* data callback for mesh link vap is not expected */
|
|
+ dev_kfree_skb_any(skb);
|
|
+}
|
|
+
|
|
+static void
|
|
+ath11k_nss_mesh_link_vdev_special_data_receive(void *dev,
|
|
+ struct sk_buff *skb,
|
|
+ __attribute__((unused)) struct napi_struct *napi)
|
|
+{
|
|
+ struct ieee80211_vif *vif;
|
|
+ struct ath11k_base *ab;
|
|
+ struct nss_wifi_vdev_per_packet_metadata *wifi_metadata = NULL;
|
|
+ struct ath11k_skb_rxcb *rxcb;
|
|
+ struct ath11k_vif *arvif;
|
|
+ struct wireless_dev *wdev = (struct wireless_dev *)dev;
|
|
+
|
|
vif = wdev_to_ieee80211_vif(wdev);
|
|
if (!vif) {
|
|
dev_kfree_skb_any(skb);
|
|
@@ -900,21 +1974,46 @@ ath11k_nss_ext_vdev_data_receive(struct
|
|
|
|
ab = arvif->ar->ab;
|
|
|
|
- skb->dev = dev;
|
|
+ wifi_metadata = (struct nss_wifi_vdev_per_packet_metadata *)(skb->head +
|
|
+ NSS_WIFI_VDEV_PER_PACKET_METADATA_OFFSET);
|
|
|
|
- /* log the original skb received from nss */
|
|
- ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "dp rx msdu from nss ext : ",
|
|
- skb->data, skb->len);
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH,
|
|
+ "dp special data from nss on mesh link vap: pkt_type %d\n",
|
|
+ wifi_metadata->pkt_type);
|
|
|
|
- ret = ath11k_nss_undecap(arvif, skb, &data_offs, ð_decap);
|
|
- if (ret) {
|
|
- ath11k_warn(ab, "error in nss ext rx undecap, type %d err %d\n",
|
|
- arvif->nss.decap, ret);
|
|
+ switch (wifi_metadata->pkt_type) {
|
|
+ case NSS_WIFI_VDEV_MESH_EXT_DATA_PKT_TYPE_RX_SPL_PACKET:
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "",
|
|
+ "special packet meta data from nss on mesh link vdev: ",
|
|
+ wifi_metadata,
|
|
+ sizeof(struct nss_wifi_vdev_per_packet_metadata));
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "",
|
|
+ "special packet payload from nss on mesh link vdev: ",
|
|
+ skb->data, skb->len);
|
|
dev_kfree_skb_any(skb);
|
|
- return;
|
|
+ break;
|
|
+ case NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_MCBC_RX:
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "",
|
|
+ "mcast packet exception from nss on mesh link vdev: ",
|
|
+ skb->data, skb->len);
|
|
+ rxcb = ATH11K_SKB_RXCB(skb);
|
|
+ rxcb->rx_desc = (struct hal_rx_desc *)skb->head;
|
|
+ rxcb->is_first_msdu = rxcb->is_last_msdu = true;
|
|
+ rxcb->is_continuation = false;
|
|
+ ath11k_dp_rx_from_nss(arvif->ar, skb, napi);
|
|
+ break;
|
|
+ case NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_MESH:
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "",
|
|
+ "static exception path from nss on mesh link vdev: ",
|
|
+ skb->data, skb->len);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+ default:
|
|
+ ath11k_warn(ab, "unknown packet type received in mesh link vdev %d",
|
|
+ wifi_metadata->pkt_type);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
}
|
|
-
|
|
- ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi);
|
|
}
|
|
|
|
int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb)
|
|
@@ -923,8 +2022,9 @@ int ath11k_nss_tx(struct ath11k_vif *arv
|
|
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;
|
|
+ char dump_msg[100] = {0};
|
|
|
|
- if(encap_type != arvif->nss.encap) {
|
|
+ if (!arvif->ar->ab->nss.debug_mode && 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;
|
|
@@ -939,16 +2039,41 @@ int ath11k_nss_tx(struct ath11k_vif *arv
|
|
ath11k_nss_tx_encap_nwifi(skb);
|
|
|
|
send:
|
|
- ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX,
|
|
- arvif->vif->type == NL80211_IFTYPE_AP_VLAN ? "ext vdev" : "",
|
|
- "nss tx msdu: ", skb->data, skb->len);
|
|
-
|
|
- if (arvif->vif->type == NL80211_IFTYPE_AP_VLAN)
|
|
+ if (arvif->vif->type == NL80211_IFTYPE_AP_VLAN) {
|
|
+ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "ext vdev",
|
|
+ "nss tx msdu: ", skb->data, skb->len);
|
|
status = nss_wifi_ext_vdev_tx_buf(arvif->nss.ctx, skb,
|
|
arvif->nss.if_num);
|
|
- else
|
|
- status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb,
|
|
- arvif->nss.if_num);
|
|
+ } else {
|
|
+ if (arvif->ar->ab->nss.debug_mode) {
|
|
+ if (encap_type == HAL_TCL_ENCAP_TYPE_ETHERNET &&
|
|
+ !is_multicast_ether_addr(skb->data)) {
|
|
+ snprintf(dump_msg, sizeof(dump_msg),
|
|
+ "nss tx ucast msdu: %d ",
|
|
+ arvif->nss.mesh_handle);
|
|
+ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "mesh",
|
|
+ dump_msg, skb->data, skb->len);
|
|
+ status = nss_wifi_meshmgr_tx_buf(arvif->nss.mesh_handle,
|
|
+ skb);
|
|
+ } else {
|
|
+ snprintf(dump_msg, sizeof(dump_msg),
|
|
+ "nss tx mcast msdu: %d ",
|
|
+ arvif->nss.if_num);
|
|
+ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "mesh",
|
|
+ dump_msg, skb->data, skb->len);
|
|
+ status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb,
|
|
+ arvif->nss.if_num);
|
|
+ }
|
|
+ } else {
|
|
+ snprintf(dump_msg, sizeof(dump_msg),
|
|
+ "nss tx msdu: %d ",
|
|
+ arvif->nss.if_num);
|
|
+ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "",
|
|
+ dump_msg, 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 | ATH11K_DBG_DP_TX),
|
|
@@ -1035,6 +2160,9 @@ static int ath11k_nss_vdev_configure(str
|
|
|
|
vdev_cfg = &vdev_msg->msg.vdev_config;
|
|
|
|
+ if (arvif->vif->type == NL80211_IFTYPE_MESH_POINT)
|
|
+ vdev_cfg->vap_ext_mode = WIFI_VDEV_EXT_MODE_MESH_LINK;
|
|
+
|
|
vdev_cfg->radio_ifnum = ar->nss.if_num;
|
|
vdev_cfg->vdev_id = arvif->vdev_id;
|
|
|
|
@@ -1075,6 +2203,37 @@ free:
|
|
return ret;
|
|
}
|
|
|
|
+static int ath11k_nss_mesh_obj_assoc_link_vap(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct nss_wifi_mesh_assoc_link_vap *msg;
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ nss_tx_status_t status;
|
|
+ int ret;
|
|
+
|
|
+ msg = kzalloc(sizeof(struct nss_wifi_mesh_assoc_link_vap), GFP_ATOMIC);
|
|
+ if (!msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ msg->link_vap_id = arvif->nss.if_num;
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "nss mesh assoc link vap %d, mesh handle %d\n",
|
|
+ arvif->nss.if_num, arvif->nss.mesh_handle);
|
|
+
|
|
+ status = nss_wifi_meshmgr_assoc_link_vap_sync(arvif->nss.mesh_handle, msg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "failed mesh obj vdev tx msg for assoc link vap nss_err:%d\n",
|
|
+ status);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+free:
|
|
+ kfree(msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static void ath11k_nss_vdev_unregister(struct ath11k_vif *arvif)
|
|
{
|
|
struct ath11k_base *ab = arvif->ar->ab;
|
|
@@ -1086,6 +2245,12 @@ static void ath11k_nss_vdev_unregister(s
|
|
ath11k_dbg(ab, ATH11K_DBG_NSS, "unregistered nss vdev %d \n",
|
|
arvif->nss.if_num);
|
|
break;
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
+ nss_unregister_wifi_vdev_if(arvif->nss.if_num);
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "unregistered nss mesh vdevs mesh link %d\n",
|
|
+ arvif->nss.if_num);
|
|
+ break;
|
|
default:
|
|
ath11k_warn(ab, "unsupported interface type %d for nss vdev unregister\n",
|
|
arvif->vif->type);
|
|
@@ -1093,6 +2258,76 @@ static void ath11k_nss_vdev_unregister(s
|
|
}
|
|
}
|
|
|
|
+static int ath11k_nss_mesh_alloc_register(struct ath11k_vif *arvif,
|
|
+ struct net_device *netdev)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct nss_wifi_mesh_config_msg *nss_msg;
|
|
+ struct arvif_nss *nss = &arvif->nss;
|
|
+ int ret = 0;
|
|
+
|
|
+ nss->mesh_ttl = ATH11K_MESH_DEFAULT_ELEMENT_TTL;
|
|
+ nss->mpath_refresh_time = 1000; /* msecs */
|
|
+ nss->mesh_forward_enabled = true;
|
|
+
|
|
+ nss_msg = kzalloc(sizeof(*nss_msg), GFP_KERNEL);
|
|
+ if (!nss_msg)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ nss_msg->ttl = nss->mesh_ttl;
|
|
+ nss_msg->mesh_path_refresh_time = nss->mpath_refresh_time;
|
|
+ nss_msg->mpp_learning_mode = mpp_mode;
|
|
+ nss_msg->block_mesh_forwarding = 0;
|
|
+ ether_addr_copy(nss_msg->local_mac_addr, arvif->vif->addr);
|
|
+ nss_msg->config_flags =
|
|
+ NSS_WIFI_MESH_CONFIG_FLAG_TTL_VALID |
|
|
+ NSS_WIFI_MESH_CONFIG_FLAG_MPATH_REFRESH_VALID |
|
|
+ NSS_WIFI_MESH_CONFIG_FLAG_MPP_LEARNING_MODE_VALID |
|
|
+ NSS_WIFI_MESH_CONFIG_FLAG_BLOCK_MESH_FWD_VALID |
|
|
+ NSS_WIFI_MESH_CONFIG_FLAG_LOCAL_MAC_VALID;
|
|
+
|
|
+ arvif->nss.mesh_handle = nss_wifi_meshmgr_if_create_sync(netdev, nss_msg,
|
|
+ ath11k_nss_mesh_obj_vdev_data_receive,
|
|
+ ath11k_nss_mesh_obj_ext_data_callback,
|
|
+ ath11k_nss_mesh_obj_vdev_event_receive);
|
|
+ if (arvif->nss.mesh_handle == NSS_WIFI_MESH_HANDLE_INVALID) {
|
|
+ ath11k_warn(ab, "failed to create meshmgr\n");
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+ kfree(nss_msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_mesh_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;
|
|
+
|
|
+ status = nss_register_wifi_vdev_if(ar->nss.ctx,
|
|
+ arvif->nss.if_num,
|
|
+ ath11k_nss_mesh_link_vdev_data_receive,
|
|
+ ath11k_nss_mesh_link_vdev_special_data_receive,
|
|
+ ath11k_nss_vdev_event_receive,
|
|
+ (struct net_device *)netdev->ieee80211_ptr,
|
|
+ features);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "failed to register nss mesh link vdev if_num %d nss_err:%d\n",
|
|
+ arvif->nss.if_num, status);
|
|
+ nss_unregister_wifi_vdev_if(arvif->nss.if_num);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS, "registered nss mesh link vdev if_num %d\n",
|
|
+ arvif->nss.if_num);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int ath11k_nss_vdev_register(struct ath11k_vif *arvif,
|
|
struct net_device *netdev)
|
|
{
|
|
@@ -1120,6 +2355,13 @@ static int ath11k_nss_vdev_register(stru
|
|
arvif->nss.if_num);
|
|
|
|
break;
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
+ if (!ab->nss.mesh_nss_offload_enabled)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ if (ath11k_nss_mesh_vdev_register(arvif, netdev))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
default:
|
|
ath11k_warn(ab, "unsupported interface type %d for nss vdev register\n",
|
|
arvif->vif->type);
|
|
@@ -1129,6 +2371,60 @@ static int ath11k_nss_vdev_register(stru
|
|
return 0;
|
|
}
|
|
|
|
+static void ath11k_nss_mesh_vdev_free(struct ath11k_vif *arvif)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ struct ath11k_nss_mpath_entry *mpath_entry, *mpath_tmp;
|
|
+ struct ath11k_nss_mpp_entry *mpp_entry, *mpp_tmp;
|
|
+ struct arvif_nss *nss = &arvif->nss, *nss_entry, *nss_tmp;
|
|
+ LIST_HEAD(mpath_local_entry);
|
|
+ LIST_HEAD(mpp_local_entry);
|
|
+ nss_tx_status_t status;
|
|
+
|
|
+ del_timer_sync(&nss->mpp_expiry_timer);
|
|
+
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_splice_tail_init(&nss->mpath_dump, &mpath_local_entry);
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+
|
|
+ list_for_each_entry_safe(mpath_entry, mpath_tmp, &mpath_local_entry, list) {
|
|
+ list_del(&mpath_entry->list);
|
|
+ kfree(mpath_entry);
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&ar->nss.dump_lock);
|
|
+ list_splice_tail_init(&nss->mpp_dump, &mpp_local_entry);
|
|
+ spin_unlock_bh(&ar->nss.dump_lock);
|
|
+
|
|
+ list_for_each_entry_safe(mpp_entry, mpp_tmp, &mpp_local_entry, list) {
|
|
+ list_del(&mpp_entry->list);
|
|
+ kfree(mpp_entry);
|
|
+ }
|
|
+
|
|
+ list_for_each_entry_safe(nss_entry, nss_tmp, &mesh_vaps, list)
|
|
+ list_del(&nss_entry->list);
|
|
+
|
|
+ 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 mesh link vdev nss_err:%d\n",
|
|
+ status);
|
|
+ else
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "nss mesh link vdev interface deallocated\n");
|
|
+
|
|
+ status = nss_wifi_meshmgr_if_destroy_sync(arvif->nss.mesh_handle);
|
|
+
|
|
+ if (status != NSS_TX_SUCCESS)
|
|
+ ath11k_warn(ab, "failed to free nss mesh object vdev nss_err:%d\n",
|
|
+ status);
|
|
+ else
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS,
|
|
+ "nss mesh object vdev interface deallocated\n");
|
|
+}
|
|
+
|
|
void ath11k_nss_vdev_free(struct ath11k_vif *arvif)
|
|
{
|
|
struct ath11k_base *ab = arvif->ar->ab;
|
|
@@ -1148,6 +2444,9 @@ void ath11k_nss_vdev_free(struct ath11k_
|
|
"nss vdev interface deallocated\n");
|
|
|
|
return;
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
+ ath11k_nss_mesh_vdev_free(arvif);
|
|
+ return;
|
|
default:
|
|
ath11k_warn(ab, "unsupported interface type %d for nss vdev dealloc\n",
|
|
arvif->vif->type);
|
|
@@ -1155,11 +2454,92 @@ void ath11k_nss_vdev_free(struct ath11k_
|
|
}
|
|
}
|
|
|
|
-static int ath11k_nss_vdev_alloc(struct ath11k_vif *arvif)
|
|
+struct arvif_nss *ath11k_nss_find_arvif_by_if_num(int if_num)
|
|
+{
|
|
+ struct arvif_nss *nss;
|
|
+
|
|
+ list_for_each_entry(nss, &mesh_vaps, list) {
|
|
+ if (if_num == nss->if_num)
|
|
+ return nss;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+int ath11k_nss_assoc_link_arvif_to_ifnum(struct ath11k_vif *arvif, int if_num)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ struct ath11k_vif *arvif_link;
|
|
+ struct wireless_dev *wdev;
|
|
+ struct arvif_nss *nss;
|
|
+ int ret;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ nss = ath11k_nss_find_arvif_by_if_num(if_num);
|
|
+ if (!nss) {
|
|
+ ath11k_warn(ab, "ath11k_nss: unable to find if_num %d\n",if_num);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ arvif_link = container_of(nss, struct ath11k_vif, nss);
|
|
+
|
|
+ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH,
|
|
+ "assoc link vap ifnum %d to mesh handle of link id %d\n",
|
|
+ arvif_link->nss.if_num, arvif->nss.if_num);
|
|
+
|
|
+ arvif_link->nss.mesh_handle = arvif->nss.mesh_handle;
|
|
+
|
|
+ ret = ath11k_nss_mesh_obj_assoc_link_vap(arvif_link);
|
|
+ if (ret)
|
|
+ ath11k_warn(ab, "failed to associate link vap to mesh vap %d\n", ret);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_mesh_vdev_alloc(struct ath11k_vif *arvif,
|
|
+ struct net_device *netdev)
|
|
+{
|
|
+ struct ath11k_base *ab = arvif->ar->ab;
|
|
+ int if_num;
|
|
+
|
|
+ if (!ab->nss.mesh_nss_offload_enabled)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ if_num = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_VAP);
|
|
+ if (if_num < 0) {
|
|
+ ath11k_warn(ab, "failed to allocate nss mesh link vdev\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ arvif->nss.if_num = if_num;
|
|
+
|
|
+ INIT_LIST_HEAD(&arvif->nss.list);
|
|
+ list_add_tail(&arvif->nss.list, &mesh_vaps);
|
|
+
|
|
+ INIT_LIST_HEAD(&arvif->nss.mpath_dump);
|
|
+ init_completion(&arvif->nss.dump_mpath_complete);
|
|
+ INIT_LIST_HEAD(&arvif->nss.mpp_dump);
|
|
+ init_completion(&arvif->nss.dump_mpp_complete);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ath11k_nss_vdev_alloc(struct ath11k_vif *arvif,
|
|
+ struct net_device *netdev)
|
|
{
|
|
struct ath11k_base *ab = arvif->ar->ab;
|
|
enum nss_dynamic_interface_type if_type;
|
|
int if_num;
|
|
+ int ret;
|
|
|
|
/* Initialize completion for verifying NSS message response */
|
|
init_completion(&arvif->nss.complete);
|
|
@@ -1181,6 +2561,14 @@ static int ath11k_nss_vdev_alloc(struct
|
|
arvif->nss.if_num);
|
|
|
|
break;
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
+ ret = ath11k_nss_mesh_vdev_alloc(arvif, netdev);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "failed to allocate nss vdev of mesh type %d\n",
|
|
+ ret);
|
|
+ return ret;
|
|
+ }
|
|
+ break;
|
|
default:
|
|
ath11k_warn(ab, "unsupported interface type %d for nss vdev alloc\n",
|
|
arvif->vif->type);
|
|
@@ -1218,7 +2606,7 @@ int ath11k_nss_vdev_create(struct ath11k
|
|
return -EINVAL;
|
|
}
|
|
|
|
- ret = ath11k_nss_vdev_alloc(arvif);
|
|
+ ret = ath11k_nss_vdev_alloc(arvif, wdev->netdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
@@ -1234,6 +2622,43 @@ int ath11k_nss_vdev_create(struct ath11k
|
|
goto unregister_vdev;
|
|
|
|
break;
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
+ ret = ath11k_nss_mesh_alloc_register(arvif, wdev->netdev);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "failed to alloc and register mesh vap %d\n", ret);
|
|
+ goto unregister_vdev;
|
|
+ }
|
|
+
|
|
+ ret = ath11k_nss_vdev_configure(arvif);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "failed to configure nss mesh link vdev\n");
|
|
+ goto unregister_vdev;
|
|
+ }
|
|
+
|
|
+ ret = ath11k_nss_mesh_obj_assoc_link_vap(arvif);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "failed to associate link vap to mesh vap %d\n", ret);
|
|
+ goto unregister_vdev;
|
|
+ }
|
|
+
|
|
+ ret = ath11k_nss_vdev_set_cmd(arvif,
|
|
+ NSS_WIFI_VDEV_CFG_MCBC_EXC_TO_HOST_CMD, 1);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab, "failed to enable mcast/bcast exception %d\n", ret);
|
|
+ goto unregister_vdev;
|
|
+ }
|
|
+
|
|
+ ath11k_debugfs_nss_mesh_vap_create(arvif);
|
|
+
|
|
+ /* This timer cb is called at specified
|
|
+ * interval to update mpp exp timeout */
|
|
+ timer_setup(&arvif->nss.mpp_expiry_timer,
|
|
+ ath11k_nss_mpp_timer_cb, 0);
|
|
+
|
|
+ /* Start the initial timer in 2 secs */
|
|
+ mod_timer(&arvif->nss.mpp_expiry_timer,
|
|
+ jiffies + msecs_to_jiffies(2 * HZ));
|
|
+ break;
|
|
default:
|
|
ret = -ENOTSUPP;
|
|
goto unregister_vdev;
|
|
@@ -1290,6 +2715,14 @@ int ath11k_nss_vdev_up(struct ath11k_vif
|
|
if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
|
|
return 0;
|
|
|
|
+ if (arvif->vif->type == NL80211_IFTYPE_MESH_POINT) {
|
|
+ status = nss_wifi_meshmgr_if_up(arvif->nss.mesh_handle);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab, "nss mesh vdev up error %d\n", status);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC);
|
|
if (!vdev_msg)
|
|
return -ENOMEM;
|
|
@@ -1342,6 +2775,14 @@ int ath11k_nss_vdev_down(struct ath11k_v
|
|
if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
|
|
return 0;
|
|
|
|
+ if (arvif->vif->type == NL80211_IFTYPE_MESH_POINT) {
|
|
+ status = nss_wifi_meshmgr_if_down(arvif->nss.mesh_handle);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ar->ab, "nss mesh vdev up error %d\n", status);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC);
|
|
if (!vdev_msg)
|
|
return -ENOMEM;
|
|
@@ -2722,6 +4163,49 @@ static int ath11k_nss_get_dynamic_interf
|
|
}
|
|
}
|
|
|
|
+static int ath11k_nss_mesh_capability(struct ath11k_base *ab)
|
|
+{
|
|
+ struct nss_wifili_msg *wlmsg = NULL;
|
|
+ nss_wifili_msg_callback_t msg_cb;
|
|
+ nss_tx_status_t status;
|
|
+ int ret = 0;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ reinit_completion(&ab->nss.complete);
|
|
+
|
|
+ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num,
|
|
+ NSS_WIFILI_SEND_MESH_CAPABILITY_INFO,
|
|
+ sizeof(struct nss_wifili_mesh_capability_info),
|
|
+ msg_cb, NULL);
|
|
+
|
|
+ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg);
|
|
+ if (status != NSS_TX_SUCCESS) {
|
|
+ ath11k_warn(ab, "nss failed to get mesh capability msg %d\n", status);
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ 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 mesh capability check\n");
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ kfree(wlmsg);
|
|
+ return 0;
|
|
+
|
|
+free:
|
|
+ kfree(wlmsg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static int ath11k_nss_init(struct ath11k_base *ab)
|
|
{
|
|
struct nss_wifili_init_msg *wim = NULL;
|
|
@@ -2855,6 +4339,15 @@ static int ath11k_nss_init(struct ath11k
|
|
|
|
kfree(wlmsg);
|
|
|
|
+ /* Create a mesh links read debugfs entry */
|
|
+ ath11k_debugfs_nss_soc_create(ab);
|
|
+
|
|
+ /* Check for mesh capability */
|
|
+ ret = ath11k_nss_mesh_capability(ab);
|
|
+
|
|
+ if (ret)
|
|
+ ath11k_err(ab, "Mesh offload is not enabled %d\n", ret);
|
|
+
|
|
ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Init Message TX Success %p %d\n",
|
|
ab->nss.ctx, ab->nss.if_num);
|
|
return 0;
|
|
--- a/drivers/net/wireless/ath/ath11k/nss.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/nss.h
|
|
@@ -8,6 +8,8 @@
|
|
|
|
#include <nss_api_if.h>
|
|
#include <nss_cmn.h>
|
|
+#include <nss_wifi_meshmgr.h>
|
|
+#include "../../../../../net/mac80211/mesh.h"
|
|
|
|
struct ath11k;
|
|
struct ath11k_base;
|
|
@@ -19,8 +21,9 @@ 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 0x40000000
|
|
-#define ATH11K_DBG_NSS_WDS 0x80000000
|
|
+#define ATH11K_DBG_NSS 0x20000000
|
|
+#define ATH11K_DBG_NSS_WDS 0x40000000
|
|
+#define ATH11K_DBG_NSS_MESH 0x80000000
|
|
|
|
/* WIFILI Supported Target Types */
|
|
#define ATH11K_WIFILI_TARGET_TYPE_UNKNOWN 0xFF
|
|
@@ -57,6 +60,7 @@ struct hal_rx_mon_ppdu_info;
|
|
/* Timeout for waiting for response from NSS on TX msg */
|
|
#define ATH11K_NSS_MSG_TIMEOUT_MS 5000
|
|
|
|
+#define ATH11K_MESH_DEFAULT_ELEMENT_TTL 31
|
|
/* Init Flags */
|
|
#define WIFILI_NSS_CCE_DISABLED 0x1
|
|
#define WIFILI_ADDTL_MEM_SEG_SET 0x000000002
|
|
@@ -100,6 +104,8 @@ do { \
|
|
u64_stats_update_end(&tstats->syncp); \
|
|
} while (0)
|
|
|
|
+#define ATH11K_MPP_EXPIRY_TIMER_INTERVAL_MS 60 * HZ
|
|
+
|
|
enum ath11k_nss_opmode {
|
|
ATH11K_NSS_OPMODE_UNKNOWN,
|
|
ATH11K_NSS_OPMODE_AP,
|
|
@@ -146,10 +152,24 @@ struct ath11k_nss_peer {
|
|
struct completion complete;
|
|
};
|
|
|
|
+struct ath11k_nss_mpath_entry {
|
|
+ struct list_head list;
|
|
+ u32 num_entries;
|
|
+ struct nss_wifi_mesh_path_dump_entry mpath[0];
|
|
+};
|
|
+
|
|
+struct ath11k_nss_mpp_entry {
|
|
+ struct list_head list;
|
|
+ u32 num_entries;
|
|
+ struct nss_wifi_mesh_proxy_path_dump_entry mpp[0];
|
|
+};
|
|
+
|
|
/* 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;
|
|
+ /* mesh handle for mesh obj vap */
|
|
+ nss_wifi_mesh_handle_t mesh_handle;
|
|
/* Used for completion status for vdev config nss messages */
|
|
struct completion complete;
|
|
/* Keep the copy of encap type for nss */
|
|
@@ -169,6 +189,25 @@ struct arvif_nss {
|
|
/* WDS cfg should be done only once for ext vdev */
|
|
bool wds_cfg_done;
|
|
bool created;
|
|
+
|
|
+ bool mpp_aging;
|
|
+ bool mpp_dump_req;
|
|
+ struct timer_list mpp_expiry_timer;
|
|
+ u8 mesh_ttl;
|
|
+ bool mesh_forward_enabled;
|
|
+ u32 metadata_type;
|
|
+ u32 mpath_refresh_time;
|
|
+
|
|
+ struct list_head list;
|
|
+ struct list_head mpath_dump;
|
|
+ /* total number of mpath entries in all of the mpath_dump list */
|
|
+ u32 mpath_dump_num_entries;
|
|
+ struct completion dump_mpath_complete;
|
|
+
|
|
+ struct list_head mpp_dump;
|
|
+ /* total number of mpp entries in all of the mpp_dump list */
|
|
+ u32 mpp_dump_num_entries;
|
|
+ struct completion dump_mpp_complete;
|
|
};
|
|
|
|
/* Structure to hold the pdev/radio related info for nss offload support */
|
|
@@ -177,6 +216,8 @@ struct ath11k_nss {
|
|
int if_num;
|
|
/* Radio/pdev Context obtained on pdev register */
|
|
void* ctx;
|
|
+ /* protects stats from nss */
|
|
+ spinlock_t dump_lock;
|
|
};
|
|
|
|
/* Structure to hold the soc related info for nss offload support */
|
|
@@ -185,11 +226,15 @@ struct ath11k_soc_nss {
|
|
bool enabled;
|
|
/* turn on/off nss stats support in ath11k */
|
|
bool stats_enabled;
|
|
+ /* Mesh offload support as advertised by nss */
|
|
+ bool mesh_nss_offload_enabled;
|
|
#ifdef CPTCFG_ATH11K_NSS_SUPPORT
|
|
/* soc nss ctx */
|
|
void* ctx;
|
|
/* if_num to be used for soc related nss messages */
|
|
int if_num;
|
|
+ /* debug mode to disable the regular mesh configuration from mac80211 */
|
|
+ bool debug_mode;
|
|
/* Completion to nss message response */
|
|
struct completion complete;
|
|
/* Response to nss messages are stored here on msg callback
|
|
@@ -245,6 +290,19 @@ void ath11k_nss_update_sta_rxrate(struct
|
|
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);
|
|
+int ath11k_nss_dump_mpath_request(struct ath11k_vif *arvif);
|
|
+int ath11k_nss_dump_mpp_request(struct ath11k_vif *arvif);
|
|
+#ifdef CPTCFG_MAC80211_MESH
|
|
+int ath11k_nss_mesh_config_path(struct ath11k *ar, struct ath11k_vif *arvif,
|
|
+ enum ieee80211_mesh_path_offld_cmd cmd,
|
|
+ struct ieee80211_mesh_path_offld *path);
|
|
+#endif
|
|
+int ath11k_nss_mesh_config_update(struct ieee80211_vif *vif, int changed);
|
|
+int ath11k_nss_assoc_link_arvif_to_ifnum(struct ath11k_vif *arvif, int if_num);
|
|
+int ath11k_nss_mesh_exception_flags(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_exception_flag_msg *nss_msg);
|
|
+int ath11k_nss_exc_rate_config(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_rate_limit_config *nss_exc_cfg);
|
|
#else
|
|
static inline int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb)
|
|
{
|
|
@@ -407,5 +465,35 @@ void ath11k_nss_ext_rx_stats(struct ath1
|
|
{
|
|
return;
|
|
}
|
|
+
|
|
+#ifdef CPTCFG_MAC80211_MESH
|
|
+static inline int
|
|
+ath11k_nss_mesh_config_path(struct ath11k *ar, struct ath11k_vif *arvif,
|
|
+ enum ieee80211_mesh_path_offld_cmd cmd,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+static inline int
|
|
+ath11k_nss_mesh_config_update(struct ieee80211_vif *vif, int changed)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+static int ath11k_nss_assoc_link_arvif_to_ifnum(struct ath11k_vif *arvif,
|
|
+ int if_num)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+static int ath11k_nss_mesh_exception_flags(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_exception_flag_msg *nss_msg)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+int ath11k_nss_exc_rate_config(struct ath11k_vif *arvif,
|
|
+ struct nss_wifi_mesh_rate_limit_config *nss_exc_cfg)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
#endif /* CPTCFG_ATH11K_NSS_SUPPORT */
|
|
#endif
|
|
--- a/include/net/mac80211.h
|
|
+++ b/include/net/mac80211.h
|
|
@@ -127,6 +127,9 @@
|
|
|
|
struct device;
|
|
|
|
+struct ieee80211_mesh_path_offld;
|
|
+enum ieee80211_mesh_path_offld_cmd;
|
|
+
|
|
/**
|
|
* enum ieee80211_max_queues - maximum number of queues
|
|
*
|
|
@@ -366,11 +369,17 @@ enum ieee80211_bss_change {
|
|
* to indicate which NSS BSS parameter changed.
|
|
*
|
|
* @BSS_CHANGED_NSS_AP_ISOLATE: AP Isolate feature in NSS mode
|
|
+ * @BSS_CHANGED_NSS_MESH_TTL: TTL update in NSS mesh mode
|
|
+ * @BSS_CHANGED_NSS_MESH_REFRESH_TIME: Mesh refresh time in NSS mesh mode
|
|
+ * @BSS_CHANGED_NSS_MESH_FWD_ENABLED: NSS offload mesh forward enabled
|
|
*
|
|
*/
|
|
|
|
enum ieee80211_nss_bss_change {
|
|
BSS_CHANGED_NSS_AP_ISOLATE = BIT(0),
|
|
+ BSS_CHANGED_NSS_MESH_TTL = BIT(1),
|
|
+ BSS_CHANGED_NSS_MESH_REFRESH_TIME = BIT(2),
|
|
+ BSS_CHANGED_NSS_MESH_FWD_ENABLED = BIT(3),
|
|
};
|
|
|
|
/*
|
|
@@ -721,6 +730,11 @@ struct ieee80211_bss_conf {
|
|
bool nss_ap_isolate;
|
|
struct ieee80211_multiple_bssid multiple_bssid;
|
|
enum nl80211_beacon_tx_mode beacon_tx_mode;
|
|
+
|
|
+ /* Mesh configuration for nss offload */
|
|
+ u8 nss_offld_ttl;
|
|
+ bool nss_offld_mesh_forward_enabled;
|
|
+ u32 nss_offld_mpath_refresh_time;
|
|
};
|
|
|
|
/**
|
|
@@ -1150,6 +1164,8 @@ ieee80211_info_get_tx_time_est(struct ie
|
|
* @skb: Packet skb (can be NULL if not provided by the driver)
|
|
* @rate: The TX rate that was used when sending the packet
|
|
* @free_list: list where processed skbs are stored to be free'd by the driver
|
|
+ * @mpdu_succ: Number of mpdus successfully transmitted
|
|
+ * @mpdu_fail: Number of mpdus failed
|
|
*/
|
|
struct ieee80211_tx_status {
|
|
struct ieee80211_sta *sta;
|
|
@@ -1161,6 +1177,8 @@ struct ieee80211_tx_status {
|
|
#else
|
|
struct sk_buff_head *free_list;
|
|
#endif
|
|
+ u32 mpdu_succ;
|
|
+ u32 mpdu_fail;
|
|
};
|
|
|
|
/**
|
|
@@ -1661,12 +1679,14 @@ struct ieee80211_channel_switch {
|
|
* @IEEE80211_VIF_GET_NOA_UPDATE: request to handle NOA attributes
|
|
* and send P2P_PS notification to the driver if NOA changed, even
|
|
* this is not pure P2P vif.
|
|
+ * @IEEE80211_HW_NSS_OFFLOAD_DEBUG_MODE: It enables the debug mode of nss offload.
|
|
*/
|
|
enum ieee80211_vif_flags {
|
|
IEEE80211_VIF_BEACON_FILTER = BIT(0),
|
|
IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1),
|
|
IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2),
|
|
IEEE80211_VIF_GET_NOA_UPDATE = BIT(3),
|
|
+ IEEE80211_VIF_NSS_OFFLOAD_DEBUG_MODE = BIT(4),
|
|
};
|
|
|
|
|
|
@@ -2529,6 +2549,7 @@ enum ieee80211_hw_flags {
|
|
IEEE80211_HW_SUPPORTS_CONC_MON_RX_DECAP,
|
|
IEEE80211_HW_SUPPORTS_NSS_OFFLOAD,
|
|
IEEE80211_HW_SUPPORTS_MULTI_BSSID_AP,
|
|
+ IEEE80211_HW_SUPPORTS_MESH_NSS_OFFLOAD,
|
|
|
|
/* keep last, obviously */
|
|
NUM_IEEE80211_HW_FLAGS
|
|
@@ -3974,6 +3995,8 @@ enum ieee80211_reconfig_type {
|
|
* @set_sar_specs: Update the SAR (TX power) settings.
|
|
* @sta_set_decap_offload: Called to notify the driver when a station is allowed
|
|
* to use rx decapsulation offload
|
|
+ * @config_mesh_offload_path: Configure mesh path table when driver supports mesh offload.
|
|
+ * This calback must be atomic.
|
|
*/
|
|
struct ieee80211_ops {
|
|
void (*tx)(struct ieee80211_hw *hw,
|
|
@@ -4297,6 +4320,12 @@ struct ieee80211_ops {
|
|
void (*sta_set_decap_offload)(struct ieee80211_hw *hw,
|
|
struct ieee80211_vif *vif,
|
|
struct ieee80211_sta *sta, bool enabled);
|
|
+#ifdef CPTCFG_MAC80211_MESH
|
|
+ void (*config_mesh_offload_path)(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_vif *vif,
|
|
+ enum ieee80211_mesh_path_offld_cmd cmd,
|
|
+ struct ieee80211_mesh_path_offld *path);
|
|
+#endif
|
|
};
|
|
|
|
/**
|
|
@@ -6989,4 +7018,100 @@ void
|
|
ieeee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
|
|
u64 color_bitmap);
|
|
|
|
+/* Defines for Mesh NSS offload */
|
|
+
|
|
+enum ieee80211_mesh_path_offld_cmd {
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPATH,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPATH,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPATH,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPP,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPP,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPP,
|
|
+};
|
|
+
|
|
+enum ieee80211_mesh_path_offld_action {
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_REFRESH = BIT(0),
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_DEL = BIT(1),
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_EXP = BIT(2),
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_LEARN = BIT(3),
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_ADD = BIT(4),
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_UPDATE = BIT(5),
|
|
+ IEEE80211_MESH_PATH_OFFLD_ACTION_PATH_NOT_FOUND = BIT(6),
|
|
+};
|
|
+
|
|
+/* Duplicate defines to make it available to driver */
|
|
+enum ieee80211_mesh_path_flags {
|
|
+ IEEE80211_MESH_PATH_ACTIVE = BIT(0),
|
|
+ IEEE80211_MESH_PATH_RESOLVING = BIT(1),
|
|
+ IEEE80211_MESH_PATH_SN_VALID = BIT(2),
|
|
+ IEEE80211_MESH_PATH_FIXED = BIT(3),
|
|
+ IEEE80211_MESH_PATH_RESOLVED = BIT(4),
|
|
+ IEEE80211_MESH_PATH_REQ_QUEUED = BIT(5),
|
|
+ IEEE80211_MESH_PATH_DELETED = BIT(6),
|
|
+};
|
|
+
|
|
+struct ieee80211_mesh_path_offld {
|
|
+ u8 mesh_da[ETH_ALEN];
|
|
+ u8 da[ETH_ALEN];
|
|
+ u8 next_hop[ETH_ALEN];
|
|
+ u8 old_next_hop[ETH_ALEN];
|
|
+ u8 ta[ETH_ALEN];
|
|
+ u32 metric;
|
|
+ unsigned long exp_time;
|
|
+ u8 hop_count;
|
|
+ u16 flags; /* See &enum ieee80211_mesh_path_flags */
|
|
+ u8 mesh_gate;
|
|
+ u8 block_mesh_fwd;
|
|
+ u8 metadata_type;
|
|
+};
|
|
+
|
|
+#ifdef CPTCFG_MAC80211_MESH
|
|
+/** ieee80211_mesh_path_offld_change_notify - Notify mesh path change event.
|
|
+ * @vif: Mesh interface on which the event is being reported.
|
|
+ * @path: Mesh path which got changed. Please note not all the entries in the
|
|
+ * path will have valid information. Based on the action code, it will be
|
|
+ * processed.
|
|
+ * @action: Type of the event.
|
|
+ */
|
|
+int ieee80211_mesh_path_offld_change_notify(struct ieee80211_vif *vif,
|
|
+ struct ieee80211_mesh_path_offld *path,
|
|
+ enum ieee80211_mesh_path_offld_action action);
|
|
+
|
|
+/** ieee80211s_update_metric_ppdu - Upate tx PPDU stats for 11s metric computation
|
|
+ *
|
|
+ * @hw: the hardware the frame was transmitted by
|
|
+ * @st: tx status information
|
|
+*/
|
|
+void ieee80211s_update_metric_ppdu(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_tx_status *st);
|
|
+
|
|
+/** mesh_nss_offld_proxy_path_exp_update - update the expiry time from nss
|
|
+ * @vif Mesh interface on which the event is being reported.
|
|
+ * @mac: dest_mac_addr of the mesh proxy path
|
|
+ * @time_diff: This is the time diff since the mesh peer is active
|
|
+ */
|
|
+void mesh_nss_offld_proxy_path_exp_update(struct ieee80211_vif *vif, u8* da,
|
|
+ u8* mesh_da, u32 time_diff);
|
|
+#else
|
|
+static inline int
|
|
+ieee80211_mesh_path_offld_change_notify(struct ieee80211_vif *vif,
|
|
+ struct ieee80211_mesh_path_offld *path,
|
|
+ enum ieee80211_mesh_path_offld_action action)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+ieee80211s_update_metric_ppdu(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_tx_status *st)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void
|
|
+mesh_nss_offld_proxy_path_exp_update(struct ieee80211_vif *vif, u8* da,
|
|
+ u8* mesh_da, u32 time_diff)
|
|
+{
|
|
+}
|
|
+#endif
|
|
+
|
|
#endif /* MAC80211_H */
|
|
--- a/net/mac80211/cfg.c
|
|
+++ b/net/mac80211/cfg.c
|
|
@@ -2289,6 +2289,7 @@ static int ieee80211_update_mesh_config(
|
|
struct mesh_config *conf;
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct ieee80211_if_mesh *ifmsh;
|
|
+ u32 nss_changed = 0;
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
ifmsh = &sdata->u.mesh;
|
|
@@ -2305,8 +2306,11 @@ static int ieee80211_update_mesh_config(
|
|
conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks;
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask))
|
|
conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;
|
|
- if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))
|
|
+ if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask)) {
|
|
conf->dot11MeshTTL = nconf->dot11MeshTTL;
|
|
+ sdata->vif.bss_conf.nss_offld_ttl = nconf->dot11MeshTTL;
|
|
+ nss_changed |= BSS_CHANGED_NSS_MESH_TTL;
|
|
+ }
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask))
|
|
conf->element_ttl = nconf->element_ttl;
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) {
|
|
@@ -2320,8 +2324,12 @@ static int ieee80211_update_mesh_config(
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))
|
|
conf->dot11MeshHWMPmaxPREQretries =
|
|
nconf->dot11MeshHWMPmaxPREQretries;
|
|
- if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask))
|
|
+ if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask)) {
|
|
conf->path_refresh_time = nconf->path_refresh_time;
|
|
+ sdata->vif.bss_conf.nss_offld_mpath_refresh_time =
|
|
+ nconf->path_refresh_time;
|
|
+ nss_changed |= BSS_CHANGED_NSS_MESH_REFRESH_TIME;
|
|
+ }
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask))
|
|
conf->min_discovery_timeout = nconf->min_discovery_timeout;
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask))
|
|
@@ -2356,8 +2364,12 @@ static int ieee80211_update_mesh_config(
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask))
|
|
conf->dot11MeshHWMPRannInterval =
|
|
nconf->dot11MeshHWMPRannInterval;
|
|
- if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask))
|
|
+ if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask)) {
|
|
conf->dot11MeshForwarding = nconf->dot11MeshForwarding;
|
|
+ sdata->vif.bss_conf.nss_offld_mesh_forward_enabled =
|
|
+ nconf->dot11MeshForwarding;
|
|
+ nss_changed |= BSS_CHANGED_NSS_MESH_FWD_ENABLED;
|
|
+ }
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) {
|
|
/* our RSSI threshold implementation is supported only for
|
|
* devices that report signal in dBm.
|
|
@@ -2398,6 +2410,7 @@ static int ieee80211_update_mesh_config(
|
|
conf->dot11MeshConnectedToAuthServer =
|
|
nconf->dot11MeshConnectedToAuthServer;
|
|
ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
|
+ ieee80211_nss_bss_info_change_notify(sdata, nss_changed);
|
|
return 0;
|
|
}
|
|
|
|
--- a/net/mac80211/debug.h
|
|
+++ b/net/mac80211/debug.h
|
|
@@ -63,6 +63,12 @@
|
|
#define MAC80211_MESH_PS_DEBUG 0
|
|
#endif
|
|
|
|
+#ifdef CPTCFG_MAC80211_MESH_OFFLOAD_DEBUG
|
|
+#define MAC80211_MESH_OFFLOAD_DEBUG 1
|
|
+#else
|
|
+#define MAC80211_MESH_OFFLOAD_DEBUG 0
|
|
+#endif
|
|
+
|
|
#ifdef CPTCFG_MAC80211_TDLS_DEBUG
|
|
#define MAC80211_TDLS_DEBUG 1
|
|
#else
|
|
@@ -182,6 +188,10 @@ do { \
|
|
_sdata_dbg(MAC80211_MESH_PS_DEBUG, \
|
|
sdata, fmt, ##__VA_ARGS__)
|
|
|
|
+#define moffld_dbg(sdata, fmt, ...) \
|
|
+ _sdata_dbg(MAC80211_MESH_OFFLOAD_DEBUG, \
|
|
+ sdata, fmt, ##__VA_ARGS__)
|
|
+
|
|
#define tdls_dbg(sdata, fmt, ...) \
|
|
_sdata_dbg(MAC80211_TDLS_DEBUG, \
|
|
sdata, fmt, ##__VA_ARGS__)
|
|
--- a/net/mac80211/debugfs.c
|
|
+++ b/net/mac80211/debugfs.c
|
|
@@ -411,6 +411,7 @@ static const char *hw_flag_names[] = {
|
|
FLAG(SUPPORTS_CONC_MON_RX_DECAP),
|
|
FLAG(SUPPORTS_NSS_OFFLOAD),
|
|
FLAG(SUPPORTS_MULTI_BSSID_AP),
|
|
+ FLAG(SUPPORTS_MESH_NSS_OFFLOAD),
|
|
#undef FLAG
|
|
};
|
|
|
|
--- a/net/mac80211/driver-ops.c
|
|
+++ b/net/mac80211/driver-ops.c
|
|
@@ -347,3 +347,23 @@ int drv_ampdu_action(struct ieee80211_lo
|
|
|
|
return ret;
|
|
}
|
|
+
|
|
+#ifdef CPTCFG_MAC80211_MESH
|
|
+void drv_config_mesh_offload_path(struct ieee80211_local *local,
|
|
+ struct ieee80211_sub_if_data *sdata,
|
|
+ enum ieee80211_mesh_path_offld_cmd cmd,
|
|
+ struct ieee80211_mesh_path_offld *path)
|
|
+{
|
|
+ if (!check_sdata_in_driver(sdata))
|
|
+ return;
|
|
+
|
|
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_MESH_NSS_OFFLOAD))
|
|
+ return;
|
|
+
|
|
+ if (local->ops->config_mesh_offload_path)
|
|
+ local->ops->config_mesh_offload_path(&local->hw,
|
|
+ &sdata->vif, cmd, path);
|
|
+
|
|
+ /* TODO: trace event */
|
|
+}
|
|
+#endif
|
|
--- a/net/mac80211/driver-ops.h
|
|
+++ b/net/mac80211/driver-ops.h
|
|
@@ -1450,4 +1450,10 @@ static inline void drv_sta_set_decap_off
|
|
trace_drv_return_void(local);
|
|
}
|
|
|
|
+#ifdef CPTCFG_MAC80211_MESH
|
|
+void drv_config_mesh_offload_path(struct ieee80211_local *local,
|
|
+ struct ieee80211_sub_if_data *sdata,
|
|
+ enum ieee80211_mesh_path_offld_cmd cmd,
|
|
+ struct ieee80211_mesh_path_offld *path);
|
|
+#endif
|
|
#endif /* __MAC80211_DRIVER_OPS */
|
|
--- a/net/mac80211/mesh.h
|
|
+++ b/net/mac80211/mesh.h
|
|
@@ -277,6 +277,10 @@ void mesh_rx_path_sel_frame(struct ieee8
|
|
struct ieee80211_mgmt *mgmt, size_t len);
|
|
struct mesh_path *
|
|
mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst);
|
|
+struct mesh_path *__mesh_path_add(struct ieee80211_sub_if_data *sdata,
|
|
+ const u8 *dst);
|
|
+int __mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
|
+ const u8 *dst, const u8 *mpp);
|
|
|
|
int mesh_path_add_gate(struct mesh_path *mpath);
|
|
int mesh_path_send_to_gates(struct mesh_path *mpath);
|
|
@@ -318,6 +322,7 @@ void mesh_path_discard_frame(struct ieee
|
|
void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
|
|
|
|
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
|
|
+void mesh_nss_offld_path_update(struct mesh_path *mpath, bool is_mpath, u8 *old_next_hop_addr);
|
|
|
|
#ifdef CPTCFG_MAC80211_MESH
|
|
static inline
|
|
--- a/net/mac80211/mesh_hwmp.c
|
|
+++ b/net/mac80211/mesh_hwmp.c
|
|
@@ -360,6 +360,13 @@ u32 airtime_link_metric_get(struct ieee8
|
|
return (u32)result;
|
|
}
|
|
|
|
+static inline struct sta_info *
|
|
+next_hop_deref_protected(struct mesh_path *mpath)
|
|
+{
|
|
+ return rcu_dereference_protected(mpath->next_hop,
|
|
+ lockdep_is_held(&mpath->state_lock));
|
|
+}
|
|
+
|
|
/**
|
|
* hwmp_route_info_get - Update routing info to originator and transmitter
|
|
*
|
|
@@ -383,9 +390,10 @@ static u32 hwmp_route_info_get(struct ie
|
|
{
|
|
struct ieee80211_local *local = sdata->local;
|
|
struct mesh_path *mpath;
|
|
- struct sta_info *sta;
|
|
+ struct sta_info *sta, *next_hop;
|
|
bool fresh_info;
|
|
const u8 *orig_addr, *ta;
|
|
+ u8 old_next_hop_addr[ETH_ALEN] = {0};
|
|
u32 orig_sn, orig_metric;
|
|
unsigned long orig_lifetime, exp_time;
|
|
u32 last_hop_metric, new_metric;
|
|
@@ -486,7 +494,10 @@ static u32 hwmp_route_info_get(struct ie
|
|
}
|
|
|
|
if (fresh_info) {
|
|
- if (rcu_access_pointer(mpath->next_hop) != sta)
|
|
+ next_hop = rcu_dereference(mpath->next_hop);
|
|
+ if (next_hop)
|
|
+ ether_addr_copy(old_next_hop_addr, next_hop->sta.addr);
|
|
+ if (next_hop != sta)
|
|
mpath->path_change_count++;
|
|
mesh_path_assign_nexthop(mpath, sta);
|
|
mpath->flags |= MESH_PATH_SN_VALID;
|
|
@@ -504,6 +515,8 @@ static u32 hwmp_route_info_get(struct ie
|
|
/* draft says preq_id should be saved to, but there does
|
|
* not seem to be any use for it, skipping by now
|
|
*/
|
|
+
|
|
+ mesh_nss_offld_path_update(mpath, true, old_next_hop_addr);
|
|
} else
|
|
spin_unlock_bh(&mpath->state_lock);
|
|
}
|
|
@@ -534,7 +547,10 @@ static u32 hwmp_route_info_get(struct ie
|
|
}
|
|
|
|
if (fresh_info) {
|
|
- if (rcu_access_pointer(mpath->next_hop) != sta)
|
|
+ next_hop = rcu_dereference(mpath->next_hop);
|
|
+ if (next_hop)
|
|
+ ether_addr_copy(old_next_hop_addr, next_hop->sta.addr);
|
|
+ if (next_hop != sta)
|
|
mpath->path_change_count++;
|
|
mesh_path_assign_nexthop(mpath, sta);
|
|
mpath->metric = last_hop_metric;
|
|
@@ -547,6 +563,8 @@ static u32 hwmp_route_info_get(struct ie
|
|
/* init it at a low value - 0 start is tricky */
|
|
ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1);
|
|
mesh_path_tx_pending(mpath);
|
|
+
|
|
+ mesh_nss_offld_path_update(mpath, true, old_next_hop_addr);
|
|
} else
|
|
spin_unlock_bh(&mpath->state_lock);
|
|
}
|
|
@@ -683,15 +701,6 @@ static void hwmp_preq_frame_process(stru
|
|
}
|
|
}
|
|
|
|
-
|
|
-static inline struct sta_info *
|
|
-next_hop_deref_protected(struct mesh_path *mpath)
|
|
-{
|
|
- return rcu_dereference_protected(mpath->next_hop,
|
|
- lockdep_is_held(&mpath->state_lock));
|
|
-}
|
|
-
|
|
-
|
|
static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_mgmt *mgmt,
|
|
const u8 *prep_elem, u32 metric)
|
|
@@ -1326,3 +1335,272 @@ void mesh_path_tx_root_frame(struct ieee
|
|
return;
|
|
}
|
|
}
|
|
+
|
|
+static int mesh_path_offld_mpath_refresh(struct ieee80211_sub_if_data *sdata,
|
|
+ u8 *mda)
|
|
+{
|
|
+ struct mesh_path *mpath;
|
|
+
|
|
+ rcu_read_lock();
|
|
+
|
|
+ mpath = mesh_path_lookup(sdata, mda);
|
|
+ if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE)) {
|
|
+ moffld_dbg(sdata,
|
|
+ "mpath lookup failed during path refresh for %pM, is_mpath %d\n",
|
|
+ mda, mpath != NULL);
|
|
+ rcu_read_unlock();
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ if (!(mpath->flags & MESH_PATH_RESOLVING) && !(mpath->flags & MESH_PATH_FIXED))
|
|
+ mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH);
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mesh_path_offld_mpath_del(struct ieee80211_sub_if_data *sdata, u8 *da)
|
|
+{
|
|
+ struct mesh_path *mpath, *mppath;
|
|
+
|
|
+ rcu_read_lock();
|
|
+
|
|
+ mpath = mesh_path_lookup(sdata, da);
|
|
+ if (!mpath) {
|
|
+ moffld_dbg(sdata, "mpath lookup failed for %pM during duplicate mpath removal\n",
|
|
+ da);
|
|
+ rcu_read_unlock();
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ mppath = mpp_path_lookup(sdata, da);
|
|
+ if (!mppath) {
|
|
+ moffld_dbg(sdata, "proxy path lookup failed for %pM during duplicate mpath removal\n",
|
|
+ da);
|
|
+ rcu_read_unlock();
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ mesh_path_del(sdata, mpath->dst);
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mesh_path_offld_mpath_exp(struct ieee80211_sub_if_data *sdata, u8 *mda)
|
|
+{
|
|
+ struct mesh_path *mpath;
|
|
+
|
|
+ rcu_read_lock();
|
|
+
|
|
+ mpath = mesh_path_lookup(sdata, mda);
|
|
+ if (!mpath) {
|
|
+ mpath = mesh_path_add(sdata, mda);
|
|
+ if (IS_ERR(mpath)) {
|
|
+ rcu_read_unlock();
|
|
+ moffld_dbg(sdata,
|
|
+ "failed to add mpath for %pM during mpath exp\n", mda);
|
|
+ return PTR_ERR(mpath);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&mpath->state_lock);
|
|
+ mpath->flags &= ~MESH_PATH_ACTIVE;
|
|
+ spin_unlock_bh(&mpath->state_lock);
|
|
+
|
|
+ if (!(mpath->flags & MESH_PATH_RESOLVING) &&
|
|
+ mesh_path_sel_is_hwmp(sdata))
|
|
+ mesh_queue_preq(mpath, PREQ_Q_F_START);
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mesh_path_offld_mpp_learn(struct ieee80211_sub_if_data *sdata,
|
|
+ u8 *da, u8 *mda)
|
|
+{
|
|
+ struct mesh_path *mppath;
|
|
+ int ret;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ mppath = mpp_path_lookup(sdata, da);
|
|
+ if (mppath) {
|
|
+ moffld_dbg(sdata, "proxy path for da %pM mesh_da %pM already exists\n",
|
|
+ da, mda);
|
|
+ rcu_read_unlock();
|
|
+ return -EEXIST;
|
|
+ }
|
|
+
|
|
+ ret = mpp_path_add(sdata, da, mda);
|
|
+ if (ret)
|
|
+ moffld_dbg(sdata, "failed to add proxy path entry (%d): da %pM mesh_da %pM\n",
|
|
+ ret, da, mda);
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int mesh_path_offld_mpp_add(struct ieee80211_sub_if_data *sdata,
|
|
+ u8 *da, u8 *mda)
|
|
+{
|
|
+ struct mesh_path *mppath;
|
|
+ int ret;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ mppath = mpp_path_lookup(sdata, da);
|
|
+ if (mppath) {
|
|
+ moffld_dbg(sdata, "proxy path for da %pM mesh_da %pM already exists\n",
|
|
+ da, mda);
|
|
+ rcu_read_unlock();
|
|
+ return -EEXIST;
|
|
+ }
|
|
+
|
|
+ ret = __mpp_path_add(sdata, da, mda);
|
|
+ if (ret)
|
|
+ moffld_dbg(sdata, "failed to add proxy path entry (%d): da %pM mesh_da %pM\n",
|
|
+ ret, da, mda);
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int mesh_path_offld_mpp_update(struct ieee80211_sub_if_data *sdata,
|
|
+ u8 *da, u8 *mda)
|
|
+{
|
|
+ struct mesh_path *mppath;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ mppath = mpp_path_lookup(sdata, da);
|
|
+ if (!mppath) {
|
|
+ moffld_dbg(sdata,
|
|
+ "proxy path lookup for da %pM failed during MPP update with mesh_da %pM\n",
|
|
+ da, mda);
|
|
+ rcu_read_unlock();
|
|
+ return -ENOENT;
|
|
+ } else {
|
|
+ spin_lock_bh(&mppath->state_lock);
|
|
+ if (!ether_addr_equal(mppath->mpp, mda))
|
|
+ memcpy(mppath->mpp, mda, ETH_ALEN);
|
|
+ mppath->exp_time = jiffies;
|
|
+ spin_unlock_bh(&mppath->state_lock);
|
|
+ }
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mesh_path_offld_mpath_not_found(struct ieee80211_sub_if_data *sdata,
|
|
+ u8 *mda, u8 *ta)
|
|
+{
|
|
+ struct mesh_path *mpath;
|
|
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
|
+
|
|
+ rcu_read_lock();
|
|
+
|
|
+ mpath = mesh_path_lookup(sdata, mda);
|
|
+ if (!mpath) {
|
|
+ mpath = mesh_path_add(sdata, mda);
|
|
+ if (IS_ERR(mpath)) {
|
|
+ moffld_dbg(sdata, "mpath add failed for mesh_da %pM (%lu)\n",
|
|
+ mda, PTR_ERR(mpath));
|
|
+ rcu_read_unlock();
|
|
+ return PTR_ERR(mpath);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!(mpath->flags & MESH_PATH_RESOLVING) &&
|
|
+ mesh_path_sel_is_hwmp(sdata))
|
|
+ mesh_queue_preq(mpath, PREQ_Q_F_START);
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ if (!is_zero_ether_addr(ta))
|
|
+ mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl,
|
|
+ mda, 0, WLAN_REASON_MESH_PATH_NOFORWARD, ta);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ieee80211s_update_metric_ppdu(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_tx_status *st)
|
|
+{
|
|
+ struct sta_info *sta;
|
|
+ int i, num_mpdu;
|
|
+ bool failed;
|
|
+
|
|
+ if (!st->sta)
|
|
+ return;
|
|
+
|
|
+ if (st->mpdu_succ) {
|
|
+ num_mpdu = st->mpdu_succ;
|
|
+ failed = false;
|
|
+ } else if (st->mpdu_fail) {
|
|
+ num_mpdu = st->mpdu_fail;
|
|
+ failed = true;
|
|
+ } else
|
|
+ return;
|
|
+
|
|
+ sta = container_of(st->sta, struct sta_info, sta);
|
|
+ if (!ieee80211_vif_is_mesh(&sta->sdata->vif))
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < num_mpdu; i++) {
|
|
+ ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, failed * 100);
|
|
+ if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) >
|
|
+ LINK_FAIL_THRESH)
|
|
+ mesh_plink_broken(sta);
|
|
+
|
|
+ if (!st->rate)
|
|
+ continue;
|
|
+
|
|
+ ewma_mesh_tx_rate_avg_add(&sta->mesh->tx_rate_avg,
|
|
+ cfg80211_calculate_bitrate(st->rate));
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(ieee80211s_update_metric_ppdu);
|
|
+
|
|
+int ieee80211_mesh_path_offld_change_notify(struct ieee80211_vif *vif,
|
|
+ struct ieee80211_mesh_path_offld *path,
|
|
+ enum ieee80211_mesh_path_offld_action action)
|
|
+{
|
|
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
+ int ret = -ENOTSUPP;
|
|
+
|
|
+ moffld_dbg(sdata, "received mesh offload event %d\n", action);
|
|
+
|
|
+ switch (action) {
|
|
+ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_REFRESH:
|
|
+ ret = mesh_path_offld_mpath_refresh(sdata, path->mesh_da);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_DEL:
|
|
+ ret = mesh_path_offld_mpath_del(sdata, path->mesh_da);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_EXP:
|
|
+ ret = mesh_path_offld_mpath_exp(sdata, path->mesh_da);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_LEARN:
|
|
+ ret = mesh_path_offld_mpp_learn(sdata, path->da, path->mesh_da);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_ADD:
|
|
+ ret = mesh_path_offld_mpp_add(sdata, path->da, path->mesh_da);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_UPDATE:
|
|
+ ret = mesh_path_offld_mpp_update(sdata, path->da,
|
|
+ path->mesh_da);
|
|
+ break;
|
|
+ case IEEE80211_MESH_PATH_OFFLD_ACTION_PATH_NOT_FOUND:
|
|
+ ret = mesh_path_offld_mpath_not_found(sdata, path->da,
|
|
+ path->ta);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(ieee80211_mesh_path_offld_change_notify);
|
|
--- a/net/mac80211/mesh_pathtbl.c
|
|
+++ b/net/mac80211/mesh_pathtbl.c
|
|
@@ -14,6 +14,7 @@
|
|
#include "wme.h"
|
|
#include "ieee80211_i.h"
|
|
#include "mesh.h"
|
|
+#include "driver-ops.h"
|
|
|
|
static void mesh_path_free_rcu(struct mesh_table *tbl, struct mesh_path *mpath);
|
|
|
|
@@ -72,6 +73,63 @@ static void mesh_table_free(struct mesh_
|
|
kfree(tbl);
|
|
}
|
|
|
|
+void mesh_nss_offld_proxy_path_exp_update(struct ieee80211_vif *vif, u8* da, u8* mesh_da, u32 inactive_time)
|
|
+{
|
|
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
+ struct mesh_table *tbl = sdata->u.mesh.mpp_paths;
|
|
+ struct mesh_path *mppath;
|
|
+ struct hlist_node *n;
|
|
+ unsigned long expiry;
|
|
+
|
|
+ spin_lock_bh(&tbl->walk_lock);
|
|
+ hlist_for_each_entry_safe(mppath, n, &tbl->walk_head, walk_list) {
|
|
+ if(!ether_addr_equal(da, mppath->dst) || !ether_addr_equal(mesh_da, mppath->mpp))
|
|
+ continue;
|
|
+ if ((!(mppath->flags & MESH_PATH_RESOLVING)) &&
|
|
+ (!(mppath->flags & MESH_PATH_FIXED))) {
|
|
+ expiry = jiffies - msecs_to_jiffies(inactive_time);
|
|
+ mppath->exp_time = time_after(mppath->exp_time, expiry) ?
|
|
+ mppath->exp_time : expiry;
|
|
+ }
|
|
+ }
|
|
+ spin_unlock_bh(&tbl->walk_lock);
|
|
+}
|
|
+EXPORT_SYMBOL(mesh_nss_offld_proxy_path_exp_update);
|
|
+
|
|
+void mesh_nss_offld_path_update(struct mesh_path *mpath, bool is_mpath, u8 *old_next_hop_addr)
|
|
+{
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ struct sta_info *next_hop;
|
|
+ struct ieee80211_sub_if_data *sdata = mpath->sdata;
|
|
+
|
|
+
|
|
+ path.metric = mpath->metric;
|
|
+ if (time_before(jiffies, mpath->exp_time))
|
|
+ path.exp_time = jiffies_to_msecs(mpath->exp_time - jiffies);
|
|
+
|
|
+ path.hop_count = mpath->hop_count;
|
|
+ path.flags = mpath->flags;
|
|
+ path.mesh_gate = mpath->is_gate;
|
|
+ if (is_mpath) {
|
|
+ ether_addr_copy(path.mesh_da, mpath->dst);
|
|
+ } else {
|
|
+ ether_addr_copy(path.mesh_da, mpath->mpp);
|
|
+ ether_addr_copy(path.da, mpath->dst);
|
|
+ }
|
|
+
|
|
+ next_hop = rcu_dereference(mpath->next_hop);
|
|
+ if (next_hop)
|
|
+ ether_addr_copy(path.next_hop, next_hop->addr);
|
|
+
|
|
+ if (old_next_hop_addr)
|
|
+ ether_addr_copy(path.old_next_hop, old_next_hop_addr);
|
|
+
|
|
+ drv_config_mesh_offload_path(sdata->local, sdata,
|
|
+ is_mpath ? IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPATH :
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPP,
|
|
+ &path);
|
|
+}
|
|
+
|
|
/**
|
|
* mesh_path_assign_nexthop - update mesh path next hop
|
|
*
|
|
@@ -209,16 +267,23 @@ static void mesh_path_move_to_queue(stru
|
|
|
|
|
|
static struct mesh_path *mpath_lookup(struct mesh_table *tbl, const u8 *dst,
|
|
- struct ieee80211_sub_if_data *sdata)
|
|
+ struct ieee80211_sub_if_data *sdata,
|
|
+ bool is_mpath)
|
|
{
|
|
struct mesh_path *mpath;
|
|
+ bool update;
|
|
+ struct sta_info *next_hop;
|
|
|
|
mpath = rhashtable_lookup(&tbl->rhead, dst, mesh_rht_params);
|
|
|
|
if (mpath && mpath_expired(mpath)) {
|
|
spin_lock_bh(&mpath->state_lock);
|
|
+ next_hop = rcu_dereference(mpath->next_hop);
|
|
+ update = !!(mpath->flags & MESH_PATH_ACTIVE);
|
|
mpath->flags &= ~MESH_PATH_ACTIVE;
|
|
spin_unlock_bh(&mpath->state_lock);
|
|
+ if (update && is_mpath)
|
|
+ mesh_nss_offld_path_update(mpath, true, next_hop ? next_hop->addr : NULL);
|
|
}
|
|
return mpath;
|
|
}
|
|
@@ -235,13 +300,13 @@ static struct mesh_path *mpath_lookup(st
|
|
struct mesh_path *
|
|
mesh_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst)
|
|
{
|
|
- return mpath_lookup(sdata->u.mesh.mesh_paths, dst, sdata);
|
|
+ return mpath_lookup(sdata->u.mesh.mesh_paths, dst, sdata, true);
|
|
}
|
|
|
|
struct mesh_path *
|
|
mpp_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst)
|
|
{
|
|
- return mpath_lookup(sdata->u.mesh.mpp_paths, dst, sdata);
|
|
+ return mpath_lookup(sdata->u.mesh.mpp_paths, dst, sdata, false);
|
|
}
|
|
|
|
static struct mesh_path *
|
|
@@ -303,6 +368,7 @@ mpp_path_lookup_by_idx(struct ieee80211_
|
|
int mesh_path_add_gate(struct mesh_path *mpath)
|
|
{
|
|
struct mesh_table *tbl;
|
|
+ struct sta_info *next_hop;
|
|
int err;
|
|
|
|
rcu_read_lock();
|
|
@@ -315,6 +381,7 @@ int mesh_path_add_gate(struct mesh_path
|
|
goto err_rcu;
|
|
}
|
|
mpath->is_gate = true;
|
|
+ next_hop = rcu_dereference(mpath->next_hop);
|
|
mpath->sdata->u.mesh.num_gates++;
|
|
|
|
spin_lock(&tbl->gates_lock);
|
|
@@ -323,6 +390,8 @@ int mesh_path_add_gate(struct mesh_path
|
|
|
|
spin_unlock_bh(&mpath->state_lock);
|
|
|
|
+ mesh_nss_offld_path_update(mpath, true, next_hop ? next_hop->addr : NULL);
|
|
+
|
|
mpath_dbg(mpath->sdata,
|
|
"Mesh path: Recorded new gate: %pM. %d known gates\n",
|
|
mpath->dst, mpath->sdata->u.mesh.num_gates);
|
|
@@ -339,16 +408,21 @@ err_rcu:
|
|
*/
|
|
static void mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath)
|
|
{
|
|
+ struct sta_info *next_hop;
|
|
+
|
|
lockdep_assert_held(&mpath->state_lock);
|
|
if (!mpath->is_gate)
|
|
return;
|
|
|
|
+ next_hop = rcu_dereference(mpath->next_hop);
|
|
mpath->is_gate = false;
|
|
spin_lock_bh(&tbl->gates_lock);
|
|
hlist_del_rcu(&mpath->gate_list);
|
|
mpath->sdata->u.mesh.num_gates--;
|
|
spin_unlock_bh(&tbl->gates_lock);
|
|
|
|
+ mesh_nss_offld_path_update(mpath, true, next_hop ? next_hop->addr : NULL);
|
|
+
|
|
mpath_dbg(mpath->sdata,
|
|
"Mesh path: Deleted gate: %pM. %d known gates\n",
|
|
mpath->dst, mpath->sdata->u.mesh.num_gates);
|
|
@@ -386,17 +460,8 @@ struct mesh_path *mesh_path_new(struct i
|
|
return new_mpath;
|
|
}
|
|
|
|
-/**
|
|
- * mesh_path_add - allocate and add a new path to the mesh path table
|
|
- * @dst: destination address of the path (ETH_ALEN length)
|
|
- * @sdata: local subif
|
|
- *
|
|
- * Returns: 0 on success
|
|
- *
|
|
- * State: the initial state of the new path is set to 0
|
|
- */
|
|
-struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
|
|
- const u8 *dst)
|
|
+struct mesh_path *__mesh_path_add(struct ieee80211_sub_if_data *sdata,
|
|
+ const u8 *dst)
|
|
{
|
|
struct mesh_table *tbl;
|
|
struct mesh_path *mpath, *new_mpath;
|
|
@@ -437,8 +502,36 @@ struct mesh_path *mesh_path_add(struct i
|
|
return new_mpath;
|
|
}
|
|
|
|
-int mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
|
- const u8 *dst, const u8 *mpp)
|
|
+/**
|
|
+ * mesh_path_add - allocate and add a new path to the mesh path table
|
|
+ * @dst: destination address of the path (ETH_ALEN length)
|
|
+ * @sdata: local subif
|
|
+ *
|
|
+ * Returns: 0 on success
|
|
+ *
|
|
+ * State: the initial state of the new path is set to 0
|
|
+ */
|
|
+struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
|
|
+ const u8 *dst)
|
|
+{
|
|
+ struct mesh_path *new_path;
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+
|
|
+ new_path = __mesh_path_add(sdata, dst);
|
|
+ if (IS_ERR(new_path))
|
|
+ return new_path;
|
|
+
|
|
+ ether_addr_copy(path.mesh_da, dst);
|
|
+
|
|
+ drv_config_mesh_offload_path(sdata->local, sdata,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPATH,
|
|
+ &path);
|
|
+
|
|
+ return new_path;
|
|
+}
|
|
+
|
|
+int __mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
|
+ const u8 *dst, const u8 *mpp)
|
|
{
|
|
struct mesh_table *tbl;
|
|
struct mesh_path *new_mpath;
|
|
@@ -474,6 +567,25 @@ int mpp_path_add(struct ieee80211_sub_if
|
|
return ret;
|
|
}
|
|
|
|
+int mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
|
+ const u8 *dst, const u8 *mpp)
|
|
+{
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ int ret;
|
|
+
|
|
+ ret = __mpp_path_add(sdata, dst, mpp);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ether_addr_copy(path.mesh_da, mpp);
|
|
+ ether_addr_copy(path.da, dst);
|
|
+
|
|
+ drv_config_mesh_offload_path(sdata->local, sdata,
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPP,
|
|
+ &path);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
|
|
/**
|
|
* mesh_plink_broken - deactivates paths and sends perr when a link breaks
|
|
@@ -524,11 +636,37 @@ static void mesh_path_free_rcu(struct me
|
|
kfree_rcu(mpath, rcu);
|
|
}
|
|
|
|
-static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath)
|
|
+static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath,
|
|
+ bool is_mpath_tbl)
|
|
{
|
|
+ struct ieee80211_mesh_path_offld path = {0};
|
|
+ struct sta_info *next_hop;
|
|
+ struct ieee80211_sub_if_data *sdata = mpath->sdata;
|
|
+
|
|
+
|
|
+ path.metric = mpath->metric;
|
|
+ path.exp_time = mpath->exp_time;
|
|
+ path.hop_count = mpath->hop_count;
|
|
+ path.flags = mpath->flags;
|
|
+ if (is_mpath_tbl) {
|
|
+ ether_addr_copy(path.mesh_da, mpath->dst);
|
|
+ } else {
|
|
+ ether_addr_copy(path.mesh_da, mpath->mpp);
|
|
+ ether_addr_copy(path.da, mpath->dst);
|
|
+ }
|
|
+
|
|
+ next_hop = rcu_dereference(mpath->next_hop);
|
|
+ if (next_hop)
|
|
+ ether_addr_copy(path.next_hop, next_hop->addr);
|
|
+
|
|
hlist_del_rcu(&mpath->walk_list);
|
|
rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params);
|
|
mesh_path_free_rcu(tbl, mpath);
|
|
+
|
|
+ drv_config_mesh_offload_path(sdata->local, sdata,
|
|
+ is_mpath_tbl ? IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPATH :
|
|
+ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPP,
|
|
+ &path);
|
|
}
|
|
|
|
/**
|
|
@@ -552,7 +690,7 @@ void mesh_path_flush_by_nexthop(struct s
|
|
spin_lock_bh(&tbl->walk_lock);
|
|
hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) {
|
|
if (rcu_access_pointer(mpath->next_hop) == sta)
|
|
- __mesh_path_del(tbl, mpath);
|
|
+ __mesh_path_del(tbl, mpath, true);
|
|
}
|
|
spin_unlock_bh(&tbl->walk_lock);
|
|
}
|
|
@@ -567,19 +705,19 @@ static void mpp_flush_by_proxy(struct ie
|
|
spin_lock_bh(&tbl->walk_lock);
|
|
hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) {
|
|
if (ether_addr_equal(mpath->mpp, proxy))
|
|
- __mesh_path_del(tbl, mpath);
|
|
+ __mesh_path_del(tbl, mpath, false);
|
|
}
|
|
spin_unlock_bh(&tbl->walk_lock);
|
|
}
|
|
|
|
-static void table_flush_by_iface(struct mesh_table *tbl)
|
|
+static void table_flush_by_iface(struct mesh_table *tbl, bool is_mpath_tbl)
|
|
{
|
|
struct mesh_path *mpath;
|
|
struct hlist_node *n;
|
|
|
|
spin_lock_bh(&tbl->walk_lock);
|
|
hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) {
|
|
- __mesh_path_del(tbl, mpath);
|
|
+ __mesh_path_del(tbl, mpath, is_mpath_tbl);
|
|
}
|
|
spin_unlock_bh(&tbl->walk_lock);
|
|
}
|
|
@@ -594,8 +732,8 @@ static void table_flush_by_iface(struct
|
|
*/
|
|
void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata)
|
|
{
|
|
- table_flush_by_iface(sdata->u.mesh.mesh_paths);
|
|
- table_flush_by_iface(sdata->u.mesh.mpp_paths);
|
|
+ table_flush_by_iface(sdata->u.mesh.mesh_paths, true);
|
|
+ table_flush_by_iface(sdata->u.mesh.mpp_paths, false);
|
|
}
|
|
|
|
/**
|
|
@@ -609,7 +747,7 @@ void mesh_path_flush_by_iface(struct iee
|
|
*/
|
|
static int table_path_del(struct mesh_table *tbl,
|
|
struct ieee80211_sub_if_data *sdata,
|
|
- const u8 *addr)
|
|
+ const u8 *addr, bool is_mpath_tbl)
|
|
{
|
|
struct mesh_path *mpath;
|
|
|
|
@@ -620,7 +758,7 @@ static int table_path_del(struct mesh_ta
|
|
return -ENXIO;
|
|
}
|
|
|
|
- __mesh_path_del(tbl, mpath);
|
|
+ __mesh_path_del(tbl, mpath, is_mpath_tbl);
|
|
spin_unlock_bh(&tbl->walk_lock);
|
|
return 0;
|
|
}
|
|
@@ -641,7 +779,7 @@ int mesh_path_del(struct ieee80211_sub_i
|
|
/* flush relevant mpp entries first */
|
|
mpp_flush_by_proxy(sdata, addr);
|
|
|
|
- err = table_path_del(sdata->u.mesh.mesh_paths, sdata, addr);
|
|
+ err = table_path_del(sdata->u.mesh.mesh_paths, sdata, addr, true);
|
|
sdata->u.mesh.mesh_paths_generation++;
|
|
return err;
|
|
}
|
|
@@ -744,7 +882,10 @@ void mesh_path_flush_pending(struct mesh
|
|
*/
|
|
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop)
|
|
{
|
|
+ struct sta_info *old_next_hop;
|
|
+
|
|
spin_lock_bh(&mpath->state_lock);
|
|
+ old_next_hop = rcu_dereference(mpath->next_hop);
|
|
mesh_path_assign_nexthop(mpath, next_hop);
|
|
mpath->sn = 0xffff;
|
|
mpath->metric = 0;
|
|
@@ -757,6 +898,8 @@ void mesh_path_fix_nexthop(struct mesh_p
|
|
/* init it at a low value - 0 start is tricky */
|
|
ewma_mesh_fail_avg_add(&next_hop->mesh->fail_avg, 1);
|
|
mesh_path_tx_pending(mpath);
|
|
+
|
|
+ mesh_nss_offld_path_update(mpath, true, old_next_hop ? old_next_hop->addr : NULL);
|
|
}
|
|
|
|
int mesh_pathtbl_init(struct ieee80211_sub_if_data *sdata)
|
|
@@ -794,7 +937,7 @@ free_path:
|
|
|
|
static
|
|
void mesh_path_tbl_expire(struct ieee80211_sub_if_data *sdata,
|
|
- struct mesh_table *tbl)
|
|
+ struct mesh_table *tbl, bool is_mpath_tbl)
|
|
{
|
|
struct mesh_path *mpath;
|
|
struct hlist_node *n;
|
|
@@ -804,15 +947,15 @@ void mesh_path_tbl_expire(struct ieee802
|
|
if ((!(mpath->flags & MESH_PATH_RESOLVING)) &&
|
|
(!(mpath->flags & MESH_PATH_FIXED)) &&
|
|
time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
|
|
- __mesh_path_del(tbl, mpath);
|
|
+ __mesh_path_del(tbl, mpath, is_mpath_tbl);
|
|
}
|
|
spin_unlock_bh(&tbl->walk_lock);
|
|
}
|
|
|
|
void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
|
|
{
|
|
- mesh_path_tbl_expire(sdata, sdata->u.mesh.mesh_paths);
|
|
- mesh_path_tbl_expire(sdata, sdata->u.mesh.mpp_paths);
|
|
+ mesh_path_tbl_expire(sdata, sdata->u.mesh.mesh_paths, true);
|
|
+ mesh_path_tbl_expire(sdata, sdata->u.mesh.mpp_paths, false);
|
|
}
|
|
|
|
void mesh_pathtbl_unregister(struct ieee80211_sub_if_data *sdata)
|
|
--- a/net/mac80211/rx.c
|
|
+++ b/net/mac80211/rx.c
|
|
@@ -2862,6 +2862,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
|
|
struct mesh_path *mppath;
|
|
char *proxied_addr;
|
|
char *mpp_addr;
|
|
+ bool update = false;
|
|
|
|
if (is_multicast_ether_addr(hdr->addr1)) {
|
|
mpp_addr = hdr->addr3;
|
|
@@ -2881,10 +2882,15 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
|
|
mpp_path_add(sdata, proxied_addr, mpp_addr);
|
|
} else {
|
|
spin_lock_bh(&mppath->state_lock);
|
|
- if (!ether_addr_equal(mppath->mpp, mpp_addr))
|
|
+ if (!ether_addr_equal(mppath->mpp, mpp_addr)) {
|
|
memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
|
|
+ update = true;
|
|
+ }
|
|
mppath->exp_time = jiffies;
|
|
spin_unlock_bh(&mppath->state_lock);
|
|
+
|
|
+ if (update)
|
|
+ mesh_nss_offld_path_update(mppath, false, NULL);
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
--- a/drivers/net/wireless/ath/ath11k/debug.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/debug.h
|
|
@@ -9,6 +9,7 @@
|
|
#include "trace.h"
|
|
#include "debugfs.h"
|
|
|
|
+extern struct dentry *debugfs_ath11k;
|
|
enum ath11k_debug_mask {
|
|
ATH11K_DBG_AHB = 0x00000001,
|
|
ATH11K_DBG_WMI = 0x00000002,
|
|
--- a/net/mac80211/tx.c
|
|
+++ b/net/mac80211/tx.c
|
|
@@ -2547,6 +2547,9 @@ static struct sk_buff *ieee80211_build_h
|
|
info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
|
|
#endif
|
|
|
|
+ info = IEEE80211_SKB_CB(skb);
|
|
+ memset(info, 0, sizeof(*info));
|
|
+
|
|
/* convert Ethernet header to proper 802.11 header (based on
|
|
* operation mode) */
|
|
ethertype = (skb->data[12] << 8) | skb->data[13];
|
|
@@ -2593,6 +2596,13 @@ static struct sk_buff *ieee80211_build_h
|
|
break;
|
|
#ifdef CPTCFG_MAC80211_MESH
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
+ if (ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD) &&
|
|
+ (sdata->vif.driver_flags & IEEE80211_VIF_NSS_OFFLOAD_DEBUG_MODE) &&
|
|
+ !(is_multicast_ether_addr(skb->data))) {
|
|
+ info->flags = IEEE80211_TX_CTL_HW_80211_ENCAP;
|
|
+ goto nss_mesh;
|
|
+ }
|
|
+
|
|
if (!is_multicast_ether_addr(skb->data)) {
|
|
struct sta_info *next_hop;
|
|
bool mpp_lookup = true;
|
|
@@ -2868,10 +2878,10 @@ static struct sk_buff *ieee80211_build_h
|
|
|
|
skb_reset_mac_header(skb);
|
|
|
|
- info = IEEE80211_SKB_CB(skb);
|
|
- memset(info, 0, sizeof(*info));
|
|
-
|
|
- info->flags = info_flags;
|
|
+#ifdef CPTCFG_MAC80211_MESH
|
|
+nss_mesh:
|
|
+#endif
|
|
+ info->flags |= info_flags;
|
|
info->ack_frame_id = info_id;
|
|
info->band = band;
|
|
info->control.flags = ctrl_flags;
|
|
@@ -3944,6 +3954,7 @@ void __ieee80211_subif_start_xmit(struct
|
|
struct ieee80211_key *key = NULL;
|
|
struct sta_info *sta;
|
|
struct sk_buff *next;
|
|
+ struct ieee80211_tx_info *info;
|
|
struct ieee80211_sub_if_data *ap_sdata;
|
|
|
|
if (unlikely(skb->len < ETH_HLEN)) {
|
|
@@ -4040,9 +4051,15 @@ void __ieee80211_subif_start_xmit(struct
|
|
goto out;
|
|
}
|
|
|
|
- ieee80211_tx_stats(dev, skb->len);
|
|
-
|
|
- ieee80211_xmit(sdata, sta, skb);
|
|
+ info = IEEE80211_SKB_CB(skb);
|
|
+ if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
|
|
+ if (sta)
|
|
+ key = rcu_dereference(sta->ptk[sta->ptk_idx]);
|
|
+ ieee80211_8023_xmit(sdata, dev, sta, key, skb);
|
|
+ } else {
|
|
+ ieee80211_tx_stats(dev, skb->len);
|
|
+ ieee80211_xmit(sdata, sta, skb);
|
|
+ }
|
|
}
|
|
goto out;
|
|
out_free:
|