[PATCH v3 RESEND] ath11k: add support for dynamic vlan (ap/vlan)

Vasanthakumar Thiagarajan quic_vthiagar at quicinc.com
Thu Jun 12 00:25:41 PDT 2025



On 6/5/2025 4:52 AM, David Rapaň wrote:
> ath11k currently lacks support for dynamic vlan (AP/VLAN), so make
> '__ath11k_mac_register' in dp_tx to register 'NL80211_IFTYPE_AP_VLAN'
> as available interface mode.
> 
> Make 'ath11k_dp_tx' in dp_tx to add metadata info to notify firmware
> that the multicast/broadcast packets are encrypted in software.
> 
> Make '__ieee80211_subif_start_xmit' in tx to offload encapsulation
> for VLAN unicast packets using 8023 xmit path instead of current
> 80211 xmit path.
> 
> Tested, long-term in production environment using OpenWrt platform
> installed on multiple AX3600 with FT:
>   Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.9.0.1-01385-QCAHKSWPL_SILICONZ-1
>   Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.9.0.1-01977-QCAHKSWPL_SILICONZ-1
> 
> Co-developed-by: Seevalamuthu Mariappan <seevalam at codeaurora.org>
> Signed-off-by: Seevalamuthu Mariappan <seevalam at codeaurora.org>
> Signed-off-by: David Rapan <david at rapan.cz>
> ---
> v1 -> v2: Move 'cpu_to_be16(ETH_P_PAE)' to earlier condition and add
>   'ieee80211_is_qos_nullfunc' check so QoS NULL Data frames are
>   properly send as open type frames with TID 7 in ethernet encap
>   instead of QoS Data with TID 0.
> v2 -> v3: Make 'hal_tx_msdu_metadata' struct to comply with current
>   coding standard (no use of bit masks in '__packed').
>   Make key retrieval in 8023 xmit path from
>   '__ieee80211_subif_start_xmit' to be on pair with the standard flow in
>   'ieee80211_subif_start_xmit_8023' where it also falls back to
>   'default_unicast_key'.
> 
>   drivers/net/wireless/ath/ath11k/core.h     |   1 +
>   drivers/net/wireless/ath/ath11k/dp_tx.c    |  83 +++++++++-
>   drivers/net/wireless/ath/ath11k/hal_desc.h | 160 ++++++++++++++++++++
>   drivers/net/wireless/ath/ath11k/mac.c      |   3 +
>   net/mac80211/tx.c                          |  15 +++
>   5 files changed, 260 insertions(+), 2 deletions(-)
> 
> base-commit: ff8069c7cf3eb0fcd53adebdf341b6aaa98bdd3b
> 
> --- a/drivers/net/wireless/ath/ath11k/core.h
> +++ b/drivers/net/wireless/ath/ath11k/core.h
> @@ -120,6 +120,7 @@ struct ath11k_skb_cb {
>   	u32 cipher;
>   	struct ath11k *ar;
>   	struct ieee80211_vif *vif;
> +	u32 pkt_offset;
>   } __packed;
>   
>   struct ath11k_skb_rxcb {
> --- a/drivers/net/wireless/ath/ath11k/dp_tx.c
> +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
> @@ -79,6 +79,43 @@ enum hal_encrypt_type ath11k_dp_tx_get_e
>   	}
>   }
>   
> +#define HTT_META_DATA_ALIGNMENT	0x8
> +
> +static int ath11k_dp_metadata_align_skb(struct sk_buff *skb, u8 align_len)
> +{
> +	if (unlikely(skb_cow_head(skb, align_len)))
> +		return -ENOMEM;
> +
> +	skb_push(skb, align_len);
> +	memset(skb->data, 0, align_len);
> +	return 0;
> +}
> +
> +static int ath11k_dp_prepare_htt_metadata(struct sk_buff *skb,
> +					 u8 *htt_metadata_size)
> +{
> +	u8 htt_desc_size;
> +	/* Size rounded of multiple of 8 bytes */
> +	u8 htt_desc_size_aligned;
> +	int ret;
> +	struct hal_tx_msdu_metadata *desc_ext;
> +
> +	htt_desc_size = sizeof(struct hal_tx_msdu_metadata);
> +	htt_desc_size_aligned = ALIGN(htt_desc_size, HTT_META_DATA_ALIGNMENT);
> +
> +	ret = ath11k_dp_metadata_align_skb(skb, htt_desc_size_aligned);
> +	if (unlikely(ret))
> +		return ret;
> +
> +	desc_ext = (struct hal_tx_msdu_metadata *)skb->data;
> +	desc_ext->info0 = le32_encode_bits(1, HAL_TX_MSDU_METADATA_INFO0_ENCRYPT_FLAG) |
> +			  le32_encode_bits(0, HAL_TX_MSDU_METADATA_INFO0_ENCRYPT_TYPE) |
> +			  le32_encode_bits(1,
> +					   HAL_TX_MSDU_METADATA_INFO0_HOST_TX_DESC_POOL);
> +	*htt_metadata_size = htt_desc_size_aligned;
> +	return 0;
> +}
> +
>   int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif,
>   		 struct ath11k_sta *arsta, struct sk_buff *skb)
>   {
> @@ -97,6 +134,9 @@ int ath11k_dp_tx(struct ath11k *ar, stru
>   	u32 ring_selector = 0;
>   	u8 ring_map = 0;
>   	bool tcl_ring_retry;
> +	bool is_diff_encap = false;
> +	u8 align_pad;
> +	u8 htt_meta_size = 0;
>   
>   	if (unlikely(test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)))
>   		return -ESHUTDOWN;
> @@ -189,7 +229,12 @@ tcl_ring_sel:
>   
>   	switch (ti.encap_type) {
>   	case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI:
> -		ath11k_dp_tx_encap_nwifi(skb);
> +		if ((arvif->vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) &&
> +		    (skb->protocol == cpu_to_be16(ETH_P_PAE) ||
> +		     ieee80211_is_qos_nullfunc(hdr->frame_control)))
> +			is_diff_encap = true;
> +		else
> +			ath11k_dp_tx_encap_nwifi(skb);

I dont think this is related to dynamic AP_VLAN

>   		break;
>   	case HAL_TCL_ENCAP_TYPE_RAW:
>   		if (!test_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags)) {
> @@ -208,6 +253,33 @@ tcl_ring_sel:
>   		goto fail_remove_idr;
>   	}
>   
> +	/* Add metadata for software encryption of vlan group traffic */
> +	if ((!test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) &&
> +	     !(info->control.flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
> +	     !info->control.hw_key && ieee80211_has_protected(hdr->frame_control)) ||
> +	    is_diff_encap) {
> +		/* HW requirement is that metadata should always point to a
> +		 * 8-byte aligned address. So we add alignment pad to start of
> +		 * buffer. HTT Metadata should be ensured to be multiple of 8-bytes
> +		 * to get 8-byte aligned start address along with align_pad added
> +		 */
> +		align_pad = ((unsigned long)skb->data) & (HTT_META_DATA_ALIGNMENT - 1);
> +		ret = ath11k_dp_metadata_align_skb(skb, align_pad);
> +		if (unlikely(ret))
> +			goto fail_remove_idr;
> +
> +		ti.pkt_offset += align_pad;
> +		ret = ath11k_dp_prepare_htt_metadata(skb, &htt_meta_size);
> +		if (unlikely(ret))
> +			goto fail_remove_idr;
> +
> +		ti.pkt_offset += htt_meta_size;
> +		ti.meta_data_flags |= HTT_TCL_META_DATA_VALID_HTT;
> +		ti.flags0 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TO_FW, 1);
> +		ti.encap_type = HAL_TCL_ENCAP_TYPE_RAW;
> +		ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN;
> +	}
> +
>   	ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE);
>   	if (unlikely(dma_mapping_error(ab->dev, ti.paddr))) {
>   		atomic_inc(&ab->soc_stats.tx_err.misc_fail);
> @@ -216,7 +288,8 @@ tcl_ring_sel:
>   		goto fail_remove_idr;
>   	}
>   
> -	ti.data_len = skb->len;
> +	ti.data_len = skb->len - ti.pkt_offset;
> +	skb_cb->pkt_offset = ti.pkt_offset;
>   	skb_cb->paddr = ti.paddr;
>   	skb_cb->vif = arvif->vif;
>   	skb_cb->ar = ar;
> @@ -272,6 +345,8 @@ fail_unmap_dma:
>   	dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE);
>   
>   fail_remove_idr:
> +	if (ti.pkt_offset)
> +		skb_pull(skb, ti.pkt_offset);
>   	spin_lock_bh(&tx_ring->tx_idr_lock);
>   	idr_remove(&tx_ring->txbuf_idr,
>   		   FIELD_GET(DP_TX_DESC_ID_MSDU_ID, ti.desc_id));
> @@ -348,6 +423,10 @@ ath11k_dp_tx_htt_tx_complete_buf(struct
>   		return;
>   	}
>   
> +	if (skb_cb->pkt_offset)
> +		/* Removing the alignment and htt meta data */
> +		skb_pull(msdu, skb_cb->pkt_offset);
> +
>   	memset(&info->status, 0, sizeof(info->status));
>   
>   	if (ts->acked) {
> --- a/drivers/net/wireless/ath/ath11k/hal_desc.h
> +++ b/drivers/net/wireless/ath/ath11k/hal_desc.h
> @@ -2490,5 +2490,165 @@
>    *		A count value that indicates the number of times the producer of
>    *		entries into this Ring has looped around the ring.
>    */
> +
> +#define HAL_TX_MSDU_METADATA_INFO0_ENCRYPT_FLAG		BIT(8)
> +#define HAL_TX_MSDU_METADATA_INFO0_ENCRYPT_TYPE		GENMASK(16, 15)
> +#define HAL_TX_MSDU_METADATA_INFO0_HOST_TX_DESC_POOL	BIT(31)
> +
> +struct hal_tx_msdu_metadata {
> +	__le32 info0;
> +	__le32 rsvd0[6];
> +} __packed;
> +
> +/* hal_tx_msdu_metadata
> + *
> + * valid_pwr
> + *		If set, tx pwr spec is valid
> + *
> + * valid_mcs_mask
> + *		If set, tx MCS mask is valid
> + *
> + * valid_nss_mask
> + *		If set, tx Nss mask is valid
> + *
> + * valid_preamble_type
> + *		If set, tx preamble spec is valid
> + *
> + * valid_retries
> + *		If set, tx retries spec is valid
> + *
> + * valid_bw_info
> + *		If set, tx dyn_bw and bw_mask are valid
> + *
> + * valid_guard_interval
> + *		If set, tx guard intv spec is valid
> + *
> + * valid_chainmask
> + *		If set, tx chainmask is valid
> + *
> + * valid_encrypt_type
> + *		If set, encrypt type is valid
> + *
> + * valid_key_flags
> + *		If set, key flags is valid
> + *
> + * valid_expire_tsf
> + *		If set, tx expire TSF spec is valid
> + *
> + * valid_chanfreq
> + *		If set, chanfreq is valid
> + *
> + * is_dsrc
> + *		If set, MSDU is a DSRC frame
> + *
> + * guard_interval
> + *		0.4us, 0.8us, 1.6us, 3.2us
> + *
> + * encrypt_type
> + *		0 = NO_ENCRYPT,
> + *		1 = ENCRYPT,
> + *		2 ~ 3 - Reserved
> + *
> + * retry_limit
> + *		Specify the maximum number of transmissions, including the
> + *		initial transmission, to attempt before giving up if no ack
> + *		is received.
> + *		If the tx rate is specified, then all retries shall use the
> + *		same rate as the initial transmission.
> + *		If no tx rate is specified, the target can choose whether to
> + *		retain the original rate during the retransmissions, or to
> + *		fall back to a more robust rate.
> + *
> + * use_dcm_11ax
> + *		If set, Use Dual subcarrier modulation.
> + *		Valid only for 11ax preamble types HE_SU
> + *		and HE_EXT_SU
> + *
> + * ltf_subtype_11ax
> + *		Takes enum values of htt_11ax_ltf_subtype_t
> + *		Valid only for 11ax preamble types HE_SU
> + *		and HE_EXT_SU
> + *
> + * dyn_bw
> + *		0 = static bw, 1 = dynamic bw
> + *
> + * bw_mask
> + *		Valid only if dyn_bw == 0 (static bw).
> + *
> + * host_tx_desc_pool
> + *		If set, Firmware allocates tx_descriptors
> + *		in WAL_BUFFERID_TX_HOST_DATA_EXP,instead
> + *		of WAL_BUFFERID_TX_TCL_DATA_EXP.
> + *		Use cases:
> + *		Any time firmware uses TQM-BYPASS for Data
> + *		TID, firmware expect host to set this bit.
> + *
> + * power
> + *		Unit of the power field is 0.5 dbm
> + *		signed value ranging from -64dbm to 63.5 dbm
> + *
> + * mcs_mask
> + *		mcs bit mask of 0 ~ 11
> + *		Setting more than one MCS isn't currently
> + *		supported by the target but is supported
> + *		in the interface in case in the future
> + *		the target supports specifications of
> + *		a limited set of MCS values.
> + *
> + * nss_mask
> + *		Nss bit mask 0 ~ 7
> + *		Setting more than one Nss isn't currently
> + *		supported by the target but is supported
> + *		in the interface in case in the future
> + *		the target supports specifications of
> + *		a limited set of Nss values.
> + *
> + * pream_type
> + *		Preamble types
> + *
> + * update_peer_cache
> + *		When set these custom values will be
> + *		used for all packets, until the next
> + *		update via this ext header.
> + *		This is to make sure not all packets
> + *		need to include this header.
> + *
> + * chain_mask
> + *		Specify which chains to transmit from
> + *
> + * key_flags
> + *		Key Index and related flags - used in mesh mode
> + *
> + * chanfreq
> + *		Channel frequency: This identifies the desired channel
> + *		frequency (in MHz) for tx frames. This is used by FW to help
> + *		determine when it is safe to transmit or drop frames for
> + *		off-channel operation.
> + *		The default value of zero indicates to FW that the corresponding
> + *		VDEV's home channel (if there is one) is the desired channel
> + *		frequency.
> + *
> + * expire_tsf_lo
> + *		tx expiry time (TSF) LSBs
> + *
> + * expire_tsf_hi
> + *		tx expiry time (TSF) MSBs
> + *
> + * learning_frame
> + *		When this flag is set, this frame will be dropped by FW
> + *		rather than being enqueued to the Transmit Queue Manager (TQM) HW.
> + *
> + * send_as_standalone
> + *		This will indicate if the msdu needs to be sent as a singleton PPDU,
> + *		i.e. with no A-MSDU or A-MPDU aggregation.
> + *		The scope is extended to other use-cases.
> + *
> + * is_host_opaque_valid
> + *		Set this bit to 1 if the host_opaque_cookie is populated
> + *		with valid information.
> + *
> + * host_opaque_cookie
> + *		Host opaque cookie for special frames
> + */

With bit fields removed, may be this doc is not of much help?

>   
>   #endif /* ATH11K_HAL_DESC_H */
> --- a/drivers/net/wireless/ath/ath11k/mac.c
> +++ b/drivers/net/wireless/ath/ath11k/mac.c
> @@ -10324,6 +10324,9 @@ static int __ath11k_mac_register(struct
>   		goto err_free_if_combs;
>   	}
>   
> +	ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
> +	ar->hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
> +
>   	if (!ab->hw_params.supports_monitor)
>   		/* There's a race between calling ieee80211_register_hw()
>   		 * and here where the monitor mode is enabled for a little
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -40,6 +40,10 @@
>   
>   /* misc utils */
>   
> +static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
> +				struct net_device *dev, struct sta_info *sta,
> +				struct ieee80211_key *key, struct sk_buff *skb);
> +
>   static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
>   				 struct sk_buff *skb, int group_addr,
>   				 int next_frag_len)
> @@ -4281,5 +4285,16 @@ void __ieee80211_subif_start_xmit(struct
>   	if (IS_ERR(sta))
>   		sta = NULL;
>   
> +	if (sta && sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
> +	    get_bss_sdata(sdata)->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED &&
> +	    !is_multicast_ether_addr(skb->data)) {
> +		struct ieee80211_key *key = rcu_dereference(sta->ptk[sta->ptk_idx]);
> +		if (!key)
> +			key = rcu_dereference(get_bss_sdata(sdata)->default_unicast_key);
> +		ieee80211_8023_xmit(sdata, dev, sta, key, skb);
> +		rcu_read_unlock();
> +		return;
> +	}
> +

In general, have mac80211 changes in a separate patch. I dont think this particular change
is really required as AP_VLAN xmit will also be 8023 if the master interface supports it.

Vasanth



More information about the ath11k mailing list