Skip to content

Commit e024fec

Browse files
committed
zephyr: test: userspace: add tests for using Intel HDA DMA from user-space
Test the SOF DMA interface from a user-space thread. Use all the exported functionality using SOF_DMA_DEV_HOST. This test is using the zephyr/soc/intel/intel_adsp/tools/cavstool.py as the host-side tool. This is required as the DMA programming is split between host and DSP side and test sequences need a test endpoint on both sides. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
1 parent 1227d56 commit e024fec

File tree

3 files changed

+271
-0
lines changed

3 files changed

+271
-0
lines changed

zephyr/test/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@ if (CONFIG_SOF_BOOT_TEST)
33
vmh.c
44
)
55
endif()
6+
7+
if (CONFIG_SOF_BOOT_TEST_STANDALONE)
8+
if (CONFIG_DT_HAS_INTEL_ADSP_HDA_HOST_IN_ENABLED AND CONFIG_USERSPACE)
9+
zephyr_library_sources(userspace/test_intel_hda_dma.c)
10+
endif()
11+
endif()

zephyr/test/userspace/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
intel_hda_dma test
2+
------------------
3+
4+
This is a standalone test to exercise the Intel HDA DMA host interface
5+
from a userspace Zephyr thread.
6+
Build with ("ptl" example):
7+
8+
./scripts/xtensa-build-zephyr.py --cmake-args=-DCONFIG_SOF_BOOT_TEST_STANDALONE=y \
9+
-o app/userspace_overlay.conf -o app/winconsole_overlay.conf ptl
10+
11+
Running test:
12+
- Copy resulting firmware (sof-ptl.ri) to device under test.
13+
- Boot and run the test with cavstool.py:
14+
sudo ./cavstool.py sof-ptl.ri
15+
- Test results printed to cavstool.py
16+
17+
References to related assets in Zephyr codebase:
18+
- cavstool.py
19+
- zephyr/soc/intel/intel_adsp/tools/cavstool.py
20+
- HD DMA tests in Zephyr
21+
- zephyr/tests/boards/intel_adsp/hda/
22+
- larger set in kernel space, using DMA interface directly without SOF dependencies
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
/*
3+
* Copyright(c) 2025 Intel Corporation.
4+
*/
5+
6+
/*
7+
* Test case for user-space use of the SOF DMA interface. The tests
8+
* transfer data from DSP to host using the host HD DMA instance.
9+
* The test uses the cavstool.py infrastructure to perform host side
10+
* programming of the HDA DMA, and to verify the transferred data.
11+
*
12+
* This test is based on the Zephyr kernel tests for Intel HD DMA
13+
* driver (zephyr/tests/boards/intel_adsp/hda/) written by Tom
14+
* Burdick. This test performs only subset of flows. Driver testing
15+
* should primarily done with the Zephyr kernel tests and this test
16+
* is solely to test the added syscall layer added in SOF.
17+
*/
18+
19+
#include <sof/boot_test.h>
20+
21+
#include <zephyr/kernel.h>
22+
#include <zephyr/ztest.h>
23+
#include <zephyr/logging/log.h>
24+
#include <sof/lib/dma.h>
25+
26+
LOG_MODULE_DECLARE(sof_boot_test, LOG_LEVEL_DBG);
27+
28+
#define USER_STACKSIZE 2048
29+
#define TEST_BUF_SIZE 256
30+
#define TEST_CHANNEL 0
31+
32+
static struct k_thread user_thread;
33+
static K_THREAD_STACK_DEFINE(user_stack, USER_STACKSIZE);
34+
35+
K_SEM_DEFINE(ipc_sem_wake_user, 0, 1);
36+
K_SEM_DEFINE(ipc_sem_wake_kernel, 0, 1);
37+
38+
static void intel_hda_dma_user(void *p1, void *p2, void *p3)
39+
{
40+
uint32_t addr_align = 0xbaabbaab;
41+
struct sof_dma *dma;
42+
struct dma_config config;
43+
struct dma_block_config dma_block_cfg;
44+
struct dma_status stat;
45+
uint8_t data_buf[TEST_BUF_SIZE];
46+
int err, channel;
47+
48+
__ASSERT(k_is_user_context(), "isn't user");
49+
50+
LOG_INF("SOF thread %s (%s)",
51+
k_is_user_context() ? "UserSpace!" : "privileged mode.",
52+
CONFIG_BOARD_TARGET);
53+
54+
/*
55+
* note: this gets a pointer to kernel memory this thread
56+
* cannot access
57+
*/
58+
dma = sof_dma_get(SOF_DMA_DIR_LMEM_TO_HMEM, 0, SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED);
59+
60+
#if NEEDS_FAULT_HANDLING
61+
/* test passing a invalid kernel handle */
62+
channel = sof_dma_request_channel(dma + 0x4000, 0);
63+
LOG_INF("sof_dma_request_channel/invalid: ret %d", channel);
64+
__ASSERT(channel == -ENOENT, "sof-dma-request-channel with invalid DMA handle");
65+
#endif
66+
67+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
68+
LOG_INF("configure DMA channel");
69+
70+
channel = sof_dma_request_channel(dma, TEST_CHANNEL);
71+
LOG_INF("sof_dma_request_channel: ret %d", channel);
72+
73+
err = sof_dma_get_attribute(dma, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT,
74+
&addr_align);
75+
__ASSERT(!err, "dma-attr-alignment error");
76+
__ASSERT(addr_align != 0xbaabbaab, "dma-attr-alignment not reported");
77+
LOG_INF("DMA addr alignment %u", addr_align);
78+
79+
/* set up a DMA transfer */
80+
memset(&dma_block_cfg, 0, sizeof(dma_block_cfg));
81+
dma_block_cfg.dest_address = 0; /* host fifo */
82+
dma_block_cfg.source_address = (uintptr_t)data_buf;
83+
dma_block_cfg.block_size = sizeof(data_buf);
84+
85+
/*
86+
* fill data ramp, this payload is expected by host test
87+
* harness
88+
*/
89+
for (uint32_t i = 0; i < TEST_BUF_SIZE; i++) {
90+
data_buf[i] = i & 0xff;
91+
}
92+
sys_cache_data_flush_range(data_buf, sizeof(data_buf));
93+
94+
memset(&config, 0, sizeof(config));
95+
config.channel_direction = MEMORY_TO_HOST;
96+
config.block_count = 1;
97+
config.head_block = &dma_block_cfg;
98+
99+
err = sof_dma_config(dma, channel, &config);
100+
__ASSERT(!err, "dma-config error");
101+
LOG_INF("sof_dma_config: success");
102+
103+
err = sof_dma_start(dma, channel);
104+
LOG_INF("sof_dma_start: ret %d", err);
105+
106+
k_sem_give(&ipc_sem_wake_kernel);
107+
LOG_INF("setup ready, waiting for kernel to configure host-side of the test");
108+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
109+
LOG_INF("start DMA test and transfer data");
110+
111+
err = sof_dma_get_status(dma, channel, &stat);
112+
LOG_INF("sof_dma_get_status start: ret %d pend %u free %u", err,
113+
stat.pending_length, stat.free);
114+
115+
err = sof_dma_reload(dma, channel, sizeof(data_buf));
116+
LOG_INF("sof_dma_reload: ret %d", err);
117+
118+
for (int i = 0; stat.pending_length < TEST_BUF_SIZE; i++) {
119+
err = sof_dma_get_status(dma, channel, &stat);
120+
LOG_INF("sof_dma_get_status %d: ret %d pend %u free %u", i, err,
121+
stat.pending_length, stat.free);
122+
123+
__ASSERT(i < 100, "DMA transfer completes in 100usec");
124+
125+
/* let DMA transfer complete */
126+
k_sleep(K_USEC(1));
127+
}
128+
129+
err = sof_dma_get_status(dma, channel, &stat);
130+
LOG_INF("sof_dma_get_status end: ret %d pend %u free %u", err,
131+
stat.pending_length, stat.free);
132+
133+
LOG_INF("transfer done, asking host to validate output");
134+
k_sem_give(&ipc_sem_wake_kernel);
135+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
136+
LOG_INF("test done, cleaning up resources");
137+
138+
err = sof_dma_stop(dma, channel);
139+
LOG_INF("sof_dma_stop: ret %d", err);
140+
141+
sof_dma_release_channel(dma, channel);
142+
LOG_INF("sof_dma_release_channel: success");
143+
144+
sof_dma_put(dma);
145+
LOG_INF("sof_dma_put: success");
146+
k_sem_give(&ipc_sem_wake_kernel);
147+
}
148+
149+
#define IPC_TIMEOUT K_MSEC(1500)
150+
#define DMA_BUF_SIZE 256
151+
152+
#define ALIGNMENT DMA_BUF_ADDR_ALIGNMENT(DT_NODELABEL(hda_host_in))
153+
static __aligned(ALIGNMENT) uint8_t dma_buf[DMA_BUF_SIZE];
154+
155+
#include <intel_adsp_hda.h>
156+
#include <../../../../zephyr/tests/boards/intel_adsp/hda/src/tests.h>
157+
158+
static int msg_validate_res;
159+
160+
static bool ipc_message(const struct device *dev, void *arg,
161+
uint32_t data, uint32_t ext_data)
162+
{
163+
LOG_DBG("HDA message received, data %u, ext_data %u", data, ext_data);
164+
msg_validate_res = ext_data;
165+
return true;
166+
}
167+
168+
static void intel_hda_dma_kernel(void)
169+
{
170+
const struct device *dma;
171+
172+
LOG_INF("run %s with buffer at address %p, size %d",
173+
__func__, (void *)dma_buf, DMA_BUF_SIZE);
174+
175+
intel_adsp_ipc_set_message_handler(INTEL_ADSP_IPC_HOST_DEV, ipc_message, NULL);
176+
177+
k_thread_create(&user_thread, user_stack, USER_STACKSIZE,
178+
intel_hda_dma_user, NULL, NULL, NULL,
179+
-1, K_USER, K_FOREVER);
180+
181+
k_thread_access_grant(&user_thread, &ipc_sem_wake_user);
182+
k_thread_access_grant(&user_thread, &ipc_sem_wake_kernel);
183+
184+
dma = DEVICE_DT_GET(DT_NODELABEL(hda_host_in));
185+
k_thread_access_grant(&user_thread, dma);
186+
187+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_RESET,
188+
TEST_CHANNEL, IPC_TIMEOUT);
189+
190+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_CONFIG,
191+
TEST_CHANNEL | (DMA_BUF_SIZE << 8), IPC_TIMEOUT);
192+
193+
k_thread_start(&user_thread);
194+
195+
LOG_INF("user started, waiting for it to be ready");
196+
197+
k_sem_give(&ipc_sem_wake_user);
198+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
199+
200+
LOG_INF("user ready, starting HDA test");
201+
202+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_START, TEST_CHANNEL, IPC_TIMEOUT);
203+
204+
k_sem_give(&ipc_sem_wake_user);
205+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
206+
207+
LOG_INF("transfer done, validating results");
208+
209+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_VALIDATE, TEST_CHANNEL,
210+
IPC_TIMEOUT);
211+
212+
hda_dump_regs(HOST_OUT, HDA_REGBLOCK_SIZE, TEST_CHANNEL, "host reset");
213+
214+
k_sem_give(&ipc_sem_wake_user);
215+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
216+
217+
LOG_INF("test done, terminate user");
218+
219+
k_thread_join(&user_thread, K_FOREVER);
220+
221+
__ASSERT(msg_validate_res == 1, "DMA transferred data invalid payload");
222+
}
223+
224+
ZTEST(userspace_intel_hda_dma, dma_mem_to_host)
225+
{
226+
intel_hda_dma_kernel();
227+
228+
ztest_test_pass();
229+
}
230+
231+
ZTEST_SUITE(userspace_intel_hda_dma, NULL, NULL, NULL, NULL, NULL);
232+
233+
/**
234+
* SOF main has booted up and IPC handling is stopped.
235+
* Run test suites with ztest_run_all.
236+
*/
237+
static int run_tests(void)
238+
{
239+
ztest_run_all(NULL, false, 1, 1);
240+
return 0;
241+
}
242+
243+
SYS_INIT(run_tests, APPLICATION, 99);

0 commit comments

Comments
 (0)