Skip to content

Commit 6901343

Browse files
yperessnashif
authored andcommitted
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>
1 parent 81f163e commit 6901343

File tree

11 files changed

+334
-38
lines changed

11 files changed

+334
-38
lines changed

doc/hardware/emulator/bus_emulators.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ I2C Emulation features
150150
In the binding of the I2C emulated bus, there's a custom property for address
151151
based forwarding. Given the following devicetree node:
152152

153-
.. code-block::
153+
.. code-block:: devicetree
154154
155155
i2c0: i2c@100 {
156156
status = "okay";
@@ -170,7 +170,7 @@ same image.
170170

171171
.. note::
172172
The ``#forward-cells`` attribute should always be 1. Each entry in the
173-
``fowards`` attribute consists of the phandle followed by the address. In
173+
``forwards`` attribute consists of the phandle followed by the address. In
174174
the example above, ``<&i2c1 0x20>`` will forward all read/write operations
175175
made to ``i2c0`` at port ``0x20`` to ``i2c1`` on the same port. Since no
176176
additional cells are used by the emulated controller, the number of cells

drivers/i2c/i2c_emul.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ struct i2c_emul_data {
3636

3737
struct i2c_emul_config {
3838
struct emul_list_for_bus emul_list;
39+
bool target_buffered_mode;
3940
const struct i2c_dt_spec *forward_list;
4041
uint16_t forward_list_size;
4142
};
@@ -89,6 +90,41 @@ static int i2c_emul_send_to_target(const struct device *dev, struct i2c_msg *msg
8990
struct i2c_emul_data *data = dev->data;
9091
const struct i2c_target_callbacks *callbacks = data->target_cfg->callbacks;
9192

93+
#ifdef CONFIG_I2C_TARGET_BUFFER_MODE
94+
const struct i2c_emul_config *config = dev->config;
95+
96+
if (config->target_buffered_mode) {
97+
for (uint8_t i = 0; i < num_msgs; ++i) {
98+
if (i2c_is_read_op(&msgs[i])) {
99+
uint8_t *ptr = NULL;
100+
uint32_t len;
101+
int rc =
102+
callbacks->buf_read_requested(data->target_cfg, &ptr, &len);
103+
104+
if (rc != 0) {
105+
return rc;
106+
}
107+
if (len > msgs[i].len) {
108+
LOG_ERR("buf_read_requested returned too many bytes");
109+
return -ENOMEM;
110+
}
111+
memcpy(msgs[i].buf, ptr, len);
112+
} else {
113+
callbacks->buf_write_received(data->target_cfg, msgs[i].buf,
114+
msgs[i].len);
115+
}
116+
if (i2c_is_stop_op(&msgs[i])) {
117+
int rc = callbacks->stop(data->target_cfg);
118+
119+
if (rc != 0) {
120+
return rc;
121+
}
122+
}
123+
}
124+
return 0;
125+
}
126+
#endif /* CONFIG_I2C_TARGET_BUFFER_MODE */
127+
92128
for (uint8_t i = 0; i < num_msgs; ++i) {
93129
LOG_DBG(" msgs[%u].flags? 0x%02x", i, msgs[i].flags);
94130
if (i2c_is_read_op(&msgs[i])) {
@@ -289,6 +325,7 @@ static const struct i2c_driver_api i2c_emul_api = {
289325
.children = emuls_##n, \
290326
.num_children = ARRAY_SIZE(emuls_##n), \
291327
}, \
328+
.target_buffered_mode = DT_INST_PROP(n, target_buffered_mode), \
292329
.forward_list = emul_forward_list_##n, \
293330
.forward_list_size = ARRAY_SIZE(emul_forward_list_##n), \
294331
}; \

dts/bindings/i2c/zephyr,i2c-emul-controller.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ include: i2c-controller.yaml
1010
properties:
1111
reg:
1212
required: true
13+
target-buffered-mode:
14+
type: boolean
15+
description: |
16+
This option is used when the I2C target is enabled and it can support
17+
buffered mode for I2C target transfer. When 'false', the target will use
18+
PIO (Programmed I/O) mode.
1319
forwards:
1420
type: phandle-array
1521
description: |

tests/drivers/i2c/i2c_emul/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ project(i2c_emul)
77

88
target_sources(app PRIVATE
99
src/emulated_target.cpp
10-
src/test_forwarding.cpp
10+
src/test_fowarding_common.cpp
1111
)
12+
if(CONFIG_I2C_TARGET_BUFFER_MODE)
13+
target_sources(app PRIVATE src/test_forwarding_buf.cpp)
14+
else()
15+
target_sources(app PRIVATE src/test_forwarding_pio.cpp)
16+
endif()
1217
target_include_directories(app PRIVATE include)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
&i2c1 {
7+
target-buffered-mode;
8+
};

tests/drivers/i2c/i2c_emul/include/emulated_target.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ extern struct i2c_target_config emulated_target_config[FORWARD_COUNT];
3131
DECLARE_FAKE_VALUE_FUNC(int, target_write_requested_##n, struct i2c_target_config *); \
3232
DECLARE_FAKE_VALUE_FUNC(int, target_write_received_##n, struct i2c_target_config *, \
3333
uint8_t); \
34-
DECLARE_FAKE_VALUE_FUNC(int, target_stop_##n, struct i2c_target_config *);
34+
DECLARE_FAKE_VALUE_FUNC(int, target_stop_##n, struct i2c_target_config *); \
35+
DECLARE_FAKE_VALUE_FUNC(int, target_buf_read_requested_##n, struct i2c_target_config *, \
36+
uint8_t **, uint32_t *) \
37+
DECLARE_FAKE_VOID_FUNC(target_buf_write_received_##n, struct i2c_target_config *, \
38+
uint8_t *, uint32_t)
3539

3640
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DECLARE_FAKE_TARGET_FUNCTIONS)
3741

@@ -42,6 +46,8 @@ DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DECLARE_FAKE_TARGET_FUNCTIONS)
4246
fn(target_write_requested_##n); \
4347
fn(target_write_received_##n); \
4448
fn(target_stop_##n); \
49+
fn(target_buf_read_requested_##n); \
50+
fn(target_buf_write_received_##n); \
4551
} while (0);
4652

4753
#define FFF_FAKES_LIST_FOREACH(fn) \

tests/drivers/i2c/i2c_emul/src/emulated_target.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,28 @@ DEFINE_FFF_GLOBALS;
1515
DEFINE_FAKE_VALUE_FUNC(int, target_write_requested_##n, struct i2c_target_config *); \
1616
DEFINE_FAKE_VALUE_FUNC(int, target_write_received_##n, struct i2c_target_config *, \
1717
uint8_t); \
18-
DEFINE_FAKE_VALUE_FUNC(int, target_stop_##n, struct i2c_target_config *);
18+
DEFINE_FAKE_VALUE_FUNC(int, target_stop_##n, struct i2c_target_config *); \
19+
DEFINE_FAKE_VALUE_FUNC(int, target_buf_read_requested_##n, struct i2c_target_config *, \
20+
uint8_t **, uint32_t *) \
21+
DEFINE_FAKE_VOID_FUNC(target_buf_write_received_##n, struct i2c_target_config *, \
22+
uint8_t *, uint32_t)
1923

2024
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DEFINE_FAKE_TARGET_FUNCTION);
2125

26+
/* clang-format off */
2227
#define DEFINE_EMULATED_CALLBACK(node_id, prop, n) \
2328
[n] = { \
2429
.write_requested = target_write_requested_##n, \
2530
.read_requested = target_read_requested_##n, \
2631
.write_received = target_write_received_##n, \
2732
.read_processed = target_read_processed_##n, \
33+
COND_CODE_1(CONFIG_I2C_TARGET_BUFFER_MODE, \
34+
(.buf_write_received = target_buf_write_received_##n, ), ()) \
35+
COND_CODE_1(CONFIG_I2C_TARGET_BUFFER_MODE, \
36+
(.buf_read_requested = target_buf_read_requested_##n, ), ()) \
2837
.stop = target_stop_##n, \
2938
},
39+
/* clang-format on */
3040

3141
struct i2c_target_callbacks emulated_callbacks[FORWARD_COUNT] = {
3242
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, DEFINE_EMULATED_CALLBACK)};
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
#include "emulated_target.hpp"
6+
#include <cstdint>
7+
8+
#include <zephyr/devicetree.h>
9+
#include <zephyr/drivers/i2c.h>
10+
#include <zephyr/fff.h>
11+
#include <zephyr/ztest.h>
12+
13+
namespace
14+
{
15+
16+
#define GET_TARGET_DEVICE(node_id, prop, n) DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, n)),
17+
18+
/* Get the devicetree constants */
19+
constexpr const struct device *controller = DEVICE_DT_GET(CONTROLLER_LABEL);
20+
constexpr const struct device *targets[FORWARD_COUNT] = {
21+
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, GET_TARGET_DEVICE)};
22+
23+
ZTEST(i2c_emul_forwarding, test_write_is_forwarded)
24+
{
25+
uint8_t data[] = {0x00, 0x01, 0x02};
26+
27+
target_buf_write_received_0_fake.custom_fake = [&data](struct i2c_target_config *,
28+
uint8_t *buf, uint32_t len) {
29+
zassert_equal(ARRAY_SIZE(data), len);
30+
zexpect_mem_equal(data, buf, len);
31+
};
32+
33+
zassert_ok(
34+
i2c_write(controller, data, ARRAY_SIZE(data), emulated_target_config[0].address));
35+
36+
// Expect 0 reads and 1 write/stop to be made
37+
zexpect_equal(0, target_buf_read_requested_0_fake.call_count);
38+
zexpect_equal(1, target_buf_write_received_0_fake.call_count);
39+
zexpect_equal(1, target_stop_0_fake.call_count);
40+
}
41+
42+
ZTEST(i2c_emul_forwarding, test_read_is_forwarded)
43+
{
44+
uint8_t expected[] = {0x01, 0x02, 0x03};
45+
uint8_t data[ARRAY_SIZE(expected)] = {};
46+
47+
/* Set the custom fake function to a lambda which captures the expected value as a reference.
48+
* This means that when the function is executed, we can access 'expected' as though it were
49+
* within the lambda's scope.
50+
*/
51+
target_buf_read_requested_0_fake.custom_fake = [&expected](struct i2c_target_config *,
52+
uint8_t **ptr, uint32_t *len) {
53+
*ptr = expected;
54+
*len = ARRAY_SIZE(expected);
55+
return 0;
56+
};
57+
58+
zassert_ok(i2c_read(controller, data, ARRAY_SIZE(expected),
59+
emulated_target_config[0].address));
60+
61+
// Expect 1 read/stop and 0 write to be made
62+
zexpect_equal(1, target_buf_read_requested_0_fake.call_count);
63+
zexpect_equal(0, target_buf_write_received_0_fake.call_count);
64+
zexpect_equal(1, target_stop_0_fake.call_count);
65+
zexpect_mem_equal(expected, data, ARRAY_SIZE(expected));
66+
}
67+
68+
ZTEST(i2c_emul_forwarding, test_failed_read_request)
69+
{
70+
uint8_t data;
71+
target_buf_read_requested_0_fake.return_val = -EINTR;
72+
73+
zassert_equal(-EINTR, i2c_read(controller, &data, 1, emulated_target_config[0].address));
74+
zexpect_equal(1, target_buf_read_requested_0_fake.call_count);
75+
zexpect_equal(0, target_buf_write_received_0_fake.call_count);
76+
zexpect_equal(0, target_stop_0_fake.call_count);
77+
}
78+
79+
ZTEST(i2c_emul_forwarding, test_read_request_overflow)
80+
{
81+
uint8_t data;
82+
83+
/* Set the custom_fake to a local lambda with no capture values. */
84+
target_buf_read_requested_0_fake.custom_fake = [](struct i2c_target_config *, uint8_t **_,
85+
uint32_t *len) {
86+
*len = UINT32_MAX;
87+
return 0;
88+
};
89+
90+
zassert_equal(-ENOMEM, i2c_read(controller, &data, 1, emulated_target_config[0].address));
91+
zexpect_equal(1, target_buf_read_requested_0_fake.call_count);
92+
zexpect_equal(0, target_buf_write_received_0_fake.call_count);
93+
zexpect_equal(0, target_stop_0_fake.call_count);
94+
}
95+
96+
ZTEST(i2c_emul_forwarding, test_transfer_is_forwarded)
97+
{
98+
uint8_t write_data[1] = {};
99+
uint8_t read_data[2] = {};
100+
101+
struct i2c_msg msgs[] = {
102+
{
103+
.buf = write_data,
104+
.len = sizeof(write_data),
105+
.flags = I2C_MSG_WRITE,
106+
},
107+
{
108+
.buf = read_data,
109+
.len = sizeof(read_data),
110+
.flags = I2C_MSG_READ | I2C_MSG_STOP,
111+
},
112+
};
113+
114+
int phase = 0;
115+
target_buf_write_received_0_fake.custom_fake = [&phase](struct i2c_target_config *,
116+
uint8_t *, uint32_t) {
117+
zassert_equal(0, phase,
118+
"Expected a call to buf_write_received before anything else");
119+
phase++;
120+
};
121+
target_buf_read_requested_0_fake.custom_fake = [&phase](struct i2c_target_config *,
122+
uint8_t **ptr, uint32_t *len) {
123+
zassert_equal(1, phase, "Expected a call to buf_Read_requested as the second step");
124+
phase++;
125+
126+
// Write a random byte. It doesn't make a difference.
127+
*ptr = (uint8_t *)&phase;
128+
*len = 1;
129+
return 0;
130+
};
131+
target_stop_0_fake.custom_fake = [&phase](struct i2c_target_config *) -> int {
132+
zassert_equal(2, phase, "Expected a call to stop as the 3rd step");
133+
phase++;
134+
return 0;
135+
};
136+
137+
zassert_ok(i2c_transfer(controller, msgs, ARRAY_SIZE(msgs),
138+
emulated_target_config[0].address));
139+
zexpect_equal(1, target_buf_write_received_0_fake.call_count);
140+
zexpect_equal(1, target_buf_read_requested_0_fake.call_count);
141+
zexpect_equal(1, target_stop_0_fake.call_count);
142+
zexpect_equal(3, phase, "Expected a total of 3 phases, but got %d", phase);
143+
}
144+
145+
ZTEST(i2c_emul_forwarding, test_call_pio_forwarded_bus_when_buffering_enabled)
146+
{
147+
uint8_t data[2];
148+
149+
zassert_ok(i2c_read(controller, data, ARRAY_SIZE(data), emulated_target_config[1].address));
150+
zexpect_equal(1, target_read_requested_1_fake.call_count);
151+
zexpect_equal(1, target_read_processed_1_fake.call_count);
152+
zexpect_equal(1, target_stop_1_fake.call_count);
153+
}
154+
155+
} // namespace

tests/drivers/i2c/i2c_emul/src/test_forwarding.cpp renamed to tests/drivers/i2c/i2c_emul/src/test_forwarding_pio.cpp

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,6 @@ constexpr const struct device *controller = DEVICE_DT_GET(CONTROLLER_LABEL);
1919
constexpr const struct device *targets[FORWARD_COUNT] = {
2020
DT_FOREACH_PROP_ELEM(CONTROLLER_LABEL, forwards, GET_TARGET_DEVICE)};
2121

22-
static void *i2c_emul_forwarding_setup(void)
23-
{
24-
// Register the target
25-
for (int i = 0; i < FORWARD_COUNT; ++i) {
26-
zassert_ok(i2c_target_register(targets[i], &emulated_target_config[i]));
27-
}
28-
29-
return NULL;
30-
}
31-
32-
static void i2c_emul_forwarding_before(void *fixture)
33-
{
34-
ARG_UNUSED(fixture);
35-
36-
// Reset all fakes
37-
FFF_FAKES_LIST_FOREACH(RESET_FAKE);
38-
FFF_RESET_HISTORY();
39-
}
40-
41-
static void i2c_emul_forwarding_teardown(void *fixture)
42-
{
43-
ARG_UNUSED(fixture);
44-
45-
// Unregister the I2C target callbacks
46-
for (int i = 0; i < FORWARD_COUNT; ++i) {
47-
zassert_ok(i2c_target_unregister(targets[i], &emulated_target_config[i]));
48-
}
49-
}
50-
51-
ZTEST_SUITE(i2c_emul_forwarding, NULL, i2c_emul_forwarding_setup, i2c_emul_forwarding_before, NULL,
52-
i2c_emul_forwarding_teardown);
53-
5422
ZTEST(i2c_emul_forwarding, test_write_is_forwarded)
5523
{
5624
// Try writing some values
@@ -271,4 +239,16 @@ ZTEST(i2c_emul_forwarding, test_forward_two_targets)
271239
"Expected to be called 0 times, got %d",
272240
target_read_processed_0_fake.call_count);
273241
}
242+
243+
ZTEST(i2c_emul_forwarding, test_error_in_write_received)
244+
{
245+
uint8_t data;
246+
247+
target_write_received_0_fake.return_val = -EINTR;
248+
zassert_equal(-EINTR, i2c_write(controller, &data, 1, emulated_target_config[0].address));
249+
zexpect_equal(1, target_write_requested_0_fake.call_count);
250+
zexpect_equal(1, target_write_received_0_fake.call_count);
251+
zexpect_equal(0, target_stop_0_fake.call_count);
252+
}
253+
274254
} // namespace

0 commit comments

Comments
 (0)