Search Linux Wireless

[RFC v1 174/256] cl8k: add rx/rx_amsdu.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Viktor Barna <viktor.barna@xxxxxxxxxx>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx>
---
 .../net/wireless/celeno/cl8k/rx/rx_amsdu.c    | 257 ++++++++++++++++++
 1 file changed, 257 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/rx/rx_amsdu.c

diff --git a/drivers/net/wireless/celeno/cl8k/rx/rx_amsdu.c b/drivers/net/wireless/celeno/cl8k/rx/rx_amsdu.c
new file mode 100644
index 000000000000..512a317227a1
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/rx/rx_amsdu.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "rx/rx_amsdu.h"
+
+struct msduhdr {
+       u8 dest[ETH_ALEN];
+       u8 source[ETH_ALEN];
+       __be16 len;
+} __packed;
+
+enum rx_amsdu_error {
+       RX_AMSDU_ERR_CORRUPTED = 0x1,
+       RX_AMSDU_ERR_LENGTH = 0x2,
+};
+
+static void set_flag_amsdu_more(struct sk_buff *skb)
+{
+       struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+
+       rx_status->flag |= RX_FLAG_AMSDU_MORE;
+}
+
+static void clear_flag_amsdu_more(struct sk_buff *skb)
+{
+       struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+
+       rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
+}
+
+static void add_80211_hdr(struct cl_amsdu_rx_state *amsdu_rx_state,
+                         struct sk_buff *skb, struct sk_buff *first_skb)
+{
+       /* Copy the 802.11 header of the first skb */
+       struct ieee80211_hdr *hdr_first = (struct ieee80211_hdr *)(first_skb->data);
+       u32 hdrlen_first = ieee80211_hdrlen(hdr_first->frame_control);
+       u32 total_bytes = hdrlen_first + amsdu_rx_state->encrypt_len;
+
+       /* Total_bytes must be smaller than IPC_RXBUF_EXTRA_HEADROOM */
+       skb_push(skb, total_bytes);
+       memcpy(skb->data, first_skb->data, total_bytes);
+}
+
+static void copy_status(struct cl_amsdu_rx_state *amsdu_rx_state,
+                       struct sk_buff *skb, struct sk_buff *first_skb)
+{
+       struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_rx_status *rx_status_first = IEEE80211_SKB_RXCB(first_skb);
+
+       /* Copy rx_status from the first skb */
+       memcpy(rx_status, rx_status_first, sizeof(struct ieee80211_rx_status));
+
+       /* If it is the last sub-frame clear RX_FLAG_AMSDU_MORE */
+       if (amsdu_rx_state->msdu_remaining_cnt == 0)
+               rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
+}
+
+static void cl_rx_amsdu_set_state(struct cl_hw *cl_hw, struct sk_buff *skb, struct hw_rxhdr *rxhdr,
+                                 u8 sta_idx, u8 tid, u32 packet_len, u8 encrypt_len)
+{
+       struct cl_amsdu_rx_state *amsdu_rx_state = &cl_hw->amsdu_rx_state;
+
+       amsdu_rx_state->msdu_cnt = rxhdr->msdu_cnt;
+       amsdu_rx_state->msdu_remaining_cnt = rxhdr->msdu_cnt - 1;
+       amsdu_rx_state->msdu_dma_align = rxhdr->msdu_dma_align;
+       amsdu_rx_state->amsdu_error = 0;
+       amsdu_rx_state->encrypt_len = encrypt_len;
+       amsdu_rx_state->packet_len = packet_len;
+       amsdu_rx_state->rxhdr = rxhdr;
+       amsdu_rx_state->first_skb = skb;
+       amsdu_rx_state->sta_idx = sta_idx;
+       amsdu_rx_state->tid = tid;
+
+       __skb_queue_head(&cl_hw->amsdu_rx_state.frames, skb);
+}
+
+static void cl_rx_amsdu_set_state_error(struct cl_hw *cl_hw,
+                                       struct hw_rxhdr *rxhdr,
+                                       enum rx_amsdu_error err)
+{
+       struct cl_amsdu_rx_state *amsdu_rx_state = &cl_hw->amsdu_rx_state;
+
+       amsdu_rx_state->msdu_cnt = rxhdr->msdu_cnt;
+       amsdu_rx_state->msdu_remaining_cnt = rxhdr->msdu_cnt - 1;
+       amsdu_rx_state->amsdu_error = err;
+}
+
+static void cl_rx_amsdu_first_length_error(struct cl_hw *cl_hw, struct sk_buff *skb,
+                                          struct hw_rxhdr *rxhdr, u32 len)
+{
+       cl_dbg_err(cl_hw, "RX-AMSDU length error (1/%u) - tailroom=%d, len=%u\n",
+                  rxhdr->msdu_cnt, skb_tailroom(skb), len);
+
+       cl_rx_amsdu_set_state_error(cl_hw, rxhdr, RX_AMSDU_ERR_LENGTH);
+
+       cl_hw->rx_info.pkt_drop_amsdu_len_error++;
+       kfree_skb(skb);
+}
+
+static void cl_rx_amsdu_sub_length_error(struct cl_hw *cl_hw, struct sk_buff *skb, u32 len)
+{
+       struct cl_amsdu_rx_state *amsdu_rx_state = &cl_hw->amsdu_rx_state;
+       struct sk_buff *skb_tail;
+       u8 sub_cnt = amsdu_rx_state->msdu_cnt - amsdu_rx_state->msdu_remaining_cnt;
+
+       cl_dbg_err(cl_hw, "RX-AMSDU length error (%u/%u) - tailroom=%d, len=%u\n",
+                  sub_cnt, amsdu_rx_state->msdu_cnt, skb_tailroom(skb), len);
+
+       /* All remaining skbs in the AMSDU will be treated as errors */
+       amsdu_rx_state->amsdu_error = RX_AMSDU_ERR_LENGTH;
+
+       /* Clear RX_FLAG_AMSDU_MORE in the last success skb that was received */
+       skb_tail = skb_peek_tail(&amsdu_rx_state->frames);
+       clear_flag_amsdu_more(skb_tail);
+
+       cl_hw->rx_info.pkt_drop_sub_amsdu_len_error++;
+       kfree_skb(skb);
+}
+
+void cl_rx_amsdu_first(struct cl_hw *cl_hw, struct sk_buff *skb,
+                      struct hw_rxhdr *rxhdr, u8 sta_idx, u8 tid, u8 encrypt_len)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data);
+       u32 hdr_len = ieee80211_hdrlen(hdr->frame_control);
+       struct msduhdr *msdu_hdr = (struct msduhdr *)(skb->data + hdr_len + encrypt_len);
+       u32 packet_len = hdr_len + encrypt_len + sizeof(struct msduhdr) + ntohs(msdu_hdr->len);
+
+       if (skb_tailroom(skb) < packet_len) {
+               cl_rx_amsdu_first_length_error(cl_hw, skb, rxhdr, packet_len);
+               return;
+       }
+
+       /* Put the WLAN header + MSDU header + payload in the skb data */
+       skb_put(skb, packet_len);
+
+       cl_rx_amsdu_set_state(cl_hw, skb, rxhdr, sta_idx, tid, packet_len, encrypt_len);
+
+       /* Must be called after cl_rx_amsdu_set_state() */
+       if (cl_hw->amsdu_rx_state.msdu_remaining_cnt > 0)
+               set_flag_amsdu_more(skb);
+}
+
+bool cl_rx_amsdu_sub(struct cl_hw *cl_hw, struct sk_buff *skb)
+{
+       /*
+        * ----------------------------------------------------------
+        * | DMA padding 4 byte alignment | MSDU HDR | MSDU PAYLOAD |
+        *  ---------------------------------------------------------
+        */
+       struct cl_amsdu_rx_state *amsdu_rx_state = &cl_hw->amsdu_rx_state;
+       struct sk_buff *first_skb = amsdu_rx_state->first_skb;
+       struct msduhdr *msdu_hdr;
+       u32 packet_len;
+
+       /*
+        * Push the dma alignment to the reserved area, so that skb->data will
+        * point to the MSDU header
+        */
+       skb_reserve(skb, amsdu_rx_state->msdu_dma_align);
+
+       msdu_hdr = (struct msduhdr *)(skb->data);
+       packet_len = sizeof(struct msduhdr) + ntohs(msdu_hdr->len);
+
+       if (skb_tailroom(skb) < packet_len) {
+               cl_rx_amsdu_sub_length_error(cl_hw, skb, packet_len);
+               return false;
+       }
+
+       /* Put the MSDU HDR + MSDU PAYLOAD into the skb data area */
+       skb_put(skb, packet_len);
+
+       amsdu_rx_state->packet_len += packet_len;
+
+       add_80211_hdr(amsdu_rx_state, skb, first_skb);
+       copy_status(amsdu_rx_state, skb, first_skb);
+
+       /* Store the pointer to sta in the skb->sk field */
+       skb->sk = first_skb->sk;
+
+       __skb_queue_tail(&amsdu_rx_state->frames, skb);
+
+       return true;
+}
+
+void cl_rx_amsdu_first_corrupted(struct cl_hw *cl_hw, struct sk_buff *skb,
+                                struct hw_rxhdr *rxhdr)
+{
+       struct ieee80211_hdr *mac_hdr = (struct ieee80211_hdr *)(skb->data);
+
+       cl_dbg_verbose(cl_hw, "Corrupted RX-AMSDU (1/%u), dest_addr=%pM\n",
+                      rxhdr->msdu_cnt, mac_hdr->addr1);
+
+       cl_rx_amsdu_set_state_error(cl_hw, rxhdr, RX_AMSDU_ERR_CORRUPTED);
+
+       cl_hw->rx_info.pkt_drop_amsdu_corrupted++;
+       kfree_skb(skb);
+}
+
+void cl_rx_amsdu_sub_error(struct cl_hw *cl_hw, struct sk_buff *skb)
+{
+       struct cl_amsdu_rx_state *amsdu_rx_state = &cl_hw->amsdu_rx_state;
+       u8 sub_cnt = amsdu_rx_state->msdu_cnt - amsdu_rx_state->msdu_remaining_cnt;
+
+       if (amsdu_rx_state->amsdu_error & RX_AMSDU_ERR_CORRUPTED) {
+               cl_hw->rx_info.pkt_drop_sub_amsdu_corrupted++;
+
+               cl_dbg_verbose(cl_hw, "Corrupted RX-AMSDU (%u/%u)\n",
+                              sub_cnt, amsdu_rx_state->msdu_cnt);
+       } else if (amsdu_rx_state->amsdu_error & RX_AMSDU_ERR_LENGTH) {
+               cl_hw->rx_info.pkt_drop_sub_amsdu_len_error++;
+
+               cl_dbg_verbose(cl_hw, "RX-AMSDU length error (%u/%u)\n",
+                              sub_cnt, amsdu_rx_state->msdu_cnt);
+       }
+
+       kfree_skb(skb);
+}
+
+void cl_rx_amsdu_reset(struct cl_hw *cl_hw)
+{
+       /* Free pending frames */
+       __skb_queue_purge(&cl_hw->amsdu_rx_state.frames);
+
+       /* Reset RX A-MSDU state */
+       memset(&cl_hw->amsdu_rx_state, 0, sizeof(struct cl_amsdu_rx_state));
+
+       __skb_queue_head_init(&cl_hw->amsdu_rx_state.frames);
+}
+
+void cl_rx_amsdu_stats(struct cl_hw *cl_hw, u8 msdu_cnt)
+{
+       /*
+        * Update A-MSDU statistics
+        * msdu_cnt 1 - 128 is mapped to 0 - 127.
+        */
+       if (msdu_cnt <= RX_MAX_MSDU_IN_AMSDU)
+               cl_hw->rx_info.amsdu_cnt[msdu_cnt - 1]++;
+       else
+               cl_dbg_err(cl_hw, "Invalid msdu_cnt [%u]\n", msdu_cnt);
+}
+
+/* Only ieee80211_hw_set() is defined in mac80211.h */
+static inline void _ieee80211_hw_clear(struct ieee80211_hw *hw,
+                                      enum ieee80211_hw_flags flg)
+{
+       return __clear_bit(flg, hw->flags);
+}
+
+#define ieee80211_hw_clear(hw, flg) _ieee80211_hw_clear(hw, IEEE80211_HW_##flg)
+
+void cl_rx_amsdu_hw_en(struct ieee80211_hw *hw, bool rxamsdu_en)
+{
+       if (rxamsdu_en)
+               ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+       else
+               ieee80211_hw_clear(hw, SUPPORTS_AMSDU_IN_AMPDU);
+}
--
2.30.0

________________________________
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose.
________________________________





[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux