From de7218c6b5fc20a66ff17b41d9da3e750e0d58ad Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 1 Dec 2025 14:31:58 +0200 Subject: [PATCH 1/4] drivers: dai: add ability to use dai.h from user threads Add user-space support to the dai.h interface. No functional impact to builds when CONFIG_USERSPACE is not set. Signed-off-by: Kai Vehmanen --- drivers/dai/CMakeLists.txt | 3 + drivers/dai/dai_handlers.c | 133 +++++++++++++++++++++++++++++++++++ include/zephyr/drivers/dai.h | 73 +++++++++++++------ 3 files changed, 189 insertions(+), 20 deletions(-) create mode 100644 drivers/dai/dai_handlers.c diff --git a/drivers/dai/CMakeLists.txt b/drivers/dai/CMakeLists.txt index 332a792981d1..523fcb6a4885 100644 --- a/drivers/dai/CMakeLists.txt +++ b/drivers/dai/CMakeLists.txt @@ -9,3 +9,6 @@ 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 + +zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/dai.h) +zephyr_library_sources_ifdef(CONFIG_USERSPACE dai_handlers.c) diff --git a/drivers/dai/dai_handlers.c b/drivers/dai/dai_handlers.c new file mode 100644 index 000000000000..debba1635fb2 --- /dev/null +++ b/drivers/dai/dai_handlers.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/** + * 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 + +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 + +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)); + K_OOPS(k_usermode_from_copy(bespoke_cfg_kernel, bespoke_cfg, size)); + + return z_impl_dai_config_set(dev, cfg, bespoke_cfg_kernel, size); +} +#include + +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 + +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 + +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 + +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 + +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 + +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 + +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 diff --git a/include/zephyr/drivers/dai.h b/include/zephyr/drivers/dai.h index 889effea783d..f3e40dd94690 100644 --- a/include/zephyr/drivers/dai.h +++ b/include/zephyr/drivers/dai.h @@ -351,7 +351,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; @@ -368,7 +370,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; @@ -396,10 +400,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; @@ -414,9 +424,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; @@ -459,9 +473,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; @@ -477,7 +495,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; @@ -497,7 +517,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; @@ -517,7 +539,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; @@ -538,8 +562,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; @@ -569,9 +596,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; @@ -590,4 +621,6 @@ static inline int dai_config_update(const struct device *dev, } #endif +#include + #endif /* ZEPHYR_INCLUDE_DRIVERS_DAI_H_ */ From 8241782083e2028e7165153f01f53354c81a7408 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 1 Dec 2025 16:25:24 +0200 Subject: [PATCH 2/4] drivers: dai: add get_properties_copy() method Add a variant of get_properties() method that writes the properties to a caller provided pointer. Unlike the old variant, this copy variant can be exported to user-space in a safe way. Signed-off-by: Kai Vehmanen --- drivers/dai/dai_handlers.c | 12 ++++++++++++ include/zephyr/drivers/dai.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/drivers/dai/dai_handlers.c b/drivers/dai/dai_handlers.c index debba1635fb2..28283e25a66b 100644 --- a/drivers/dai/dai_handlers.c +++ b/drivers/dai/dai_handlers.c @@ -59,6 +59,18 @@ static inline int z_vrfy_dai_config_get(const struct device *dev, } #include +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 + static inline int z_vrfy_dai_trigger(const struct device *dev, enum dai_dir dir, enum dai_trigger_cmd cmd) diff --git a/include/zephyr/drivers/dai.h b/include/zephyr/drivers/dai.h index f3e40dd94690..606a2f325668 100644 --- a/include/zephyr/drivers/dai.h +++ b/include/zephyr/drivers/dai.h @@ -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); @@ -456,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 + */ +__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; + + return api->get_properties_copy(dev, dir, stream_id, dst); +} + /** * @brief Send a trigger command. * From 70f5e6fca7a64654163185c1aa1152d6c04212d1 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 2 Dec 2025 14:40:32 +0200 Subject: [PATCH 3/4] drivers: dai: make user-space support build-time selectable The DAI interface is not used from user-space in all configurations where Zephyr user-space is enabled, so it is beneficial to have a build option to contorl whether the DAI syscalls are included or not. Signed-off-by: Kai Vehmanen --- drivers/dai/CMakeLists.txt | 6 ++++-- drivers/dai/Kconfig | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/dai/CMakeLists.txt b/drivers/dai/CMakeLists.txt index 523fcb6a4885..6aedee9d3c7d 100644 --- a/drivers/dai/CMakeLists.txt +++ b/drivers/dai/CMakeLists.txt @@ -10,5 +10,7 @@ add_subdirectory_ifdef(CONFIG_DAI_NXP_MICFIL nxp/micfil) add_subdirectory_ifdef(CONFIG_DAI_NXP_SAI nxp/sai) # zephyr-keep-sorted-stop -zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/dai.h) -zephyr_library_sources_ifdef(CONFIG_USERSPACE dai_handlers.c) +if(CONFIG_DAI_USERSPACE) + zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/dai.h) + zephyr_library_sources(dai_handlers.c) +endif() diff --git a/drivers/dai/Kconfig b/drivers/dai/Kconfig index ea46c593bac9..456c86a2292f 100644 --- a/drivers/dai/Kconfig +++ b/drivers/dai/Kconfig @@ -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" From 3c5607b1fa7c806b7c4ea37d82479ded7ee31926 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 1 Dec 2025 16:26:01 +0200 Subject: [PATCH 4/4] drivers: dai: intel: ssp: add get_properties_copy support Add support for new get_properties_copy() method. This allows to use ssp driver from user-space threads. Signed-off-by: Kai Vehmanen --- drivers/dai/intel/ssp/ssp.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/dai/intel/ssp/ssp.c b/drivers/dai/intel/ssp/ssp.c index d86155b65af0..653f1d481687 100644 --- a/drivers/dai/intel/ssp/ssp.c +++ b/drivers/dai/intel/ssp/ssp.c @@ -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; + } + + 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); @@ -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, };