zephyr/drivers/gpio/gpio_utils.h
Peter A. Bigot d75495709d drivers: gpio: fix mis-use of slist API in callback processing
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>
2019-02-05 12:33:25 -06:00

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_ */