diff --git a/dp/inc/cdp_txrx_cmn_struct.h b/dp/inc/cdp_txrx_cmn_struct.h index d41e884897..78c01b34b6 100644 --- a/dp/inc/cdp_txrx_cmn_struct.h +++ b/dp/inc/cdp_txrx_cmn_struct.h @@ -1274,6 +1274,7 @@ enum cdp_pdev_param_type { * @cdp_ipa_enabled : set ipa mode * @cdp_psoc_param_vdev_stats_hw_offload: Configure HW vdev stats offload * @cdp_pdev_param_undecoded_metadata_enable: Undecoded metadata capture enable + * @cdp_vdev_param_traffic_end_ind: Traffic end indication enable/disable */ typedef union cdp_config_param_t { /* peer params */ @@ -1359,6 +1360,7 @@ typedef union cdp_config_param_t { bool cdp_pdev_param_undecoded_metadata_enable; bool cdp_sawf_enabled; bool cdp_drop_3addr_mcast; + bool cdp_vdev_param_traffic_end_ind; } cdp_config_param_type; /** @@ -1438,6 +1440,7 @@ enum cdp_pdev_bpr_param { * @CDP_UPDATE_DSCP_TO_TID_MAP: Set DSCP to TID map id * @CDP_SET_MCAST_VDEV : Set primary mcast vdev * @CDP_ENABLE_WRAP: qwrap ap + * @CDP_ENABLE_TRAFFIC_END_INDICATION: enable/disable traffic end indication */ enum cdp_vdev_param_type { CDP_ENABLE_NAWDS, @@ -1479,6 +1482,9 @@ enum cdp_vdev_param_type { CDP_SET_MCAST_VDEV, CDP_DROP_3ADDR_MCAST, CDP_ENABLE_WRAP, +#ifdef DP_TRAFFIC_END_INDICATION + CDP_ENABLE_TRAFFIC_END_INDICATION, +#endif }; /* diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index a917d5c7f4..1c5a51f188 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -6780,6 +6780,45 @@ static inline void dp_vdev_save_mld_addr(struct dp_vdev *vdev, } #endif +#ifdef DP_TRAFFIC_END_INDICATION +/* + * dp_tx_traffic_end_indication_attach() - Initialize data end indication + * related members in VDEV + * @vdev: DP vdev handle + * + * Return: None + */ +static inline void +dp_tx_vdev_traffic_end_indication_attach(struct dp_vdev *vdev) +{ + qdf_nbuf_queue_init(&vdev->end_ind_pkt_q); +} + +/* + * dp_tx_vdev_traffic_end_indication_detach() - De-init data end indication + * related members in VDEV + * @vdev: DP vdev handle + * + * Return: None + */ +static inline void +dp_tx_vdev_traffic_end_indication_detach(struct dp_vdev *vdev) +{ + qdf_nbuf_t nbuf; + + while ((nbuf = qdf_nbuf_queue_remove(&vdev->end_ind_pkt_q)) != NULL) + qdf_nbuf_free(nbuf); +} +#else +static inline void +dp_tx_vdev_traffic_end_indication_attach(struct dp_vdev *vdev) +{} + +static inline void +dp_tx_vdev_traffic_end_indication_detach(struct dp_vdev *vdev) +{} +#endif + /* * dp_vdev_attach_wifi3() - attach txrx vdev * @txrx_pdev: Datapath PDEV handle @@ -6900,6 +6939,7 @@ static QDF_STATUS dp_vdev_attach_wifi3(struct cdp_soc_t *cdp_soc, vdev->prev_tx_enq_tstamp = 0; vdev->prev_rx_deliver_tstamp = 0; vdev->skip_sw_tid_classification = DP_TX_HW_DSCP_TID_MAP_VALID; + dp_tx_vdev_traffic_end_indication_attach(vdev); dp_vdev_pdev_list_add(soc, pdev, vdev); pdev->vdev_count++; @@ -7290,6 +7330,7 @@ static QDF_STATUS dp_vdev_detach_wifi3(struct cdp_soc_t *cdp_soc, dp_txrx_reset_vdev_stats_id(cdp_soc, vdev->vdev_stats_id); dp_tx_vdev_multipass_deinit(vdev); + dp_tx_vdev_traffic_end_indication_detach(vdev); if (vdev->vdev_dp_ext_handle) { qdf_mem_free(vdev->vdev_dp_ext_handle); @@ -10604,6 +10645,11 @@ dp_set_vdev_param(struct cdp_soc_t *cdp_soc, uint8_t vdev_id, case CDP_ENABLE_WRAP: vdev->wrap_vdev = val.cdp_vdev_param_wrap; break; +#ifdef DP_TRAFFIC_END_INDICATION + case CDP_ENABLE_TRAFFIC_END_INDICATION: + vdev->traffic_end_ind_en = val.cdp_vdev_param_traffic_end_ind; + break; +#endif default: break; } diff --git a/dp/wifi3.0/dp_tx.c b/dp/wifi3.0/dp_tx.c index bd20a38e0f..75bcd404f3 100644 --- a/dp/wifi3.0/dp_tx.c +++ b/dp/wifi3.0/dp_tx.c @@ -575,7 +575,8 @@ static uint8_t dp_tx_prepare_htt_metadata(struct dp_vdev *vdev, qdf_nbuf_t nbuf, if (vdev->mesh_vdev || msdu_info->is_tx_sniffer || HTT_TX_MSDU_EXT2_DESC_FLAG_VALID_KEY_FLAGS_GET(msdu_info-> - meta_data[0])) { + meta_data[0]) || + msdu_info->exception_fw) { if (qdf_unlikely(qdf_nbuf_headroom(nbuf) < htt_desc_size_aligned)) { nbuf = qdf_nbuf_realloc_headroom(nbuf, @@ -1032,6 +1033,211 @@ static inline int dp_tx_is_nbuf_marked_exception(struct dp_soc *soc, } #endif +#ifdef DP_TRAFFIC_END_INDICATION +/** + * dp_tx_get_traffic_end_indication_pkt() - Allocate and prepare packet to send + * as indication to fw to inform that + * data stream has ended + * @vdev: DP vdev handle + * @nbuf: original buffer from network stack + * + * Return: NULL on failure, + * nbuf on success + */ +static inline qdf_nbuf_t +dp_tx_get_traffic_end_indication_pkt(struct dp_vdev *vdev, + qdf_nbuf_t nbuf) +{ + /* Packet length should be enough to copy upto L3 header */ + uint8_t end_nbuf_len = 64; + uint8_t htt_desc_size_aligned; + uint8_t htt_desc_size; + qdf_nbuf_t end_nbuf; + + if (qdf_unlikely(QDF_NBUF_CB_GET_PACKET_TYPE(nbuf) == + QDF_NBUF_CB_PACKET_TYPE_END_INDICATION)) { + htt_desc_size = sizeof(struct htt_tx_msdu_desc_ext2_t); + htt_desc_size_aligned = (htt_desc_size + 7) & ~0x7; + + end_nbuf = qdf_nbuf_queue_remove(&vdev->end_ind_pkt_q); + if (!end_nbuf) { + end_nbuf = qdf_nbuf_alloc(NULL, + (htt_desc_size_aligned + + end_nbuf_len), + htt_desc_size_aligned, + 8, false); + if (!end_nbuf) { + dp_err("Packet allocation failed"); + goto out; + } + } else { + qdf_nbuf_reset(end_nbuf, htt_desc_size_aligned, 8); + } + qdf_mem_copy(qdf_nbuf_data(end_nbuf), qdf_nbuf_data(nbuf), + end_nbuf_len); + qdf_nbuf_set_pktlen(end_nbuf, end_nbuf_len); + + return end_nbuf; + } +out: + return NULL; +} + +/** + * dp_tx_send_traffic_end_indication_pkt() - Send indication packet to FW + * via exception path. + * @vdev: DP vdev handle + * @end_nbuf: skb to send as indication + * @msdu_info: msdu_info of original nbuf + * @peer_id: peer id + * + * Return: None + */ +static inline void +dp_tx_send_traffic_end_indication_pkt(struct dp_vdev *vdev, + qdf_nbuf_t end_nbuf, + struct dp_tx_msdu_info_s *msdu_info, + uint16_t peer_id) +{ + struct dp_tx_msdu_info_s e_msdu_info = {0}; + qdf_nbuf_t nbuf; + struct htt_tx_msdu_desc_ext2_t *desc_ext = + (struct htt_tx_msdu_desc_ext2_t *)(e_msdu_info.meta_data); + e_msdu_info.tx_queue = msdu_info->tx_queue; + e_msdu_info.tid = msdu_info->tid; + e_msdu_info.exception_fw = 1; + desc_ext->host_tx_desc_pool = 1; + desc_ext->traffic_end_indication = 1; + nbuf = dp_tx_send_msdu_single(vdev, end_nbuf, &e_msdu_info, + peer_id, NULL); + if (nbuf) { + dp_err("Traffic end indication packet tx failed"); + qdf_nbuf_free(nbuf); + } +} + +/** + * dp_tx_traffic_end_indication_set_desc_flag() - Set tx descriptor flag to + * mark it trafic end indication + * packet. + * @tx_desc: Tx descriptor pointer + * @msdu_info: msdu_info structure pointer + * + * Return: None + */ +static inline void +dp_tx_traffic_end_indication_set_desc_flag(struct dp_tx_desc_s *tx_desc, + struct dp_tx_msdu_info_s *msdu_info) +{ + struct htt_tx_msdu_desc_ext2_t *desc_ext = + (struct htt_tx_msdu_desc_ext2_t *)(msdu_info->meta_data); + + if (qdf_unlikely(desc_ext->traffic_end_indication)) + tx_desc->flags |= DP_TX_DESC_FLAG_TRAFFIC_END_IND; +} + +/** + * dp_tx_traffic_end_indication_enq_ind_pkt() - Enqueue the packet instead of + * freeing which are associated + * with traffic end indication + * flagged descriptor. + * @soc: dp soc handle + * @desc: Tx descriptor pointer + * @nbuf: buffer pointer + * + * Return: True if packet gets enqueued else false + */ +static bool +dp_tx_traffic_end_indication_enq_ind_pkt(struct dp_soc *soc, + struct dp_tx_desc_s *desc, + qdf_nbuf_t nbuf) +{ + struct dp_vdev *vdev = NULL; + + if (qdf_unlikely((desc->flags & + DP_TX_DESC_FLAG_TRAFFIC_END_IND) != 0)) { + vdev = dp_vdev_get_ref_by_id(soc, desc->vdev_id, + DP_MOD_ID_TX_COMP); + if (vdev) { + qdf_nbuf_queue_add(&vdev->end_ind_pkt_q, nbuf); + dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_TX_COMP); + return true; + } + } + return false; +} + +/** + * dp_tx_traffic_end_indication_is_enabled() - get the feature + * enable/disable status + * @vdev: dp vdev handle + * + * Return: True if feature is enable else false + */ +static inline bool +dp_tx_traffic_end_indication_is_enabled(struct dp_vdev *vdev) +{ + return qdf_unlikely(vdev->traffic_end_ind_en); +} + +static inline qdf_nbuf_t +dp_tx_send_msdu_single_wrapper(struct dp_vdev *vdev, qdf_nbuf_t nbuf, + struct dp_tx_msdu_info_s *msdu_info, + uint16_t peer_id, qdf_nbuf_t end_nbuf) +{ + if (dp_tx_traffic_end_indication_is_enabled(vdev)) + end_nbuf = dp_tx_get_traffic_end_indication_pkt(vdev, nbuf); + + nbuf = dp_tx_send_msdu_single(vdev, nbuf, msdu_info, peer_id, NULL); + + if (qdf_unlikely(end_nbuf)) + dp_tx_send_traffic_end_indication_pkt(vdev, end_nbuf, + msdu_info, peer_id); + return nbuf; +} +#else +static inline qdf_nbuf_t +dp_tx_get_traffic_end_indication_pkt(struct dp_vdev *vdev, + qdf_nbuf_t nbuf) +{ + return NULL; +} + +static inline void +dp_tx_send_traffic_end_indication_pkt(struct dp_vdev *vdev, + qdf_nbuf_t end_nbuf, + struct dp_tx_msdu_info_s *msdu_info, + uint16_t peer_id) +{} + +static inline void +dp_tx_traffic_end_indication_set_desc_flag(struct dp_tx_desc_s *tx_desc, + struct dp_tx_msdu_info_s *msdu_info) +{} + +static inline bool +dp_tx_traffic_end_indication_enq_ind_pkt(struct dp_soc *soc, + struct dp_tx_desc_s *desc, + qdf_nbuf_t nbuf) +{ + return false; +} + +static inline bool +dp_tx_traffic_end_indication_is_enabled(struct dp_vdev *vdev) +{ + return false; +} + +static inline qdf_nbuf_t +dp_tx_send_msdu_single_wrapper(struct dp_vdev *vdev, qdf_nbuf_t nbuf, + struct dp_tx_msdu_info_s *msdu_info, + uint16_t peer_id, qdf_nbuf_t end_nbuf) +{ + return dp_tx_send_msdu_single(vdev, nbuf, msdu_info, peer_id, NULL); +} +#endif + /** * dp_tx_desc_prepare_single - Allocate and prepare Tx descriptor * @vdev: DP vdev handle @@ -1149,6 +1355,8 @@ struct dp_tx_desc_s *dp_tx_prepare_desc_single(struct dp_vdev *vdev, tx_desc->length = qdf_nbuf_headlen(nbuf); tx_desc->pkt_offset = align_pad + htt_hdr_size; tx_desc->flags |= DP_TX_DESC_FLAG_TO_FW; + dp_tx_traffic_end_indication_set_desc_flag(tx_desc, + msdu_info); is_exception = 1; tx_desc->length -= tx_desc->pkt_offset; } @@ -2301,6 +2509,10 @@ void dp_tx_comp_free_buf(struct dp_soc *soc, struct dp_tx_desc_s *desc) if (desc->flags & DP_TX_DESC_FLAG_MESH_MODE) return dp_mesh_tx_comp_free_buff(soc, desc); + + if (dp_tx_traffic_end_indication_enq_ind_pkt(soc, desc, nbuf)) + return; + nbuf_free: qdf_nbuf_free(nbuf); } @@ -3362,6 +3574,7 @@ qdf_nbuf_t dp_tx_send(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, */ struct dp_tx_msdu_info_s msdu_info = {0}; struct dp_vdev *vdev = NULL; + qdf_nbuf_t end_nbuf = NULL; if (qdf_unlikely(vdev_id >= MAX_VDEV_CNT)) return nbuf; @@ -3510,8 +3723,9 @@ qdf_nbuf_t dp_tx_send(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, * SRNG. There is no need to setup a MSDU extension descriptor. */ dp_tx_prefetch_nbuf_data(nbuf); - nbuf = dp_tx_send_msdu_single(vdev, nbuf, &msdu_info, peer_id, NULL); + nbuf = dp_tx_send_msdu_single_wrapper(vdev, nbuf, &msdu_info, + peer_id, end_nbuf); return nbuf; send_multiple: diff --git a/dp/wifi3.0/dp_tx.h b/dp/wifi3.0/dp_tx.h index 87c641c5b4..0203b696d2 100644 --- a/dp/wifi3.0/dp_tx.h +++ b/dp/wifi3.0/dp_tx.h @@ -59,6 +59,7 @@ #define DP_TX_DESC_FLAG_UNMAP_DONE 0x800 #define DP_TX_DESC_FLAG_TX_COMP_ERR 0x1000 #define DP_TX_DESC_FLAG_FLUSH 0x2000 +#define DP_TX_DESC_FLAG_TRAFFIC_END_IND 0x4000 #define DP_TX_EXT_DESC_FLAG_METADATA_VALID 0x1 diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index 3365a77d3b..dd9fbf4474 100644 --- a/dp/wifi3.0/dp_types.h +++ b/dp/wifi3.0/dp_types.h @@ -3390,6 +3390,12 @@ struct dp_vdev { uint32_t roaming_peer_status; union dp_align_mac_addr roaming_peer_mac; #endif +#ifdef DP_TRAFFIC_END_INDICATION + /* per vdev feature enable/disable status */ + bool traffic_end_ind_en; + /* per vdev nbuf queue for traffic end indication packets */ + qdf_nbuf_queue_t end_ind_pkt_q; +#endif }; enum {