1*cf707bd0SJens Wiklander /* SPDX-License-Identifier: BSD-2-Clause */ 2*cf707bd0SJens Wiklander /* 3*cf707bd0SJens Wiklander * Copyright (c) 2024, Linaro Limited 4*cf707bd0SJens Wiklander */ 5*cf707bd0SJens Wiklander 6*cf707bd0SJens Wiklander #ifndef __KERNEL_CALLOUT_H 7*cf707bd0SJens Wiklander #define __KERNEL_CALLOUT_H 8*cf707bd0SJens Wiklander 9*cf707bd0SJens Wiklander #include <stdbool.h> 10*cf707bd0SJens Wiklander #include <stdint.h> 11*cf707bd0SJens Wiklander #include <sys/queue.h> 12*cf707bd0SJens Wiklander 13*cf707bd0SJens Wiklander /* 14*cf707bd0SJens Wiklander * struct callout - callout reference 15*cf707bd0SJens Wiklander * @callback: function to be called when a callout expires 16*cf707bd0SJens Wiklander * @expiry_value: callout expiry time counter value 17*cf707bd0SJens Wiklander * @period: ticks to next timeout 18*cf707bd0SJens Wiklander * @link: linked list element 19*cf707bd0SJens Wiklander * 20*cf707bd0SJens Wiklander * @callback is called from an interrupt handler so thread resources must 21*cf707bd0SJens Wiklander * not be used. The main callout service lock is held while @callback is 22*cf707bd0SJens Wiklander * called so callout_rem() and callout_add() can't be used, but it is safe 23*cf707bd0SJens Wiklander * to call callout_set_next_timeout() if the call period should be changed. 24*cf707bd0SJens Wiklander * @callback returns true if it should be called again in @period ticks 25*cf707bd0SJens Wiklander * or false if the callout should be removed and inactivated. Returning 26*cf707bd0SJens Wiklander * false from @callback is the equivalent of calling callout_rem() on the 27*cf707bd0SJens Wiklander * callout reference. 28*cf707bd0SJens Wiklander */ 29*cf707bd0SJens Wiklander struct callout { 30*cf707bd0SJens Wiklander bool (*callback)(struct callout *co); 31*cf707bd0SJens Wiklander uint64_t expiry_value; 32*cf707bd0SJens Wiklander uint64_t period; 33*cf707bd0SJens Wiklander TAILQ_ENTRY(callout) link; 34*cf707bd0SJens Wiklander }; 35*cf707bd0SJens Wiklander 36*cf707bd0SJens Wiklander /* 37*cf707bd0SJens Wiklander * callout_add() - Add a callout 38*cf707bd0SJens Wiklander * @co: callout reference 39*cf707bd0SJens Wiklander * @callback: callback function accociated with the callout 40*cf707bd0SJens Wiklander * @ms: time to next callout in milliseconds 41*cf707bd0SJens Wiklander * 42*cf707bd0SJens Wiklander * Adds a callout to the callout service with an associated callback 43*cf707bd0SJens Wiklander * function @callback that is to be called in @ms milliseconds. 44*cf707bd0SJens Wiklander * 45*cf707bd0SJens Wiklander * If callout_add() is called before callout_service_init() has been called 46*cf707bd0SJens Wiklander * then it will be called @ms milliseconds after callout_service_init() has 47*cf707bd0SJens Wiklander * been called. 48*cf707bd0SJens Wiklander * 49*cf707bd0SJens Wiklander * The callout structure can reside in global data or on the heap. It's 50*cf707bd0SJens Wiklander * safe to embed it inside another struct, but it must not be freed until 51*cf707bd0SJens Wiklander * removed with callout_rem() or equivalent. 52*cf707bd0SJens Wiklander * 53*cf707bd0SJens Wiklander * The function takes the main callout service for synchronization so it 54*cf707bd0SJens Wiklander * can't be called from within a callback function in a callout or there's 55*cf707bd0SJens Wiklander * deadlock. 56*cf707bd0SJens Wiklander */ 57*cf707bd0SJens Wiklander void callout_add(struct callout *co, bool (*callback)(struct callout *co), 58*cf707bd0SJens Wiklander uint32_t ms); 59*cf707bd0SJens Wiklander 60*cf707bd0SJens Wiklander /* 61*cf707bd0SJens Wiklander * callout_rem() - Remove a callout 62*cf707bd0SJens Wiklander * @co: callout reference 63*cf707bd0SJens Wiklander * 64*cf707bd0SJens Wiklander * Removes a callout previously added to the callout service with 65*cf707bd0SJens Wiklander * callout_add(). Note that when the callback function in a callout 66*cf707bd0SJens Wiklander * returns false the callout is also removed. 67*cf707bd0SJens Wiklander * 68*cf707bd0SJens Wiklander * It's safe to try to remove a callback even if it isn't active any 69*cf707bd0SJens Wiklander * longer. Nothing will happen in that case, but it's guaranteed to be 70*cf707bd0SJens Wiklander * inactive and it's safe to free the memory after callout_rem() has 71*cf707bd0SJens Wiklander * returned. 72*cf707bd0SJens Wiklander */ 73*cf707bd0SJens Wiklander void callout_rem(struct callout *co); 74*cf707bd0SJens Wiklander 75*cf707bd0SJens Wiklander /* 76*cf707bd0SJens Wiklander * callout_set_next_timeout() - set time to next callout 77*cf707bd0SJens Wiklander * @co: callout reference 78*cf707bd0SJens Wiklander * @ms: time to next callout in milliseconds 79*cf707bd0SJens Wiklander * 80*cf707bd0SJens Wiklander * Updates the @co->ticks field with the new number of ticks based on @ms. 81*cf707bd0SJens Wiklander * This value is used to when to calculate the time of the next callout 82*cf707bd0SJens Wiklander * following then one already set. 83*cf707bd0SJens Wiklander * 84*cf707bd0SJens Wiklander * Must only be called from @co->callback() when the callout is triggered. 85*cf707bd0SJens Wiklander */ 86*cf707bd0SJens Wiklander void callout_set_next_timeout(struct callout *co, uint32_t ms); 87*cf707bd0SJens Wiklander 88*cf707bd0SJens Wiklander /* 89*cf707bd0SJens Wiklander * struct callout_timer_desc - callout timer descriptor 90*cf707bd0SJens Wiklander * @disable_timeout: disables the timer from triggering an interrupt 91*cf707bd0SJens Wiklander * @set_next_timeout: sets the next timeout and enables the timer 92*cf707bd0SJens Wiklander * @ms_to_ticks: converts milliseconds to ticks, the counter value 93*cf707bd0SJens Wiklander * unit 94*cf707bd0SJens Wiklander * @get_now: get the current counter value 95*cf707bd0SJens Wiklander * @is_per_cpu: flag to indicate if this timer is per CPU (true) or 96*cf707bd0SJens Wiklander * global (false). 97*cf707bd0SJens Wiklander * 98*cf707bd0SJens Wiklander * This descriptor provides an abstract timer interface first used by 99*cf707bd0SJens Wiklander * callout_service_init() and then stored to be used by 100*cf707bd0SJens Wiklander * callout_service_cb(). 101*cf707bd0SJens Wiklander * 102*cf707bd0SJens Wiklander * When @is_per_cpu is true there is one private timer per CPU so 103*cf707bd0SJens Wiklander * @disable_timeout() and @set_next_timeout() only affects the timer on the 104*cf707bd0SJens Wiklander * current CPU. If for instance @set_next_timeout() is called on a new CPU 105*cf707bd0SJens Wiklander * compared to last time the timer on the old CPU will remain unchanged. 106*cf707bd0SJens Wiklander * Timer interrupts may trigger based on obsolete configuration, the 107*cf707bd0SJens Wiklander * callout service is expected to handle this gracefully. 108*cf707bd0SJens Wiklander */ 109*cf707bd0SJens Wiklander struct callout_timer_desc { 110*cf707bd0SJens Wiklander void (*disable_timeout)(const struct callout_timer_desc *desc); 111*cf707bd0SJens Wiklander void (*set_next_timeout)(const struct callout_timer_desc *desc, 112*cf707bd0SJens Wiklander uint64_t expiry_value); 113*cf707bd0SJens Wiklander uint64_t (*ms_to_ticks)(const struct callout_timer_desc *desc, 114*cf707bd0SJens Wiklander uint32_t ms); 115*cf707bd0SJens Wiklander uint64_t (*get_now)(const struct callout_timer_desc *desc); 116*cf707bd0SJens Wiklander bool is_per_cpu; 117*cf707bd0SJens Wiklander }; 118*cf707bd0SJens Wiklander 119*cf707bd0SJens Wiklander /* 120*cf707bd0SJens Wiklander * callout_service_init() - Initialize the callout service 121*cf707bd0SJens Wiklander * @desc: Pointer to the timer interface 122*cf707bd0SJens Wiklander * 123*cf707bd0SJens Wiklander * The callout service is initialized with the supplied timer interface 124*cf707bd0SJens Wiklander */ 125*cf707bd0SJens Wiklander void callout_service_init(const struct callout_timer_desc *desc); 126*cf707bd0SJens Wiklander 127*cf707bd0SJens Wiklander /* 128*cf707bd0SJens Wiklander * callout_service_cb() - Callout service callback 129*cf707bd0SJens Wiklander * 130*cf707bd0SJens Wiklander * Called from interrupt service function for the timer. 131*cf707bd0SJens Wiklander */ 132*cf707bd0SJens Wiklander void callout_service_cb(void); 133*cf707bd0SJens Wiklander 134*cf707bd0SJens Wiklander #endif /*__KERNEL_CALLOUT_H*/ 135