mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-20 10:51:27 +00:00
425 lines
12 KiB
Diff
425 lines
12 KiB
Diff
From e2a727bd1d752b315b15decd1312e0bd7e536050 Mon Sep 17 00:00:00 2001
|
|
From: Aditya Kumar Singh <quic_adisi@quicinc.com>
|
|
Date: Thu, 11 May 2023 16:29:28 +0530
|
|
Subject: [PATCH] ath12k: add peer channel width switch command support
|
|
|
|
Currently, separate set peer param command for each peer is being sent
|
|
for changing channel width. However this leads to firmware buffer overflow
|
|
during multi-client testing.
|
|
|
|
This can be optimised by using WMI_PEER_CHAN_WIDTH_SWITCH_CMDID. This command
|
|
clubs N number of peers into single command. The value of N is advertised
|
|
by firmware in service ready event 2.
|
|
|
|
Add support to form and send WMI_PEER_CHAN_WIDTH_SWITCH_CMDID during peer
|
|
channel width change.
|
|
|
|
Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
|
|
---
|
|
drivers/net/wireless/ath/ath12k/core.h | 11 ++
|
|
drivers/net/wireless/ath/ath12k/mac.c | 94 +++++++++++
|
|
drivers/net/wireless/ath/ath12k/wmi.c | 225 +++++++++++++++++++++++++
|
|
drivers/net/wireless/ath/ath12k/wmi.h | 33 ++++
|
|
4 files changed, 363 insertions(+)
|
|
|
|
--- a/drivers/net/wireless/ath/ath12k/core.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/core.h
|
|
@@ -347,6 +347,11 @@ struct ath12k_link_vif_pvt {
|
|
};
|
|
#endif
|
|
|
|
+struct ath12k_peer_ch_width_switch_data {
|
|
+ int count;
|
|
+ struct wmi_chan_width_peer_arg peer_arg[];
|
|
+};
|
|
+
|
|
struct ath12k_link_vif {
|
|
struct ath12k_base *ab;
|
|
struct ath12k_dp *dp;
|
|
@@ -410,6 +415,10 @@ struct ath12k_link_vif {
|
|
bool mvr_processing;
|
|
bool pending_csa_up;
|
|
int num_stations;
|
|
+
|
|
+ struct completion peer_ch_width_switch_send;
|
|
+ struct work_struct peer_ch_width_switch_work;
|
|
+ struct ath12k_peer_ch_width_switch_data *peer_ch_width_switch_data;
|
|
};
|
|
|
|
struct ath12k_vif {
|
|
@@ -1386,6 +1395,8 @@ struct ath12k_base {
|
|
|
|
int userpd_id;
|
|
|
|
+ u32 chwidth_num_peer_caps;
|
|
+
|
|
/* must be last */
|
|
u8 drv_priv[0] __aligned(sizeof(void *));
|
|
};
|
|
--- a/drivers/net/wireless/ath/ath12k/mac.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/mac.c
|
|
@@ -4877,6 +4877,9 @@ ath12k_mac_assign_link_vif( struct ath12
|
|
INIT_WORK(&arvif->update_bcn_template_work,
|
|
ath12k_update_bcn_template_work);
|
|
arvif->num_stations = 0;
|
|
+ init_completion(&arvif->peer_ch_width_switch_send);
|
|
+ INIT_WORK(&arvif->peer_ch_width_switch_work,
|
|
+ ath12k_wmi_peer_chan_width_switch_work);
|
|
}
|
|
}
|
|
|
|
@@ -6168,6 +6171,7 @@ static int ath12k_station_assoc(struct a
|
|
|
|
spin_lock_bh(&ar->data_lock);
|
|
|
|
+ arsta->bw = bandwidth;
|
|
arvif->num_stations++;
|
|
ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
|
|
"mac station %pM connected to vdev %u. num_stations=%u\n",
|
|
@@ -6281,6 +6285,73 @@ static int ath12k_station_unauthorize(st
|
|
return ret;
|
|
}
|
|
|
|
+static int ath12k_mac_set_peer_ch_switch_data(struct ath12k_link_vif *arvif,
|
|
+ struct ath12k_link_sta *arsta)
|
|
+{
|
|
+ struct ath12k *ar = arvif->ar;
|
|
+ struct ath12k_peer_ch_width_switch_data *peer_data;
|
|
+ struct wmi_chan_width_peer_arg *peer_arg;
|
|
+ struct ieee80211_link_sta *link_sta;
|
|
+ struct ieee80211_vif *vif = arvif->ahvif->vif;
|
|
+ struct cfg80211_chan_def def;
|
|
+ u16 ru_punct_bitmap;
|
|
+
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
+
|
|
+ if (!ar->ab->chwidth_num_peer_caps)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ if (WARN_ON(ath12k_mac_vif_chan(vif, &def, arvif->link_id)))
|
|
+ return -EINVAL;
|
|
+
|
|
+ peer_data = arvif->peer_ch_width_switch_data;
|
|
+
|
|
+ if (!peer_data) {
|
|
+ peer_data = kzalloc(struct_size(peer_data, peer_arg,
|
|
+ arvif->num_stations),
|
|
+ GFP_KERNEL);
|
|
+ if (!peer_data)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ peer_data->count = 0;
|
|
+ arvif->peer_ch_width_switch_data = peer_data;
|
|
+ }
|
|
+
|
|
+ peer_arg = &peer_data->peer_arg[peer_data->count++];
|
|
+
|
|
+
|
|
+ ru_punct_bitmap = 0;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ link_sta = ath12k_get_link_sta(arsta);
|
|
+
|
|
+ if (link_sta) {
|
|
+ if (link_sta->he_cap.has_he && link_sta->eht_cap.has_eht)
|
|
+ ru_punct_bitmap = def.ru_punct_bitmap;
|
|
+
|
|
+ if (ieee80211_vif_is_mesh(vif) && link_sta->ru_punct_bitmap)
|
|
+ ru_punct_bitmap = link_sta->ru_punct_bitmap;
|
|
+ }
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ ether_addr_copy(peer_arg->mac_addr.addr, arsta->addr);
|
|
+ peer_arg->chan_width = arsta->bw;
|
|
+ peer_arg->puncture_20mhz_bitmap = ~ru_punct_bitmap;
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+
|
|
+ if (peer_data->count == 1) {
|
|
+ reinit_completion(&arvif->peer_ch_width_switch_send);
|
|
+ ieee80211_queue_work(ar->ah->hw, &arvif->peer_ch_width_switch_work);
|
|
+ }
|
|
+
|
|
+ if (peer_data->count == arvif->num_stations)
|
|
+ complete(&arvif->peer_ch_width_switch_send);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static void ath12k_sta_rc_update_wk(struct work_struct *wk)
|
|
{
|
|
struct ath12k *ar;
|
|
@@ -6341,6 +6412,10 @@ static void ath12k_sta_rc_update_wk(stru
|
|
ath12k_dbg(ar->ab, ATH12K_DBG_PEER, "mac BW change for sta %pM new BW %d\n",
|
|
arsta->addr, bw);
|
|
|
|
+ err = ath12k_mac_set_peer_ch_switch_data(arvif, arsta);
|
|
+ if (!err || err == -EINVAL)
|
|
+ goto err_unlock;
|
|
+
|
|
err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id,
|
|
WMI_PEER_CHWIDTH, bw);
|
|
if (err)
|
|
@@ -10082,6 +10157,9 @@ static int ath12k_mac_op_add_interface(s
|
|
INIT_WORK(&ahvif->deflink.update_bcn_template_work,
|
|
ath12k_update_bcn_template_work);
|
|
ahvif->deflink.num_stations = 0;
|
|
+ init_completion(&ahvif->deflink.peer_ch_width_switch_send);
|
|
+ INIT_WORK(&ahvif->deflink.peer_ch_width_switch_work,
|
|
+ ath12k_wmi_peer_chan_width_switch_work);
|
|
|
|
ahvif->key_cipher = INVALID_CIPHER;
|
|
|
|
@@ -10136,6 +10214,7 @@ static void ath12k_mac_remove_link_inter
|
|
|
|
cancel_work_sync(&arvif->update_obss_color_notify_work);
|
|
cancel_work_sync(&arvif->update_bcn_template_work);
|
|
+ cancel_work_sync(&arvif->peer_ch_width_switch_work);
|
|
|
|
lockdep_assert_held(&ah->conf_mutex);
|
|
|
|
--- a/drivers/net/wireless/ath/ath12k/wmi.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
|
|
@@ -6201,6 +6201,7 @@ static int ath12k_wmi_tlv_svc_rdy_ext2_p
|
|
}
|
|
ab->max_msduq_per_tid = parse->param.max_msduq_per_tid;
|
|
ab->default_msduq_per_tid = parse->param.default_msduq_per_tid;
|
|
+ ab->chwidth_num_peer_caps = parse->param.chwidth_num_peer_caps;
|
|
break;
|
|
|
|
case WMI_TAG_ARRAY_STRUCT:
|
|
@@ -14374,3 +14375,187 @@ int ath12k_wmi_svc_send_disable(struct a
|
|
|
|
return ret;
|
|
}
|
|
+
|
|
+static void ath12k_wmi_put_peer_list(struct ath12k_base *ab,
|
|
+ struct wmi_chan_width_peer_list *peer_list,
|
|
+ struct wmi_chan_width_peer_arg *peer_arg,
|
|
+ u32 num_peers, int start_idx)
|
|
+{
|
|
+ struct wmi_chan_width_peer_list *itr;
|
|
+ struct wmi_chan_width_peer_arg *arg_itr;
|
|
+ int i;
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
|
|
+ "wmi peer channel width switch command peer list\n");
|
|
+
|
|
+ for (i = 0; i < num_peers; i++) {
|
|
+ itr = &peer_list[i];
|
|
+ arg_itr = &peer_arg[start_idx + i];
|
|
+
|
|
+ itr->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_CHAN_WIDTH_PEER_LIST,
|
|
+ sizeof(*itr));
|
|
+ ether_addr_copy(itr->mac_addr.addr, arg_itr->mac_addr.addr);
|
|
+ itr->chan_width = cpu_to_le32(arg_itr->chan_width);
|
|
+ itr->puncture_20mhz_bitmap = cpu_to_le32(arg_itr->puncture_20mhz_bitmap);
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
|
|
+ " (%u) width %u addr %pM punct_bitmap 0x%x\n",
|
|
+ i + 1, arg_itr->chan_width, arg_itr->mac_addr.addr,
|
|
+ arg_itr->puncture_20mhz_bitmap);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ath12k_wmi_peer_chan_width_switch(struct ath12k *ar,
|
|
+ struct wmi_peer_chan_width_switch_arg *arg)
|
|
+{
|
|
+ struct ath12k_base *ab = ar->ab;
|
|
+ struct ath12k_pdev_wmi *wmi = ar->wmi;
|
|
+ struct wmi_peer_chan_width_switch_req_cmd *cmd;
|
|
+ struct wmi_chan_width_peer_list *peer_list;
|
|
+ struct wmi_tlv *tlv;
|
|
+ u32 num_peers;
|
|
+ size_t peer_list_len;
|
|
+ struct sk_buff *skb;
|
|
+ void *ptr;
|
|
+ int ret, len;
|
|
+
|
|
+ num_peers = arg->num_peers;
|
|
+
|
|
+ if (WARN_ON(num_peers > ab->chwidth_num_peer_caps))
|
|
+ return -EINVAL;
|
|
+
|
|
+ peer_list_len = num_peers * sizeof(*peer_list);
|
|
+
|
|
+ len = sizeof(*cmd) + TLV_HDR_SIZE + peer_list_len;
|
|
+
|
|
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
|
|
+ if (!skb)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ cmd = (struct wmi_peer_chan_width_switch_req_cmd *)skb->data;
|
|
+ cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_PEER_CHAN_WIDTH_SWITCH_CMD,
|
|
+ sizeof(*cmd));
|
|
+ cmd->num_peers = cpu_to_le32(num_peers);
|
|
+ cmd->vdev_var = cpu_to_le32(arg->vdev_var);
|
|
+
|
|
+ ptr = skb->data + sizeof(*cmd);
|
|
+ tlv = (struct wmi_tlv *)ptr;
|
|
+
|
|
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, peer_list_len);
|
|
+ peer_list = (struct wmi_chan_width_peer_list *)tlv->value;
|
|
+
|
|
+ ath12k_wmi_put_peer_list(ab, peer_list, arg->peer_arg, num_peers,
|
|
+ arg->start_idx);
|
|
+
|
|
+ ptr += peer_list_len;
|
|
+
|
|
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_PEER_CHAN_WIDTH_SWITCH_CMDID);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ab, "wmi failed to send peer chan width switch command (%d)\n",
|
|
+ ret);
|
|
+ dev_kfree_skb(skb);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
|
|
+ "wmi peer chan width switch cmd sent num_peers %d \n",
|
|
+ num_peers);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void ath12k_wmi_set_peers_chan_width(struct ath12k_link_vif *arvif,
|
|
+ struct wmi_chan_width_peer_arg *peer_arg,
|
|
+ int num, u8 start_idx)
|
|
+{
|
|
+ struct ath12k *ar = arvif->ar;
|
|
+ struct wmi_chan_width_peer_arg *arg;
|
|
+ int i, err;
|
|
+
|
|
+ for (i = 0; i < num; i++) {
|
|
+ arg = &peer_arg[start_idx + i];
|
|
+
|
|
+ err = ath12k_wmi_set_peer_param(ar, arg->mac_addr.addr,
|
|
+ arvif->vdev_id, WMI_PEER_CHWIDTH,
|
|
+ arg->chan_width);
|
|
+ if (err) {
|
|
+ ath12k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n",
|
|
+ arg->mac_addr.addr, arg->chan_width, err);
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void ath12k_wmi_peer_chan_width_switch_work(struct work_struct *work)
|
|
+{
|
|
+ struct ath12k_link_vif *arvif = container_of(work, struct ath12k_link_vif,
|
|
+ peer_ch_width_switch_work);
|
|
+ struct ath12k *ar = arvif->ar;
|
|
+ struct ath12k_peer_ch_width_switch_data *data;
|
|
+ struct wmi_peer_chan_width_switch_arg arg;
|
|
+ unsigned long time_left = 0;
|
|
+ int count_left, curr_count, max_count_per_cmd = ar->ab->chwidth_num_peer_caps;
|
|
+ int cmd_num = 0, ret;
|
|
+
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+
|
|
+ /* possible that the worker got scheduled after complete was triggered. In
|
|
+ * this case we don't wait for timeout */
|
|
+ if (arvif->peer_ch_width_switch_data->count == arvif->num_stations)
|
|
+ goto send_cmd;
|
|
+
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+
|
|
+ time_left = wait_for_completion_timeout(&arvif->peer_ch_width_switch_send,
|
|
+ ATH12K_PEER_CH_WIDTH_SWITCH_TIMEOUT_HZ);
|
|
+ if (time_left == 0) {
|
|
+ /* Even though timeout occured, we would send the command for the peers
|
|
+ * for which we received sta rc update event, hence not returning */
|
|
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
|
+ "timed out waiting for all peers in peer channel width switch\n");
|
|
+ }
|
|
+
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+
|
|
+send_cmd:
|
|
+
|
|
+ data = arvif->peer_ch_width_switch_data;
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ arg.vdev_var = arvif->vdev_id;
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+
|
|
+ arg.vdev_var |= ATH12K_PEER_VALID_VDEV_ID | ATH12K_PEER_PUNCT_BITMAP_VALID;
|
|
+ arg.peer_arg = data->peer_arg;
|
|
+
|
|
+ count_left = data->count;
|
|
+
|
|
+ while (count_left > 0) {
|
|
+ if (count_left <= max_count_per_cmd)
|
|
+ curr_count = count_left;
|
|
+ else
|
|
+ curr_count = max_count_per_cmd;
|
|
+
|
|
+ count_left -= curr_count;
|
|
+
|
|
+ cmd_num++;
|
|
+
|
|
+ arg.num_peers = curr_count;
|
|
+ arg.start_idx = (cmd_num - 1) * max_count_per_cmd;
|
|
+
|
|
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
|
+ "wmi peer channel width switch command num %u\n",
|
|
+ cmd_num);
|
|
+
|
|
+ ret = ath12k_wmi_peer_chan_width_switch(ar, &arg);
|
|
+ if (ret) {
|
|
+ /* fallback */
|
|
+ ath12k_wmi_set_peers_chan_width(arvif, arg.peer_arg, arg.num_peers,
|
|
+ arg.start_idx);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ kfree(arvif->peer_ch_width_switch_data);
|
|
+ arvif->peer_ch_width_switch_data = NULL;
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+}
|
|
--- a/drivers/net/wireless/ath/ath12k/wmi.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
|
|
@@ -8242,6 +8242,36 @@ struct wmi_vdev_adfs_ocac_complete_event
|
|
u32 center_freq2;
|
|
} __packed;
|
|
|
|
+#define ATH12K_PEER_VALID_VDEV_ID (1 << 31)
|
|
+#define ATH12K_PEER_PUNCT_BITMAP_VALID (1 << 30)
|
|
+#define ATH12K_PEER_CH_WIDTH_SWITCH_TIMEOUT_HZ (5 * HZ)
|
|
+
|
|
+struct wmi_chan_width_peer_arg {
|
|
+ struct wmi_mac_addr mac_addr;
|
|
+ u32 chan_width;
|
|
+ u32 puncture_20mhz_bitmap;
|
|
+};
|
|
+
|
|
+struct wmi_peer_chan_width_switch_arg {
|
|
+ u32 num_peers;
|
|
+ u32 vdev_var;
|
|
+ u32 start_idx;
|
|
+ struct wmi_chan_width_peer_arg *peer_arg;
|
|
+};
|
|
+
|
|
+struct wmi_peer_chan_width_switch_req_cmd {
|
|
+ __le32 tlv_header;
|
|
+ __le32 num_peers;
|
|
+ __le32 vdev_var;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_chan_width_peer_list {
|
|
+ __le32 tlv_header;
|
|
+ struct wmi_mac_addr mac_addr;
|
|
+ __le32 chan_width;
|
|
+ __le32 puncture_20mhz_bitmap;
|
|
+} __packed;
|
|
+
|
|
#define ATH12K_FW_STATS_BUF_SIZE (1024 * 1024)
|
|
|
|
void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
|
|
@@ -8450,4 +8480,5 @@ int ath12k_wmi_config_peer_ppeds_routing
|
|
u32 service_code, u32 priority_valid,
|
|
u32 src_info, bool ppe_routing_enable);
|
|
#endif
|
|
+void ath12k_wmi_peer_chan_width_switch_work(struct work_struct *work);
|
|
#endif
|