Skip to content

Commit f816c22

Browse files
committed
device: add new device_put API
Add a new API to "put" a device. This function will decrease device usage counter and, if it has a de-init operation, it will be called. This, together with device_get() can be used to initialize/de-initialize at runtime. 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>
1 parent b71909d commit f816c22

File tree

2 files changed

+74
-14
lines changed

2 files changed

+74
-14
lines changed

include/zephyr/device.h

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ typedef int16_t device_handle_t;
168168
#define DEVICE_DEFINE(dev_id, name, init_fn, pm, data, config, level, prio, \
169169
api) \
170170
Z_DEVICE_STATE_DEFINE(dev_id); \
171-
Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, name, init_fn, 0U, pm, data, \
172-
config, level, prio, api, \
171+
Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, name, init_fn, NULL, 0U, pm, \
172+
data, config, level, prio, api, \
173173
&Z_DEVICE_STATE_NAME(dev_id))
174174

175175
/**
@@ -231,7 +231,7 @@ typedef int16_t device_handle_t;
231231
...) \
232232
Z_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \
233233
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \
234-
DEVICE_DT_NAME(node_id), init_fn, \
234+
DEVICE_DT_NAME(node_id), init_fn, NULL, \
235235
Z_DEVICE_DT_FLAGS(node_id), pm, data, config, level, \
236236
prio, api, \
237237
&Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)), \
@@ -472,6 +472,8 @@ typedef uint8_t device_flags_t;
472472
struct device_ops {
473473
/** Initialization function */
474474
int (*init)(const struct device *dev);
475+
/** De-initialization function */
476+
int (*deinit)(const struct device *dev);
475477
};
476478

477479
/**
@@ -832,6 +834,20 @@ size_t z_device_get_all_static(const struct device **devices);
832834
*/
833835
__syscall int device_get(const struct device *dev);
834836

837+
/**
838+
* Put a device.
839+
*
840+
* When putting a device, its usage count will be decreased and, if it reaches
841+
* 0, its de-initialization routine will be called.
842+
*
843+
* @param dev Device instance
844+
*
845+
* @retval -errno Device failed to be put (de-initialization failed).
846+
* @retval >=0 Device was successfully put. Values > 0 indicate the current
847+
* reference count.
848+
*/
849+
__syscall int device_put(const struct device *dev);
850+
835851
/**
836852
* @brief Verify that a device is ready for use.
837853
*
@@ -1125,6 +1141,7 @@ device_get_dt_nodelabels(const struct device *dev)
11251141
*
11261142
* @param name_ Name of the device.
11271143
* @param init_fn_ Init function (optional).
1144+
* @param deinit_fn_ De-init function (optional).
11281145
* @param flags_ Device flags.
11291146
* @param pm_ Reference to @ref pm_device_base (optional).
11301147
* @param data_ Reference to device data.
@@ -1135,15 +1152,15 @@ device_get_dt_nodelabels(const struct device *dev)
11351152
* @param node_id_ Devicetree node identifier
11361153
* @param dev_id_ Device identifier token, as passed to Z_DEVICE_BASE_DEFINE
11371154
*/
1138-
#define Z_DEVICE_INIT(name_, init_fn_, flags_, pm_, data_, config_, api_, state_, \
1139-
deps_, node_id_, dev_id_) \
1155+
#define Z_DEVICE_INIT(name_, init_fn_, deinit_fn_, flags_, pm_, data_, config_, api_, \
1156+
state_, deps_, node_id_, dev_id_) \
11401157
{ \
11411158
.name = name_, \
11421159
.config = (config_), \
11431160
.api = (api_), \
11441161
.state = (state_), \
11451162
.data = (data_), \
1146-
.ops = { .init = (init_fn_) }, \
1163+
.ops = { .init = (init_fn_), .deinit = (deinit_fn_) }, \
11471164
.flags = (flags_), \
11481165
IF_ENABLED(CONFIG_DEVICE_DEPS, (.deps = (deps_),)) /**/ \
11491166
IF_ENABLED(CONFIG_PM_DEVICE, Z_DEVICE_INIT_PM_BASE(pm_)) /**/ \
@@ -1181,6 +1198,7 @@ device_get_dt_nodelabels(const struct device *dev)
11811198
* @param dev_id Device identifier (used to name the defined @ref device).
11821199
* @param name Name of the device.
11831200
* @param init_fn Init function.
1201+
* @param deinit_fn De-init function.
11841202
* @param flags Device flags.
11851203
* @param pm Reference to @ref pm_device_base associated with the device.
11861204
* (optional).
@@ -1191,15 +1209,15 @@ device_get_dt_nodelabels(const struct device *dev)
11911209
* @param api Reference to device API.
11921210
* @param ... Optional dependencies, manually specified.
11931211
*/
1194-
#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, flags, pm, data, config, level, prio, \
1195-
api, state, deps) \
1212+
#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, deinit_fn, flags, pm, data, config, \
1213+
level, prio, api, state, deps) \
11961214
COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \
11971215
COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (), (const)) \
11981216
STRUCT_SECTION_ITERABLE_NAMED_ALTERNATE( \
11991217
device, COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (device_mutable), (device)), \
12001218
Z_DEVICE_SECTION_NAME(level, prio), DEVICE_NAME_GET(dev_id)) = \
1201-
Z_DEVICE_INIT(name, init_fn, flags, pm, data, config, api, state, deps, node_id, \
1202-
dev_id)
1219+
Z_DEVICE_INIT(name, init_fn, deinit_fn, flags, pm, data, config, api, state, deps, \
1220+
node_id, dev_id)
12031221

12041222
/**
12051223
* @brief Issue an error if the given init level is not supported.
@@ -1252,8 +1270,8 @@ device_get_dt_nodelabels(const struct device *dev)
12521270
* @param state Reference to device state.
12531271
* @param ... Optional dependencies, manually specified.
12541272
*/
1255-
#define Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, flags, pm, data, config,\
1256-
level, prio, api, state, ...) \
1273+
#define Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, deinit_fn, flags, pm, \
1274+
data, config, level, prio, api, state, ...) \
12571275
Z_DEVICE_NAME_CHECK(name); \
12581276
\
12591277
IF_ENABLED(CONFIG_DEVICE_DEPS, \
@@ -1263,8 +1281,8 @@ device_get_dt_nodelabels(const struct device *dev)
12631281
(IF_ENABLED(DT_NODE_EXISTS(node_id), \
12641282
(Z_DEVICE_DT_METADATA_DEFINE(node_id, dev_id);))))\
12651283
\
1266-
Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, flags, pm, data, \
1267-
config, level, prio, api, state, \
1284+
Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, deinit_fn, flags, \
1285+
pm, data, config, level, prio, api, state, \
12681286
Z_DEVICE_DEPS_NAME(dev_id)); \
12691287
\
12701288
Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, level, prio); \

kernel/device.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,40 @@ int z_impl_device_get(const struct device *dev)
166166
return ret;
167167
}
168168

169+
int z_impl_device_put(const struct device *dev)
170+
{
171+
int ret = 0;
172+
173+
if (dev == NULL) {
174+
return -ENODEV;
175+
}
176+
177+
#ifdef CONFIG_MULTITHREADING
178+
if (!k_is_pre_kernel()) {
179+
(void)k_sem_take(&dev->state->usage_lock, K_FOREVER);
180+
}
181+
#endif
182+
183+
__ASSERT(dev->state->usage > 0U, "Unbalanced device get/put");
184+
185+
if ((dev->state->usage == 1U) && (dev->ops.deinit != NULL)) {
186+
ret = dev->ops.deinit(dev);
187+
}
188+
189+
if (ret == 0) {
190+
dev->state->usage--;
191+
ret = dev->state->usage;
192+
}
193+
194+
#ifdef CONFIG_MULTITHREADING
195+
if (!k_is_pre_kernel()) {
196+
(void)k_sem_give(&dev->state->usage_lock);
197+
}
198+
#endif
199+
200+
return ret;
201+
}
202+
169203
#ifdef CONFIG_USERSPACE
170204
static inline int z_vrfy_device_get(const struct device *dev)
171205
{
@@ -174,6 +208,14 @@ static inline int z_vrfy_device_get(const struct device *dev)
174208
return z_impl_device_get(dev);
175209
}
176210
#include <zephyr/syscalls/device_get_mrsh.c>
211+
212+
static inline int z_vrfy_device_put(const struct device *dev)
213+
{
214+
K_OOPS(K_SYSCALL_OBJ_INIT(dev, K_OBJ_ANY));
215+
216+
return z_impl_device_put(dev);
217+
}
218+
#include <zephyr/syscalls/device_put_mrsh.c>
177219
#endif
178220

179221
#ifdef CONFIG_DEVICE_DEPS

0 commit comments

Comments
 (0)