This merely adds some parsing, generation and
sanity checks with placeholders for real
code/functionality to be added later.

Signed-off-by: Michal Kazior <michal.kaz...@tieto.com>
---
 drivers/net/wireless/ath/ath10k/htt.h    |   5 +
 drivers/net/wireless/ath/ath10k/htt_rx.c | 198 ++++++++++++++++++++++++++++++-
 drivers/net/wireless/ath/ath10k/htt_tx.c |  53 +++++++++
 3 files changed, 255 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath10k/htt.h 
b/drivers/net/wireless/ath/ath10k/htt.h
index cb6d4fd687da..65dcd22f31df 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1752,6 +1752,11 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
                                u8 max_subfrms_ampdu,
                                u8 max_subfrms_amsdu);
 void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
+                            __le32 token,
+                            __le16 fetch_seq_num,
+                            struct htt_tx_fetch_record *records,
+                            size_t num_records);
 
 void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
                               bool is_mgmt);
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c 
b/drivers/net/wireless/ath/ath10k/htt_rx.c
index cc957a625605..be7dc88b3316 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -1982,6 +1982,198 @@ static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, 
struct sk_buff *skb)
        tasklet_schedule(&htt->rx_replenish_task);
 }
 
+static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
+                                                  const __le32 *resp_ids,
+                                                  int num_resp_ids)
+{
+       int i;
+       u32 resp_id;
+
+       ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm num_resp_ids 
%d\n",
+                  num_resp_ids);
+
+       for (i = 0; i < num_resp_ids; i++) {
+               resp_id = le32_to_cpu(resp_ids[i]);
+
+               ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm resp_id 
%u\n",
+                          resp_id);
+
+               /* TODO: free resp_id */
+       }
+}
+
+static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct htt_resp *resp = (struct htt_resp *)skb->data;
+       struct htt_tx_fetch_record *record;
+       size_t len;
+       size_t max_num_bytes;
+       size_t max_num_msdus;
+       const __le32 *resp_ids;
+       u16 num_records;
+       u16 num_resp_ids;
+       u16 peer_id;
+       u8 tid;
+       int i;
+
+       ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
+
+       len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_ind);
+       if (unlikely(skb->len < len)) {
+               ath10k_warn(ar, "received corrupted tx_fetch_ind event: buffer 
too short\n");
+               return;
+       }
+
+       num_records = le16_to_cpu(resp->tx_fetch_ind.num_records);
+       num_resp_ids = le16_to_cpu(resp->tx_fetch_ind.num_resp_ids);
+
+       len += sizeof(resp->tx_fetch_ind.records[0]) * num_records;
+       len += sizeof(resp->tx_fetch_ind.resp_ids[0]) * num_resp_ids;
+
+       if (unlikely(skb->len < len)) {
+               ath10k_warn(ar, "received corrupted tx_fetch_ind event: too 
many records/resp_ids\n");
+               return;
+       }
+
+       ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind num records %hu num 
resps %hu seq %hu\n",
+                  num_records, num_resp_ids,
+                  le16_to_cpu(resp->tx_fetch_ind.fetch_seq_num));
+
+       /* TODO: runtime sanity checks */
+
+       for (i = 0; i < num_records; i++) {
+               record = &resp->tx_fetch_ind.records[i];
+               peer_id = MS(le16_to_cpu(record->info),
+                            HTT_TX_FETCH_RECORD_INFO_PEER_ID);
+               tid = MS(le16_to_cpu(record->info),
+                        HTT_TX_FETCH_RECORD_INFO_TID);
+               max_num_msdus = le16_to_cpu(record->num_msdus);
+               max_num_bytes = le32_to_cpu(record->num_bytes);
+
+               ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch record %i 
peer_id %hu tid %hhu msdus %zu bytes %zu\n",
+                          i, peer_id, tid, max_num_msdus, max_num_bytes);
+
+               if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
+                   unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
+                       ath10k_warn(ar, "received out of range peer_id %hu tid 
%hhu\n",
+                                   peer_id, tid);
+                       continue;
+               }
+
+               /* TODO: dequeue and submit tx to device */
+       }
+
+       resp_ids = ath10k_htt_get_tx_fetch_ind_resp_ids(&resp->tx_fetch_ind);
+       ath10k_htt_rx_tx_fetch_resp_id_confirm(ar, resp_ids, num_resp_ids);
+
+       /* TODO: generate and send fetch response to device */
+}
+
+static void ath10k_htt_rx_tx_fetch_confirm(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       const struct htt_resp *resp = (void *)skb->data;
+       size_t len;
+       int num_resp_ids;
+
+       ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm\n");
+
+       len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_confirm);
+       if (unlikely(skb->len < len)) {
+               ath10k_warn(ar, "received corrupted tx_fetch_confirm event: 
buffer too short\n");
+               return;
+       }
+
+       num_resp_ids = le16_to_cpu(resp->tx_fetch_confirm.num_resp_ids);
+       len += sizeof(resp->tx_fetch_confirm.resp_ids[0]) * num_resp_ids;
+
+       if (unlikely(skb->len < len)) {
+               ath10k_warn(ar, "received corrupted tx_fetch_confirm event: 
resp_ids buffer overflow\n");
+               return;
+       }
+
+       ath10k_htt_rx_tx_fetch_resp_id_confirm(ar,
+                                              resp->tx_fetch_confirm.resp_ids,
+                                              num_resp_ids);
+}
+
+static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       const struct htt_resp *resp = (void *)skb->data;
+       const struct htt_tx_mode_switch_record *record;
+       size_t len;
+       size_t num_records;
+       enum htt_tx_mode_switch_mode mode;
+       bool enable;
+       u16 info0;
+       u16 info1;
+       u16 threshold;
+       u16 peer_id;
+       u8 tid;
+       int i;
+
+       ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx mode switch ind\n");
+
+       len = sizeof(resp->hdr) + sizeof(resp->tx_mode_switch_ind);
+       if (unlikely(skb->len < len)) {
+               ath10k_warn(ar, "received corrupted tx_mode_switch_ind event: 
buffer too short\n");
+               return;
+       }
+
+       info0 = le16_to_cpu(resp->tx_mode_switch_ind.info0);
+       info1 = le16_to_cpu(resp->tx_mode_switch_ind.info1);
+
+       enable = !!(info0 & HTT_TX_MODE_SWITCH_IND_INFO0_ENABLE);
+       num_records = MS(info0, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
+       mode = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_MODE);
+       threshold = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
+
+       ath10k_dbg(ar, ATH10K_DBG_HTT,
+                  "htt rx tx mode switch ind info0 0x%04hx info1 0x%04hx 
enable %d num records %zd mode %d threshold %hu\n",
+                  info0, info1, enable, num_records, mode, threshold);
+
+       len += sizeof(resp->tx_mode_switch_ind.records[0]) * num_records;
+
+       if (unlikely(skb->len < len)) {
+               ath10k_warn(ar, "received corrupted tx_mode_switch_mode_ind 
event: too many records\n");
+               return;
+       }
+
+       switch (mode) {
+       case HTT_TX_MODE_SWITCH_PUSH:
+       case HTT_TX_MODE_SWITCH_PUSH_PULL:
+               break;
+       default:
+               ath10k_warn(ar, "received invalid tx_mode_switch_mode_ind mode 
%d, ignoring\n",
+                           mode);
+               return;
+       }
+
+       if (!enable)
+               return;
+
+       /* TODO: apply configuration */
+
+       for (i = 0; i < num_records; i++) {
+               record = &resp->tx_mode_switch_ind.records[i];
+               info0 = le16_to_cpu(record->info0);
+               peer_id = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_PEER_ID);
+               tid = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_TID);
+
+               if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
+                   unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
+                       ath10k_warn(ar, "received out of range peer_id %hu tid 
%hhu\n",
+                                   peer_id, tid);
+                       continue;
+               }
+
+               /* TODO: apply configuration */
+       }
+
+       /* TODO: apply configuration */
+}
+
 void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ath10k_htt *htt = &ar->htt;
@@ -2124,9 +2316,13 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, 
struct sk_buff *skb)
        case HTT_T2H_MSG_TYPE_AGGR_CONF:
                break;
        case HTT_T2H_MSG_TYPE_TX_FETCH_IND:
+               ath10k_htt_rx_tx_fetch_ind(ar, skb);
+               break;
        case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
+               ath10k_htt_rx_tx_fetch_confirm(ar, skb);
+               break;
        case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND:
-               /* TODO: Implement pull-push logic */
+               ath10k_htt_rx_tx_mode_switch_ind(ar, skb);
                break;
        case HTT_T2H_MSG_TYPE_EN_STATS:
        default:
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c 
b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 860661d3812f..225f0561b3fd 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -526,6 +526,59 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
        return 0;
 }
 
+int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
+                            __le32 token,
+                            __le16 fetch_seq_num,
+                            struct htt_tx_fetch_record *records,
+                            size_t num_records)
+{
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       u16 resp_id;
+       int len = 0;
+       int ret;
+
+       len += sizeof(cmd->hdr);
+       len += sizeof(cmd->tx_fetch_resp);
+       len += sizeof(cmd->tx_fetch_resp.records[0]) * num_records;
+
+       skb = ath10k_htc_alloc_skb(ar, len);
+       if (!skb)
+               return -ENOMEM;
+
+       resp_id = 0; /* TODO: allocate resp_id */
+       ret = 0;
+       if (ret)
+               goto err_free_skb;
+
+       skb_put(skb, len);
+       cmd = (struct htt_cmd *)skb->data;
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FETCH_RESP;
+       cmd->tx_fetch_resp.resp_id = cpu_to_le16(resp_id);
+       cmd->tx_fetch_resp.fetch_seq_num = fetch_seq_num;
+       cmd->tx_fetch_resp.num_records = cpu_to_le16(num_records);
+       cmd->tx_fetch_resp.token = token;
+
+       memcpy(cmd->tx_fetch_resp.records, records,
+              sizeof(records[0]) * num_records);
+
+       ret = ath10k_htc_send(&ar->htc, ar->htt.eid, skb);
+       if (ret) {
+               ath10k_warn(ar, "failed to submit htc command: %d\n", ret);
+               goto err_free_resp_id;
+       }
+
+       return 0;
+
+err_free_resp_id:
+       (void)resp_id; /* TODO: free resp_id */
+
+err_free_skb:
+       dev_kfree_skb_any(skb);
+
+       return ret;
+}
+
 static u8 ath10k_htt_tx_get_vdev_id(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to