i2c_emul: Add support for CONFIG_I2C_TARGET_BUFFER_MODE
Add emulation and test to support the buffered target mode. Signed-off-by: Yuval Peress <peress@google.com>
This commit is contained in:
parent
81f163e6fb
commit
690134356c
11 changed files with 334 additions and 38 deletions
|
@ -150,7 +150,7 @@ I2C Emulation features
|
||||||
In the binding of the I2C emulated bus, there's a custom property for address
|
In the binding of the I2C emulated bus, there's a custom property for address
|
||||||
based forwarding. Given the following devicetree node:
|
based forwarding. Given the following devicetree node:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block:: devicetree
|
||||||
|
|
||||||
i2c0: i2c@100 {
|
i2c0: i2c@100 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
@ -170,7 +170,7 @@ same image.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The ``#forward-cells`` attribute should always be 1. Each entry in the
|
The ``#forward-cells`` attribute should always be 1. Each entry in the
|
||||||
``fowards`` attribute consists of the phandle followed by the address. In
|
``forwards`` attribute consists of the phandle followed by the address. In
|
||||||
the example above, ``<&i2c1 0x20>`` will forward all read/write operations
|
the example above, ``<&i2c1 0x20>`` will forward all read/write operations
|
||||||
made to ``i2c0`` at port ``0x20`` to ``i2c1`` on the same port. Since no
|
made to ``i2c0`` at port ``0x20`` to ``i2c1`` on the same port. Since no
|
||||||
additional cells are used by the emulated controller, the number of cells
|
additional cells are used by the emulated controller, the number of cells
|
||||||
|
|
|
@ -36,6 +36,7 @@ struct i2c_emul_data {
|
||||||
|
|
||||||
struct i2c_emul_config {
|
struct i2c_emul_config {
|
||||||
struct emul_list_for_bus emul_list;
|
struct emul_list_for_bus emul_list;
|
||||||
|
bool target_buffered_mode;
|
||||||
const struct i2c_dt_spec *forward_list;
|
const struct i2c_dt_spec *forward_list;
|
||||||
uint16_t forward_list_size;
|
uint16_t forward_list_size;
|
||||||
};
|
};
|
||||||
|
@ -89,6 +90,41 @@ static int i2c_emul_send_to_target(const struct device *dev, struct i2c_msg *msg
|
||||||
struct i2c_emul_data *data = dev->data;
|
struct i2c_emul_data *data = dev->data;
|
||||||
const struct i2c_target_callbacks *callbacks = data->target_cfg->callbacks;
|
const struct i2c_target_callbacks *callbacks = data->target_cfg->callbacks;
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_TARGET_BUFFER_MODE
|
||||||
|
const struct i2c_emul_config *config = dev->config;
|
||||||
|
|
||||||
|
if (config->target_buffered_mode) {
|
||||||
|
for (uint8_t i = 0; i < num_msgs; ++i) {
|
||||||
|
if (i2c_is_read_op(&msgs[i])) {
|
||||||
|
uint8_t *ptr = NULL;
|
||||||
|
uint32_t len;
|
||||||
|
int rc =
|
||||||
|
callbacks->buf_read_requested(data->target_cfg, &ptr, &len);
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
if (len > msgs[i].len) {
|
||||||
|
LOG_ERR("buf_read_requested returned too many bytes");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
memcpy(msgs[i].buf, ptr, len);
|
||||||
|
} else {
|
||||||
|
callbacks->buf_write_received(data->target_cfg, msgs[i].buf,
|
||||||
|
msgs[i].len);
|
||||||
|
}
|
||||||
|
if (i2c_is_stop_op(&msgs[i])) {
|
||||||
|
int rc = callbacks->stop(data->target_cfg);
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_I2C_TARGET_BUFFER_MODE */
|
||||||
|
|
||||||
for (uint8_t i = 0; i < num_msgs; ++i) {
|
for (uint8_t i = 0; i < num_msgs; ++i) {
|
||||||
LOG_DBG(" msgs[%u].flags? 0x%02x", i, msgs[i].flags);
|
LOG_DBG(" msgs[%u].flags? 0x%02x", i, msgs[i].flags);
|
||||||
if (i2c_is_read_op(&msgs[i])) {
|
if (i2c_is_read_op(&msgs[i])) {
|
||||||
|
@ -289,6 +325,7 @@ static const struct i2c_driver_api i2c_emul_api = {
|
||||||
.children = emuls_##n, \
|
.children = emuls_##n, \
|
||||||
.num_children = ARRAY_SIZE(emuls_##n), \
|
.num_children = ARRAY_SIZE(emuls_##n), \
|
||||||
}, \
|
}, \
|
||||||
|
.target_buffered_mode = DT_INST_PROP(n, target_buffered_mode), \
|
||||||
.forward_list = emul_forward_list_##n, \
|
.forward_list = emul_forward_list_##n, \
|
||||||
.forward_list_size = ARRAY_SIZE(emul_forward_list_##n), \
|
.forward_list_size = ARRAY_SIZE(emul_forward_list_##n), \
|
||||||
}; \
|
}; \
|
||||||
|
|
|
@ -10,6 +10,12 @@ include: i2c-controller.yaml
|
||||||
properties:
|
properties:
|
||||||
reg:
|
reg:
|
||||||
required: true
|
required: true
|
||||||
|
target-buffered-mode:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
This option is used when the I2C target is enabled and it can support
|
||||||
|
buffered mode for I2C target transfer. When 'false', the target will use
|
||||||
|
PIO (Programmed I/O) mode.
|
||||||
forwards:
|
forwards:
|
||||||
type: phandle-array
|
type: phandle-array
|
||||||
description: |
|
description: |
|
||||||
|
|
|
@ -7,6 +7,11 @@ project(i2c_emul)
|
||||||
|
|
||||||
target_sources(app PRIVATE
|
target_sources(app PRIVATE
|
||||||
src/emulated_target.cpp
|
src/emulated_target.cpp
|
||||||
src/test_forwarding.cpp
|
src/test_fowarding_common.cpp
|
||||||
)
|
)
|
||||||
|
if(CONFIG_I2C_TARGET_BUFFER_MODE)
|
||||||
|
target_sources(app PRIVATE src/test_forwarding_buf.cpp)
|
||||||
|
else()
|
||||||
|
target_sources(app PRIVATE src/test_forwarding_pio.cpp)
|
||||||
|
endif()
|
||||||
target_include_directories(app PRIVATE include)
|
target_include_directories(app PRIVATE include)
|
||||||
|
|
8
tests/drivers/i2c/i2c_emul/boards/native_sim.buf.overlay
Normal file
8
tests/drivers/i2c/i2c_emul/boards/native_sim.buf.overlay
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
&i2c1 {
|
||||||
|
target-buffered-mode;
|
||||||
|
};
|
|
@ -31,7 +31,11 @@ extern struct i2c_target_config emulated_target_config[FORWARD_COUNT];
|
||||||
DECLARE_FAKE_VALUE_FUNC(int, target_write_requested_##n, struct i2c_target_config *); \
|
DECLARE_FAKE_VALUE_FUNC(int, target_write_requested_##n, struct i2c_target_config *); \
|
||||||
DECLARE_FAKE_VALUE_FUNC(int, target_write_received_##n, struct i2c_target_config *, \
|
DECLARE_FAKE_VALUE_FUNC(int, target_write_received_##n, struct i2c_target_config *, \
|
||||||
uint8_t); \
|
uint8_t); \
|
||||||
DECLARE_FAKE_VALUE_FUNC(int, target_stop_##n, struct i2c_target_config *);
|
DECLARE_FAKE_VALUE_FUNC(int, target_stop_##n, struct i2c_target_config *); \
|
||||||
|
DECLARE_FAKE_VALUE_FUNC(int, target_buf_read_requested_##n, struct i2c_target_config *, \
|
||||||
|
uint8_t **, uint32_t *) \
|
||||||
|
DECLARE_FAKE_VOID_FUNC(target_buf_write_received_##n, struct i2c_target_config *, \
|
||||||
|
uint8_t *, uint32_t)
|
||||||
|
|
||||||
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DECLARE_FAKE_TARGET_FUNCTIONS)
|
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DECLARE_FAKE_TARGET_FUNCTIONS)
|
||||||
|
|
||||||
|
@ -42,6 +46,8 @@ DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DECLARE_FAKE_TARGET_FUNCTIONS)
|
||||||
fn(target_write_requested_##n); \
|
fn(target_write_requested_##n); \
|
||||||
fn(target_write_received_##n); \
|
fn(target_write_received_##n); \
|
||||||
fn(target_stop_##n); \
|
fn(target_stop_##n); \
|
||||||
|
fn(target_buf_read_requested_##n); \
|
||||||
|
fn(target_buf_write_received_##n); \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
#define FFF_FAKES_LIST_FOREACH(fn) \
|
#define FFF_FAKES_LIST_FOREACH(fn) \
|
||||||
|
|
|
@ -15,18 +15,28 @@ DEFINE_FFF_GLOBALS;
|
||||||
DEFINE_FAKE_VALUE_FUNC(int, target_write_requested_##n, struct i2c_target_config *); \
|
DEFINE_FAKE_VALUE_FUNC(int, target_write_requested_##n, struct i2c_target_config *); \
|
||||||
DEFINE_FAKE_VALUE_FUNC(int, target_write_received_##n, struct i2c_target_config *, \
|
DEFINE_FAKE_VALUE_FUNC(int, target_write_received_##n, struct i2c_target_config *, \
|
||||||
uint8_t); \
|
uint8_t); \
|
||||||
DEFINE_FAKE_VALUE_FUNC(int, target_stop_##n, struct i2c_target_config *);
|
DEFINE_FAKE_VALUE_FUNC(int, target_stop_##n, struct i2c_target_config *); \
|
||||||
|
DEFINE_FAKE_VALUE_FUNC(int, target_buf_read_requested_##n, struct i2c_target_config *, \
|
||||||
|
uint8_t **, uint32_t *) \
|
||||||
|
DEFINE_FAKE_VOID_FUNC(target_buf_write_received_##n, struct i2c_target_config *, \
|
||||||
|
uint8_t *, uint32_t)
|
||||||
|
|
||||||
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DEFINE_FAKE_TARGET_FUNCTION);
|
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DEFINE_FAKE_TARGET_FUNCTION);
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
#define DEFINE_EMULATED_CALLBACK(node_id, prop, n) \
|
#define DEFINE_EMULATED_CALLBACK(node_id, prop, n) \
|
||||||
[n] = { \
|
[n] = { \
|
||||||
.write_requested = target_write_requested_##n, \
|
.write_requested = target_write_requested_##n, \
|
||||||
.read_requested = target_read_requested_##n, \
|
.read_requested = target_read_requested_##n, \
|
||||||
.write_received = target_write_received_##n, \
|
.write_received = target_write_received_##n, \
|
||||||
.read_processed = target_read_processed_##n, \
|
.read_processed = target_read_processed_##n, \
|
||||||
|
COND_CODE_1(CONFIG_I2C_TARGET_BUFFER_MODE, \
|
||||||
|
(.buf_write_received = target_buf_write_received_##n, ), ()) \
|
||||||
|
COND_CODE_1(CONFIG_I2C_TARGET_BUFFER_MODE, \
|
||||||
|
(.buf_read_requested = target_buf_read_requested_##n, ), ()) \
|
||||||
.stop = target_stop_##n, \
|
.stop = target_stop_##n, \
|
||||||
},
|
},
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
struct i2c_target_callbacks emulated_callbacks[FORWARD_COUNT] = {
|
struct i2c_target_callbacks emulated_callbacks[FORWARD_COUNT] = {
|
||||||
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DEFINE_EMULATED_CALLBACK)};
|
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DEFINE_EMULATED_CALLBACK)};
|
||||||
|
|
155
tests/drivers/i2c/i2c_emul/src/test_forwarding_buf.cpp
Normal file
155
tests/drivers/i2c/i2c_emul/src/test_forwarding_buf.cpp
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include "emulated_target.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <zephyr/devicetree.h>
|
||||||
|
#include <zephyr/drivers/i2c.h>
|
||||||
|
#include <zephyr/fff.h>
|
||||||
|
#include <zephyr/ztest.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
#define GET_TARGET_DEVICE(node_id, prop, n) DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, n)),
|
||||||
|
|
||||||
|
/* Get the devicetree constants */
|
||||||
|
constexpr const struct device *controller = DEVICE_DT_GET(CONTROLLER_LABEL);
|
||||||
|
constexpr const struct device *targets[FORWARD_COUNT] = {
|
||||||
|
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, GET_TARGET_DEVICE)};
|
||||||
|
|
||||||
|
ZTEST(i2c_emul_forwarding, test_write_is_forwarded)
|
||||||
|
{
|
||||||
|
uint8_t data[] = {0x00, 0x01, 0x02};
|
||||||
|
|
||||||
|
target_buf_write_received_0_fake.custom_fake = [&data](struct i2c_target_config *,
|
||||||
|
uint8_t *buf, uint32_t len) {
|
||||||
|
zassert_equal(ARRAY_SIZE(data), len);
|
||||||
|
zexpect_mem_equal(data, buf, len);
|
||||||
|
};
|
||||||
|
|
||||||
|
zassert_ok(
|
||||||
|
i2c_write(controller, data, ARRAY_SIZE(data), emulated_target_config[0].address));
|
||||||
|
|
||||||
|
// Expect 0 reads and 1 write/stop to be made
|
||||||
|
zexpect_equal(0, target_buf_read_requested_0_fake.call_count);
|
||||||
|
zexpect_equal(1, target_buf_write_received_0_fake.call_count);
|
||||||
|
zexpect_equal(1, target_stop_0_fake.call_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTEST(i2c_emul_forwarding, test_read_is_forwarded)
|
||||||
|
{
|
||||||
|
uint8_t expected[] = {0x01, 0x02, 0x03};
|
||||||
|
uint8_t data[ARRAY_SIZE(expected)] = {};
|
||||||
|
|
||||||
|
/* Set the custom fake function to a lambda which captures the expected value as a reference.
|
||||||
|
* This means that when the function is executed, we can access 'expected' as though it were
|
||||||
|
* within the lambda's scope.
|
||||||
|
*/
|
||||||
|
target_buf_read_requested_0_fake.custom_fake = [&expected](struct i2c_target_config *,
|
||||||
|
uint8_t **ptr, uint32_t *len) {
|
||||||
|
*ptr = expected;
|
||||||
|
*len = ARRAY_SIZE(expected);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
zassert_ok(i2c_read(controller, data, ARRAY_SIZE(expected),
|
||||||
|
emulated_target_config[0].address));
|
||||||
|
|
||||||
|
// Expect 1 read/stop and 0 write to be made
|
||||||
|
zexpect_equal(1, target_buf_read_requested_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_buf_write_received_0_fake.call_count);
|
||||||
|
zexpect_equal(1, target_stop_0_fake.call_count);
|
||||||
|
zexpect_mem_equal(expected, data, ARRAY_SIZE(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTEST(i2c_emul_forwarding, test_failed_read_request)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
target_buf_read_requested_0_fake.return_val = -EINTR;
|
||||||
|
|
||||||
|
zassert_equal(-EINTR, i2c_read(controller, &data, 1, emulated_target_config[0].address));
|
||||||
|
zexpect_equal(1, target_buf_read_requested_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_buf_write_received_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_stop_0_fake.call_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTEST(i2c_emul_forwarding, test_read_request_overflow)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
/* Set the custom_fake to a local lambda with no capture values. */
|
||||||
|
target_buf_read_requested_0_fake.custom_fake = [](struct i2c_target_config *, uint8_t **_,
|
||||||
|
uint32_t *len) {
|
||||||
|
*len = UINT32_MAX;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
zassert_equal(-ENOMEM, i2c_read(controller, &data, 1, emulated_target_config[0].address));
|
||||||
|
zexpect_equal(1, target_buf_read_requested_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_buf_write_received_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_stop_0_fake.call_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTEST(i2c_emul_forwarding, test_transfer_is_forwarded)
|
||||||
|
{
|
||||||
|
uint8_t write_data[1] = {};
|
||||||
|
uint8_t read_data[2] = {};
|
||||||
|
|
||||||
|
struct i2c_msg msgs[] = {
|
||||||
|
{
|
||||||
|
.buf = write_data,
|
||||||
|
.len = sizeof(write_data),
|
||||||
|
.flags = I2C_MSG_WRITE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.buf = read_data,
|
||||||
|
.len = sizeof(read_data),
|
||||||
|
.flags = I2C_MSG_READ | I2C_MSG_STOP,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
int phase = 0;
|
||||||
|
target_buf_write_received_0_fake.custom_fake = [&phase](struct i2c_target_config *,
|
||||||
|
uint8_t *, uint32_t) {
|
||||||
|
zassert_equal(0, phase,
|
||||||
|
"Expected a call to buf_write_received before anything else");
|
||||||
|
phase++;
|
||||||
|
};
|
||||||
|
target_buf_read_requested_0_fake.custom_fake = [&phase](struct i2c_target_config *,
|
||||||
|
uint8_t **ptr, uint32_t *len) {
|
||||||
|
zassert_equal(1, phase, "Expected a call to buf_Read_requested as the second step");
|
||||||
|
phase++;
|
||||||
|
|
||||||
|
// Write a random byte. It doesn't make a difference.
|
||||||
|
*ptr = (uint8_t *)&phase;
|
||||||
|
*len = 1;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
target_stop_0_fake.custom_fake = [&phase](struct i2c_target_config *) -> int {
|
||||||
|
zassert_equal(2, phase, "Expected a call to stop as the 3rd step");
|
||||||
|
phase++;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
zassert_ok(i2c_transfer(controller, msgs, ARRAY_SIZE(msgs),
|
||||||
|
emulated_target_config[0].address));
|
||||||
|
zexpect_equal(1, target_buf_write_received_0_fake.call_count);
|
||||||
|
zexpect_equal(1, target_buf_read_requested_0_fake.call_count);
|
||||||
|
zexpect_equal(1, target_stop_0_fake.call_count);
|
||||||
|
zexpect_equal(3, phase, "Expected a total of 3 phases, but got %d", phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTEST(i2c_emul_forwarding, test_call_pio_forwarded_bus_when_buffering_enabled)
|
||||||
|
{
|
||||||
|
uint8_t data[2];
|
||||||
|
|
||||||
|
zassert_ok(i2c_read(controller, data, ARRAY_SIZE(data), emulated_target_config[1].address));
|
||||||
|
zexpect_equal(1, target_read_requested_1_fake.call_count);
|
||||||
|
zexpect_equal(1, target_read_processed_1_fake.call_count);
|
||||||
|
zexpect_equal(1, target_stop_1_fake.call_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -19,38 +19,6 @@ constexpr const struct device *controller = DEVICE_DT_GET(CONTROLLER_LABEL);
|
||||||
constexpr const struct device *targets[FORWARD_COUNT] = {
|
constexpr const struct device *targets[FORWARD_COUNT] = {
|
||||||
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, GET_TARGET_DEVICE)};
|
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, GET_TARGET_DEVICE)};
|
||||||
|
|
||||||
static void *i2c_emul_forwarding_setup(void)
|
|
||||||
{
|
|
||||||
// Register the target
|
|
||||||
for (int i = 0; i < FORWARD_COUNT; ++i) {
|
|
||||||
zassert_ok(i2c_target_register(targets[i], &emulated_target_config[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i2c_emul_forwarding_before(void *fixture)
|
|
||||||
{
|
|
||||||
ARG_UNUSED(fixture);
|
|
||||||
|
|
||||||
// Reset all fakes
|
|
||||||
FFF_FAKES_LIST_FOREACH(RESET_FAKE);
|
|
||||||
FFF_RESET_HISTORY();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i2c_emul_forwarding_teardown(void *fixture)
|
|
||||||
{
|
|
||||||
ARG_UNUSED(fixture);
|
|
||||||
|
|
||||||
// Unregister the I2C target callbacks
|
|
||||||
for (int i = 0; i < FORWARD_COUNT; ++i) {
|
|
||||||
zassert_ok(i2c_target_unregister(targets[i], &emulated_target_config[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ZTEST_SUITE(i2c_emul_forwarding, NULL, i2c_emul_forwarding_setup, i2c_emul_forwarding_before, NULL,
|
|
||||||
i2c_emul_forwarding_teardown);
|
|
||||||
|
|
||||||
ZTEST(i2c_emul_forwarding, test_write_is_forwarded)
|
ZTEST(i2c_emul_forwarding, test_write_is_forwarded)
|
||||||
{
|
{
|
||||||
// Try writing some values
|
// Try writing some values
|
||||||
|
@ -271,4 +239,16 @@ ZTEST(i2c_emul_forwarding, test_forward_two_targets)
|
||||||
"Expected to be called 0 times, got %d",
|
"Expected to be called 0 times, got %d",
|
||||||
target_read_processed_0_fake.call_count);
|
target_read_processed_0_fake.call_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZTEST(i2c_emul_forwarding, test_error_in_write_received)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
target_write_received_0_fake.return_val = -EINTR;
|
||||||
|
zassert_equal(-EINTR, i2c_write(controller, &data, 1, emulated_target_config[0].address));
|
||||||
|
zexpect_equal(1, target_write_requested_0_fake.call_count);
|
||||||
|
zexpect_equal(1, target_write_received_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_stop_0_fake.call_count);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
79
tests/drivers/i2c/i2c_emul/src/test_fowarding_common.cpp
Normal file
79
tests/drivers/i2c/i2c_emul/src/test_fowarding_common.cpp
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include "emulated_target.hpp"
|
||||||
|
|
||||||
|
#include <zephyr/devicetree.h>
|
||||||
|
#include <zephyr/drivers/i2c.h>
|
||||||
|
#include <zephyr/fff.h>
|
||||||
|
#include <zephyr/ztest.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
#define GET_TARGET_DEVICE(node_id, prop, n) DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, n)),
|
||||||
|
|
||||||
|
/* Get the devicetree constants */
|
||||||
|
constexpr const struct device *controller = DEVICE_DT_GET(CONTROLLER_LABEL);
|
||||||
|
constexpr const struct device *targets[FORWARD_COUNT] = {
|
||||||
|
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, GET_TARGET_DEVICE)};
|
||||||
|
|
||||||
|
static void *i2c_emul_forwarding_setup(void)
|
||||||
|
{
|
||||||
|
// Register the target
|
||||||
|
for (int i = 0; i < FORWARD_COUNT; ++i) {
|
||||||
|
zassert_ok(i2c_target_register(targets[i], &emulated_target_config[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_emul_forwarding_before(void *fixture)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(fixture);
|
||||||
|
|
||||||
|
// Reset all fakes
|
||||||
|
FFF_FAKES_LIST_FOREACH(RESET_FAKE);
|
||||||
|
FFF_RESET_HISTORY();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_emul_forwarding_teardown(void *fixture)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(fixture);
|
||||||
|
|
||||||
|
// Unregister the I2C target callbacks
|
||||||
|
for (int i = 0; i < FORWARD_COUNT; ++i) {
|
||||||
|
zassert_ok(i2c_target_unregister(targets[i], &emulated_target_config[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTEST_SUITE(i2c_emul_forwarding, NULL, i2c_emul_forwarding_setup, i2c_emul_forwarding_before, NULL,
|
||||||
|
i2c_emul_forwarding_teardown);
|
||||||
|
|
||||||
|
/* Common tests */
|
||||||
|
|
||||||
|
ZTEST(i2c_emul_forwarding, test_invalid_address_for_target)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
int rc = i2c_write(targets[0], &data, 1, emulated_target_config[0].address + 1);
|
||||||
|
zassert_equal(-EINVAL, rc, "Expected %d (-EINVAL), but got %d", -EINVAL, rc);
|
||||||
|
zexpect_equal(0, target_read_requested_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_read_processed_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_write_requested_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_write_received_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_buf_write_received_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_buf_read_requested_0_fake.call_count);
|
||||||
|
zexpect_equal(0, target_stop_0_fake.call_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTEST(i2c_emul_forwarding, test_error_in_stop)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
target_stop_0_fake.return_val = -EINTR;
|
||||||
|
zassert_equal(-EINTR, i2c_write(controller, &data, 1, emulated_target_config[0].address));
|
||||||
|
zexpect_equal(1, target_stop_0_fake.call_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -2,6 +2,16 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
drivers.i2c.emul:
|
drivers.i2c.emul.target_pio:
|
||||||
platform_allow:
|
platform_allow:
|
||||||
- native_sim
|
- native_sim
|
||||||
|
extra_configs:
|
||||||
|
- CONFIG_I2C_TARGET_BUFFER_MODE=n
|
||||||
|
drivers.i2c.emul.target_buf:
|
||||||
|
platform_allow:
|
||||||
|
- native_sim
|
||||||
|
extra_configs:
|
||||||
|
- CONFIG_I2C_TARGET_BUFFER_MODE=y
|
||||||
|
extra_dtc_overlay_files:
|
||||||
|
- "boards/native_sim.overlay"
|
||||||
|
- "boards/native_sim.buf.overlay"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue