diff --git a/src/audio/Kconfig b/src/audio/Kconfig index 4a8a8ef231e5..b7966e7054c0 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -166,6 +166,14 @@ config KPB_CLI_Q help Use a Zephyr queue instead of SOF notifiers +config KPB_NOTIFIER + bool "Enable Notifier for KPB events" + default y + help + Enable KPB client events delivered through the SOF notifier API. + This transport can be enabled independently of the Async Messaging + Service (AMS), including together with AMS. + config KPB_FORCE_COPY_TYPE_NORMAL bool "KPB force copy type normal" default y diff --git a/src/audio/kpb.c b/src/audio/kpb.c index 4eb5e75a9422..bbe292d58e2b 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -51,7 +51,8 @@ #include #include #include -#else +#endif +#if CONFIG_KPB_NOTIFIER #include #endif @@ -120,10 +121,13 @@ struct comp_data { static struct k_queue *kpb_q; static struct k_thread kpb_thread; K_KERNEL_STACK_DEFINE(kpb_stack, 4096); -#elif !CONFIG_AMS +#endif + +#if CONFIG_KPB_NOTIFIER static void kpb_event_handler(void *arg, enum notify_id type, void *event_data); static int kpb_register_client(struct comp_data *kpb, struct kpb_client *cli); #endif + static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli); static enum task_state kpb_draining_task(void *arg); static int kpb_buffer_data(struct comp_dev *dev, @@ -762,10 +766,12 @@ static void kpb_free(struct comp_dev *dev) kpb_ams_kpd_notification); if (ret) comp_err(dev, "AMS unregister error %d", ret); -#else +#endif/* CONFIG_KPB_CLI_Q */ + +#if CONFIG_KPB_NOTIFIER /* Unregister KPB from notifications */ notifier_unregister(dev, NULL, NOTIFIER_ID_KPB_CLIENT_EVT); -#endif/* CONFIG_KPB_CLI_Q */ +#endif/* CONFIG_KPB_NOTIFIER */ /* Reclaim memory occupied by history buffer */ kpb_free_history_buffer(kpb->hd.c_hb); @@ -930,23 +936,33 @@ static int kpb_prepare(struct comp_dev *dev) kpb->clients[i].r_ptr = NULL; } +#if CONFIG_KPB_NOTIFIER + /* Register KPB for notification *on the current core* */ + ret = notifier_register(dev, NULL, NOTIFIER_ID_KPB_CLIENT_EVT, kpb_event_handler); + + if (ret < 0) { + kpb_free_history_buffer(kpb->hd.c_hb); + kpb->hd.c_hb = NULL; + return -ENOMEM; + } +#endif + #if !CONFIG_KPB_CLI_Q #if CONFIG_AMS /* AMS Register KPB for notification */ ret = ams_helper_register_consumer(dev, &kpb->kpd_uuid_id, ams_kpd_msg_uuid, kpb_ams_kpd_notification); -#else - /* Register KPB for notification *on the current core* */ - ret = notifier_register(dev, NULL, NOTIFIER_ID_KPB_CLIENT_EVT, kpb_event_handler); -#endif /* CONFIG_AMS */ -#endif - if (ret < 0) { +#if CONFIG_KPB_NOTIFIER + notifier_unregister(dev, NULL, NOTIFIER_ID_KPB_CLIENT_EVT); +#endif kpb_free_history_buffer(kpb->hd.c_hb); kpb->hd.c_hb = NULL; return -ENOMEM; } +#endif /* CONFIG_AMS */ +#endif #ifndef CONFIG_IPC_MAJOR_4 /* Search for KPB related sinks. @@ -1072,7 +1088,7 @@ static int kpb_reset(struct comp_dev *dev) kpb_reset_history_buffer(kpb->hd.c_hb); } -#if !CONFIG_KPB_CLI_Q && !CONFIG_AMS +#if CONFIG_KPB_NOTIFIER /* Unregister KPB from notifications */ notifier_unregister(dev, NULL, NOTIFIER_ID_KPB_CLIENT_EVT); #endif @@ -1576,7 +1592,6 @@ static int kpb_buffer_data(struct comp_dev *dev, return ret; } -#ifndef CONFIG_AMS /** * \brief Main event dispatcher. * \param[in] arg - KPB component internal data. @@ -1584,6 +1599,7 @@ static int kpb_buffer_data(struct comp_dev *dev, * \param[in] event_data - event specific data. * \return none. */ +#if CONFIG_KPB_NOTIFIER static void kpb_event_handler(void *arg, enum notify_id type, void *event_data) { struct comp_dev *dev = arg; @@ -1599,7 +1615,14 @@ static void kpb_event_handler(void *arg, enum notify_id type, void *event_data) kpb_register_client(kpb, cli); break; case KPB_EVENT_UNREGISTER_CLIENT: - /*TODO*/ + if (!cli || cli->id >= KPB_MAX_NO_OF_CLIENTS || + kpb->clients[cli->id].state == KPB_CLIENT_UNREGISTERED) + break; + + kpb->clients[cli->id].state = KPB_CLIENT_UNREGISTERED; + kpb->clients[cli->id].r_ptr = NULL; + kpb->clients[cli->id].sink = NULL; + kpb->kpb_no_of_clients--; break; case KPB_EVENT_BEGIN_DRAINING: kpb_init_draining(dev, cli); @@ -1656,7 +1679,7 @@ static int kpb_register_client(struct comp_data *kpb, struct kpb_client *cli) return ret; } -#endif /* CONFIG_AMS */ +#endif /* CONFIG_KPB_NOTIFIER */ /** * \brief Prepare history buffer for draining. diff --git a/src/audio/module_adapter/CMakeLists.txt b/src/audio/module_adapter/CMakeLists.txt index ca9ffbd8f787..2405d79c2317 100644 --- a/src/audio/module_adapter/CMakeLists.txt +++ b/src/audio/module_adapter/CMakeLists.txt @@ -75,6 +75,12 @@ endif() iadk/system_agent.cpp library/native_system_service.c ) + if(CONFIG_INTEL_MODULES AND CONFIG_COMP_KPB) + zephyr_library_sources( + library/kpb_service.cpp + library/kpb_service_bridge.c + ) + endif() zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_PROXY library/userspace_proxy.c diff --git a/src/audio/module_adapter/Kconfig b/src/audio/module_adapter/Kconfig index d7325a80dc26..81c98b889554 100644 --- a/src/audio/module_adapter/Kconfig +++ b/src/audio/module_adapter/Kconfig @@ -223,6 +223,7 @@ endif # Cadence config INTEL_MODULES bool "Intel modules" depends on LIBRARY_MANAGER + select KPB_NOTIFIER if COMP_KPB help Select for a Intel modules API implementation. This will cause module adapter component to include IADK module diff --git a/src/audio/module_adapter/library/kpb_service.cpp b/src/audio/module_adapter/library/kpb_service.cpp new file mode 100644 index 000000000000..163218f4ef0e --- /dev/null +++ b/src/audio/module_adapter/library/kpb_service.cpp @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright 2026 Intel Corporation. All rights reserved. + +#include +#include +#include +#include + +namespace intel_adsp +{ + +struct KpbToken { + const ModuleHandle *owner; + void *kpb_client; +}; + +/** + * \class KpbServiceV1 + * \brief SOF implementation of the KPB service v1 interface. + */ +class KpbServiceV1 final : public KpbInterface { +public: + ErrorCode::Type RegisterKpbClient(const ModuleHandle& module_handle, KpbToken& token, + size_t current_history_depth, size_t max_history_depth, + uint32_t output_pin) const override + { + struct kpb_service_client *client; + AdspErrorCode error = kpb_service_register_client( + current_history_depth, + max_history_depth, + output_pin, &client); + + if (error == ADSP_NO_ERROR) { + token.owner = &module_handle; + token.kpb_client = client; + } + + return error; + } + + ErrorCode::Type SignalDetection(KpbToken& token, void* data) const override + { + if (!token.owner || !token.kpb_client || !data) + return ADSP_INVALID_PARAMETERS; + + return kpb_service_signal_detection( + reinterpret_cast(token.kpb_client), + *static_cast(data)); + } + + ErrorCode::Type SignalVoiceDetection(KpbToken&, void*) const override + { + return ADSP_NO_ERROR; + } + + ErrorCode::Type SignalSilenceDetection(KpbToken&, void*) const override + { + return ADSP_NO_ERROR; + } + + ErrorCode::Type UnregisterKpbClient(KpbToken& token) const override + { + if (!token.kpb_client) { + token.owner = NULL; + return ADSP_NO_ERROR; + } + + AdspErrorCode error = kpb_service_unregister_client( + reinterpret_cast(token.kpb_client)); + + if (error == ADSP_NO_ERROR) { + token.owner = NULL; + token.kpb_client = NULL; + } + + return error; + } + + ErrorCode::Type RegisterWovProducer(const ModuleHandle&) const override + { + return ADSP_NO_ERROR; + } + + ErrorCode::Type RegisterKpbProducer(const ModuleHandle&) const override + { + return ADSP_NO_ERROR; + } + + ErrorCode::Type RequestWovConsumer(const ModuleHandle&) const override + { + return ADSP_NO_ERROR; + } +}; + +/** + * \class KpbServiceV2 + * \brief KPB service v2 implemented as the compatible subset of v1. + */ +class KpbServiceV2 final : public KpbInterfaceV2 { +public: + ErrorCode::Type RegisterKpbClient(const ModuleHandle& module_handle, KpbToken& token, + size_t current_history_depth, size_t max_history_depth, + uint32_t output_pin) const override + { + return v1_.RegisterKpbClient(module_handle, token, current_history_depth, + max_history_depth, output_pin); + } + + ErrorCode::Type SignalDetection(KpbToken& token, void* data) const override + { + return v1_.SignalDetection(token, data); + } + + ErrorCode::Type UnregisterKpbClient(KpbToken& token) const override + { + return v1_.UnregisterKpbClient(token); + } + + ErrorCode::Type RegisterKpbProducer(const ModuleHandle& module_handle) const override + { + return v1_.RegisterKpbProducer(module_handle); + } + +private: + KpbServiceV1 v1_; +}; + +static APP_TASK_DATA KpbServiceV1 kpb_service_v1; +static APP_TASK_DATA KpbServiceV2 kpb_service_v2; + +} /* namespace intel_adsp */ + +extern "C" struct system_service_iface *native_kpb_service_get_interface_v1(void) +{ + return reinterpret_cast( + &intel_adsp::kpb_service_v1); +} + +extern "C" struct system_service_iface *native_kpb_service_get_interface_v2(void) +{ + return reinterpret_cast( + &intel_adsp::kpb_service_v2); +} diff --git a/src/audio/module_adapter/library/kpb_service_bridge.c b/src/audio/module_adapter/library/kpb_service_bridge.c new file mode 100644 index 000000000000..7d415810ec74 --- /dev/null +++ b/src/audio/module_adapter/library/kpb_service_bridge.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright 2026 Intel Corporation. All rights reserved. + +/* + * It is better to keep most of the implementation in C because SOF C headers + * do not provide extern "C" blocks; C++ is used as a wrapper. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct kpb_service_client { + struct kpb_event_data event; + struct kpb_client client; + size_t current_history_depth; + size_t max_history_depth; + bool in_use; +}; + +static APP_TASK_DATA struct kpb_service_client clients[KPB_MAX_NO_OF_CLIENTS]; + +static bool kpb_service_client_is_valid(const struct kpb_service_client *client) +{ + int i; + + for (i = 0; i < KPB_MAX_NO_OF_CLIENTS; i++) { + if (client == &clients[i]) + return clients[i].in_use; + } + + return false; +} + +static void kpb_service_send_event(struct kpb_service_client *client, + enum kpb_event event_id) +{ + client->event.event_id = event_id; + client->event.client_data = &client->client; + + notifier_event(client, NOTIFIER_ID_KPB_CLIENT_EVT, NOTIFIER_TARGET_CORE_ALL_MASK, + &client->event, sizeof(client->event)); +} + +AdspErrorCode kpb_service_register_client(size_t current_history_depth, + size_t max_history_depth, uint32_t output_pin, + struct kpb_service_client **client) +{ + struct kpb_service_client *new_client; + + if (!client || output_pin >= KPB_MAX_NO_OF_CLIENTS) + return ADSP_INVALID_PARAMETERS; + + max_history_depth = MIN(max_history_depth, (size_t)KPB_MAX_DRAINING_REQ); + current_history_depth = MIN(current_history_depth, max_history_depth); + + *client = NULL; + new_client = &clients[output_pin]; + if (new_client->in_use) + return ADSP_BUSY_RESOURCE; + + new_client->in_use = true; + new_client->current_history_depth = current_history_depth; + new_client->max_history_depth = max_history_depth; + new_client->client.id = output_pin; + new_client->client.drain_req = current_history_depth; + new_client->client.state = KPB_CLIENT_UNREGISTERED; + new_client->client.r_ptr = NULL; + new_client->client.sink = NULL; + kpb_service_send_event(new_client, KPB_EVENT_REGISTER_CLIENT); + + *client = new_client; + return ADSP_NO_ERROR; +} + +AdspErrorCode kpb_service_signal_detection(struct kpb_service_client *client, + uint32_t phrase_length) +{ + size_t history_depth; + + if (!kpb_service_client_is_valid(client)) + return ADSP_INVALID_PARAMETERS; + + history_depth = client->current_history_depth + phrase_length; + if (history_depth < client->current_history_depth || + history_depth > client->max_history_depth) + history_depth = client->max_history_depth; + + client->client.drain_req = history_depth; + kpb_service_send_event(client, KPB_EVENT_BEGIN_DRAINING); + return ADSP_NO_ERROR; +} + +AdspErrorCode kpb_service_unregister_client(struct kpb_service_client *client) +{ + if (!kpb_service_client_is_valid(client)) + return ADSP_INVALID_PARAMETERS; + + kpb_service_send_event(client, KPB_EVENT_UNREGISTER_CLIENT); + client->in_use = false; + return ADSP_NO_ERROR; +} diff --git a/src/audio/module_adapter/library/native_system_service.c b/src/audio/module_adapter/library/native_system_service.c index 24091c1c3c91..68c71f3806b8 100644 --- a/src/audio/module_adapter/library/native_system_service.c +++ b/src/audio/module_adapter/library/native_system_service.c @@ -191,6 +191,14 @@ AdspErrorCode native_system_service_get_interface(enum interface_id id, (void)id; *iface = NULL; + +#if CONFIG_COMP_KPB + if (id == INTERFACE_ID_KPB_SERVICE) { + *iface = native_kpb_service_get_interface_v1(); + return ADSP_NO_ERROR; + } +#endif + return ADSP_SERVICE_UNAVAILABLE; } @@ -204,6 +212,22 @@ AdspErrorCode native_system_service_get_interface_versioned(enum interface_id id (void)id; (void)version; *iface = NULL; + +#if CONFIG_COMP_KPB + if (id == INTERFACE_ID_KPB_SERVICE) { + switch (version) { + case INTERFACE_VERSION_KPB_SERVICE_V1: + *iface = native_kpb_service_get_interface_v1(); + return ADSP_NO_ERROR; + case INTERFACE_VERSION_KPB_SERVICE_V2: + *iface = native_kpb_service_get_interface_v2(); + return ADSP_NO_ERROR; + default: + return ADSP_SERVICE_VERSION_UNAVAILABLE; + } + } +#endif + return ADSP_SERVICE_UNAVAILABLE; } diff --git a/src/include/sof/audio/module_adapter/iadk/kpb_interface.h b/src/include/sof/audio/module_adapter/iadk/kpb_interface.h new file mode 100644 index 000000000000..19decfa27f63 --- /dev/null +++ b/src/include/sof/audio/module_adapter/iadk/kpb_interface.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 Intel Corporation. All rights reserved. + */ + +#ifndef _KPB_INTERFACE_H_ +#define _KPB_INTERFACE_H_ + +#include +#include +#include "system_error.h" + +namespace intel_adsp +{ +class ModuleHandle; + +/** + * \struct KpbToken + * \brief Opaque state for a KPB client registration. + */ +struct KpbToken; + +/** + * \class KpbInterface + * \brief Version 1 KPB service interface for IADK modules. + */ +class KpbInterface { +public: + /** + * \brief Register an IADK module as a KPB client. + * \param module_handle Handle of the module registering the client. + * \param token Storage for the registered client's token. + * \param current_history_depth Initial history depth in milliseconds. + * \param max_hist_depth Maximum history depth in milliseconds. + * \param output_pin Zero-based buffered output pin index. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type RegisterKpbClient(const ModuleHandle& module_handle, + KpbToken& token, + size_t current_history_depth, + size_t max_hist_depth, + uint32_t output_pin) const = 0; + + /** + * \brief Report key-phrase detection for a registered client. + * \param token Token of the registered client. + * \param data Pointer to the detected phrase length in milliseconds. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type SignalDetection(KpbToken& token, void* data) const = 0; + + /** + * \brief Report voice detection for a registered client. + * \param token Token of the registered client. + * \param data Event-specific data (unused by the current native service). + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type SignalVoiceDetection(KpbToken& token, void* data) const = 0; + + /** + * \brief Report silence detection for a registered client. + * \param token Token of the registered client. + * \param data Event-specific data (unused by the current native service). + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type SignalSilenceDetection(KpbToken& token, void* data) const = 0; + + /** + * \brief Unregister a KPB client. + * \param token Token of the client to unregister. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type UnregisterKpbClient(KpbToken& token) const = 0; + + /** + * \brief Register an IADK module as a WOV producer. + * \param module_handle Handle of the module registering as a producer. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type RegisterWovProducer(const ModuleHandle& module_handle) const = 0; + + /** + * \brief Register an IADK module as a KPB producer. + * \param module_handle Handle of the module registering as a producer. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type RegisterKpbProducer(const ModuleHandle& module_handle) const = 0; + + /** + * \brief Request a WOV consumer for an IADK module. + * \param module_handle Handle of the module requesting a consumer. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type RequestWovConsumer(const ModuleHandle& module_handle) const = 0; +}; + +/** + * \class KpbInterfaceV2 + * \brief Version 2 KPB service interface for IADK modules. + */ +class KpbInterfaceV2 { +public: + /** + * \brief Register an IADK module as a KPB client. + * \param module_handle Handle of the module registering the client. + * \param token Storage for the registered client's token. + * \param current_history_depth Initial history depth in milliseconds. + * \param max_hist_depth Maximum history depth in milliseconds. + * \param output_pin Zero-based buffered output pin index. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type RegisterKpbClient(const ModuleHandle& module_handle, + KpbToken& token, + size_t current_history_depth, + size_t max_hist_depth, + uint32_t output_pin) const = 0; + + /** + * \brief Report key-phrase detection for a registered client. + * \param token Token of the registered client. + * \param data Pointer to the detected phrase length in milliseconds. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type SignalDetection(KpbToken& token, void* data) const = 0; + + /** + * \brief Unregister a KPB client. + * \param token Token of the client to unregister. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type UnregisterKpbClient(KpbToken& token) const = 0; + + /** + * \brief Register an IADK module as a KPB producer. + * \param module_handle Handle of the module registering as a producer. + * \return SOF error code, with ADSP_NO_ERROR on success. + */ + virtual ErrorCode::Type RegisterKpbProducer(const ModuleHandle& module_handle) const = 0; +}; + +} /* namespace intel_adsp */ + +#endif /* _KPB_INTERFACE_H_ */ diff --git a/src/include/sof/audio/module_adapter/library/kpb_service.h b/src/include/sof/audio/module_adapter/library/kpb_service.h new file mode 100644 index 000000000000..edca146412ac --- /dev/null +++ b/src/include/sof/audio/module_adapter/library/kpb_service.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2026 Intel Corporation. All rights reserved. + */ + +#ifndef SOF_AUDIO_MODULE_ADAPTER_LIBRARY_KPB_SERVICE_H +#define SOF_AUDIO_MODULE_ADAPTER_LIBRARY_KPB_SERVICE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \struct kpb_service_client + * \brief Opaque handle for a client registered with the SOF KPB component. + */ +struct kpb_service_client; + +/** + * \brief Register an IADK client with the SOF KPB component. + * + * \param current_history_depth Initial history depth in milliseconds. + * \param max_history_depth Maximum history depth in milliseconds. + * \param output_pin Zero-based buffered output pin index. + * \param client Location for the registered client handle. + * \return ADSP error code. + */ +AdspErrorCode kpb_service_register_client(size_t current_history_depth, + size_t max_history_depth, uint32_t output_pin, + struct kpb_service_client **client); + +/** + * \brief Notify KPB about key phrase detection. + * + * \param client Registered client handle. + * \param phrase_length Detected phrase length in milliseconds. + * \return ADSP error code. + */ +AdspErrorCode kpb_service_signal_detection(struct kpb_service_client *client, + uint32_t phrase_length); + +/** + * \brief Unregister an IADK client from the SOF KPB component. + * + * \param client Registered client handle. + * \return ADSP error code. + */ +AdspErrorCode kpb_service_unregister_client(struct kpb_service_client *client); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SOF_AUDIO_MODULE_ADAPTER_LIBRARY_KPB_SERVICE_H */ diff --git a/src/include/sof/audio/module_adapter/library/native_system_service.h b/src/include/sof/audio/module_adapter/library/native_system_service.h index a3d4c991d8fb..dc40bd784f46 100644 --- a/src/include/sof/audio/module_adapter/library/native_system_service.h +++ b/src/include/sof/audio/module_adapter/library/native_system_service.h @@ -42,6 +42,20 @@ AdspErrorCode native_system_service_send_notif_msg(enum notification_target noti AdspErrorCode native_system_service_get_interface(enum interface_id id, struct system_service_iface **iface); +/** + * \brief Get the KPB service v1 interface instance. + * + * \return The KPB service interface. + */ +struct system_service_iface *native_kpb_service_get_interface_v1(void); + +/** + * \brief Get the KPB service v2 interface instance. + * + * \return The KPB service interface. + */ +struct system_service_iface *native_kpb_service_get_interface_v2(void); + /** * \brief Retrieve a versioned system service interface. *