Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 62 additions & 52 deletions src/audio/kpb.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,14 @@ struct comp_data {
#if CONFIG_KPB_CLI_Q
#include <zephyr/kernel.h>

static struct k_queue *kpb_q;
/*
* Only a single KPB instance is currently supported: the queue, thread and
* dev below are global. Requests are copied by value, so user space never
* passes pointers to the kernel.
*/
K_MSGQ_DEFINE(kpb_cli_msgq, sizeof(struct kpb_client), KPB_MAX_NO_OF_CLIENTS,
__alignof__(struct kpb_client));
static struct comp_dev *kpb_cli_q_dev;
static struct k_thread kpb_thread;
K_KERNEL_STACK_DEFINE(kpb_stack, 4096);
#endif
Expand Down Expand Up @@ -479,37 +486,34 @@ static int kpb_params(struct comp_dev *dev, struct sof_ipc_stream_params *params
#if CONFIG_KPB_CLI_Q
static void kpb_thread_fn(void *p1, void *p2, void *p3)
{
struct k_queue *q = p1;
struct comp_dev *dev = p1;
struct kpb_client cli;

for (;;) {
struct kpb_client *cli = k_queue_get(q, K_FOREVER);

kpb_init_draining(cli->dev, cli);
k_msgq_get(&kpb_cli_msgq, &cli, K_FOREVER);
/* keep the LL thread (kpb_copy()) from preempting draining setup */
k_sched_lock();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't used this k_sched_lock() interface much. Seems to be ok here...

kpb_init_draining(dev, &cli);
Comment thread
serhiy-katsyuba-intel marked this conversation as resolved.
Resolved
k_sched_unlock();
}
}

void kpb_notifier_schedule(struct kpb_client *cli)
int z_impl_kpb_notifier_schedule(uint32_t id, uint32_t drain_req)
{
k_queue_alloc_append(cli->queue, cli);
}
EXPORT_SYMBOL(kpb_notifier_schedule);
struct kpb_client cli = {
.id = id,
.drain_req = drain_req,
};

void z_impl_kpb_notifier_init(struct comp_dev *dev, struct kpb_client *cli)
{
comp_dbg(dev, "adding new client to %p", kpb_q);
k_thread_access_grant(k_current_get(), kpb_q);
cli->queue = kpb_q;
cli->dev = dev;
}
if (id >= KPB_MAX_NO_OF_CLIENTS || drain_req > KPB_MAX_DRAINING_REQ)
return -EINVAL;

#include <zephyr/internal/syscall_handler.h>
void z_vrfy_kpb_notifier_init(struct comp_dev *dev, struct kpb_client *cli)
{
K_OOPS(K_SYSCALL_MEMORY_WRITE(dev, sizeof(*dev)));
K_OOPS(K_SYSCALL_MEMORY_WRITE(cli, sizeof(*cli)));
z_impl_kpb_notifier_init(dev, cli);
if (!kpb_cli_q_dev)
return -ENODEV;

return k_msgq_put(&kpb_cli_msgq, &cli, K_NO_WAIT);
}
#include <zephyr/syscalls/kpb_notifier_init_mrsh.c>
EXPORT_SYMBOL(z_impl_kpb_notifier_schedule);
#endif

/*
Expand Down Expand Up @@ -551,6 +555,13 @@ __cold static struct comp_dev *kpb_new(const struct comp_driver *drv,
return NULL;
}

#if CONFIG_KPB_CLI_Q
if (kpb_cli_q_dev) {
comp_cl_err(&comp_kpb, "only one KPB instance is supported with KPB_CLI_Q");
return NULL;
}
#endif

dev = comp_alloc(drv, sizeof(*dev));
if (!dev)
return NULL;
Expand Down Expand Up @@ -608,18 +619,13 @@ __cold static struct comp_dev *kpb_new(const struct comp_driver *drv,
#endif

#if CONFIG_KPB_CLI_Q
kpb_q = k_object_alloc(K_OBJ_QUEUE);
if (!kpb_q) {
comp_free_device(dev);
return NULL;
}

k_queue_init(kpb_q);
k_thread_create(&kpb_thread, kpb_stack, 4096, kpb_thread_fn, kpb_q, NULL, NULL,
k_msgq_purge(&kpb_cli_msgq);
k_thread_create(&kpb_thread, kpb_stack, 4096, kpb_thread_fn, dev, NULL, NULL,
1, 0, K_FOREVER);

k_thread_cpu_pin(&kpb_thread, cpu_get_id());
k_thread_start(&kpb_thread);
kpb_cli_q_dev = dev;
#endif

return dev;
Expand Down Expand Up @@ -770,8 +776,9 @@ __cold static void kpb_free(struct comp_dev *dev)
comp_info(dev, "entry");

#if CONFIG_KPB_CLI_Q
kpb_cli_q_dev = NULL;
k_thread_abort(&kpb_thread);
k_object_free(kpb_q);
k_msgq_purge(&kpb_cli_msgq);
#elif CONFIG_AMS
/* Unregister KPB as AMS consumer */
int ret;
Expand Down Expand Up @@ -1705,7 +1712,9 @@ static int kpb_register_client(struct comp_data *kpb, struct kpb_client *cli)
static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli)
{
struct comp_data *kpb = comp_get_drvdata(dev);
bool is_sink_ready = (comp_buffer_get_sink_state(kpb->host_sink) == COMP_STATE_ACTIVE);
/* sinks are NULL before bind and after reset/unbind */
bool is_sink_ready = kpb->host_sink && kpb->sel_sink &&
comp_buffer_get_sink_state(kpb->host_sink) == COMP_STATE_ACTIVE;
size_t sample_width = kpb->config.sampling_width;
size_t drain_req = (size_t)cli->drain_req * kpb->config.channels *
(kpb->config.sampling_freq / 1000) *
Expand Down Expand Up @@ -2015,28 +2024,29 @@ static enum task_state kpb_draining_task(void *arg)
} else {
draining_data->next_copy_time = 0;
}
}

if (draining_data->drain_req == 0) {
/* We have finished draining of requested data however
* while we were draining real time stream could provided
* new data which needs to be copy to host.
/* also reached with a zero-length (no history) draining request */
if (draining_data->drain_req == 0) {
/* We have finished draining of requested data however
* while we were draining real time stream could provided
* new data which needs to be copy to host.
*/
comp_cl_info(&comp_kpb, "kpb: update drain_req by %zu",
*rt_stream_update);
kpb_lock(kpb);
draining_data->drain_req += *rt_stream_update;
*rt_stream_update = 0;
if (!draining_data->drain_req && kpb->state == KPB_STATE_DRAINING) {
/* Draining is done. Now switch KPB to copy real time
* stream to client's sink. This state is called
* "draining on demand"
* Note! If KPB state changed during draining due to
* i.e reset request we should not change that state.
*/
comp_cl_info(&comp_kpb, "kpb: update drain_req by %zu",
*rt_stream_update);
kpb_lock(kpb);
draining_data->drain_req += *rt_stream_update;
*rt_stream_update = 0;
if (!draining_data->drain_req && kpb->state == KPB_STATE_DRAINING) {
/* Draining is done. Now switch KPB to copy real time
* stream to client's sink. This state is called
* "draining on demand"
* Note! If KPB state changed during draining due to
* i.e reset request we should not change that state.
*/
kpb_change_state(kpb, KPB_STATE_HOST_COPY);
}
kpb_unlock(kpb);
kpb_change_state(kpb, KPB_STATE_HOST_COPY);
}
kpb_unlock(kpb);
}

out:
Expand Down
2 changes: 1 addition & 1 deletion src/audio/module_adapter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ endif # Cadence
config INTEL_MODULES
bool "Intel modules"
depends on LIBRARY_MANAGER
select KPB_NOTIFIER if COMP_KPB
select KPB_NOTIFIER if COMP_KPB && !KPB_CLI_Q
help
Select for a Intel modules API implementation.
This will cause module adapter component to include IADK module
Expand Down
34 changes: 30 additions & 4 deletions src/audio/module_adapter/library/kpb_service_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@
* do not provide extern "C" blocks; C++ is used as a wrapper.
*/

#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <sof/audio/component.h>
#include <sof/audio/kpb.h>
#include <sof/audio/module_adapter/library/kpb_service.h>
#if !CONFIG_KPB_CLI_Q
#include <sof/lib/notifier.h>
#endif

struct kpb_service_client {
#if !CONFIG_KPB_CLI_Q
struct kpb_event_data event;
#endif
struct kpb_client client;
size_t current_history_depth;
size_t max_history_depth;
Expand All @@ -37,15 +42,37 @@ static bool kpb_service_client_is_valid(const struct kpb_service_client *client)
return false;
}

static void kpb_service_send_event(struct kpb_service_client *client,
enum kpb_event event_id)
#if CONFIG_KPB_CLI_Q
static AdspErrorCode kpb_service_send_event(struct kpb_service_client *client,
enum kpb_event event_id)
{
/* KPB draining does not depend on registration, so only draining is queued */
if (event_id != KPB_EVENT_BEGIN_DRAINING)
return ADSP_NO_ERROR;

switch (kpb_notifier_schedule(client->client.id, client->client.drain_req)) {
case 0:
return ADSP_NO_ERROR;
case -ENODEV:
return ADSP_SERVICE_UNAVAILABLE;
case -ENOMSG:
return ADSP_BUSY_RESOURCE;
default:
return ADSP_INVALID_PARAMETERS;
}
}
#else
static AdspErrorCode 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));
return ADSP_NO_ERROR;
}
#endif

AdspErrorCode kpb_service_register_client(size_t current_history_depth,
size_t max_history_depth, uint32_t output_pin,
Expand Down Expand Up @@ -86,8 +113,7 @@ AdspErrorCode kpb_service_signal_detection(struct kpb_service_client *client,

client->client.drain_req = MIN(client->current_history_depth + phrase_length,
client->max_history_depth);
kpb_service_send_event(client, KPB_EVENT_BEGIN_DRAINING);
return ADSP_NO_ERROR;
return kpb_service_send_event(client, KPB_EVENT_BEGIN_DRAINING);
}

AdspErrorCode kpb_service_unregister_client(struct kpb_service_client *client)
Expand Down
19 changes: 14 additions & 5 deletions src/include/sof/audio/kpb.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,6 @@ enum kpb_client_state {
};

struct kpb_client {
struct comp_dev *dev;
struct k_queue *queue;
uint8_t id; /**< id associated with output sink */
uint32_t drain_req; /**< normalized value of buffered bytes */
enum kpb_client_state state; /**< current state of a client */
Expand Down Expand Up @@ -256,13 +254,24 @@ struct kpb_fmt_dev_list {
};

#if CONFIG_KPB_CLI_Q
void kpb_notifier_schedule(struct kpb_client *cli);
#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION)
#include <sof/compiler_attributes.h>
__syscall void kpb_notifier_init(struct comp_dev *dev, struct kpb_client *cli);
/**
* \brief Request draining of the KPB history buffer via the client queue.
*
* Callable from user space. The request is handled asynchronously by the
* KPB client queue thread.
*
* \param[in] id Client ID, i.e. the KPB output pin.
* \param[in] drain_req Requested draining length in milliseconds.
* \return 0 on success, -EINVAL on invalid arguments, -ENODEV if no KPB
* instance exists, -ENOMSG if the queue is full.
*/
__syscall int kpb_notifier_schedule(uint32_t id, uint32_t drain_req);
#include <zephyr/syscalls/kpb.h>
#else
#define kpb_notifier_init z_impl_kpb_notifier_init
int z_impl_kpb_notifier_schedule(uint32_t id, uint32_t drain_req);
#define kpb_notifier_schedule z_impl_kpb_notifier_schedule
#endif
#endif

Expand Down
3 changes: 3 additions & 0 deletions zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,9 @@ zephyr_syscall_header(include/sof/lib/cpu.h)
zephyr_library_sources_ifdef(CONFIG_USERSPACE syscall/cpu.c)
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/audio/module_adapter/module/generic.h)
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/audio/kpb.h)
if(CONFIG_USERSPACE AND CONFIG_KPB_CLI_Q)
zephyr_library_sources(syscall/kpb.c)
endif()
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/lib/fast-get.h)
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/ipc/ipc_reply.h)
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/schedule/dp_schedule.h)
Expand Down
24 changes: 24 additions & 0 deletions zephyr/syscall/kpb.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright 2026 Intel Corporation.

#include <sof/audio/component.h>
#include <sof/audio/kpb.h>
#include <zephyr/kernel.h>
#include <zephyr/internal/syscall_handler.h>

/**
* \brief Userspace verification wrapper for kpb_notifier_schedule().
*
* Only scalar arguments are passed, their ranges are checked by the
* implementation, so no memory access validation is required.
*
* \param[in] id Client ID, i.e. the KPB output pin.
* \param[in] drain_req Requested draining length in milliseconds.
* \return 0 on success, negative error code otherwise.
*/
static inline int z_vrfy_kpb_notifier_schedule(uint32_t id, uint32_t drain_req)
{
return z_impl_kpb_notifier_schedule(id, drain_req);
}
#include <zephyr/syscalls/kpb_notifier_schedule_mrsh.c>
Loading