device: add API to visit required devices
The static device dependencies from devicetree are not the only ones that might be present at runtime. Add API that allows visiting required devices without assuming that handles for or pointers to them can be accessed as a static contiguous sequence. Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
This commit is contained in:
parent
e9e28c0562
commit
b29abe3710
3 changed files with 132 additions and 0 deletions
|
@ -465,6 +465,24 @@ device_from_handle(device_handle_t dev_handle)
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prototype for functions used when iterating over a set of devices.
|
||||||
|
*
|
||||||
|
* Such a function may be used in API that identifies a set of devices and
|
||||||
|
* provides a visitor API supporting caller-specific interaction with each
|
||||||
|
* device in the set.
|
||||||
|
*
|
||||||
|
* The visit is said to succeed if the visitor returns a non-negative value.
|
||||||
|
*
|
||||||
|
* @param dev a device in the set being iterated
|
||||||
|
*
|
||||||
|
* @param context state used to support the visitor function
|
||||||
|
*
|
||||||
|
* @return A non-negative number to allow walking to continue, and a negative
|
||||||
|
* error code to case the iteration to stop.
|
||||||
|
*/
|
||||||
|
typedef int (*device_visitor_callback_t)(const struct device *dev, void *context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the set of handles for devicetree dependencies of this device.
|
* @brief Get the set of handles for devicetree dependencies of this device.
|
||||||
*
|
*
|
||||||
|
@ -498,6 +516,43 @@ device_required_handles_get(const struct device *dev,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Visit every device that @p dev directly requires.
|
||||||
|
*
|
||||||
|
* Zephyr maintains information about which devices are directly required by
|
||||||
|
* another device; for example an I2C-based sensor driver will require an I2C
|
||||||
|
* controller for communication. Required devices can derive from
|
||||||
|
* statically-defined devicetree relationships or dependencies registered
|
||||||
|
* at runtime.
|
||||||
|
*
|
||||||
|
* This API supports operating on the set of required devices. Example uses
|
||||||
|
* include making sure required devices are ready before the requiring device
|
||||||
|
* is used, and releasing them when the requiring device is no longer needed.
|
||||||
|
*
|
||||||
|
* There is no guarantee on the order in which required devices are visited.
|
||||||
|
*
|
||||||
|
* If the @p visitor function returns a negative value iteration is halted,
|
||||||
|
* and the returned value from the visitor is returned from this function.
|
||||||
|
*
|
||||||
|
* @note This API is not available to unprivileged threads.
|
||||||
|
*
|
||||||
|
* @param dev a device of interest. The devices that this device depends on
|
||||||
|
* will be used as the set of devices to visit. This parameter must not be
|
||||||
|
* null.
|
||||||
|
*
|
||||||
|
* @param visitor_cb the function that should be invoked on each device in the
|
||||||
|
* dependency set. This parameter must not be null.
|
||||||
|
*
|
||||||
|
* @param context state that is passed through to the visitor function. This
|
||||||
|
* parameter may be null if @p visitor tolerates a null @p context.
|
||||||
|
*
|
||||||
|
* @return The number of devices that were visited if all visits succeed, or
|
||||||
|
* the negative value returned from the first visit that did not succeed.
|
||||||
|
*/
|
||||||
|
int device_required_foreach(const struct device *dev,
|
||||||
|
device_visitor_callback_t visitor_cb,
|
||||||
|
void *context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve the device structure for a driver by name
|
* @brief Retrieve the device structure for a driver by name
|
||||||
*
|
*
|
||||||
|
|
|
@ -162,6 +162,28 @@ bool z_device_ready(const struct device *dev)
|
||||||
return dev->state->initialized && (dev->state->init_res == 0);
|
return dev->state->initialized && (dev->state->init_res == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int device_required_foreach(const struct device *dev,
|
||||||
|
device_visitor_callback_t visitor_cb,
|
||||||
|
void *context)
|
||||||
|
{
|
||||||
|
size_t handle_count = 0;
|
||||||
|
const device_handle_t *handles =
|
||||||
|
device_required_handles_get(dev, &handle_count);
|
||||||
|
|
||||||
|
/* Iterate over fixed devices */
|
||||||
|
for (size_t i = 0; i < handle_count; ++i) {
|
||||||
|
device_handle_t dh = handles[i];
|
||||||
|
const struct device *rdev = device_from_handle(dh);
|
||||||
|
int rc = visitor_cb(rdev, context);
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle_count;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_DEVICE
|
#ifdef CONFIG_PM_DEVICE
|
||||||
int device_pm_control_nop(const struct device *unused_device,
|
int device_pm_control_nop(const struct device *unused_device,
|
||||||
uint32_t unused_ctrl_command,
|
uint32_t unused_ctrl_command,
|
||||||
|
|
|
@ -74,23 +74,51 @@ static bool check_handle(device_handle_t hdl,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct requires_context {
|
||||||
|
uint8_t ndevs;
|
||||||
|
const struct device *rdevs[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int requires_visitor(const struct device *dev,
|
||||||
|
void *context)
|
||||||
|
{
|
||||||
|
struct requires_context *ctx = context;
|
||||||
|
const struct device **rdp = ctx->rdevs;
|
||||||
|
|
||||||
|
while (rdp < (ctx->rdevs + ctx->ndevs)) {
|
||||||
|
if (*rdp == NULL) {
|
||||||
|
*rdp = dev;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
++rdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
static void test_requires(void)
|
static void test_requires(void)
|
||||||
{
|
{
|
||||||
size_t nhdls = 0;
|
size_t nhdls = 0;
|
||||||
const device_handle_t *hdls;
|
const device_handle_t *hdls;
|
||||||
const struct device *dev;
|
const struct device *dev;
|
||||||
|
struct requires_context ctx = { 0 };
|
||||||
|
|
||||||
/* TEST_GPIO: no req */
|
/* TEST_GPIO: no req */
|
||||||
dev = device_get_binding(DT_LABEL(TEST_GPIO));
|
dev = device_get_binding(DT_LABEL(TEST_GPIO));
|
||||||
zassert_equal(dev, DEVICE_DT_GET(TEST_GPIO), NULL);
|
zassert_equal(dev, DEVICE_DT_GET(TEST_GPIO), NULL);
|
||||||
hdls = device_required_handles_get(dev, &nhdls);
|
hdls = device_required_handles_get(dev, &nhdls);
|
||||||
zassert_equal(nhdls, 0, NULL);
|
zassert_equal(nhdls, 0, NULL);
|
||||||
|
zassert_equal(0, device_required_foreach(dev, requires_visitor, &ctx),
|
||||||
|
NULL);
|
||||||
|
|
||||||
/* TEST_I2C: no req */
|
/* TEST_I2C: no req */
|
||||||
dev = device_get_binding(DT_LABEL(TEST_I2C));
|
dev = device_get_binding(DT_LABEL(TEST_I2C));
|
||||||
zassert_equal(dev, DEVICE_DT_GET(TEST_I2C), NULL);
|
zassert_equal(dev, DEVICE_DT_GET(TEST_I2C), NULL);
|
||||||
hdls = device_required_handles_get(dev, &nhdls);
|
hdls = device_required_handles_get(dev, &nhdls);
|
||||||
zassert_equal(nhdls, 0, NULL);
|
zassert_equal(nhdls, 0, NULL);
|
||||||
|
zassert_equal(0, device_required_foreach(dev, requires_visitor, &ctx),
|
||||||
|
NULL);
|
||||||
|
|
||||||
/* TEST_DEVA: TEST_I2C GPIO */
|
/* TEST_DEVA: TEST_I2C GPIO */
|
||||||
dev = device_get_binding(DT_LABEL(TEST_DEVA));
|
dev = device_get_binding(DT_LABEL(TEST_DEVA));
|
||||||
|
@ -100,12 +128,39 @@ static void test_requires(void)
|
||||||
zassert_true(check_handle(DEV_HDL(TEST_I2C), hdls, nhdls), NULL);
|
zassert_true(check_handle(DEV_HDL(TEST_I2C), hdls, nhdls), NULL);
|
||||||
zassert_true(check_handle(DEV_HDL(TEST_GPIO), hdls, nhdls), NULL);
|
zassert_true(check_handle(DEV_HDL(TEST_GPIO), hdls, nhdls), NULL);
|
||||||
|
|
||||||
|
/* Visit fails if not enough space */
|
||||||
|
ctx = (struct requires_context){
|
||||||
|
.ndevs = 1,
|
||||||
|
};
|
||||||
|
zassert_equal(-ENOSPC, device_required_foreach(dev, requires_visitor, &ctx),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Visit succeeds if enough space. */
|
||||||
|
ctx = (struct requires_context){
|
||||||
|
.ndevs = 2,
|
||||||
|
};
|
||||||
|
zassert_equal(2, device_required_foreach(dev, requires_visitor, &ctx),
|
||||||
|
NULL);
|
||||||
|
zassert_true((ctx.rdevs[0] == device_from_handle(DEV_HDL(TEST_I2C)))
|
||||||
|
|| (ctx.rdevs[1] == device_from_handle(DEV_HDL(TEST_I2C))),
|
||||||
|
NULL);
|
||||||
|
zassert_true((ctx.rdevs[0] == device_from_handle(DEV_HDL(TEST_GPIO)))
|
||||||
|
|| (ctx.rdevs[1] == device_from_handle(DEV_HDL(TEST_GPIO))),
|
||||||
|
NULL);
|
||||||
|
|
||||||
/* TEST_GPIOX: TEST_I2C */
|
/* TEST_GPIOX: TEST_I2C */
|
||||||
dev = device_get_binding(DT_LABEL(TEST_GPIOX));
|
dev = device_get_binding(DT_LABEL(TEST_GPIOX));
|
||||||
zassert_equal(dev, DEVICE_DT_GET(TEST_GPIOX), NULL);
|
zassert_equal(dev, DEVICE_DT_GET(TEST_GPIOX), NULL);
|
||||||
hdls = device_required_handles_get(dev, &nhdls);
|
hdls = device_required_handles_get(dev, &nhdls);
|
||||||
zassert_equal(nhdls, 1, NULL);
|
zassert_equal(nhdls, 1, NULL);
|
||||||
zassert_true(check_handle(DEV_HDL(TEST_I2C), hdls, nhdls), NULL);
|
zassert_true(check_handle(DEV_HDL(TEST_I2C), hdls, nhdls), NULL);
|
||||||
|
ctx = (struct requires_context){
|
||||||
|
.ndevs = 3,
|
||||||
|
};
|
||||||
|
zassert_equal(1, device_required_foreach(dev, requires_visitor, &ctx),
|
||||||
|
NULL);
|
||||||
|
zassert_true(ctx.rdevs[0] == device_from_handle(DEV_HDL(TEST_I2C)),
|
||||||
|
NULL);
|
||||||
|
|
||||||
/* TEST_DEVB: TEST_I2C TEST_GPIOX */
|
/* TEST_DEVB: TEST_I2C TEST_GPIOX */
|
||||||
dev = device_get_binding(DT_LABEL(TEST_DEVB));
|
dev = device_get_binding(DT_LABEL(TEST_DEVB));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue