diff mbox series

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

Message ID DB6PR08MB26934CD2145BE9BC3DF6980ADF9FA@DB6PR08MB2693.eurprd08.prod.outlook.com
State New
Headers show
Series [v2] ath11k: add support for dynamic vlan (ap/vlan) | expand

Commit Message

David Rapaƈ May 20, 2025, 9:16 p.m. UTC
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.

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.

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@codeaurora.org>
Signed-off-by: David Rapan <david@rapan.cz>
---
 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
diff mbox series

Patch

--- 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;
+
 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);