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

Jeff Johnson jeff.johnson at oss.qualcomm.com
Wed May 21 09:32:39 PDT 2025


On 5/20/2025 2:16 PM, David Rapaň wrote:
> ath11k currently lacks support for dynamic vlan (AP/VLAN), so this patch
> adds feature advertisement in '__ath11k_mac_register' alongside
> all the other neccessary components supporting its function, including
> prerequisites for software encryption of VLAN group traffics.
> VLAN unicast packets shall be taking 8023 xmit path if encapsulation
> offload is enabled and multicast/broadcast then 80211 xmit path.
> 
> Metadata info in dp_tx added to notify firmware that the
> multicast/broadcast packets are encrypted in software.

Use imperative mood in your commit text
https://www.kernel.org/doc/html/latest/process/submitting-patches.html#describe-your-changes
> 
> Changes since v1:
>  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.
>  WIthout this change are as QoS Data with TID 0.

patch revision history goes after the "---"

> 
> 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
> 
> Signed-off-by: Seevalamuthu Mariappan <seevalam at codeaurora.org>
> Signed-off-by: David Rapan <david at rapan.cz>

Who authored this code? You are submitting it as the author, but the S-O-B
chain implies Seevalamuthu Mariappan was the author.

> ---
>  drivers/net/wireless/ath/ath11k/core.h  |   1 +
>  drivers/net/wireless/ath/ath11k/dp_tx.c |  82 +++++++++-
>  drivers/net/wireless/ath/ath11k/dp_tx.h | 197 ++++++++++++++++++++++++
>  drivers/net/wireless/ath/ath11k/mac.c   |   3 +
>  net/mac80211/tx.c                       |  15 +++
>  5 files changed, 296 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,42 @@ 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 htt_tx_msdu_desc_ext *desc_ext;
> +
> +	htt_desc_size = sizeof(struct htt_tx_msdu_desc_ext);
> +	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 htt_tx_msdu_desc_ext *)skb->data;
> +	desc_ext->valid_encrypt_type = 1;
> +	desc_ext->encrypt_type = 0;
> +	desc_ext->host_tx_desc_pool = 1;
> +	*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 +133,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 +228,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);
>  		break;
>  	case HAL_TCL_ENCAP_TYPE_RAW:
>  		if (!test_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags)) {
> @@ -208,6 +252,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 +287,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 +344,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 +422,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/dp_tx.h
> +++ b/drivers/net/wireless/ath/ath11k/dp_tx.h
> @@ -17,6 +17,203 @@ struct ath11k_dp_htt_wbm_tx_status {
>  	u16 peer_id;
>  };
>  
> +/* htt_tx_msdu_desc_ext
> + *
> + * 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
> + */
> +struct htt_tx_msdu_desc_ext {
> +	u32
> +		valid_pwr            :  1,
> +		valid_mcs_mask       :  1,
> +		valid_nss_mask       :  1,
> +		valid_preamble_type  :  1,
> +		valid_retries        :  1,
> +		valid_bw_info        :  1,
> +		valid_guard_interval :  1,
> +		valid_chainmask      :  1,
> +		valid_encrypt_type   :  1,
> +		valid_key_flags      :  1,
> +		valid_expire_tsf     :  1,
> +		valid_chanfreq       :  1,
> +		is_dsrc              :  1,
> +		guard_interval       :  2,
> +		encrypt_type         :  2,
> +		retry_limit          :  4,
> +		use_dcm_11ax         :  1,
> +		ltf_subtype_11ax     :  2,
> +		dyn_bw               :  1,
> +		bw_mask              :  6,
> +		host_tx_desc_pool    :  1;
> +	u32
> +		power                :  8,
> +		mcs_mask             : 12,
> +		nss_mask             :  8,
> +		pream_type           :  3,
> +		update_peer_cache    :  1;
> +	u32
> +		chain_mask           :  8,
> +		key_flags            :  8,
> +		chanfreq             : 16;
> +
> +	u32 expire_tsf_lo;
> +	u32 expire_tsf_hi;
> +
> +	u32
> +		learning_frame       :  1,
> +		send_as_standalone   :  1,
> +		is_host_opaque_valid :  1,
> +		rsvd0                : 29;
> +	u32
> +		host_opaque_cookie   : 16,
> +		rsvd1                : 16;
> +} __packed;

we should not be using bitmasks with packed in upstream code.
QSDK may have done this, but it's not ok upstream
and why isn't this being added to hal_desc.h?


> +
>  void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts);
>  int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab);
>  int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif,
> --- 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)
> @@ -4257,6 +4261,7 @@ void __ieee80211_subif_start_xmit(struct
>  {
>  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
>  	struct ieee80211_local *local = sdata->local;
> +	struct ieee80211_key *key = NULL;
>  	struct sta_info *sta;
>  	struct sk_buff *next;
>  	int len = skb->len;
> @@ -4281,5 +4286,15 @@ void __ieee80211_subif_start_xmit(struct
>  	if (IS_ERR(sta))
>  		sta = NULL;
>  
> +	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
> +	    get_bss_sdata(sdata)->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED &&
> +	    !is_multicast_ether_addr(skb->data)) {
> +		if (sta)
> +			key = rcu_dereference(sta->ptk[sta->ptk_idx]);
> +		ieee80211_8023_xmit(sdata, dev, sta, key, skb);
> +		rcu_read_unlock();
> +		return;
> +	}
> +
>  	skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
>  	ieee80211_aggr_check(sdata, sta, skb);




More information about the ath11k mailing list