From fd6c088d2154fc3cb06c2e599c2a4d52b6a080b6 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Mon, 29 Sep 2025 12:05:07 +0200 Subject: [PATCH 1/5] i2c: target: eeprom_target: add changed callback For some use cases it can be interesting to know when the eeprom has been written to by the I2C host (E.G. once a stop has been received after a write transaction), so add a "changed" callback. Multiple EEPROM instances are supported, so pass the device as argument to the callback. Signed-off-by: Peter Korsgaard --- drivers/i2c/target/eeprom_target.c | 19 +++++++++++++++++++ include/zephyr/drivers/i2c/target/eeprom.h | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/i2c/target/eeprom_target.c b/drivers/i2c/target/eeprom_target.c index 69ae35cfcd8d0..d14c2e66e9dd3 100644 --- a/drivers/i2c/target/eeprom_target.c +++ b/drivers/i2c/target/eeprom_target.c @@ -18,12 +18,15 @@ LOG_MODULE_REGISTER(i2c_target); struct i2c_eeprom_target_data { + const struct device *dev; struct i2c_target_config config; uint32_t buffer_size; uint8_t *buffer; uint32_t buffer_idx; uint32_t idx_write_cnt; uint8_t address_width; + eeprom_target_changed_handler_t changed_handler; + bool changed; }; struct i2c_eeprom_target_config { @@ -32,6 +35,14 @@ struct i2c_eeprom_target_config { uint8_t *buffer; }; +void eeprom_target_set_changed_callback(const struct device *dev, + eeprom_target_changed_handler_t handler) +{ + struct i2c_eeprom_target_data *data = dev->data; + + data->changed_handler = handler; +} + int eeprom_target_program(const struct device *dev, const uint8_t *eeprom_data, unsigned int length) { @@ -131,6 +142,7 @@ static int eeprom_target_write_received(struct i2c_target_config *config, data->idx_write_cnt++; } else { data->buffer[data->buffer_idx++] = val; + data->changed = true; } data->buffer_idx = data->buffer_idx % data->buffer_size; @@ -169,6 +181,11 @@ static int eeprom_target_stop(struct i2c_target_config *config) data->idx_write_cnt = 0; + if (data->changed && data->changed_handler) { + data->changed_handler(data->dev); + } + data->changed = false; + return 0; } @@ -191,6 +208,7 @@ static void eeprom_target_buf_write_received(struct i2c_target_config *config, if (len > 0) { memcpy(&data->buffer[data->buffer_idx], ptr, len); + data->changed = true; } } @@ -251,6 +269,7 @@ static int i2c_eeprom_target_init(const struct device *dev) return -ENODEV; } + data->dev = dev; data->buffer_size = cfg->buffer_size; data->buffer = cfg->buffer; data->config.address = cfg->bus.addr; diff --git a/include/zephyr/drivers/i2c/target/eeprom.h b/include/zephyr/drivers/i2c/target/eeprom.h index 3146c8b29b942..87d65ce749202 100644 --- a/include/zephyr/drivers/i2c/target/eeprom.h +++ b/include/zephyr/drivers/i2c/target/eeprom.h @@ -21,6 +21,23 @@ * @{ */ +/** + * @typedef eeprom_target_changed_handler_t + * @brief Define the application callback handler function signature + * + * @param dev Pointer to the device structure for the driver instance. + */ +typedef void (*eeprom_target_changed_handler_t)(const struct device *dev); + +/** + * @brief Set the EEPROM changed callback handler + * + * @param dev Pointer to the device structure for the driver instance. + * @param handler Handler to call on EEPROM changes + */ +void eeprom_target_set_changed_callback(const struct device *dev, + eeprom_target_changed_handler_t handler); + /** * @brief Program memory of the virtual EEPROM * From aaa1f94e3e6b32ff3faf9e2bad87c653db89cc36 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Thu, 2 Oct 2025 15:02:15 +0200 Subject: [PATCH 2/5] i2c: target: eeprom_target: add _get_size() For some use cases it can be interesting to know how big the EEPROM is, so add a getter for it. Signed-off-by: Peter Korsgaard --- drivers/i2c/target/eeprom_target.c | 7 +++++++ include/zephyr/drivers/i2c/target/eeprom.h | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/i2c/target/eeprom_target.c b/drivers/i2c/target/eeprom_target.c index d14c2e66e9dd3..66f306b84fa32 100644 --- a/drivers/i2c/target/eeprom_target.c +++ b/drivers/i2c/target/eeprom_target.c @@ -43,6 +43,13 @@ void eeprom_target_set_changed_callback(const struct device *dev, data->changed_handler = handler; } +unsigned int eeprom_target_get_size(const struct device *dev) +{ + struct i2c_eeprom_target_data *data = dev->data; + + return data->buffer_size; +} + int eeprom_target_program(const struct device *dev, const uint8_t *eeprom_data, unsigned int length) { diff --git a/include/zephyr/drivers/i2c/target/eeprom.h b/include/zephyr/drivers/i2c/target/eeprom.h index 87d65ce749202..4ec04126e2f72 100644 --- a/include/zephyr/drivers/i2c/target/eeprom.h +++ b/include/zephyr/drivers/i2c/target/eeprom.h @@ -38,6 +38,15 @@ typedef void (*eeprom_target_changed_handler_t)(const struct device *dev); void eeprom_target_set_changed_callback(const struct device *dev, eeprom_target_changed_handler_t handler); +/** + * @brief Get size of the virtual EEPROM + * + * @param dev Pointer to the device structure for the driver instance. + * + * @retval Size of EEPROM in bytes + */ +unsigned int eeprom_target_get_size(const struct device *dev); + /** * @brief Program memory of the virtual EEPROM * From a9cbbdbc9cb1fe884e946e69e4cdcb6b4e3de0e4 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Thu, 2 Oct 2025 15:02:15 +0200 Subject: [PATCH 3/5] i2c: target: eeprom_target: add _get_data() The current functions to access the EEPROM data are not ideal, E.G.: - eeprom_target_program() is hard coded to write to the beginning of the EEPROM data - eeprom_target_read() reads a single byte, so large overhead and external buffering to access the entire data So as an alternative, simply add a getter for the data. Signed-off-by: Peter Korsgaard --- drivers/i2c/target/eeprom_target.c | 7 +++++++ include/zephyr/drivers/i2c/target/eeprom.h | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/i2c/target/eeprom_target.c b/drivers/i2c/target/eeprom_target.c index 66f306b84fa32..114623517ce04 100644 --- a/drivers/i2c/target/eeprom_target.c +++ b/drivers/i2c/target/eeprom_target.c @@ -50,6 +50,13 @@ unsigned int eeprom_target_get_size(const struct device *dev) return data->buffer_size; } +uint8_t *eeprom_target_get_data(const struct device *dev) +{ + struct i2c_eeprom_target_data *data = dev->data; + + return data->buffer; +} + int eeprom_target_program(const struct device *dev, const uint8_t *eeprom_data, unsigned int length) { diff --git a/include/zephyr/drivers/i2c/target/eeprom.h b/include/zephyr/drivers/i2c/target/eeprom.h index 4ec04126e2f72..185718e046cc4 100644 --- a/include/zephyr/drivers/i2c/target/eeprom.h +++ b/include/zephyr/drivers/i2c/target/eeprom.h @@ -47,6 +47,15 @@ void eeprom_target_set_changed_callback(const struct device *dev, */ unsigned int eeprom_target_get_size(const struct device *dev); +/** + * @brief Get a pointer to the data of the virtual EEPROM + * + * @param dev Pointer to the device structure for the driver instance. + * + * @retval Pointer to EEPROM data + */ +uint8_t *eeprom_target_get_data(const struct device *dev); + /** * @brief Program memory of the virtual EEPROM * From 3b9c01695b64fdf514ed8473aac0ed16e22055f5 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Thu, 2 Oct 2025 15:11:06 +0200 Subject: [PATCH 4/5] samples: drivers: target_eeprom: drop unregistration It is not very handy that the EEPROM stops responding after 1 second, so drop that. Signed-off-by: Peter Korsgaard --- samples/drivers/i2c/target_eeprom/src/main.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/samples/drivers/i2c/target_eeprom/src/main.c b/samples/drivers/i2c/target_eeprom/src/main.c index d1c42f9a40164..7a09aefc8d4b3 100644 --- a/samples/drivers/i2c/target_eeprom/src/main.c +++ b/samples/drivers/i2c/target_eeprom/src/main.c @@ -27,14 +27,5 @@ int main(void) printk("i2c target driver registered\n"); - k_msleep(1000); - - if (i2c_target_driver_unregister(eeprom) < 0) { - printk("Failed to unregister i2c target driver\n"); - return 0; - } - - printk("i2c target driver unregistered\n"); - return 0; } From b0061a2a5fb60453a0b14614ba461d54dac9181a Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Thu, 2 Oct 2025 15:15:33 +0200 Subject: [PATCH 5/5] samples: drivers: target_eeprom: add a changed callback And show the usages of the new _get_size() / _get_data() getters. Signed-off-by: Peter Korsgaard --- samples/drivers/i2c/target_eeprom/src/main.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/samples/drivers/i2c/target_eeprom/src/main.c b/samples/drivers/i2c/target_eeprom/src/main.c index 7a09aefc8d4b3..75c424bb9f9f9 100644 --- a/samples/drivers/i2c/target_eeprom/src/main.c +++ b/samples/drivers/i2c/target_eeprom/src/main.c @@ -11,6 +11,23 @@ static const struct device *eeprom = DEVICE_DT_GET(DT_NODELABEL(eeprom0)); +static void on_changed(const struct device *dev) +{ + unsigned int size, i; + uint8_t *data; + + size = eeprom_target_get_size(dev); + data = eeprom_target_get_data(dev); + + printk("Eeprom changed, now contains:\n"); + for (i = 0; i < size; i++) { + char sep = i % 16 == 15 ? '\n' : ' '; + + printk("%02x%c", data[i], sep); + } + printk("\n"); +} + int main(void) { printk("i2c target sample\n"); @@ -20,6 +37,8 @@ int main(void) return 0; } + eeprom_target_set_changed_callback(eeprom, on_changed); + if (i2c_target_driver_register(eeprom) < 0) { printk("Failed to register i2c target driver\n"); return 0;