mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-22 20:01:53 +00:00
662 lines
20 KiB
Diff
662 lines
20 KiB
Diff
From 0b2c0e6cee6ea4770e32df4139b3a887dd49eb80 Mon Sep 17 00:00:00 2001
|
|
From: Aditya Kumar Singh <quic_adisi@quicinc.com>
|
|
Date: Tue, 29 Nov 2022 16:16:13 +0530
|
|
Subject: [PATCH 2/2] ath12k: add multi-vdev restart support
|
|
|
|
Multi-vdev restart command is used to update the channel to all affected vdevs
|
|
at once. Among all vdev params, only channel could vary in this command. Hence,
|
|
during channel change, this command is useful.
|
|
|
|
Currently, during channel change, each vdev is brought down first, then channel
|
|
is changed followed by bringing vdev up. In multi-vif scenario, this might lead
|
|
to FW buffer overflow. Instead, multi vdev restart comand could be used.
|
|
|
|
Add support for multi vdev restart command and send it during channel switch.
|
|
|
|
Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
|
|
---
|
|
drivers/net/wireless/ath/ath12k/core.h | 4 +
|
|
drivers/net/wireless/ath/ath12k/mac.c | 189 +++++++++++++++++++-
|
|
drivers/net/wireless/ath/ath12k/wmi.c | 235 ++++++++++++++++++++++---
|
|
drivers/net/wireless/ath/ath12k/wmi.h | 47 +++++
|
|
4 files changed, 448 insertions(+), 27 deletions(-)
|
|
|
|
--- a/drivers/net/wireless/ath/ath12k/core.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/core.h
|
|
@@ -362,6 +362,8 @@ struct ath12k_link_vif {
|
|
struct work_struct update_obss_color_notify_work;
|
|
u8 link_id;
|
|
struct ath12k_vif *ahvif;
|
|
+
|
|
+ bool mvr_processing;
|
|
};
|
|
|
|
struct ath12k_vif {
|
|
@@ -870,6 +872,8 @@ struct ath12k {
|
|
bool fw_stats_done;
|
|
|
|
unsigned long last_tx_power_update;
|
|
+
|
|
+ struct completion mvr_complete;
|
|
};
|
|
|
|
struct ath12k_band_cap {
|
|
--- a/drivers/net/wireless/ath/ath12k/mac.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/mac.c
|
|
@@ -9941,12 +9941,19 @@ static int ath12k_vdev_restart_sequence(
|
|
memcpy(&arvif->chanctx, new_ctx, sizeof(*new_ctx));
|
|
spin_unlock_bh(&ar->data_lock);
|
|
|
|
- if (vif_down_failed_map & BIT_ULL(vdev_index))
|
|
+ /* vdev is already restarted via mvr, need to setup
|
|
+ * certain config alone after restart */
|
|
+ if (vdev_index == -1) {
|
|
+ ret = ath12k_mac_vdev_config_after_start(arvif, &new_ctx->def);
|
|
+ if (!ret)
|
|
+ goto beacon_tmpl_setup;
|
|
+ } else if (vif_down_failed_map & BIT_ULL(vdev_index)) {
|
|
ret = ath12k_mac_vdev_restart(arvif, &new_ctx->def, false,
|
|
new_ctx->radar_enabled);
|
|
- else
|
|
+ } else {
|
|
ret = ath12k_mac_vdev_restart(arvif, &new_ctx->def, true,
|
|
new_ctx->radar_enabled);
|
|
+ }
|
|
|
|
if (ret) {
|
|
ath12k_warn(ar->ab, "failed to restart vdev %d: %d\n",
|
|
@@ -9957,6 +9964,7 @@ static int ath12k_vdev_restart_sequence(
|
|
return ret;
|
|
}
|
|
|
|
+beacon_tmpl_setup:
|
|
if (!arvif->is_up)
|
|
return -EOPNOTSUPP;
|
|
|
|
@@ -10211,6 +10219,45 @@ static void ath12k_mac_update_rx_channel
|
|
ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
|
|
}
|
|
|
|
+static int
|
|
+ath12k_mac_multi_vdev_restart(struct ath12k *ar,
|
|
+ const struct cfg80211_chan_def *chandef,
|
|
+ u32 *vdev_id, int len,
|
|
+ bool radar_enabled)
|
|
+{
|
|
+ struct ath12k_base *ab = ar->ab;
|
|
+ struct wmi_pdev_multiple_vdev_restart_req_arg arg = {};
|
|
+ int ret, i;
|
|
+
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
+
|
|
+ arg.vdev_ids.id_len = len;
|
|
+
|
|
+ for (i = 0; i < len; i++)
|
|
+ arg.vdev_ids.id[i] = vdev_id[i];
|
|
+
|
|
+ arg.channel.freq = chandef->chan->center_freq;
|
|
+ arg.channel.band_center_freq1 = chandef->center_freq1;
|
|
+ arg.channel.band_center_freq2 = chandef->center_freq2;
|
|
+ arg.channel.mode =
|
|
+ ath12k_phymodes[chandef->chan->band][chandef->width];
|
|
+
|
|
+ arg.channel.min_power = 0;
|
|
+ arg.channel.max_power = chandef->chan->max_power * 2;
|
|
+ arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
|
|
+ arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
|
|
+ arg.channel.chan_radar = !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
|
|
+ arg.channel.passive = arg.channel.chan_radar;
|
|
+ arg.channel.freq2_radar = radar_enabled;
|
|
+ arg.channel.passive |= !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR);
|
|
+ arg.ru_punct_bitmap = ~chandef->ru_punct_bitmap;
|
|
+ ret = ath12k_wmi_pdev_multiple_vdev_restart(ar, &arg);
|
|
+ if (ret)
|
|
+ ath12k_warn(ab, "mac failed to do mvr (%d)\n", ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static void
|
|
ath12k_mac_update_vif_chan_extras(struct ath12k *ar,
|
|
struct ieee80211_vif_chanctx_switch *vifs,
|
|
@@ -10369,6 +10416,148 @@ ath12k_mac_update_vif_chan(struct ath12k
|
|
arvif->vdev_id, ret);
|
|
}
|
|
}
|
|
+}
|
|
+
|
|
+static void
|
|
+ath12k_mac_update_vif_chan_mvr(struct ath12k *ar,
|
|
+ struct ieee80211_vif_chanctx_switch *vifs,
|
|
+ int n_vifs)
|
|
+{
|
|
+ struct ath12k_base *ab = ar->ab;
|
|
+ struct ath12k_link_vif *arvif, *tx_arvif;
|
|
+ struct ath12k_vif *ahvif;
|
|
+ struct cfg80211_chan_def *chandef;
|
|
+ struct ieee80211_vif *tx_vif;
|
|
+ int ret, i, time_left, trans_vdev_index, vdev_idx, n_vdevs = 0;
|
|
+ u32 vdev_ids[TARGET_NUM_VDEVS];
|
|
+
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
+
|
|
+ chandef = &vifs[0].new_ctx->def;
|
|
+ tx_arvif = NULL;
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx switch via mvr");
|
|
+
|
|
+ ath12k_mac_update_rx_channel(ar, NULL, vifs, n_vifs);
|
|
+
|
|
+ for (i = 0; i < n_vifs; i++) {
|
|
+ ahvif = (void *)vifs[i].vif->drv_priv;
|
|
+ arvif = ahvif->link[vifs[i].link_conf->link_id];
|
|
+
|
|
+ if (WARN_ON(!arvif))
|
|
+ continue;
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_MAC,
|
|
+ "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n",
|
|
+ arvif->vdev_id,
|
|
+ vifs[i].old_ctx->def.chan->center_freq,
|
|
+ vifs[i].new_ctx->def.chan->center_freq,
|
|
+ vifs[i].old_ctx->def.width,
|
|
+ vifs[i].new_ctx->def.width);
|
|
+
|
|
+ if (!arvif->is_started) {
|
|
+ memcpy(&arvif->chanctx, vifs[i].new_ctx, sizeof(*vifs[i].new_ctx));
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (ahvif->vif->mbssid_tx_vif &&
|
|
+ ahvif == (struct ath12k_vif *)ahvif->vif->mbssid_tx_vif->drv_priv) {
|
|
+ tx_vif = ahvif->vif->mbssid_tx_vif;
|
|
+ /* TODO ML mbssid changes */
|
|
+ tx_arvif = &ath12k_vif_to_ahvif(tx_vif)->deflink;
|
|
+ trans_vdev_index = i;
|
|
+ }
|
|
+
|
|
+ arvif->mvr_processing = true;
|
|
+ vdev_ids[n_vdevs++] = arvif->vdev_id;
|
|
+ }
|
|
+
|
|
+ if (!n_vdevs) {
|
|
+ ath12k_dbg(ab, ATH12K_DBG_MAC,
|
|
+ "mac 0 vdevs available to switch chan ctx via mvr\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ reinit_completion(&ar->mvr_complete);
|
|
+
|
|
+ ret = ath12k_mac_multi_vdev_restart(ar, chandef, vdev_ids, n_vdevs,
|
|
+ vifs[0].new_ctx->radar_enabled);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ab, "mac failed to send mvr command (%d)\n", ret);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ time_left = wait_for_completion_timeout(&ar->mvr_complete,
|
|
+ WMI_MVR_CMD_TIMEOUT_HZ);
|
|
+ if (!time_left) {
|
|
+ ath12k_err(ar->ab, "mac mvr cmd response timed out\n");
|
|
+ /* fallback to restarting one-by-one */
|
|
+ return ath12k_mac_update_vif_chan(ar, vifs, n_vifs);
|
|
+ }
|
|
+
|
|
+ if (tx_arvif) {
|
|
+ vdev_idx = -1;
|
|
+
|
|
+ if (tx_arvif->mvr_processing) {
|
|
+ /* failed to restart tx vif via mvr, fallback */
|
|
+ arvif->mvr_processing = false;
|
|
+ vdev_idx = trans_vdev_index;
|
|
+ ath12k_err(ab,
|
|
+ "mac failed to restart mbssid tx vdev %d via mvr cmd\n",
|
|
+ tx_arvif->vdev_id);
|
|
+ }
|
|
+
|
|
+ ret = ath12k_vdev_restart_sequence(tx_arvif,
|
|
+ vifs[trans_vdev_index].new_ctx,
|
|
+ BIT_ULL(trans_vdev_index),
|
|
+ vdev_idx);
|
|
+ if (ret)
|
|
+ ath12k_warn(ab,
|
|
+ "mac failed to bring up mbssid tx vdev %d after mvr (%d)\n",
|
|
+ tx_arvif->vdev_id, ret);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < n_vifs; i++) {
|
|
+ ahvif = (void *)vifs[i].vif->drv_priv;
|
|
+ arvif = ahvif->link[vifs[i].link_conf->link_id];
|
|
+
|
|
+ if (WARN_ON(!arvif))
|
|
+ continue;
|
|
+
|
|
+ vdev_idx = -1;
|
|
+
|
|
+ if (ahvif->vif->mbssid_tx_vif && arvif == tx_arvif)
|
|
+ continue;
|
|
+
|
|
+ if (arvif->mvr_processing) {
|
|
+ /* failed to restart vdev via mvr, fallback */
|
|
+ arvif->mvr_processing = false;
|
|
+ vdev_idx = i;
|
|
+ ath12k_err(ab, "mac failed to restart vdev %d via mvr cmd\n",
|
|
+ arvif->vdev_id);
|
|
+ }
|
|
+
|
|
+ ret = ath12k_vdev_restart_sequence(arvif, vifs[i].new_ctx,
|
|
+ BIT_ULL(i), vdev_idx);
|
|
+ if (ret && ret != -EOPNOTSUPP)
|
|
+ ath12k_warn(ab, "mac failed to bring up vdev %d after mvr (%d)\n",
|
|
+ arvif->vdev_id, ret);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+ath12k_mac_process_update_vif_chan(struct ath12k *ar,
|
|
+ struct ieee80211_vif_chanctx_switch *vifs,
|
|
+ int n_vifs)
|
|
+{
|
|
+ struct ath12k_base *ab = ar->ab;
|
|
+
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
+
|
|
+ if (ath12k_wmi_is_mvr_supported(ab))
|
|
+ ath12k_mac_update_vif_chan_mvr(ar, vifs, n_vifs);
|
|
+ else
|
|
+ ath12k_mac_update_vif_chan(ar, vifs, n_vifs);
|
|
|
|
ath12k_mac_update_vif_chan_extras(ar, vifs, n_vifs);
|
|
}
|
|
@@ -10400,7 +10589,7 @@ ath12k_mac_update_active_vif_chan(struct
|
|
ath12k_mac_change_chanctx_fill_iter,
|
|
&arg);
|
|
|
|
- ath12k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs);
|
|
+ ath12k_mac_process_update_vif_chan(ar, arg.vifs, arg.n_vifs);
|
|
|
|
kfree(arg.vifs);
|
|
out:
|
|
@@ -11321,7 +11510,7 @@ ath12k_mac_op_switch_vif_chanctx(struct
|
|
ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
|
|
"mac chanctx switch n_vifs %d mode %d\n",
|
|
n_vifs, mode);
|
|
- ath12k_mac_update_vif_chan(ar, vifs, n_vifs);
|
|
+ ath12k_mac_process_update_vif_chan(ar, vifs, n_vifs);
|
|
|
|
mutex_unlock(&ar->conf_mutex);
|
|
|
|
@@ -13616,6 +13805,7 @@ static int ath12k_mac_setup(struct ath12
|
|
init_completion(&ar->scan.completed);
|
|
init_completion(&ar->thermal.wmi_sync);
|
|
init_completion(&ar->mlo_setup_done);
|
|
+ init_completion(&ar->mvr_complete);
|
|
|
|
INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
|
|
INIT_WORK(&ar->regd_update_work, ath12k_regd_update_work);
|
|
--- a/drivers/net/wireless/ath/ath12k/wmi.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
|
|
@@ -956,55 +956,55 @@ int ath12k_wmi_vdev_down(struct ath12k *
|
|
}
|
|
|
|
static void ath12k_wmi_put_wmi_channel(struct wmi_channel *chan,
|
|
- struct wmi_vdev_start_req_arg *arg)
|
|
+ struct wmi_channel_arg chan_arg)
|
|
{
|
|
- u32 center_freq1 = arg->channel.band_center_freq1;
|
|
+ u32 center_freq1 = chan_arg.band_center_freq1;
|
|
|
|
memset(chan, 0, sizeof(*chan));
|
|
|
|
- chan->mhz = arg->channel.freq;
|
|
- chan->band_center_freq1 = arg->channel.band_center_freq1;
|
|
+ chan->mhz = chan_arg.freq;
|
|
+ chan->band_center_freq1 = chan_arg.band_center_freq1;
|
|
|
|
- if ((arg->channel.mode == MODE_11AX_HE160) || (arg->channel.mode == MODE_11BE_EHT160)) {
|
|
- if (arg->channel.freq > arg->channel.band_center_freq1)
|
|
+ if ((chan_arg.mode == MODE_11AX_HE160) || (chan_arg.mode == MODE_11BE_EHT160)) {
|
|
+ if (chan_arg.freq > chan_arg.band_center_freq1)
|
|
chan->band_center_freq1 = center_freq1 + 40;
|
|
else
|
|
chan->band_center_freq1 = center_freq1 - 40;
|
|
|
|
- chan->band_center_freq2 = arg->channel.band_center_freq1;
|
|
+ chan->band_center_freq2 = chan_arg.band_center_freq1;
|
|
|
|
- } else if (arg->channel.mode == MODE_11AC_VHT80_80) {
|
|
- chan->band_center_freq2 = arg->channel.band_center_freq2;
|
|
+ } else if (chan_arg.mode == MODE_11AC_VHT80_80) {
|
|
+ chan->band_center_freq2 = chan_arg.band_center_freq2;
|
|
} else {
|
|
chan->band_center_freq2 = 0;
|
|
}
|
|
|
|
- chan->info |= u32_encode_bits(arg->channel.mode, WMI_CHAN_INFO_MODE);
|
|
- if (arg->channel.passive)
|
|
+ chan->info |= u32_encode_bits(chan_arg.mode, WMI_CHAN_INFO_MODE);
|
|
+ if (chan_arg.passive)
|
|
chan->info |= WMI_CHAN_INFO_PASSIVE;
|
|
- if (arg->channel.allow_ibss)
|
|
+ if (chan_arg.allow_ibss)
|
|
chan->info |= WMI_CHAN_INFO_ADHOC_ALLOWED;
|
|
- if (arg->channel.allow_ht)
|
|
+ if (chan_arg.allow_ht)
|
|
chan->info |= WMI_CHAN_INFO_ALLOW_HT;
|
|
- if (arg->channel.allow_vht)
|
|
+ if (chan_arg.allow_vht)
|
|
chan->info |= WMI_CHAN_INFO_ALLOW_VHT;
|
|
- if (arg->channel.allow_he)
|
|
+ if (chan_arg.allow_he)
|
|
chan->info |= WMI_CHAN_INFO_ALLOW_HE;
|
|
- if (arg->channel.ht40plus)
|
|
+ if (chan_arg.ht40plus)
|
|
chan->info |= WMI_CHAN_INFO_HT40_PLUS;
|
|
- if (arg->channel.chan_radar)
|
|
+ if (chan_arg.chan_radar)
|
|
chan->info |= WMI_CHAN_INFO_DFS;
|
|
- if (arg->channel.freq2_radar)
|
|
+ if (chan_arg.freq2_radar)
|
|
chan->info |= WMI_CHAN_INFO_DFS_FREQ2;
|
|
|
|
- chan->reg_info_1 = u32_encode_bits(arg->channel.max_power,
|
|
+ chan->reg_info_1 = u32_encode_bits(chan_arg.max_power,
|
|
WMI_CHAN_REG_INFO1_MAX_PWR) |
|
|
- u32_encode_bits(arg->channel.max_reg_power,
|
|
+ u32_encode_bits(chan_arg.max_reg_power,
|
|
WMI_CHAN_REG_INFO1_MAX_REG_PWR);
|
|
|
|
- chan->reg_info_2 = u32_encode_bits(arg->channel.max_antenna_gain,
|
|
+ chan->reg_info_2 = u32_encode_bits(chan_arg.max_antenna_gain,
|
|
WMI_CHAN_REG_INFO2_ANT_MAX) |
|
|
- u32_encode_bits(arg->channel.max_power, WMI_CHAN_REG_INFO2_MAX_TX_PWR);
|
|
+ u32_encode_bits(chan_arg.max_power, WMI_CHAN_REG_INFO2_MAX_TX_PWR);
|
|
}
|
|
|
|
int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg,
|
|
@@ -1067,7 +1067,7 @@ int ath12k_wmi_vdev_start(struct ath12k
|
|
ptr = skb->data + sizeof(*cmd);
|
|
chan = ptr;
|
|
|
|
- ath12k_wmi_put_wmi_channel(chan, arg);
|
|
+ ath12k_wmi_put_wmi_channel(chan, arg->channel);
|
|
|
|
chan->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_CHANNEL,
|
|
sizeof(*chan));
|
|
@@ -9471,6 +9471,108 @@ static void ath12k_wmi_event_teardown_co
|
|
kfree(tb);
|
|
}
|
|
|
|
+static void ath12k_wmi_process_mvr_event(struct ath12k *ab, u32 *vdev_id_bm,
|
|
+ u32 num_vdev_bm)
|
|
+{
|
|
+ struct ath12k *ar = NULL;
|
|
+ struct ath12k_link_vif *arvif = NULL;
|
|
+ u32 vdev_bitmap, bit_pos;
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
|
|
+ "wmi mvr resp num_vdev_bm %d vdev_id_bm[0]=0x%x vdev_id_bm[1]=0x%x\n",
|
|
+ num_vdev_bm, vdev_id_bm[0],
|
|
+ (num_vdev_bm == WMI_MVR_RESP_VDEV_BM_MAX_LEN ?
|
|
+ vdev_id_bm[1] : 0x00));
|
|
+
|
|
+ /* 31-0 bits processing */
|
|
+ vdev_bitmap = vdev_id_bm[0];
|
|
+
|
|
+ for (bit_pos = 0; bit_pos < 32; bit_pos++) {
|
|
+
|
|
+ if (!test_bit(bit_pos, &vdev_bitmap))
|
|
+ continue;
|
|
+
|
|
+ arvif = ath12k_mac_get_arvif_by_vdev_id(ab, bit_pos);
|
|
+ if (!arvif) {
|
|
+ ath12k_warn(ab, "wmi mvr resp for unknown vdev %d", bit_pos);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ arvif->mvr_processing = false;
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
|
|
+ "wmi mvr vdev %d restarted\n", bit_pos);
|
|
+ }
|
|
+
|
|
+ /* TODO: 63-32 bits processing
|
|
+ * Add support to parse bitmap once support for
|
|
+ * TARGET_NUM_VDEVS > 32 is added
|
|
+ */
|
|
+
|
|
+ if (arvif)
|
|
+ ar = arvif->ar;
|
|
+
|
|
+ if (ar)
|
|
+ complete(&ar->mvr_complete);
|
|
+}
|
|
+
|
|
+static int ath12k_wmi_tlv_mvr_event_parse(struct ath12k_base *ab,
|
|
+ u16 tag, u16 len,
|
|
+ const void *ptr, void *data)
|
|
+{
|
|
+ struct wmi_pdev_mvr_resp_event_parse *parse = data;
|
|
+ struct wmi_pdev_mvr_resp_event_fixed_param *fixed_param;
|
|
+
|
|
+ switch(tag) {
|
|
+ case WMI_TAG_MULTIPLE_VDEV_RESTART_RESPONSE_EVENT:
|
|
+ fixed_param = (struct wmi_pdev_mvr_resp_event_fixed_param *)ptr;
|
|
+
|
|
+ if (fixed_param->status) {
|
|
+ ath12k_warn(ab, "wmi mvr resp event status %u\n",
|
|
+ fixed_param->status);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ memcpy(&parse->fixed_param, fixed_param,
|
|
+ sizeof(struct wmi_pdev_mvr_resp_event_fixed_param));
|
|
+ break;
|
|
+ case WMI_TAG_ARRAY_UINT32:
|
|
+ if ((len > WMI_MVR_RESP_VDEV_BM_MAX_LEN_BYTES) || (len == 0)) {
|
|
+ ath12k_warn(ab, "wmi invalid vdev id len in mvr resp %u\n",
|
|
+ len);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ parse->num_vdevs_bm = len / sizeof(u32);
|
|
+ memcpy(parse->vdev_id_bm, ptr, len);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void ath12k_wmi_event_mvr_response(struct ath12k_base *ab,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ struct wmi_pdev_mvr_resp_event_parse parse = {};
|
|
+ int ret;
|
|
+
|
|
+ ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
|
|
+ ath12k_wmi_tlv_mvr_event_parse,
|
|
+ &parse);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ab, "wmi failed to parse mvr response tlv %d\n",
|
|
+ ret);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi mvr resp for pdev %d\n",
|
|
+ parse.fixed_param.pdev_id);
|
|
+
|
|
+ ath12k_wmi_process_mvr_event(ab, parse.vdev_id_bm, parse.num_vdevs_bm);
|
|
+}
|
|
+
|
|
static void ath12k_wmi_tlv_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
|
|
{
|
|
struct wmi_cmd_hdr *cmd_hdr;
|
|
@@ -9617,6 +9719,9 @@ static void ath12k_wmi_tlv_op_rx(struct
|
|
case WMI_MLO_TEARDOWN_COMPLETE_EVENTID:
|
|
ath12k_wmi_event_teardown_complete(ab, skb);
|
|
break;
|
|
+ case WMI_PDEV_MULTIPLE_VDEV_RESTART_RESP_EVENTID:
|
|
+ ath12k_wmi_event_mvr_response(ab, skb);
|
|
+ break;
|
|
/* TODO: Add remaining events */
|
|
default:
|
|
ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
|
|
@@ -10073,3 +10178,82 @@ int ath12k_wmi_mlo_teardown(struct ath12
|
|
|
|
return ret;
|
|
}
|
|
+
|
|
+bool ath12k_wmi_is_mvr_supported(struct ath12k_base *ab)
|
|
+{
|
|
+ struct ath12k_wmi_base *wmi_ab = &ab->wmi_ab;
|
|
+
|
|
+ return test_bit(WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART,
|
|
+ wmi_ab->svc_map) &&
|
|
+ test_bit(WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART_RESPONSE_SUPPORT,
|
|
+ wmi_ab->svc_map);
|
|
+}
|
|
+
|
|
+int ath12k_wmi_pdev_multiple_vdev_restart(struct ath12k *ar,
|
|
+ struct wmi_pdev_multiple_vdev_restart_req_arg *arg)
|
|
+{
|
|
+ struct ath12k_pdev_wmi *wmi = ar->wmi;
|
|
+ struct wmi_pdev_multiple_vdev_restart_request_cmd *cmd;
|
|
+ struct wmi_channel *chan;
|
|
+ struct wmi_tlv *tlv;
|
|
+ u32 num_vdev_ids;
|
|
+ __le32 *vdev_ids;
|
|
+ size_t vdev_ids_len;
|
|
+ struct sk_buff *skb;
|
|
+ void *ptr;
|
|
+ int ret, len, i;
|
|
+
|
|
+ if (WARN_ON(arg->vdev_ids.id_len > TARGET_NUM_VDEVS))
|
|
+ return -EINVAL;
|
|
+
|
|
+ num_vdev_ids = arg->vdev_ids.id_len;
|
|
+ vdev_ids_len = num_vdev_ids * sizeof(__le32);
|
|
+
|
|
+ len = sizeof(*cmd) + TLV_HDR_SIZE + vdev_ids_len +
|
|
+ sizeof(*chan);
|
|
+
|
|
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
|
|
+ if (!skb)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ cmd = (struct wmi_pdev_multiple_vdev_restart_request_cmd *)skb->data;
|
|
+ cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMD,
|
|
+ sizeof(*cmd));
|
|
+ cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id);
|
|
+ cmd->num_vdevs = cpu_to_le32(arg->vdev_ids.id_len);
|
|
+ cmd->puncture_20mhz_bitmap = cpu_to_le32(arg->ru_punct_bitmap);
|
|
+
|
|
+ cmd->flags = cpu_to_le32(WMI_MVR_RESPONSE_SUPPORT_EXPECTED);
|
|
+
|
|
+ ptr = skb->data + sizeof(*cmd);
|
|
+ tlv = (struct wmi_tlv *)ptr;
|
|
+
|
|
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, vdev_ids_len);
|
|
+ vdev_ids = (__le32 *)tlv->value;
|
|
+
|
|
+ for (i = 0; i < num_vdev_ids; i++)
|
|
+ vdev_ids[i] = cpu_to_le32(arg->vdev_ids.id[i]);
|
|
+
|
|
+ ptr += TLV_HDR_SIZE + vdev_ids_len;
|
|
+ chan = (struct wmi_channel *)ptr;
|
|
+
|
|
+ ath12k_wmi_put_wmi_channel(chan, arg->channel);
|
|
+
|
|
+ chan->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_CHANNEL, sizeof(*chan));
|
|
+ ptr += sizeof(*chan);
|
|
+
|
|
+ ret = ath12k_wmi_cmd_send(wmi, skb,
|
|
+ WMI_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMDID);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ar->ab, "wmi failed to send mvr command (%d)\n",
|
|
+ ret);
|
|
+ dev_kfree_skb(skb);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
|
+ "wmi mvr cmd sent num_vdevs %d freq %d\n",
|
|
+ num_vdev_ids, arg->channel.freq);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
--- a/drivers/net/wireless/ath/ath12k/wmi.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
|
|
@@ -1919,6 +1919,7 @@ enum wmi_tlv_tag {
|
|
WMI_TAG_MUEDCA_PARAMS_CONFIG_EVENT = 0x32a,
|
|
WMI_TAG_SERVICE_READY_EXT2_EVENT = 0x334,
|
|
WMI_TAG_FILS_DISCOVERY_TMPL_CMD = 0x344,
|
|
+ WMI_TAG_MULTIPLE_VDEV_RESTART_RESPONSE_EVENT = 0x365,
|
|
WMI_TAG_MAC_PHY_CAPABILITIES_EXT = 0x36F,
|
|
WMI_TAG_PDEV_SRG_BSS_COLOR_BITMAP_CMD = 0x37b,
|
|
WMI_TAG_PDEV_SRG_PARTIAL_BSSID_BITMAP_CMD,
|
|
@@ -2176,6 +2177,7 @@ enum wmi_tlv_service {
|
|
WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET = 213,
|
|
WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219,
|
|
WMI_TLV_SERVICE_EXT2_MSG = 220,
|
|
+ WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART_RESPONSE_SUPPORT = 235,
|
|
WMI_TLV_SERVICE_5_9GHZ_SUPPORT = 247,
|
|
WMI_TLV_SERVICE_SRG_SRP_SPATIAL_REUSE_SUPPORT = 249,
|
|
|
|
@@ -6354,6 +6356,47 @@ struct wmi_mlo_teardown_complete_fixed_p
|
|
u32 status;
|
|
} __packed;
|
|
|
|
+/* Inform FW that host expects response for multi-vdev
|
|
+ * restart command */
|
|
+#define WMI_MVR_RESPONSE_SUPPORT_EXPECTED 0x1
|
|
+#define WMI_MVR_CMD_TIMEOUT_HZ (2 * HZ)
|
|
+#define WMI_MVR_RESP_VDEV_BM_MAX_LEN 2
|
|
+#define WMI_MVR_RESP_VDEV_BM_MAX_LEN_BYTES (WMI_MVR_RESP_VDEV_BM_MAX_LEN * 4)
|
|
+
|
|
+struct wmi_vdev_ids_arg {
|
|
+ u32 id_len;
|
|
+ u32 id[17]; /* TARGET_NUM_VDEVS */
|
|
+};
|
|
+
|
|
+struct wmi_pdev_multiple_vdev_restart_req_arg {
|
|
+ struct wmi_vdev_ids_arg vdev_ids;
|
|
+ struct wmi_channel_arg channel;
|
|
+ u16 ru_punct_bitmap;
|
|
+};
|
|
+
|
|
+struct wmi_pdev_multiple_vdev_restart_request_cmd {
|
|
+ __le32 tlv_header;
|
|
+ __le32 pdev_id;
|
|
+ __le32 requestor_id;
|
|
+ __le32 disable_hw_ack;
|
|
+ __le32 cac_duration_ms;
|
|
+ __le32 num_vdevs;
|
|
+ __le32 flags;
|
|
+ __le32 puncture_20mhz_bitmap;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_pdev_mvr_resp_event_fixed_param {
|
|
+ u32 pdev_id;
|
|
+ u32 requestor_id;
|
|
+ u32 status;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_pdev_mvr_resp_event_parse {
|
|
+ struct wmi_pdev_mvr_resp_event_fixed_param fixed_param;
|
|
+ u32 num_vdevs_bm;
|
|
+ u32 vdev_id_bm[];
|
|
+} __packed;
|
|
+
|
|
#define ATH12K_FW_STATS_BUF_SIZE (1024 * 1024)
|
|
|
|
void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
|
|
@@ -6522,4 +6565,7 @@ int ath12k_wmi_mlo_setup(struct ath12k *
|
|
struct wmi_mlo_setup_params *mlo_params);
|
|
int ath12k_wmi_mlo_ready(struct ath12k *ar);
|
|
int ath12k_wmi_mlo_teardown(struct ath12k *ar);
|
|
+bool ath12k_wmi_is_mvr_supported(struct ath12k_base *ab);
|
|
+int ath12k_wmi_pdev_multiple_vdev_restart(struct ath12k *ar,
|
|
+ struct wmi_pdev_multiple_vdev_restart_req_arg *arg);
|
|
#endif
|