lede/package/kernel/mac80211/patches/nss/214-ath11k-qos-null-frame-tx-over-wmi.patch
2021-06-29 11:47:43 +08:00

474 lines
15 KiB
Diff

From 0e29b669153f100b60107d5f6b3fe407b71ba79a Mon Sep 17 00:00:00 2001
From: Sowmiya Sree Elavalagan <ssreeela@codeaurora.org>
Date: Wed, 30 Sep 2020 22:33:42 +0530
Subject: [PATCH] ath11k: QOS null frame tx over wmi
Added support to send qos null frame through FW.
NSS driver does not support QOS null frame tx.
Hence this is brought for nss offload case to send
qos null frame. QOS null packet queued from mac80211
is sent to FW through wmi interface. This happens only if FW supports
qos null tx, this is based on service bit received in ext2 service
event from FW. On successful transmission of QOS null frame status
is set 0 in the event received for this wmi message. This is status
is sent to mac80211 for further handling.
Signed-off-by: Sowmiya Sree Elavalagan <ssreeela@codeaurora.org>
---
drivers/net/wireless/ath/ath11k/mac.c | 28 ++++-
drivers/net/wireless/ath/ath11k/wmi.c | 200 ++++++++++++++++++++++++++--------
drivers/net/wireless/ath/ath11k/wmi.h | 46 +++++++-
3 files changed, 220 insertions(+), 54 deletions(-)
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -5314,6 +5314,16 @@ static int ath11k_mac_mgmt_tx_wmi(struct
ATH11K_SKB_CB(skb)->paddr = paddr;
+ if (ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+ ret = ath11k_wmi_qos_null_send(ar, arvif->vdev_id, buf_id, skb);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send qos null frame over wmi: %d\n", ret);
+ goto err_unmap_buf;
+ }
+
+ return 0;
+ }
+
ret = ath11k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb);
if (ret) {
ath11k_warn(ar->ab, "failed to send mgmt frame: %d\n", ret);
@@ -5378,8 +5388,8 @@ static void ath11k_mgmt_over_wmi_tx_work
}
}
-static int ath11k_mac_mgmt_tx(struct ath11k *ar, struct sk_buff *skb,
- bool is_prb_rsp)
+static int ath11k_mac_tx_over_wmi(struct ath11k *ar, struct sk_buff *skb,
+ bool is_prb_rsp)
{
struct sk_buff_head *q = &ar->wmi_mgmt_tx_queue;
@@ -5439,7 +5449,7 @@ static void ath11k_mac_op_tx(struct ieee
} else if (ieee80211_is_mgmt(hdr->frame_control)) {
frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control);
is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control);
- ret = ath11k_mac_mgmt_tx(ar, skb, is_prb_rsp);
+ ret = ath11k_mac_tx_over_wmi(ar, skb, is_prb_rsp);
if (ret) {
if (ret != -EBUSY)
ath11k_warn(ar->ab, "failed to queue management frame %d\n",
@@ -5460,6 +5470,20 @@ static void ath11k_mac_op_tx(struct ieee
spin_unlock_bh(&ar->data_lock);
}
return;
+ } else if (ar->ab->nss.enabled &&
+ ieee80211_is_qos_nullfunc(hdr->frame_control) &&
+ test_bit(WMI_TLV_SERVICE_QOS_NULL_FRAME_TX_OVER_WMI,
+ ar->ab->wmi_ab.svc_map)) {
+ /* NSS driver does not support tx qos null pkt hence it is offload
+ * to fw via wmi path similar to mgmt frames
+ */
+ ret = ath11k_mac_tx_over_wmi(ar, skb, false);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to queue qos null frame %d\n",
+ ret);
+ ieee80211_free_txskb(ar->hw, skb);
+ }
+ return;
}
if (ar->ab->nss.enabled)
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -104,7 +104,7 @@ static const struct wmi_tlv_policy wmi_t
[WMI_TAG_MGMT_RX_HDR]
= { .min_len = sizeof(struct wmi_mgmt_rx_hdr) },
[WMI_TAG_MGMT_TX_COMPL_EVENT]
- = { .min_len = sizeof(struct wmi_mgmt_tx_compl_event) },
+ = { .min_len = sizeof(struct wmi_tx_compl_event) },
[WMI_TAG_SCAN_EVENT]
= { .min_len = sizeof(struct wmi_scan_event) },
[WMI_TAG_PEER_STA_KICKOUT_EVENT]
@@ -662,6 +662,55 @@ int ath11k_wmi_mgmt_send(struct ath11k *
return ret;
}
+int ath11k_wmi_qos_null_send(struct ath11k *ar, u32 vdev_id, u32 buf_id,
+ struct sk_buff *frame)
+{
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
+ struct wmi_qos_null_tx_cmd *cmd;
+ struct wmi_tlv *frame_tlv;
+ struct sk_buff *skb;
+ u32 buf_len;
+ int len, ret = 0;
+
+ buf_len = frame->len < WMI_QOS_NULL_SEND_BUF_LEN ?
+ frame->len : WMI_QOS_NULL_SEND_BUF_LEN;
+
+ len = sizeof(*cmd) + sizeof(*frame_tlv) + roundup(buf_len, 4);
+
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_qos_null_tx_cmd *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_QOS_NULL_FRAME_TX_SEND) |
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+ cmd->vdev_id = vdev_id;
+ cmd->desc_id = buf_id;
+ cmd->paddr_lo = lower_32_bits(ATH11K_SKB_CB(frame)->paddr);
+ cmd->paddr_hi = upper_32_bits(ATH11K_SKB_CB(frame)->paddr);
+ cmd->frame_len = frame->len;
+ cmd->buf_len = buf_len;
+
+ frame_tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd));
+ frame_tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
+ FIELD_PREP(WMI_TLV_LEN, buf_len);
+
+ memcpy(frame_tlv->value, frame->data, buf_len);
+
+ ath11k_ce_byte_swap(frame_tlv->value, buf_len);
+
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_QOS_NULL_FRAME_TX_SEND_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab,
+ "failed to submit WMI_QOS_NULL_FRAME_TX_SEND_CMDID cmd\n");
+ dev_kfree_skb(skb);
+ }
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "wmi QOS null tx send cmd sent successfully\n");
+ return ret;
+}
+
int ath11k_wmi_vdev_create(struct ath11k *ar, u8 *macaddr,
struct vdev_create_params *param)
{
@@ -5371,7 +5420,7 @@ static int ath11k_pull_mgmt_rx_params_tl
return 0;
}
-static int wmi_process_mgmt_tx_comp(struct ath11k *ar, struct wmi_mgmt_tx_compl_event *tx_compl_param)
+static int wmi_process_tx_comp(struct ath11k *ar, struct wmi_tx_compl_event *tx_compl_param)
{
struct sk_buff *msdu;
struct ieee80211_tx_info *info;
@@ -5441,12 +5493,13 @@ skip_mgmt_stats:
return 0;
}
-static int ath11k_pull_mgmt_tx_compl_param_tlv(struct ath11k_base *ab,
- struct sk_buff *skb,
- struct wmi_mgmt_tx_compl_event *param)
+static int ath11k_pull_tx_compl_param_tlv(struct ath11k_base *ab,
+ struct sk_buff *skb,
+ struct wmi_tx_compl_event *param,
+ int event_id)
{
const void **tb;
- const struct wmi_mgmt_tx_compl_event *ev;
+ const struct wmi_tx_compl_event *ev;
int ret;
tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
@@ -5456,7 +5509,7 @@ static int ath11k_pull_mgmt_tx_compl_par
return ret;
}
- ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT];
+ ev = tb[event_id];
if (!ev) {
ath11k_warn(ab, "failed to fetch mgmt tx compl ev");
kfree(tb);
@@ -6932,10 +6985,11 @@ exit:
static void ath11k_mgmt_tx_compl_event(struct ath11k_base *ab, struct sk_buff *skb)
{
- struct wmi_mgmt_tx_compl_event tx_compl_param = {0};
+ struct wmi_tx_compl_event tx_compl_param = {0};
struct ath11k *ar;
- if (ath11k_pull_mgmt_tx_compl_param_tlv(ab, skb, &tx_compl_param) != 0) {
+ if (ath11k_pull_tx_compl_param_tlv(ab, skb, &tx_compl_param,
+ WMI_TAG_MGMT_TX_COMPL_EVENT) != 0) {
ath11k_warn(ab, "failed to extract mgmt tx compl event");
return;
}
@@ -6948,7 +7002,7 @@ static void ath11k_mgmt_tx_compl_event(s
goto exit;
}
- wmi_process_mgmt_tx_comp(ar, &tx_compl_param);
+ wmi_process_tx_comp(ar, &tx_compl_param);
ath11k_dbg(ab, ATH11K_DBG_MGMT,
"mgmt tx compl ev pdev_id %d, desc_id %d, status %d",
@@ -6959,6 +7013,36 @@ exit:
rcu_read_unlock();
}
+static void ath11k_qos_null_compl_event(struct ath11k_base *ab, struct sk_buff *skb)
+{
+ struct wmi_tx_compl_event tx_compl_param = {0};
+ struct ath11k *ar;
+
+ if (ath11k_pull_tx_compl_param_tlv(ab, skb, &tx_compl_param,
+ WMI_TAG_QOS_NULL_FRAME_TX_STATUS) != 0) {
+ ath11k_warn(ab, "failed to extract qos null tx compl event");
+ return;
+ }
+
+ rcu_read_lock();
+ ar = ath11k_mac_get_ar_by_pdev_id(ab, tx_compl_param.pdev_id);
+ if (!ar) {
+ ath11k_warn(ab, "invalid pdev id %d in qos_null_tx_compl_event\n",
+ tx_compl_param.pdev_id);
+ goto exit;
+ }
+
+ wmi_process_tx_comp(ar, &tx_compl_param);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "QOS null tx compl ev pdev_id %d, desc_id %d, status %d",
+ tx_compl_param.pdev_id, tx_compl_param.desc_id,
+ tx_compl_param.status);
+
+exit:
+ rcu_read_unlock();
+}
+
static struct ath11k *ath11k_get_ar_on_scan_abort(struct ath11k_base *ab,
u32 vdev_id)
{
@@ -7330,47 +7414,67 @@ static void ath11k_vdev_install_key_comp
rcu_read_unlock();
}
-static void ath11k_service_available_event(struct ath11k_base *ab, struct sk_buff *skb)
+static int ath11k_wmi_tlv_services_parser(struct ath11k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
{
- const void **tb;
const struct wmi_service_available_event *ev;
- int ret;
+ u32 *wmi_ext2_service_bitmap;
int i, j;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
- if (IS_ERR(tb)) {
- ret = PTR_ERR(tb);
- ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
- return;
- }
+ switch (tag) {
+ case WMI_TAG_SERVICE_AVAILABLE_EVENT:
+ ev = (struct wmi_service_available_event *)ptr;
+ for (i = 0, j = WMI_MAX_SERVICE;
+ i < WMI_SERVICE_SEGMENT_BM_SIZE32 && j < WMI_MAX_EXT_SERVICE;
+ i++) {
+ do {
+ if (ev->wmi_service_segment_bitmap[i] &
+ BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32))
+ set_bit(j, ab->wmi_ab.svc_map);
+ } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32);
+ }
- ev = tb[WMI_TAG_SERVICE_AVAILABLE_EVENT];
- if (!ev) {
- ath11k_warn(ab, "failed to fetch svc available ev");
- kfree(tb);
- return;
- }
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "wmi_ext_service_bitmap 0:0x%x, 1:0x%x, 2:0x%x, 3:0x%x",
+ ev->wmi_service_segment_bitmap[0],
+ ev->wmi_service_segment_bitmap[1],
+ ev->wmi_service_segment_bitmap[2],
+ ev->wmi_service_segment_bitmap[3]);
- /* TODO: Use wmi_service_segment_offset information to get the service
- * especially when more services are advertised in multiple sevice
- * available events.
- */
- for (i = 0, j = WMI_MAX_SERVICE;
- i < WMI_SERVICE_SEGMENT_BM_SIZE32 && j < WMI_MAX_EXT_SERVICE;
- i++) {
- do {
- if (ev->wmi_service_segment_bitmap[i] &
- BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32))
- set_bit(j, ab->wmi_ab.svc_map);
- } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32);
+ break;
+
+ case WMI_TAG_ARRAY_UINT32:
+ wmi_ext2_service_bitmap = (u32 *)ptr;
+ for (i = 0, j = WMI_MAX_EXT_SERVICE;
+ i < WMI_SERVICE_SEGMENT_BM_SIZE32 && j < WMI_MAX_EXT2_SERVICE;
+ i++) {
+ do {
+ if (wmi_ext2_service_bitmap[i] &
+ BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32))
+ set_bit(j, ab->wmi_ab.svc_map);
+ } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32);
+ }
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "wmi_ext2_service__bitmap 0:0x%x, 1:0x%x, 2:0x%x, 3:0x%x",
+ wmi_ext2_service_bitmap[0], wmi_ext2_service_bitmap[1],
+ wmi_ext2_service_bitmap[2], wmi_ext2_service_bitmap[3]);
+
+ break;
}
+ return 0;
+}
- ath11k_dbg(ab, ATH11K_DBG_WMI,
- "wmi_ext_service_bitmap 0:0x%x, 1:0x%x, 2:0x%x, 3:0x%x",
- ev->wmi_service_segment_bitmap[0], ev->wmi_service_segment_bitmap[1],
- ev->wmi_service_segment_bitmap[2], ev->wmi_service_segment_bitmap[3]);
+static void ath11k_service_available_event(struct ath11k_base *ab, struct sk_buff *skb)
+{
+ int ret = 0;
- kfree(tb);
+ ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len,
+ ath11k_wmi_tlv_services_parser,
+ NULL);
+ if (ret)
+ ath11k_warn(ab, "failed to parse services available tlv %d\n", ret);
}
static void ath11k_peer_assoc_conf_event(struct ath11k_base *ab, struct sk_buff *skb)
@@ -8439,6 +8543,9 @@ static void ath11k_wmi_tlv_op_rx(struct
case WMI_DIAG_EVENTID:
ath11k_wmi_diag_event(ab, skb);
break;
+ case WMI_QOS_NULL_FRAME_TX_COMPLETION_EVENTID:
+ ath11k_qos_null_compl_event(ab, skb);
+ break;
/* TODO: Add remaining events */
default:
ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -358,6 +358,7 @@ enum wmi_tlv_cmd_id {
WMI_BCN_OFFLOAD_CTRL_CMDID,
WMI_BSS_COLOR_CHANGE_ENABLE_CMDID,
WMI_VDEV_BCN_OFFLOAD_QUIET_CONFIG_CMDID,
+ WMI_QOS_NULL_FRAME_TX_SEND_CMDID,
WMI_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_GRP_BA_NEG),
WMI_ADDBA_SEND_CMDID,
WMI_ADDBA_STATUS_CMDID,
@@ -687,6 +688,8 @@ enum wmi_tlv_event_id {
WMI_MGMT_TX_COMPLETION_EVENTID,
WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID,
WMI_TBTTOFFSET_EXT_UPDATE_EVENTID,
+ WMI_HOST_SWBA_V2_EVENTID,
+ WMI_QOS_NULL_FRAME_TX_COMPLETION_EVENTID,
WMI_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_CMD(WMI_GRP_BA_NEG),
WMI_TX_ADDBA_COMPLETE_EVENTID,
WMI_BA_RSP_SSN_EVENTID,
@@ -1878,6 +1881,9 @@ enum wmi_tlv_tag {
/* TODO add all the missing cmds */
WMI_TAG_PDEV_PEER_PKTLOG_FILTER_CMD = 0x301,
WMI_TAG_PDEV_PEER_PKTLOG_FILTER_INFO,
+ /* TODO add all the missing cmds */
+ WMI_TAG_QOS_NULL_FRAME_TX_SEND = 0x3A6,
+ WMI_TAG_QOS_NULL_FRAME_TX_STATUS,
WMI_TAG_MAX
};
@@ -2136,8 +2142,21 @@ 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_CONFIGURE_ROAM_TRIGGER_PARAM_SUPPORT = 254,
+ WMI_TLV_SERVICE_CFR_TA_RA_AS_FP_SUPPORT = 255,
+ WMI_MAX_EXT_SERVICE = 256,
+
+ WMI_TLV_SERVICE_CFR_CAPTURE_COUNT_SUPPORT = 256,
+ WMI_TLV_SERVICE_OCV_SUPPORT = 257,
+ WMI_TLV_SERVICE_LL_STATS_PER_CHAN_RX_TX_TIME_SUPPORT = 258,
+ WMI_TLV_SERVICE_THERMAL_MULTI_CLIENT_SUPPORT = 259,
+ WMI_TLV_SERVICE_NAN_SEND_NAN_ENABLE_RESPONSE_TO_HOST = 260,
+ WMI_TLV_SERVICE_UNIFIED_LL_GET_STA_CMD_SUPPORT = 261,
+ WMI_TLV_SERVICE_FSE_CMEM_ALLOC_SUPPORT = 262,
+ WMI_TLV_SERVICE_PASSIVE_SCAN_START_TIME_ENHANCE = 263,
+ WMI_TLV_SERVICE_QOS_NULL_FRAME_TX_OVER_WMI = 264,
- WMI_MAX_EXT_SERVICE
+ WMI_MAX_EXT2_SERVICE
};
enum wmi_unit_test_cmdid {
@@ -3823,6 +3842,7 @@ struct wmi_scan_chan_list_cmd {
} __packed;
#define WMI_MGMT_SEND_DOWNLD_LEN 64
+#define WMI_QOS_NULL_SEND_BUF_LEN 64
#define WMI_TX_PARAMS_DWORD0_POWER GENMASK(7, 0)
#define WMI_TX_PARAMS_DWORD0_MCS_MASK GENMASK(19, 8)
@@ -3833,9 +3853,10 @@ struct wmi_scan_chan_list_cmd {
#define WMI_TX_PARAMS_DWORD1_BW_MASK GENMASK(14, 8)
#define WMI_TX_PARAMS_DWORD1_PREAMBLE_TYPE GENMASK(19, 15)
#define WMI_TX_PARAMS_DWORD1_FRAME_TYPE BIT(20)
-#define WMI_TX_PARAMS_DWORD1_RSVD GENMASK(31, 21)
+#define WMI_TX_PARAMS_DWORD1_CFR_CAPTURE BIT(21)
+#define WMI_TX_PARAMS_DWORD1_RSVD GENMASK(31, 22)
-struct wmi_mgmt_send_params {
+struct wmi_tx_send_params {
u32 tlv_header;
u32 tx_params_dword0;
u32 tx_params_dword1;
@@ -4606,7 +4627,7 @@ struct wmi_rssi_ctl_ext {
u32 rssi_ctl_ext[MAX_ANTENNA_EIGHT - ATH_MAX_ANTENNA];
};
-struct wmi_mgmt_tx_compl_event {
+struct wmi_tx_compl_event {
u32 desc_id;
u32 status;
u32 pdev_id;
@@ -5721,6 +5742,17 @@ enum ath11k_wmi_tpc_stats_ctl_array {
ATH11K_TPC_STATS_CTL_160ARRAY,
};
+struct wmi_qos_null_tx_cmd {
+ u32 tlv_header;
+ u32 vdev_id;
+ u32 desc_id;
+ u32 paddr_lo;
+ u32 paddr_hi;
+ u32 frame_len;
+ u32 buf_len;
+ u32 tx_params_valid;
+} __packed;
+
#define WMI_MAX_MEM_REQS 32
#define MAX_RADIOS 3
@@ -5736,7 +5768,7 @@ struct ath11k_wmi_base {
struct completion service_ready;
struct completion unified_ready;
- DECLARE_BITMAP(svc_map, WMI_MAX_EXT_SERVICE);
+ DECLARE_BITMAP(svc_map, WMI_MAX_EXT2_SERVICE);
wait_queue_head_t tx_credits_wq;
const struct wmi_peer_flags_map *peer_flags;
u32 num_mem_chunks;
@@ -5917,6 +5949,8 @@ int ath11k_wmi_cmd_send(struct ath11k_pd
struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len);
int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id,
struct sk_buff *frame);
+int ath11k_wmi_qos_null_send(struct ath11k *ar, u32 vdev_id, u32 buf_id,
+ struct sk_buff *frame);
int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id,
struct ieee80211_mutable_offsets *offs,
struct sk_buff *bcn);