]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: ath12k: Add support for 4-address frame notification
authorTamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
Mon, 25 May 2026 11:09:41 +0000 (16:39 +0530)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Mon, 1 Jun 2026 16:58:06 +0000 (09:58 -0700)
mac80211 currently relies on receiving 4-address frames from connected
stations to trigger AP_VLAN interface creation. However, when ethernet
encapsulation offload is enabled, mac80211 only receives 802.3 frames
and cannot differentiate between 3-address and 4-address formats,
preventing AP_VLAN creation.

Enable mac80211 to detect 4-address traffic by converting 802.3 frames
back into 802.11 frames in the driver and setting the FROM_DS and TO_DS
bits using the RX_MSDU_END_INFO5_FROM_DS and RX_MSDU_END_INFO5_TO_DS
fields. This restores 4-address frame visibility to mac80211 and allows
it to trigger AP_VLAN interface creation.

Skip this frame conversion once the AP_VLAN interface is created and the
station is attached to it.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1

Signed-off-by: Tamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Link: https://patch.msgid.link/20260525110942.2890212-6-tamizh.raja@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/dp_peer.h
drivers/net/wireless/ath/ath12k/dp_rx.c
drivers/net/wireless/ath/ath12k/dp_rx.h
drivers/net/wireless/ath/ath12k/hal.h
drivers/net/wireless/ath/ath12k/mac.c
drivers/net/wireless/ath/ath12k/peer.c
drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c
drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c
drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c

index 113b8040010fa3e1d3b6c2cec850a459ce47be96..f5067e66f1e182ec1cdd45e6173725150723faff 100644 (file)
@@ -143,6 +143,8 @@ struct ath12k_dp_peer {
        struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS];
        struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1];
        struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1];
+
+       bool use_4addr;
 };
 
 struct ath12k_dp_link_peer *
index 8b69afc69ad412dca3306136c30a1f1d45609b2f..06e74124e57eb91fc02a730e333dbac6c69bf049 100644 (file)
@@ -1142,7 +1142,8 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev,
 void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu,
                            enum hal_encrypt_type enctype,
                            bool decrypted,
-                           struct hal_rx_desc_data *rx_info)
+                           struct hal_rx_desc_data *rx_info,
+                           struct ath12k_dp_peer *peer)
 {
        enum ath12k_dp_rx_decap_type decap_type = rx_info->decap_type;
        struct ethhdr *ehdr;
@@ -1166,6 +1167,13 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu
                        break;
                }
 
+               if (peer && !peer->use_4addr &&
+                   rx_info->is_from_ds && rx_info->is_to_ds) {
+                       ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
+                                                  decap_type);
+                       break;
+               }
+
                /* PN for mcast packets will be validated in mac80211;
                 * remove eth header and add 802.11 header.
                 */
index 55a31e669b3b062ad8659257c0d5f51d0d65ed26..0660db070e9217a853e7dca6ac1cce0c96b4dbcc 100644 (file)
@@ -190,7 +190,8 @@ void ath12k_dp_extract_rx_desc_data(struct ath12k_hal *hal,
 void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu,
                            enum hal_encrypt_type enctype,
                            bool decrypted,
-                           struct hal_rx_desc_data *rx_info);
+                           struct hal_rx_desc_data *rx_info,
+                           struct ath12k_dp_peer *peer);
 void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struct *napi,
                               struct sk_buff *msdu,
                               struct hal_rx_desc_data *rx_info);
index bf4f7dbae86693d5a583f8a1e2cd2496ff467a64..21c551d8b24814fa97ac4a140b735b21c9a92ca1 100644 (file)
@@ -738,7 +738,9 @@ struct hal_rx_desc_data {
            addr2_present:1,
            is_mcbc:1,
            seq_ctl_valid:1,
-           fc_valid:1;
+           fc_valid:1,
+           is_to_ds:1,
+           is_from_ds:1;
        u16 msdu_len;
        u16 peer_id;
        u16 seq_no;
index 2c1eadf548ac4e69956b6cb2794e60e11355b502..a6e4b660da81c9075dda50e10ed61ec8127e8089 100644 (file)
@@ -6630,6 +6630,7 @@ skip_nawds:
                                                                 arsta->addr);
                if (peer && peer->dp_peer) {
                        peer->dp_peer->ucast_ra_only = true;
+                       peer->dp_peer->use_4addr = true;
                } else {
                        spin_unlock_bh(&dp->dp_lock);
                        ath12k_warn(ar->ab, "failed to find DP peer for %pM\n",
index b5a0ba149a284488abd0221a335a6b0ac3d810fa..c222bdaa333c523d939256387daac7c95343c01f 100644 (file)
@@ -255,6 +255,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,
                                                 ar->hw_link_id);
        }
 
+       if (vif->type == NL80211_IFTYPE_AP && peer->dp_peer)
+               peer->dp_peer->ucast_ra_only = true;
+
        return ret;
 }
 
index a5e290edaa898c3f77b94b217a455ae4a5a105f1..5c7eaaf200f93fb77dcc6d31901b33350a614bc0 100644 (file)
@@ -323,9 +323,9 @@ static void ath12k_wifi7_dp_rx_h_csum_offload(struct sk_buff *msdu,
                           CHECKSUM_NONE : CHECKSUM_UNNECESSARY;
 }
 
-static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev,
-                                     struct sk_buff *msdu,
-                                     struct hal_rx_desc_data *rx_info)
+static int ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev,
+                                    struct sk_buff *msdu,
+                                    struct hal_rx_desc_data *rx_info)
 {
        struct ath12k_skb_rxcb *rxcb;
        enum hal_encrypt_type enctype;
@@ -347,6 +347,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev,
 
        peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id);
        if (peer) {
+               /*
+                * Drop the 3-address multicast packet from 4-address
+                * peer To avoid receiving the duplicate multicast packet
+                * Specifically from AP interface in 3-address format
+                */
+               if (rxcb->is_mcbc &&
+                   rx_info->decap_type == DP_RX_DECAP_TYPE_ETHERNET2_DIX) {
+                       if (peer->use_4addr &&
+                           !(rx_info->is_from_ds && rx_info->is_to_ds))
+                               return -EINVAL;
+               }
+
                /* resetting mcbc bit because mcbc packets are unicast
                 * packets only for AP as STA sends unicast packets.
                 */
@@ -387,15 +399,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev,
        }
 
        ath12k_wifi7_dp_rx_h_csum_offload(msdu, rx_info);
-       ath12k_dp_rx_h_undecap(dp_pdev, msdu, enctype, is_decrypted, rx_info);
+       ath12k_dp_rx_h_undecap(dp_pdev, msdu, enctype, is_decrypted, rx_info,
+                              peer);
 
        if (!is_decrypted || rx_info->is_mcbc)
-               return;
+               return 0;
 
        if (rx_info->decap_type != DP_RX_DECAP_TYPE_ETHERNET2_DIX) {
                hdr = (void *)msdu->data;
                hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
        }
+
+       return 0;
 }
 
 static int ath12k_wifi7_dp_rx_msdu_coalesce(struct ath12k_hal *hal,
@@ -554,7 +569,9 @@ static int ath12k_wifi7_dp_rx_process_msdu(struct ath12k_pdev_dp *dp_pdev,
        }
 
        ath12k_dp_rx_h_ppdu(dp_pdev, rx_info);
-       ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info);
+       ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info);
+       if (ret)
+               goto free_out;
 
        rx_info->rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED;
 
@@ -1035,7 +1052,7 @@ mic_fail:
 
        ath12k_dp_rx_h_ppdu(dp_pdev, rx_info);
        ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, true,
-                              rx_info);
+                              rx_info, NULL);
        ieee80211_rx(ath12k_pdev_dp_to_hw(dp_pdev), msdu);
        return -EINVAL;
 }
@@ -1590,6 +1607,7 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev,
        u8 l3pad_bytes = rx_info->l3_pad_bytes;
        struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
        u32 hal_rx_desc_sz = dp->ab->hal.hal_desc_sz;
+       int ret;
 
        if (!rxcb->is_frag && ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE)) {
                /* First buffer will be freed by the caller, so deduct it's length */
@@ -1634,7 +1652,9 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev,
                return -EINVAL;
 
        ath12k_dp_rx_h_ppdu(dp_pdev, rx_info);
-       ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info);
+       ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info);
+       if (ret)
+               return ret;
 
        rxcb->tid = rx_info->tid;
 
@@ -1680,7 +1700,7 @@ static bool ath12k_wifi7_dp_rx_h_tkip_mic_err(struct ath12k_pdev_dp *dp_pdev,
                                     RX_FLAG_DECRYPTED);
 
        ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, false,
-                              rx_info);
+                              rx_info, NULL);
        return false;
 }
 
index 1eefb931a853ee3f934a9d96eb01264ec19363f0..8cebb229ebed872e6859dacee691bb4c1387ea1f 100644 (file)
@@ -184,6 +184,20 @@ static u8 ath12k_hal_rx_desc_get_l3_pad_bytes_qcc2072(struct hal_rx_desc *desc)
                             RX_MSDU_END_INFO5_L3_HDR_PADDING);
 }
 
+static inline
+u8 ath12k_wifi7_hal_rx_h_from_ds_qcc2072(struct hal_rx_desc *desc)
+{
+       return le16_get_bits(desc->u.qcc2072.msdu_end.info5,
+                            RX_MSDU_END_INFO5_FROM_DS);
+}
+
+static inline
+u8 ath12k_wifi7_hal_rx_h_to_ds_qcc2072(struct hal_rx_desc *desc)
+{
+       return le16_get_bits(desc->u.qcc2072.msdu_end.info5,
+                            RX_MSDU_END_INFO5_TO_DS);
+}
+
 static u32 ath12k_hal_rx_desc_get_mpdu_start_tag_qcc2072(struct hal_rx_desc *desc)
 {
        return le32_get_bits(desc->u.qcc2072.mpdu_start_tag,
@@ -397,6 +411,8 @@ static void ath12k_hal_extract_rx_desc_data_qcc2072(struct hal_rx_desc_data *rx_
        rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcc2072(rx_desc);
        rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcc2072(ldesc);
        rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcc2072(rx_desc);
+       rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcc2072(rx_desc);
+       rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcc2072(rx_desc);
        rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_qcc2072(rx_desc);
        rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_qcc2072(rx_desc);
        rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_qcc2072(rx_desc);
index ba9ce1e718e8870d1363a6e78340525c102c8b4d..9d5180ef83b4846b985d4b4d5d12fa3c8697c431 100644 (file)
@@ -699,6 +699,20 @@ u8 ath12k_hal_rx_desc_get_mpdu_tid_qcn9274(struct hal_rx_desc *desc)
                             RX_MSDU_END_INFO5_TID);
 }
 
+static inline
+u8 ath12k_wifi7_hal_rx_h_from_ds_qcn9274(struct hal_rx_desc *desc)
+{
+       return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5,
+                            RX_MSDU_END_INFO5_FROM_DS);
+}
+
+static inline
+u8 ath12k_wifi7_hal_rx_h_to_ds_qcn9274(struct hal_rx_desc *desc)
+{
+       return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5,
+                            RX_MSDU_END_INFO5_TO_DS);
+}
+
 static inline
 u16 ath12k_hal_rx_desc_get_mpdu_peer_id_qcn9274(struct hal_rx_desc *desc)
 {
@@ -914,6 +928,8 @@ void ath12k_hal_extract_rx_desc_data_qcn9274(struct hal_rx_desc_data *rx_desc_da
        rx_desc_data->seq_ctl_valid =
                ath12k_hal_rx_desc_get_mpdu_seq_ctl_vld_qcn9274(rx_desc);
        rx_desc_data->fc_valid = ath12k_hal_rx_desc_get_mpdu_fc_valid_qcn9274(rx_desc);
+       rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcn9274(rx_desc);
+       rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcn9274(rx_desc);
        rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcn9274(rx_desc);
        rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcn9274(ldesc);
        rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcn9274(rx_desc);
index e64e512cac7df4bbcae1626a3fe9e8c5529d5991..efbbc1cbd3e42d0699234bcabe1471f9e0cca59c 100644 (file)
@@ -280,6 +280,20 @@ u8 ath12k_hal_rx_desc_get_l3_pad_bytes_wcn7850(struct hal_rx_desc *desc)
                            RX_MSDU_END_INFO5_L3_HDR_PADDING);
 }
 
+static inline
+u8 ath12k_wifi7_hal_rx_h_from_ds_wcn7850(struct hal_rx_desc *desc)
+{
+       return le16_get_bits(desc->u.wcn7850.msdu_end.info5,
+                            RX_MSDU_END_INFO5_FROM_DS);
+}
+
+static inline
+u8 ath12k_wifi7_hal_rx_h_to_ds_wcn7850(struct hal_rx_desc *desc)
+{
+       return le16_get_bits(desc->u.wcn7850.msdu_end.info5,
+                            RX_MSDU_END_INFO5_TO_DS);
+}
+
 static inline
 bool ath12k_hal_rx_desc_encrypt_valid_wcn7850(struct hal_rx_desc *desc)
 {
@@ -599,6 +613,8 @@ void ath12k_hal_extract_rx_desc_data_wcn7850(struct hal_rx_desc_data *rx_desc_da
        rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_wcn7850(rx_desc);
        rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_wcn7850(ldesc);
        rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_wcn7850(rx_desc);
+       rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_wcn7850(rx_desc);
+       rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_wcn7850(rx_desc);
        rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_wcn7850(rx_desc);
        rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_wcn7850(rx_desc);
        rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_wcn7850(rx_desc);