zephyr/subsys/emul/emul.c
Aaron Massey 9d7b9dc807 emul: Simplify emulator bus initialization and API
Allow emulator creators to write an init function that can be used
across multiple busses so as to reduce the boilerplate and cognitive
load in creating an emulator.

Part of this change includes allowing access to the emul struct from a
field in a {bus}_struct api (e.g. i2c_struct), which removes the need for
sporadic usages of CONTAINER_OF to access the emul struct.

Overall, this change simplifies and reduces the amount of boilerplate
code to get a device emulator up and running, thus reducing excise work
to writing tests.

TEST=twister on accel,espi, and eeprom drivers tests

Signed-off-by: Aaron Massey <aaronmassey@google.com>
2022-07-19 11:43:30 -05:00

93 lines
2.1 KiB
C

/*
* Copyright 2020 Google LLC
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_LEVEL CONFIG_EMUL_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(emul);
#include <zephyr/device.h>
#include <zephyr/drivers/emul.h>
#include <string.h>
const struct emul *emul_get_binding(const char *name)
{
const struct emul *emul_it;
for (emul_it = __emul_list_start; emul_it < __emul_list_end; emul_it++) {
if (strcmp(emul_it->dev_label, name) == 0) {
return emul_it;
}
}
return NULL;
}
int emul_init_for_bus(const struct device *dev)
{
const struct emul_list_for_bus *cfg = dev->config;
/*
* Walk the list of children, find the corresponding emulator and
* initialise it.
*/
const struct emul_link_for_bus *elp;
const struct emul_link_for_bus *const end = cfg->children + cfg->num_children;
LOG_INF("Registering %d emulator(s) for %s", cfg->num_children, dev->name);
for (elp = cfg->children; elp < end; elp++) {
const struct emul *emul = emul_get_binding(elp->label);
__ASSERT(emul, "Cannot find emulator for '%s'", elp->label);
switch (emul->bus_type) {
case EMUL_BUS_TYPE_I2C:
emul->bus.i2c->target = emul;
break;
case EMUL_BUS_TYPE_ESPI:
emul->bus.espi->target = emul;
break;
case EMUL_BUS_TYPE_SPI:
emul->bus.spi->target = emul;
break;
}
int rc = emul->init(emul, dev);
if (rc != 0) {
LOG_WRN("Init %s emulator failed: %d",
elp->label, rc);
}
switch (emul->bus_type) {
#ifdef CONFIG_I2C_EMUL
case EMUL_BUS_TYPE_I2C:
rc = i2c_emul_register(dev, emul->dev_label, emul->bus.i2c);
break;
#endif /* CONFIG_I2C_EMUL */
#ifdef CONFIG_ESPI_EMUL
case EMUL_BUS_TYPE_ESPI:
rc = espi_emul_register(dev, emul->dev_label, emul->bus.espi);
break;
#endif /* CONFIG_ESPI_EMUL */
#ifdef CONFIG_SPI_EMUL
case EMUL_BUS_TYPE_SPI:
rc = spi_emul_register(dev, emul->dev_label, emul->bus.spi);
break;
#endif /* CONFIG_SPI_EMUL */
default:
rc = -EINVAL;
LOG_WRN("Found no emulated bus enabled to register emulator %s",
elp->label);
}
if (rc != 0) {
LOG_WRN("Failed to register emulator for %s: %d", elp->label, rc);
}
}
return 0;
}