d75495709d
The iterator over registered callbacks failed to account for the possibility that the callback would remove itself from the list. If this occurred any remaining callbacks would no longer be reachable from the node. Switch to the slist iterator that is safe for self-removal. Note that the slist API remains unsafe for removal of subsequent nodes. Even with the corrected code removal of the next callback registration (cached in tmp) will result in it being called anyway, with the remaining unremoved registrations not being called. If the next callback were removed and re-registered on a different device, the callbacks would be invoked for the wrong device. Resolve this by a documentation change describing the conditions under which a change to callback registration from within a callback are permitted. Add a similar note regarding the effect of adding a callback. The current event invocation behavior for callbacks added within an event is explicitly left unspecified, though in the current slist implementation newly added callbacks will not be invoked until the next event. Closes #10186 Signed-off-by: Peter A. Bigot <pab@pabigot.com>
68 lines
1.6 KiB
C
68 lines
1.6 KiB
C
/*
|
|
* Copyright (c) 2016 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file Header where utility code can be found for GPIO drivers
|
|
*/
|
|
|
|
#ifndef ZEPHYR_DRIVERS_GPIO_GPIO_UTILS_H_
|
|
#define ZEPHYR_DRIVERS_GPIO_GPIO_UTILS_H_
|
|
|
|
|
|
/**
|
|
* @brief Generic function to insert or remove a callback from a callback list
|
|
*
|
|
* @param callbacks A pointer to the original list of callbacks (can be NULL)
|
|
* @param callback A pointer of the callback to insert or remove from the list
|
|
* @param set A boolean indicating insertion or removal of the callback
|
|
*
|
|
* @return 0 on success, negative errno otherwise.
|
|
*/
|
|
static inline int _gpio_manage_callback(sys_slist_t *callbacks,
|
|
struct gpio_callback *callback,
|
|
bool set)
|
|
{
|
|
__ASSERT(callback, "No callback!");
|
|
__ASSERT(callback->handler, "No callback handler!");
|
|
|
|
if (!sys_slist_is_empty(callbacks)) {
|
|
if (!sys_slist_find_and_remove(callbacks, &callback->node)) {
|
|
if (!set) {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (set) {
|
|
sys_slist_prepend(callbacks, &callback->node);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Generic function to go through and fire callback from a callback list
|
|
*
|
|
* @param list A pointer on the gpio callback list
|
|
* @param port A pointer on the gpio driver instance
|
|
* @param pins The actual pin mask that triggered the interrupt
|
|
*/
|
|
static inline void _gpio_fire_callbacks(sys_slist_t *list,
|
|
struct device *port,
|
|
u32_t pins)
|
|
{
|
|
struct gpio_callback *cb, *tmp;
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(list, cb, tmp, node) {
|
|
if (cb->pin_mask & pins) {
|
|
__ASSERT(cb->handler, "No callback handler!");
|
|
cb->handler(port, cb, pins);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* ZEPHYR_DRIVERS_GPIO_GPIO_UTILS_H_ */
|