From 7a29dbdab3b282c1d0d2705e519c921011a83e82 Mon Sep 17 00:00:00 2001 From: Lijo Cyriac George Date: Sun, 26 Apr 2026 02:56:07 +0530 Subject: [PATCH 01/24] module: alsa: optimize ALSA endpoint logging Adjust log priority to reduce verbosity in process calls. Signed-off-by: ffrancis --- .../endpoint/alsa_device/capi/src/capi_alsa_device.c | 10 +++++----- .../alsa_device/lib/src/tinyalsa/alsa_device_driver.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c index 9acf6379..f6b2cfe9 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c +++ b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c @@ -55,7 +55,7 @@ static void capi_alsa_device_dma_wait_thread(void *arg) continue; } - AR_MSG(DBG_HIGH_PRIO, "CAPI_ALSA_DEVICE: DMA thread read %d bytes", me_ptr->read_buffer_size); + AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: DMA thread read %d bytes", me_ptr->read_buffer_size); /* Mark data as ready */ me_ptr->data_ready = TRUE; @@ -63,7 +63,7 @@ static void capi_alsa_device_dma_wait_thread(void *arg) /* Signal framework using STM signal */ if (me_ptr->signal_ptr && me_ptr->enable_stm) { - AR_MSG(DBG_HIGH_PRIO, "CAPI_ALSA_DEVICE: signaling the container"); + AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: signaling the container"); posal_signal_send(me_ptr->signal_ptr); } } @@ -1169,7 +1169,7 @@ capi_err_t capi_alsa_device_process_sink(capi_t *_pif, capi_stream_data_t *input { need_to_underrun = TRUE; is_input_available = FALSE; - AR_MSG_ISLAND(DBG_ERROR_PRIO, "CAPI: Failed to interleave data"); + AR_MSG(DBG_ERROR_PRIO, "CAPI: Failed to interleave data"); } } } @@ -1224,7 +1224,7 @@ capi_err_t capi_alsa_device_process_sink(capi_t *_pif, capi_stream_data_t *input return CAPI_EFAILED; } - AR_MSG_ISLAND(DBG_HIGH_PRIO, "CAPI: alsa_device_driver_write successful, total bytes copied: %d", total_bytes_copied); + AR_MSG(DBG_LOW_PRIO, "CAPI: alsa_device_driver_write successful, total bytes copied: %d", total_bytes_copied); return capi_result; } @@ -1429,7 +1429,7 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp me_ptr->data_ready = FALSE; - AR_MSG_ISLAND(DBG_HIGH_PRIO, "CAPI_ALSA_DEVICE: Process source successful, bytes: %d", total_bytes); + AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: Process source successful, bytes: %d", total_bytes); return capi_result; } diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/lib/src/tinyalsa/alsa_device_driver.c b/fwk/platform/modules/generic/endpoint/alsa_device/lib/src/tinyalsa/alsa_device_driver.c index e27e266d..721a4f1b 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/lib/src/tinyalsa/alsa_device_driver.c +++ b/fwk/platform/modules/generic/endpoint/alsa_device/lib/src/tinyalsa/alsa_device_driver.c @@ -146,7 +146,7 @@ ar_result_t alsa_device_driver_read(alsa_device_driver_t *alsa_device_driver_ptr return AR_EFAILED; } - AR_MSG(DBG_HIGH_PRIO, "ALSA_DEVICE_DRIVER: pcm_read success, bytes: %d", num_bytes); + AR_MSG(DBG_LOW_PRIO, "ALSA_DEVICE_DRIVER: pcm_read success, bytes: %d", num_bytes); return AR_EOK; } @@ -159,7 +159,7 @@ ar_result_t alsa_device_driver_write(alsa_device_driver_t *alsa_device_driver_pt return AR_EFAILED; } - //AR_MSG(DBG_HIGH_PRIO, "ALSA_DEVICE_DRIVER: pcm write success \n"); + AR_MSG(DBG_LOW_PRIO, "ALSA_DEVICE_DRIVER: pcm write success \n"); return AR_EOK; } From d5ceb3b1a5641547ceb4d4a775de6b4ca2136e88 Mon Sep 17 00:00:00 2001 From: ankisuma Date: Thu, 23 Apr 2026 13:56:57 +0530 Subject: [PATCH 02/24] fw: api: align debug priorities with AR log levels Map DBG_* priorities to appropriate AR log levels and route messages through AR_LOG_* macros. Signed-off-by: ffrancis --- fwk/api/ar_utils/linux/ar_msg.h | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/fwk/api/ar_utils/linux/ar_msg.h b/fwk/api/ar_utils/linux/ar_msg.h index efc34e04..be4ae898 100644 --- a/fwk/api/ar_utils/linux/ar_msg.h +++ b/fwk/api/ar_utils/linux/ar_msg.h @@ -50,11 +50,11 @@ // Legacy debug priority messages -#define DBG_LOW_PRIO AR_HIGH_PRIO /**< Low priority debug message. */ -#define DBG_MED_PRIO AR_HIGH_PRIO /**< Medium priority debug message. */ +#define DBG_LOW_PRIO AR_LOW_PRIO /**< Low priority debug message. */ +#define DBG_MED_PRIO AR_MED_PRIO /**< Medium priority debug message. */ #define DBG_HIGH_PRIO AR_HIGH_PRIO /**< High priority debug message. */ #define DBG_ERROR_PRIO AR_ERROR_PRIO /**< Error priority debug message. */ -#define DBG_FATAL_PRIO AR_HIGH_PRIO /**< Fatal priority debug message. */ +#define DBG_FATAL_PRIO AR_FATAL_PRIO /**< Fatal priority debug message. */ #define MSG_LEGACY_ERROR DBG_ERROR_PRIO #define MSG_LEGACY_HIGH DBG_HIGH_PRIO #define MSG_LEGACY_MED DBG_MED_PRIO @@ -122,14 +122,26 @@ do { \ if (ar_log_debugmsg_enable) \ { \ - static const char *msg_tag = AR_MSG_TAG; \ - static const char filename[] = LOCAL_FILE_NAME; \ - static const uint32_t line_no = __LINE__; \ - ar_log( xx_ss_mask, msg_tag, filename, __FUNCTION__, line_no, xx_fmt, ##__VA_ARGS__); \ if (DBG_FATAL_PRIO == (xx_ss_mask)) \ { \ assert(0); \ } \ + else if (DBG_ERROR_PRIO == (xx_ss_mask)) \ + { \ + AR_LOG_ERR(AR_MSG_TAG, xx_fmt, ##__VA_ARGS__) \ + } \ + else if (DBG_HIGH_PRIO == (xx_ss_mask)) \ + { \ + AR_LOG_INFO(AR_MSG_TAG, xx_fmt, ##__VA_ARGS__) \ + } \ + else if (DBG_MED_PRIO == (xx_ss_mask)) \ + { \ + AR_LOG_DEBUG(AR_MSG_TAG, xx_fmt, ##__VA_ARGS__) \ + } \ + else if (DBG_LOW_PRIO == (xx_ss_mask)) \ + { \ + AR_LOG_VERBOSE(AR_MSG_TAG, xx_fmt, ##__VA_ARGS__) \ + } \ } \ } while(0) #endif //if defined (ARSPF_PLATFORM_LRH) From 62552244874ce23acb9347e33c5076fc97574983 Mon Sep 17 00:00:00 2001 From: Harendra Gautam Date: Sat, 25 Apr 2026 17:35:36 +0530 Subject: [PATCH 03/24] module: alsa: update tinyalsa library dependency update shared library dependency from libtinyalsa to liboss_tinyalsa Signed-off-by: ffrancis --- .../modules/generic/endpoint/alsa_device/build/Android.mk | 2 +- fwk/spf/utils/build/Android.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/build/Android.mk b/fwk/platform/modules/generic/endpoint/alsa_device/build/Android.mk index a8860100..4577b765 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/build/Android.mk +++ b/fwk/platform/modules/generic/endpoint/alsa_device/build/Android.mk @@ -24,7 +24,7 @@ ALSA_DEVICE_CFLAGS_64 += -march=armv8-a+crypto ALSA_DEVICE_SHARED_LIBS := \ liblx-osal \ libar-gpr \ - libtinyalsa + liboss_tinyalsa ALSA_DEVICE_HEADER_LIBS := \ libspf_api \ diff --git a/fwk/spf/utils/build/Android.mk b/fwk/spf/utils/build/Android.mk index 4edfe000..c23866cd 100644 --- a/fwk/spf/utils/build/Android.mk +++ b/fwk/spf/utils/build/Android.mk @@ -69,7 +69,7 @@ LOCAL_SHARED_LIBRARIES := \ liblx-osal \ libar-gpr \ libdiag \ - libtinyalsa + liboss_tinyalsa LOCAL_HEADER_LIBRARIES := libposal_headers libspf_api libspf_interfaces_headers libapm_headers libamdb_headers libirm_headers libdls_headers LOCAL_STATIC_LIBRARIES := \ From c6675fc976ebebc8359019634514c1a0ea620732 Mon Sep 17 00:00:00 2001 From: kisany Date: Wed, 18 Feb 2026 19:42:29 +0530 Subject: [PATCH 04/24] fwk: gen_cntr: Reset interrupt counters Signal miss after enabling DMA in callback. Reset and raised interrupts after handling completes. Signed-off-by: ffrancis --- .../gen_cntr/ext/err_check/src/gen_cntr_err_handler.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fwk/spf/containers/gen_cntr/ext/err_check/src/gen_cntr_err_handler.c b/fwk/spf/containers/gen_cntr/ext/err_check/src/gen_cntr_err_handler.c index b4419171..ee907d56 100644 --- a/fwk/spf/containers/gen_cntr/ext/err_check/src/gen_cntr_err_handler.c +++ b/fwk/spf/containers/gen_cntr/ext/err_check/src/gen_cntr_err_handler.c @@ -98,7 +98,9 @@ ar_result_t gen_cntr_check_handle_signal_miss(gen_cntr_t *me_ptr, bool_t is_afte { GEN_CNTR_MSG(me_ptr->topo.gu.log_id, DBG_ERROR_PRIO, "Error in handling signal miss before process"); } - + // reset the counters to make sure false interrupts during the error handling is ignored + me_ptr->st_module.processed_interrupt_counter = 0; + me_ptr->st_module.raised_interrupt_counter = 0; *continue_processing = FALSE; } From 04d2e7400230b9fae45f3162fbfc75dd5b2008d3 Mon Sep 17 00:00:00 2001 From: laksrao Date: Tue, 24 Feb 2026 13:07:31 +0530 Subject: [PATCH 05/24] Misaligned memory access fixed by padding the API structure memory Misaligned memory access fixed by padding the API structure memory Signed-off-by: ffrancis --- .../module/imcl/api/imcl_bt_sideband_encoder_feedback_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fwk/spf/interfaces/module/imcl/api/imcl_bt_sideband_encoder_feedback_api.h b/fwk/spf/interfaces/module/imcl/api/imcl_bt_sideband_encoder_feedback_api.h index 14ca9ebc..e6d18b04 100644 --- a/fwk/spf/interfaces/module/imcl/api/imcl_bt_sideband_encoder_feedback_api.h +++ b/fwk/spf/interfaces/module/imcl/api/imcl_bt_sideband_encoder_feedback_api.h @@ -55,7 +55,7 @@ typedef struct imcl_param_id_bt_sideband_encoder_feedback_t /* Payload of IMCL_PARAM_ID_BT_SIDEBAND_ENCODER_FEEDBACK_V2*/ typedef struct imcl_param_id_bt_sideband_encoder_feedback_v2_t { - uint8_t num_sidebands; + uint32_t num_sidebands; /** number of sideband payloads */ #ifdef __H2XML__ imcl_param_id_bt_sideband_encoder_feedback_v2_payload_t payload[0]; From 9bea324a3e88c9145651d41d64519a0bfe4a8b44 Mon Sep 17 00:00:00 2001 From: himachau Date: Fri, 6 Mar 2026 19:56:15 +0530 Subject: [PATCH 06/24] fwk: Always-on voice buffering to DDR in LPI. Audio DAM module buffers incoming PCM in LPI and drains it in batches to a downstream endpoint that writes into an HLOS-provided DDR shared circular buffer. HLOS reads from DDR asynchronously and uses watermark events to coordinate writer progress. Signed-off-by: ffrancis --- .../core/inc/container_utils.h | 1 + .../container_utils/core/src/cu_cmd_handler.c | 48 ++++++ .../cmn/container_utils/core/src/cu_gpr_if.c | 1 + .../ext/metadata/src/gen_topo_metadata.c | 140 ++++++++++++++++++ .../metadata/src/gen_topo_metadata_island.c | 21 +++ .../gen_cntr/core/src/gen_cntr_cmd_handler.c | 5 + .../module/imcl/api/imcl_dam_detection_api.h | 10 ++ .../metadata/api/dam_batch_metadata_api.h | 52 +++++++ .../module/metadata/api/module_cmn_metadata.h | 40 +++++ .../api/audio_dam_buffer_api.h | 14 ++ .../capi/src/capi_audio_dam_buffer.c | 47 ++++++ .../capi/src/capi_audio_dam_buffer_i.h | 10 ++ .../src/capi_audio_dam_buffer_imc_utils.c | 11 +- .../capi/src/capi_audio_dam_buffer_island.c | 14 +- ...capi_audio_dam_buffer_output_ctrl_island.c | 53 +++++++ .../lib/inc/audio_dam_driver.h | 3 +- .../lib/src/audio_dam_driver_read_island.c | 13 +- .../capi/src/capi_history_buffer.c | 63 +++++--- .../capi/src/capi_history_buffer_i.h | 2 +- .../capi/src/capi_history_buffer_imcl_utils.c | 136 ++++++++++------- .../capi/src/capi_history_buffer_imcl_utils.h | 22 ++- .../src/capi_sh_mem_pull_push_mode.c | 51 ++++++- .../src/pull_push_mode.h | 3 + 23 files changed, 666 insertions(+), 94 deletions(-) create mode 100644 fwk/spf/interfaces/module/metadata/api/dam_batch_metadata_api.h diff --git a/fwk/spf/containers/cmn/container_utils/core/inc/container_utils.h b/fwk/spf/containers/cmn/container_utils/core/inc/container_utils.h index 6070d639..ac2e9590 100644 --- a/fwk/spf/containers/cmn/container_utils/core/inc/container_utils.h +++ b/fwk/spf/containers/cmn/container_utils/core/inc/container_utils.h @@ -726,6 +726,7 @@ ar_result_t cu_register_module_events(cu_base_t *me_ptr, gpr_packet_t *packet_pt ar_result_t cu_register_module_events_v2(cu_base_t *me_ptr, gpr_packet_t *packet_ptr); ar_result_t cu_handle_cntr_events_reg_dereg(cu_base_t *me_ptr, topo_reg_event_t *reg_event_payload_ptr, bool_t is_register); ar_result_t cu_raise_container_events_to_clients(cu_base_t *me_ptr, uint32_t event_id, int8_t *payload_ptr, uint32_t payload_size); +ar_result_t cu_handle_md_tracking_internal_event(cu_base_t * me_ptr, gpr_packet_t *packet_ptr, spf_cfg_data_type_t cfg_type); ar_result_t cu_graph_connect(cu_base_t *me_ptr); ar_result_t cu_graph_control_connect(cu_base_t *me_ptr); diff --git a/fwk/spf/containers/cmn/container_utils/core/src/cu_cmd_handler.c b/fwk/spf/containers/cmn/container_utils/core/src/cu_cmd_handler.c index 99b149be..9cd40197 100644 --- a/fwk/spf/containers/cmn/container_utils/core/src/cu_cmd_handler.c +++ b/fwk/spf/containers/cmn/container_utils/core/src/cu_cmd_handler.c @@ -1779,3 +1779,51 @@ ar_result_t cu_process_cmd_queue(cu_base_t *me_ptr) me_ptr->flags.apm_cmd_context = FALSE; return result; } +ar_result_t cu_handle_md_tracking_internal_event(cu_base_t *me_ptr, gpr_packet_t *packet_ptr, spf_cfg_data_type_t cfg_type) +{ + ar_result_t result = AR_EOK; + + // Validate packet pointer + if (NULL == packet_ptr) + { + CU_MSG(me_ptr->gu_ptr->log_id, DBG_ERROR_PRIO, "invalid packet_ptr received."); + return AR_EBADPARAM; + } + + module_cmn_md_tracking_md_event_rsp_handle_t *tracking_event_ptr = GPR_PKT_GET_PAYLOAD(module_cmn_md_tracking_md_event_rsp_handle_t, packet_ptr); + + switch (tracking_event_ptr->tracking_rsp_cfg) + { + case TRACKING_MD_EVENT_RSP_SET_CFG: + { + + uint8_t *param_data_ptr = (uint8_t *)(tracking_event_ptr + 1); + + result = cu_set_get_cfgs_packed_loop(me_ptr, + param_data_ptr, + packet_ptr->dst_port, + tracking_event_ptr->tracking_event_payload_size, + TRUE, /* is_set_cfg */ + FALSE, /*is_out_of_band*/ + FALSE, /*is_deregister*/ + cfg_type); + + CU_MSG(me_ptr->gu_ptr->log_id, DBG_HIGH_PRIO, "done tracking MD event with result %lu", result); + + __gpr_cmd_free(packet_ptr); + break; + + } + default: + { + result = CAPI_EBADPARAM; + CU_MSG(me_ptr->gu_ptr->log_id, DBG_ERROR_PRIO, "Unsupported Tracking event RSP CFG: %lu", tracking_event_ptr->tracking_rsp_cfg); + + __gpr_cmd_free(packet_ptr); + break; + } + } + + + return result; +} diff --git a/fwk/spf/containers/cmn/container_utils/core/src/cu_gpr_if.c b/fwk/spf/containers/cmn/container_utils/core/src/cu_gpr_if.c index 7d7b021e..1ed940d8 100644 --- a/fwk/spf/containers/cmn/container_utils/core/src/cu_gpr_if.c +++ b/fwk/spf/containers/cmn/container_utils/core/src/cu_gpr_if.c @@ -72,6 +72,7 @@ uint32_t cu_gpr_callback(gpr_packet_t *packet, void *callback_data) case IMCL_INTER_PROC_TRIGGER_MSG_GPR: case IMCL_INTER_PROC_POLLING_MSG_GPR: case IMCL_INTER_PROC_PEER_STATE_UPDATE: + case EVENT_ID_MODULE_CMN_METADATA_CUSTOM_TRACKING_EVENT: // for polling message, first push to cmd Q. from there, we'll open the message and push to the ctrl port // Q // which will be polled at process boundary diff --git a/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata.c b/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata.c index 8b69e1fd..aa8ce8bc 100644 --- a/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata.c +++ b/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata.c @@ -15,6 +15,7 @@ #include "gen_topo_capi.h" #include "spf_ref_counter.h" #include "thin_topo_inline.h" +#include "dam_batch_metadata_api.h" static ar_result_t gen_topo_metadata_create_with_tracking(gen_topo_t * topo_ptr, module_cmn_md_list_t ** md_list_pptr, @@ -524,6 +525,124 @@ static ar_result_t gen_topo_raise_md_tracking_event(gen_topo_tracking_md_context return result; } +static ar_result_t gen_topo_raise_custom_md_tracking_event(gen_topo_tracking_md_context_t *cb_context_ptr, uint32_t ref_count) +{ + + //define outgoing payload struct + struct module_tracking_event_payload_t + { + module_cmn_md_tracking_md_event_rsp_handle_t tracking_header; + + struct tracking_data_t + { + apm_module_param_data_t header; + dam_batch_end_md_gen_t data; + }tracking_data; + }; + + ar_result_t result = AR_EOK; + if (NULL == cb_context_ptr->tracking_payload_ptr) + { + TOPO_MSG(cb_context_ptr->log_id, + DBG_HIGH_PRIO, + "MD_DBG: NULL md_tracking_ptr = 0x%X in callback", + cb_context_ptr->tracking_payload_ptr); + return result; + } + + if ((MODULE_CMN_MD_TRACKING_EVENT_POLICY_EACH == cb_context_ptr->flags.tracking_policy) || + ((MODULE_CMN_MD_TRACKING_EVENT_POLICY_LAST == cb_context_ptr->flags.tracking_policy) && (0 == ref_count))) + { + + uint32_t opcode = EVENT_ID_MODULE_CMN_METADATA_CUSTOM_TRACKING_EVENT; + + struct module_tracking_event_payload_t md_payload_send; + memset(&md_payload_send, 0, sizeof(md_payload_send)); + + module_cmn_md_tracking_payload_t *md_tracking_ptr = + (module_cmn_md_tracking_payload_t *)cb_context_ptr->tracking_payload_ptr; + + md_payload_send.tracking_header.tracking_payload.metadata_id = cb_context_ptr->metadata_id; + md_payload_send.tracking_header.tracking_payload.source_module_instance = md_tracking_ptr->src_port; + md_payload_send.tracking_header.tracking_payload.module_instance_id = cb_context_ptr->module_instance_id; + md_payload_send.tracking_header.tracking_payload.token_lsw = md_tracking_ptr->token_lsw; + md_payload_send.tracking_header.tracking_payload.token_msw = md_tracking_ptr->token_msw; + md_payload_send.tracking_header.tracking_payload.flags = (0 == ref_count) ? TRUE : FALSE; + md_payload_send.tracking_header.tracking_payload.status = cb_context_ptr->render_status; + + md_payload_send.tracking_header.tracking_rsp_cfg = TRACKING_MD_EVENT_RSP_SET_CFG; + md_payload_send.tracking_header.tracking_event_payload_size = sizeof(struct tracking_data_t); + + /* populate the payload header */ + dam_batch_end_md_gen_t *md_payload_ptr = (dam_batch_end_md_gen_t *)cb_context_ptr->md_payload_ptr; + + if (NULL == md_payload_ptr) + { + TOPO_MSG(cb_context_ptr->log_id, + DBG_ERROR_PRIO, + "MD_DBG: Null metadata payload pointer for DAM_BATCH_DONE_MD_ID_MARKER"); + return AR_EBADPARAM; + } + + apm_module_param_data_t *payload_header_ptr = &md_payload_send.tracking_data.header; + dam_batch_end_md_gen_t *payload_data_ptr = &md_payload_send.tracking_data.data; + + payload_header_ptr->module_instance_id = md_tracking_ptr->dest_port; + payload_header_ptr->param_id = md_payload_ptr->param_id; + payload_header_ptr->param_size = sizeof(dam_batch_end_md_gen_t); + + memscpy(payload_data_ptr, payload_header_ptr->param_size, md_payload_ptr, payload_header_ptr->param_size); + + TOPO_MSG(cb_context_ptr->log_id, + DBG_HIGH_PRIO, + "MD_DBG: md_event_param_id = %lu, md_param out_ch_idx = %lu", + payload_header_ptr->param_id, payload_data_ptr->output_port_idx); + + bool_t is_registered = FALSE; + (void)__gpr_cmd_is_registered(md_tracking_ptr->src_port, &is_registered); + // if stream close is done prior to render EOS, then client must not receive render EOS + if (is_registered) + { + gpr_cmd_alloc_send_t args; + args.src_domain_id = md_tracking_ptr->src_domain_id; + args.dst_domain_id = md_tracking_ptr->dst_domain_id; + args.src_port = md_tracking_ptr->src_port; + args.dst_port = md_tracking_ptr->dest_port; + args.token = md_tracking_ptr->token_msw; + args.opcode = opcode; + args.payload = &md_payload_send; + args.payload_size = sizeof(struct module_tracking_event_payload_t); + args.client_data = 0; + __gpr_cmd_alloc_send(&args); + + TOPO_MSG(cb_context_ptr->log_id, + DBG_HIGH_PRIO, + "MD_DBG: Raising tracking event for MD_ID (0x%lx) (src port 0x%lX), render status = %lu, " + "policy 0x%x ref_count = %lu cmd_opcode 0x%lX", + cb_context_ptr->metadata_id, + md_tracking_ptr->src_port, + cb_context_ptr->render_status, + cb_context_ptr->flags.tracking_policy, + ref_count, + opcode); + } + else + { + TOPO_MSG(cb_context_ptr->log_id, + DBG_HIGH_PRIO, + "MD_DBG: Not Raising tracking event for MD_ID (0x%lx) as client has closed the source module. " + "(src port 0x%lX), render status = %lu, policy 0x%x ref_count = %lu cmd_opcode 0x%lX", + cb_context_ptr->metadata_id, + md_tracking_ptr->src_port, + cb_context_ptr->render_status, + cb_context_ptr->flags.tracking_policy, + ref_count, + opcode); + } + } + return result; +} + void gen_topo_send_md_render_status(void *context_ptr, uint32_t ref_count) { gen_topo_tracking_md_context_t *cb_context_ptr = (gen_topo_tracking_md_context_t *)context_ptr; @@ -537,6 +656,11 @@ void gen_topo_send_md_render_status(void *context_ptr, uint32_t ref_count) { gen_topo_raise_eos_tracking_event(cb_context_ptr, ref_count); } + else if((DAM_BATCH_END_MD_ID_MARKER == cb_context_ptr->metadata_id) && + (MODULE_CMN_MD_TRACKING_USE_CUSTOM_EVENT == cb_context_ptr->tracking_payload_ptr->flags.requires_custom_event)) + { + gen_topo_raise_custom_md_tracking_event(cb_context_ptr, ref_count); + } else { gen_topo_raise_md_tracking_event(cb_context_ptr, ref_count); @@ -737,6 +861,22 @@ static ar_result_t gen_topo_metadata_create_with_tracking(gen_topo_t TOPO_MSG(log_id, DBG_ERROR_PRIO, "MD_DBG: Failed to create a metadata tracking pointer"); THROW(ar_result, result); } + + module_cmn_md_tracking_payload_t *md_tracking_payload_ptr = &tracking_info_ptr->tracking_payload; + + if( 0 == md_tracking_payload_ptr->src_domain_id && MODULE_CMN_MD_IS_INTERNAL_CLIENT_MD == flags.is_client_metadata) + { + uint32_t host_domain; + __gpr_cmd_get_host_domain_id(&host_domain); + + md_tracking_payload_ptr->src_domain_id = host_domain; + } + + if(0 == md_tracking_payload_ptr->dst_domain_id) + { + md_tracking_payload_ptr->dst_domain_id = md_tracking_payload_ptr->src_domain_id; + } + md_ptr->tracking_ptr = md_tracking_ref_ptr; memscpy(md_ptr->tracking_ptr, sizeof(module_cmn_md_tracking_payload_t), diff --git a/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata_island.c b/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata_island.c index b70b1ca1..6c7a18eb 100644 --- a/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata_island.c +++ b/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata_island.c @@ -15,6 +15,7 @@ #include "gen_topo_capi.h" #include "spf_ref_counter.h" #include "thin_topo_inline.h" +#include "dam_batch_metadata_api.h" #define PRINT_MD_PROP_DBG_ISLAND(str1, str2, len_per_ch, str3, ...) \ TOPO_MSG_ISLAND(topo_ptr->gu.log_id, \ @@ -1654,6 +1655,26 @@ capi_err_t gen_topo_capi_metadata_destroy(void * context_ptr, override_ctrl_to_disable_tracking_event); break; } + case DAM_BATCH_END_MD_ID_MARKER: + { + if (NULL != md_ptr->tracking_ptr) + { + // Exit island here since we need to do a mem free operation which is in nlpi + gen_topo_exit_island_temporarily(topo_ptr); + gen_topo_raise_tracking_event(topo_ptr, + md_sink_miid, + md_list_ptr, + !is_dropped, + (void *)&md_ptr->metadata_buf, //since metadata have payload + override_ctrl_to_disable_tracking_event); + } + + //Dont need to exit island here since gen_topo_free_md can return nodes in island as well + //if md node belongs to lpi pool + ar_result = gen_topo_free_md(topo_ptr, md_list_ptr, md_ptr, head_pptr); + + break; + } default: // For MODULE_CMN_MD_ID_DFG, this is sufficient { #ifdef METADATA_DEBUGGING diff --git a/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c b/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c index c0c65ca6..d278c113 100644 --- a/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c +++ b/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c @@ -591,6 +591,11 @@ ar_result_t gen_cntr_gpr_cmd(cu_base_t *base_ptr) cu_set_get_cfgs_packed(base_ptr, packet_ptr, SPF_CFG_DATA_SHARED_PERSISTENT); break; } + case EVENT_ID_MODULE_CMN_METADATA_CUSTOM_TRACKING_EVENT: + { + cu_handle_md_tracking_internal_event(base_ptr, packet_ptr, SPF_CFG_DATA_TYPE_DEFAULT); + break; + } default: { TRY(result, cu_gpr_cmd(base_ptr)); diff --git a/fwk/spf/interfaces/module/imcl/api/imcl_dam_detection_api.h b/fwk/spf/interfaces/module/imcl/api/imcl_dam_detection_api.h index c7186a5d..1228aea0 100644 --- a/fwk/spf/interfaces/module/imcl/api/imcl_dam_detection_api.h +++ b/fwk/spf/interfaces/module/imcl/api/imcl_dam_detection_api.h @@ -314,6 +314,16 @@ typedef enum * will exit island when the buffer is full, enter when the DAM is buffering * and Downstream module is done processing, wait for PARAM_ID_AUDIO_DAM_ALLOW_DCM_ISLAND_ENTRY */ + AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING_MD_TRACKING_EVENT = 5, + /* Open gate for corresponding gate and send out data in batches along with + * the metadata i.e. only after batch amount of data is accumulated in the + * dam buffer, DAM sends the batch data and metadata repeatedly till gate + * close is requested. The metadata is inserted at the end of the batch. + * This metadata is consumed by the downstream and a GPR event is raised + * to DAM indicating that the batch is consumed. DAM will exit island when + * the buffer is full, enters back when the DAM is buffering and the metadata + * is consumed, wait for PARAM_ID_AUDIO_DAM_HANDLE_BATCH_TRACKING_EVENT */ + AUDIO_DAM_BATCH_INVALID = 0xFFFFFFFF /* Invalid option for gate_open */ } audio_dam_gate_ctrl_op_t; diff --git a/fwk/spf/interfaces/module/metadata/api/dam_batch_metadata_api.h b/fwk/spf/interfaces/module/metadata/api/dam_batch_metadata_api.h new file mode 100644 index 00000000..61886a3d --- /dev/null +++ b/fwk/spf/interfaces/module/metadata/api/dam_batch_metadata_api.h @@ -0,0 +1,52 @@ +#ifndef DAM_BATCH_METADATA_API_H +#define DAM_BATCH_METADATA_API_H +/** + * \file dam_batch_metadata_api.h + * \brief + * This file contains metadata IDs used by DAM to send batch tracking + * information downstream. + * + * \copyright + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#include "ar_defs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /*__cplusplus*/ + +/** + Metadata ID for indicating the end of DAM Batch. + + module_cmn_md_t structure has to set the metadata_id field to this id + when the metadata is DAM_BATCH_END_MD_ID_MARKER. + + DAM_BATCH_END_MD_ID_MARKER payload for internal propagation within SPF is defined by dam_batch_end_md_gen_t +*/ +#define DAM_BATCH_END_MD_ID_MARKER 0x0A00106A + + +typedef struct dam_batch_end_md_gen_t dam_batch_end_md_gen_t; + +/** Payload sent along with DAM_BATCH_END_MD_ID_MARKER metadata +*/ +struct dam_batch_end_md_gen_t +{ + uint32_t param_id; + /*Parameter ID need to be handled on recieving tracking event*/ + + uint32_t output_port_idx; + /*DAM output port index where Duty Cycling is enabled*/ +}; + + +#ifdef __cplusplus +} +#endif /*__cplusplus */ + +// clang-format on + +#endif /* #ifndef DAM_BATCH_METADATA_API_H */ diff --git a/fwk/spf/interfaces/module/metadata/api/module_cmn_metadata.h b/fwk/spf/interfaces/module/metadata/api/module_cmn_metadata.h index ee1e8afd..29b7d42e 100644 --- a/fwk/spf/interfaces/module/metadata/api/module_cmn_metadata.h +++ b/fwk/spf/interfaces/module/metadata/api/module_cmn_metadata.h @@ -380,6 +380,46 @@ struct module_cmn_md_eos_t /**************************************** DFG - End *************************************/ +/* + * The custom event is raised if the SPF internal metadata is set as custom tracking metadata. + * The event will be sent to the GPR client from SPF in the following scenarios. + * + * Metadata is dropped in the SPF. + * Metadata is consumed by the the module and not propagated further. + * All sink modules including the hardware end-points would consume the metadata. +*/ +#define EVENT_ID_MODULE_CMN_METADATA_CUSTOM_TRACKING_EVENT 0x0300100F + +typedef struct module_cmn_md_tracking_md_event_rsp_handle_t module_cmn_md_tracking_md_event_rsp_handle_t; + +/*============================================================================== + Type Definitions +==============================================================================*/ +typedef enum +{ + TRACKING_MD_EVENT_RSP_INVALID = 0, + /* Invalid option for the tracking cmd */ + + TRACKING_MD_EVENT_RSP_SET_CFG = 1, + /* Handle the tracking event as SET config after reciving it at the CU layer. */ + +} module_cmn_tracking_event_rsp_cfg_t; + +/** Custom tracking event payload for metadata +*/ +struct module_cmn_md_tracking_md_event_rsp_handle_t +{ + metadata_tracking_event_t tracking_payload; + /* Tracking payload for the metadata used for generic events*/ + + module_cmn_tracking_event_rsp_cfg_t tracking_rsp_cfg; + /* GPR Tracking event command configuration */ + + uint32_t tracking_event_payload_size; + /* Size of the tracking metadata event payload */ + +}; + /** @} */ /* end_addtogroup capi_if_ext_metadata */ #ifdef __cplusplus diff --git a/fwk/spf/modules/audio_dam_buffer/api/audio_dam_buffer_api.h b/fwk/spf/modules/audio_dam_buffer/api/audio_dam_buffer_api.h index d2d407e8..f8be99c1 100644 --- a/fwk/spf/modules/audio_dam_buffer/api/audio_dam_buffer_api.h +++ b/fwk/spf/modules/audio_dam_buffer/api/audio_dam_buffer_api.h @@ -360,6 +360,20 @@ struct param_id_audio_ctrl_to_data_port_map_t #include "spf_end_pack.h" ; +/*============================================================================== + Constants +==============================================================================*/ + + /** + * PARAM_ID_AUDIO_DAM_HANDLE_BATCH_END_TRACKING_EVENT + * + * DAM sends this parameter to downstream as part of the tracking metadata payload. + * After consuming the batch, downstream raise a GPR tracking event back to inform + * about batch consumption through this parameter. + * + */ +#define PARAM_ID_AUDIO_DAM_HANDLE_BATCH_END_TRACKING_EVENT 0x08001BD3 + /** @} <-- End of the module -- > */ diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c index 04a0ec58..058a9cae 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c @@ -1456,6 +1456,52 @@ capi_err_t capi_audio_dam_buffer_set_param_non_island(capi_t *ca break; } + case PARAM_ID_AUDIO_DAM_HANDLE_BATCH_END_TRACKING_EVENT: + { + + if (params_ptr->actual_data_len < sizeof(dam_batch_end_md_gen_t)) + { + DAM_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "capi_audio_dam: Param id 0x%lx Bad param size %lu", + (uint32_t)param_id, + params_ptr->actual_data_len); + return CAPI_ENEEDMORE; + } + + dam_batch_end_md_gen_t *md_event_ptr = (dam_batch_end_md_gen_t *)params_ptr->data_ptr; + + uint32_t op_port_index = md_event_ptr->output_port_idx; + + uint32_t op_arr_index = get_arr_index_from_port_index(me_ptr, op_port_index, FALSE); + + if(UMAX_32 == op_arr_index) + { + DAM_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "capi_audio_dam: Param id 0x%lx, recieved invalid port index %lu", + (uint32_t)param_id, + params_ptr->actual_data_len); + return CAPI_EBADPARAM; + } + + // Store the flag + me_ptr->out_port_info_arr[op_arr_index].ready_for_island_entry = TRUE; + + bool_t can_enter_island = capi_audio_dam_check_island_entry_cond(me_ptr); + + // island entry conditions, pending_bytes -> 0, DCM mode enabled and port is intialized + if (can_enter_island) + { + result |= capi_dam_duty_cycling_buf_send_message_to_dcm(me_ptr, (uint32_t)SPF_MSG_CMD_DCM_REQ_FOR_UNBLOCK_ISLAND_ENTRY); + } + else + { + DAM_MSG(me_ptr->miid, DBG_MED_PRIO, "Island entry conditions not met.. Buffering data"); + } + + break; + } default: { DAM_MSG(me_ptr->miid, DBG_ERROR_PRIO, "capi_audio_dam: Unsupported Param id ::0x%x \n", param_id); @@ -2391,6 +2437,7 @@ static capi_err_t capi_audio_dam_data_port_op_handler(capi_audio_dam_t *me_ptr, me_ptr->out_port_info_arr[arr_index].is_open = FALSE; me_ptr->out_port_info_arr[arr_index].gate_ctrl_op = AUDIO_DAM_BATCH_INVALID; me_ptr->out_port_info_arr[arr_index].is_dcm_duty_cycling_enabled = FALSE; + me_ptr->out_port_info_arr[arr_index].handle_md_batch_tracking = FALSE; } break; diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h index e5b538c0..25879549 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h @@ -25,6 +25,7 @@ #include "capi_intf_extn_data_port_operation.h" #include "capi_intf_extn_metadata.h" #include "other_metadata.h" +#include "dam_batch_metadata_api.h" #include "platform_internal_dcm_if.h" #ifdef __cplusplus @@ -179,6 +180,9 @@ typedef struct /* indicate if the EOS is yet to sent */ bool_t is_dcm_duty_cycling_enabled; bool_t ready_for_island_entry; + + /*Indicate if metadata needs to be sent after every batch in batching mode*/ + bool_t handle_md_batch_tracking; } _aud_dam_output_port_info; typedef struct @@ -359,6 +363,8 @@ capi_err_t capi_audio_dam_imcl_trigger_island_entry(capi_audio_dam_t *me_ptr, uint32_t op_arr_index, vw_imcl_header_t *header_ptr); +bool_t capi_audio_dam_check_island_entry_cond(capi_audio_dam_t* me_ptr); + ///////////////////////////////////// GENERIC UTILS /////////////////////////////////////// capi_err_t capi_check_and_close_the_gate(capi_audio_dam_t *me_ptr, uint32_t op_arr_index, bool_t is_destroy); @@ -420,6 +426,10 @@ capi_err_t capi_dam_insert_flushing_eos_at_out_port(capi_audio_dam_t *me_ptr, bool_t skip_voting_on_eos, eos_type_t eos_type); +capi_err_t capi_dam_insert_tracking_md_at_out_port(capi_audio_dam_t *me_ptr, + capi_stream_data_t *output, + uint32_t output_port_index); + capi_vtbl_t *capi_audio_dam_buffer_get_vtable(); static inline POSAL_HEAP_ID capi_audio_dam_get_peer_heap_id(capi_audio_dam_t *me_ptr, uint32_t op_arr_idx) diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c index d4cf322d..6ff92cfe 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c @@ -21,7 +21,6 @@ static capi_err_t capi_audio_dam_imcl_set_hdlr_drain_history_data(capi_audio_dam uint32_t op_arr_index, param_id_audio_dam_data_flow_ctrl_t *cfg_ptr); -static bool_t capi_audio_dam_check_island_entry_cond(capi_audio_dam_t* me_ptr); static capi_err_t capi_audio_dam_raise_allow_duty_cycling(capi_audio_dam_t *me_ptr, bool_t allow_duty_cycling) { capi_err_t result = CAPI_EOK; @@ -164,6 +163,7 @@ capi_err_t capi_audio_dam_imcl_set_hdlr_flow_ctrl_v2(capi_audio_dam_t *me_ptr, } case AUDIO_DAM_BATCH_STREAM: case AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING: + case AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING_MD_TRACKING_EVENT: { DAM_MSG(me_ptr->miid, DBG_HIGH_PRIO, @@ -177,12 +177,17 @@ capi_err_t capi_audio_dam_imcl_set_hdlr_flow_ctrl_v2(capi_audio_dam_t *me_ptr, result = capi_audio_dam_imcl_handle_gate_open(me_ptr, op_arr_index, (param_id_audio_dam_data_flow_ctrl_t *)cfg_ptr); - if (AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING == cfg_ptr->gate_ctrl) + if (AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING == cfg_ptr->gate_ctrl || AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING_MD_TRACKING_EVENT == cfg_ptr->gate_ctrl) { me_ptr->out_port_info_arr[op_arr_index].is_dcm_duty_cycling_enabled = TRUE; me_ptr->out_port_info_arr[op_arr_index].ready_for_island_entry = TRUE; me_ptr->out_port_info_arr[op_arr_index].gate_ctrl_op = AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING; + if(AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING_MD_TRACKING_EVENT == cfg_ptr->gate_ctrl) + { + me_ptr->out_port_info_arr[op_arr_index].handle_md_batch_tracking = TRUE; + } + capi_audio_dam_raise_allow_duty_cycling(me_ptr, TRUE); } break; @@ -239,7 +244,7 @@ static capi_err_t capi_audio_dam_imcl_set_hdlr_drain_history_data(capi_audio_dam return result; } -static bool_t capi_audio_dam_check_island_entry_cond(capi_audio_dam_t* me_ptr) +bool_t capi_audio_dam_check_island_entry_cond(capi_audio_dam_t* me_ptr) { uint32_t pending_bytes_to_read = 0; bool_t trigger_dcm_island_entry = TRUE; diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c index bc73efe0..866e736b 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c @@ -253,12 +253,14 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i } } + bool_t is_batch_sent = false; result = audio_dam_stream_read(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, num_output_chs, output[port_index]->buf_ptr, &is_timestamp_valid, &output[port_index]->timestamp, - &output_frame_len_us); + &output_frame_len_us, + &is_batch_sent); if (AR_ENEEDMORE == result) { @@ -273,6 +275,16 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i capi_audio_dam_handle_pending_eos(me_ptr, output, arr_idx, port_index); } + if(me_ptr->out_port_info_arr[arr_idx].handle_md_batch_tracking && is_batch_sent) //send the marker metadata as batch is sent + { + if (CAPI_EOK == (result = capi_dam_insert_tracking_md_at_out_port(me_ptr, output[port_index], port_index))) + { + DAM_MSG_ISLAND(me_ptr->miid, + DBG_HIGH_PRIO, + "DAM: Inserted tracking MD after Batch"); + } + } + output[port_index]->flags.is_timestamp_valid = is_timestamp_valid; if (me_ptr->out_port_info_arr[arr_idx].ftrt_unread_data_len_in_us) diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c index c185af09..1cfcd8c1 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c @@ -289,3 +289,56 @@ capi_err_t capi_dam_insert_flushing_eos_at_out_port(capi_audio_dam_t *me_ptr, return capi_result; } + +capi_err_t capi_dam_insert_tracking_md_at_out_port(capi_audio_dam_t *me_ptr, + capi_stream_data_t *output, + uint32_t output_port_index) +{ + capi_err_t capi_result = CAPI_EOK; + capi_stream_data_v2_t *out_stream_ptr = (capi_stream_data_v2_t *)output; + module_cmn_md_tracking_t batch_tracking_md_info; + memset(&batch_tracking_md_info, 0, sizeof(batch_tracking_md_info)); + + if (CAPI_STREAM_V2 != out_stream_ptr->flags.stream_data_version) + { + DAM_MSG_ISLAND(me_ptr->miid, DBG_ERROR_PRIO, "capi_audio_dam: stream version must be 1"); + return CAPI_EFAILED; + } + module_cmn_md_list_t **md_list_pptr = &out_stream_ptr->metadata_list_ptr; + module_cmn_md_t *new_md_ptr = NULL; + dam_batch_end_md_gen_t *md_payload_ptr = NULL; + capi_heap_id_t heap; + heap.heap_id = (uint32_t)me_ptr->heap_id; + module_cmn_md_flags_t flags; + + batch_tracking_md_info.heap_info.heap_id = heap.heap_id; + batch_tracking_md_info.tracking_payload.flags.requires_custom_event = MODULE_CMN_MD_TRACKING_USE_CUSTOM_EVENT; + batch_tracking_md_info.tracking_payload.dest_port = me_ptr->miid; + batch_tracking_md_info.tracking_payload.src_port = me_ptr->miid; + + flags.word = 0; + flags.version = MODULE_CMN_MD_VERSION; + flags.tracking_mode = MODULE_CMN_MD_TRACKING_CONFIG_ENABLE_FOR_DROP_OR_CONSUME; + flags.tracking_policy = MODULE_CMN_MD_TRACKING_EVENT_POLICY_LAST; + /*if metadata splits into multiple path, DAM should be informed only when last event done*/ + + capi_result = me_ptr->metadata_handler.metadata_create_with_tracking(me_ptr->metadata_handler.context_ptr, + md_list_pptr, + sizeof(dam_batch_end_md_gen_t), + heap, + DAM_BATCH_END_MD_ID_MARKER, + flags, + &batch_tracking_md_info, + &new_md_ptr); + + new_md_ptr->metadata_id = DAM_BATCH_END_MD_ID_MARKER; + new_md_ptr->offset = output->buf_ptr[0].actual_data_len/me_ptr->operating_mf.bytes_per_sample; + md_payload_ptr = (dam_batch_end_md_gen_t *)&new_md_ptr->metadata_buf; + md_payload_ptr->output_port_idx = output_port_index; //output port index to handle Duty cycling + md_payload_ptr->param_id = PARAM_ID_AUDIO_DAM_HANDLE_BATCH_END_TRACKING_EVENT; //Param ID to set after tracking event + + DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, "DAM: Created and inserted tracking MD at output port index:%lu with offset: %lu", + output_port_index, new_md_ptr->offset); + + return capi_result; +} \ No newline at end of file diff --git a/fwk/spf/modules/audio_dam_buffer/lib/inc/audio_dam_driver.h b/fwk/spf/modules/audio_dam_buffer/lib/inc/audio_dam_driver.h index 73e6b759..5526fc49 100644 --- a/fwk/spf/modules/audio_dam_buffer/lib/inc/audio_dam_driver.h +++ b/fwk/spf/modules/audio_dam_buffer/lib/inc/audio_dam_driver.h @@ -324,7 +324,8 @@ ar_result_t audio_dam_stream_read(audio_dam_stream_reader_t *reader_handle, capi_buf_t * output_buf_arr, bool_t * output_buf_ts_is_valid, int64_t * output_buf_ts, - uint32_t * output_buf_len_in_us); + uint32_t * output_buf_len_in_us, + bool_t * is_batch_sent); /* * Adjusts the read pointer diff --git a/fwk/spf/modules/audio_dam_buffer/lib/src/audio_dam_driver_read_island.c b/fwk/spf/modules/audio_dam_buffer/lib/src/audio_dam_driver_read_island.c index 1f37f51e..5dbf36c5 100644 --- a/fwk/spf/modules/audio_dam_buffer/lib/src/audio_dam_driver_read_island.c +++ b/fwk/spf/modules/audio_dam_buffer/lib/src/audio_dam_driver_read_island.c @@ -186,7 +186,8 @@ static ar_result_t audio_dam_stream_batch_read(audio_dam_stream_reader_t *reader capi_buf_t * output_buf_arr, // in/out bool_t * output_buf_ts_is_valid, // out int64_t * output_buf_ts, // out - uint32_t * output_buf_len_in_us) // out + uint32_t * output_buf_len_in_us, // out + bool_t * is_batch_sent) { ar_result_t result = AR_EOK; uint32_t requested_batch_us = reader_handle->data_batching_us; @@ -235,6 +236,10 @@ static ar_result_t audio_dam_stream_batch_read(audio_dam_stream_reader_t *reader if (AR_EOK == result) { reader_handle->pending_batch_bytes -= output_buf_arr[0].actual_data_len; + if(reader_handle->pending_batch_bytes == 0) + { + *is_batch_sent = true; + } } return result; @@ -245,7 +250,8 @@ ar_result_t audio_dam_stream_read(audio_dam_stream_reader_t *reader_handle, capi_buf_t *output_buf_arr, // in/out bool_t *output_buf_ts_is_valid, // out int64_t *output_buf_ts, // out - uint32_t *output_buf_len_in_us) // out + uint32_t *output_buf_len_in_us, // out + bool_t *is_batch_sent) { ar_result_t result = AR_EOK; @@ -270,7 +276,8 @@ ar_result_t audio_dam_stream_read(audio_dam_stream_reader_t *reader_handle, output_buf_arr, output_buf_ts_is_valid, output_buf_ts, - output_buf_len_in_us); + output_buf_len_in_us, + is_batch_sent); } else { diff --git a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer.c b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer.c index 660875c9..db55d95d 100644 --- a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer.c +++ b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer.c @@ -343,25 +343,22 @@ static capi_err_t capi_history_buffer_process(capi_t *_pif, capi_stream_data_t * if (TRUE == me_ptr->is_trigger_detected) { /* Populate flow control param payload. */ - param_id_audio_dam_data_flow_ctrl_t flow_ctrl_cfg; - flow_ctrl_cfg.is_gate_open = TRUE; - flow_ctrl_cfg.read_offset_in_us = keyword_length_in_us; + param_id_audio_dam_data_flow_ctrl_v2_t flow_ctrl_cfg; + memset(&flow_ctrl_cfg, 0, sizeof(param_id_audio_dam_data_flow_ctrl_v2_t)); - flow_ctrl_cfg.num_best_channels = 0; // TODO: update best channels info if needed. This is optional. + flow_ctrl_cfg.gate_ctrl = AUDIO_DAM_GATE_OPEN; + flow_ctrl_cfg.read_offset_in_us = keyword_length_in_us; /* Send flow control param to DAM module through control port.*/ if (CAPI_EOK != - capi_history_buffer_send_flow_ctrl_msg_to_dam(me_ptr, &flow_ctrl_cfg)) + capi_history_buffer_send_flow_ctrl_v2_msg_to_dam(me_ptr, &flow_ctrl_cfg)) { AR_MSG(DBG_ERROR_PRIO, "CAPI History Buffer: Sending GATE OPEN to DAM Failed."); me_ptr->is_trigger_detected = FALSE; return CAPI_EFAILED; } - else - { - AR_MSG(DBG_MED_PRIO, "CAPI History Buffer: Sent gate open to audio DAM module"); - } + AR_MSG(DBG_MED_PRIO, "0x%lx: CAPI History Buffer: Sent Gate open with flow 0x%lx and batch_size 0x%lx to Cntrl Port", me_ptr->miid, flow_ctrl_cfg.gate_ctrl, keyword_length_in_us); /* If keyword is detected, module needs to send the detection event to the DSP clients. Detection module populates the current detection info into the event payload and raises the detection event. @@ -672,13 +669,13 @@ static capi_err_t capi_history_buffer_set_param(capi_t *_pif, "Received history buffer reset, sending gate close to DAM"); /* Populate dam gate control IMC param payload. */ - param_id_audio_dam_data_flow_ctrl_t ctrl_cfg; - memset(&ctrl_cfg, 0, sizeof(param_id_audio_dam_data_flow_ctrl_t)); + param_id_audio_dam_data_flow_ctrl_v2_t ctrl_cfg; + memset(&ctrl_cfg, 0, sizeof(param_id_audio_dam_data_flow_ctrl_v2_t)); // populate payload for gate close - ctrl_cfg.is_gate_open = FALSE; + ctrl_cfg.gate_ctrl = AUDIO_DAM_GATE_CLOSE; - result = capi_history_buffer_send_flow_ctrl_msg_to_dam(me_ptr, &ctrl_cfg); + result = capi_history_buffer_send_flow_ctrl_v2_msg_to_dam(me_ptr, &ctrl_cfg); // update flag to show, detection event is finished. me_ptr->is_event_inprogess = FALSE; @@ -743,18 +740,42 @@ static capi_err_t capi_history_buffer_set_param(capi_t *_pif, param_id_history_buffer_mode_t *cfg_ptr = (param_id_history_buffer_mode_t *)params_ptr->data_ptr; - me_ptr->mode.data_flow_mode = cfg_ptr->data_flow_mode; - - if (me_ptr->mode.data_flow_mode != HISTORY_BUFFER_ON_DEMAND) + switch(cfg_ptr->data_flow_mode) { - me_ptr->mode.data_flow_mode = HISTORY_BUFFER_ON_DEMAND; - me_ptr->mode.batch_size_ms = 0; - - DE_DBG(me_ptr->miid, DBG_HIGH_PRIO, "Unsupported mode %u, switched to ON_DEMAND", me_ptr->mode.data_flow_mode); + case HISTORY_BUFFER_BATCHING: + { + me_ptr->data_flow_mode = HISTORY_BUFFER_BATCHING; + + if(cfg_ptr->batch_size_ms) + { + me_ptr->hist_buffer_duration_msec = cfg_ptr->batch_size_ms; + me_ptr->max_dam_buffer_size_us = (me_ptr->hist_buffer_duration_msec * 1000); + } + + me_ptr->is_event_inprogess = TRUE; + + if (CAPI_FAILED(capi_history_buffer_resize_and_batch_gate_open_to_dam(me_ptr))) + { + AR_MSG(DBG_HIGH_PRIO, "Unable to send buffer resize and gate open to DAM"); + } + + break; + } + case HISTORY_BUFFER_ON_DEMAND: + { + me_ptr->data_flow_mode = HISTORY_BUFFER_ON_DEMAND; + break; + } + default: + { + me_ptr->data_flow_mode = HISTORY_BUFFER_ON_DEMAND; + DE_DBG(me_ptr->miid, DBG_HIGH_PRIO, "Unsupported mode %u, switched to ON_DEMAND", me_ptr->data_flow_mode); + break; + } } DE_DBG(me_ptr->miid, DBG_HIGH_PRIO, "Configured data flow mode is: %u and batch size in msec: %u", - me_ptr->mode.data_flow_mode, me_ptr->mode.batch_size_ms); + me_ptr->data_flow_mode, me_ptr->hist_buffer_duration_msec); break; } diff --git a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_i.h b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_i.h index dee0eb8c..15bcadbb 100644 --- a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_i.h +++ b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_i.h @@ -132,7 +132,7 @@ typedef struct capi_history_buffer_t history_buffer_ctrl_port_info_t ctrl_port_info[HISTORY_BUFFER_MAX_CONTROL_PORTS]; /* Control port info structure, currently applicable to one input port. */ - param_id_history_buffer_mode_t mode; + uint32_t data_flow_mode; } capi_history_buffer_t; diff --git a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.c b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.c index 360980f2..e713a0c6 100644 --- a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.c +++ b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.c @@ -283,9 +283,9 @@ capi_err_t capi_history_buffer_handle_intf_extn_ctrl_port_operation(capi_history Module can use capi_history_buffer_imcl_send_to_peer() if it needs to send any intents. */ /* Send history buffer resize command to the DAM Module, to adjust the size of circular buffer */ - if (CAPI_FAILED(capi_history_buffer_imcl_send_resize_to_dam(me_ptr))) + if (CAPI_FAILED(capi_history_buffer_resize_and_batch_gate_open_to_dam(me_ptr))) { - AR_MSG(DBG_HIGH_PRIO, "Unable to send history buffer resize to DAM"); + AR_MSG(DBG_HIGH_PRIO, "Unable to send buffer resize and gate open to DAM"); } } } @@ -821,74 +821,63 @@ capi_err_t capi_history_buffer_imcl_send_to_peer(capi_event_callback_info_t *eve } /* ========================================================================= - * FUNCTION : capi_history_buffer_send_flow_ctrl_msg_to_dam + * FUNCTION : capi_history_buffer_send_flow_ctrl_v2_msg_to_dam * * Refer capi_history_buffer_imcl_utils.h for function usage. * ========================================================================= */ -capi_err_t capi_history_buffer_send_flow_ctrl_msg_to_dam(capi_history_buffer_t * me_ptr, - param_id_audio_dam_data_flow_ctrl_t *param_ptr) +capi_err_t capi_history_buffer_send_flow_ctrl_v2_msg_to_dam(capi_history_buffer_t *me_ptr, param_id_audio_dam_data_flow_ctrl_v2_t *param_ptr) { capi_err_t result = CAPI_EOK; - /* Check the control ports that are connected and check supported Intent IDs. - Need to check if each control port supports the intent ID INTENT_ID_AUDIO_DAM_DETECTION_ENGINE_CTRL - indicating its a control port to DAM */ - for (uint32_t ctrl_port_idx = 0; ctrl_port_idx < HISTORY_BUFFER_MAX_CONTROL_PORTS; ctrl_port_idx++) + // define outgoing payload structure. + struct dam_flow_ctrl_v2_payload_t { - /* Send intents to only connected port and if control port supports intent id detection ctrl */ - if ((me_ptr->ctrl_port_info[ctrl_port_idx].state != CTRL_PORT_PEER_CONNECTED) && - capi_history_buffer_check_intent_id_support(me_ptr, ctrl_port_idx, INTENT_ID_AUDIO_DAM_DETECTION_ENGINE_CTRL)) - { - continue; - } + vw_imcl_header_t header; + param_id_audio_dam_data_flow_ctrl_v2_t cfg; + }; - AR_MSG(DBG_HIGH_PRIO, - "Sending IMC message through control port id 0x%lx", - me_ptr->ctrl_port_info[ctrl_port_idx].port_id); + // get the port index, can use for loop if more than one. + uint32_t ctrl_port_idx = 0; - // get control port id for the port index. - uint32_t control_port_id = me_ptr->ctrl_port_info[ctrl_port_idx].port_id; + /* Step A - Get buffers*/ + // Request buffers for this particular control port id. + capi_buf_t buffer; - /* Step A - Get recurring buffers*/ - // Request recurring buffers for this particular control port id. - capi_buf_t buffer; - capi_history_buffer_imcl_get_recurring_buf(&me_ptr->cb_info, control_port_id, &buffer); - if (NULL == buffer.data_ptr || buffer.max_data_len < sizeof(vw_imcl_header_t)) - { - DE_DBG(me_ptr->miid, DBG_ERROR_PRIO, "Received NULL intent buffer or invalid size %lu", buffer.max_data_len); - result = CAPI_EFAILED; - } - else /* Step B- Populate and send the buffer */ - { - // create the payload with outgoing param ids. - struct dam_flow_ctrl_payload_t - { - vw_imcl_header_t header; - param_id_audio_dam_data_flow_ctrl_t cfg; - }; + result = capi_history_buffer_imcl_get_one_time_buf( &me_ptr->cb_info, + me_ptr->ctrl_port_info[ctrl_port_idx].port_id, + sizeof(struct dam_flow_ctrl_v2_payload_t), + &buffer); - struct dam_flow_ctrl_payload_t *temp = (struct dam_flow_ctrl_payload_t *)buffer.data_ptr; + if (CAPI_EOK != result) + { + AR_MSG(DBG_ERROR_PRIO, + "0x%lx: CAPI_HISTORY_BUFFER_IMCL: capi_cmn_imcl_get_one_time_buf() failed with result : %lu", + me_ptr->miid, + result); + return CAPI_EFAILED; + } - // populate param payload - uint32_t param_len = - sizeof(param_id_audio_dam_data_flow_ctrl_t) + (param_ptr->num_best_channels * sizeof(uint32_t)); + /* Step B- Populate and send the buffer */ + struct dam_flow_ctrl_v2_payload_t *temp = (struct dam_flow_ctrl_v2_payload_t *)buffer.data_ptr; - temp->header.opcode = PARAM_ID_AUDIO_DAM_DATA_FLOW_CTRL; - temp->header.actual_data_len = param_len; + buffer.actual_data_len = sizeof(struct dam_flow_ctrl_v2_payload_t); - // copy param struct param_id_audio_dam_data_flow_ctrl_t payload to intent buffer - memscpy(&temp->cfg, buffer.max_data_len - sizeof(vw_imcl_header_t), param_ptr, param_len); + temp->header.opcode = PARAM_ID_AUDIO_DAM_DATA_FLOW_CTRL_V2; + temp->header.actual_data_len = sizeof(param_id_audio_dam_data_flow_ctrl_v2_t); - buffer.actual_data_len = param_len + sizeof(vw_imcl_header_t); + memset(&temp->cfg, 0, sizeof(param_id_audio_dam_data_flow_ctrl_v2_t)); - /*To send data over to the IMCL peer*/ - imcl_outgoing_data_flag_t flags; - flags.should_send = TRUE; // FALSE == buffer is returned to fwk and not sent to peer module. - flags.is_trigger = TRUE; // FALSE == polling, refer intf extension for more details - result = capi_history_buffer_imcl_send_to_peer(&me_ptr->cb_info, &buffer, control_port_id, flags); - } - } - return result; + temp->cfg.gate_ctrl = (audio_dam_gate_ctrl_op_t) param_ptr->gate_ctrl; + temp->cfg.read_offset_in_us = param_ptr->read_offset_in_us; + + AR_MSG(DBG_LOW_PRIO, "0x%lx: CAPI_HISTORY_BUFFER: Sending data flow msg v2 %lu to DAM", me_ptr->miid, temp->cfg.gate_ctrl); + + /*To send data over to the IMCL peer*/ + imcl_outgoing_data_flag_t flags; + flags.should_send = TRUE; // FALSE == buffer is returned to fwk and not sent to peer module. + flags.is_trigger = TRUE; // FALSE == polling, refer intf extension for more details + + return capi_history_buffer_imcl_send_to_peer(&me_ptr->cb_info, &buffer, me_ptr->ctrl_port_info[ctrl_port_idx].port_id, flags); } capi_err_t capi_history_buffer_imcl_send_resize_to_dam(capi_history_buffer_t *me_ptr) @@ -959,4 +948,41 @@ capi_err_t capi_history_buffer_imcl_send_resize_to_dam(capi_history_buffer_t *me } } return result; -} \ No newline at end of file +} + +capi_err_t capi_history_buffer_resize_and_batch_gate_open_to_dam(capi_history_buffer_t *me_ptr) +{ + capi_err_t result = CAPI_EOK; + /*Send the RESIZE to the DAM Module*/ + result = capi_history_buffer_imcl_send_resize_to_dam(me_ptr); + + if (CAPI_EOK != result) + { + AR_MSG(DBG_MED_PRIO, "CAPI History Buffer: Couldn't send resize to DAM "); + return result; + } + + if(HISTORY_BUFFER_BATCHING == me_ptr->data_flow_mode) + { + + /* Populate dam gate control IMC param payload. */ + param_id_audio_dam_data_flow_ctrl_v2_t flow_ctrl_cfg; + memset(&flow_ctrl_cfg, 0, sizeof(param_id_audio_dam_data_flow_ctrl_v2_t)); + + flow_ctrl_cfg.gate_ctrl = AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING_MD_TRACKING_EVENT; + flow_ctrl_cfg.read_offset_in_us = me_ptr->max_dam_buffer_size_us; + + result = capi_history_buffer_send_flow_ctrl_v2_msg_to_dam(me_ptr, &flow_ctrl_cfg); + + if (CAPI_EOK != result) + { + AR_MSG(DBG_MED_PRIO, "0x%lx: CAPI History Buffer: Couldn't send Gate open with flow 0x%lx and batch_size 0x%lx to Cntrl Port", + me_ptr->miid, flow_ctrl_cfg.gate_ctrl, me_ptr->max_dam_buffer_size_us); + return result; + } + } + + AR_MSG(DBG_MED_PRIO, "CAPI History Buffer: DAM cfg sent successfully!"); + + return result; +} diff --git a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.h b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.h index 0c30083e..0484ae56 100644 --- a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.h +++ b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.h @@ -217,21 +217,20 @@ capi_err_t capi_history_buffer_handle_intf_extn_ctrl_port_operation(capi_history capi_err_t capi_history_buffer_handle_incoming_imc_message(capi_history_buffer_t *me_ptr, capi_buf_t *params_ptr); /* ========================================================================= - * FUNCTION : capi_history_buffer_send_flow_ctrl_msg_to_dam + * FUNCTION : capi_history_buffer_send_flow_ctrl_v2_msg_to_dam * DESCRIPTION: Sends flow control IMC message to DAM. - * is_gate_open = TRUE : sends gate open, DAM will start draining the - * buffered data + * is_gate_open = TRUE : sends gate open with flow intent, DAM will start + * draining the buffered data * is_gate_open = FALSE: sends gate close, DAM will stop draining data and - * goes back to buffering mode. + * goes back to buffering mode. * * Sending a intent to the peer module involves two steps, - * STEP-A) Get recurring buffer from framework, for this module must have registered for - * recurring buffers. If not module can request for onetime buffers. + * STEP-A) request one time buffer from framework. * STEP-B) Populate the payload of intent buffer and raise an event to send IMC message * to peer module. * ========================================================================= */ -capi_err_t capi_history_buffer_send_flow_ctrl_msg_to_dam(capi_history_buffer_t * me_ptr, - param_id_audio_dam_data_flow_ctrl_t *param_ptr); +capi_err_t capi_history_buffer_send_flow_ctrl_v2_msg_to_dam(capi_history_buffer_t * me_ptr, + param_id_audio_dam_data_flow_ctrl_v2_t *param_ptr); /* ========================================================================= * FUNCTION : capi_history_buffer_imcl_send_resize_to_dam @@ -240,6 +239,13 @@ capi_err_t capi_history_buffer_send_flow_ctrl_msg_to_dam(capi_history_buffer_t * * ========================================================================= */ capi_err_t capi_history_buffer_imcl_send_resize_to_dam(capi_history_buffer_t *me_ptr); +/* ========================================================================= + * FUNCTION : capi_history_buffer_resize_and_batch_gate_open_to_dam + * + * Utility to send resize and batch gate open command to audio dam module + * ========================================================================= */ +capi_err_t capi_history_buffer_resize_and_batch_gate_open_to_dam(capi_history_buffer_t *me_ptr); + /* ========================================================================= * FUNCTION : capi_history_buffer_validate_intent_id * diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c b/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c index 29343a43..cd9e94ef 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c @@ -597,6 +597,55 @@ static capi_err_t capi_pm_set_param(capi_t *_pif, capi_pm_check_n_enable_module_buffer_access_extension(me_ptr); break; } + case INTF_EXTN_PARAM_ID_CNTR_DUTY_CYCLING_ENABLED: + { + if (params_ptr->actual_data_len < sizeof(intf_extn_param_id_cntr_duty_cycling_enabled_t)) + { + AR_MSG(DBG_ERROR_PRIO, + "Invalid payload size for CNTR_DUTY_CYCLING_ENABLED %d", + params_ptr->actual_data_len); + return CAPI_ENEEDMORE; + } + intf_extn_param_id_cntr_duty_cycling_enabled_t *payload_ptr = + (intf_extn_param_id_cntr_duty_cycling_enabled_t *)params_ptr->data_ptr; + + me_ptr->is_cntr_duty_cycle_enabled = payload_ptr->is_cntr_duty_cycling; + + bool_t allow_duty_cycling = TRUE; + intf_extn_event_id_allow_duty_cycling_v2_t event_payload; + PULL_PUSH_MSG(miid, DBG_HIGH_PRIO, "Raise allow_duty_cycling: %d", allow_duty_cycling); + + event_payload.allow_duty_cycling = allow_duty_cycling; + + /* Create event */ + capi_event_data_to_dsp_service_t to_send; + to_send.param_id = INTF_EXTN_EVENT_ID_ALLOW_DUTY_CYCLING; + to_send.payload.actual_data_len = sizeof(intf_extn_event_id_allow_duty_cycling_v2_t); + to_send.payload.max_data_len = sizeof(intf_extn_event_id_allow_duty_cycling_v2_t); + to_send.payload.data_ptr = (int8_t *)&event_payload; + + /* Create event info */ + capi_event_info_t event_info; + event_info.port_info.is_input_port = FALSE; + event_info.port_info.is_valid = FALSE; + event_info.payload.actual_data_len = sizeof(to_send); + event_info.payload.max_data_len = sizeof(to_send); + event_info.payload.data_ptr = (int8_t *)&to_send; + + capi_result = me_ptr->cb_info.event_cb(me_ptr->cb_info.event_context, CAPI_EVENT_DATA_TO_DSP_SERVICE, &event_info); + + if (CAPI_EOK != capi_result) + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, "Failed to raise INTF_EXTN_EVENT_ID_ALLOW_DUTY_CYCLING event"); + } + else + { + PULL_PUSH_MSG(miid, DBG_HIGH_PRIO, "Raised INTF_EXTN_EVENT_ID_ALLOW_DUTY_CYCLING event allow_duty_cycling:%d", event_payload.allow_duty_cycling); + } + + PULL_PUSH_MSG(miid, DBG_LOW_PRIO, "is_cntr_duty_cycle_enabled configured to %lu", me_ptr->is_cntr_duty_cycle_enabled); + break; + } default: { CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); @@ -1111,7 +1160,7 @@ static capi_err_t capi_pm_process_get_properties(capi_pm_t *me_ptr, capi_proplis { /** Can pass the list of IE list supported by this module to * be updated in the common utlitlity */ - uint32_t supported_extension_list[] = { INTF_EXTN_IMCL, INTF_EXTN_MODULE_BUFFER_ACCESS }; + uint32_t supported_extension_list[] = { INTF_EXTN_IMCL, INTF_EXTN_MODULE_BUFFER_ACCESS, INTF_EXTN_DUTY_CYCLING_ISLAND_MODE }; uint32_t num_supported_extns = sizeof(supported_extension_list) / sizeof(uint32_t); capi_result = capi_cmn_check_and_update_intf_extn_status(num_supported_extns, supported_extension_list, diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h index b1b18f45..db2cf5b9 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h @@ -121,6 +121,9 @@ typedef struct capi_pm_t // container frame duration. uint32_t frame_dur_us; + + bool_t is_cntr_duty_cycle_enabled; + } capi_pm_t; /*------------------------------------------------------------------------ From ac22be2f0ea2d5e5c2b3790c7f44aa2024440917 Mon Sep 17 00:00:00 2001 From: himachau Date: Thu, 12 Mar 2026 21:40:02 +0530 Subject: [PATCH 07/24] Remove data path control listening on DCM island entry The data path control listening functionality has been removed from the Duty Cycle Manager (DCM) island entry path. This code block was specifically designed for IoT use cases and is not required for the general implementation. When entering island mode, the container now skips the call to gen_cntr_listen_to_controls(), which previously registered listeners for both control commands and data triggers. The control listening step should only be performed for IoT applications, but the current implementation lacks the necessary generalization to conditionally enable this behavior. This change prevents unnecessary wakeups from data triggers during island entry, improving power efficiency for non-IoT use cases. Signed-off-by: ffrancis --- fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c b/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c index d278c113..28af0d4d 100644 --- a/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c +++ b/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c @@ -2376,7 +2376,10 @@ ar_result_t gen_cntr_initiate_duty_cycle_island_entry(cu_base_t *base_ptr) if (!me_ptr->cu.pm_info.flags.module_disallows_duty_cycling) { - gen_cntr_listen_to_controls(me_ptr); + /* Listen only to the control commands and not Data triggers + wakeups. This step should not be skipped for IoT application. + TODO: Generalize this implementation*/ + //gen_cntr_listen_to_controls(me_ptr); gen_cntr_handle_events_after_cmds(me_ptr, FALSE, result); cu_send_island_entry_ack_to_dcm(&me_ptr->cu); } From 3647365e971f3babd7467891c66d4e48bb0b40d7 Mon Sep 17 00:00:00 2001 From: himachau Date: Mon, 23 Mar 2026 10:59:11 +0530 Subject: [PATCH 08/24] fwk: Add header timestamp support in shared memory for PUSH Add support for header data along with PCM in the shared memory buffer used by the PUSH module. Signed-off-by: ffrancis --- .../api/sh_mem_pull_push_mode_api.h | 97 ++++++ .../src/capi_sh_mem_pull_push_mode.c | 101 +++++- .../src/pull_push_mode.c | 322 +++++++++++++++++- .../src/pull_push_mode.h | 19 ++ 4 files changed, 531 insertions(+), 8 deletions(-) diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/api/sh_mem_pull_push_mode_api.h b/fwk/spf/modules/sh_mem_pull_push_mode/api/sh_mem_pull_push_mode_api.h index 846f9d9f..ee23e70c 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/api/sh_mem_pull_push_mode_api.h +++ b/fwk/spf/modules/sh_mem_pull_push_mode/api/sh_mem_pull_push_mode_api.h @@ -427,6 +427,101 @@ struct sh_mem_pull_push_mode_cfg_t ; typedef struct sh_mem_pull_push_mode_cfg_t sh_mem_pull_push_mode_cfg_t; +/*============================================================================== + Param ID +==============================================================================*/ + +/** @ingroup ar_spf_mod_ep_shmempp_mods + Identifier of the parameter that configures header information for Push mode. + This parameter enables timestamp information to be written alongside PCM data + in the shared circular buffer. + + @msgpayload + sh_mem_push_mode_header_cfg_t + */ +#define PARAM_ID_SH_MEM_PUSH_MODE_HEADER_CFG 0x08001BDF + +/*============================================================================== + Param structure definitions +==============================================================================*/ +#include "spf_begin_pack.h" +struct sh_mem_push_mode_header_cfg_t +{ + uint32_t header_type; + /**< Bit field to enable/disable additional information to be sent along with + the audio data in the buffer. + + Bit 0: Include UTC timestamp (0=disabled, 1=enabled) + Bits 1-31: Reserved for future use*/ + + /*#< @h2xmle_description {Bit field to enable/disable additional header information. + Bit 0: UTC timestamp + Bits 1-31: Reserved} + @h2xmle_default {0} + @h2xmle_range {0..0xFFFFFFFF} + @h2xmle_policy {Basic} */ +} +#include "spf_end_pack.h" +; +typedef struct sh_mem_push_mode_header_cfg_t sh_mem_push_mode_header_cfg_t; + +/*============================================================================== + Header Structure Definitions +==============================================================================*/ + +/** @ingroup ar_spf_mod_ep_shmempp_mods + Sync word for header validation. Value is ASCII "SPFM" (0x5350464D). */ +#define SH_MEM_PUSH_MODE_HEADER_SYNC_WORD 0x5350464D + +/** @ingroup ar_spf_mod_ep_shmempp_mods + Batch header structure that appears at the start of each batch in shared memory. + Contains sync word for validation. */ +struct sh_mem_push_mode_batch_header_t +{ + uint32_t sync_word; + /**< Sync word for validation. Must be 0x5350464D ("SPFM"). */ +}; + +typedef struct sh_mem_push_mode_batch_header_t sh_mem_push_mode_batch_header_t; + +/** @ingroup ar_spf_mod_ep_shmempp_mods + Parameter header type information structure (PARAM_ID + SIZE). + This header precedes each header type data/payload in the batch header. */ +struct sh_mem_push_mode_param_header_t +{ + uint32_t param_id; + /**< Parameter ID identifying the type of data that follows. */ + + uint32_t actual_size; + /**< Actual size of the payload in bytes */ + + uint32_t padding_size; + /**< Size of the padded bytes in param payload, to make next header SYNC_WORD 4-byte aligned. */ +}; + +typedef struct sh_mem_push_mode_param_header_t sh_mem_push_mode_param_header_t; + +/** @ingroup ar_spf_mod_ep_shmempp_mods + Header Parameter ID for UTC timestamp. */ +#define PARAM_ID_HEADER_TYPE_UTC_TIMESTAMP 0x08001BE0 + +/** @ingroup ar_spf_mod_ep_shmempp_mods + Timestamp parameter payload structure. + Can be used for both Propagated timestamp and UTC timestamp parameters. */ +struct sh_mem_push_mode_timestamp_payload_t +{ + uint32_t timestamp_us_lsw; + /**< Lower 32 bits of the 64-bit timestamp, in microseconds*/ + + uint32_t timestamp_us_msw; + /**< Upper 32 bits of the 64-bit timestamp, in microseconds*/ +}; + +typedef struct sh_mem_push_mode_timestamp_payload_t sh_mem_push_mode_timestamp_payload_t; + +/** @ingroup ar_spf_mod_ep_shmempp_mods + Header Parameter ID for PCM data. */ +#define PARAM_ID_HEADER_TYPE_PCM_DATA 0x08001BE1 /** @ingroup ar_spf_mod_ep_shmempp_mods Identifier for the Shared Memory Pull mode module, which writes data to the @@ -531,6 +626,8 @@ typedef struct sh_mem_pull_push_mode_cfg_t sh_mem_pull_push_mode_cfg_t; @h2xmlm_InsertParameter @h2xml_Select {"event_cfg_sh_mem_pull_push_mode_watermark_t"} @h2xmlm_InsertParameter + @h2xml_Select {"sh_mem_push_mode_header_cfg_t"} + @h2xmlm_InsertParameter @} <-- End of the Module --> */ diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c b/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c index cd9e94ef..f1817887 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c @@ -58,7 +58,8 @@ static void capi_pm_check_n_enable_module_buffer_access_extension(capi_pm_t *me_ // note that push/pull module only process CAPI_INTERLEAVED format, hence just checking for validaty is sufficient bool_t need_to_enable_extension = (TRUE == pull_push_check_media_fmt_validity(&me_ptr->pull_push_mode_info)) && - (CAPI_INTERLEAVED == me_ptr->pull_push_mode_info.media_fmt.data_interleaving); + (CAPI_INTERLEAVED == me_ptr->pull_push_mode_info.media_fmt.data_interleaving) && + (FALSE == me_ptr->is_header_enabled); // check if circular buffer size is set and mulitple of container framelength if (me_ptr->pull_push_mode_info.shared_circ_buf_size && me_ptr->frame_dur_us) @@ -323,6 +324,14 @@ static capi_err_t capi_pm_end(capi_t *_pif) } capi_pm_t *me_ptr = (capi_pm_t *)_pif; + // Free header buffer if allocated (PUSH mode specific cleanup) + if (NULL != me_ptr->header_buffer_ptr) + { + posal_memory_free(me_ptr->header_buffer_ptr); + me_ptr->header_buffer_ptr = NULL; + me_ptr->header_buffer_size = 0; + } + pull_push_mode_deinit(&(me_ptr->pull_push_mode_info)); return capi_result; @@ -391,6 +400,96 @@ static capi_err_t capi_pm_set_param(capi_t *_pif, } break; } + case PARAM_ID_SH_MEM_PUSH_MODE_HEADER_CFG: + { + // Validate this parameter is only for PUSH mode + if (PUSH_MODE != me_ptr->pull_push_mode_info.mode) + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, + "param id 0x%lX only supported for PUSH mode", param_id); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + + // Validate payload size + if (params_ptr->actual_data_len < sizeof(sh_mem_push_mode_header_cfg_t)) + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, + "param id 0x%lX: Insufficient payload size %d", + param_id, + params_ptr->actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + + // Get the configuration payload + sh_mem_push_mode_header_cfg_t *cfg_ptr = + (sh_mem_push_mode_header_cfg_t *)params_ptr->data_ptr; + + // Validate reserved bits + uint32_t unreserved_bits = cfg_ptr->header_type & ~(HEADER_TYPE_RESERVED_BITS); + if (unreserved_bits == 0) + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, + "No Supported header type found, header type received is 0x%x", + cfg_ptr->header_type); + break; + } + + // Calculate required header buffer size based on enabled header types + uint32_t required_size = sizeof(sh_mem_push_mode_batch_header_t) + sizeof(sh_mem_push_mode_param_header_t); // Sync word + PCM header + + if (cfg_ptr->header_type & HEADER_TYPE_UTC_TIMESTAMP) + { + required_size += sizeof(sh_mem_push_mode_param_header_t); // Timestamp param header + required_size += sizeof(sh_mem_push_mode_timestamp_payload_t); // Timestamp payload + } + + // Free existing buffer if already allocated and size not same + if (NULL != me_ptr->header_buffer_ptr && required_size != me_ptr->header_buffer_size) + { + posal_memory_free(me_ptr->header_buffer_ptr); + me_ptr->header_buffer_ptr = NULL; + me_ptr->header_buffer_size = 0; + + PULL_PUSH_MSG(miid, DBG_HIGH_PRIO, "Freed existing header buffer"); + + } + + if(NULL == me_ptr->header_buffer_ptr) + { + // Allocate new header buffer + me_ptr->header_buffer_ptr = + (uint8_t *)posal_memory_malloc(required_size, me_ptr->heap_mem.heap_id); + + if (NULL == me_ptr->header_buffer_ptr) + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, + "Failed to allocate header buffer of size %d", required_size); + CAPI_SET_ERROR(capi_result, CAPI_ENOMEMORY); + break; + } + + me_ptr->header_buffer_size = required_size; + } + + // Initialize buffer to zero + memset(me_ptr->header_buffer_ptr, 0, required_size); + + // Store the header type flags + me_ptr->header_type_flags = cfg_ptr->header_type; + // Update the module state + me_ptr->is_header_enabled = TRUE; + me_ptr->is_update_header = TRUE; + + PULL_PUSH_MSG(miid, DBG_MED_PRIO, + "Header buffer allocated: size=%d bytes, type=0x%x, enabled=%d", + required_size, + me_ptr->header_type_flags, + me_ptr->is_header_enabled); + + break; + } case PARAM_ID_MEDIA_FORMAT: { if (params_ptr->actual_data_len < sizeof(media_format_t)) diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c index 0edc1108..862f7c26 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c @@ -28,6 +28,7 @@ INCLUDE FILES FOR MODULE #else #include "test_util.h" #endif +#include "dam_batch_metadata_api.h" // Important: Disabling code to bump up thread priority whenever position buffer gets updated. // Initially priority change was introduced for the thread to be able to update position buffer @@ -564,6 +565,237 @@ capi_err_t pull_mode_read_input(capi_t *_pif, capi_stream_data_t *input[], capi_ return result; } +static capi_err_t push_mode_write_batch_header(capi_pm_t *me_ptr, + uint32_t *write_index_ptr, + uint64_t timestamp) +{ + capi_err_t result = CAPI_EOK; + + // Validate input parameters + if (NULL == me_ptr || NULL == write_index_ptr) + { + PULL_PUSH_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Invalid parameters for header write"); + return CAPI_EBADPARAM; + } + + uint32_t miid = me_ptr->pull_push_mode_info.miid; + + // Validate header buffer is allocated + if (NULL == me_ptr->header_buffer_ptr || 0 == me_ptr->header_buffer_size) + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, "Header buffer not allocated"); + return CAPI_EFAILED; + } + + // Use pre-allocated buffer + uint8_t *local_header_buf_ptr = me_ptr->header_buffer_ptr; + uint32_t max_size = me_ptr->header_buffer_size; + uint32_t local_offset = 0; + + // Clear buffer + memset(local_header_buf_ptr, 0, max_size); + + // Save the starting write index to calculate DDR addresses later + uint32_t header_start_index = *write_index_ptr; + + // 1. Write Batch Header - Sync Word + sh_mem_push_mode_batch_header_t *batch_hdr_ptr = + (sh_mem_push_mode_batch_header_t *)&local_header_buf_ptr[local_offset]; + batch_hdr_ptr->sync_word = SH_MEM_PUSH_MODE_HEADER_SYNC_WORD; + local_offset += sizeof(sh_mem_push_mode_batch_header_t); + + PULL_PUSH_MSG(miid, DBG_LOW_PRIO, + "Batch header: sync_word=0x%x", batch_hdr_ptr->sync_word); + + // 2. Write UTC Timestamp Parameter (if enabled) + if (me_ptr->header_type_flags & HEADER_TYPE_UTC_TIMESTAMP) + { + // Write parameter header + sh_mem_push_mode_param_header_t *ts_param_hdr_ptr = + (sh_mem_push_mode_param_header_t *)&local_header_buf_ptr[local_offset]; + ts_param_hdr_ptr->param_id = PARAM_ID_HEADER_TYPE_UTC_TIMESTAMP; + ts_param_hdr_ptr->actual_size = sizeof(sh_mem_push_mode_timestamp_payload_t); + ts_param_hdr_ptr->padding_size = 0; // No padding needed for timestamp + local_offset += sizeof(sh_mem_push_mode_param_header_t); + + // Write timestamp payload + sh_mem_push_mode_timestamp_payload_t *ts_payload_ptr = + (sh_mem_push_mode_timestamp_payload_t *)&local_header_buf_ptr[local_offset]; + ts_payload_ptr->timestamp_us_lsw = (uint32_t)(timestamp & 0xFFFFFFFF); + ts_payload_ptr->timestamp_us_msw = (uint32_t)(timestamp >> 32); + local_offset += sizeof(sh_mem_push_mode_timestamp_payload_t); + + PULL_PUSH_MSG(miid, DBG_LOW_PRIO, + "UTC timestamp: 0x%08x%08x", + ts_payload_ptr->timestamp_us_msw, + ts_payload_ptr->timestamp_us_lsw); + } + + // 3. Write PCM Data Parameter Header (size will be updated later) + sh_mem_push_mode_param_header_t *pcm_param_hdr_ptr = + (sh_mem_push_mode_param_header_t *)&local_header_buf_ptr[local_offset]; + pcm_param_hdr_ptr->param_id = PARAM_ID_HEADER_TYPE_PCM_DATA; + pcm_param_hdr_ptr->actual_size = 0; // Will be updated when batch ends + pcm_param_hdr_ptr->padding_size = 0; // Will be updated when batch ends + + + uint32_t pcm_param_actual_size_offset = (uint8_t*)&pcm_param_hdr_ptr->actual_size - local_header_buf_ptr; + uint32_t pcm_param_padding_size_offset = (uint8_t*)&pcm_param_hdr_ptr->padding_size - local_header_buf_ptr; + + local_offset += sizeof(sh_mem_push_mode_param_header_t); + + // Sanity check: ensure we didn't overflow the buffer + if (local_offset > max_size) + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, + "Header size %d exceeds allocated size %d", + local_offset, max_size); + return CAPI_EFAILED; + } + + // 4. Write header to DDR with wrap-around handling + pull_push_mode_t *pm_ptr = &me_ptr->pull_push_mode_info; + uint32_t circ_buf_size = pm_ptr->shared_circ_buf_size; + uint32_t bytes_to_write = local_offset; + uint32_t current_write_index = *write_index_ptr; + uint32_t bytes_written = 0; + + while (bytes_to_write) + { + // Calculate space available till end of buffer + uint32_t rem_lin_size = circ_buf_size - current_write_index; + uint32_t bytes_to_copy = MIN(bytes_to_write, rem_lin_size); + + // Copy to DDR + memscpy(pm_ptr->shared_circ_buf_start_ptr + current_write_index, + bytes_to_copy, + &local_header_buf_ptr[bytes_written], + bytes_to_copy); + + bytes_to_write -= bytes_to_copy; + bytes_written += bytes_to_copy; + + current_write_index = current_write_index + bytes_to_copy; + if (current_write_index >= pm_ptr->shared_circ_buf_size) + { + current_write_index = 0; + } + } + + // 5. Calculate DDR indices with wrap-around + uint32_t param_actual_size_sh_mem_index = + (header_start_index + pcm_param_actual_size_offset) % circ_buf_size; + me_ptr->pcm_param_actual_size_ptr = + (uint32_t *)(pm_ptr->shared_circ_buf_start_ptr + param_actual_size_sh_mem_index); + + uint32_t param_padding_sh_mem_index = + (header_start_index + pcm_param_padding_size_offset) % circ_buf_size; + me_ptr->pcm_param_padding_size_ptr = + (uint32_t *)(pm_ptr->shared_circ_buf_start_ptr + param_padding_sh_mem_index); + + + PULL_PUSH_MSG(miid, DBG_HIGH_PRIO, + "Batch header written to DDR: size=%d bytes, start_index=%d, end_index=%d", + bytes_written, *write_index_ptr, current_write_index); + + PULL_PUSH_MSG(miid, DBG_LOW_PRIO, + "SH MEM: actual_size=0x%lx (index=%d), padding_size=0x%lx (index=%d)", + *me_ptr->pcm_param_actual_size_ptr, param_actual_size_sh_mem_index, + *me_ptr->pcm_param_padding_size_ptr, param_padding_sh_mem_index); + + *write_index_ptr = current_write_index; + me_ptr->batch_bytes_written = local_offset; + + return result; +} + +capi_err_t push_mode_end_header_batch(capi_pm_t *_pif, uint64_t timestamp) +{ + capi_pm_t *capi_ptr = (capi_pm_t *)_pif; + pull_push_mode_t *me_ptr = &(capi_ptr->pull_push_mode_info); + uint32_t rem_lin_size, temp_wr_ind, padding_bytes = 0; + int8_t *write_ptr = NULL; + sh_mem_pull_push_mode_position_buffer_t *pos_buf_ptr = me_ptr->shared_pos_buf_ptr; + uint32_t miid = me_ptr->miid; + + // add padding bytes if write_index is not 4-byte aligned + uint32_t misaligned_bytes = capi_ptr->pcm_bytes_written % BYTE_FOR_HEADER_ALIGNMENT; + + if(misaligned_bytes) + { + padding_bytes = BYTE_FOR_HEADER_ALIGNMENT - misaligned_bytes; + + rem_lin_size = me_ptr->shared_circ_buf_size - capi_ptr->batch_write_index; + + write_ptr = (int8_t *)(me_ptr->shared_circ_buf_start_ptr + capi_ptr->batch_write_index); + if(padding_bytes <= rem_lin_size) + { + memset(write_ptr, 0, padding_bytes); + capi_ptr->batch_write_index += padding_bytes; + } + else + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, "Padding bytes %d exceeds remaining linear memory %d, shared memory is not 4-byte aligned", + padding_bytes, rem_lin_size); + + memset(write_ptr, 0, rem_lin_size); + capi_ptr->batch_write_index += rem_lin_size; + } + + if (capi_ptr->batch_write_index >= me_ptr->shared_circ_buf_size) + { + capi_ptr->batch_write_index = 0; + } + } + + + // Update header size + *capi_ptr->pcm_param_actual_size_ptr = capi_ptr->pcm_bytes_written; + *capi_ptr->pcm_param_padding_size_ptr = padding_bytes; + capi_ptr->batch_bytes_written += (capi_ptr->pcm_bytes_written + padding_bytes); + + temp_wr_ind = pos_buf_ptr->index; + write_ptr = (int8_t *)(me_ptr->shared_circ_buf_start_ptr + temp_wr_ind); + rem_lin_size = me_ptr->shared_circ_buf_size - temp_wr_ind; + + //cache flush now, check for watermark event and update index (Handle the wrap around case here) + if(capi_ptr->batch_bytes_written < rem_lin_size) + { +#ifndef DISABLE_CACHE_OPERATIONS + posal_cache_flush_v2(&write_ptr, capi_ptr->batch_bytes_written); +#endif + pull_push_mode_check_send_watermark_event(capi_ptr, temp_wr_ind, capi_ptr->batch_write_index); + } + else + { +#ifndef DISABLE_CACHE_OPERATIONS + posal_cache_flush_v2(&write_ptr, rem_lin_size); +#endif + pull_push_mode_check_send_watermark_event(capi_ptr, temp_wr_ind, me_ptr->shared_circ_buf_size); + + if(0 != capi_ptr->batch_write_index) + { + write_ptr = (int8_t *)(me_ptr->shared_circ_buf_start_ptr); + temp_wr_ind = 0; +#ifndef DISABLE_CACHE_OPERATIONS + posal_cache_flush_v2(&write_ptr, capi_ptr->batch_bytes_written - rem_lin_size); +#endif + pull_push_mode_check_send_watermark_event(capi_ptr, temp_wr_ind, capi_ptr->batch_write_index); + } + } + + /** update position buffer with new index*/ + pull_mode_update_pos_buffer(pos_buf_ptr, me_ptr->ist_priority, capi_ptr->batch_write_index , timestamp); + + PULL_PUSH_MSG(miid, DBG_HIGH_PRIO, + "Batch written actual bytes %lu, padding bytes %lu, next batch starts at index %d", + *capi_ptr->pcm_param_actual_size_ptr, *capi_ptr->pcm_param_padding_size_ptr, capi_ptr->batch_write_index); + + capi_ptr->is_update_header = TRUE; + + return CAPI_EOK; +} + capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], capi_stream_data_t *output[]) { capi_err_t result = CAPI_EOK; @@ -669,6 +901,32 @@ capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], cap } else // buffer access extension is disabled. { + // Use batch_write_index for tracking within batch when headers enabled + if (capi_ptr->is_header_enabled && 0 != module_buf_ptr[0].actual_data_len) + { + // Write batch header at the start of a new batch (when headers enabled and not in middle of batch) + if (capi_ptr->is_update_header) + { + capi_ptr->pcm_bytes_written = 0; + // Initialize batch tracking + capi_ptr->batch_write_index = write_index; + if (CAPI_FAILED(result = push_mode_write_batch_header(capi_ptr, &capi_ptr->batch_write_index, timestamp))) + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, "Failed to write batch header"); + return result; + } + + capi_ptr->is_update_header = FALSE; + + PULL_PUSH_MSG(miid, DBG_HIGH_PRIO, + "Batch header written at index %d, PCM data starts at index %d", + write_index, capi_ptr->batch_write_index); + } + + write_index = capi_ptr->batch_write_index; + + } + uint32_t bytes_to_copy, bytes_copied = 0, bytes_copied_per_channel = 0, bytes_copied_per_channel_now = 0; while (module_buf_ptr[0].actual_data_len > 0) { @@ -683,8 +941,12 @@ capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], cap memscpy(write_ptr, bytes_to_copy, (module_buf_ptr[0].data_ptr + bytes_copied), bytes_to_copy); #ifndef DISABLE_CACHE_OPERATIONS - posal_cache_flush_v2(&write_ptr, bytes_to_copy); + if (!capi_ptr->is_header_enabled) + { + posal_cache_flush_v2(&write_ptr, bytes_to_copy); + } #endif + module_buf_ptr[0].actual_data_len -= bytes_to_copy; bytes_copied += bytes_to_copy; } @@ -715,7 +977,10 @@ capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], cap } #ifndef DISABLE_CACHE_OPERATIONS - posal_cache_flush_v2(&write_ptr, bytes_to_copy); + if (!capi_ptr->is_header_enabled) + { + posal_cache_flush_v2(&write_ptr, bytes_to_copy); + } #endif bytes_copied_per_channel_now = bytes_to_copy / me_ptr->media_fmt.num_channels; @@ -727,9 +992,17 @@ capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], cap bytes_copied_per_channel += bytes_copied_per_channel_now; } - temp_wr_ind = write_index; - write_index += bytes_to_copy; - pull_push_mode_check_send_watermark_event(capi_ptr, temp_wr_ind, write_index); + // Only check watermark if headers disabled (will check at batch end if enabled) + if (!capi_ptr->is_header_enabled) + { + temp_wr_ind = write_index; + write_index += bytes_to_copy; + pull_push_mode_check_send_watermark_event(capi_ptr, temp_wr_ind, write_index); + } + else + { + write_index += bytes_to_copy; + } if (write_index >= me_ptr->shared_circ_buf_size) { write_index = 0; @@ -740,16 +1013,51 @@ capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], cap if (CAPI_INTERLEAVED == me_ptr->media_fmt.data_interleaving) { module_buf_ptr[0].actual_data_len = bytes_copied; + capi_ptr->pcm_bytes_written += bytes_copied; } else { // for unpacked v2 only first ch buffer lens need to be used. module_buf_ptr[0].actual_data_len = bytes_copied_per_channel; + capi_ptr->pcm_bytes_written += (bytes_copied_per_channel * me_ptr->media_fmt.num_channels); } } - /** update position buffer with new index*/ - pull_mode_update_pos_buffer(pos_buf_ptr, me_ptr->ist_priority, write_index, timestamp); + if (!capi_ptr->is_header_enabled) + { + /** update position buffer with new index*/ + pull_mode_update_pos_buffer(pos_buf_ptr, me_ptr->ist_priority, write_index, timestamp); + } + else + { + capi_ptr->batch_write_index = write_index; + capi_stream_data_v2_t *inputv2 = (capi_stream_data_v2_t *)input[0]; + bool_t is_batch_completed = (input[0]->flags.marker_eos || input[0]->flags.end_of_frame) ? TRUE: FALSE; + + //check for dam batch marker md + if (NULL != inputv2->metadata_list_ptr && !is_batch_completed) + { + module_cmn_md_list_t *node_ptr = inputv2->metadata_list_ptr; + module_cmn_md_list_t *next_ptr = NULL; + while (node_ptr) + { + module_cmn_md_t * md_ptr = (module_cmn_md_t *)node_ptr->obj_ptr; + next_ptr = node_ptr->next_ptr; + if (DAM_BATCH_END_MD_ID_MARKER == md_ptr->metadata_id) + { + is_batch_completed = TRUE; + break; + } + + node_ptr = next_ptr; + } + } + + if(is_batch_completed) + { + push_mode_end_header_batch(capi_ptr, timestamp); + } + } // raise EOS marker event if ((*input)->flags.marker_eos) diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h index db2cf5b9..918ee6a9 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h @@ -37,6 +37,11 @@ extern "C" { #define MAX_EVENT_CLIENTS 4 +#define HEADER_TYPE_UTC_TIMESTAMP 0x00000001 + +#define HEADER_TYPE_RESERVED_BITS 0xFFFFFFFE +#define BYTE_FOR_HEADER_ALIGNMENT 4 + /* debug message */ #define MIID_UNKNOWN 0 #define PULL_PUSH_MSG_PREFIX "CAPI PM:[%lX] " @@ -124,6 +129,20 @@ typedef struct capi_pm_t bool_t is_cntr_duty_cycle_enabled; + /*header related parameter*/ + bool_t is_header_enabled; + bool_t is_update_header; + uint32_t header_type_flags; + uint8_t *header_buffer_ptr; + uint32_t header_buffer_size; + + uint32_t *pcm_param_actual_size_ptr; + uint32_t *pcm_param_padding_size_ptr; + + uint32_t batch_write_index; + uint32_t pcm_bytes_written; + uint32_t batch_bytes_written; + } capi_pm_t; /*------------------------------------------------------------------------ From 87906576950cfdf0a2e393a6688cf1e16622b699 Mon Sep 17 00:00:00 2001 From: himachau Date: Wed, 25 Mar 2026 21:37:37 +0530 Subject: [PATCH 09/24] Add DAM functionality to drain the partial data and DFG support This change adds support for partial batch drain and DFG metadata handling in the Audio DAM buffer module. It refactors EOS handling, introduces configuration fields and error checks, and improves device-switch handling for partial batch drain scenarios. Signed-off-by: ffrancis --- .../capi/src/capi_audio_dam_buffer.c | 62 +++++++++--- .../capi/src/capi_audio_dam_buffer_i.h | 13 ++- .../src/capi_audio_dam_buffer_imc_utils.c | 11 +++ .../capi/src/capi_audio_dam_buffer_island.c | 98 +++++++++++++++---- ...capi_audio_dam_buffer_output_ctrl_island.c | 46 ++++++++- 5 files changed, 196 insertions(+), 34 deletions(-) diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c index 058a9cae..3e17f2ba 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c @@ -1488,6 +1488,16 @@ capi_err_t capi_audio_dam_buffer_set_param_non_island(capi_t *ca // Store the flag me_ptr->out_port_info_arr[op_arr_index].ready_for_island_entry = TRUE; + /* Decrease the count and check in island entry condition if this count is zero. */ + if(0 < me_ptr->out_port_info_arr[op_arr_index].ref_count_batch_end_md) + { + --me_ptr->out_port_info_arr[op_arr_index].ref_count_batch_end_md; + } + else + { + DAM_MSG(me_ptr->miid, DBG_MED_PRIO, "Invalid Ref count of batch end md"); + } + bool_t can_enter_island = capi_audio_dam_check_island_entry_cond(me_ptr); // island entry conditions, pending_bytes -> 0, DCM mode enabled and port is intialized @@ -2438,6 +2448,7 @@ static capi_err_t capi_audio_dam_data_port_op_handler(capi_audio_dam_t *me_ptr, me_ptr->out_port_info_arr[arr_index].gate_ctrl_op = AUDIO_DAM_BATCH_INVALID; me_ptr->out_port_info_arr[arr_index].is_dcm_duty_cycling_enabled = FALSE; me_ptr->out_port_info_arr[arr_index].handle_md_batch_tracking = FALSE; + me_ptr->out_port_info_arr[arr_index].is_partial_batch_drain_enabled = FALSE; } break; @@ -2517,28 +2528,53 @@ static capi_err_t capi_audio_dam_data_port_op_handler(capi_audio_dam_t *me_ptr, */ for (uint32_t arr_idx = 0; arr_idx < me_ptr->max_output_ports; arr_idx++) { - if ((!me_ptr->out_port_info_arr[arr_idx].is_gate_opened) || - (!is_dam_output_port_initialized(me_ptr, arr_idx)) || - (!me_ptr->out_port_info_arr[arr_idx].is_started) || - (!me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr->is_batch_streaming)) + /** reset the circ buffer if gate is closed */ + if ((!me_ptr->out_port_info_arr[arr_idx].is_gate_opened) && + (is_dam_output_port_initialized(me_ptr, arr_idx))) + { + + // adjust rd ptr inorder to drop all the data + uint32_t read_offset_in_us = 0; + audio_dam_stream_read_adjust(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, + read_offset_in_us, + NULL, + FALSE); + + continue; + } + else if ((!me_ptr->out_port_info_arr[arr_idx].is_gate_opened) || + (!is_dam_output_port_initialized(me_ptr, arr_idx)) || + (!me_ptr->out_port_info_arr[arr_idx].is_started) || + (!me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr->is_batch_streaming)) { continue; } - audio_dam_force_set_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr); + uint32_t pending_bytes = 0; + audio_dam_get_stream_reader_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, &pending_bytes); - // check if the DCM is eneabled and pending bytes is non-zero (capture transition from 0 -> non-zero number) - if (FALSE == me_ptr->out_port_info_arr[arr_idx].is_dcm_duty_cycling_enabled) + /* complete the current batch first then create next batch with remaining bytes*/ + if ((0 < pending_bytes) && me_ptr->out_port_info_arr[arr_idx].is_partial_batch_drain_enabled) { - posal_island_trigger_island_exit(); - capi_dam_duty_cycling_buf_send_message_to_dcm(me_ptr, (uint32_t)SPF_MSG_CMD_DCM_REQ_FOR_ISLAND_EXIT); + /* treat graph close as eos and drain partial data as seperate batch*/ + me_ptr->out_port_info_arr[arr_idx].is_handle_partial_drain = TRUE; } + else + { + audio_dam_force_set_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr); - uint32_t pending_bytes = 0; - audio_dam_get_stream_reader_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, - &pending_bytes); + // check if the DCM is eneabled and pending bytes is non-zero (capture transition from 0 -> non-zero number) + if (FALSE == me_ptr->out_port_info_arr[arr_idx].is_dcm_duty_cycling_enabled) + { + posal_island_trigger_island_exit(); + capi_dam_duty_cycling_buf_send_message_to_dcm(me_ptr, (uint32_t)SPF_MSG_CMD_DCM_REQ_FOR_ISLAND_EXIT); + } + } + + uint32_t unread_bytes = 0; + audio_dam_get_stream_reader_unread_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, &unread_bytes); - me_ptr->out_port_info_arr[arr_idx].bytes_before_eos = pending_bytes; + me_ptr->out_port_info_arr[arr_idx].bytes_before_eos = unread_bytes; if (me_ptr->out_port_info_arr[arr_idx].bytes_before_eos) { me_ptr->out_port_info_arr[arr_idx].pending_eos = TRUE; diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h index 25879549..793012d9 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h @@ -183,6 +183,16 @@ typedef struct /*Indicate if metadata needs to be sent after every batch in batching mode*/ bool_t handle_md_batch_tracking; + /* ref count of end metadata sent for island entry condition*/ + uint32_t ref_count_batch_end_md; + + /* Indicate if partial data needs to be drained if eos is received*/ + bool_t is_partial_batch_drain_enabled; + bool_t is_handle_partial_drain; + + /*Handle frame size mismatch*/ + bool_t send_dfg_md; + } _aud_dam_output_port_info; typedef struct @@ -428,7 +438,8 @@ capi_err_t capi_dam_insert_flushing_eos_at_out_port(capi_audio_dam_t *me_ptr, capi_err_t capi_dam_insert_tracking_md_at_out_port(capi_audio_dam_t *me_ptr, capi_stream_data_t *output, - uint32_t output_port_index); + uint32_t output_port_index, + bool_t send_dfg_md); capi_vtbl_t *capi_audio_dam_buffer_get_vtable(); diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c index 6ff92cfe..cb5d2910 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c @@ -186,6 +186,7 @@ capi_err_t capi_audio_dam_imcl_set_hdlr_flow_ctrl_v2(capi_audio_dam_t *me_ptr, if(AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING_MD_TRACKING_EVENT == cfg_ptr->gate_ctrl) { me_ptr->out_port_info_arr[op_arr_index].handle_md_batch_tracking = TRUE; + me_ptr->out_port_info_arr[op_arr_index].is_partial_batch_drain_enabled = TRUE; } capi_audio_dam_raise_allow_duty_cycling(me_ptr, TRUE); @@ -265,12 +266,22 @@ bool_t capi_audio_dam_check_island_entry_cond(capi_audio_dam_t* me_ptr) DAM_MSG(me_ptr->miid, DBG_MED_PRIO, "Processing on port_index:%d still going on", i); trigger_dcm_island_entry = FALSE; } + if(me_ptr->out_port_info_arr[i].is_handle_partial_drain) + { + DAM_MSG(me_ptr->miid, DBG_MED_PRIO, "EOS patial batch needs to be handled on port_index: %d", i); + return FALSE; + } audio_dam_get_stream_reader_pending_bytes(me_ptr->out_port_info_arr[i].strm_reader_ptr, &pending_bytes_to_read); if (pending_bytes_to_read) { DAM_MSG(me_ptr->miid, DBG_MED_PRIO, "Batching stream data buffering started on port_index: %d, pending_bytes:%lu", i, pending_bytes_to_read); return FALSE; } + if(0 != me_ptr->out_port_info_arr[i].ref_count_batch_end_md && me_ptr->out_port_info_arr[i].handle_md_batch_tracking) + { + DAM_MSG(me_ptr->miid, DBG_MED_PRIO, "Ref batch end md count is: %lu", me_ptr->out_port_info_arr[i].ref_count_batch_end_md); + return FALSE; + } } return trigger_dcm_island_entry; } diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c index 866e736b..70bfa8ac 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c @@ -54,7 +54,8 @@ uint32_t capi_dam_get_ctrl_port_arr_idx_from_ctrl_port_id(capi_audio_dam_t *me_p static capi_err_t capi_audio_dam_handle_and_drop_metadata(capi_audio_dam_t *me_ptr, capi_stream_data_t *input, - uint32_t ip_port_index) + uint32_t ip_port_index, + bool_t *is_eos_recieved) { capi_err_t capi_result = CAPI_EOK; capi_stream_data_v2_t *in_stream_ptr = (capi_stream_data_v2_t *)input; @@ -76,21 +77,8 @@ static capi_err_t capi_audio_dam_handle_and_drop_metadata(capi_audio_dam_t *me else if (MODULE_CMN_MD_ID_EOS == node_ptr->obj_ptr->metadata_id) { DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, "EOS rcvd"); - - for (uint32_t op_arr_index = 0; op_arr_index < me_ptr->max_output_ports; op_arr_index++) - { - /** do not reset the circ buffer if gate is open*/ - if (!me_ptr->out_port_info_arr[op_arr_index].is_gate_opened) - { - - // adjust rd ptr inorder to drop all the data - uint32_t read_offset_in_us = 0; - audio_dam_stream_read_adjust(me_ptr->out_port_info_arr[op_arr_index].strm_reader_ptr, - read_offset_in_us, - NULL, - FALSE); - } - } + // handle the eos after stream write + *is_eos_recieved = TRUE; } if (me_ptr->metadata_handler.metadata_destroy) @@ -114,6 +102,44 @@ static capi_err_t capi_audio_dam_handle_and_drop_metadata(capi_audio_dam_t *me return capi_result; } +static capi_err_t capi_audio_dam_handle_eos_and_partial_batch(capi_audio_dam_t *me_ptr, bool_t new_data_arrived, bool_t is_eos_recieved) +{ + capi_err_t capi_result = CAPI_EOK; + + for (uint32_t op_arr_index = 0; op_arr_index < me_ptr->max_output_ports; op_arr_index++) + { + + if(new_data_arrived && me_ptr->out_port_info_arr[op_arr_index].is_handle_partial_drain) + { + DAM_MSG_ISLAND(me_ptr->miid, DBG_ERROR_PRIO, " Previous EOS event is not handled yet, Merging new data in the partial batch."); + me_ptr->out_port_info_arr[op_arr_index].is_handle_partial_drain = FALSE; + } + + if(is_eos_recieved) + { + /** do not reset the circ buffer if gate is open*/ + if (!me_ptr->out_port_info_arr[op_arr_index].is_gate_opened) + { + // adjust rd ptr inorder to drop all the data + uint32_t read_offset_in_us = 0; + audio_dam_stream_read_adjust(me_ptr->out_port_info_arr[op_arr_index].strm_reader_ptr, + read_offset_in_us, + NULL, + FALSE); + } + + /** Handle partial batch */ + if(me_ptr->out_port_info_arr[op_arr_index].is_partial_batch_drain_enabled) + { + me_ptr->out_port_info_arr[op_arr_index].is_handle_partial_drain = TRUE; + } + + } + } + + return capi_result; +} + /*------------------------------------------------------------------------ Function name: capi_audio_dam_buffer_process Processes an input buffer and generates an output buffer. @@ -137,6 +163,8 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i if (input) { + bool_t is_eos_recieved = FALSE; + for (uint32_t arr_idx = 0; arr_idx < me_ptr->max_input_ports; arr_idx++) { // Check the port index from arr index @@ -149,7 +177,7 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i continue; } - result |= capi_audio_dam_handle_and_drop_metadata(me_ptr, input[port_index], port_index); + result |= capi_audio_dam_handle_and_drop_metadata(me_ptr, input[port_index], port_index, &is_eos_recieved); // input port buffers sanity check if ((NULL == me_ptr->in_port_info_arr[arr_idx].strm_writer_ptr) || @@ -179,6 +207,11 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i input[port_index]->flags.is_timestamp_valid, input[port_index]->buf_ptr[0].actual_data_len); #endif + + + bool_t new_data_arrived = input[port_index]->buf_ptr[0].actual_data_len > 0? TRUE : FALSE; + capi_audio_dam_handle_eos_and_partial_batch(me_ptr, new_data_arrived, is_eos_recieved); + } } @@ -240,9 +273,36 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i uint32_t output_frame_len_us = 0; bool_t is_timestamp_valid = FALSE; + // Handle partial drain: Wait for pending bytes to become zero, then force drain if (me_ptr->out_port_info_arr[arr_idx].is_dcm_duty_cycling_enabled) { bool_t is_batching_req_met = audio_dam_if_batching_req_met(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr); + + if(TRUE == me_ptr->out_port_info_arr[arr_idx].is_handle_partial_drain) + { + uint32_t pending_batch_bytes = 0; + audio_dam_get_stream_reader_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, &pending_batch_bytes); + + // Only drain when pending bytes become zero (current batch completes) + if(0 == pending_batch_bytes) + { + //force set the unread bytes in DAM buffer to pending bytes + audio_dam_force_set_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr); + + audio_dam_get_stream_reader_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, &pending_batch_bytes); + DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, " Partial batch created with total_data_bytes: %lu.", pending_batch_bytes); + + //pending bytes must be non-zero for island exit + if(0 < pending_batch_bytes) + { + is_batching_req_met = TRUE; + me_ptr->out_port_info_arr[arr_idx].send_dfg_md = TRUE; + } + + me_ptr->out_port_info_arr[arr_idx].is_handle_partial_drain = FALSE; + } + } + if (is_batching_req_met) { DAM_MSG_ISLAND(me_ptr->miid, DBG_MED_PRIO, "voting for island exit"); @@ -277,12 +337,14 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i if(me_ptr->out_port_info_arr[arr_idx].handle_md_batch_tracking && is_batch_sent) //send the marker metadata as batch is sent { - if (CAPI_EOK == (result = capi_dam_insert_tracking_md_at_out_port(me_ptr, output[port_index], port_index))) + if (CAPI_EOK == (result = capi_dam_insert_tracking_md_at_out_port(me_ptr, output[port_index], port_index, me_ptr->out_port_info_arr[arr_idx].send_dfg_md))) { DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, "DAM: Inserted tracking MD after Batch"); + ++me_ptr->out_port_info_arr[arr_idx].ref_count_batch_end_md; } + me_ptr->out_port_info_arr[arr_idx].send_dfg_md = FALSE; } output[port_index]->flags.is_timestamp_valid = is_timestamp_valid; diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c index 1cfcd8c1..a04a0d8f 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c @@ -139,7 +139,7 @@ capi_err_t capi_check_and_close_the_gate(capi_audio_dam_t *me_ptr, uint32_t op_a // For Acoustic Activity Detection usecase, we do not support best channel feature, so we dont have to revert the channel order. if (FALSE == me_ptr->out_port_info_arr[op_arr_index].is_peer_aad) { - posal_island_trigger_island_exit(); + posal_island_trigger_island_exit(); capi_audio_dam_reorder_chs_at_gate_close(me_ptr, op_arr_index, is_destroy); // In AAD usecase, do not update KPPS in process context. @@ -292,7 +292,8 @@ capi_err_t capi_dam_insert_flushing_eos_at_out_port(capi_audio_dam_t *me_ptr, capi_err_t capi_dam_insert_tracking_md_at_out_port(capi_audio_dam_t *me_ptr, capi_stream_data_t *output, - uint32_t output_port_index) + uint32_t output_port_index, + bool_t send_dfg_md) { capi_err_t capi_result = CAPI_EOK; capi_stream_data_v2_t *out_stream_ptr = (capi_stream_data_v2_t *)output; @@ -331,14 +332,55 @@ capi_err_t capi_dam_insert_tracking_md_at_out_port(capi_audio_dam_t *me_ptr, &batch_tracking_md_info, &new_md_ptr); + if (capi_result) + { + DAM_MSG_ISLAND(me_ptr->miid, DBG_ERROR_PRIO, "Failed to create metadata entry END md with error 0x%x", capi_result); + return CAPI_EFAILED; + } + new_md_ptr->metadata_id = DAM_BATCH_END_MD_ID_MARKER; new_md_ptr->offset = output->buf_ptr[0].actual_data_len/me_ptr->operating_mf.bytes_per_sample; md_payload_ptr = (dam_batch_end_md_gen_t *)&new_md_ptr->metadata_buf; md_payload_ptr->output_port_idx = output_port_index; //output port index to handle Duty cycling md_payload_ptr->param_id = PARAM_ID_AUDIO_DAM_HANDLE_BATCH_END_TRACKING_EVENT; //Param ID to set after tracking event + new_md_ptr->metadata_flag.buf_sample_association = MODULE_CMN_MD_BUFFER_ASSOCIATED; //Metadata reaches along with the DFG to downstream containers + DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, "DAM: Created and inserted tracking MD at output port index:%lu with offset: %lu", output_port_index, new_md_ptr->offset); + if(send_dfg_md) + { + //Inset DFG metadata to handle the framesize mismatch only for partial batches + + uint32_t dfg_payload_size = 0; // No metadata specific payload for dfg as of now + bool_t IN_BAND_PAYLOAD = FALSE; + module_cmn_md_t *md_ptr_dfg = NULL; + + capi_result = me_ptr->metadata_handler.metadata_create(me_ptr->metadata_handler.context_ptr, + md_list_pptr, + dfg_payload_size, + heap, + IN_BAND_PAYLOAD, + &md_ptr_dfg); + + if (capi_result) + { + DAM_MSG_ISLAND(me_ptr->miid, DBG_ERROR_PRIO, "Failed to create metadata entry for DFG with error 0x%x", capi_result); + return CAPI_EFAILED; + } + + md_ptr_dfg->metadata_flag.buf_sample_association = MODULE_CMN_MD_BUFFER_ASSOCIATED; + + // Update the metadata parameters + md_ptr_dfg->metadata_id = MODULE_CMN_MD_ID_DFG; + DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, "Generated DFG MD offset %d", new_md_ptr->offset ); + md_ptr_dfg->offset = new_md_ptr->offset; + + // Update flags to denote data flow state + out_stream_ptr->flags.end_of_frame = 1; + + } + return capi_result; } \ No newline at end of file From 0c141e39d6cefcdff30c6d01e39bfb58a14e9c4f Mon Sep 17 00:00:00 2001 From: aditchan Date: Fri, 27 Mar 2026 20:57:31 +0530 Subject: [PATCH 10/24] fwk: adding h2xml annotation for the header cfg param Add h2xml annotations for header cfg param so the header configuration parameter is described correctly in calibration tooling. Update shmem push mode timestamp handling to generate UTC-based timestamps for the header payload. The change: - adds state to track the UTC time module - allocates and resets the module when timestamp headers are enabled - queries network UTC time and converts the local timestamp before writing the payload - frees the UTC time module during module cleanup This fixes incomplete timestamp handling in push mode and ensures the exported header carries UTC-aligned time information. Signed-off-by: ffrancis --- .../api/sh_mem_pull_push_mode_api.h | 3 ++ .../src/capi_sh_mem_pull_push_mode.c | 28 ++++++++++++++++++- .../src/pull_push_mode.c | 17 +++++++++-- .../src/pull_push_mode.h | 9 ++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/api/sh_mem_pull_push_mode_api.h b/fwk/spf/modules/sh_mem_pull_push_mode/api/sh_mem_pull_push_mode_api.h index ee23e70c..bda9582f 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/api/sh_mem_pull_push_mode_api.h +++ b/fwk/spf/modules/sh_mem_pull_push_mode/api/sh_mem_pull_push_mode_api.h @@ -440,6 +440,9 @@ typedef struct sh_mem_pull_push_mode_cfg_t sh_mem_pull_push_mode_cfg_t; sh_mem_push_mode_header_cfg_t */ #define PARAM_ID_SH_MEM_PUSH_MODE_HEADER_CFG 0x08001BDF +/** @h2xmlp_parameter {"PARAM_ID_SH_MEM_PUSH_MODE_HEADER_CFG", PARAM_ID_SH_MEM_PUSH_MODE_HEADER_CFG} + @h2xmlp_description {Configures the header info.\n} + @h2xmlp_toolPolicy {Calibration} */ /*============================================================================== Param structure definitions diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c b/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c index f1817887..5504488e 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c @@ -307,7 +307,6 @@ capi_err_t capi_push_mode_init(capi_t *_pif, capi_proplist_t *init_set_propertie me_ptr->vtbl.vtbl_ptr = &push_mode_vtbl; me_ptr->pull_push_mode_info.mode = PUSH_MODE; - memset(&me_ptr->pull_push_mode_info.media_fmt, 0, sizeof(pm_media_fmt_t)); capi_result = capi_pm_process_init(me_ptr, init_set_properties); @@ -324,6 +323,13 @@ static capi_err_t capi_pm_end(capi_t *_pif) } capi_pm_t *me_ptr = (capi_pm_t *)_pif; + // Free UTC time module if allocated + if (NULL != me_ptr->ts_data.utc_time_module_ptr) + { + posal_memory_free(me_ptr->ts_data.utc_time_module_ptr); + me_ptr->ts_data.utc_time_module_ptr = NULL; + } + // Free header buffer if allocated (PUSH mode specific cleanup) if (NULL != me_ptr->header_buffer_ptr) { @@ -443,6 +449,26 @@ static capi_err_t capi_pm_set_param(capi_t *_pif, { required_size += sizeof(sh_mem_push_mode_param_header_t); // Timestamp param header required_size += sizeof(sh_mem_push_mode_timestamp_payload_t); // Timestamp payload + + if(me_ptr->ts_data.utc_time_module_ptr) + { + posal_reset_utc_time_module((void *)me_ptr->ts_data.utc_time_module_ptr); + } + else + { + uint32_t posal_timer_size = posal_get_time_module_size(); + if(posal_timer_size) + { + uint8_t* ptr = (uint8_t *)posal_memory_malloc(posal_timer_size, (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id); + if(NULL == ptr) + { + PULL_PUSH_MSG(miid, DBG_HIGH_PRIO, "Failed to allocated time module of size %lu", posal_timer_size); + return CAPI_EFAILED; + } + me_ptr->ts_data.utc_time_module_ptr = ptr; + posal_reset_utc_time_module((void *)me_ptr->ts_data.utc_time_module_ptr); + } + } } // Free existing buffer if already allocated and size not same diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c index 862f7c26..4c7f3ad6 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c @@ -619,10 +619,21 @@ static capi_err_t push_mode_write_batch_header(capi_pm_t *me_ptr, local_offset += sizeof(sh_mem_push_mode_param_header_t); // Write timestamp payload - sh_mem_push_mode_timestamp_payload_t *ts_payload_ptr = + sh_mem_push_mode_timestamp_payload_t *ts_payload_ptr = (sh_mem_push_mode_timestamp_payload_t *)&local_header_buf_ptr[local_offset]; - ts_payload_ptr->timestamp_us_lsw = (uint32_t)(timestamp & 0xFFFFFFFF); - ts_payload_ptr->timestamp_us_msw = (uint32_t)(timestamp >> 32); + + uint32_t utc_ts_lsw = (uint32_t)(timestamp & 0xFFFFFFFF); + uint32_t utc_ts_msw = (uint32_t)(timestamp >> 32); + PULL_PUSH_MSG(miid, DBG_LOW_PRIO, + "PRE UTC timestamp: 0x%08x%08x", + utc_ts_msw, + utc_ts_lsw); + + posal_query_utc_time_from_nw((void *)me_ptr->ts_data.utc_time_module_ptr, TIME_USEC); + + posal_date_time_get_utc_time((void *)me_ptr->ts_data.utc_time_module_ptr, (posal_time)timestamp,TIME_USEC, &utc_ts_msw, &utc_ts_lsw); + ts_payload_ptr->timestamp_us_lsw = utc_ts_lsw; + ts_payload_ptr->timestamp_us_msw = utc_ts_msw; local_offset += sizeof(sh_mem_push_mode_timestamp_payload_t); PULL_PUSH_MSG(miid, DBG_LOW_PRIO, diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h index 918ee6a9..a9fc0c1c 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h @@ -20,6 +20,7 @@ #include "capi_util.h" #endif #include "posal_timer.h" +#include "posal_time.h" #include "sh_mem_pull_push_mode_api.h" #include "ar_error_codes.h" #include "capi_types.h" @@ -52,6 +53,7 @@ extern "C" { * Structure definitions * ----------------------------------------------------------------------*/ #define QFORMAT_TO_BIT_WIDTH(q) ((PCM_Q_FACTOR_15 == q) ? 16 : ( (PCM_Q_FACTOR_27 == q ) ? 24 : 32) ) +#define TIME_USEC 1 typedef struct pm_media_fmt_t { @@ -111,6 +113,11 @@ typedef struct capi_pm_media_fmt_t capi_standard_data_format_t std; } capi_pm_media_fmt_t; + +typedef struct capi_push_ts_data_t { + void *utc_time_module_ptr; // assign this is UTC TS is needed +} capi_push_ts_data_t; + typedef struct capi_pm_t { /* v-table pointer */ @@ -142,6 +149,8 @@ typedef struct capi_pm_t uint32_t batch_write_index; uint32_t pcm_bytes_written; uint32_t batch_bytes_written; + // UTC timestamp handling + capi_push_ts_data_t ts_data; } capi_pm_t; From bd1d15b843998a7db952e8554beb29879ebe4687 Mon Sep 17 00:00:00 2001 From: hkadamba Date: Wed, 1 Apr 2026 11:26:35 +0530 Subject: [PATCH 11/24] Media format propagation fix for handling pending zeros due to EOS case Prevent media format propagation when the downstream module still has pending zeros to emit for EOS handling. The data path previously treated an empty downstream buffer as enough to propagate media format as long as the input port was started. In the EOS zero-padding case, that allowed propagation to happen while the next module still had pending output to drain. Add a check for pending_zeros_at_eos before propagating media format. This avoids advancing media format state too early and fixes incorrect handling around EOS-driven zero output. Signed-off-by: ffrancis --- .../gen_topo/core/src/gen_topo_data_process_island.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fwk/spf/containers/cmn/topologies/gen_topo/core/src/gen_topo_data_process_island.c b/fwk/spf/containers/cmn/topologies/gen_topo/core/src/gen_topo_data_process_island.c index a4915b38..190face2 100644 --- a/fwk/spf/containers/cmn/topologies/gen_topo/core/src/gen_topo_data_process_island.c +++ b/fwk/spf/containers/cmn/topologies/gen_topo/core/src/gen_topo_data_process_island.c @@ -694,7 +694,8 @@ GEN_TOPO_STATIC ar_result_t gen_topo_check_copy_between_modules(gen_topo_t * // Only try to propogate media fomrat if the next port state is started because MF will only be propogated if the // port state is started this will avoid infinite loop in GC. // Ref Test Case: dtmf_gen_cfg_cmd_seq_1 , dtmf_gen_cfg_cmd_seq_2 & dtmf_gen_cfg_cmd_seq_3 - if ((0 == next_bufs_ptr[0].actual_data_len) && (TOPO_PORT_STATE_STARTED == next_in_port_ptr->common.state)) + if ((0 == next_bufs_ptr[0].actual_data_len) && (TOPO_PORT_STATE_STARTED == next_in_port_ptr->common.state) + && (0 == next_module_ptr->pending_zeros_at_eos)) { gen_topo_propagate_media_fmt_from_module(topo_ptr, TRUE /* is_data_path*/, module_list_ptr); From 6c8158f99736073436f601e50373af13689887e4 Mon Sep 17 00:00:00 2001 From: Harsh Bansal Date: Thu, 9 Apr 2026 20:00:46 +0530 Subject: [PATCH 12/24] Handle At-Gap state when DFG moves out of container Treat DFG metadata like flushing EOS so it can propagate across containers even when the peer holds a partial frame. Update trigger policy handling to allow processing when DFG is paired with end_of_frame, matching existing flushing EOS behavior. Propagate DFG state through peer metadata and reset logic to ensure proper output reset and correct at-gap state transition. Signed-off-by: ffrancis --- .../core/src/gen_topo_trigger_policy_island.c | 10 +++++++--- .../gen_topo/ext/metadata/inc/gen_topo_metadata.h | 4 ++-- .../ext/metadata/src/gen_topo_metadata_island.c | 11 +++++++---- .../gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c | 4 ++-- .../peer_cntr/src/gen_cntr_peer_cntr_output_island.c | 10 +++++----- fwk/spf/containers/olc/core/src/olc_data_handler.c | 4 ++-- .../containers/spl_cntr/core/src/spl_cntr_buf_util.c | 4 ++-- 7 files changed, 27 insertions(+), 20 deletions(-) diff --git a/fwk/spf/containers/cmn/topologies/gen_topo/core/src/gen_topo_trigger_policy_island.c b/fwk/spf/containers/cmn/topologies/gen_topo/core/src/gen_topo_trigger_policy_island.c index 96c17542..6224c57a 100644 --- a/fwk/spf/containers/cmn/topologies/gen_topo/core/src/gen_topo_trigger_policy_island.c +++ b/fwk/spf/containers/cmn/topologies/gen_topo/core/src/gen_topo_trigger_policy_island.c @@ -1437,13 +1437,17 @@ gen_topo_data_need_t gen_topo_in_port_needs_data(gen_topo_t *topo_ptr, gen_topo_ else #endif { - // if there's a flushing EOS stuck, input is optional - if (in_port_ptr->common.sdata.flags.marker_eos) + // if there's a flushing EOS or DFG stuck, input is optional + /* Fix for DFG: + Ideally checking just for end_of_frame should be sufficient for DFG. + Since end_of_frame has wider scope threfore to limit the fix, DFG MD is looked up.*/ + if (in_port_ptr->common.sdata.flags.marker_eos || + (in_port_ptr->common.sdata.flags.end_of_frame && gen_topo_md_list_has_dfg(in_port_ptr->common.sdata.metadata_list_ptr))) { #ifdef TRIGGER_DEBUG_DEEP TOPO_MSG(topo_ptr->gu.log_id, DBG_LOW_PRIO, - " Module 0x%lX: Input port id:0x%lx, port has EOS. need data optionally", + " Module 0x%lX: Input port id:0x%lx, port has EOS or DFG. need data optionally", in_port_ptr->gu.cmn.module_ptr->module_instance_id, in_port_ptr->gu.cmn.id); #endif diff --git a/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/inc/gen_topo_metadata.h b/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/inc/gen_topo_metadata.h index fd3d5ff2..3cae0ed5 100644 --- a/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/inc/gen_topo_metadata.h +++ b/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/inc/gen_topo_metadata.h @@ -224,8 +224,8 @@ ar_result_t gen_topo_validate_metadata_eof(gen_topo_module_t *module_ptr); void gen_topo_populate_metadata_for_peer_cntr(gen_topo_t * gen_topo_ptr, gu_ext_out_port_t * ext_out_port_ptr, module_cmn_md_list_t **md_list_pptr, - module_cmn_md_list_t **out_md_list_pptr, - bool_t * out_buf_has_flushing_eos_ptr); + module_cmn_md_list_t **out_md_list_pptr, + bool_t * out_buf_has_flushing_eos_dfg_ptr); ar_result_t gen_topo_check_realloc_md_list_in_peer_heap_id(uint32_t log_id, gu_ext_out_port_t * ext_out_port_ptr, diff --git a/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata_island.c b/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata_island.c index 6c7a18eb..ba1969b6 100644 --- a/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata_island.c +++ b/fwk/spf/containers/cmn/topologies/gen_topo/ext/metadata/src/gen_topo_metadata_island.c @@ -1159,9 +1159,9 @@ void gen_topo_populate_metadata_for_peer_cntr(gen_topo_t * gen_topo_pt gu_ext_out_port_t * ext_out_port_ptr, module_cmn_md_list_t **md_list_pptr, module_cmn_md_list_t **out_md_list_pptr, - bool_t * out_buf_has_flushing_eos_ptr) + bool_t * out_buf_has_flushing_eos_dfg_ptr) { - *out_buf_has_flushing_eos_ptr = FALSE; + *out_buf_has_flushing_eos_dfg_ptr = FALSE; if ((!md_list_pptr) || (!(*md_list_pptr))) { @@ -1204,7 +1204,7 @@ void gen_topo_populate_metadata_for_peer_cntr(gen_topo_t * gen_topo_pt } if (eos_metadata_ptr->flags.is_flushing_eos) { - *out_buf_has_flushing_eos_ptr = TRUE; + *out_buf_has_flushing_eos_dfg_ptr = TRUE; // Check if container voting changes need to be deferred for this Flushing EOS. gen_topo_check_eos_md_update_defer_voting_flag(&ext_out_port_ptr->int_out_port_ptr->cmn, md_ptr); @@ -1213,7 +1213,10 @@ void gen_topo_populate_metadata_for_peer_cntr(gen_topo_t * gen_topo_pt // should free only contr ref ptr. others should stay for next containers use gen_topo_free_eos_cargo(gen_topo_ptr, md_ptr, eos_metadata_ptr); } - + else if(MODULE_CMN_MD_ID_DFG == md_ptr->metadata_id) + { + *out_buf_has_flushing_eos_dfg_ptr = TRUE; + } #if defined(METADATA_DEBUGGING) TOPO_MSG_ISLAND(gen_topo_ptr->gu.log_id, DBG_HIGH_PRIO, diff --git a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c index 0014430d..6ed8a32e 100644 --- a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c +++ b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c @@ -776,12 +776,12 @@ PT_CNTR_STATIC ar_result_t pt_cntr_post_process_peer_ext_output(pt_cntr_t &(ext_out_port_ptr->gc.gu), &(sdata_ptr->metadata_list_ptr)); - bool_t out_buf_has_flushing_eos = FALSE; + bool_t out_buf_has_flushing_eos_dfg = FALSE; gen_topo_populate_metadata_for_peer_cntr(&(me_ptr->gc.topo), &(ext_out_port_ptr->gc.gu), &(sdata_ptr->metadata_list_ptr), &out_buf_ptr->metadata_list_ptr, - &out_buf_has_flushing_eos); + &out_buf_has_flushing_eos_dfg); } #ifdef PT_CNTR_TIME_PROP_ENABLE diff --git a/fwk/spf/containers/gen_cntr/ext/peer_cntr/src/gen_cntr_peer_cntr_output_island.c b/fwk/spf/containers/gen_cntr/ext/peer_cntr/src/gen_cntr_peer_cntr_output_island.c index e32faa3b..155c4c06 100644 --- a/fwk/spf/containers/gen_cntr/ext/peer_cntr/src/gen_cntr_peer_cntr_output_island.c +++ b/fwk/spf/containers/gen_cntr/ext/peer_cntr/src/gen_cntr_peer_cntr_output_island.c @@ -256,7 +256,7 @@ static ar_result_t gen_cntr_output_buf_set_up_peer_cntr(gen_cntr_t *me_ptr, gen_ static ar_result_t gen_cntr_populate_peer_cntr_out_buf(gen_cntr_t * me_ptr, gen_cntr_ext_out_port_t *ext_out_port_ptr, - bool_t * out_buf_has_flushing_eos_ptr) + bool_t * out_buf_has_flushing_eos_dfg_ptr) { ar_result_t result = AR_EOK; @@ -345,7 +345,7 @@ static ar_result_t gen_cntr_populate_peer_cntr_out_buf(gen_cntr_t * &(ext_out_port_ptr->gu), &(ext_out_port_ptr->md_list_ptr), outbuf_md_list_pptr, - out_buf_has_flushing_eos_ptr); + out_buf_has_flushing_eos_dfg_ptr); } #ifdef VERBOSE_DEBUGGING @@ -515,7 +515,7 @@ static ar_result_t gen_cntr_send_peer_cntr_out_buffers(gen_cntr_t * spf_msg_t * media_fmt_msg_ptr) { ar_result_t result = AR_EOK; - bool_t out_buf_has_flushing_eos = FALSE; + bool_t out_buf_has_flushing_eos_dfg = FALSE; if (media_fmt_msg_ptr && media_fmt_msg_ptr->payload_ptr) { @@ -583,7 +583,7 @@ static ar_result_t gen_cntr_send_peer_cntr_out_buffers(gen_cntr_t * return result; } - gen_cntr_populate_peer_cntr_out_buf(me_ptr, ext_out_port_ptr, &out_buf_has_flushing_eos); + gen_cntr_populate_peer_cntr_out_buf(me_ptr, ext_out_port_ptr, &out_buf_has_flushing_eos_dfg); // if module output unpacked, container needs to convert to packed. // note that internal output port may have new media format that hasn't been propagated to ext @@ -646,7 +646,7 @@ static ar_result_t gen_cntr_send_peer_cntr_out_buffers(gen_cntr_t * gen_cntr_clear_ext_out_bufs(ext_out_port_ptr, TRUE /*clear_max*/); // if flushing EOS was sent out, reset the ext out port - if (out_buf_has_flushing_eos) + if (out_buf_has_flushing_eos_dfg) { gen_cntr_ext_out_port_basic_reset(me_ptr, ext_out_port_ptr); if (FALSE == me_ptr->topo.flags.defer_voting_on_dfs_change) diff --git a/fwk/spf/containers/olc/core/src/olc_data_handler.c b/fwk/spf/containers/olc/core/src/olc_data_handler.c index 28f5d910..be6eeda7 100644 --- a/fwk/spf/containers/olc/core/src/olc_data_handler.c +++ b/fwk/spf/containers/olc/core/src/olc_data_handler.c @@ -981,12 +981,12 @@ ar_result_t olc_populate_peer_cntr_out_buf(olc_t *me_ptr, olc_ext_out_port_t *ex DATA_BUFFER_FLAG_TIMESTAMP_VALID_SHIFT); out_buf_ptr->timestamp = ts.value; - bool_t out_buf_has_flushing_eos; + bool_t out_buf_has_flushing_eos_dfg = FALSE; gen_topo_populate_metadata_for_peer_cntr(&(me_ptr->topo), &(ext_out_port_ptr->gu), &(ext_out_port_ptr->md_list_ptr), &(out_buf_ptr->metadata_list_ptr), - &out_buf_has_flushing_eos); + &out_buf_has_flushing_eos_dfg); #ifdef VERBOSE_DEBUGGING OLC_MSG(me_ptr->topo.gu.log_id, diff --git a/fwk/spf/containers/spl_cntr/core/src/spl_cntr_buf_util.c b/fwk/spf/containers/spl_cntr/core/src/spl_cntr_buf_util.c index b1df8988..27089566 100644 --- a/fwk/spf/containers/spl_cntr/core/src/spl_cntr_buf_util.c +++ b/fwk/spf/containers/spl_cntr/core/src/spl_cntr_buf_util.c @@ -1685,12 +1685,12 @@ ar_result_t spl_cntr_deliver_output_buffer(spl_cntr_t *me_ptr, spl_cntr_ext_out_ // Move md from ext out port to data msg. We always deliver the entire buffer, so we can also deliver // all metadata. - bool_t OUT_BUF_HAS_FLUSHING_EOS_UNUSED = FALSE; + bool_t OUT_BUF_HAS_FLUSHING_EOS_DFG_UNUSED = FALSE; gen_topo_populate_metadata_for_peer_cntr(&(me_ptr->topo.t_base), &(ext_out_port_ptr->gu), &(int_out_port_ptr->md_list_ptr), &(out_buf_msg_ptr->metadata_list_ptr), - &OUT_BUF_HAS_FLUSHING_EOS_UNUSED); + &OUT_BUF_HAS_FLUSHING_EOS_DFG_UNUSED); // At this point, md_list is NULL so it cannot contain any flushing eos. Set marker to FALSE. int_out_port_ptr->t_base.common.sdata.flags.marker_eos = FALSE; From 18cf7f9cc810278fc7f70711eaa70294fc402d46 Mon Sep 17 00:00:00 2001 From: aditchan Date: Sun, 12 Apr 2026 17:40:07 +0530 Subject: [PATCH 13/24] DAM Changes for downstream SG Stop corner case Handle AON buffering stop and reset corner cases Fix DAM and shmem push-mode handling for downstream SG stop scenarios seen with AON buffering. Reset DAM output state fully on port stop, drop stale partial data, and improve batch completion and metadata signaling behavior. In shmem push mode, finalize active batches cleanly across resets to keep valid data visible and downstream state consistent. Signed-off-by: ffrancis --- .../capi/src/capi_audio_dam_buffer.c | 24 ++++++ .../capi/src/capi_audio_dam_buffer_i.h | 3 +- .../src/capi_audio_dam_buffer_imc_utils.c | 3 +- .../capi/src/capi_audio_dam_buffer_island.c | 57 ++++++++------ ...capi_audio_dam_buffer_output_ctrl_island.c | 7 +- .../capi/src/capi_history_buffer_imcl_utils.c | 76 +++++++++++++++++++ .../capi/src/capi_history_buffer_imcl_utils.h | 8 ++ .../src/capi_sh_mem_pull_push_mode.c | 32 ++++++-- .../src/pull_push_mode.c | 41 +++++++++- .../src/pull_push_mode.h | 5 +- 10 files changed, 219 insertions(+), 37 deletions(-) diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c index 3e17f2ba..71527fd6 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer.c @@ -2449,6 +2449,7 @@ static capi_err_t capi_audio_dam_data_port_op_handler(capi_audio_dam_t *me_ptr, me_ptr->out_port_info_arr[arr_index].is_dcm_duty_cycling_enabled = FALSE; me_ptr->out_port_info_arr[arr_index].handle_md_batch_tracking = FALSE; me_ptr->out_port_info_arr[arr_index].is_partial_batch_drain_enabled = FALSE; + me_ptr->out_port_info_arr[arr_index].is_handle_partial_drain = FALSE; } break; @@ -2475,6 +2476,14 @@ static capi_err_t capi_audio_dam_data_port_op_handler(capi_audio_dam_t *me_ptr, me_ptr->out_port_info_arr[arr_index].is_dcm_duty_cycling_enabled = TRUE; } + else if(AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING_MD_TRACKING_EVENT == me_ptr->out_port_info_arr[arr_index].gate_ctrl_op) + { + me_ptr->out_port_info_arr[arr_index].handle_md_batch_tracking = TRUE; + me_ptr->out_port_info_arr[arr_index].is_partial_batch_drain_enabled = TRUE; + me_ptr->out_port_info_arr[arr_index].is_dcm_duty_cycling_enabled = TRUE; + me_ptr->out_port_info_arr[arr_index].is_handle_partial_drain = FALSE; + } + /** for outputs trigger policy is optional present if gate is opened, other wise it is non-trigger * blocked*/ if (me_ptr->out_port_info_arr[arr_index].is_gate_opened) @@ -2591,6 +2600,21 @@ static capi_err_t capi_audio_dam_data_port_op_handler(capi_audio_dam_t *me_ptr, me_ptr->out_port_info_arr[arr_index].is_started = FALSE; me_ptr->out_port_info_arr[arr_index].gate_ctrl_op = AUDIO_DAM_BATCH_INVALID; me_ptr->out_port_info_arr[arr_index].is_dcm_duty_cycling_enabled = FALSE; + me_ptr->out_port_info_arr[arr_index].handle_md_batch_tracking = FALSE; + me_ptr->out_port_info_arr[arr_index].is_partial_batch_drain_enabled = FALSE; + me_ptr->out_port_info_arr[arr_index].is_handle_partial_drain = FALSE; + me_ptr->out_port_info_arr[arr_index].pending_eos = FALSE; + me_ptr->out_port_info_arr[arr_index].bytes_before_eos = 0; + + if (is_dam_output_port_initialized(me_ptr, arr_index)) + { + // Reset read offset to zero to drop all stale data + uint32_t read_offset_in_us = 0; + audio_dam_stream_read_adjust(me_ptr->out_port_info_arr[arr_index].strm_reader_ptr, + read_offset_in_us, + NULL, + FALSE); + } } break; diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h index 793012d9..8eaab5ab 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_i.h @@ -439,7 +439,8 @@ capi_err_t capi_dam_insert_flushing_eos_at_out_port(capi_audio_dam_t *me_ptr, capi_err_t capi_dam_insert_tracking_md_at_out_port(capi_audio_dam_t *me_ptr, capi_stream_data_t *output, uint32_t output_port_index, - bool_t send_dfg_md); + bool_t send_dfg_md, + bool_t is_eos_case); capi_vtbl_t *capi_audio_dam_buffer_get_vtable(); diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c index cb5d2910..9cb1dba6 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_imc_utils.c @@ -181,7 +181,7 @@ capi_err_t capi_audio_dam_imcl_set_hdlr_flow_ctrl_v2(capi_audio_dam_t *me_ptr, { me_ptr->out_port_info_arr[op_arr_index].is_dcm_duty_cycling_enabled = TRUE; me_ptr->out_port_info_arr[op_arr_index].ready_for_island_entry = TRUE; - me_ptr->out_port_info_arr[op_arr_index].gate_ctrl_op = AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING; + if(AUDIO_DAM_BATCH_STREAM_WITH_ISLAND_DUTY_CYCLING_MD_TRACKING_EVENT == cfg_ptr->gate_ctrl) { @@ -189,6 +189,7 @@ capi_err_t capi_audio_dam_imcl_set_hdlr_flow_ctrl_v2(capi_audio_dam_t *me_ptr, me_ptr->out_port_info_arr[op_arr_index].is_partial_batch_drain_enabled = TRUE; } + me_ptr->out_port_info_arr[op_arr_index].gate_ctrl_op = cfg_ptr->gate_ctrl; capi_audio_dam_raise_allow_duty_cycling(me_ptr, TRUE); } break; diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c index 70bfa8ac..2231cd07 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_island.c @@ -274,34 +274,35 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i bool_t is_timestamp_valid = FALSE; // Handle partial drain: Wait for pending bytes to become zero, then force drain - if (me_ptr->out_port_info_arr[arr_idx].is_dcm_duty_cycling_enabled) + bool_t is_batching_req_met = FALSE; + if(TRUE == me_ptr->out_port_info_arr[arr_idx].is_handle_partial_drain) { - bool_t is_batching_req_met = audio_dam_if_batching_req_met(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr); + uint32_t pending_batch_bytes = 0; + audio_dam_get_stream_reader_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, &pending_batch_bytes); - if(TRUE == me_ptr->out_port_info_arr[arr_idx].is_handle_partial_drain) + // Only drain when pending bytes become zero (current batch completes) + if(0 == pending_batch_bytes) { - uint32_t pending_batch_bytes = 0; + //force set the unread bytes in DAM buffer to pending bytes + audio_dam_force_set_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr); + audio_dam_get_stream_reader_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, &pending_batch_bytes); + DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, " Partial batch created with total_data_bytes: %lu.", pending_batch_bytes); - // Only drain when pending bytes become zero (current batch completes) - if(0 == pending_batch_bytes) + //pending bytes must be non-zero for island exit + if(0 < pending_batch_bytes) { - //force set the unread bytes in DAM buffer to pending bytes - audio_dam_force_set_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr); - - audio_dam_get_stream_reader_pending_bytes(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, &pending_batch_bytes); - DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, " Partial batch created with total_data_bytes: %lu.", pending_batch_bytes); - - //pending bytes must be non-zero for island exit - if(0 < pending_batch_bytes) - { - is_batching_req_met = TRUE; - me_ptr->out_port_info_arr[arr_idx].send_dfg_md = TRUE; - } - - me_ptr->out_port_info_arr[arr_idx].is_handle_partial_drain = FALSE; + is_batching_req_met = TRUE; + me_ptr->out_port_info_arr[arr_idx].send_dfg_md = TRUE; } + + me_ptr->out_port_info_arr[arr_idx].is_handle_partial_drain = FALSE; } + } + + if (me_ptr->out_port_info_arr[arr_idx].is_dcm_duty_cycling_enabled) + { + is_batching_req_met |= audio_dam_if_batching_req_met(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr); if (is_batching_req_met) { @@ -313,6 +314,7 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i } } + bool_t is_batch_sent = false; result = audio_dam_stream_read(me_ptr->out_port_info_arr[arr_idx].strm_reader_ptr, num_output_chs, @@ -329,15 +331,24 @@ capi_err_t capi_audio_dam_buffer_process(capi_t *capi_ptr, capi_stream_data_t *i } else if (AR_EOK == result) // If read was successful update the current timestamp. { - if (me_ptr->out_port_info_arr[arr_idx].pending_eos) + /* Snapshot pending_eos before handle_pending_eos can clear it, so we can detect + whether EOS was actually inserted in this process call. */ + bool_t was_eos_pending = me_ptr->out_port_info_arr[arr_idx].pending_eos; + if (was_eos_pending) { posal_island_trigger_island_exit(); capi_audio_dam_handle_pending_eos(me_ptr, output, arr_idx, port_index); } - if(me_ptr->out_port_info_arr[arr_idx].handle_md_batch_tracking && is_batch_sent) //send the marker metadata as batch is sent + if(me_ptr->out_port_info_arr[arr_idx].handle_md_batch_tracking && (is_batch_sent)) //send the marker metadata as batch is sent { - if (CAPI_EOK == (result = capi_dam_insert_tracking_md_at_out_port(me_ptr, output[port_index], port_index, me_ptr->out_port_info_arr[arr_idx].send_dfg_md))) + bool_t is_eos_case = was_eos_pending && !me_ptr->out_port_info_arr[arr_idx].pending_eos; + if (is_eos_case) + { + me_ptr->out_port_info_arr[arr_idx].send_dfg_md = FALSE; // enforce EOS wins over DFG if pending EOS is there. + } + + if (CAPI_EOK == (result = capi_dam_insert_tracking_md_at_out_port(me_ptr, output[port_index], port_index, me_ptr->out_port_info_arr[arr_idx].send_dfg_md, is_eos_case))) { DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, diff --git a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c index a04a0d8f..9c56955d 100644 --- a/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c +++ b/fwk/spf/modules/audio_dam_buffer/capi/src/capi_audio_dam_buffer_output_ctrl_island.c @@ -293,7 +293,8 @@ capi_err_t capi_dam_insert_flushing_eos_at_out_port(capi_audio_dam_t *me_ptr, capi_err_t capi_dam_insert_tracking_md_at_out_port(capi_audio_dam_t *me_ptr, capi_stream_data_t *output, uint32_t output_port_index, - bool_t send_dfg_md) + bool_t send_dfg_md, + bool_t is_eos_case) { capi_err_t capi_result = CAPI_EOK; capi_stream_data_v2_t *out_stream_ptr = (capi_stream_data_v2_t *)output; @@ -344,7 +345,9 @@ capi_err_t capi_dam_insert_tracking_md_at_out_port(capi_audio_dam_t *me_ptr, md_payload_ptr->output_port_idx = output_port_index; //output port index to handle Duty cycling md_payload_ptr->param_id = PARAM_ID_AUDIO_DAM_HANDLE_BATCH_END_TRACKING_EVENT; //Param ID to set after tracking event - new_md_ptr->metadata_flag.buf_sample_association = MODULE_CMN_MD_BUFFER_ASSOCIATED; //Metadata reaches along with the DFG to downstream containers + /* END md is sample-associated only for EOS; buffer-associated for all other batch completions. */ + new_md_ptr->metadata_flag.buf_sample_association = is_eos_case ? MODULE_CMN_MD_SAMPLE_ASSOCIATED : MODULE_CMN_MD_BUFFER_ASSOCIATED; + DAM_MSG_ISLAND(me_ptr->miid, DBG_HIGH_PRIO, "DAM: Created and inserted tracking MD at output port index:%lu with offset: %lu", output_port_index, new_md_ptr->offset); diff --git a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.c b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.c index e713a0c6..3113a137 100644 --- a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.c +++ b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.c @@ -880,6 +880,73 @@ capi_err_t capi_history_buffer_send_flow_ctrl_v2_msg_to_dam(capi_history_buffer_ return capi_history_buffer_imcl_send_to_peer(&me_ptr->cb_info, &buffer, me_ptr->ctrl_port_info[ctrl_port_idx].port_id, flags); } +/* ========================================================================= + * FUNCTION : capi_history_buffer_send_allow_dcm_island_entry_to_dam + * + * Refer capi_history_buffer_imcl_utils.h for function usage. + * ========================================================================= */ +capi_err_t capi_history_buffer_send_allow_dcm_island_entry_to_dam(capi_history_buffer_t *me_ptr) +{ + /* Only one control port supported (HISTORY_BUFFER_MAX_CONTROL_PORTS = 1) */ + uint32_t ctrl_port_idx = 0; + + // Safety check: ensure ctrl port is connected before sending. + if (CTRL_PORT_PEER_CONNECTED != me_ptr->ctrl_port_info[ctrl_port_idx].state) + { + AR_MSG(DBG_HIGH_PRIO, + "0x%lx: CAPI_HISTORY_BUFFER: ctrl port not connected, skipping allow_dcm_island_entry", + me_ptr->miid); + return CAPI_EOK; + } + + // Populate the payload + param_id_audio_dam_allow_dcm_island_entry_t dam_process_done; + dam_process_done.process_done = TRUE; + + // Populate the IMCL header. + vw_imcl_header_t header; + header.opcode = PARAM_ID_AUDIO_DAM_ALLOW_DCM_ISLAND_ENTRY; + header.actual_data_len = sizeof(param_id_audio_dam_allow_dcm_island_entry_t); + + // Get a one-time buffer from the framework + capi_buf_t buffer; + capi_err_t result = capi_history_buffer_imcl_get_one_time_buf(&me_ptr->cb_info, + me_ptr->ctrl_port_info[ctrl_port_idx].port_id, + sizeof(header) + header.actual_data_len, + &buffer); + if (CAPI_EOK != result || NULL == buffer.data_ptr) + { + AR_MSG(DBG_ERROR_PRIO, + "0x%lx: CAPI_HISTORY_BUFFER: received NULL payload ptr while sending allow_dcm_island_entry to DAM", + me_ptr->miid); + return CAPI_EFAILED; + } + + // Copy the vw_imcl_header + uint32_t bytes_copied = memscpy(buffer.data_ptr, buffer.max_data_len, &header, sizeof(header)); + bytes_copied += memscpy(buffer.data_ptr + bytes_copied, + buffer.max_data_len - bytes_copied, + &dam_process_done, + header.actual_data_len); + + buffer.actual_data_len = bytes_copied; + + AR_MSG(DBG_HIGH_PRIO, + "0x%lx: CAPI_HISTORY_BUFFER: Sending ALLOW_DCM_ISLAND_ENTRY to DAM for first batch island entry", + me_ptr->miid); + + // To send data over to the IMCL peer + imcl_outgoing_data_flag_t flags; + flags.should_send = TRUE; + flags.is_trigger = TRUE; + + return capi_history_buffer_imcl_send_to_peer(&me_ptr->cb_info, + &buffer, + me_ptr->ctrl_port_info[ctrl_port_idx].port_id, + flags); +} + + capi_err_t capi_history_buffer_imcl_send_resize_to_dam(capi_history_buffer_t *me_ptr) { capi_err_t result = CAPI_EOK; @@ -980,6 +1047,15 @@ capi_err_t capi_history_buffer_resize_and_batch_gate_open_to_dam(capi_history_bu me_ptr->miid, flow_ctrl_cfg.gate_ctrl, me_ptr->max_dam_buffer_size_us); return result; } + + result = capi_history_buffer_send_allow_dcm_island_entry_to_dam(me_ptr); + if (CAPI_EOK != result) + { + AR_MSG(DBG_MED_PRIO, + "0x%lx: CAPI History Buffer: Couldn't send allow_dcm_island_entry to DAM for first batch", + me_ptr->miid); + result = CAPI_EOK; /* Non-fatal: tracking mechanism handles subsequent batches */ + } } AR_MSG(DBG_MED_PRIO, "CAPI History Buffer: DAM cfg sent successfully!"); diff --git a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.h b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.h index 0484ae56..ffb7f673 100644 --- a/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.h +++ b/fwk/spf/modules/history_buffer/capi/src/capi_history_buffer_imcl_utils.h @@ -246,6 +246,14 @@ capi_err_t capi_history_buffer_imcl_send_resize_to_dam(capi_history_buffer_t *me * ========================================================================= */ capi_err_t capi_history_buffer_resize_and_batch_gate_open_to_dam(capi_history_buffer_t *me_ptr); +/* ========================================================================= + * FUNCTION : capi_history_buffer_send_allow_dcm_island_entry_to_dam + * + * Sends PARAM_ID_AUDIO_DAM_ALLOW_DCM_ISLAND_ENTRY to DAM via IMCL. + * Called at ctrl port connect (after gate open) to trigger island entry for the first batch. + * ========================================================================= */ +capi_err_t capi_history_buffer_send_allow_dcm_island_entry_to_dam(capi_history_buffer_t *me_ptr); + /* ========================================================================= * FUNCTION : capi_history_buffer_validate_intent_id * diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c b/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c index 5504488e..ef56949b 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/capi_sh_mem_pull_push_mode.c @@ -1000,15 +1000,33 @@ static capi_err_t capi_pm_set_properties(capi_t *_pif, capi_proplist_t *props_pt case CAPI_ALGORITHMIC_RESET: { PULL_PUSH_MSG(pm_info_ptr->miid, DBG_HIGH_PRIO, "Set property received for algorithmic reset"); - + if ((PUSH_MODE == me_ptr->pull_push_mode_info.mode) && me_ptr->is_header_enabled && !me_ptr->is_update_header && (me_ptr->pcm_bytes_written > 0)) + { + PULL_PUSH_MSG(pm_info_ptr->miid, DBG_HIGH_PRIO, + "Algo reset: finalizing in-progress batch " + "(pcm_bytes_written=%lu) before reset", + me_ptr->pcm_bytes_written); + push_mode_end_header_batch(me_ptr, posal_timer_get_time()); + } if (NULL != me_ptr->pull_push_mode_info.shared_pos_buf_ptr) { - PULL_PUSH_MSG(pm_info_ptr->miid, DBG_HIGH_PRIO, "Resetting shared position structure"); - memset(me_ptr->pull_push_mode_info.shared_pos_buf_ptr, - 0, - sizeof(sh_mem_pull_push_mode_position_buffer_t)); - - me_ptr->pull_push_mode_info.next_read_index = 0; + if (PUSH_MODE == me_ptr->pull_push_mode_info.mode && me_ptr->is_header_enabled) + { + /* Defer position buffer reset to the next process call so HLOS can + * still read the last finalized batch. Resetting here would move + * pos_buf->index back to 0, making the just-written batch unreadable. */ + me_ptr->is_pending_pos_buf_reset = TRUE; + PULL_PUSH_MSG(pm_info_ptr->miid, DBG_HIGH_PRIO, + "Algo reset: deferring position buffer reset to next process call"); + } + else + { + PULL_PUSH_MSG(pm_info_ptr->miid, DBG_HIGH_PRIO, "Resetting shared position structure"); + memset(me_ptr->pull_push_mode_info.shared_pos_buf_ptr, + 0, + sizeof(sh_mem_pull_push_mode_position_buffer_t)); + me_ptr->pull_push_mode_info.next_read_index = 0; + } } break; } diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c index 4c7f3ad6..a0b3dbe0 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c @@ -803,7 +803,7 @@ capi_err_t push_mode_end_header_batch(capi_pm_t *_pif, uint64_t timestamp) *capi_ptr->pcm_param_actual_size_ptr, *capi_ptr->pcm_param_padding_size_ptr, capi_ptr->batch_write_index); capi_ptr->is_update_header = TRUE; - + capi_ptr->pcm_bytes_written = 0; return CAPI_EOK; } @@ -831,8 +831,45 @@ capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], cap pos_buf_ptr); return CAPI_EFAILED; } + + // Deferred from algo reset: reset pos buffer now + if (capi_ptr->is_pending_pos_buf_reset) + { + PULL_PUSH_MSG(miid, DBG_HIGH_PRIO, "Applying deferred position buffer reset before new write"); + if (NULL != me_ptr->shared_pos_buf_ptr) { + memset(me_ptr->shared_pos_buf_ptr, 0, sizeof(sh_mem_pull_push_mode_position_buffer_t)); + } + me_ptr->next_read_index = 0; + capi_ptr->is_pending_pos_buf_reset = FALSE; + } + + // Track last valid timestamp for invalid timestamp handling + if ((*input)->flags.is_timestamp_valid) + { + capi_ptr->last_valid_timestamp = (*input)->timestamp; + } + + if (capi_ptr->is_header_enabled) + { + if (!(*input)->flags.is_timestamp_valid) + { + PULL_PUSH_MSG(miid, DBG_ERROR_PRIO, + "Invalid timestamp detected, dropping frame with %lu bytes", + module_buf_ptr[0].actual_data_len); + + if (!capi_ptr->is_update_header && (capi_ptr->pcm_bytes_written > 0)) + { + PULL_PUSH_MSG(miid, DBG_HIGH_PRIO, + "Finalizing in-progress batch (pcm_bytes=%lu) before dropping invalid timestamp frame", + capi_ptr->pcm_bytes_written); + push_mode_end_header_batch(capi_ptr, capi_ptr->last_valid_timestamp); + } + + return CAPI_EOK; + } + } - uint64_t timestamp = (*input)->flags.is_timestamp_valid ? (*input)->timestamp : posal_timer_get_time(); + uint64_t timestamp = (*input)->flags.is_timestamp_valid ? (*input)->timestamp : posal_timer_get_time(); uint32_t write_index = pos_buf_ptr->index, temp_wr_ind; // input data not is not available, nothing to do diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h index a9fc0c1c..2f895c8a 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.h @@ -139,6 +139,7 @@ typedef struct capi_pm_t /*header related parameter*/ bool_t is_header_enabled; bool_t is_update_header; + bool_t is_pending_pos_buf_reset; uint32_t header_type_flags; uint8_t *header_buffer_ptr; uint32_t header_buffer_size; @@ -151,7 +152,8 @@ typedef struct capi_pm_t uint32_t batch_bytes_written; // UTC timestamp handling capi_push_ts_data_t ts_data; - + uint64_t last_valid_timestamp; // tracks last valid timestamp received in current batch + } capi_pm_t; /*------------------------------------------------------------------------ @@ -187,6 +189,7 @@ void pull_push_mode_deinit(pull_push_mode_t *pm_ptr); capi_err_t pull_mode_read_input(capi_t *_pif, capi_stream_data_t *input[], capi_stream_data_t *output[]); capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], capi_stream_data_t *output[]); +capi_err_t push_mode_end_header_batch(capi_pm_t *_pif, uint64_t timestamp); capi_err_t pull_push_mode_watermark_levels_init(pull_push_mode_t *pm_ptr, uint32_t num_water_mark_levels, From dac19be2b49368d06dba055f94771153b5dfb6ac Mon Sep 17 00:00:00 2001 From: Sai Raghu Vamsi Anumula Date: Thu, 16 Apr 2026 06:18:36 +0530 Subject: [PATCH 14/24] Fix in PTC external data port asynchronous graph open handling. Handle AON buffering stop, reset, and PTC async open corner cases Tighten DAM and shmem push-mode handling for downstream SG stop scenarios observed during AON buffering. Fix race conditions in PTC external data port asynchronous graph open handling by preventing background threads from referencing invalid port indices, avoiding possible data corruption. Signed-off-by: ffrancis --- .../gen_cntr/core/src/gen_cntr_cmd_handler.c | 4 + .../gen_cntr/ext/pass_thru_cntr/inc/pt_cntr.h | 1 + .../ext/pass_thru_cntr/src/pt_cmd_handler.c | 114 +++++++++++++++--- .../ext/pass_thru_cntr/stub_src/pt_cntr.c | 5 + 4 files changed, 107 insertions(+), 17 deletions(-) diff --git a/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c b/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c index 28af0d4d..7b3f6db4 100644 --- a/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c +++ b/fwk/spf/containers/gen_cntr/core/src/gen_cntr_cmd_handler.c @@ -354,6 +354,10 @@ static ar_result_t gen_cntr_handle_rest_of_graph_open(cu_base_t *base_ptr, void // note: since newly opened modules/ports are not going to change the order of started_sorted_module_list therefore // not updating it + if (check_if_pass_thru_container(me_ptr)) + { + TRY(result, pt_cntr_init_data_ports_post_async_create_finish(&me_ptr->topo)); + } TRY(result, gen_cntr_allocate_wait_mask_arr(me_ptr)); diff --git a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/inc/pt_cntr.h b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/inc/pt_cntr.h index b7260fc1..78628dc0 100644 --- a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/inc/pt_cntr.h +++ b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/inc/pt_cntr.h @@ -194,6 +194,7 @@ ar_result_t pt_cntr_create_module(gen_topo_t *topo_ptr, gen_topo_graph_init_t *graph_init_data_ptr); ar_result_t pt_cntr_init_ext_in_port(void *base_ptr, gu_ext_in_port_t *gu_ext_port_ptr); ar_result_t pt_cntr_init_ext_out_port(void *base_ptr, gu_ext_out_port_t *gu_ext_port_ptr); +ar_result_t pt_cntr_init_data_ports_post_async_create_finish(gen_topo_t *topo_ptr); ar_result_t pt_cntr_deinit_ext_in_port(void *base_ptr, gu_ext_in_port_t *gu_ext_port_ptr); diff --git a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cmd_handler.c b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cmd_handler.c index 395d62a9..66f3c127 100644 --- a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cmd_handler.c +++ b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cmd_handler.c @@ -113,9 +113,10 @@ ar_result_t pt_cntr_post_operate_on_ext_in_port(void *base_ gu_ext_in_port_t **ext_in_port_pptr, spf_cntr_sub_graph_list_t *spf_sg_list_ptr) { - ar_result_t result = AR_EOK; - gen_cntr_ext_in_port_t *ext_in_port_ptr = (gen_cntr_ext_in_port_t *)*ext_in_port_pptr; - gen_topo_input_port_t *in_port_ptr = (gen_topo_input_port_t *)ext_in_port_ptr->gu.int_in_port_ptr; + ar_result_t result = AR_EOK; + pt_cntr_t *me_ptr = (pt_cntr_t *)base_ptr; + pt_cntr_ext_in_port_t *ext_in_port_ptr = (pt_cntr_ext_in_port_t *)*ext_in_port_pptr; + gen_topo_input_port_t *in_port_ptr = (gen_topo_input_port_t *)ext_in_port_ptr->gc.gu.int_in_port_ptr; if (TOPO_SG_OP_STOP & sg_ops) { @@ -125,6 +126,26 @@ ar_result_t pt_cntr_post_operate_on_ext_in_port(void *base_ { // todo: check if the module's first input is getting started, then stop pushing zeros from the next module's // input + + // if only the external input is just opened for the first time, its possible that the buffer was never assigned + // to this port check if the port has valid MF and doesnt have the buffer, then we should trigger the buffer + // assignment and proc list update logic + // + // Note: if the port was stopped and getting started, then buffer may have been already assigned and going from + // stop to start will not trigger proc list update or buffer size change because the ext input rely on the self SG + // state which might have been started and buffer is already assigned on the first port start. + if (TOPO_PORT_STATE_STARTED == in_port_ptr->common.state && (TRUE == in_port_ptr->common.flags.is_mf_valid) && + (NULL == ext_in_port_ptr->topo_in_buf_ptr)) + { + CU_MSG(me_ptr->gc.cu.gu_ptr->log_id, + DBG_LOW_PRIO, + "cu_handle_sg_mgmt_cmd. sg_op %lu handling post operate on external input id 0x%lx", + sg_ops, + ext_in_port_ptr->gc.gu.int_in_port_ptr->cmn.id); + + result |= pt_cntr_update_module_process_list(me_ptr); + result |= pt_cntr_assign_port_buffers(me_ptr); + } } /* If ext input port receives both a self and peer stop/flush from upstream (any order) @@ -341,20 +362,17 @@ static ar_result_t pt_cntr_check_n_validate_module_static_properties(pt_cntr_t * ar_result_t pt_cntr_init_ext_in_port(void *base_ptr, gu_ext_in_port_t *gu_ext_port_ptr) { ar_result_t result = AR_EOK; - pt_cntr_ext_in_port_t *ext_in_port_ptr = (pt_cntr_ext_in_port_t *)gu_ext_port_ptr; - pt_cntr_input_port_t *in_port_ptr = (pt_cntr_input_port_t *)ext_in_port_ptr->gc.gu.int_in_port_ptr; - pt_cntr_module_t *module_ptr = (pt_cntr_module_t *)in_port_ptr->gc.gu.cmn.module_ptr; result = gen_cntr_init_ext_in_port(base_ptr, gu_ext_port_ptr); - in_port_ptr->sdata_ptr = &in_port_ptr->gc.common.sdata; - module_ptr->in_port_sdata_pptr[in_port_ptr->gc.gu.cmn.index] = &in_port_ptr->gc.common.sdata; - #ifdef VERBOSE_DEBUGGING pt_cntr_t *me_ptr = (pt_cntr_t *)base_ptr; + pt_cntr_ext_in_port_t *ext_in_port_ptr = (pt_cntr_ext_in_port_t *)gu_ext_port_ptr; + pt_cntr_input_port_t *in_port_ptr = (pt_cntr_input_port_t *)ext_in_port_ptr->gc.gu.int_in_port_ptr; + pt_cntr_module_t *module_ptr = (pt_cntr_module_t *)in_port_ptr->gc.gu.cmn.module_ptr; GEN_CNTR_MSG(me_ptr->gc.topo.gu.log_id, DBG_LOW_PRIO, - "Assigned self sdata ptr 0x%lx to module 0x%lx in_port_id 0x%x", + "Assigned sdata ptr 0x%lx to module 0x%lx in_port_id 0x%x", in_port_ptr->sdata_ptr, module_ptr->gc.topo.gu.module_instance_id, in_port_ptr->gc.gu.cmn.id); @@ -365,20 +383,17 @@ ar_result_t pt_cntr_init_ext_in_port(void *base_ptr, gu_ext_in_port_t *gu_ext_po ar_result_t pt_cntr_init_ext_out_port(void *base_ptr, gu_ext_out_port_t *gu_ext_port_ptr) { ar_result_t result = AR_EOK; - pt_cntr_ext_out_port_t *ext_out_port_ptr = (pt_cntr_ext_out_port_t *)gu_ext_port_ptr; - pt_cntr_output_port_t *out_port_ptr = (pt_cntr_output_port_t *)ext_out_port_ptr->gc.gu.int_out_port_ptr; - pt_cntr_module_t *module_ptr = (pt_cntr_module_t *)out_port_ptr->gc.gu.cmn.module_ptr; result = gen_cntr_init_ext_out_port(base_ptr, gu_ext_port_ptr); - out_port_ptr->sdata_ptr = &out_port_ptr->gc.common.sdata; - module_ptr->out_port_sdata_pptr[out_port_ptr->gc.gu.cmn.index] = &out_port_ptr->gc.common.sdata; - #ifdef VERBOSE_DEBUGGING pt_cntr_t *me_ptr = (pt_cntr_t *)base_ptr; + pt_cntr_ext_out_port_t *ext_out_port_ptr = (pt_cntr_ext_out_port_t *)gu_ext_port_ptr; + pt_cntr_output_port_t *out_port_ptr = (pt_cntr_output_port_t *)ext_out_port_ptr->gc.gu.int_out_port_ptr; + pt_cntr_module_t *module_ptr = (pt_cntr_module_t *)out_port_ptr->gc.gu.cmn.module_ptr; GEN_CNTR_MSG(me_ptr->gc.topo.gu.log_id, DBG_LOW_PRIO, - "Assigned self sdata ptr 0x%lx to module 0x%lx out_port_id 0x%x", + "Assigned sdata ptr 0x%lx to module 0x%lx out_port_id 0x%x", out_port_ptr->sdata_ptr, module_ptr->gc.topo.gu.module_instance_id, out_port_ptr->gc.gu.cmn.id); @@ -541,3 +556,68 @@ ar_result_t pt_cntr_create_module(gen_topo_t *topo_ptr, return result; } + +// some data ports could be added through async gu, and those ports would get a valid port index only after calling +// gu_async_create_finish(), PTC has dependency on the port indices hence newly opened ports should be handled +// under critical section through this function. +ar_result_t pt_cntr_init_data_ports_post_async_create_finish(gen_topo_t *topo_ptr) +{ + ar_result_t result = AR_EOK; + + gu_t *gu_ptr = get_gu_ptr_for_current_command_context(&topo_ptr->gu); + + // Consider only updated modules, for new or default status modules data port scratch will assigned during + // prepare. for updated modules scratch info needs to be updated here itself since the module may be part of + // the proc list and there is a chance that module process can be called before prepare is received. + + // New ports may be added. need to set it to module. + for (gu_ext_in_port_list_t *ext_in_port_list_ptr = gu_ptr->ext_in_port_list_ptr; (NULL != ext_in_port_list_ptr); + LIST_ADVANCE(ext_in_port_list_ptr)) + { + pt_cntr_input_port_t *in_port_ptr = + (pt_cntr_input_port_t *)ext_in_port_list_ptr->ext_in_port_ptr->int_in_port_ptr; + + pt_cntr_module_t *module_ptr = + (pt_cntr_module_t *)ext_in_port_list_ptr->ext_in_port_ptr->int_in_port_ptr->cmn.module_ptr; + + if (NULL == in_port_ptr->sdata_ptr) + { + in_port_ptr->sdata_ptr = &in_port_ptr->gc.common.sdata; + module_ptr->in_port_sdata_pptr[in_port_ptr->gc.gu.cmn.index] = &in_port_ptr->gc.common.sdata; + + GEN_CNTR_MSG(topo_ptr->gu.log_id, + DBG_LOW_PRIO, + "Assigning self sdata ptr 0x%lx to module 0x%lx external in_port_id 0x%x", + in_port_ptr->sdata_ptr, + module_ptr->gc.topo.gu.module_instance_id, + in_port_ptr->gc.gu.cmn.id); + } + } + + // New ports may be added. need to intialize sdata ptr for the ext port + for (gu_ext_out_port_list_t *ext_out_port_list_ptr = gu_ptr->ext_out_port_list_ptr; + (NULL != ext_out_port_list_ptr); + LIST_ADVANCE(ext_out_port_list_ptr)) + { + pt_cntr_output_port_t *out_port_ptr = + (pt_cntr_output_port_t *)ext_out_port_list_ptr->ext_out_port_ptr->int_out_port_ptr; + + pt_cntr_module_t *module_ptr = + (pt_cntr_module_t *)ext_out_port_list_ptr->ext_out_port_ptr->int_out_port_ptr->cmn.module_ptr; + + if (NULL == out_port_ptr->sdata_ptr) + { + out_port_ptr->sdata_ptr = &out_port_ptr->gc.common.sdata; + module_ptr->out_port_sdata_pptr[out_port_ptr->gc.gu.cmn.index] = &out_port_ptr->gc.common.sdata; + + GEN_CNTR_MSG(topo_ptr->gu.log_id, + DBG_LOW_PRIO, + "Assigning self sdata ptr 0x%lx to module 0x%lx out_port_id 0x%x", + out_port_ptr->sdata_ptr, + module_ptr->gc.topo.gu.module_instance_id, + out_port_ptr->gc.gu.cmn.id); + } + } + + return result; +} \ No newline at end of file diff --git a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/stub_src/pt_cntr.c b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/stub_src/pt_cntr.c index e870507e..91dde1f2 100644 --- a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/stub_src/pt_cntr.c +++ b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/stub_src/pt_cntr.c @@ -70,6 +70,11 @@ ar_result_t pt_cntr_validate_media_fmt_thresh(pt_cntr_t *me_ptr) } ar_result_t pt_cntr_validate_topo_at_open(pt_cntr_t *me_ptr) +{ + return AR_EUNSUPPORTED; +} + +ar_result_t pt_cntr_init_data_ports_post_async_create_finish(gen_topo_t *topo_ptr) { return AR_EUNSUPPORTED; } \ No newline at end of file From 4baa1008b3667dffb8e6d3e3ca2a6207f2db3471 Mon Sep 17 00:00:00 2001 From: aragir Date: Thu, 16 Apr 2026 21:14:56 +0530 Subject: [PATCH 15/24] SHMEM Module, Incorrect Batch index after TS discontinuity Maintain correct header-based batch tracking across timestamp discontinuities in shmem push-mode writes. Fixes incorrect header updates triggered by timestamp discontinuity handling. Signed-off-by: ffrancis --- .../modules/sh_mem_pull_push_mode/src/pull_push_mode.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c index a0b3dbe0..d791b298 100644 --- a/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c +++ b/fwk/spf/modules/sh_mem_pull_push_mode/src/pull_push_mode.c @@ -880,6 +880,10 @@ capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], cap me_ptr->curr_shared_buf_ptr = NULL; module_buf_ptr[0].data_ptr = NULL; } + if(capi_ptr->is_header_enabled) + { + write_index = capi_ptr->batch_write_index; + } } else if (me_ptr->curr_shared_buf_ptr) { @@ -950,10 +954,10 @@ capi_err_t push_mode_write_output(capi_t *_pif, capi_stream_data_t *input[], cap else // buffer access extension is disabled. { // Use batch_write_index for tracking within batch when headers enabled - if (capi_ptr->is_header_enabled && 0 != module_buf_ptr[0].actual_data_len) + if (capi_ptr->is_header_enabled) { // Write batch header at the start of a new batch (when headers enabled and not in middle of batch) - if (capi_ptr->is_update_header) + if (capi_ptr->is_update_header && 0 != module_buf_ptr[0].actual_data_len) { capi_ptr->pcm_bytes_written = 0; // Initialize batch tracking From 400abf734eba2f252cd939287799a856c6b9c180 Mon Sep 17 00:00:00 2001 From: ritirai Date: Fri, 17 Apr 2026 00:26:01 +0530 Subject: [PATCH 16/24] Fix invalid address access from buffer pool Prevent invalid list-node access while removing cached subgraph, data-link, and control-link entries during multi-client cleanup. Fixes list traversal during multi-client cleanup Signed-off-by: ffrancis --- .../apm/ext/multi_client/src/apm_multi_client.c | 14 +++++++++----- .../ext/multi_client/src/apm_multi_client_utils.c | 8 ++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/fwk/spf/apm/ext/multi_client/src/apm_multi_client.c b/fwk/spf/apm/ext/multi_client/src/apm_multi_client.c index d79b963f..df205b03 100644 --- a/fwk/spf/apm/ext/multi_client/src/apm_multi_client.c +++ b/fwk/spf/apm/ext/multi_client/src/apm_multi_client.c @@ -1103,14 +1103,16 @@ ar_result_t apm_multi_client_update_sg_calibration_state(apm_t *apm_info_ptr) { ar_result_t result = AR_EOK; apm_sub_graph_t *sub_graph_node_ptr = NULL; - spf_list_node_t *temp_cached_ptr; + spf_list_node_t *curr_ptr = apm_info_ptr->curr_cmd_ctrl_ptr->db_default_set_cfg_cmd_ctrl.parsed_sg_list_ptr; gpr_packet_t *packet_curr_client = (gpr_packet_t *)apm_info_ptr->curr_cmd_ctrl_ptr->cmd_msg.payload_ptr; while (NULL != curr_ptr) { - uint32_t sub_graph_id = *((uint32_t *)(curr_ptr->obj_ptr)); + spf_list_node_t *next_curr_ptr = curr_ptr->next_ptr; + + uint32_t sub_graph_id = *((uint32_t *)(curr_ptr->obj_ptr)); result = apm_db_get_sub_graph_node(&apm_info_ptr->graph_info, sub_graph_id, &sub_graph_node_ptr, APM_DB_OBJ_QUERY); @@ -1135,14 +1137,16 @@ ar_result_t apm_multi_client_update_sg_calibration_state(apm_t *apm_info_ptr) packet_curr_client->src_port, sub_graph_id); - temp_cached_ptr = curr_ptr; + uint32_t * temp_cached_obj_ptr = (uint32_t *)(curr_ptr->obj_ptr); apm_db_remove_node_from_list(&apm_info_ptr->curr_cmd_ctrl_ptr->db_default_set_cfg_cmd_ctrl.parsed_sg_list_ptr, (void *)(curr_ptr->obj_ptr), &apm_info_ptr->curr_cmd_ctrl_ptr->db_default_set_cfg_cmd_ctrl.num_subgraph_node); - posal_memory_free(temp_cached_ptr->obj_ptr); - curr_ptr = curr_ptr->next_ptr; + posal_memory_free((void*)temp_cached_obj_ptr); + temp_cached_obj_ptr = NULL; + + curr_ptr = next_curr_ptr; } return result; diff --git a/fwk/spf/apm/ext/multi_client/src/apm_multi_client_utils.c b/fwk/spf/apm/ext/multi_client/src/apm_multi_client_utils.c index f4a88376..a812e6ca 100644 --- a/fwk/spf/apm/ext/multi_client/src/apm_multi_client_utils.c +++ b/fwk/spf/apm/ext/multi_client/src/apm_multi_client_utils.c @@ -289,6 +289,8 @@ ar_result_t apm_multi_client_free_conn(apm_t *apm_info_ptr, apm_sub_graph_t *sub while (conn_cfg_list_ptr) { + spf_list_node_t *next_conn_cfg_list_ptr = conn_cfg_list_ptr->next_ptr; + apm_module_data_port_conn_t *conn_cfg_ptr = (apm_module_data_port_conn_t *)conn_cfg_list_ptr->obj_ptr; // All the connection from this SG should be removed either originating or ending @@ -322,7 +324,7 @@ ar_result_t apm_multi_client_free_conn(apm_t *apm_info_ptr, apm_sub_graph_t *sub conn_cfg_ptr = NULL; } - LIST_ADVANCE(conn_cfg_list_ptr); + conn_cfg_list_ptr = next_conn_cfg_list_ptr; } return result; @@ -340,6 +342,8 @@ ar_result_t apm_multi_client_free_ctrl_link(apm_t *apm_info_ptr, apm_sub_graph_t while (ctrl_link_cfg_list_ptr) { + spf_list_node_t *next_ctrl_link_cfg_list_ptr = ctrl_link_cfg_list_ptr->next_ptr; + apm_module_ctrl_port_conn_t *ctrl_link_cfg_ptr = (apm_module_ctrl_port_conn_t *)ctrl_link_cfg_list_ptr->obj_ptr; // All the connection from this SG should be removed either originating or ending @@ -373,7 +377,7 @@ ar_result_t apm_multi_client_free_ctrl_link(apm_t *apm_info_ptr, apm_sub_graph_t ctrl_link_cfg_ptr = NULL; } - LIST_ADVANCE(ctrl_link_cfg_list_ptr); + ctrl_link_cfg_list_ptr = next_ctrl_link_cfg_list_ptr; } return result; From 8c1df72a17b8525f6b8579a351dcff9b38597ced Mon Sep 17 00:00:00 2001 From: ayugupt Date: Sat, 18 Apr 2026 11:33:39 +0530 Subject: [PATCH 17/24] Fix dynamic event handling in pass-through container Process pending framework events in the pass-through container while an asynchronous command is still active. Detect active command context before clearing triggers so module-raised dynamic events are handled in the data path at the correct time. Signed-off-by: ffrancis --- .../gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c index 6ed8a32e..3236fd90 100644 --- a/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c +++ b/fwk/spf/containers/gen_cntr/ext/pass_thru_cntr/src/pt_cntr_process.c @@ -2270,6 +2270,12 @@ ar_result_t pt_cntr_signal_trigger(cu_base_t *cu_ptr, uint32_t channel_bit_index // } } + if (me_ptr->gc.cu.cmd_msg.payload_ptr) + { + // if async command processing is going on then check for any pending event handling + gen_cntr_handle_fwk_events_in_data_path(&me_ptr->gc); + } + /*clear the trigger signal */ posal_signal_clear_inline(me_ptr->gc.st_module.trigger_signal_ptr); From fa7d20a58cf111c491ecf829f12b070b676f70f5 Mon Sep 17 00:00:00 2001 From: Lijo Cyriac George Date: Sun, 31 May 2026 23:53:22 +0530 Subject: [PATCH 18/24] alsa: fix capture endpoint implementation Fix resource leaks, lifecycle ordering, and data path correctness issues in the ALSA device source endpoint. Ensure proper teardown sequencing, correct 24-bit PCM format handling, and consistent interleaving between the advertised media format and the capture process path. Signed-off-by: ffrancis --- .../alsa_device/capi/src/capi_alsa_device.c | 92 +++++++++++++------ .../alsa_device/capi/src/capi_alsa_device_i.h | 2 + .../lib/src/tinyalsa/alsa_device_driver.c | 9 +- 3 files changed, 75 insertions(+), 28 deletions(-) diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c index f6b2cfe9..23ecf0c2 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c +++ b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c @@ -336,7 +336,7 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me } case FWK_EXTN_PROPERTY_ID_STM_CTRL: { - if (payload_ptr->actual_data_len < sizeof(capi_prop_stm_ctrl_t)) + if (payload_ptr->actual_data_len < sizeof(capi_custom_property_t) + sizeof(capi_prop_stm_ctrl_t)) { AR_MSG(DBG_ERROR_PRIO, "Property id 0x%lx Bad param size %lu", @@ -353,6 +353,12 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me if (ALSA_DEVICE_SOURCE == me_ptr->direction && me_ptr->enable_stm && me_ptr->state != ALSA_DEVICE_INTERFACE_START) { + if (!me_ptr->ep_mf_received) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI_ALSA_DEVICE: STM enable received before MF config, ignoring"); + break; + } capi_result = alsa_device_driver_open(&me_ptr->alsa_device_driver, me_ptr->direction); if (capi_result != AR_EOK) { @@ -366,6 +372,7 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: alsa_device_driver_prepare failed with error code %d", capi_result); + alsa_device_driver_close(&me_ptr->alsa_device_driver); return CAPI_EFAILED; } @@ -375,13 +382,14 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: alsa_device_driver_start failed with error code %d", capi_result); + alsa_device_driver_close(&me_ptr->alsa_device_driver); return CAPI_EFAILED; } if (NULL == me_ptr->read_buffer) { struct pcm_config *config = &me_ptr->alsa_device_driver.config; - me_ptr->read_buffer_size = config->period_size * config->channels * (me_ptr->bit_width / 8); + me_ptr->read_buffer_size = config->period_size * config->channels * me_ptr->bytes_per_channel; me_ptr->read_buffer = (int8_t *)posal_memory_malloc( me_ptr->read_buffer_size, @@ -391,6 +399,7 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me { AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to allocate read_buffer, size=%d", me_ptr->read_buffer_size); + alsa_device_driver_close(&me_ptr->alsa_device_driver); return CAPI_ENOMEMORY; } @@ -404,7 +413,7 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me ar_result_t thread_result = posal_thread_launch(&me_ptr->dma_wait_thread, "ALSA_DMA_WAIT", - ALSA_DEVICE_STACK_SIZE, + ALSA_DEVICE_DMA_THREAD_STACK_SIZE, 0, capi_alsa_device_dma_wait_thread, (void *)me_ptr, @@ -413,6 +422,9 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me if (AR_EOK != thread_result) { AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to create DMA wait thread"); + posal_memory_free(me_ptr->read_buffer); + me_ptr->read_buffer = NULL; + alsa_device_driver_close(&me_ptr->alsa_device_driver); return CAPI_EFAILED; } @@ -740,27 +752,40 @@ capi_err_t capi_alsa_device_end(capi_t *_pif) capi_alsa_device_t *me_ptr = (capi_alsa_device_t *)_pif; - if (me_ptr->is_thread_running) + if (me_ptr->state == ALSA_DEVICE_INTERFACE_START) { - me_ptr->exit_thread = TRUE; - } + if (me_ptr->is_thread_running) + { + me_ptr->exit_thread = TRUE; + } - ar_result = alsa_device_driver_stop(&me_ptr->alsa_device_driver); - if (ar_result != AR_EOK) - { - AR_MSG(DBG_ERROR_PRIO, - "CAPI_ALSA_DEVICE: alsa_device_driver_stop failed with error code %d", - ar_result); - capi_result = CAPI_EFAILED; - } + ar_result = alsa_device_driver_stop(&me_ptr->alsa_device_driver); + if (ar_result != AR_EOK) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI_ALSA_DEVICE: alsa_device_driver_stop failed with error code %d", + ar_result); + capi_result = CAPI_EFAILED; + } - ar_result = alsa_device_driver_close(&me_ptr->alsa_device_driver); - if (ar_result != AR_EOK) - { - AR_MSG(DBG_ERROR_PRIO, - "CAPI_ALSA_DEVICE: alsa_device_driver_close failed with error code %d", - ar_result); - capi_result = CAPI_EFAILED; + /* pcm_stop() above unblocks the DMA thread from pcm_read(). Join it now so + * read_buffer is not freed while the thread is still writing into it. */ + if (me_ptr->dma_wait_thread != NULL) + { + ar_result_t thread_result = AR_EOK; + posal_thread_join(me_ptr->dma_wait_thread, &thread_result); + me_ptr->dma_wait_thread = NULL; + AR_MSG(DBG_HIGH_PRIO, "CAPI_ALSA_DEVICE: DMA wait thread joined"); + } + + ar_result = alsa_device_driver_close(&me_ptr->alsa_device_driver); + if (ar_result != AR_EOK) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI_ALSA_DEVICE: alsa_device_driver_close failed with error code %d", + ar_result); + capi_result = CAPI_EFAILED; + } } if (me_ptr->read_buffer) @@ -1099,7 +1124,7 @@ capi_err_t capi_alsa_device_process_sink(capi_t *_pif, capi_stream_data_t *input num_samples_per_intr = me_ptr->int_samples_per_period; num_channels = me_ptr->num_channels; bytes_per_channel = me_ptr->bytes_per_channel; - bytes_per_sample = me_ptr->bit_width / 8; + bytes_per_sample = me_ptr->bytes_per_channel; word_size = bytes_per_sample << 3; total_bytes = bytes_per_channel * num_samples_per_intr * num_channels; expected_ip_len_per_ch = bytes_per_channel * num_samples_per_intr; @@ -1356,7 +1381,7 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp } num_channels = me_ptr->num_channels; - bytes_per_sample = me_ptr->bit_width / 8; + bytes_per_sample = me_ptr->bytes_per_channel; word_size = bytes_per_sample << 3; total_bytes = me_ptr->read_buffer_size; @@ -1371,14 +1396,16 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp uint32_t bytes_per_ch = total_bytes / num_channels; for (uint32_t ch = 0; ch < num_channels; ch++) { - memset(output[port]->buf_ptr[ch].data_ptr, 0, bytes_per_ch); - output[port]->buf_ptr[ch].actual_data_len = bytes_per_ch; + uint32_t fill_bytes = min(bytes_per_ch, output[port]->buf_ptr[ch].max_data_len); + memset(output[port]->buf_ptr[ch].data_ptr, 0, fill_bytes); + output[port]->buf_ptr[ch].actual_data_len = fill_bytes; } } else // CAPI_INTERLEAVED { - memset(output[port]->buf_ptr[0].data_ptr, 0, total_bytes); - output[port]->buf_ptr[0].actual_data_len = total_bytes; + uint32_t fill_bytes = min(total_bytes, output[port]->buf_ptr[0].max_data_len); + memset(output[port]->buf_ptr[0].data_ptr, 0, fill_bytes); + output[port]->buf_ptr[0].actual_data_len = fill_bytes; } return CAPI_EOK; } @@ -1596,10 +1623,12 @@ ar_result_t capi_alsa_device_set_hw_ep_mf_cfg(param_id_hw_ep_mf_t *alsa_device_c if (DATA_FORMAT_COMPR_OVER_PCM_PACKETIZED == me_ptr->data_format) { me_ptr->gen_cntr_alsa_device_media_fmt.header.format_header.data_format = CAPI_COMPR_OVER_PCM_PACKETIZED; + me_ptr->gen_cntr_alsa_device_media_fmt.format.data_interleaving = CAPI_INTERLEAVED; } else { me_ptr->gen_cntr_alsa_device_media_fmt.header.format_header.data_format = CAPI_FIXED_POINT; + me_ptr->gen_cntr_alsa_device_media_fmt.format.data_interleaving = CAPI_DEINTERLEAVED_UNPACKED; } AR_MSG(DBG_HIGH_PRIO, "ALSA Device: Initialized data_format for SOURCE direction: %d", @@ -1626,6 +1655,15 @@ ar_result_t capi_alsa_device_set_hw_ep_mf_cfg(param_id_hw_ep_mf_t *alsa_device_c me_ptr->int_samples_per_period = me_ptr->sample_rate / NUM_MS_PER_SEC; } alsa_device_driver_set_cfg(&me_ptr->alsa_device_driver, alsa_device_cfg_ptr); + + /* If frame size arrived before MF, period_size was computed with rate=0. + * Recompute now that config->rate is valid. */ + if (me_ptr->frame_size_cfg_received) + { + param_id_frame_size_factor_t frame_size_cfg = { .frame_size_factor = me_ptr->frame_size_ms }; + alsa_device_driver_set_frame_size_cfg(&frame_size_cfg, &me_ptr->alsa_device_driver); + } + // Set flag to true me_ptr->ep_mf_received = TRUE; diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h index c102caef..372711f4 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h +++ b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h @@ -33,6 +33,8 @@ Note: this module is not defined as Signal Triggered Module */ #define ALSA_DEVICE_NUM_FRAMEWORK_EXTENSIONS_SOURCE 1 #define ALSA_DEVICE_NUM_FRAMEWORK_EXTENSIONS_SINK 0 +#define ALSA_DEVICE_DMA_THREAD_STACK_SIZE 8192 + /* Number of milliseconds in a second*/ #define NUM_MS_PER_SEC 1000 diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/lib/src/tinyalsa/alsa_device_driver.c b/fwk/platform/modules/generic/endpoint/alsa_device/lib/src/tinyalsa/alsa_device_driver.c index 721a4f1b..340f8559 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/lib/src/tinyalsa/alsa_device_driver.c +++ b/fwk/platform/modules/generic/endpoint/alsa_device/lib/src/tinyalsa/alsa_device_driver.c @@ -28,7 +28,7 @@ ar_result_t alsa_device_driver_set_cfg(alsa_device_driver_t *alsa_device_driver_ if (alsa_device_cfg_ptr->bit_width == 32) config->format = PCM_FORMAT_S32_LE; else if (alsa_device_cfg_ptr->bit_width == 24) - config->format = PCM_FORMAT_S24_3LE; + config->format = PCM_FORMAT_S24_LE; else if (alsa_device_cfg_ptr->bit_width == 16) config->format = PCM_FORMAT_S16_LE; @@ -85,6 +85,11 @@ ar_result_t alsa_device_driver_open(alsa_device_driver_t *alsa_device_driver_ptr AR_MSG(DBG_ERROR_PRIO, "ALSA_DEVICE_DRIVER: Unable to open PCM device %u (%s)\n", alsa_device_driver_ptr->device_id, pcm_get_error(alsa_device_driver_ptr->pcm)); + if (alsa_device_driver_ptr->pcm) + { + pcm_close(alsa_device_driver_ptr->pcm); + alsa_device_driver_ptr->pcm = NULL; + } return AR_EFAILED; } @@ -184,9 +189,11 @@ ar_result_t alsa_device_driver_close(alsa_device_driver_t *alsa_device_driver_pt { AR_MSG(DBG_ERROR_PRIO, "ALSA_DEVICE_DRIVER: pcm close failed with error: %s\n", pcm_get_error(alsa_device_driver_ptr->pcm)); + alsa_device_driver_ptr->pcm = NULL; return AR_EFAILED; } + alsa_device_driver_ptr->pcm = NULL; AR_MSG(DBG_HIGH_PRIO, "ALSA_DEVICE_DRIVER: pcm close success.\n"); return AR_EOK; From 799ce306787962d07c995b774084afdd345fbbfb Mon Sep 17 00:00:00 2001 From: Lijo Cyriac George Date: Mon, 1 Jun 2026 01:41:36 +0530 Subject: [PATCH 19/24] alsa: fix capture thread race condition and synchronization Prevent concurrent access to read_buffer between the DMA wait thread and process_source by introducing a mutex and condition variable. The DMA thread now waits for process_source to consume read_buffer before issuing the next pcm_read, eliminating the data race on data_ready and the buffer overwrite observed with back-to-back pcm_read completions due to ALSA buffer accumulation. Also sync int_samples_per_period with the actual period_size negotiated by the ALSA driver after pcm_open, keeping the framework threshold and DMA read size consistent when hardware rounds the requested period size. Signed-off-by: ffrancis --- .../alsa_device/capi/src/capi_alsa_device.c | 91 +++++++++++++++++-- .../alsa_device/capi/src/capi_alsa_device_i.h | 6 ++ 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c index 23ecf0c2..a8f2c699 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c +++ b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c @@ -57,15 +57,25 @@ static void capi_alsa_device_dma_wait_thread(void *arg) AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: DMA thread read %d bytes", me_ptr->read_buffer_size); - /* Mark data as ready */ + /* Mark data as ready and signal the framework */ + posal_nmutex_lock(me_ptr->buf_lock); me_ptr->data_ready = TRUE; + posal_nmutex_unlock(me_ptr->buf_lock); - /* Signal framework using STM signal */ if (me_ptr->signal_ptr && me_ptr->enable_stm) { AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: signaling the container"); posal_signal_send(me_ptr->signal_ptr); } + + /* Block until process_source consumes read_buffer before issuing next pcm_read. + * This prevents overwriting read_buffer while process_source is still copying it. */ + posal_nmutex_lock(me_ptr->buf_lock); + while (me_ptr->data_ready && !me_ptr->exit_thread) + { + posal_condvar_wait(me_ptr->buf_consumed_cond, me_ptr->buf_lock); + } + posal_nmutex_unlock(me_ptr->buf_lock); } me_ptr->is_thread_running = FALSE; @@ -166,7 +176,20 @@ static capi_err_t capi_alsa_device_common_init(capi_t *_pif, capi_proplist_t *in return capi_result; } - return capi_result; + if (AR_EOK != posal_nmutex_create(&me_ptr->buf_lock, (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id)) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to create buf_lock"); + return CAPI_EFAILED; + } + + if (AR_EOK != posal_condvar_create(&me_ptr->buf_consumed_cond, (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id)) + { + posal_nmutex_destroy(&me_ptr->buf_lock); + AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to create buf_consumed_cond"); + return CAPI_EFAILED; + } + + return CAPI_EOK; } /*------------------------------------------------------------------------ @@ -366,6 +389,23 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me return CAPI_EFAILED; } + /* Sync int_samples_per_period with the actual period_size the ALSA driver + * negotiated — it may differ from the requested size due to HW constraints. */ + uint32_t actual_period_size = me_ptr->alsa_device_driver.config.period_size; + if (actual_period_size != me_ptr->int_samples_per_period) + { + AR_MSG(DBG_HIGH_PRIO, + "CAPI_ALSA_DEVICE: ALSA adjusted period_size: requested=%d actual=%d frames", + me_ptr->int_samples_per_period, actual_period_size); + me_ptr->int_samples_per_period = actual_period_size; + capi_result = capi_alsa_device_raise_thresh_delay_events(me_ptr); + if (CAPI_EOK != capi_result) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI_ALSA_DEVICE: Failed to raise threshold event after period_size adjustment"); + } + } + capi_result = alsa_device_driver_prepare(&me_ptr->alsa_device_driver); if (capi_result != AR_EOK) { @@ -759,6 +799,14 @@ capi_err_t capi_alsa_device_end(capi_t *_pif) me_ptr->exit_thread = TRUE; } + /* Wake the DMA thread if it is blocked on buf_consumed_cond waiting for + * process_source to consume. pcm_stop() handles the case where it is + * blocked inside pcm_read(). Both are needed to cover either scenario. */ + posal_nmutex_lock(me_ptr->buf_lock); + me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->buf_lock); + posal_condvar_signal(me_ptr->buf_consumed_cond); + ar_result = alsa_device_driver_stop(&me_ptr->alsa_device_driver); if (ar_result != AR_EOK) { @@ -796,6 +844,9 @@ capi_err_t capi_alsa_device_end(capi_t *_pif) AR_MSG(DBG_HIGH_PRIO, "CAPI_ALSA_DEVICE: read_buffer freed"); } + posal_condvar_destroy(&me_ptr->buf_consumed_cond); + posal_nmutex_destroy(&me_ptr->buf_lock); + me_ptr->state = ALSA_DEVICE_INTERFACE_STOP; me_ptr->vtbl.vtbl_ptr = NULL; return capi_result; @@ -1385,8 +1436,12 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp word_size = bytes_per_sample << 3; total_bytes = me_ptr->read_buffer_size; + posal_nmutex_lock(me_ptr->buf_lock); + if (!me_ptr->data_ready) { + posal_nmutex_unlock(me_ptr->buf_lock); + // Data not ready - this is an underrun condition AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Underrun - no data available in read_buffer"); @@ -1410,26 +1465,42 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp return CAPI_EOK; } - // Data is ready - copy from read_buffer + // Data is ready - copy from read_buffer under lock to prevent DMA thread overwrite if (CAPI_DEINTERLEAVED_UNPACKED == me_ptr->gen_cntr_alsa_device_media_fmt.format.data_interleaving) { + uint32_t bytes_per_ch = total_bytes / num_channels; + + for (uint32_t ch = 0; ch < num_channels; ch++) + { + if (output[port]->buf_ptr[ch].max_data_len < bytes_per_ch) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI_ALSA_DEVICE: Output buffer too small ch %d. Required: %d, Available: %d", + ch, bytes_per_ch, output[port]->buf_ptr[ch].max_data_len); + me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->buf_lock); + posal_condvar_signal(me_ptr->buf_consumed_cond); + return CAPI_ENOMEMORY; + } + } + capi_buf_t intlv_buf; intlv_buf.data_ptr = me_ptr->read_buffer; intlv_buf.actual_data_len = total_bytes; intlv_buf.max_data_len = me_ptr->read_buffer_size; - // Deinterleave to output buffers if (AR_EOK != spf_intlv_to_deintlv(&intlv_buf, output[port]->buf_ptr, num_channels, word_size)) { AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to deinterleave data"); + me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->buf_lock); + posal_condvar_signal(me_ptr->buf_consumed_cond); return CAPI_EFAILED; } - // Update actual data length for each channel - uint32_t bytes_per_ch = total_bytes / num_channels; for (uint32_t ch = 0; ch < num_channels; ch++) { output[port]->buf_ptr[ch].actual_data_len = bytes_per_ch; @@ -1437,13 +1508,15 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp } else // CAPI_INTERLEAVED { - // For interleaved output, copy directly to first buffer if (output[port]->buf_ptr[0].max_data_len < total_bytes) { AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Output buffer too small. Required: %d, Available: %d", total_bytes, output[port]->buf_ptr[0].max_data_len); + me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->buf_lock); + posal_condvar_signal(me_ptr->buf_consumed_cond); return CAPI_ENOMEMORY; } @@ -1455,6 +1528,8 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp } me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->buf_lock); + posal_condvar_signal(me_ptr->buf_consumed_cond); AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: Process source successful, bytes: %d", total_bytes); diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h index 372711f4..9875f87d 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h +++ b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h @@ -16,6 +16,8 @@ #include "alsa_device_api.h" #include "capi_cmn.h" #include "alsa_device_driver.h" +#include "posal_nmutex.h" +#include "posal_condvar.h" /*===================================================================== Macros @@ -116,6 +118,10 @@ typedef struct capi_alsa_device int8_t *read_buffer; // Buffer to hold one period of captured data uint32_t read_buffer_size; // Size of read buffer in bytes bool_t data_ready; // Flag: data available in read buffer + + /* Synchronization between DMA thread and process_source */ + posal_nmutex_t buf_lock; // Protects read_buffer and data_ready + posal_condvar_t buf_consumed_cond; // Signaled by process_source when data_ready cleared } capi_alsa_device_t; /*------------------------------------------------------------------------ From cda44251e8a00d1d8da5d59e17f52602d5bc24fe Mon Sep 17 00:00:00 2001 From: Kumar Anurag Singh Date: Fri, 5 Jun 2026 20:52:04 -0700 Subject: [PATCH 20/24] fwk: gen_cntr: fix %lu format specifier for uint32_t log args %lu expects unsigned long (8 bytes on 64-bit LP64), but the arguments are uint32_t (4 bytes). This causes the logger to read 8 bytes off the va_args stack, producing garbage values in the "Output buffer is too small" error message. Fix both occurrences to use %u. Signed-off-by: ffrancis --- .../gen_cntr/core/src/gen_cntr_data_handler_island.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fwk/spf/containers/gen_cntr/core/src/gen_cntr_data_handler_island.c b/fwk/spf/containers/gen_cntr/core/src/gen_cntr_data_handler_island.c index dcaa4729..584d1435 100644 --- a/fwk/spf/containers/gen_cntr/core/src/gen_cntr_data_handler_island.c +++ b/fwk/spf/containers/gen_cntr/core/src/gen_cntr_data_handler_island.c @@ -332,7 +332,7 @@ static ar_result_t gen_cntr_check_output_space_availability(gen_cntr_t * GEN_CNTR_MSG_ISLAND(me_ptr->topo.gu.log_id, DBG_ERROR_PRIO, - "Output buffer is too small (%lu - %lu) < (%lu) ", + "Output buffer is too small (%u - %u) < (%u) ", max_data_len, actual_data_len, actual_len); @@ -387,7 +387,7 @@ static ar_result_t gen_cntr_check_output_space_availability(gen_cntr_t * { GEN_CNTR_MSG_ISLAND(me_ptr->topo.gu.log_id, DBG_ERROR_PRIO, - "Output buffer is too small (%lu - %lu) < (%lu) ", + "Output buffer is too small (%u - %u) < (%u) ", ext_out_port_ptr->buf.max_data_len, ext_out_port_ptr->buf.actual_data_len, *total_len_ptr); From 63c7a4448292fb66e5e15696da42834d1a8eabc3 Mon Sep 17 00:00:00 2001 From: Lijo Cyriac George Date: Sat, 6 Jun 2026 12:56:47 +0530 Subject: [PATCH 21/24] fwk: spf: modules: enable sync and splitter Register sync, priority_sync and splitter with AMDB by switching to BUILD_ARE_MODULES. Signed-off-by: ffrancis --- fwk/spf/modules/priority_sync/build/Android.mk | 15 +++++++++++++-- fwk/spf/modules/simple_splitter/build/Android.mk | 16 ++++++++++++++-- fwk/spf/modules/sync_module/build/Android.mk | 15 +++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/fwk/spf/modules/priority_sync/build/Android.mk b/fwk/spf/modules/priority_sync/build/Android.mk index 36d4e6de..1e3bf713 100644 --- a/fwk/spf/modules/priority_sync/build/Android.mk +++ b/fwk/spf/modules/priority_sync/build/Android.mk @@ -11,7 +11,7 @@ include $(BUILD_HEADER_LIBRARY) include $(CLEAR_VARS) -LOCAL_MODULE := capi_priority_sync +LOCAL_MODULE := lib_priority_sync LOCAL_MODULE_TAGS := optional LOCAL_VENDOR_MODULE := true LOCAL_C_INCLUDES := \ @@ -42,5 +42,16 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_HEADER_LIBRARIES := libposal_headers libspf_api libspf_interfaces_headers libspf_utils_headers libapm_headers libamdb_headers LOCAL_STATIC_LIBRARIES := libposal libspf_interfaces libapm libamdb -include $(BUILD_STATIC_LIBRARY) +LOCAL_SPF_MODULE_KCONFIG := CONFIG_PRIORITY_SYNC +LOCAL_SPF_MODULE_NAME := $(LOCAL_MODULE) +LOCAL_SPF_MODULE_MAJOR_VER := 1 +LOCAL_SPF_MODULE_MINOR_VER := 0 +LOCAL_SPF_MODULE_AMDB_ITYPE := "capi" +LOCAL_SPF_MODULE_AMDB_MTYPE := "generic" +LOCAL_SPF_MODULE_AMDB_MID := "0x07001028" +LOCAL_SPF_MODULE_AMDB_TAG := "capi_priority_sync" +LOCAL_SPF_MODULE_AMDB_MOD_NAME := "MODULE_ID_PRIORITY_SYNC" +LOCAL_SPF_MODULE_H2XML_HEADERS := "$(LOCAL_PATH)/api/priority_sync_api.h" + +include $(BUILD_ARE_MODULES) diff --git a/fwk/spf/modules/simple_splitter/build/Android.mk b/fwk/spf/modules/simple_splitter/build/Android.mk index 6c0d8854..641502f7 100644 --- a/fwk/spf/modules/simple_splitter/build/Android.mk +++ b/fwk/spf/modules/simple_splitter/build/Android.mk @@ -11,7 +11,7 @@ include $(BUILD_HEADER_LIBRARY) include $(CLEAR_VARS) -LOCAL_MODULE := capi_splitter +LOCAL_MODULE := lib_splitter LOCAL_MODULE_TAGS := optional LOCAL_VENDOR_MODULE := true LOCAL_C_INCLUDES := \ @@ -22,6 +22,7 @@ LOCAL_C_INCLUDES := \ LOCAL_SRC_FILES := \ capi/src/capi_splitter.c \ capi/src/capi_splitter_island.c \ + capi/src/capi_splitter_md_island.c \ capi/src/capi_splitter_utils.c LOCAL_CFLAGS += -flto -O3 -Wall -ffixed-x18 -std=c17 -g @@ -40,5 +41,16 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_HEADER_LIBRARIES := libposal_headers libspf_api libspf_interfaces_headers libspf_utils_headers libapm_headers libamdb_headers LOCAL_STATIC_LIBRARIES := libposal libspf_interfaces libapm libamdb -include $(BUILD_STATIC_LIBRARY) +LOCAL_SPF_MODULE_KCONFIG := CONFIG_SPLITTER +LOCAL_SPF_MODULE_NAME := $(LOCAL_MODULE) +LOCAL_SPF_MODULE_MAJOR_VER := 1 +LOCAL_SPF_MODULE_MINOR_VER := 0 +LOCAL_SPF_MODULE_AMDB_ITYPE := "capi" +LOCAL_SPF_MODULE_AMDB_MTYPE := "generic" +LOCAL_SPF_MODULE_AMDB_MID := "0x07001011" +LOCAL_SPF_MODULE_AMDB_TAG := "capi_splitter" +LOCAL_SPF_MODULE_AMDB_MOD_NAME := "MODULE_ID_SPLITTER" +LOCAL_SPF_MODULE_H2XML_HEADERS := "$(LOCAL_PATH)/api/splitter_api.h" + +include $(BUILD_ARE_MODULES) diff --git a/fwk/spf/modules/sync_module/build/Android.mk b/fwk/spf/modules/sync_module/build/Android.mk index 16c94970..fbcad2f2 100644 --- a/fwk/spf/modules/sync_module/build/Android.mk +++ b/fwk/spf/modules/sync_module/build/Android.mk @@ -11,7 +11,7 @@ include $(BUILD_HEADER_LIBRARY) include $(CLEAR_VARS) -LOCAL_MODULE := capi_sync +LOCAL_MODULE := lib_sync LOCAL_MODULE_TAGS := optional LOCAL_VENDOR_MODULE := true LOCAL_C_INCLUDES := \ @@ -45,5 +45,16 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_HEADER_LIBRARIES := libposal_headers libspf_api libspf_interfaces_headers libspf_utils_headers libapm_headers libamdb_headers LOCAL_STATIC_LIBRARIES := libposal libspf_interfaces libapm libamdb -include $(BUILD_STATIC_LIBRARY) +LOCAL_SPF_MODULE_KCONFIG := CONFIG_SYNC +LOCAL_SPF_MODULE_NAME := $(LOCAL_MODULE) +LOCAL_SPF_MODULE_MAJOR_VER := 1 +LOCAL_SPF_MODULE_MINOR_VER := 0 +LOCAL_SPF_MODULE_AMDB_ITYPE := "capi" +LOCAL_SPF_MODULE_AMDB_MTYPE := "generic" +LOCAL_SPF_MODULE_AMDB_MID := "0x07001038" +LOCAL_SPF_MODULE_AMDB_TAG := "capi_sync" +LOCAL_SPF_MODULE_AMDB_MOD_NAME := "MODULE_ID_SYNC" +LOCAL_SPF_MODULE_H2XML_HEADERS := "$(LOCAL_PATH)/api/sync_api.h" + +include $(BUILD_ARE_MODULES) From 9043b5ceca12e14c0363a12bd3858a25b8f6dcce Mon Sep 17 00:00:00 2001 From: Lijo Cyriac George Date: Sat, 6 Jun 2026 12:57:01 +0530 Subject: [PATCH 22/24] fwk: spf: utils: remove static linking of ARE modules Remove sync, priority_sync and splitter from libspf_utils static dependencies as they are now registered as ARE modules. Signed-off-by: ffrancis --- fwk/spf/utils/build/Android.mk | 3 --- 1 file changed, 3 deletions(-) diff --git a/fwk/spf/utils/build/Android.mk b/fwk/spf/utils/build/Android.mk index c23866cd..eb6d0511 100644 --- a/fwk/spf/utils/build/Android.mk +++ b/fwk/spf/utils/build/Android.mk @@ -73,9 +73,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_HEADER_LIBRARIES := libposal_headers libspf_api libspf_interfaces_headers libapm_headers libamdb_headers libirm_headers libdls_headers LOCAL_STATIC_LIBRARIES := \ - capi_priority_sync \ - capi_splitter \ - capi_sync \ libamdb \ libapm \ libdls \ From 8b192272590a80591325f8ff36c33376835f1614 Mon Sep 17 00:00:00 2001 From: Lijo Cyriac George Date: Sat, 6 Jun 2026 13:30:54 +0530 Subject: [PATCH 23/24] alsa: source: use pcm_wait in DMA thread and pcm_read in process_source Replace blocking pcm_read in DMA thread with pcm_wait to only poll for hardware buffer availability. Move pcm_read to process_source where it reads directly into the output buffer, eliminating the intermediate read_buffer copy for the interleaved case. Signed-off-by: ffrancis --- .../alsa_device/capi/src/capi_alsa_device.c | 214 ++++++++++-------- .../alsa_device/capi/src/capi_alsa_device_i.h | 12 +- 2 files changed, 127 insertions(+), 99 deletions(-) diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c index a8f2c699..42b135e1 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c +++ b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c @@ -28,54 +28,69 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp capi_err_t capi_alsa_device_process_sink(capi_t *_pif, capi_stream_data_t *input[], capi_stream_data_t *output[]); -/* Thread function that performs blocking reads into read_buffer and signals framework */ +/* Thread function that polls for hardware buffer availability and signals framework */ static void capi_alsa_device_dma_wait_thread(void *arg) { capi_alsa_device_t *me_ptr = (capi_alsa_device_t *)arg; AR_MSG(DBG_HIGH_PRIO, "CAPI_ALSA_DEVICE: DMA wait thread started for instance 0x%lx", me_ptr->iid); + bool_t needs_recovery = FALSE; + while (!me_ptr->exit_thread) { - /* - * TODO: Ideally this thread should use alsa_device_driver_wait() to only poll - * for hardware buffer availability and signal the framework, while the actual - * alsa_device_driver_read() is done in process_source. However, the - * alsa_device_driver_wait() based approach was found to be unreliable in - * practice. Until this is investigated and resolved, a blocking - * alsa_device_driver_read() is used here as a safe and functional workaround — - * it blocks until the data is available. - */ - ar_result_t result = alsa_device_driver_read(&me_ptr->alsa_device_driver, - me_ptr->read_buffer, - me_ptr->read_buffer_size); + /* Wait until process_source completes before calling pcm_wait or + * pcm_prepare. Ensures no concurrent PCM operations on the same handle. */ + posal_nmutex_lock(me_ptr->data_ready_lock); + while (me_ptr->data_ready && !me_ptr->exit_thread) + { + posal_condvar_wait(me_ptr->process_done_cond, me_ptr->data_ready_lock); + } + posal_nmutex_unlock(me_ptr->data_ready_lock); + + if (me_ptr->exit_thread) break; + + /* Recover from XRUN here — after confirming process_source is not + * concurrently executing pcm_read on the same PCM handle. */ + if (needs_recovery) + { + needs_recovery = FALSE; + if (AR_EOK == alsa_device_driver_prepare(&me_ptr->alsa_device_driver)) + { + /* Source (capture): pcm_start required to restart DMA. + * Sink (playback): stream restarts automatically on next pcm_write. */ + if (ALSA_DEVICE_SOURCE == me_ptr->direction) + { + alsa_device_driver_start(&me_ptr->alsa_device_driver); + } + } + } + + ar_result_t result = alsa_device_driver_wait(&me_ptr->alsa_device_driver); if (result != AR_EOK) { - AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Read failed in DMA thread, result=%d", result); + if (!me_ptr->exit_thread) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: pcm_wait failed in DMA thread, result=%d", result); + /* Set recovery flag for non-timeout errors (XRUN etc.). + * Timeout means no data yet — just retry pcm_wait. */ + if (AR_ETIMEOUT != result) + { + needs_recovery = TRUE; + } + } continue; } - AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: DMA thread read %d bytes", me_ptr->read_buffer_size); - - /* Mark data as ready and signal the framework */ - posal_nmutex_lock(me_ptr->buf_lock); + /* Mark hardware data available and signal the framework */ + posal_nmutex_lock(me_ptr->data_ready_lock); me_ptr->data_ready = TRUE; - posal_nmutex_unlock(me_ptr->buf_lock); + posal_nmutex_unlock(me_ptr->data_ready_lock); if (me_ptr->signal_ptr && me_ptr->enable_stm) { - AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: signaling the container"); posal_signal_send(me_ptr->signal_ptr); } - - /* Block until process_source consumes read_buffer before issuing next pcm_read. - * This prevents overwriting read_buffer while process_source is still copying it. */ - posal_nmutex_lock(me_ptr->buf_lock); - while (me_ptr->data_ready && !me_ptr->exit_thread) - { - posal_condvar_wait(me_ptr->buf_consumed_cond, me_ptr->buf_lock); - } - posal_nmutex_unlock(me_ptr->buf_lock); } me_ptr->is_thread_running = FALSE; @@ -176,16 +191,16 @@ static capi_err_t capi_alsa_device_common_init(capi_t *_pif, capi_proplist_t *in return capi_result; } - if (AR_EOK != posal_nmutex_create(&me_ptr->buf_lock, (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id)) + if (AR_EOK != posal_nmutex_create(&me_ptr->data_ready_lock, (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id)) { - AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to create buf_lock"); + AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to create data_ready_lock"); return CAPI_EFAILED; } - if (AR_EOK != posal_condvar_create(&me_ptr->buf_consumed_cond, (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id)) + if (AR_EOK != posal_condvar_create(&me_ptr->process_done_cond, (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id)) { - posal_nmutex_destroy(&me_ptr->buf_lock); - AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to create buf_consumed_cond"); + posal_nmutex_destroy(&me_ptr->data_ready_lock); + AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to create process_done_cond"); return CAPI_EFAILED; } @@ -462,8 +477,12 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me if (AR_EOK != thread_result) { AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to create DMA wait thread"); - posal_memory_free(me_ptr->read_buffer); - me_ptr->read_buffer = NULL; + if (me_ptr->read_buffer) + { + posal_memory_free(me_ptr->read_buffer); + me_ptr->read_buffer = NULL; + } + alsa_device_driver_stop(&me_ptr->alsa_device_driver); alsa_device_driver_close(&me_ptr->alsa_device_driver); return CAPI_EFAILED; } @@ -599,7 +618,6 @@ static capi_err_t capi_alsa_device_process_get_properties(capi_alsa_device_t *me { AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Not valid media format size %d", payload_ptr->actual_data_len); return CAPI_ENEEDMORE; - break; } capi_media_fmt_v2_t *media_fmt_ptr = (capi_media_fmt_v2_t *)(payload_ptr->data_ptr); @@ -799,14 +817,6 @@ capi_err_t capi_alsa_device_end(capi_t *_pif) me_ptr->exit_thread = TRUE; } - /* Wake the DMA thread if it is blocked on buf_consumed_cond waiting for - * process_source to consume. pcm_stop() handles the case where it is - * blocked inside pcm_read(). Both are needed to cover either scenario. */ - posal_nmutex_lock(me_ptr->buf_lock); - me_ptr->data_ready = FALSE; - posal_nmutex_unlock(me_ptr->buf_lock); - posal_condvar_signal(me_ptr->buf_consumed_cond); - ar_result = alsa_device_driver_stop(&me_ptr->alsa_device_driver); if (ar_result != AR_EOK) { @@ -816,8 +826,14 @@ capi_err_t capi_alsa_device_end(capi_t *_pif) capi_result = CAPI_EFAILED; } - /* pcm_stop() above unblocks the DMA thread from pcm_read(). Join it now so - * read_buffer is not freed while the thread is still writing into it. */ + /* Unblock DMA thread if it is waiting on process_done_cond for process_source. + * pcm_stop() handles the case where it is blocked inside pcm_wait(). */ + posal_nmutex_lock(me_ptr->data_ready_lock); + me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->data_ready_lock); + posal_condvar_signal(me_ptr->process_done_cond); + + /* Join the DMA thread now that both blocking paths are unblocked. */ if (me_ptr->dma_wait_thread != NULL) { ar_result_t thread_result = AR_EOK; @@ -844,8 +860,8 @@ capi_err_t capi_alsa_device_end(capi_t *_pif) AR_MSG(DBG_HIGH_PRIO, "CAPI_ALSA_DEVICE: read_buffer freed"); } - posal_condvar_destroy(&me_ptr->buf_consumed_cond); - posal_nmutex_destroy(&me_ptr->buf_lock); + posal_condvar_destroy(&me_ptr->process_done_cond); + posal_nmutex_destroy(&me_ptr->data_ready_lock); me_ptr->state = ALSA_DEVICE_INTERFACE_STOP; me_ptr->vtbl.vtbl_ptr = NULL; @@ -1406,8 +1422,8 @@ bool_t capi_alsa_device_check_data_sufficiency(capi_stream_data_t *input, /*--------------------------------------------------------------------- Function name: capi_alsa_device_process_source - DESCRIPTION: Processes capture (source) - copies data from read_buffer - populated by DMA thread to output buffers + DESCRIPTION: Processes capture (source) - reads PCM data directly + into output buffers via pcm_read -----------------------------------------------------------------------*/ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *input[], capi_stream_data_t *output[]) { @@ -1431,41 +1447,27 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp return CAPI_EBADPARAM; } - num_channels = me_ptr->num_channels; - bytes_per_sample = me_ptr->bytes_per_channel; - word_size = bytes_per_sample << 3; - total_bytes = me_ptr->read_buffer_size; - - posal_nmutex_lock(me_ptr->buf_lock); - + posal_nmutex_lock(me_ptr->data_ready_lock); if (!me_ptr->data_ready) { - posal_nmutex_unlock(me_ptr->buf_lock); - - // Data not ready - this is an underrun condition - AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Underrun - no data available in read_buffer"); - - // Fill output with zeros - if (CAPI_DEINTERLEAVED_UNPACKED == me_ptr->gen_cntr_alsa_device_media_fmt.format.data_interleaving) - { - uint32_t bytes_per_ch = total_bytes / num_channels; - for (uint32_t ch = 0; ch < num_channels; ch++) - { - uint32_t fill_bytes = min(bytes_per_ch, output[port]->buf_ptr[ch].max_data_len); - memset(output[port]->buf_ptr[ch].data_ptr, 0, fill_bytes); - output[port]->buf_ptr[ch].actual_data_len = fill_bytes; - } - } - else // CAPI_INTERLEAVED - { - uint32_t fill_bytes = min(total_bytes, output[port]->buf_ptr[0].max_data_len); - memset(output[port]->buf_ptr[0].data_ptr, 0, fill_bytes); - output[port]->buf_ptr[0].actual_data_len = fill_bytes; - } + posal_nmutex_unlock(me_ptr->data_ready_lock); + /* Spurious trigger: data_ready is FALSE, meaning the DMA thread has not yet + * returned from pcm_wait. It is therefore not blocked on process_done_cond, + * so no signal is needed here. */ + AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: Spurious trigger, no data ready"); return CAPI_EOK; } + /* data_ready is TRUE: DMA thread has completed pcm_wait and is now blocked on + * process_done_cond waiting for this function to complete pcm_read. The lock + * is released before pcm_read since the DMA thread cannot proceed until + * process_done_cond is signaled at the end of this function. */ + posal_nmutex_unlock(me_ptr->data_ready_lock); + + num_channels = me_ptr->num_channels; + bytes_per_sample = me_ptr->bytes_per_channel; + word_size = bytes_per_sample << 3; + total_bytes = me_ptr->read_buffer_size; - // Data is ready - copy from read_buffer under lock to prevent DMA thread overwrite if (CAPI_DEINTERLEAVED_UNPACKED == me_ptr->gen_cntr_alsa_device_media_fmt.format.data_interleaving) { uint32_t bytes_per_ch = total_bytes / num_channels; @@ -1477,13 +1479,27 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Output buffer too small ch %d. Required: %d, Available: %d", ch, bytes_per_ch, output[port]->buf_ptr[ch].max_data_len); + posal_nmutex_lock(me_ptr->data_ready_lock); me_ptr->data_ready = FALSE; - posal_nmutex_unlock(me_ptr->buf_lock); - posal_condvar_signal(me_ptr->buf_consumed_cond); + posal_nmutex_unlock(me_ptr->data_ready_lock); + posal_condvar_signal(me_ptr->process_done_cond); return CAPI_ENOMEMORY; } } + ar_result_t result = alsa_device_driver_read(&me_ptr->alsa_device_driver, + me_ptr->read_buffer, + total_bytes); + if (result != AR_EOK) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: pcm_read failed in process_source, result=%d", result); + posal_nmutex_lock(me_ptr->data_ready_lock); + me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->data_ready_lock); + posal_condvar_signal(me_ptr->process_done_cond); + return CAPI_EFAILED; + } + capi_buf_t intlv_buf; intlv_buf.data_ptr = me_ptr->read_buffer; intlv_buf.actual_data_len = total_bytes; @@ -1495,9 +1511,10 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp word_size)) { AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Failed to deinterleave data"); + posal_nmutex_lock(me_ptr->data_ready_lock); me_ptr->data_ready = FALSE; - posal_nmutex_unlock(me_ptr->buf_lock); - posal_condvar_signal(me_ptr->buf_consumed_cond); + posal_nmutex_unlock(me_ptr->data_ready_lock); + posal_condvar_signal(me_ptr->process_done_cond); return CAPI_EFAILED; } @@ -1514,22 +1531,33 @@ capi_err_t capi_alsa_device_process_source(capi_t *_pif, capi_stream_data_t *inp "CAPI_ALSA_DEVICE: Output buffer too small. Required: %d, Available: %d", total_bytes, output[port]->buf_ptr[0].max_data_len); + posal_nmutex_lock(me_ptr->data_ready_lock); me_ptr->data_ready = FALSE; - posal_nmutex_unlock(me_ptr->buf_lock); - posal_condvar_signal(me_ptr->buf_consumed_cond); + posal_nmutex_unlock(me_ptr->data_ready_lock); + posal_condvar_signal(me_ptr->process_done_cond); return CAPI_ENOMEMORY; } - memscpy(output[port]->buf_ptr[0].data_ptr, - output[port]->buf_ptr[0].max_data_len, - me_ptr->read_buffer, - total_bytes); + ar_result_t result = alsa_device_driver_read(&me_ptr->alsa_device_driver, + output[port]->buf_ptr[0].data_ptr, + total_bytes); + if (result != AR_EOK) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: pcm_read failed in process_source, result=%d", result); + posal_nmutex_lock(me_ptr->data_ready_lock); + me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->data_ready_lock); + posal_condvar_signal(me_ptr->process_done_cond); + return CAPI_EFAILED; + } + output[port]->buf_ptr[0].actual_data_len = total_bytes; } + posal_nmutex_lock(me_ptr->data_ready_lock); me_ptr->data_ready = FALSE; - posal_nmutex_unlock(me_ptr->buf_lock); - posal_condvar_signal(me_ptr->buf_consumed_cond); + posal_nmutex_unlock(me_ptr->data_ready_lock); + posal_condvar_signal(me_ptr->process_done_cond); AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: Process source successful, bytes: %d", total_bytes); diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h index 9875f87d..0f3df78a 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h +++ b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h @@ -109,19 +109,19 @@ typedef struct capi_alsa_device void * signal_ptr; // Signal ptr for STM uint32_t enable_stm; - /* Thread management for source direction */ + /* Thread management for DMA wait thread (used by both source and sink) */ posal_thread_t dma_wait_thread; // Thread handle for DMA waiting bool_t is_thread_running; // Thread running state bool_t exit_thread; // Thread exit flag - /* Buffer to store the data read form ALSA driver in wait thread */ - int8_t *read_buffer; // Buffer to hold one period of captured data + /* Intermediate buffer for deinterleaved capture path (interleaved staging) */ + int8_t *read_buffer; // Intermediate buffer for deinterleaved conversion uint32_t read_buffer_size; // Size of read buffer in bytes - bool_t data_ready; // Flag: data available in read buffer + bool_t data_ready; // Flag: pcm_wait has returned, period is available /* Synchronization between DMA thread and process_source */ - posal_nmutex_t buf_lock; // Protects read_buffer and data_ready - posal_condvar_t buf_consumed_cond; // Signaled by process_source when data_ready cleared + posal_nmutex_t data_ready_lock; // Protects data_ready + posal_condvar_t process_done_cond; // Signaled by process_source after pcm_read completes } capi_alsa_device_t; /*------------------------------------------------------------------------ From 84166d7bcd2d76be889dbbbb21e1544b2d4fa6d3 Mon Sep 17 00:00:00 2001 From: Lijo Cyriac George Date: Sat, 6 Jun 2026 16:47:07 +0530 Subject: [PATCH 24/24] alsa: sink: enable signal-triggered playback for sink direction Declare FWK_EXTN_STM for both source and sink, open PCM and launch DMA wait thread for sink in STM_CTRL handler. Add pcm_prepare recovery on pcm_wait failure and fix DMA thread serialization with process_sink. Signed-off-by: ffrancis --- .../alsa_device/capi/src/capi_alsa_device.c | 79 +++++++++---------- .../alsa_device/capi/src/capi_alsa_device_i.h | 10 +-- 2 files changed, 40 insertions(+), 49 deletions(-) diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c index 42b135e1..00e56450 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c +++ b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device.c @@ -39,7 +39,7 @@ static void capi_alsa_device_dma_wait_thread(void *arg) while (!me_ptr->exit_thread) { - /* Wait until process_source completes before calling pcm_wait or + /* Wait until process_sink/source completes before calling pcm_wait or * pcm_prepare. Ensures no concurrent PCM operations on the same handle. */ posal_nmutex_lock(me_ptr->data_ready_lock); while (me_ptr->data_ready && !me_ptr->exit_thread) @@ -388,8 +388,7 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me AR_MSG(DBG_HIGH_PRIO, "FWK_EXTN_PROPERTY_ID_STM_CTRL enable_stm %d", me_ptr->enable_stm); - if (ALSA_DEVICE_SOURCE == me_ptr->direction && - me_ptr->enable_stm && me_ptr->state != ALSA_DEVICE_INTERFACE_START) + if (me_ptr->enable_stm && me_ptr->state != ALSA_DEVICE_INTERFACE_START) { if (!me_ptr->ep_mf_received) { @@ -431,17 +430,20 @@ static capi_err_t capi_alsa_device_process_set_properties(capi_alsa_device_t *me return CAPI_EFAILED; } - capi_result = alsa_device_driver_start(&me_ptr->alsa_device_driver); - if (capi_result != AR_EOK) + if (ALSA_DEVICE_SOURCE == me_ptr->direction) { - AR_MSG(DBG_ERROR_PRIO, - "CAPI_ALSA_DEVICE: alsa_device_driver_start failed with error code %d", - capi_result); - alsa_device_driver_close(&me_ptr->alsa_device_driver); - return CAPI_EFAILED; + capi_result = alsa_device_driver_start(&me_ptr->alsa_device_driver); + if (capi_result != AR_EOK) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI_ALSA_DEVICE: alsa_device_driver_start failed with error code %d", + capi_result); + alsa_device_driver_close(&me_ptr->alsa_device_driver); + return CAPI_EFAILED; + } } - if (NULL == me_ptr->read_buffer) + if (ALSA_DEVICE_SOURCE == me_ptr->direction && NULL == me_ptr->read_buffer) { struct pcm_config *config = &me_ptr->alsa_device_driver.config; me_ptr->read_buffer_size = config->period_size * config->channels * me_ptr->bytes_per_channel; @@ -551,15 +553,9 @@ static capi_err_t capi_alsa_device_process_get_properties(capi_alsa_device_t *me mod_prop.init_memory_req = sizeof(capi_alsa_device_t); mod_prop.stack_size = ALSA_DEVICE_STACK_SIZE; - if (ALSA_DEVICE_SOURCE == dir) - { - num_intf_extns = ALSA_DEVICE_NUM_FRAMEWORK_EXTENSIONS_SOURCE; - fwk_extn_ids[0] = FWK_EXTN_STM; - } - else - { - num_intf_extns = ALSA_DEVICE_NUM_FRAMEWORK_EXTENSIONS_SINK; - } + num_intf_extns = ALSA_DEVICE_NUM_FRAMEWORK_EXTENSIONS; + /* Both source and sink declare FWK_EXTN_STM for signal-triggered operation. */ + fwk_extn_ids[0] = FWK_EXTN_STM; mod_prop.num_fwk_extns = num_intf_extns; mod_prop.fwk_extn_ids_arr = fwk_extn_ids; @@ -826,7 +822,7 @@ capi_err_t capi_alsa_device_end(capi_t *_pif) capi_result = CAPI_EFAILED; } - /* Unblock DMA thread if it is waiting on process_done_cond for process_source. + /* Unblock DMA thread if it is waiting on process_done_cond for process_sink/source. * pcm_stop() handles the case where it is blocked inside pcm_wait(). */ posal_nmutex_lock(me_ptr->data_ready_lock); me_ptr->data_ready = FALSE; @@ -1206,30 +1202,18 @@ capi_err_t capi_alsa_device_process_sink(capi_t *_pif, capi_stream_data_t *input if (me_ptr->state != ALSA_DEVICE_INTERFACE_START) { - ar_result = alsa_device_driver_open(&me_ptr->alsa_device_driver, me_ptr->direction); - if (ar_result != AR_EOK) - { - AR_MSG(DBG_ERROR_PRIO, "alsa_device_driver_open failed with error code %d", ar_result); - return CAPI_EFAILED; - } - AR_MSG(DBG_HIGH_PRIO, - "CAPI_ALSA_DEVICE: alsa_device_driver_open success rc: %d", - ar_result); - - ar_result = alsa_device_driver_prepare(&me_ptr->alsa_device_driver); - if (ar_result != AR_EOK) - { - AR_MSG(DBG_ERROR_PRIO, - "CAPI_ALSA_DEVICE: alsa_device_driver_prepare failed with error code %d", - ar_result); - return CAPI_EFAILED; - } - AR_MSG(DBG_HIGH_PRIO, - "CAPI_ALSA_DEVICE: alsa_device_driver_prepare success rc: %d", - ar_result); + AR_MSG(DBG_ERROR_PRIO, "CAPI_ALSA_DEVICE: Sink not started"); + return CAPI_EFAILED; + } - me_ptr->state =ALSA_DEVICE_INTERFACE_START; + posal_nmutex_lock(me_ptr->data_ready_lock); + if (!me_ptr->data_ready) + { + posal_nmutex_unlock(me_ptr->data_ready_lock); + AR_MSG(DBG_LOW_PRIO, "CAPI_ALSA_DEVICE: Spurious trigger, no data ready"); + return CAPI_EOK; } + posal_nmutex_unlock(me_ptr->data_ready_lock); // Size checks is_input_available = capi_alsa_device_check_data_sufficiency(*input, @@ -1313,9 +1297,18 @@ capi_err_t capi_alsa_device_process_sink(capi_t *_pif, capi_stream_data_t *input if (ar_result != AR_EOK) { AR_MSG(DBG_ERROR_PRIO, "alsa_device_driver_write failed with error code %d", ar_result); + posal_nmutex_lock(me_ptr->data_ready_lock); + me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->data_ready_lock); + posal_condvar_signal(me_ptr->process_done_cond); return CAPI_EFAILED; } + posal_nmutex_lock(me_ptr->data_ready_lock); + me_ptr->data_ready = FALSE; + posal_nmutex_unlock(me_ptr->data_ready_lock); + posal_condvar_signal(me_ptr->process_done_cond); + AR_MSG(DBG_LOW_PRIO, "CAPI: alsa_device_driver_write successful, total bytes copied: %d", total_bytes_copied); return capi_result; diff --git a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h index 0f3df78a..2c8af2e2 100644 --- a/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h +++ b/fwk/platform/modules/generic/endpoint/alsa_device/capi/src/capi_alsa_device_i.h @@ -30,10 +30,8 @@ #define min(A,B) ((A) < (B) ? (A) : (B)) #endif - /* Number of CAPI Framework extension needed -Note: this module is not defined as Signal Triggered Module */ -#define ALSA_DEVICE_NUM_FRAMEWORK_EXTENSIONS_SOURCE 1 -#define ALSA_DEVICE_NUM_FRAMEWORK_EXTENSIONS_SINK 0 + /* Number of CAPI Framework extensions needed — both source and sink declare FWK_EXTN_STM */ +#define ALSA_DEVICE_NUM_FRAMEWORK_EXTENSIONS 1 #define ALSA_DEVICE_DMA_THREAD_STACK_SIZE 8192 @@ -119,9 +117,9 @@ typedef struct capi_alsa_device uint32_t read_buffer_size; // Size of read buffer in bytes bool_t data_ready; // Flag: pcm_wait has returned, period is available - /* Synchronization between DMA thread and process_source */ + /* Synchronization between DMA thread and process_sink/source */ posal_nmutex_t data_ready_lock; // Protects data_ready - posal_condvar_t process_done_cond; // Signaled by process_source after pcm_read completes + posal_condvar_t process_done_cond; // Signaled by process_sink/source after pcm_write/read completes } capi_alsa_device_t; /*------------------------------------------------------------------------