diff --git a/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.yaml b/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.yaml index 3d08a71a07a..874e451eb53 100644 --- a/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.yaml +++ b/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.yaml @@ -17,6 +17,7 @@ supported: - counter - gpio - i2c + - i2s - ieee802154 - pwm - spi diff --git a/boards/arm/nrf5340dk_nrf5340/nrf5340dk_nrf5340_cpuapp.yaml b/boards/arm/nrf5340dk_nrf5340/nrf5340dk_nrf5340_cpuapp.yaml index 738f12a8114..6eaded2454f 100644 --- a/boards/arm/nrf5340dk_nrf5340/nrf5340dk_nrf5340_cpuapp.yaml +++ b/boards/arm/nrf5340dk_nrf5340/nrf5340dk_nrf5340_cpuapp.yaml @@ -11,6 +11,7 @@ flash: 1024 supported: - gpio - i2c + - i2s - pwm - watchdog - usb_cdc diff --git a/tests/drivers/i2s/i2s_api/Kconfig b/tests/drivers/i2s/i2s_api/Kconfig index cf73b55ac87..ab3c2121fef 100644 --- a/tests/drivers/i2s/i2s_api/Kconfig +++ b/tests/drivers/i2s/i2s_api/Kconfig @@ -12,3 +12,25 @@ config I2S_TEST_SEPARATE_DEVICES bool "Use two separate I2S ports for loopback" help Use separate I2S ports for transmit and receive. + +config I2S_TEST_USE_I2S_DIR_BOTH + bool "Use I2S_DIR_BOTH value to perform RX/TX transfers" + help + Use the I2S_DIR_BOTH enumeration value to trigger commands in test + cases involving both reception and transmission. Use of this option + is essential for devices that cannot independently start and stop + the RX and TX streams. + +config I2S_TEST_USE_GPIO_LOOPBACK + bool "Use GPIO loopback" + help + Use wiring between the data-out and data-in pins for looping back + data. This option is intended to be used for devices that do not + provide the internal loopback functionality. + +config I2S_TEST_ALLOWED_DATA_OFFSET + int "Allowed offset in received data" + default 0 + help + Maximum allowed offset between sent and received samples. Non-zero + value of this option may be needed when GPIO loopback is used. diff --git a/tests/drivers/i2s/i2s_api/boards/nrf52840dk_nrf52840.conf b/tests/drivers/i2s/i2s_api/boards/nrf52840dk_nrf52840.conf new file mode 100644 index 00000000000..8f1cc86aa7a --- /dev/null +++ b/tests/drivers/i2s/i2s_api/boards/nrf52840dk_nrf52840.conf @@ -0,0 +1,3 @@ +CONFIG_I2S_TEST_USE_I2S_DIR_BOTH=y +CONFIG_I2S_TEST_USE_GPIO_LOOPBACK=y +CONFIG_I2S_TEST_ALLOWED_DATA_OFFSET=2 diff --git a/tests/drivers/i2s/i2s_api/boards/nrf52840dk_nrf52840.overlay b/tests/drivers/i2s/i2s_api/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 00000000000..43a5ce45dd0 --- /dev/null +++ b/tests/drivers/i2s/i2s_api/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,11 @@ +&uart1 { + status = "disabled"; +}; + +&i2s0 { + status = "okay"; + sck-pin = <36>; + lrck-pin = <35>; + sdout-pin = <34>; + sdin-pin = <33>; +}; diff --git a/tests/drivers/i2s/i2s_api/boards/nrf5340dk_nrf5340_cpuapp.conf b/tests/drivers/i2s/i2s_api/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 00000000000..8f1cc86aa7a --- /dev/null +++ b/tests/drivers/i2s/i2s_api/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1,3 @@ +CONFIG_I2S_TEST_USE_I2S_DIR_BOTH=y +CONFIG_I2S_TEST_USE_GPIO_LOOPBACK=y +CONFIG_I2S_TEST_ALLOWED_DATA_OFFSET=2 diff --git a/tests/drivers/i2s/i2s_api/boards/nrf5340dk_nrf5340_cpuapp.overlay b/tests/drivers/i2s/i2s_api/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 00000000000..ceeba511761 --- /dev/null +++ b/tests/drivers/i2s/i2s_api/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,11 @@ +&uart1 { + status = "disabled"; +}; + +&i2s0 { + status = "okay"; + sck-pin = <37>; + lrck-pin = <36>; + sdout-pin = <33>; + sdin-pin = <32>; +}; diff --git a/tests/drivers/i2s/i2s_api/src/common.c b/tests/drivers/i2s/i2s_api/src/common.c index fcdb5c82c73..96829eea057 100644 --- a/tests/drivers/i2s/i2s_api/src/common.c +++ b/tests/drivers/i2s/i2s_api/src/common.c @@ -9,6 +9,9 @@ #include #include "i2s_api_test.h" +K_MEM_SLAB_DEFINE(rx_mem_slab, BLOCK_SIZE, NUM_RX_BLOCKS, 32); +K_MEM_SLAB_DEFINE(tx_mem_slab, BLOCK_SIZE, NUM_TX_BLOCKS, 32); + /* The data_l represent a sine wave */ ZTEST_DMEM int16_t data_l[SAMPLE_NO] = { 6392, 12539, 18204, 23169, 27244, 30272, 32137, 32767, 32137, @@ -35,7 +38,28 @@ static void fill_buf(int16_t *tx_block, int att) static int verify_buf(int16_t *rx_block, int att) { - for (int i = 0; i < SAMPLE_NO; i++) { + int sample_no = SAMPLE_NO; + +#if (CONFIG_I2S_TEST_ALLOWED_DATA_OFFSET > 0) + static ZTEST_DMEM int offset = -1; + + if (offset < 0) { + do { + ++offset; + if (offset > CONFIG_I2S_TEST_ALLOWED_DATA_OFFSET) { + TC_PRINT("Allowed data offset exceeded\n"); + return -TC_FAIL; + } + } while (rx_block[2 * offset] != data_l[0] >> att); + + TC_PRINT("Using data offset: %d\n", offset); + } + + rx_block += 2 * offset; + sample_no -= offset; +#endif + + for (int i = 0; i < sample_no; i++) { if (rx_block[2 * i] != data_l[i] >> att) { TC_PRINT("Error: att %d: data_l mismatch at position " "%d, expected %d, actual %d\n", @@ -81,8 +105,8 @@ int verify_buf_const(int16_t *rx_block, int16_t val_l, int16_t val_r) return TC_PASS; } -int tx_block_write_slab(const struct device *dev_i2s, int att, int err, - struct k_mem_slab *slab) +static int tx_block_write_slab(const struct device *dev_i2s, int att, int err, + struct k_mem_slab *slab) { char tx_block[BLOCK_SIZE]; int ret; @@ -98,8 +122,13 @@ int tx_block_write_slab(const struct device *dev_i2s, int att, int err, return TC_PASS; } -int rx_block_read_slab(const struct device *dev_i2s, int att, - struct k_mem_slab *slab) +int tx_block_write(const struct device *dev_i2s, int att, int err) +{ + return tx_block_write_slab(dev_i2s, att, err, &tx_mem_slab); +} + +static int rx_block_read_slab(const struct device *dev_i2s, int att, + struct k_mem_slab *slab) { char rx_block[BLOCK_SIZE]; size_t rx_size; @@ -118,3 +147,60 @@ int rx_block_read_slab(const struct device *dev_i2s, int att, return TC_PASS; } + +int rx_block_read(const struct device *dev_i2s, int att) +{ + return rx_block_read_slab(dev_i2s, att, &rx_mem_slab); +} + +int configure_stream(const struct device *dev_i2s, enum i2s_dir dir) +{ + int ret; + struct i2s_config i2s_cfg; + + i2s_cfg.word_size = 16U; + i2s_cfg.channels = 2U; + i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S; + i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ; + i2s_cfg.block_size = BLOCK_SIZE; + i2s_cfg.timeout = TIMEOUT; + + if (dir == I2S_DIR_TX) { + /* Configure the Transmit port as Master */ + i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER + | I2S_OPT_BIT_CLK_MASTER; + } else if (dir == I2S_DIR_RX) { + /* Configure the Receive port as Slave */ + i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE + | I2S_OPT_BIT_CLK_SLAVE; + } else { /* dir == I2S_DIR_BOTH */ + i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER + | I2S_OPT_BIT_CLK_MASTER; + } + + if (!IS_ENABLED(CONFIG_I2S_TEST_USE_GPIO_LOOPBACK)) { + i2s_cfg.options |= I2S_OPT_LOOPBACK; + } + + if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { + i2s_cfg.mem_slab = &tx_mem_slab; + ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg); + if (ret < 0) { + TC_PRINT("Failed to configure I2S TX stream (%d)\n", + ret); + return -TC_FAIL; + } + } + + if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { + i2s_cfg.mem_slab = &rx_mem_slab; + ret = i2s_configure(dev_i2s, I2S_DIR_RX, &i2s_cfg); + if (ret < 0) { + TC_PRINT("Failed to configure I2S RX stream (%d)\n", + ret); + return -TC_FAIL; + } + } + + return TC_PASS; +} diff --git a/tests/drivers/i2s/i2s_api/src/i2s_api_test.h b/tests/drivers/i2s/i2s_api/src/i2s_api_test.h index 39fa40a9961..ae8c427ea7f 100644 --- a/tests/drivers/i2s/i2s_api/src/i2s_api_test.h +++ b/tests/drivers/i2s/i2s_api/src/i2s_api_test.h @@ -9,6 +9,7 @@ #define I2S_API_TEST_H #include +#include void test_i2s_tx_transfer_configure_0(void); void test_i2s_rx_transfer_configure_0(void); @@ -28,10 +29,21 @@ void test_i2s_state_running_neg(void); void test_i2s_state_stopping_neg(void); void test_i2s_state_error_neg(void); -extern struct k_mem_slab rx_0_mem_slab; -extern struct k_mem_slab tx_0_mem_slab; -extern struct k_mem_slab rx_1_mem_slab; -extern struct k_mem_slab tx_1_mem_slab; +void test_i2s_dir_both_transfer_configure_0(void); +void test_i2s_dir_both_transfer_short(void); +void test_i2s_dir_both_transfer_long(void); +void test_i2s_dir_both_rx_empty_timeout(void); +void test_i2s_dir_both_transfer_restart(void); +void test_i2s_dir_both_transfer_rx_overrun(void); +void test_i2s_dir_both_transfer_tx_underrun(void); + +void test_i2s_dir_both_transfer_configure_1(void); +void test_i2s_dir_both_state_running_neg(void); +void test_i2s_dir_both_state_stopping_neg(void); +void test_i2s_dir_both_state_error_neg(void); + +extern struct k_mem_slab rx_mem_slab; +extern struct k_mem_slab tx_mem_slab; #define SAMPLE_NO 32 #define TIMEOUT 2000 @@ -48,12 +60,15 @@ extern int16_t data_r[SAMPLE_NO]; #endif #define BLOCK_SIZE (2 * sizeof(data_l)) -int rx_block_read_slab(const struct device *dev_i2s, int att, - struct k_mem_slab *slab); -int tx_block_write_slab(const struct device *dev_i2s, int att, int err, - struct k_mem_slab *slab); +#define NUM_RX_BLOCKS 4 +#define NUM_TX_BLOCKS 4 + +int tx_block_write(const struct device *dev_i2s, int att, int err); +int rx_block_read(const struct device *dev_i2s, int att); void fill_buf_const(int16_t *tx_block, int16_t val_l, int16_t val_r); int verify_buf_const(int16_t *rx_block, int16_t val_l, int16_t val_r); +int configure_stream(const struct device *dev_i2s, enum i2s_dir dir); + #endif diff --git a/tests/drivers/i2s/i2s_api/src/main.c b/tests/drivers/i2s/i2s_api/src/main.c index 651cc6a8503..71e43751aad 100644 --- a/tests/drivers/i2s/i2s_api/src/main.c +++ b/tests/drivers/i2s/i2s_api/src/main.c @@ -16,8 +16,8 @@ void test_main(void) const struct device *dev_i2s_tx; k_thread_access_grant(k_current_get(), - &rx_0_mem_slab, &tx_0_mem_slab, - &rx_1_mem_slab, &tx_1_mem_slab); + &rx_mem_slab, &tx_mem_slab); + dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); if (dev_i2s_rx != NULL) { k_object_access_grant(dev_i2s_rx, k_current_get()); @@ -29,47 +29,79 @@ void test_main(void) } ztest_test_suite(i2s_loopback_test, - ztest_unit_test(test_i2s_tx_transfer_configure_0), - ztest_unit_test(test_i2s_rx_transfer_configure_0), - ztest_unit_test(test_i2s_transfer_short), - ztest_unit_test(test_i2s_transfer_long), - ztest_unit_test(test_i2s_rx_sync_start), - ztest_unit_test(test_i2s_rx_empty_timeout), - ztest_unit_test(test_i2s_transfer_restart), - ztest_unit_test(test_i2s_transfer_rx_overrun), - ztest_unit_test(test_i2s_transfer_tx_underrun)); + ztest_unit_test(test_i2s_tx_transfer_configure_0), + ztest_unit_test(test_i2s_rx_transfer_configure_0), + ztest_unit_test(test_i2s_transfer_short), + ztest_unit_test(test_i2s_transfer_long), + ztest_unit_test(test_i2s_rx_sync_start), + ztest_unit_test(test_i2s_rx_empty_timeout), + ztest_unit_test(test_i2s_transfer_restart), + ztest_unit_test(test_i2s_transfer_rx_overrun), + ztest_unit_test(test_i2s_transfer_tx_underrun)); ztest_run_test_suite(i2s_loopback_test); ztest_test_suite(i2s_states_test, - ztest_unit_test(test_i2s_tx_transfer_configure_1), - ztest_unit_test(test_i2s_rx_transfer_configure_1), - ztest_unit_test(test_i2s_state_not_ready_neg), - ztest_unit_test(test_i2s_state_ready_neg), - ztest_unit_test(test_i2s_state_running_neg), - ztest_unit_test(test_i2s_state_stopping_neg), - ztest_unit_test(test_i2s_state_error_neg)); + ztest_unit_test(test_i2s_tx_transfer_configure_1), + ztest_unit_test(test_i2s_rx_transfer_configure_1), + ztest_unit_test(test_i2s_state_not_ready_neg), + ztest_unit_test(test_i2s_state_ready_neg), + ztest_unit_test(test_i2s_state_running_neg), + ztest_unit_test(test_i2s_state_stopping_neg), + ztest_unit_test(test_i2s_state_error_neg)); ztest_run_test_suite(i2s_states_test); + ztest_test_suite(i2s_dir_both_loopback_test, + ztest_unit_test(test_i2s_dir_both_transfer_configure_0), + ztest_unit_test(test_i2s_dir_both_transfer_short), + ztest_unit_test(test_i2s_dir_both_transfer_long), + ztest_unit_test(test_i2s_dir_both_transfer_restart), + ztest_unit_test(test_i2s_dir_both_transfer_rx_overrun), + ztest_unit_test(test_i2s_dir_both_transfer_tx_underrun)); + ztest_run_test_suite(i2s_dir_both_loopback_test); + + ztest_test_suite(i2s_dir_both_states_test, + ztest_unit_test(test_i2s_dir_both_transfer_configure_1), + ztest_unit_test(test_i2s_dir_both_state_running_neg), + ztest_unit_test(test_i2s_dir_both_state_stopping_neg), + ztest_unit_test(test_i2s_dir_both_state_error_neg)); + ztest_run_test_suite(i2s_dir_both_states_test); + /* Now run all tests in user mode */ ztest_test_suite(i2s_user_loopback_test, - ztest_user_unit_test(test_i2s_tx_transfer_configure_0), - ztest_user_unit_test(test_i2s_rx_transfer_configure_0), - ztest_user_unit_test(test_i2s_transfer_short), - ztest_user_unit_test(test_i2s_transfer_long), - ztest_user_unit_test(test_i2s_rx_sync_start), - ztest_user_unit_test(test_i2s_rx_empty_timeout), - ztest_user_unit_test(test_i2s_transfer_restart), - ztest_user_unit_test(test_i2s_transfer_tx_underrun), - ztest_user_unit_test(test_i2s_transfer_rx_overrun)); + ztest_user_unit_test(test_i2s_tx_transfer_configure_0), + ztest_user_unit_test(test_i2s_rx_transfer_configure_0), + ztest_user_unit_test(test_i2s_transfer_short), + ztest_user_unit_test(test_i2s_transfer_long), + ztest_user_unit_test(test_i2s_rx_sync_start), + ztest_user_unit_test(test_i2s_rx_empty_timeout), + ztest_user_unit_test(test_i2s_transfer_restart), + ztest_user_unit_test(test_i2s_transfer_tx_underrun), + ztest_user_unit_test(test_i2s_transfer_rx_overrun)); ztest_run_test_suite(i2s_user_loopback_test); ztest_test_suite(i2s_user_states_test, - ztest_user_unit_test(test_i2s_tx_transfer_configure_1), - ztest_user_unit_test(test_i2s_rx_transfer_configure_1), - ztest_user_unit_test(test_i2s_state_not_ready_neg), - ztest_user_unit_test(test_i2s_state_ready_neg), - ztest_user_unit_test(test_i2s_state_running_neg), - ztest_user_unit_test(test_i2s_state_stopping_neg), - ztest_user_unit_test(test_i2s_state_error_neg)); + ztest_user_unit_test(test_i2s_tx_transfer_configure_1), + ztest_user_unit_test(test_i2s_rx_transfer_configure_1), + ztest_user_unit_test(test_i2s_state_not_ready_neg), + ztest_user_unit_test(test_i2s_state_ready_neg), + ztest_user_unit_test(test_i2s_state_running_neg), + ztest_user_unit_test(test_i2s_state_stopping_neg), + ztest_user_unit_test(test_i2s_state_error_neg)); ztest_run_test_suite(i2s_user_states_test); + + ztest_test_suite(i2s_dir_both_user_loopback_test, + ztest_user_unit_test(test_i2s_dir_both_transfer_configure_0), + ztest_user_unit_test(test_i2s_dir_both_transfer_short), + ztest_user_unit_test(test_i2s_dir_both_transfer_long), + ztest_user_unit_test(test_i2s_dir_both_transfer_restart), + ztest_user_unit_test(test_i2s_dir_both_transfer_rx_overrun), + ztest_user_unit_test(test_i2s_dir_both_transfer_tx_underrun)); + ztest_run_test_suite(i2s_dir_both_user_loopback_test); + + ztest_test_suite(i2s_dir_both_user_states_test, + ztest_user_unit_test(test_i2s_dir_both_transfer_configure_1), + ztest_user_unit_test(test_i2s_dir_both_state_running_neg), + ztest_user_unit_test(test_i2s_dir_both_state_stopping_neg), + ztest_user_unit_test(test_i2s_dir_both_state_error_neg)); + ztest_run_test_suite(i2s_dir_both_user_states_test); } diff --git a/tests/drivers/i2s/i2s_api/src/test_i2s_dir_both_loopback.c b/tests/drivers/i2s/i2s_api/src/test_i2s_dir_both_loopback.c new file mode 100644 index 00000000000..de377f62f0b --- /dev/null +++ b/tests/drivers/i2s/i2s_api/src/test_i2s_dir_both_loopback.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2017 comsuisse AG + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "i2s_api_test.h" + +/* The test cases here are copied from test_i2s_loopback.c and adapted for use + * on devices that cannot independently start and stop the RX and TX streams + * and require the use of the I2S_DIR_BOTH value for RX/TX transfers. + */ + +static ZTEST_DMEM const struct device *dev_i2s; +static ZTEST_DMEM bool dir_both_supported; + +void test_i2s_dir_both_transfer_configure_0(void) +{ + int ret; + + dev_i2s = device_get_binding(I2S_DEV_NAME_RX); + zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_RX " not found"); + + ret = configure_stream(dev_i2s, I2S_DIR_BOTH); + zassert_equal(ret, TC_PASS, NULL); + + /* Check if the tested driver supports the I2S_DIR_BOTH value. + * Use the DROP trigger for this, as in the current state of the driver + * (READY, both TX and RX queues empty) it is actually a no-op. + */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DROP); + dir_both_supported = (ret == 0); + + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + zassert_true(dir_both_supported, + "I2S_DIR_BOTH value is supposed to be supported."); + } +} + +/** @brief Short I2S transfer. + * + * - START trigger starts both the transmission and reception. + * - Sending / receiving a short sequence of data returns success. + * - DRAIN trigger empties the transmit queue and stops both streams. + */ +void test_i2s_dir_both_transfer_short(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + int ret; + + /* Prefill TX queue */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d->OK\n", 1); + + ret = tx_block_write(dev_i2s, 1, 0); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d->OK\n", 2); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d<-OK\n", 1); + + ret = tx_block_write(dev_i2s, 2, 0); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d->OK\n", 3); + + /* All data written, drain TX queue and stop both streams. */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + + ret = rx_block_read(dev_i2s, 1); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d<-OK\n", 2); + + ret = rx_block_read(dev_i2s, 2); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d<-OK\n", 3); + + /* TODO: Verify the interface is in READY state when i2s_state_get + * function is available. + */ +} + +#define TEST_I2S_TRANSFER_LONG_REPEAT_COUNT 100 + +/** @brief Long I2S transfer. + * + * - START trigger starts both the transmission and reception. + * - Sending / receiving a long sequence of data returns success. + * - DRAIN trigger empties the transmit queue and stops both streams. + */ +void test_i2s_dir_both_transfer_long(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + int ret; + + /* Prefill TX queue */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + for (int i = 0; i < TEST_I2S_TRANSFER_LONG_REPEAT_COUNT; i++) { + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + } + + /* All data written, all but one data block read, flush TX queue + * and stop both streams. + */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + + /* TODO: Verify the interface is in READY state when i2s_state_get + * function is available. + */ +} + +/** @brief Re-start I2S transfer. + * + * - STOP trigger stops transfer / reception at the end of the current block, + * consecutive START trigger restarts transfer / reception with the next data + * block. + */ +void test_i2s_dir_both_transfer_restart(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + int ret; + + /* Prefill TX queue */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d->OK\n", 1); + + ret = tx_block_write(dev_i2s, 1, 0); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d->OK\n", 2); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_STOP); + zassert_equal(ret, 0, "RX/TX STOP trigger failed"); + + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d<-OK\n", 1); + + TC_PRINT("Stop transmission\n"); + + /* Keep interface inactive */ + k_sleep(K_MSEC(1000)); + + TC_PRINT("Start transmission\n"); + + /* Prefill TX queue */ + ret = tx_block_write(dev_i2s, 2, 0); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d->OK\n", 3); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + + ret = rx_block_read(dev_i2s, 1); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d<-OK\n", 2); + + ret = rx_block_read(dev_i2s, 2); + zassert_equal(ret, TC_PASS, NULL); + TC_PRINT("%d<-OK\n", 3); +} + +/** @brief RX buffer overrun. + * + * - In case of RX buffer overrun it is possible to read out RX data blocks + * that are stored in the RX queue. + * - Reading from an empty RX queue when the RX buffer overrun occurred results + * in an error. + * - Sending PREPARE trigger after the RX buffer overrun occurred changes + * the interface state to READY. + */ +void test_i2s_dir_both_transfer_rx_overrun(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + size_t rx_size; + int ret; + char rx_buf[BLOCK_SIZE]; + + /* Prefill TX queue */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + for (int i = 0; i < NUM_RX_BLOCKS; i++) { + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + } + + /* All data written, flush TX queue and stop the transmission */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + + /* Wait for transmission to finish */ + k_sleep(K_MSEC(200)); + + /* Read one data block, expect success even if RX queue is already in + * the error state. + */ + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + + /* Attempt to read more data blocks than are available in the RX queue */ + for (int i = 0; i < NUM_RX_BLOCKS; i++) { + ret = i2s_buf_read(dev_i2s, rx_buf, &rx_size); + if (ret != 0) { + break; + } + } + zassert_equal(ret, -EIO, "RX overrun error not detected"); + + ret = i2s_trigger(dev_i2s, I2S_DIR_RX, I2S_TRIGGER_PREPARE); + zassert_equal(ret, 0, "RX PREPARE trigger failed"); + + /* Transmit and receive one more data block */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + + k_sleep(K_MSEC(200)); +} + +/** @brief TX buffer underrun. + * + * - An attempt to write to the TX queue when TX buffer underrun has occurred + * results in an error. + * - Sending PREPARE trigger after the TX buffer underrun occurred changes + * the interface state to READY. + */ +void test_i2s_dir_both_transfer_tx_underrun(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + int ret; + + /* Prefill TX queue */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + + k_sleep(K_MSEC(200)); + + /* Write one more TX data block, expect an error */ + ret = tx_block_write(dev_i2s, 2, -EIO); + zassert_equal(ret, TC_PASS, NULL); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_PREPARE); + zassert_equal(ret, 0, "RX/TX PREPARE trigger failed"); + + k_sleep(K_MSEC(200)); + + /* Transmit and receive two more data blocks */ + ret = tx_block_write(dev_i2s, 1, 0); + zassert_equal(ret, TC_PASS, NULL); + ret = tx_block_write(dev_i2s, 1, 0); + zassert_equal(ret, TC_PASS, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + ret = rx_block_read(dev_i2s, 1); + zassert_equal(ret, TC_PASS, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + ret = rx_block_read(dev_i2s, 1); + zassert_equal(ret, TC_PASS, NULL); + + k_sleep(K_MSEC(200)); +} diff --git a/tests/drivers/i2s/i2s_api/src/test_i2s_dir_both_states.c b/tests/drivers/i2s/i2s_api/src/test_i2s_dir_both_states.c new file mode 100644 index 00000000000..a8e6e6c2d78 --- /dev/null +++ b/tests/drivers/i2s/i2s_api/src/test_i2s_dir_both_states.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017 comsuisse AG + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "i2s_api_test.h" + +/* The test cases here are copied from test_i2s_states.c and adapted for use + * on devices that cannot independently start and stop the RX and TX streams + * and require the use of the I2S_DIR_BOTH value for RX/TX transfers. + */ + +static ZTEST_DMEM const struct device *dev_i2s; +static ZTEST_DMEM bool dir_both_supported; + +void test_i2s_dir_both_transfer_configure_1(void) +{ + int ret; + + dev_i2s = device_get_binding(I2S_DEV_NAME_RX); + zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_RX " not found"); + + ret = configure_stream(dev_i2s, I2S_DIR_BOTH); + zassert_equal(ret, TC_PASS, NULL); + + /* Check if the tested driver supports the I2S_DIR_BOTH value. + * Use the DROP trigger for this, as in the current state of the driver + * (READY, both TX and RX queues empty) it is actually a no-op. + */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DROP); + dir_both_supported = (ret == 0); + + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + zassert_true(dir_both_supported, + "I2S_DIR_BOTH value is supposed to be supported."); + } +} + +#define TEST_I2S_STATE_RUNNING_NEG_REPEAT_COUNT 5 + +/** @brief Verify all failure cases in RUNNING state. + * + * - Sending START, PREPARE trigger in RUNNING state returns failure. + */ +void test_i2s_dir_both_state_running_neg(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + int ret; + + /* Prefill TX queue */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + for (int i = 0; i < TEST_I2S_STATE_RUNNING_NEG_REPEAT_COUNT; i++) { + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + + /* Send invalid triggers, expect failure */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, -EIO, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_PREPARE); + zassert_equal(ret, -EIO, NULL); + } + + /* All data written, drain TX queue and stop both streams. */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); +} + +/** @brief Verify all failure cases in STOPPING state. + * + * - Sending START, STOP, DRAIN, PREPARE trigger in STOPPING state returns + * failure. + */ +void test_i2s_dir_both_state_stopping_neg(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + int ret; + + /* Prefill TX queue */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + + /* All data written, all but one data block read, flush TX queue and + * stop both streams. + */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + + /* Send invalid triggers, expect failure */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, -EIO, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_STOP); + zassert_equal(ret, -EIO, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, -EIO, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_PREPARE); + zassert_equal(ret, -EIO, NULL); + + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + + /* This is incase the RX channel is stuck in STOPPING state. + * Clear out the state before running the next test. + */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DROP); + zassert_equal(ret, 0, "RX/TX DROP trigger failed"); +} + +/** @brief Verify all failure cases in ERROR state. + * + * - Sending START, STOP, DRAIN trigger in ERROR state returns failure. + */ +void test_i2s_dir_both_state_error_neg(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + size_t rx_size; + int ret; + char rx_buf[BLOCK_SIZE]; + + /* Prefill TX queue */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + for (int i = 0; i < NUM_RX_BLOCKS; i++) { + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + } + + /* Wait for transmission to finish */ + k_sleep(K_MSEC(200)); + + /* Read one data block, expect success even if RX queue is already in + * the error state. + */ + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + + /* Attempt to read more data blocks than are available in the RX queue */ + for (int i = 0; i < NUM_RX_BLOCKS; i++) { + ret = i2s_buf_read(dev_i2s, rx_buf, &rx_size); + if (ret != 0) { + break; + } + } + zassert_equal(ret, -EIO, "RX overrun error not detected"); + + /* Write one more TX data block, expect an error */ + ret = tx_block_write(dev_i2s, 2, -EIO); + zassert_equal(ret, TC_PASS, NULL); + + /* Send invalid triggers, expect failure */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, -EIO, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_STOP); + zassert_equal(ret, -EIO, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, -EIO, NULL); + + /* Recover from ERROR state */ + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_PREPARE); + zassert_equal(ret, 0, "RX/TX PREPARE trigger failed"); + + /* Transmit and receive one more data block */ + ret = tx_block_write(dev_i2s, 0, 0); + zassert_equal(ret, TC_PASS, NULL); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed"); + ret = i2s_trigger(dev_i2s, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + ret = rx_block_read(dev_i2s, 0); + zassert_equal(ret, TC_PASS, NULL); + + k_sleep(K_MSEC(200)); +} diff --git a/tests/drivers/i2s/i2s_api/src/test_i2s_loopback.c b/tests/drivers/i2s/i2s_api/src/test_i2s_loopback.c index 6c50a223a98..f04de90fb35 100644 --- a/tests/drivers/i2s/i2s_api/src/test_i2s_loopback.c +++ b/tests/drivers/i2s/i2s_api/src/test_i2s_loopback.c @@ -10,72 +10,31 @@ #include #include "i2s_api_test.h" -#define NUM_RX_BLOCKS 4 -#define NUM_TX_BLOCKS 4 -K_MEM_SLAB_DEFINE(rx_0_mem_slab, BLOCK_SIZE, NUM_RX_BLOCKS, 32); -K_MEM_SLAB_DEFINE(tx_0_mem_slab, BLOCK_SIZE, NUM_TX_BLOCKS, 32); - -static int tx_block_write(const struct device *dev_i2s, int att, int err) -{ - return tx_block_write_slab(dev_i2s, att, err, &tx_0_mem_slab); -} -static int rx_block_read(const struct device *dev_i2s, int att) -{ - return rx_block_read_slab(dev_i2s, att, &rx_0_mem_slab); -} +static ZTEST_DMEM const struct device *dev_i2s_rx; +static ZTEST_DMEM const struct device *dev_i2s_tx; /** Configure I2S TX transfer. */ void test_i2s_tx_transfer_configure_0(void) { - const struct device *dev_i2s; - struct i2s_config i2s_cfg; int ret; - dev_i2s = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_TX " not found"); + dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); + zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Configure */ - - i2s_cfg.word_size = 16U; - i2s_cfg.channels = 2U; - i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S; - /* Configure the Transmit port as Master */ - i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER; - i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ; - i2s_cfg.block_size = BLOCK_SIZE; - i2s_cfg.mem_slab = &tx_0_mem_slab; - i2s_cfg.timeout = TIMEOUT; - i2s_cfg.options |= I2S_OPT_LOOPBACK; - - ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg); - zassert_equal(ret, 0, "Failed to configure I2S TX stream"); + ret = configure_stream(dev_i2s_tx, I2S_DIR_TX); + zassert_equal(ret, TC_PASS, NULL); } /** Configure I2S RX transfer. */ void test_i2s_rx_transfer_configure_0(void) { - const struct device *dev_i2s; - struct i2s_config i2s_cfg; int ret; - dev_i2s = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_RX " not found"); + dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); + zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - /* Configure */ - - i2s_cfg.word_size = 16U; - i2s_cfg.channels = 2U; - i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S; - /* Configure the Receive port as Slave */ - i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE; - i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ; - i2s_cfg.block_size = BLOCK_SIZE; - i2s_cfg.mem_slab = &rx_0_mem_slab; - i2s_cfg.timeout = TIMEOUT; - i2s_cfg.options |= I2S_OPT_LOOPBACK; - - ret = i2s_configure(dev_i2s, I2S_DIR_RX, &i2s_cfg); - zassert_equal(ret, 0, "Failed to configure I2S RX stream"); + ret = configure_stream(dev_i2s_rx, I2S_DIR_RX); + zassert_equal(ret, TC_PASS, NULL); } /** @brief Short I2S transfer. @@ -88,16 +47,14 @@ void test_i2s_rx_transfer_configure_0(void) */ void test_i2s_transfer_short(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + int ret; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ ret = tx_block_write(dev_i2s_tx, 0, 0); zassert_equal(ret, TC_PASS, NULL); @@ -156,16 +113,14 @@ void test_i2s_transfer_short(void) */ void test_i2s_transfer_long(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + int ret; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ ret = tx_block_write(dev_i2s_tx, 0, 0); zassert_equal(ret, TC_PASS, NULL); @@ -212,18 +167,16 @@ void test_i2s_transfer_long(void) */ void test_i2s_rx_sync_start(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + size_t rx_size; int ret; char buf[BLOCK_SIZE]; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ for (int n = 0; n < NUM_TX_BLOCKS; n++) { fill_buf_const((uint16_t *)buf, 1, 2); @@ -266,15 +219,11 @@ void test_i2s_rx_sync_start(void) */ void test_i2s_rx_empty_timeout(void) { - const struct device *dev_i2s; size_t rx_size; int ret; char buf[BLOCK_SIZE]; - dev_i2s = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_RX " not found"); - - ret = i2s_buf_read(dev_i2s, buf, &rx_size); + ret = i2s_buf_read(dev_i2s_rx, buf, &rx_size); zassert_equal(ret, -EAGAIN, "i2s_read did not timed out"); } @@ -286,16 +235,14 @@ void test_i2s_rx_empty_timeout(void) */ void test_i2s_transfer_restart(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + int ret; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ ret = tx_block_write(dev_i2s_tx, 0, 0); zassert_equal(ret, TC_PASS, NULL); @@ -373,18 +320,16 @@ void test_i2s_transfer_restart(void) */ void test_i2s_transfer_rx_overrun(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + size_t rx_size; int ret; char rx_buf[BLOCK_SIZE]; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ ret = tx_block_write(dev_i2s_tx, 0, 0); zassert_equal(ret, TC_PASS, NULL); @@ -453,16 +398,14 @@ void test_i2s_transfer_rx_overrun(void) */ void test_i2s_transfer_tx_underrun(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + int ret; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ ret = tx_block_write(dev_i2s_tx, 0, 0); zassert_equal(ret, TC_PASS, NULL); diff --git a/tests/drivers/i2s/i2s_api/src/test_i2s_states.c b/tests/drivers/i2s/i2s_api/src/test_i2s_states.c index fc7de0cfc53..5c7dcffdec5 100644 --- a/tests/drivers/i2s/i2s_api/src/test_i2s_states.c +++ b/tests/drivers/i2s/i2s_api/src/test_i2s_states.c @@ -9,73 +9,31 @@ #include #include "i2s_api_test.h" -#define NUM_RX_BLOCKS 4 -#define NUM_TX_BLOCKS 4 - -K_MEM_SLAB_DEFINE(rx_1_mem_slab, BLOCK_SIZE, NUM_RX_BLOCKS, 32); -K_MEM_SLAB_DEFINE(tx_1_mem_slab, BLOCK_SIZE, NUM_TX_BLOCKS, 32); - -static int tx_block_write(const struct device *dev_i2s, int att, int err) -{ - return tx_block_write_slab(dev_i2s, att, err, &tx_1_mem_slab); -} -static int rx_block_read(const struct device *dev_i2s, int att) -{ - return rx_block_read_slab(dev_i2s, att, &rx_1_mem_slab); -} +static ZTEST_DMEM const struct device *dev_i2s_rx; +static ZTEST_DMEM const struct device *dev_i2s_tx; /** Configure I2S TX transfer. */ void test_i2s_tx_transfer_configure_1(void) { - const struct device *dev_i2s; - struct i2s_config i2s_cfg; int ret; - dev_i2s = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_TX " not found"); + dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); + zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Configure */ - - i2s_cfg.word_size = 16U; - i2s_cfg.channels = 2U; - i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S; - /* Configure the Transmit port as Master */ - i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER; - i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ; - i2s_cfg.block_size = BLOCK_SIZE; - i2s_cfg.mem_slab = &tx_1_mem_slab; - i2s_cfg.timeout = TIMEOUT; - i2s_cfg.options |= I2S_OPT_LOOPBACK; - - ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg); - zassert_equal(ret, 0, "Failed to configure I2S TX stream"); + ret = configure_stream(dev_i2s_tx, I2S_DIR_TX); + zassert_equal(ret, TC_PASS, NULL); } /** Configure I2S RX transfer. */ void test_i2s_rx_transfer_configure_1(void) { - const struct device *dev_i2s; - struct i2s_config i2s_cfg; int ret; - dev_i2s = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_RX " not found"); + dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); + zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - /* Configure */ - - i2s_cfg.word_size = 16U; - i2s_cfg.channels = 2U; - i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S; - /* Configure the Receive port as Slave */ - i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE; - i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ; - i2s_cfg.block_size = BLOCK_SIZE; - i2s_cfg.mem_slab = &rx_1_mem_slab; - i2s_cfg.timeout = TIMEOUT; - i2s_cfg.options |= I2S_OPT_LOOPBACK; - - ret = i2s_configure(dev_i2s, I2S_DIR_RX, &i2s_cfg); - zassert_equal(ret, 0, "Failed to configure I2S RX stream"); + ret = configure_stream(dev_i2s_rx, I2S_DIR_RX); + zassert_equal(ret, TC_PASS, NULL); } /** @brief Verify all failure cases in NOT_READY state. @@ -87,21 +45,13 @@ void test_i2s_rx_transfer_configure_1(void) */ void test_i2s_state_not_ready_neg(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; struct i2s_config i2s_cfg; size_t rx_size; int ret; char rx_buf[BLOCK_SIZE]; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - i2s_cfg.frame_clk_freq = 0U; - i2s_cfg.mem_slab = &rx_1_mem_slab; + i2s_cfg.mem_slab = &rx_mem_slab; ret = i2s_configure(dev_i2s_rx, I2S_DIR_RX, &i2s_cfg); zassert_equal(ret, 0, "Failed to configure I2S RX stream"); @@ -125,7 +75,7 @@ void test_i2s_state_not_ready_neg(void) zassert_equal(ret, -EIO, NULL); i2s_cfg.frame_clk_freq = 0U; - i2s_cfg.mem_slab = &tx_1_mem_slab; + i2s_cfg.mem_slab = &tx_mem_slab; ret = i2s_configure(dev_i2s_tx, I2S_DIR_TX, &i2s_cfg); zassert_equal(ret, 0, "Failed to configure I2S TX stream"); @@ -155,31 +105,11 @@ void test_i2s_state_not_ready_neg(void) */ void test_i2s_state_ready_neg(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; - struct i2s_config i2s_cfg; int ret; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Configure RX stream changing its state to READY */ - - i2s_cfg.word_size = 16U; - i2s_cfg.channels = 2U; - i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S; - /* Configure the Receive port as Slave */ - i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE; - i2s_cfg.frame_clk_freq = 8000U; - i2s_cfg.block_size = BLOCK_SIZE; - i2s_cfg.mem_slab = &rx_1_mem_slab; - i2s_cfg.timeout = TIMEOUT; - - ret = i2s_configure(dev_i2s_rx, I2S_DIR_RX, &i2s_cfg); - zassert_equal(ret, 0, "Failed to configure I2S RX stream"); + ret = configure_stream(dev_i2s_rx, I2S_DIR_RX); + zassert_equal(ret, TC_PASS, NULL); /* Send RX stream triggers */ @@ -193,15 +123,8 @@ void test_i2s_state_ready_neg(void) zassert_equal(ret, -EIO, NULL); /* Configure TX stream changing its state to READY */ - - /* Configure the Transmit port as Master */ - i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER; - - i2s_cfg.options |= I2S_OPT_LOOPBACK; - i2s_cfg.mem_slab = &tx_1_mem_slab; - - ret = i2s_configure(dev_i2s_tx, I2S_DIR_TX, &i2s_cfg); - zassert_equal(ret, 0, "Failed to configure I2S RX stream"); + ret = configure_stream(dev_i2s_rx, I2S_DIR_TX); + zassert_equal(ret, TC_PASS, NULL); /* Send TX stream triggers */ @@ -223,16 +146,14 @@ void test_i2s_state_ready_neg(void) */ void test_i2s_state_running_neg(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + int ret; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ ret = tx_block_write(dev_i2s_tx, 0, 0); zassert_equal(ret, TC_PASS, NULL); @@ -282,16 +203,14 @@ void test_i2s_state_running_neg(void) */ void test_i2s_state_stopping_neg(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + int ret; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ ret = tx_block_write(dev_i2s_tx, 0, 0); zassert_equal(ret, TC_PASS, NULL); @@ -354,18 +273,16 @@ void test_i2s_state_stopping_neg(void) */ void test_i2s_state_error_neg(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + size_t rx_size; int ret; char rx_buf[BLOCK_SIZE]; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ ret = tx_block_write(dev_i2s_tx, 0, 0); zassert_equal(ret, TC_PASS, NULL); diff --git a/tests/drivers/i2s/i2s_api/testcase.yaml b/tests/drivers/i2s/i2s_api/testcase.yaml index 0961ae72183..4105c8be606 100644 --- a/tests/drivers/i2s/i2s_api/testcase.yaml +++ b/tests/drivers/i2s/i2s_api/testcase.yaml @@ -2,3 +2,11 @@ tests: drivers.i2s: depends_on: i2s tags: drivers userspace + filter: not CONFIG_I2S_TEST_USE_GPIO_LOOPBACK + drivers.i2s.gpio_loopback: + depends_on: i2s + tags: drivers userspace + filter: CONFIG_I2S_TEST_USE_GPIO_LOOPBACK + harness: ztest + harness_config: + fixture: gpio_loopback