Skip to content
Merged
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
5 changes: 5 additions & 0 deletions drivers/dai/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ add_subdirectory_ifdef(CONFIG_DAI_NXP_ESAI nxp/esai)
add_subdirectory_ifdef(CONFIG_DAI_NXP_MICFIL nxp/micfil)
add_subdirectory_ifdef(CONFIG_DAI_NXP_SAI nxp/sai)
# zephyr-keep-sorted-stop

if(CONFIG_DAI_USERSPACE)
zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/dai.h)
zephyr_library_sources(dai_handlers.c)
endif()
6 changes: 6 additions & 0 deletions drivers/dai/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ config DAI_INIT_PRIORITY
help
Device driver initialization priority.

config DAI_USERSPACE
bool "DAI user-space support"
depends on USERSPACE
help
Expose the DAI interface to user-space threads via syscalls.

module = DAI
module-str = dai
source "subsys/logging/Kconfig.template.log_config"
Expand Down
145 changes: 145 additions & 0 deletions drivers/dai/dai_handlers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright (c) 2025 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/drivers/dai.h>
#include <zephyr/internal/syscall_handler.h>

/**
* Maximum size of bespoke objects passed to DAI driver.
* The objects get allocated temporarily on stack for validation,
* so size needs to be limited.
*/
#define DAI_MAX_BESPOKE_CFG_SIZE 256

static inline int z_vrfy_dai_probe(const struct device *dev)
{
K_OOPS(K_SYSCALL_DRIVER_DAI(dev, probe));

return z_impl_dai_probe(dev);
}
#include <zephyr/syscalls/dai_probe_mrsh.c>

static inline int z_vrfy_dai_remove(const struct device *dev)
{
K_OOPS(K_SYSCALL_DRIVER_DAI(dev, remove));

return z_impl_dai_remove(dev);
}
#include <zephyr/syscalls/dai_remove_mrsh.c>

static inline int z_vrfy_dai_config_set(const struct device *dev,
const struct dai_config *cfg,
const void *bespoke_cfg,
size_t size)
{
uint8_t bespoke_cfg_kernel[DAI_MAX_BESPOKE_CFG_SIZE];

if (size > DAI_MAX_BESPOKE_CFG_SIZE) {
return -EINVAL;
}

K_OOPS(K_SYSCALL_DRIVER_DAI(dev, config_set));
Comment thread
kv2019i marked this conversation as resolved.
K_OOPS(k_usermode_from_copy(bespoke_cfg_kernel, bespoke_cfg, size));

return z_impl_dai_config_set(dev, cfg, bespoke_cfg_kernel, size);
}
Comment on lines +45 to +48

Copilot AI Dec 9, 2025

Copy link

Choose a reason for hiding this comment

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

Missing validation for NULL or zero-size bespoke_cfg. If size is 0 and bespoke_cfg is NULL, the k_usermode_from_copy will fail. Consider checking if size is 0 and handling it appropriately, potentially skipping the copy when size == 0. This pattern should also be applied to z_vrfy_dai_config_update.

Suggested change
K_OOPS(k_usermode_from_copy(bespoke_cfg_kernel, bespoke_cfg, size));
return z_impl_dai_config_set(dev, cfg, bespoke_cfg_kernel, size);
}
if (size > 0) {
K_OOPS(k_usermode_from_copy(bespoke_cfg_kernel, bespoke_cfg, size));
return z_impl_dai_config_set(dev, cfg, bespoke_cfg_kernel, size);
} else {
/* No bespoke config to copy, pass NULL */
return z_impl_dai_config_set(dev, cfg, NULL, 0);
}

Copilot uses AI. Check for mistakes.
#include <zephyr/syscalls/dai_config_set_mrsh.c>

static inline int z_vrfy_dai_config_get(const struct device *dev,
struct dai_config *cfg,
enum dai_dir dir)
{
K_OOPS(K_SYSCALL_DRIVER_DAI(dev, config_get));
K_OOPS(K_SYSCALL_MEMORY_WRITE(cfg, sizeof(*cfg)));

return z_impl_dai_config_get(dev, cfg, dir);
}
#include <zephyr/syscalls/dai_config_get_mrsh.c>

static inline int z_vrfy_dai_get_properties_copy(const struct device *dev,
enum dai_dir dir,
int stream_id,
struct dai_properties *dst)
{
K_OOPS(K_SYSCALL_DRIVER_DAI(dev, get_properties_copy));
K_OOPS(K_SYSCALL_MEMORY_WRITE(dst, sizeof(*dst)));

return z_impl_dai_get_properties_copy(dev, dir, stream_id, dst);
}
#include <zephyr/syscalls/dai_get_properties_copy_mrsh.c>

static inline int z_vrfy_dai_trigger(const struct device *dev,
enum dai_dir dir,
enum dai_trigger_cmd cmd)
{
K_OOPS(K_SYSCALL_DRIVER_DAI(dev, trigger));

return z_impl_dai_trigger(dev, dir, cmd);
}
#include <zephyr/syscalls/dai_trigger_mrsh.c>

static inline int z_vrfy_dai_ts_config(const struct device *dev, struct dai_ts_cfg *cfg)
{
struct dai_ts_cfg cfg_kernel;

K_OOPS(K_SYSCALL_DRIVER_DAI(dev, ts_config));
K_OOPS(k_usermode_from_copy(&cfg_kernel, cfg, sizeof(cfg_kernel)));

return z_impl_dai_ts_config(dev, &cfg_kernel);
}
#include <zephyr/syscalls/dai_ts_config_mrsh.c>

static inline int z_vrfy_dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg)
{
struct dai_ts_cfg cfg_kernel;

K_OOPS(K_SYSCALL_DRIVER_DAI(dev, ts_start));
K_OOPS(k_usermode_from_copy(&cfg_kernel, cfg, sizeof(cfg_kernel)));

return z_impl_dai_ts_start(dev, &cfg_kernel);
}
#include <zephyr/syscalls/dai_ts_start_mrsh.c>

static inline int z_vrfy_dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg)
{
struct dai_ts_cfg cfg_kernel;

K_OOPS(K_SYSCALL_DRIVER_DAI(dev, ts_stop));
K_OOPS(k_usermode_from_copy(&cfg_kernel, cfg, sizeof(cfg_kernel)));

return z_impl_dai_ts_stop(dev, &cfg_kernel);
}
#include <zephyr/syscalls/dai_ts_stop_mrsh.c>

static inline int z_vrfy_dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg,
struct dai_ts_data *tsd)
{
struct dai_ts_cfg cfg_kernel;

K_OOPS(K_SYSCALL_DRIVER_DAI(dev, ts_get));
K_OOPS(k_usermode_from_copy(&cfg_kernel, cfg, sizeof(cfg_kernel)));
K_OOPS(K_SYSCALL_MEMORY_WRITE(tsd, sizeof(*tsd)));

return z_impl_dai_ts_get(dev, &cfg_kernel, tsd);
}
#include <zephyr/syscalls/dai_ts_get_mrsh.c>

static inline int z_vrfy_dai_config_update(const struct device *dev,
const void *bespoke_cfg,
size_t size)
{
uint8_t bespoke_cfg_kernel[DAI_MAX_BESPOKE_CFG_SIZE];

if (size > DAI_MAX_BESPOKE_CFG_SIZE) {
return -EINVAL;
}

K_OOPS(K_SYSCALL_DRIVER_DAI(dev, config_update));
K_OOPS(k_usermode_from_copy(bespoke_cfg_kernel, bespoke_cfg, size));

return z_impl_dai_config_update(dev, bespoke_cfg_kernel, size);
}
#include <zephyr/syscalls/dai_config_update_mrsh.c>
15 changes: 15 additions & 0 deletions drivers/dai/intel/ssp/ssp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2548,6 +2548,20 @@ static const struct dai_properties *dai_ssp_get_properties(const struct device *
return prop;
}

static int dai_ssp_get_properties_copy(const struct device *dev,
enum dai_dir dir, int stream_id, struct dai_properties *prop)
{
const struct dai_properties *kernel_prop = dai_ssp_get_properties(dev, dir, stream_id);

if (!prop) {
return -EINVAL;
}

Comment thread
kv2019i marked this conversation as resolved.
memcpy(prop, kernel_prop, sizeof(*kernel_prop));

return 0;
}

static void ssp_acquire_ip(struct dai_intel_ssp *dp)
{
struct dai_intel_ssp_plat_data *ssp = dai_get_plat_data(dp);
Expand Down Expand Up @@ -2730,6 +2744,7 @@ static DEVICE_API(dai, dai_intel_ssp_api_funcs) = {
.config_get = dai_ssp_config_get,
.trigger = dai_ssp_trigger,
.get_properties = dai_ssp_get_properties,
.get_properties_copy = dai_ssp_get_properties_copy,
.config_update = dai_ssp_dma_control_set,
};

Expand Down
102 changes: 82 additions & 20 deletions include/zephyr/drivers/dai.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ __subsystem struct dai_driver_api {
const struct dai_properties *(*get_properties)(const struct device *dev,
enum dai_dir dir,
int stream_id);
int (*get_properties_copy)(const struct device *dev,
enum dai_dir dir,
int stream_id,
struct dai_properties *dst);

int (*trigger)(const struct device *dev, enum dai_dir dir,
enum dai_trigger_cmd cmd);
Expand Down Expand Up @@ -351,7 +355,9 @@ __subsystem struct dai_driver_api {
*
* @retval 0 If successful.
*/
static inline int dai_probe(const struct device *dev)
__syscall int dai_probe(const struct device *dev);

static inline int z_impl_dai_probe(const struct device *dev)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand All @@ -368,7 +374,9 @@ static inline int dai_probe(const struct device *dev)
*
* @retval 0 If successful.
*/
static inline int dai_remove(const struct device *dev)
__syscall int dai_remove(const struct device *dev);

static inline int z_impl_dai_remove(const struct device *dev)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand Down Expand Up @@ -396,10 +404,16 @@ static inline int dai_remove(const struct device *dev)
* @retval -EINVAL Invalid argument.
* @retval -ENOSYS DAI_DIR_BOTH value is not supported.
*/
static inline int dai_config_set(const struct device *dev,
const struct dai_config *cfg,
const void *bespoke_cfg,
size_t size)

__syscall int dai_config_set(const struct device *dev,
const struct dai_config *cfg,
const void *bespoke_cfg,
size_t size);

static inline int z_impl_dai_config_set(const struct device *dev,
const struct dai_config *cfg,
const void *bespoke_cfg,
size_t size)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand All @@ -414,9 +428,13 @@ static inline int dai_config_set(const struct device *dev,
* @param dir Stream direction: RX or TX as defined by DAI_DIR_*
* @return 0 if success, negative if invalid parameters or DAI un-configured
*/
static inline int dai_config_get(const struct device *dev,
struct dai_config *cfg,
enum dai_dir dir)
__syscall int dai_config_get(const struct device *dev,
struct dai_config *cfg,
enum dai_dir dir);

static inline int z_impl_dai_config_get(const struct device *dev,
struct dai_config *cfg,
enum dai_dir dir)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand All @@ -442,6 +460,31 @@ static inline const struct dai_properties *dai_get_properties(const struct devic
return api->get_properties(dev, dir, stream_id);
}

/**
* @brief Fetch properties of a DAI driver
*
* @param dev Pointer to the device structure for the driver instance
* @param dir Stream direction: RX or TX as defined by DAI_DIR_*
* @param stream_id Stream id: some drivers may have stream specific
* properties, this id specifies the stream.
* @param dst address where to write properties to
* @retval Zero on success
Comment thread
kv2019i marked this conversation as resolved.
*/
__syscall int dai_get_properties_copy(const struct device *dev,
enum dai_dir dir,
int stream_id,
struct dai_properties *dst);

static inline int z_impl_dai_get_properties_copy(const struct device *dev,
enum dai_dir dir,
int stream_id,
struct dai_properties *dst)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Comment thread
kv2019i marked this conversation as resolved.
return api->get_properties_copy(dev, dir, stream_id, dst);
}

/**
* @brief Send a trigger command.
*
Expand All @@ -459,9 +502,13 @@ static inline const struct dai_properties *dai_get_properties(const struct devic
* @retval -ENOMEM RX/TX memory block not available.
* @retval -ENOSYS DAI_DIR_BOTH value is not supported.
*/
static inline int dai_trigger(const struct device *dev,
enum dai_dir dir,
enum dai_trigger_cmd cmd)
__syscall int dai_trigger(const struct device *dev,
enum dai_dir dir,
enum dai_trigger_cmd cmd);

static inline int z_impl_dai_trigger(const struct device *dev,
enum dai_dir dir,
enum dai_trigger_cmd cmd)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand All @@ -477,7 +524,9 @@ static inline int dai_trigger(const struct device *dev,
*
* @retval 0 If successful.
*/
static inline int dai_ts_config(const struct device *dev, struct dai_ts_cfg *cfg)
__syscall int dai_ts_config(const struct device *dev, struct dai_ts_cfg *cfg);

static inline int z_impl_dai_ts_config(const struct device *dev, struct dai_ts_cfg *cfg)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand All @@ -497,7 +546,9 @@ static inline int dai_ts_config(const struct device *dev, struct dai_ts_cfg *cfg
*
* @retval 0 If successful.
*/
static inline int dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg)
__syscall int dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg);

static inline int z_impl_dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand All @@ -517,7 +568,9 @@ static inline int dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg)
*
* @retval 0 If successful.
*/
static inline int dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg)
__syscall int dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg);

static inline int z_impl_dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand All @@ -538,8 +591,11 @@ static inline int dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg)
*
* @retval 0 If successful.
*/
static inline int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg,
struct dai_ts_data *tsd)
__syscall int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg,
struct dai_ts_data *tsd);

static inline int z_impl_dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg,
struct dai_ts_data *tsd)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand Down Expand Up @@ -569,9 +625,13 @@ static inline int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg,
* @retval -ENOSYS If the configuration update operation is not implemented.
* @retval <0 Negative errno code if failure.
*/
static inline int dai_config_update(const struct device *dev,
const void *bespoke_cfg,
size_t size)
__syscall int dai_config_update(const struct device *dev,
const void *bespoke_cfg,
size_t size);

static inline int z_impl_dai_config_update(const struct device *dev,
const void *bespoke_cfg,
size_t size)
{
const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;

Expand All @@ -590,4 +650,6 @@ static inline int dai_config_update(const struct device *dev,
}
#endif

#include <zephyr/syscalls/dai.h>

#endif /* ZEPHYR_INCLUDE_DRIVERS_DAI_H_ */