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@xxxxxxxxxxxxxx> Signed-off-by: Seevalamuthu Mariappan <seevalam@xxxxxxxxxxxxxx> Signed-off-by: David Rapan <david@xxxxxxxx> --- 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); 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 + */ #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; + } + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); ieee80211_aggr_check(sdata, sta, skb);