Browse Source

device: add new device_deinit API

Add a new API to de-initialize a device. When a device is
de-initialized, it will release any resources it has acquired
(e.g. pins, memory, clocks, DMA channels, etc.) and its status
will be left as in its reset state.

It is the responsability of the caller to ensure that the device is
ready to be de-initialized.

For now, deinit call always initializes to NULL. New macros will be
introduced to not break existing device APIs.

Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
pull/85182/head
Gerard Marull-Paretas 6 months ago committed by Henrik Brix Andersen
parent
commit
8e59d4cd9d
  1. 51
      include/zephyr/device.h
  2. 4
      include/zephyr/drivers/can.h
  3. 4
      include/zephyr/drivers/i2c.h
  4. 2
      include/zephyr/drivers/smbus.h
  5. 4
      include/zephyr/drivers/spi.h
  6. 2
      include/zephyr/net/ethernet.h
  7. 4
      include/zephyr/net/net_if.h
  8. 33
      kernel/device.c

51
include/zephyr/device.h

@ -167,8 +167,8 @@ typedef int16_t device_handle_t; @@ -167,8 +167,8 @@ typedef int16_t device_handle_t;
#define DEVICE_DEFINE(dev_id, name, init_fn, pm, data, config, level, prio, \
api) \
Z_DEVICE_STATE_DEFINE(dev_id); \
Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, name, init_fn, 0U, pm, data, \
config, level, prio, api, \
Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, name, init_fn, NULL, 0U, pm, \
data, config, level, prio, api, \
&Z_DEVICE_STATE_NAME(dev_id))
/**
@ -220,7 +220,7 @@ typedef int16_t device_handle_t; @@ -220,7 +220,7 @@ typedef int16_t device_handle_t;
...) \
Z_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \
DEVICE_DT_NAME(node_id), init_fn, \
DEVICE_DT_NAME(node_id), init_fn, NULL, \
Z_DEVICE_DT_FLAGS(node_id), pm, data, config, level, \
prio, api, \
&Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)), \
@ -455,6 +455,8 @@ typedef uint8_t device_flags_t; @@ -455,6 +455,8 @@ typedef uint8_t device_flags_t;
struct device_ops {
/** Initialization function */
int (*init)(const struct device *dev);
/** De-initialization function */
int (*deinit)(const struct device *dev);
};
/**
@ -834,6 +836,25 @@ __syscall bool device_is_ready(const struct device *dev); @@ -834,6 +836,25 @@ __syscall bool device_is_ready(const struct device *dev);
*/
__syscall int device_init(const struct device *dev);
/**
* @brief De-initialize a device.
*
* When a device is de-initialized, it will release any resources it has
* acquired (e.g. pins, memory, clocks, DMA channels, etc.) and its status will
* be left as in its reset state.
*
* @warning It is the responsability of the caller to ensure that the device is
* ready to be de-initialized.
*
* @param dev device to be de-initialized.
*
* @retval 0 If successful
* @retval -EPERM If device has not been initialized.
* @retval -ENOTSUP If device does not support de-initialization.
* @retval -errno For any other errors.
*/
__syscall int device_deinit(const struct device *dev);
/**
* @}
*/
@ -1078,6 +1099,7 @@ device_get_dt_nodelabels(const struct device *dev) @@ -1078,6 +1099,7 @@ device_get_dt_nodelabels(const struct device *dev)
*
* @param name_ Name of the device.
* @param init_fn_ Init function (optional).
* @param deinit_fn_ De-init function (optional).
* @param flags_ Device flags.
* @param pm_ Reference to @ref pm_device_base (optional).
* @param data_ Reference to device data.
@ -1088,15 +1110,15 @@ device_get_dt_nodelabels(const struct device *dev) @@ -1088,15 +1110,15 @@ device_get_dt_nodelabels(const struct device *dev)
* @param node_id_ Devicetree node identifier
* @param dev_id_ Device identifier token, as passed to Z_DEVICE_BASE_DEFINE
*/
#define Z_DEVICE_INIT(name_, init_fn_, flags_, pm_, data_, config_, api_, state_, \
deps_, node_id_, dev_id_) \
#define Z_DEVICE_INIT(name_, init_fn_, deinit_fn_, flags_, pm_, data_, config_, api_, \
state_, deps_, node_id_, dev_id_) \
{ \
.name = name_, \
.config = (config_), \
.api = (api_), \
.state = (state_), \
.data = (data_), \
.ops = { .init = (init_fn_) }, \
.ops = { .init = (init_fn_), .deinit = (deinit_fn_) }, \
.flags = (flags_), \
IF_ENABLED(CONFIG_DEVICE_DEPS, (.deps = (deps_),)) /**/ \
IF_ENABLED(CONFIG_PM_DEVICE, Z_DEVICE_INIT_PM_BASE(pm_)) /**/ \
@ -1134,6 +1156,7 @@ device_get_dt_nodelabels(const struct device *dev) @@ -1134,6 +1156,7 @@ device_get_dt_nodelabels(const struct device *dev)
* @param dev_id Device identifier (used to name the defined @ref device).
* @param name Name of the device.
* @param init_fn Init function.
* @param deinit_fn De-init function.
* @param flags Device flags.
* @param pm Reference to @ref pm_device_base associated with the device.
* (optional).
@ -1144,15 +1167,15 @@ device_get_dt_nodelabels(const struct device *dev) @@ -1144,15 +1167,15 @@ device_get_dt_nodelabels(const struct device *dev)
* @param api Reference to device API.
* @param ... Optional dependencies, manually specified.
*/
#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, flags, pm, data, config, level, prio, \
api, state, deps) \
#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, deinit_fn, flags, pm, data, config, \
level, prio, api, state, deps) \
COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \
COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (), (const)) \
STRUCT_SECTION_ITERABLE_NAMED_ALTERNATE( \
device, COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (device_mutable), (device)), \
Z_DEVICE_SECTION_NAME(level, prio), DEVICE_NAME_GET(dev_id)) = \
Z_DEVICE_INIT(name, init_fn, flags, pm, data, config, api, state, deps, node_id, \
dev_id)
Z_DEVICE_INIT(name, init_fn, deinit_fn, flags, pm, data, config, api, state, deps, \
node_id, dev_id)
/**
* @brief Issue an error if the given init level is not supported.
@ -1205,8 +1228,8 @@ device_get_dt_nodelabels(const struct device *dev) @@ -1205,8 +1228,8 @@ device_get_dt_nodelabels(const struct device *dev)
* @param state Reference to device state.
* @param ... Optional dependencies, manually specified.
*/
#define Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, flags, pm, data, config,\
level, prio, api, state, ...) \
#define Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, deinit_fn, flags, pm, \
data, config, level, prio, api, state, ...) \
Z_DEVICE_NAME_CHECK(name); \
\
IF_ENABLED(CONFIG_DEVICE_DEPS, \
@ -1216,8 +1239,8 @@ device_get_dt_nodelabels(const struct device *dev) @@ -1216,8 +1239,8 @@ device_get_dt_nodelabels(const struct device *dev)
(IF_ENABLED(DT_NODE_EXISTS(node_id), \
(Z_DEVICE_DT_METADATA_DEFINE(node_id, dev_id);))))\
\
Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, flags, pm, data, \
config, level, prio, api, state, \
Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, deinit_fn, flags, \
pm, data, config, level, prio, api, state, \
Z_DEVICE_DEPS_NAME(dev_id)); \
\
Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, level, prio); \

4
include/zephyr/drivers/can.h

@ -761,8 +761,8 @@ struct can_device_state { @@ -761,8 +761,8 @@ struct can_device_state {
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \
DEVICE_DT_NAME(node_id), \
&UTIL_CAT(Z_DEVICE_DT_DEV_ID(node_id), _init), \
Z_DEVICE_DT_FLAGS(node_id), pm, data, config, \
level, prio, api, \
NULL, Z_DEVICE_DT_FLAGS(node_id), pm, data, \
config, level, prio, api, \
&(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)).devstate), \
__VA_ARGS__)

4
include/zephyr/drivers/i2c.h

@ -668,8 +668,8 @@ static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs @@ -668,8 +668,8 @@ static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \
DEVICE_DT_NAME(node_id), \
&UTIL_CAT(Z_DEVICE_DT_DEV_ID(node_id), _init), \
Z_DEVICE_DT_FLAGS(node_id), pm, data, config, \
level, prio, api, \
NULL, Z_DEVICE_DT_FLAGS(node_id), pm, data, \
config, level, prio, api, \
&(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)).devstate), \
__VA_ARGS__)

2
include/zephyr/drivers/smbus.h

@ -510,7 +510,7 @@ static inline void smbus_xfer_stats(const struct device *dev, uint8_t sent, @@ -510,7 +510,7 @@ static inline void smbus_xfer_stats(const struct device *dev, uint8_t sent,
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id), \
DEVICE_DT_NAME(node_id), \
&UTIL_CAT(Z_DEVICE_DT_DEV_NAME(node_id), _init),\
Z_DEVICE_DT_FLAGS(node_id), pm_device, \
NULL, Z_DEVICE_DT_FLAGS(node_id), pm_device, \
data_ptr, cfg_ptr, level, prio, \
api_ptr, \
&(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_NAME \

4
include/zephyr/drivers/spi.h

@ -579,7 +579,7 @@ struct spi_device_state { @@ -579,7 +579,7 @@ struct spi_device_state {
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \
DEVICE_DT_NAME(node_id), \
&UTIL_CAT(Z_DEVICE_DT_DEV_ID(node_id), _init), \
Z_DEVICE_DT_FLAGS(node_id), pm_device, \
NULL, Z_DEVICE_DT_FLAGS(node_id), pm_device, \
data_ptr, cfg_ptr, level, prio, \
api_ptr, \
&(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)).devstate), \
@ -614,7 +614,7 @@ static inline void spi_transceive_stats(const struct device *dev, int error, @@ -614,7 +614,7 @@ static inline void spi_transceive_stats(const struct device *dev, int error,
api, ...) \
Z_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \
DEVICE_DT_NAME(node_id), init_fn, \
DEVICE_DT_NAME(node_id), init_fn, NULL, \
Z_DEVICE_DT_FLAGS(node_id), pm, data, config, \
level, prio, api, \
&Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)), \

2
include/zephyr/net/ethernet.h

@ -1171,7 +1171,7 @@ static inline bool net_eth_is_vlan_interface(struct net_if *iface) @@ -1171,7 +1171,7 @@ static inline bool net_eth_is_vlan_interface(struct net_if *iface)
init_fn, pm, data, config, prio, \
api, mtu) \
Z_DEVICE_STATE_DEFINE(dev_id); \
Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, \
Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, NULL, \
Z_DEVICE_DT_FLAGS(node_id), pm, data, \
config, POST_KERNEL, prio, api, \
&Z_DEVICE_STATE_NAME(dev_id));

4
include/zephyr/net/net_if.h

@ -3378,7 +3378,7 @@ extern int net_stats_prometheus_scrape(struct prometheus_collector *collector, @@ -3378,7 +3378,7 @@ extern int net_stats_prometheus_scrape(struct prometheus_collector *collector,
init_fn, pm, data, config, prio, \
api, l2, l2_ctx_type, mtu) \
Z_DEVICE_STATE_DEFINE(dev_id); \
Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, \
Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, NULL, \
Z_DEVICE_DT_FLAGS(node_id), pm, data, \
config, POST_KERNEL, prio, api, \
&Z_DEVICE_STATE_NAME(dev_id)); \
@ -3526,7 +3526,7 @@ extern int net_stats_prometheus_scrape(struct prometheus_collector *collector, @@ -3526,7 +3526,7 @@ extern int net_stats_prometheus_scrape(struct prometheus_collector *collector,
#define Z_NET_DEVICE_OFFLOAD_INIT(node_id, dev_id, name, init_fn, pm, \
data, config, prio, api, mtu) \
Z_DEVICE_STATE_DEFINE(dev_id); \
Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, \
Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, NULL, \
Z_DEVICE_DT_FLAGS(node_id), pm, data, \
config, POST_KERNEL, prio, api, \
&Z_DEVICE_STATE_NAME(dev_id)); \

33
kernel/device.c

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <zephyr/device.h>
@ -142,6 +143,38 @@ bool z_impl_device_is_ready(const struct device *dev) @@ -142,6 +143,38 @@ bool z_impl_device_is_ready(const struct device *dev)
return dev->state->initialized && (dev->state->init_res == 0U);
}
int z_impl_device_deinit(const struct device *dev)
{
int ret;
if (!dev->state->initialized) {
return -EPERM;
}
if (dev->ops.deinit == NULL) {
return -ENOTSUP;
}
ret = dev->ops.deinit(dev);
if (ret < 0) {
return ret;
}
dev->state->initialized = false;
return 0;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_device_deinit(const struct device *dev)
{
K_OOPS(K_SYSCALL_OBJ_INIT(dev, K_OBJ_ANY));
return z_impl_device_deinit(dev);
}
#include <zephyr/syscalls/device_deinit_mrsh.c>
#endif
#ifdef CONFIG_DEVICE_DEPS
static int device_visitor(const device_handle_t *handles,

Loading…
Cancel
Save