rtio: Add default i2c submit handler

Use the RTIO work queue to fake the i2c submit calls for drivers which
haven't yet implemented the API. Applications can change the size of
the work queue pool depending on how much traffic they have on the buses.

Signed-off-by: Yuval Peress <peress@google.com>
This commit is contained in:
Yuval Peress 2024-07-01 01:43:41 -06:00 committed by Carles Cufí
commit 8974c248cf
60 changed files with 811 additions and 31 deletions

View file

@ -212,6 +212,20 @@ There is a small cost to each RTIO context and iodev. This cost could be weighed
against using a thread for each concurrent I/O operation or custom queues and
threads per peripheral. RTIO is much lower cost than that.
Supported Buses
***************
To check if your bus supports RTIO natively, you can check the driver API implementation, if the
driver implements the ``iodev_submit`` function of the bus API, then RTIO is supported. If the
driver doesn't support the RTIO APIs, it will set the submit function to
``i2c_iodev_submit_fallback``.
I2C buses have a default implementation which allows apps to leverage the RTIO work queue while
vendors implement the submit function. With this queue, any I2C bus driver that does not implement
the ``iodev_submit`` function will defer to a work item which will perform a blocking I2C
transaction. To change the pool size, set a different value to
:kconfig:option`CONFIG_RTIO_WORKQ_POOL_ITEMS`.
API Reference
*************

View file

@ -6,7 +6,10 @@ zephyr_library()
zephyr_library_sources(i2c_common.c)
zephyr_library_sources_ifdef(CONFIG_I2C_RTIO i2c_rtio.c)
zephyr_library_sources_ifdef(CONFIG_I2C_RTIO
i2c_rtio.c
i2c_rtio_default.c
)
zephyr_library_sources_ifdef(CONFIG_I2C_SHELL i2c_shell.c)
zephyr_library_sources_ifdef(CONFIG_I2C_BITBANG i2c_bitbang.c)
zephyr_library_sources_ifdef(CONFIG_I2C_TELINK_B91 i2c_b91.c)

View file

@ -54,16 +54,11 @@ config I2C_CALLBACK
help
API and implementations of i2c_transfer_cb.
config HAS_I2C_RTIO
bool
help
This option must be selected by I2C controller drivers that optionally implement the RTIO
interface.
config I2C_RTIO
bool "I2C RTIO API"
depends on HAS_I2C_RTIO
select EXPERIMENTAL
select RTIO
select RTIO_WORKQ
help
API and implementations of I2C for RTIO
@ -177,7 +172,6 @@ config I2C_MCUX_LPI2C
default y
depends on DT_HAS_NXP_IMX_LPI2C_ENABLED
depends on CLOCK_CONTROL
select HAS_I2C_RTIO
select PINCTRL
help
Enable the mcux LPI2C driver.

View file

@ -17,7 +17,6 @@ if I2C_NRFX
config I2C_NRFX_TWI
def_bool y
depends on DT_HAS_NORDIC_NRF_TWI_ENABLED
select HAS_I2C_RTIO
select NRFX_TWI0 if HAS_HW_NRF_TWI0
select NRFX_TWI1 if HAS_HW_NRF_TWI1

View file

@ -7,6 +7,5 @@ config I2C_SAM_TWIHS
bool "Atmel SAM (TWIHS) I2C driver"
default y
depends on DT_HAS_ATMEL_SAM_I2C_TWIHS_ENABLED
select HAS_I2C_RTIO
help
Enable Atmel SAM MCU Family (TWIHS) I2C bus driver.

View file

@ -67,6 +67,9 @@ static int gpio_i2c_switch_transfer(const struct device *dev, struct i2c_msg *ms
static const struct i2c_driver_api gpio_i2c_switch_api_funcs = {
.configure = gpio_i2c_switch_configure,
.transfer = gpio_i2c_switch_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static int gpio_i2c_switch_init(const struct device *dev)

View file

@ -275,6 +275,9 @@ end:
static const struct i2c_driver_api i2c_ambiq_driver_api = {
.configure = i2c_ambiq_configure,
.transfer = i2c_ambiq_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#ifdef CONFIG_PM_DEVICE

View file

@ -733,13 +733,14 @@ static void i2c_atciic100_irq_handler(void *arg)
}
static const struct i2c_driver_api i2c_atciic100_driver = {
.configure = (i2c_api_configure_t)i2c_atciic100_configure,
.transfer = (i2c_api_full_io_t)i2c_atciic100_transfer,
.configure = (i2c_api_configure_t)i2c_atciic100_configure,
.transfer = (i2c_api_full_io_t)i2c_atciic100_transfer,
#if defined(CONFIG_I2C_TARGET)
.target_register =
(i2c_api_target_register_t)i2c_atciic100_target_register,
.target_unregister =
(i2c_api_target_unregister_t)i2c_atciic100_target_unregister
.target_register = (i2c_api_target_register_t)i2c_atciic100_target_register,
.target_unregister = (i2c_api_target_unregister_t)i2c_atciic100_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};

View file

@ -150,6 +150,9 @@ static int i2c_b91_init(const struct device *dev)
static const struct i2c_driver_api i2c_b91_api = {
.configure = i2c_b91_configure,
.transfer = i2c_b91_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1,

View file

@ -923,6 +923,9 @@ static const struct i2c_driver_api iproc_i2c_driver_api = {
.target_register = iproc_i2c_target_register,
.target_unregister = iproc_i2c_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define IPROC_I2C_DEVICE_INIT(n) \

View file

@ -421,7 +421,10 @@ static int i2c_cc13xx_cc26xx_init(const struct device *dev)
static const struct i2c_driver_api i2c_cc13xx_cc26xx_driver_api = {
.configure = i2c_cc13xx_cc26xx_configure,
.transfer = i2c_cc13xx_cc26xx_transfer
.transfer = i2c_cc13xx_cc26xx_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
PINCTRL_DT_INST_DEFINE(0);

View file

@ -382,9 +382,11 @@ static int i2c_cc32xx_init(const struct device *dev)
static const struct i2c_driver_api i2c_cc32xx_driver_api = {
.configure = i2c_cc32xx_configure,
.transfer = i2c_cc32xx_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
PINCTRL_DT_INST_DEFINE(0);
static const struct i2c_cc32xx_config i2c_cc32xx_config = {

View file

@ -1023,6 +1023,9 @@ static const struct i2c_driver_api funcs = {
.target_register = i2c_dw_slave_register,
.target_unregister = i2c_dw_slave_unregister,
#endif /* CONFIG_I2C_TARGET */
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static int i2c_dw_initialize(const struct device *dev)

View file

@ -300,6 +300,9 @@ static const struct i2c_driver_api i2c_emul_api = {
.target_register = i2c_emul_target_register,
.target_unregister = i2c_emul_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define EMUL_LINK_AND_COMMA(node_id) \

View file

@ -297,6 +297,9 @@ static const struct i2c_driver_api i2c_kb1200_api = {
.configure = i2c_kb1200_configure,
.get_config = i2c_kb1200_get_config,
.transfer = i2c_kb1200_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define KB1200_FSMBM_DEV(inst) DEVICE_DT_INST_GET(inst),

View file

@ -730,7 +730,10 @@ static const struct i2c_driver_api i2c_esp32_driver_api = {
.configure = i2c_esp32_configure,
.get_config = i2c_esp32_get_config,
.transfer = i2c_esp32_transfer,
.recover_bus = i2c_esp32_recover
.recover_bus = i2c_esp32_recover,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static int IRAM_ATTR i2c_esp32_init(const struct device *dev)

View file

@ -644,6 +644,9 @@ error:
static const struct i2c_driver_api i2c_gd32_driver_api = {
.configure = i2c_gd32_configure,
.transfer = i2c_gd32_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static int i2c_gd32_init(const struct device *dev)

View file

@ -208,6 +208,9 @@ static const struct i2c_driver_api i2c_gecko_driver_api = {
.target_register = i2c_gecko_target_register,
.target_unregister = i2c_gecko_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#if defined(CONFIG_I2C_TARGET)

View file

@ -145,6 +145,9 @@ static const struct i2c_driver_api api = {
.get_config = i2c_gpio_get_config,
.transfer = i2c_gpio_transfer,
.recover_bus = i2c_gpio_recover_bus,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static int i2c_gpio_init(const struct device *dev)

View file

@ -497,7 +497,11 @@ static const struct i2c_driver_api i2c_cat1_driver_api = {
.transfer = ifx_cat1_i2c_transfer,
.get_config = ifx_cat1_i2c_get_config,
.target_register = ifx_cat1_i2c_target_register,
.target_unregister = ifx_cat1_i2c_target_unregister};
.target_unregister = ifx_cat1_i2c_target_unregister,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
/* Macros for I2C instance declaration */
#define INFINEON_CAT1_I2C_INIT(n) \

View file

@ -430,7 +430,11 @@ static const struct i2c_driver_api i2c_xmc4_driver_api = {
.transfer = ifx_xmc4_i2c_transfer,
.get_config = ifx_xmc4_i2c_get_config,
.target_register = ifx_xmc4_i2c_target_register,
.target_unregister = ifx_xmc4_i2c_target_unregister};
.target_unregister = ifx_xmc4_i2c_target_unregister,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
/* Macros for I2C instance declaration */
#define XMC4_IRQ_HANDLER_INIT(index) \

View file

@ -363,6 +363,9 @@ static int i2c_imx_init(const struct device *dev)
static const struct i2c_driver_api i2c_imx_driver_api = {
.configure = i2c_imx_configure,
.transfer = i2c_imx_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define I2C_IMX_INIT(n) \

View file

@ -1461,6 +1461,9 @@ static const struct i2c_driver_api i2c_enhance_driver_api = {
.target_register = i2c_enhance_target_register,
.target_unregister = i2c_enhance_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#ifdef CONFIG_I2C_TARGET

View file

@ -1255,6 +1255,9 @@ static const struct i2c_driver_api i2c_it8xxx2_driver_api = {
.get_config = i2c_it8xxx2_get_config,
.transfer = i2c_it8xxx2_transfer,
.recover_bus = i2c_it8xxx2_recover_bus,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE

View file

@ -141,6 +141,9 @@ static const struct i2c_driver_api i2c_litex_driver_api = {
.get_config = i2c_litex_get_config,
.transfer = i2c_litex_transfer,
.recover_bus = i2c_litex_recover_bus,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
/* Device Instantiation */

View file

@ -308,7 +308,6 @@ restore:
}
#endif /* CONFIG_I2C_STM32_BUS_RECOVERY */
static const struct i2c_driver_api api_funcs = {
.configure = i2c_stm32_runtime_configure,
.transfer = i2c_stm32_transfer,
@ -320,6 +319,9 @@ static const struct i2c_driver_api api_funcs = {
.target_register = i2c_stm32_target_register,
.target_unregister = i2c_stm32_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#ifdef CONFIG_PM_DEVICE

View file

@ -345,6 +345,9 @@ static const struct i2c_driver_api i2c_api = {
.transfer = lpc11u6x_i2c_transfer,
.target_register = lpc11u6x_i2c_slave_register,
.target_unregister = lpc11u6x_i2c_slave_unregister,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define LPC11U6X_I2C_INIT(idx) \

View file

@ -838,6 +838,9 @@ static const struct i2c_driver_api api = {
#ifdef CONFIG_I2C_TARGET
.target_register = api_target_register,
.target_unregister = api_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
.recover_bus = api_recover_bus,
};

View file

@ -235,6 +235,9 @@ static int mss_i2c_transfer(const struct device *dev, struct i2c_msg *msgs, uint
static const struct i2c_driver_api mss_i2c_driver_api = {
.configure = mss_i2c_configure,
.transfer = mss_i2c_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static void mss_i2c_reset(const struct device *dev)

View file

@ -836,6 +836,9 @@ static const struct i2c_driver_api i2c_xec_driver_api = {
.target_register = i2c_xec_target_register,
.target_unregister = i2c_xec_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static int i2c_xec_init(const struct device *dev)

View file

@ -1040,6 +1040,9 @@ static const struct i2c_driver_api i2c_xec_driver_api = {
.target_register = i2c_xec_target_register,
.target_unregister = i2c_xec_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static int i2c_xec_init(const struct device *dev)

View file

@ -348,6 +348,9 @@ static const struct i2c_driver_api i2c_mcux_driver_api = {
#ifdef CONFIG_I2C_CALLBACK
.transfer_cb = i2c_mcux_transfer_cb,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define I2C_DEVICE_INIT_MCUX(n) \

View file

@ -526,6 +526,9 @@ static const struct i2c_driver_api mcux_flexcomm_driver_api = {
.target_register = mcux_flexcomm_target_register,
.target_unregister = mcux_flexcomm_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define I2C_MCUX_FLEXCOMM_DEVICE(id) \

View file

@ -155,6 +155,9 @@ static int i2c_nios2_init(const struct device *dev);
static const struct i2c_driver_api i2c_nios2_driver_api = {
.configure = i2c_nios2_configure,
.transfer = i2c_nios2_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static struct i2c_nios2_data i2c_nios2_dev_data = {

View file

@ -209,6 +209,9 @@ static const struct i2c_driver_api i2c_port_npcx_driver_api = {
.target_register = i2c_npcx_target_register,
.target_unregister = i2c_npcx_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
/* I2C port init macro functions */

View file

@ -289,8 +289,11 @@ static int i2c_nrfx_twim_recover_bus(const struct device *dev)
}
static const struct i2c_driver_api i2c_nrfx_twim_driver_api = {
.configure = i2c_nrfx_twim_configure,
.transfer = i2c_nrfx_twim_transfer,
.configure = i2c_nrfx_twim_configure,
.transfer = i2c_nrfx_twim_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
.recover_bus = i2c_nrfx_twim_recover_bus,
};

View file

@ -739,6 +739,9 @@ static const struct i2c_driver_api i2c_numaker_driver_api = {
#ifdef CONFIG_I2C_TARGET
.target_register = i2c_numaker_slave_register,
.target_unregister = i2c_numaker_slave_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
.recover_bus = i2c_numaker_recover_bus,
};

View file

@ -347,6 +347,9 @@ static int i2c_rcar_init(const struct device *dev)
static const struct i2c_driver_api i2c_rcar_driver_api = {
.configure = i2c_rcar_configure,
.transfer = i2c_rcar_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
/* Device Instantiation */

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2024 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/i2c/rtio.h>
#include <zephyr/rtio/rtio.h>
#include <zephyr/rtio/work.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(i2c_rtio, CONFIG_I2C_LOG_LEVEL);
static int i2c_iodev_submit_rx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2],
uint8_t *num_msgs)
{
__ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_RX);
msgs[0].buf = iodev_sqe->sqe.rx.buf;
msgs[0].len = iodev_sqe->sqe.rx.buf_len;
msgs[0].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_READ;
*num_msgs = 1;
return 0;
}
static int i2c_iodev_submit_tx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2],
uint8_t *num_msgs)
{
__ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_TX);
msgs[0].buf = (uint8_t *)iodev_sqe->sqe.tx.buf;
msgs[0].len = iodev_sqe->sqe.tx.buf_len;
msgs[0].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_WRITE;
*num_msgs = 1;
return 0;
}
static int i2c_iodev_submit_tiny_tx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2],
uint8_t *num_msgs)
{
__ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_TINY_TX);
msgs[0].buf = (uint8_t *)iodev_sqe->sqe.tiny_tx.buf;
msgs[0].len = iodev_sqe->sqe.tiny_tx.buf_len;
msgs[0].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_WRITE;
*num_msgs = 1;
return 0;
}
static int i2c_iodev_submit_txrx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2],
uint8_t *num_msgs)
{
__ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_TXRX);
msgs[0].buf = (uint8_t *)iodev_sqe->sqe.txrx.tx_buf;
msgs[0].len = iodev_sqe->sqe.txrx.buf_len;
msgs[0].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_WRITE;
msgs[1].buf = iodev_sqe->sqe.txrx.rx_buf;
msgs[1].len = iodev_sqe->sqe.txrx.buf_len;
msgs[1].flags =
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) |
((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) |
I2C_MSG_READ;
*num_msgs = 2;
return 0;
}
void i2c_iodev_submit_work_handler(struct rtio_iodev_sqe *iodev_sqe)
{
const struct i2c_dt_spec *dt_spec = (const struct i2c_dt_spec *)iodev_sqe->sqe.iodev->data;
const struct device *dev = dt_spec->bus;
LOG_DBG("Sync RTIO work item for: %p", (void *)iodev_sqe);
struct rtio_iodev_sqe *transaction_current = iodev_sqe;
struct i2c_msg msgs[2];
uint8_t num_msgs;
int rc = 0;
do {
/* Convert the iodev_sqe back to an i2c_msg */
switch (transaction_current->sqe.op) {
case RTIO_OP_RX:
rc = i2c_iodev_submit_rx(transaction_current, msgs, &num_msgs);
break;
case RTIO_OP_TX:
rc = i2c_iodev_submit_tx(transaction_current, msgs, &num_msgs);
break;
case RTIO_OP_TINY_TX:
rc = i2c_iodev_submit_tiny_tx(transaction_current, msgs, &num_msgs);
break;
case RTIO_OP_TXRX:
rc = i2c_iodev_submit_txrx(transaction_current, msgs, &num_msgs);
break;
default:
LOG_ERR("Invalid op code %d for submission %p", transaction_current->sqe.op,
(void *)&transaction_current->sqe);
rc = -EIO;
break;
}
if (rc == 0) {
__ASSERT_NO_MSG(num_msgs > 0);
rc = i2c_transfer(dev, msgs, num_msgs, dt_spec->addr);
transaction_current = rtio_txn_next(transaction_current);
}
} while (rc == 0 && transaction_current != NULL);
if (rc != 0) {
rtio_iodev_sqe_err(iodev_sqe, rc);
} else {
rtio_iodev_sqe_ok(iodev_sqe, 0);
}
}
void i2c_iodev_submit_fallback(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
{
LOG_DBG("Executing fallback for dev: %p, sqe: %p", (void *)dev, (void *)iodev_sqe);
struct rtio_work_req *req = rtio_work_req_alloc();
if (req == NULL) {
rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
return;
}
rtio_work_req_submit(req, iodev_sqe, i2c_iodev_submit_work_handler);
}

View file

@ -257,7 +257,10 @@ static int rv32m1_lpi2c_init(const struct device *dev)
static const struct i2c_driver_api rv32m1_lpi2c_driver_api = {
.configure = rv32m1_lpi2c_configure,
.transfer = rv32m1_lpi2c_transfer,
.transfer = rv32m1_lpi2c_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define RV32M1_LPI2C_DEVICE(id) \

View file

@ -761,10 +761,12 @@ static int i2c_sam0_initialize(const struct device *dev)
return 0;
}
static const struct i2c_driver_api i2c_sam0_driver_api = {
.configure = i2c_sam0_configure,
.transfer = i2c_sam0_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#ifdef CONFIG_I2C_SAM0_DMA_DRIVEN

View file

@ -591,6 +591,9 @@ static int i2c_sam_twim_initialize(const struct device *dev)
static const struct i2c_driver_api i2c_sam_twim_driver_api = {
.configure = i2c_sam_twim_configure,
.transfer = i2c_sam_twim_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define I2C_TWIM_SAM_SLEW_REGS(n) \

View file

@ -353,6 +353,9 @@ static int i2c_sam_twi_initialize(const struct device *dev)
static const struct i2c_driver_api i2c_sam_twi_driver_api = {
.configure = i2c_sam_twi_configure,
.transfer = i2c_sam_twi_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define I2C_TWI_SAM_INIT(n) \

View file

@ -118,6 +118,9 @@ static const struct i2c_driver_api api = {
.get_config = i2c_sbcon_get_config,
.transfer = i2c_sbcon_transfer,
.recover_bus = i2c_sbcon_recover_bus,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static int i2c_sbcon_init(const struct device *dev)

View file

@ -324,6 +324,9 @@ static const struct i2c_driver_api i2c_sc18im_driver_api = {
.configure = i2c_sc18im_configure,
.get_config = i2c_sc18im_get_config,
.transfer = i2c_sc18im_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define I2C_SC18IM_DEFINE(n) \

View file

@ -114,7 +114,10 @@ static int i2c_sedi_api_full_io(const struct device *dev, struct i2c_msg *msgs,
static const struct i2c_driver_api i2c_sedi_apis = {
.configure = i2c_sedi_api_configure,
.transfer = i2c_sedi_api_full_io
.transfer = i2c_sedi_api_full_io,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#ifdef CONFIG_PM_DEVICE

View file

@ -316,10 +316,12 @@ static int i2c_sifive_init(const struct device *dev)
return 0;
}
static const struct i2c_driver_api i2c_sifive_api = {
.configure = i2c_sifive_configure,
.transfer = i2c_sifive_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
/* Device instantiation */

View file

@ -557,6 +557,9 @@ static const struct i2c_driver_api i2c_smartbond_driver_api = {
#ifdef CONFIG_I2C_CALLBACK
.transfer_cb = i2c_smartbond_transfer_cb,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static int i2c_smartbond_resume(const struct device *dev)

View file

@ -154,6 +154,9 @@ static int tca954x_channel_init(const struct device *dev)
static const struct i2c_driver_api tca954x_api_funcs = {
.configure = tca954x_configure,
.transfer = tca954x_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
BUILD_ASSERT(CONFIG_I2C_TCA954X_CHANNEL_INIT_PRIO > CONFIG_I2C_TCA954X_ROOT_INIT_PRIO,

View file

@ -627,6 +627,9 @@ static const struct i2c_driver_api i2c_xilinx_axi_driver_api = {
.target_register = i2c_xilinx_axi_target_register,
.target_unregister = i2c_xilinx_axi_target_unregister,
#endif
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
#define I2C_XILINX_AXI_INIT(n, compat) \

View file

@ -1008,6 +1008,18 @@ static inline int i2c_transfer_signal(const struct device *dev,
#if defined(CONFIG_I2C_RTIO) || defined(__DOXYGEN__)
/**
* @brief Fallback submit implementation
*
* This implementation will schedule a blocking I2C transaction on the bus via the RTIO work
* queue. It is only used if the I2C driver did not implement the iodev_submit function.
*
* @param dev Pointer to the device structure for an I2C controller driver.
* @param iodev_sqe Prepared submissions queue entry connected to an iodev
* defined by I2C_DT_IODEV_DEFINE.
*/
void i2c_iodev_submit_fallback(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe);
/**
* @brief Submit request(s) to an I2C device with RTIO
*
@ -1020,6 +1032,10 @@ static inline void i2c_iodev_submit(struct rtio_iodev_sqe *iodev_sqe)
const struct device *dev = dt_spec->bus;
const struct i2c_driver_api *api = (const struct i2c_driver_api *)dev->api;
if (api->iodev_submit == NULL) {
rtio_iodev_sqe_err(iodev_sqe, -ENOSYS);
return;
}
api->iodev_submit(dt_spec->bus, iodev_sqe);
}

View file

@ -14,9 +14,10 @@ tests:
- drivers
- i2c
drivers.i2c.ram.rtio:
filter: CONFIG_HAS_I2C_RTIO
filter: CONFIG_I2C_RTIO
extra_configs:
- CONFIG_I2C_RTIO=y
- CONFIG_ZTEST_THREAD_PRIORITY=2
drivers.i2c.ram.pm:
filter: CONFIG_HAS_PM
extra_configs:
@ -24,9 +25,10 @@ tests:
- CONFIG_PM_DEVICE=y
- CONFIG_PM_DEVICE_RUNTIME=y
drivers.i2c.ram.pm.rtio:
filter: CONFIG_HAS_PM and CONFIG_HAS_I2C_RTIO
filter: CONFIG_HAS_PM and CONFIG_I2C_RTIO
extra_configs:
- CONFIG_PM=y
- CONFIG_PM_DEVICE=y
- CONFIG_PM_DEVICE_RUNTIME=y
- CONFIG_I2C_RTIO=y
- CONFIG_ZTEST_THREAD_PRIORITY=2

View file

@ -0,0 +1,13 @@
# Copyright (c) 2024 Google LLC
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(rtio_i2c_test)
target_sources(app PRIVATE
src/blocking_emul.cpp
src/main.cpp
)
target_include_directories(app PRIVATE include)

View file

@ -0,0 +1,10 @@
/* Copyright (c) 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
&i2c0 {
blocking_emul: blocking-emul@80 {
compatible = "zephyr,blocking-emul";
reg = <0x80>;
};
};

View file

@ -0,0 +1,9 @@
# Copyright (c) 2024 Google LLC
# SPDX-License-Identifier: Apache-2.0
description: |
A generic blocking target device used for testing async RTIO APIs
compatible: "zephyr,blocking-emul"
include: i2c-device.yaml

View file

@ -0,0 +1,32 @@
/* Copyright (c) 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <functional>
/*
* We need to override fff's function signature away from a standard function pointer.
* By using std::function<> we're able to leverage capture variables since additional storage must
* be allocated for them. In the tests, we will include asserts in the lambda functions. This
* provides the ability to assert inside the lambda and get errors that map to a line within the
* test function. If we use plain function pointers, we have to move the expected value outside
* of the test function and make it a static global of the compilational unit.
*
* Example:
* @code
* const uint8_t expected_value = 40;
* my_function_fake.custom_fake = [&expected_value](uint8_t value) {
* zassert_equal(expected_value, value);
* };
* my_function(expected_value);
* @endcode
*/
#define CUSTOM_FFF_FUNCTION_TEMPLATE(RETURN, FUNCNAME, ...) \
std::function<RETURN(__VA_ARGS__)> FUNCNAME
#include <zephyr/fff.h>
DECLARE_FAKE_VALUE_FUNC(int, blocking_emul_i2c_transfer, const struct emul *, struct i2c_msg *, int,
int);

View file

@ -0,0 +1,24 @@
# Copyright (c) 2024 Google LLC
# SPDX-License-Identifier: Apache-2.0
# Features being tested
CONFIG_LOG=y
CONFIG_RTIO=y
CONFIG_I2C=y
CONFIG_I2C_RTIO=y
# Testing
CONFIG_ZTEST=y
CONFIG_EMUL=y
# The number of pending requests allowed
CONFIG_RTIO_WORKQ_POOL_ITEMS=2
# Used for debugging
CONFIG_I2C_LOG_LEVEL_DBG=y
CONFIG_I2C_DUMP_MESSAGES=y
# Language features for capturing lambdas
CONFIG_CPP=y
CONFIG_STD_CPP17=y
CONFIG_REQUIRES_FULL_LIBCPP=y

View file

@ -0,0 +1,38 @@
/* Copyright (c) 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
#include "blocking_emul.hpp"
#include <zephyr/device.h>
#include <zephyr/drivers/emul.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/i2c_emul.h>
#include <zephyr/logging/log.h>
#define DT_DRV_COMPAT zephyr_blocking_emul
LOG_MODULE_REGISTER(blocking_emul, CONFIG_I2C_LOG_LEVEL);
DEFINE_FAKE_VALUE_FUNC(int, blocking_emul_i2c_transfer, const struct emul *, struct i2c_msg *, int,
int);
static struct i2c_emul_api blocking_emul_i2c_api = {
.transfer = blocking_emul_i2c_transfer,
};
static int blocking_emul_init(const struct emul *target, const struct device *parent)
{
return 0;
}
static int blocking_dev_init(const struct device *dev)
{
return 0;
}
#define DECLARE_DRV(n) \
EMUL_DT_INST_DEFINE(n, blocking_emul_init, NULL, NULL, &blocking_emul_i2c_api, NULL); \
DEVICE_DT_INST_DEFINE(n, blocking_dev_init, NULL, NULL, NULL, POST_KERNEL, 99, NULL);
DT_INST_FOREACH_STATUS_OKAY(DECLARE_DRV)

View file

@ -0,0 +1,347 @@
/* Copyright (c) 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
#include "blocking_emul.hpp"
#include <zephyr/drivers/i2c.h>
#include <zephyr/fff.h>
#include <zephyr/rtio/rtio.h>
#include <zephyr/ztest.h>
DEFINE_FFF_GLOBALS;
static const struct device *i2c_dev = DEVICE_DT_GET(DT_NODELABEL(i2c0));
I2C_DT_IODEV_DEFINE(blocking_emul_iodev, DT_NODELABEL(blocking_emul));
RTIO_DEFINE(test_rtio_ctx, 4, 4);
static void rtio_i2c_before(void *fixture)
{
ARG_UNUSED(fixture);
RESET_FAKE(blocking_emul_i2c_transfer);
FFF_RESET_HISTORY();
rtio_sqe_drop_all(&test_rtio_ctx);
struct rtio_cqe *cqe;
while ((cqe = rtio_cqe_consume(&test_rtio_ctx)) != NULL) {
rtio_cqe_release(&test_rtio_ctx, cqe);
}
}
ZTEST_SUITE(rtio_i2c, NULL, NULL, rtio_i2c_before, NULL, NULL);
ZTEST(rtio_i2c, test_emulated_api_uses_fallback_submit)
{
zassert_not_null(i2c_dev->api);
zassert_equal_ptr(i2c_iodev_submit_fallback,
((const struct i2c_driver_api *)i2c_dev->api)->iodev_submit);
}
ZTEST(rtio_i2c, test_fallback_submit_tx)
{
uint8_t data[] = {0x01, 0x02, 0x03};
struct i2c_msg msg = {
.buf = data,
.len = ARRAY_SIZE(data),
.flags = I2C_MSG_WRITE | I2C_MSG_STOP,
};
blocking_emul_i2c_transfer_fake.custom_fake =
[&msg](const struct emul *, struct i2c_msg *msgs, int msg_count, int) {
zassert_equal(1, msg_count);
zassert_equal(msg.len, msgs[0].len);
zassert_mem_equal(msg.buf, msgs[0].buf, msg.len);
zassert_equal(msg.flags, msgs[0].flags);
return 0;
};
struct rtio_sqe *sqe = i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg, 1);
zassert_not_null(sqe);
zassert_ok(rtio_submit(&test_rtio_ctx, 1));
zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count);
struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx);
zassert_ok(cqe->result);
rtio_cqe_release(&test_rtio_ctx, cqe);
}
ZTEST(rtio_i2c, test_fallback_submit_invalid_op)
{
struct rtio_sqe *sqe = rtio_sqe_acquire(&test_rtio_ctx);
zassert_not_null(sqe);
sqe->op = UINT8_MAX;
sqe->prio = RTIO_PRIO_NORM;
sqe->iodev = &blocking_emul_iodev;
sqe->userdata = NULL;
zassert_ok(rtio_submit(&test_rtio_ctx, 1));
zassert_equal(0, blocking_emul_i2c_transfer_fake.call_count);
struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx);
zassert_equal(-EIO, cqe->result);
rtio_cqe_release(&test_rtio_ctx, cqe);
}
ZTEST(rtio_i2c, test_fallback_submit_tiny_tx)
{
uint8_t data[] = {0x01, 0x02, 0x03};
struct rtio_sqe *sqe = rtio_sqe_acquire(&test_rtio_ctx);
blocking_emul_i2c_transfer_fake.custom_fake =
[&data](const struct emul *, struct i2c_msg *msgs, int msg_count, int) {
zassert_equal(1, msg_count);
zassert_equal(ARRAY_SIZE(data), msgs[0].len);
zassert_mem_equal(data, msgs[0].buf, msgs[0].len);
zassert_equal(I2C_MSG_WRITE | I2C_MSG_STOP, msgs[0].flags);
return 0;
};
zassert_not_null(sqe);
rtio_sqe_prep_tiny_write(sqe, &blocking_emul_iodev, RTIO_PRIO_NORM, data, ARRAY_SIZE(data),
NULL);
zassert_ok(rtio_submit(&test_rtio_ctx, 1));
zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count);
struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx);
zassert_ok(cqe->result);
rtio_cqe_release(&test_rtio_ctx, cqe);
}
ZTEST(rtio_i2c, test_fallback_submit_txrx)
{
uint8_t tx_data[] = {0x01, 0x02, 0x03};
uint8_t rx_data[ARRAY_SIZE(tx_data)] = {0};
struct rtio_sqe *sqe = rtio_sqe_acquire(&test_rtio_ctx);
blocking_emul_i2c_transfer_fake.custom_fake =
[&tx_data](const struct emul *, struct i2c_msg *msgs, int msg_count, int) {
zassert_equal(2, msg_count);
// First message should be a 'tx'
zassert_equal(ARRAY_SIZE(tx_data), msgs[0].len);
zassert_mem_equal(tx_data, msgs[0].buf, msgs[0].len);
zassert_equal(I2C_MSG_WRITE, msgs[0].flags);
// Second message should be an 'rx'
zassert_equal(ARRAY_SIZE(tx_data), msgs[1].len);
zassert_equal(I2C_MSG_READ | I2C_MSG_STOP, msgs[1].flags);
for (uint8_t i = 0; i < msgs[1].len; ++i) {
msgs[1].buf[i] = msgs[0].buf[i];
}
return 0;
};
zassert_not_null(sqe);
rtio_sqe_prep_transceive(sqe, &blocking_emul_iodev, RTIO_PRIO_NORM, tx_data, rx_data,
ARRAY_SIZE(tx_data), NULL);
zassert_ok(rtio_submit(&test_rtio_ctx, 1));
zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count);
struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx);
zassert_ok(cqe->result);
zassert_mem_equal(tx_data, rx_data, ARRAY_SIZE(tx_data));
rtio_cqe_release(&test_rtio_ctx, cqe);
}
ZTEST(rtio_i2c, test_fallback_submit_rx)
{
uint8_t expected_buffer[] = {0x00, 0x01, 0x02};
uint8_t buffer[ARRAY_SIZE(expected_buffer)] = {0};
struct i2c_msg msg = {
.buf = buffer,
.len = ARRAY_SIZE(buffer),
.flags = I2C_MSG_READ | I2C_MSG_STOP,
};
blocking_emul_i2c_transfer_fake.custom_fake =
[&msg](const struct emul *, struct i2c_msg *msgs, int msg_count, int) {
zassert_equal(1, msg_count);
zassert_equal(msg.len, msgs[0].len);
zassert_equal(msg.flags, msgs[0].flags);
for (uint8_t i = 0; i < msg.len; ++i) {
msgs[0].buf[i] = i;
}
return 0;
};
struct rtio_sqe *sqe = i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg, 1);
zassert_not_null(sqe);
zassert_ok(rtio_submit(&test_rtio_ctx, 1));
zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count);
struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx);
zassert_ok(cqe->result);
zassert_mem_equal(buffer, expected_buffer, ARRAY_SIZE(expected_buffer));
rtio_cqe_release(&test_rtio_ctx, cqe);
}
ZTEST(rtio_i2c, test_fallback_transaction_error)
{
uint8_t buffer[3];
struct rtio_sqe *phase1 = rtio_sqe_acquire(&test_rtio_ctx);
struct rtio_sqe *phase2 = rtio_sqe_acquire(&test_rtio_ctx);
blocking_emul_i2c_transfer_fake.return_val = -EIO;
zassert_not_null(phase1);
zassert_not_null(phase2);
rtio_sqe_prep_read(phase1, &blocking_emul_iodev, RTIO_PRIO_NORM, buffer, ARRAY_SIZE(buffer),
NULL);
rtio_sqe_prep_read(phase2, &blocking_emul_iodev, RTIO_PRIO_NORM, buffer, ARRAY_SIZE(buffer),
NULL);
phase1->flags |= RTIO_SQE_TRANSACTION;
zassert_ok(rtio_submit(&test_rtio_ctx, 2));
zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count);
struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx);
zassert_equal(-EIO, cqe->result);
rtio_cqe_release(&test_rtio_ctx, cqe);
// We have another CQE for the transaction that must be cleared out.
cqe = rtio_cqe_consume_block(&test_rtio_ctx);
rtio_cqe_release(&test_rtio_ctx, cqe);
}
ZTEST(rtio_i2c, test_fallback_transaction)
{
uint8_t buffer[3];
struct rtio_sqe *phase1 = rtio_sqe_acquire(&test_rtio_ctx);
struct rtio_sqe *phase2 = rtio_sqe_acquire(&test_rtio_ctx);
zassert_not_null(phase1);
zassert_not_null(phase2);
rtio_sqe_prep_read(phase1, &blocking_emul_iodev, RTIO_PRIO_NORM, buffer, ARRAY_SIZE(buffer),
NULL);
rtio_sqe_prep_read(phase2, &blocking_emul_iodev, RTIO_PRIO_NORM, buffer, ARRAY_SIZE(buffer),
NULL);
phase1->flags |= RTIO_SQE_TRANSACTION;
zassert_ok(rtio_submit(&test_rtio_ctx, 2));
zassert_equal(2, blocking_emul_i2c_transfer_fake.call_count);
struct rtio_cqe *cqe;
// Check the first part of the transaction.
cqe = rtio_cqe_consume_block(&test_rtio_ctx);
zassert_ok(cqe->result);
rtio_cqe_release(&test_rtio_ctx, cqe);
// We have another CQE for the transaction that must be cleared out.
cqe = rtio_cqe_consume_block(&test_rtio_ctx);
zassert_ok(cqe->result);
rtio_cqe_release(&test_rtio_ctx, cqe);
}
ZTEST(rtio_i2c, test_work_queue_overflow)
{
BUILD_ASSERT(CONFIG_RTIO_WORKQ_POOL_ITEMS == 2);
uint8_t data[][2] = {
{0x01, 0x02},
{0x03, 0x04},
{0x05, 0x06},
};
struct i2c_msg msg[] = {
{
.buf = data[0],
.len = 2,
.flags = I2C_MSG_WRITE | I2C_MSG_STOP,
},
{
.buf = data[1],
.len = 2,
.flags = I2C_MSG_READ | I2C_MSG_STOP,
},
{
.buf = data[2],
.len = 2,
.flags = I2C_MSG_READ | I2C_MSG_ADDR_10_BITS | I2C_MSG_STOP,
},
};
BUILD_ASSERT(ARRAY_SIZE(data) == ARRAY_SIZE(msg));
blocking_emul_i2c_transfer_fake.custom_fake =
[&msg](const struct emul *, struct i2c_msg *msgs, int msg_count, int) {
zassert_equal(1, msg_count);
int msg_idx = i2c_is_read_op(&msgs[0]) ? 1 : 0;
zassert_equal(msg[msg_idx].len, msgs[0].len);
zassert_mem_equal(msg[msg_idx].buf, msgs[0].buf, msg[msg_idx].len,
"Expected [0x%02x, 0x%02x] but got [0x%02x, 0x%02x]",
msg[msg_idx].buf[0], msg[msg_idx].buf[1], msgs[0].buf[0],
msgs[0].buf[1]);
zassert_equal(msg[msg_idx].flags, msgs[0].flags);
return 0;
};
struct rtio_sqe *sqe_write =
i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg[0], 1);
struct rtio_sqe *sqe_read = i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg[1], 1);
struct rtio_sqe *sqe_dropped =
i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg[2], 1);
zassert_not_null(sqe_write);
zassert_not_null(sqe_read);
zassert_not_null(sqe_dropped);
/* Add userdata so we can match these up with the CQEs */
sqe_write->userdata = &msg[0];
sqe_read->userdata = &msg[1];
sqe_dropped->userdata = &msg[2];
zassert_ok(rtio_submit(&test_rtio_ctx, 3));
zassert_equal(2, blocking_emul_i2c_transfer_fake.call_count);
struct rtio_cqe *cqe[] = {
rtio_cqe_consume_block(&test_rtio_ctx),
rtio_cqe_consume_block(&test_rtio_ctx),
rtio_cqe_consume_block(&test_rtio_ctx),
};
/*
* We need to make sure that we got back results for all 3 messages and that there are no
* duplicates
*/
uint8_t msg_seen_mask = 0;
for (unsigned int i = 0; i < ARRAY_SIZE(cqe); ++i) {
int msg_idx = (struct i2c_msg *)cqe[i]->userdata - msg;
zassert_true(msg_idx >= 0 && msg_idx < 3);
msg_seen_mask |= BIT(msg_idx);
if (msg_idx == 0 || msg_idx == 1) {
/* Expect the first 2 to succeed */
zassert_ok(cqe[i]->result);
} else {
zassert_equal(-ENOMEM, cqe[i]->result);
}
}
/* Make sure bits 0, 1, and 2 were set. */
zassert_equal(0x7, msg_seen_mask);
rtio_cqe_release(&test_rtio_ctx, cqe[0]);
rtio_cqe_release(&test_rtio_ctx, cqe[1]);
rtio_cqe_release(&test_rtio_ctx, cqe[2]);
}

View file

@ -0,0 +1,7 @@
# Copyright (c) 2024 Google LLC
# SPDX-License-Identifier: Apache-2.0
tests:
rtio.i2c:
platform_allow: native_sim
tags: rtio