wifi: ath12k: Add 4-address mode support for eth offload

Currently driver does not enable the hardware/firmware support for handling
4-address multicast frames in the Tx/Rx path when 8023_ENCAP_OFFLOAD is
enabled. Add the required support to ensure correct processing of multicast
traffic in 4-address mode.

Enable this functionality by setting the WMI_VDEV_PARAM_AP_ENABLE_NAWDS
vdev parameter when the 8023_ENCAP_OFFLOAD feature is active. Override
peer metadata values for 4-address multicast packet transmission by using
the station's ast_hash and ast_idx instead of vdev-level metadata,
and set HAL_TCL_DATA_CMD_INFO4_IDX_LOOKUP_OVERRIDE to indicate this
override.

Suppress firmware peer-map events for 4-address frames by setting the
WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE flag during WMI
initialization. This prevents inconsistencies in the host's peer list.

Add the IEEE80211_OFFLOAD_ENCAP_4ADDR VIF offload flag to notify mac80211
that 4-address Ethernet encapsulation offload is supported.

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-4-tamizh.raja@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
This commit is contained in:
Tamizh Chelvam Raja 2026-05-25 16:39:39 +05:30 committed by Jeff Johnson
parent 2f57f737db
commit 729cad3c3c
8 changed files with 73 additions and 14 deletions

View File

@ -355,6 +355,7 @@ struct ath12k_link_vif {
struct wiphy_work bcn_tx_work;
bool set_wds_vdev_param;
bool nawds_enabled;
};
struct ath12k_vif {
@ -495,6 +496,8 @@ struct ath12k_link_sta {
u8 addr[ETH_ALEN];
u16 tcl_metadata;
u16 ast_hash;
u16 ast_idx;
/* the following are protected by ar->data_lock */
u32 changed; /* IEEE80211_RC_* */

View File

@ -6576,6 +6576,7 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst
struct ath12k_dp_link_peer *peer;
struct ath12k_link_vif *arvif;
struct ath12k_link_sta *arsta;
struct ath12k_vif *ahvif;
struct ath12k_dp *dp;
unsigned long links;
struct ath12k *ar;
@ -6589,10 +6590,11 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst
continue;
arvif = arsta->arvif;
ahvif = arvif->ahvif;
ar = arvif->ar;
if (arvif->set_wds_vdev_param)
goto skip_use_4addr;
goto skip_nawds;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
"setting USE_4ADDR for peer %pM\n", arsta->addr);
@ -6607,7 +6609,21 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst
return ret;
}
skip_use_4addr:
if (ahvif->dp_vif.tx_encap_type != ATH12K_HW_TXRX_ETHERNET)
goto skip_nawds;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_AP_ENABLE_NAWDS,
WDS_EXT_ENABLE);
if (ret) {
ath12k_warn(ar->ab, "failed to set vdev %d nawds parameter: %d\n",
arvif->vdev_id, ret);
return ret;
}
arvif->nawds_enabled = true;
skip_nawds:
dp = ath12k_ab_to_dp(ar->ab);
spin_lock_bh(&dp->dp_lock);
peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arvif->vdev_id,
@ -10098,12 +10114,14 @@ static void ath12k_mac_update_vif_offload(struct ath12k_link_vif *arvif)
vif->offload_flags &= ~(IEEE80211_OFFLOAD_ENCAP_ENABLED |
IEEE80211_OFFLOAD_DECAP_ENABLED);
if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) {
ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_ETHERNET;
else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags))
vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR;
} else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) {
ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_RAW;
else
} else {
ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_NATIVE_WIFI;
}
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
param_id, ahvif->dp_vif.tx_encap_type);

View File

@ -224,6 +224,8 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,
HTT_TCL_META_DATA_PEER_ID) |
u16_encode_bits(0,
HTT_TCL_META_DATA_VALID_HTT);
arsta->ast_hash = peer->ast_hash;
arsta->ast_idx = peer->hw_peer_id;
peer->link_id = arsta->link_id;
/* Fill ML info into created peer */

View File

@ -57,6 +57,8 @@ static int ath12k_wifi7_dp_prepare_htt_metadata(struct sk_buff *skb)
return 0;
}
#define ATH12K_AST_HASH_MASK 0xF
/* TODO: Remove the export once this file is built with wifi7 ko */
int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif,
struct ath12k_link_sta *arsta, struct sk_buff *skb,
@ -78,6 +80,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a
struct ath12k_dp_vif *dp_vif = &ahvif->dp_vif;
struct ath12k_dp_link_vif *dp_link_vif;
struct dp_tx_ring *tx_ring;
struct ethhdr *eth = NULL;
u8 pool_id;
u8 hal_ring_id;
int ret;
@ -96,6 +99,9 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a
!ieee80211_is_data(hdr->frame_control))
return -EOPNOTSUPP;
if (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP)
eth = (struct ethhdr *)skb->data;
pool_id = skb_get_queue_mapping(skb) & (ATH12K_HW_MAX_QUEUES - 1);
/* Let the default ring selection be based on current processor
@ -124,9 +130,16 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a
ti.bank_id = dp_link_vif->bank_id;
ti.meta_data_flags = dp_link_vif->tcl_metadata;
ti.bss_ast_hash = dp_link_vif->ast_hash;
ti.bss_ast_idx = dp_link_vif->ast_idx;
if (ieee80211_has_a4(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr3) && arsta) {
if (eth && is_multicast_ether_addr(eth->h_dest) && arsta) {
ti.meta_data_flags = arsta->tcl_metadata;
ti.bss_ast_hash = arsta->ast_hash & ATH12K_AST_HASH_MASK;
ti.bss_ast_idx = arsta->ast_idx;
ti.lookup_override = true;
} else if (!eth && ieee80211_has_a4(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr3) && arsta) {
ti.meta_data_flags = arsta->tcl_metadata;
ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW);
}
@ -146,7 +159,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a
msdu_ext_desc = true;
}
if (gsn_valid) {
if (gsn_valid && !ti.lookup_override) {
/* Reset and Initialize meta_data_flags with Global Sequence
* Number (GSN) info.
*/
@ -154,6 +167,14 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a
u32_encode_bits(HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM,
HTT_TCL_META_DATA_TYPE) |
u32_encode_bits(mcbc_gsn, HTT_TCL_META_DATA_GLOBAL_SEQ_NUM);
/*
* Since NAWDS enabled for this vdev firmware expects
* this flag to be set for sending 3-address multicast frame.
*/
ti.meta_data_flags |=
u32_encode_bits(arvif->nawds_enabled,
HTT_TCL_META_DATA_GLOBAL_SEQ_HOST_INSPECTED);
}
ti.encap_type = ath12k_dp_tx_get_encap_type(ab, skb);
@ -164,11 +185,13 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a
ti.lmac_id = dp_link_vif->lmac_id;
ti.vdev_id = dp_link_vif->vdev_id;
if (gsn_valid)
ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID;
ti.bss_ast_hash = dp_link_vif->ast_hash;
ti.bss_ast_idx = dp_link_vif->ast_idx;
if (gsn_valid && !ti.lookup_override)
ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID;
else if (arvif->nawds_enabled && is_mcast && !ti.lookup_override)
ti.meta_data_flags |=
u32_encode_bits(1, HTT_TCL_META_DATA_HOST_INSPECTED_MISSION);
ti.dscp_tid_tbl_idx = 0;
if (skb->ip_summed == CHECKSUM_PARTIAL &&

View File

@ -59,7 +59,9 @@ void ath12k_wifi7_hal_tx_cmd_desc_setup(struct ath12k_base *ab,
le32_encode_bits(ti->lmac_id, HAL_TCL_DATA_CMD_INFO3_PMAC_ID) |
le32_encode_bits(ti->vdev_id, HAL_TCL_DATA_CMD_INFO3_VDEV_ID);
tcl_cmd->info4 = le32_encode_bits(ti->bss_ast_idx,
tcl_cmd->info4 = le32_encode_bits(ti->lookup_override,
HAL_TCL_DATA_CMD_INFO4_IDX_LOOKUP_OVERRIDE) |
le32_encode_bits(ti->bss_ast_idx,
HAL_TCL_DATA_CMD_INFO4_SEARCH_INDEX) |
le32_encode_bits(ti->bss_ast_hash,
HAL_TCL_DATA_CMD_INFO4_CACHE_SET_NUM);

View File

@ -34,6 +34,7 @@ struct hal_tx_info {
u8 dscp_tid_tbl_idx;
bool enable_mesh;
int bank_id;
bool lookup_override;
};
/* TODO: Check if the actual desc macros can be used instead */

View File

@ -4192,7 +4192,8 @@ ath12k_wmi_copy_resource_config(struct ath12k_base *ab,
cpu_to_le32(1 << WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT);
wmi_cfg->ema_max_vap_cnt = cpu_to_le32(tg_cfg->ema_max_vap_cnt);
wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period);
wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET);
wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET |
WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE);
}
static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi,

View File

@ -2334,6 +2334,14 @@ enum wmi_preamble {
WMI_VDEV_PREAMBLE_SHORT = 2,
};
/*
* This will be used to set for WMI_VDEV_PARAM_AP_ENABLE_NAWDS
* whenever 4addr station connects in wds offload case.
* This is for enabling multicast to unicast conversion support in
* firmware
*/
#define WDS_EXT_ENABLE 1
enum wmi_peer_4addr_allow_frame {
WMI_PEER_4ADDR_ALLOW_DATA_FRAME = 1,
WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME = 2,
@ -2559,6 +2567,7 @@ struct wmi_init_cmd {
#define WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION GENMASK(5, 4)
#define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5)
#define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9)
#define WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE BIT(18)
#define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18)
struct ath12k_wmi_resource_config_params {