/*$file${include::qf.h} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /* * Model: qpc.qm * File: ${include::qf.h} * * This code has been generated by QM 5.2.5 . * DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost. * * This code is covered by the following QP license: * License # : LicenseRef-QL-dual * Issued to : Any user of the QP/C real-time embedded framework * Framework(s) : qpc * Support ends : 2023-12-31 * License scope: * * Copyright (C) 2005 Quantum Leaps, LLC . * * SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial * * This software is dual-licensed under the terms of the open source GNU * General Public License version 3 (or any later version), or alternatively, * under the terms of one of the closed source Quantum Leaps commercial * licenses. * * The terms of the open source GNU General Public License version 3 * can be found at: * * The terms of the closed source Quantum Leaps commercial licenses * can be found at: * * Redistributions in source code must retain this top-level comment block. * Plagiarizing this software to sidestep the license obligations is illegal. * * Contact information: * * */ /*$endhead${include::qf.h} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*! @file * @brief QF/C platform-independent public interface. */ #ifndef QF_H_ #define QF_H_ /*==========================================================================*/ /*$declare${QF-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF-config::QF_MAX_ACTIVE} ..............................................*/ #ifndef QF_MAX_ACTIVE /*! Maximum number of active objects (configurable value in qf_port.h) * Valid values: [1U..64U]; default 32U */ #define QF_MAX_ACTIVE 32U #endif /* ndef QF_MAX_ACTIVE */ /*${QF-config::QF_MAX_ACTIVE exceeds the maximu~} ..........................*/ #if (QF_MAX_ACTIVE > 64U) #error QF_MAX_ACTIVE exceeds the maximum of 64U; #endif /* (QF_MAX_ACTIVE > 64U) */ /*${QF-config::QF_MAX_TICK_RATE} ...........................................*/ #ifndef QF_MAX_TICK_RATE /*! Maximum number of clock rates (configurable value in qf_port.h) * Valid values: [0U..15U]; default 1U */ #define QF_MAX_TICK_RATE 1U #endif /* ndef QF_MAX_TICK_RATE */ /*${QF-config::QF_MAX_TICK_RATE exceeds the max~} ..........................*/ #if (QF_MAX_TICK_RATE > 15U) #error QF_MAX_TICK_RATE exceeds the maximum of 15U; #endif /* (QF_MAX_TICK_RATE > 15U) */ /*${QF-config::QF_MAX_EPOOL} ...............................................*/ #ifndef QF_MAX_EPOOL /*! Maximum number of event pools (configurable value in qf_port.h) * Valid values: [0U..15U]; default 3U * * @note * #QF_MAX_EPOOL set to zero means that dynamic events are NOT configured * and should not be used in the application. */ #define QF_MAX_EPOOL 3U #endif /* ndef QF_MAX_EPOOL */ /*${QF-config::QF_MAX_EPOOL exceeds the maximum~} ..........................*/ #if (QF_MAX_EPOOL > 15U) #error QF_MAX_EPOOL exceeds the maximum of 15U; #endif /* (QF_MAX_EPOOL > 15U) */ /*${QF-config::QF_TIMEEVT_CTR_SIZE} ........................................*/ #ifndef QF_TIMEEVT_CTR_SIZE /*! Size of the QTimeEvt counter (configurable value in qf_port.h) * Valid values: 1U, 2U, or 4U; default 4U */ #define QF_TIMEEVT_CTR_SIZE 4U #endif /* ndef QF_TIMEEVT_CTR_SIZE */ /*${QF-config::QF_TIMEEVT_CTR_SIZE defined inco~} ..........................*/ #if (QF_TIMEEVT_CTR_SIZE != 1U) && (QF_TIMEEVT_CTR_SIZE != 2U) && (QF_TIMEEVT_CTR_SIZE != 4U) #error QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U; #endif /* (QF_TIMEEVT_CTR_SIZE != 1U) && (QF_TIMEEVT_CTR_SIZE != 2U) && (QF_TIMEEVT_CTR_SIZE != 4U) */ /*${QF-config::QF_EVENT_SIZ_SIZE} ..........................................*/ #ifndef QF_EVENT_SIZ_SIZE /*! Size of the event-size (configurable value in qf_port.h) * Valid values: 1U, 2U, or 4U; default 2U */ #define QF_EVENT_SIZ_SIZE 2U #endif /* ndef QF_EVENT_SIZ_SIZE */ /*${QF-config::QF_EVENT_SIZ_SIZE defined incorr~} ..........................*/ #if (QF_EVENT_SIZ_SIZE != 1U) && (QF_EVENT_SIZ_SIZE != 2U) && (QF_EVENT_SIZ_SIZE != 4U) #error QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U; #endif /* (QF_EVENT_SIZ_SIZE != 1U) && (QF_EVENT_SIZ_SIZE != 2U) && (QF_EVENT_SIZ_SIZE != 4U) */ /*$enddecl${QF-config} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*==========================================================================*/ /*$declare${QF-types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF-types::QPSetBits} ...................................................*/ #if (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) /*! bitmask for the internal representation of QPSet elements */ typedef uint16_t QPSetBits; #endif /* (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) */ /*${QF-types::QPSetBits} ...................................................*/ #if (16 < QF_MAX_ACTIVE) typedef uint32_t QPSetBits; #endif /* (16 < QF_MAX_ACTIVE) */ /*${QF-types::QPSetBits} ...................................................*/ #if (QF_MAX_ACTIVE <= 8U) typedef uint8_t QPSetBits; #endif /* (QF_MAX_ACTIVE <= 8U) */ /*${QF-types::QTimeEvtCtr} .................................................*/ #if (QF_TIMEEVT_CTR_SIZE == 2U) /*! Data type to store the block-size defined based on the macro * #QF_TIMEEVT_CTR_SIZE. * * @details * The dynamic range of this data type determines the maximum block * size that can be managed by the pool. */ typedef uint16_t QTimeEvtCtr; #endif /* (QF_TIMEEVT_CTR_SIZE == 2U) */ /*${QF-types::QTimeEvtCtr} .................................................*/ #if (QF_TIMEEVT_CTR_SIZE == 4U) typedef uint32_t QTimeEvtCtr; #endif /* (QF_TIMEEVT_CTR_SIZE == 4U) */ /*${QF-types::QTimeEvtCtr} .................................................*/ #if (QF_TIMEEVT_CTR_SIZE == 1U) typedef uint8_t QTimeEvtCtr; #endif /* (QF_TIMEEVT_CTR_SIZE == 1U) */ /*${QF-types::QF_LOG2} .....................................................*/ #ifndef QF_LOG2 /*! Log-base-2 calculation when hardware acceleration * is NOT provided (#QF_LOG2 not defined). * @static @private @memberof QF */ uint_fast8_t QF_LOG2(QPSetBits x); #endif /* ndef QF_LOG2 */ /*${QF-types::QPrioSpec} ...................................................*/ /*! Priority specification for Active Objects in QP * * @details * Active Object priorities in QP are integer numbers in the range * [1..#QF_MAX_ACTIVE], whereas the special priority number 0 is reserved * for the lowest-priority idle thread. The QP framework uses the *direct* * priority numbering, in which higher numerical values denote higher urgency. * For example, an AO with priority 32 has higher urgency than an AO with * priority 23. * * ::QPrioSpec allows an application developer to assign **two** * priorities to a given AO (see also Q_PRIO()): * * 1. The "QF-priority", which resides in the least-significant byte * of the ::QPrioSpec data type. The "QF-priority" must be **unique** * for each thread in the system and higher numerical values represent * higher urgency (direct pirority numbering). * * 2. The "preemption-threshold" priority, which resides in the most- * significant byte of the ::QPrioSpec data type. The second priority * cannot be lower than the "QF-priority", but does NOT need to be * unuque. * * In the QP native preemptive kernels, like QK and QXK, the "preemption- * threshold" priority is used as to implement the "preemption-threshold * scheduling" (PTS). It determines the conditions under which a given * thread can be *preempted* by other threads. Specifically, a given * thread can be preempted only by another thread with a *higher* * priority than the "preemption-threshold" of the original thread. * * ![QF-priority and preemption-threshold relations](qp-prio.png) * * @note * For backwards-compatibility, ::QPrioSpec data type might contain only * the "QF-priority" component (and the "preemption-threshold" component * left at zero). In that case, the "preemption-threshold" will be assumed * to be the same as the "QF-priority". This corresponds exactly to the * previous semantics of AO priority. * * @remark * When QP runs on top of 3rd-party kernels/RTOSes or general-purpose * operating systems, sthe second priority can have different meaning, * depending on the specific RTOS/GPOS used. */ typedef uint16_t QPrioSpec; /*${QF-types::QSchedStatus} ................................................*/ /*! The scheduler lock status used in some real-time kernels */ typedef uint_fast16_t QSchedStatus; /*${QF-types::QPSet} .......................................................*/ /*! @brief Priority Set of up to #QF_MAX_ACTIVE elements * @class QPSet * * @details * The priority set represents the set of active objects that are ready to * run and need to be considered by the scheduling algorithm. The set is * capable of storing up to #QF_MAX_ACTIVE priority levels, which can be * configured in the rage 1..64, inclusive. */ typedef struct { /* public: */ #if (QF_MAX_ACTIVE <= 32) /*! bitmask with a bit for each element */ QPSetBits volatile bits; #endif /* (QF_MAX_ACTIVE <= 32) */ #if (32 < QF_MAX_ACTIVE) /*! bitmasks with a bit for each element */ QPSetBits volatile bits[2]; #endif /* (32 < QF_MAX_ACTIVE) */ } QPSet; /* public: */ /*! Make the priority set empty */ static inline void QPSet_setEmpty(QPSet * const me) { #if (QF_MAX_ACTIVE <= 32) me->bits = 0U; #else me->bits[0] = 0U; me->bits[1] = 0U; #endif } /*! Return 'true' if the priority set is empty */ static inline bool QPSet_isEmpty(QPSet const * const me) { #if (QF_MAX_ACTIVE <= 32) return (me->bits == 0U); #else return (me->bits[0] == 0U) ? (me->bits[1] == 0U) : false; #endif } /*! Return 'true' if the priority set is NOT empty */ static inline bool QPSet_notEmpty(QPSet const * const me) { #if (QF_MAX_ACTIVE <= 32) return (me->bits != 0U); #else return (me->bits[0] != 0U) ? true : (me->bits[1] != 0U); #endif } /*! Return 'true' if the priority set has the element n. */ static inline bool QPSet_hasElement(QPSet const * const me, uint_fast8_t const n) { #if (QF_MAX_ACTIVE <= 32U) return (me->bits & (1U << (n - 1U))) != 0U; #else return (n <= 32U) ? ((me->bits[0] & ((uint32_t)1U << (n - 1U))) != 0U) : ((me->bits[1] & ((uint32_t)1U << (n - 33U))) != 0U); #endif } /*! insert element `n` into the set (n = 1..::QF_MAX_ACTIVE) */ static inline void QPSet_insert(QPSet * const me, uint_fast8_t const n) { #if (QF_MAX_ACTIVE <= 32U) me->bits = (me->bits | (1U << (n - 1U))); #else if (n <= 32U) { me->bits[0] = (me->bits[0] | ((uint32_t)1U << (n - 1U))); } else { me->bits[1] = (me->bits[1] | ((uint32_t)1U << (n - 33U))); } #endif } /*! Remove element `n` from the set (n = 1U..::QF_MAX_ACTIVE) */ static inline void QPSet_remove(QPSet * const me, uint_fast8_t const n) { #if (QF_MAX_ACTIVE <= 32U) me->bits = (me->bits & (QPSetBits)(~((QPSetBits)1U << (n - 1U)))); #else if (n <= 32U) { (me->bits[0] = (me->bits[0] & ~((uint32_t)1U << (n - 1U)))); } else { (me->bits[1] = (me->bits[1] & ~((uint32_t)1U << (n - 33U)))); } #endif } /*! Find the maximum element in the set, returns zero if the set is empty */ static inline uint_fast8_t QPSet_findMax(QPSet const * const me) { #if (QF_MAX_ACTIVE <= 32) return QF_LOG2(me->bits); #else return (me->bits[1] != 0U) ? (QF_LOG2(me->bits[1]) + 32U) : (QF_LOG2(me->bits[0])); #endif } /*${QF-types::QSubscrList} .................................................*/ /*! Subscriber List (for publish-subscribe) * * @details * This data type represents a set of Active Objects that subscribe to * a given signal. The set is represented as priority-set, where each * bit corresponds to the unique QF-priority of an AO (see ::QPrioSpec). */ typedef QPSet QSubscrList; /*$enddecl${QF-types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*$declare${QF::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF::QActive} ...........................................................*/ /*! @brief Active object class (based on the QHsm implementation strategy) * @class QActive * @extends QHsm * * @details * Active objects are encapsulated tasks (each containing an event queue and * a state machine) that communicate with one another asynchronously by * sending and receiving events. Within an active object, events are * processed in a run-to-completion (RTC) fashion, while QF encapsulates * all the details of thread-safe event exchange and queuing.
* * QActive represents an active object that uses the QHsm-style * implementation strategy for state machines. This strategy is tailored * to manual coding, but it is also supported by the QM modeling tool. * The resulting code is slower than in the ::QMsm-style implementation * strategy. * * @note * QActive is not intended to be instantiated directly, but rather serves * as the abstract base class for derivation of active objects in the * applications. * * @sa QMActive * * @usage * The following example illustrates how to derive an active object from * QActive. * @include qf_qactive.c */ typedef struct QActive { /* protected: */ QHsm super; /* private: */ #ifdef QF_EQUEUE_TYPE /*! OS-dependent event-queue type * @private @memberof QActive * * @details * The type of the queue depends on the underlying operating system or * a kernel. Many kernels support "message queues" that can be adapted * to deliver QF events to the active object. Alternatively, QF provides * a native event queue implementation that can be used as well. * * @note * The native QF event queue is configured by defining the macro * #QF_EQUEUE_TYPE as ::QEQueue. */ QF_EQUEUE_TYPE eQueue; #endif /* def QF_EQUEUE_TYPE */ #ifdef QF_OS_OBJECT_TYPE /*! OS-dependent per-thread object * @private @memberof QActive * * @details * This data might be used in various ways, depending on the QF port. * In some ports me->osObject is used to block the calling thread when * the native QF queue is empty. In other QF ports the OS-dependent * object might be used differently. */ QF_OS_OBJECT_TYPE osObject; #endif /* def QF_OS_OBJECT_TYPE */ #ifdef QF_THREAD_TYPE /*! OS-dependent representation of the thread of the active object * @private @memberof QActive * * @details * This data might be used in various ways, depending on the QF port. * In some ports me->thread is used store the thread handle. In other ports * me->thread can be a pointer to the Thread-Local-Storage (TLS). */ QF_THREAD_TYPE thread; #endif /* def QF_THREAD_TYPE */ /* public: */ /*! QF-priority [1..#QF_MAX_ACTIVE] of this AO. * @private @memberof QActive * @sa ::QPrioSpec */ uint8_t prio; /*! preemption-threshold [1..#QF_MAX_ACTIVE] of this AO. * @private @memberof QActive * @sa ::QPrioSpec */ uint8_t pthre; /* private: */ } QActive; /* protected: */ /*! ::QActive constructor (abstract base class) * @protected @memberof QActive * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] initial pointer to the top-most initial state-handler * function in the derived active object * @sa QHsm_ctor() */ void QActive_ctor(QActive * const me, QStateHandler const initial); /* private: */ /*! Starts execution of an active object and registers the object * with the framework * @private @memberof QActive * * @details * Starts execution of the AO and registers the AO with the framework. * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] prioSpec priority specification for the AO (See ::QPrioSpec) * @param[in] qSto pointer to the storage for the ring buffer of the * event queue * @param[in] qLen length of the event queue [# ::QEvt* pointers] * @param[in] stkSto pointer to the stack storage (might be NULL) * @param[in] stkSize stack size [bytes] * @param[in] par pointer to an extra parameter (might be NULL) * * @usage * The following example shows starting an AO when a per-task stack * is needed: * @include qf_start.c */ void QActive_start_(QActive * const me, QPrioSpec const prioSpec, QEvt const * * const qSto, uint_fast16_t const qLen, void * const stkSto, uint_fast16_t const stkSize, void const * const par); /* protected: */ #ifdef QF_ACTIVE_STOP /*! Stops execution of an active object and removes it from the * framework's supervision * @protected @memberof QActive * * @param[in,out] me current instance pointer (see @ref oop) * * @attention * QActive_stop() must be called only from the AO that is about * to stop its execution. By that time, any pointers or references * to the AO are considered invalid (dangling) and it becomes * illegal for the rest of the application to post events to the AO. */ void QActive_stop(QActive * const me); #endif /* def QF_ACTIVE_STOP */ /* private: */ /*! Posts an event `e` directly to the event queue of the active object * using the First-In-First-Out (FIFO) policy. * @private @memberof QActive * * @details * Direct event posting is the simplest asynchronous communication * method available in QF. * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] e pointer to the event to be posted * @param[in] margin number of required free slots in the queue * after posting the event or ::QF_NO_MARGIN. * @param[in] sender pointer to a sender object (used in QS only) * * @returns * 'true' (success) if the posting succeeded (with the provided margin) * and 'false' (failure) when the posting fails. * * @precondition{qf_actq,100} * - event pointer must be valid * * @postcondition{qf_actq,190} * - the event must be posted if (`margin` == ::QF_NO_MARGIN) * * @attention * For `margin` == ::QF_NO_MARGIN, this function will assert internally * if the event posting fails. In that case, it is unnecessary to check * the retrun value from this function. * * @note * This function might be implemented differently in various QP/C++ * ports. The provided implementation assumes that the ::QEQueue * class is used for the ::QActive event queue. * * @sa * QActive_postLIFO() * * @usage * @include qf_post.c */ bool QActive_post_(QActive * const me, QEvt const * const e, uint_fast16_t const margin, void const * const sender); /*! Posts an event `e` directly to the event queue of the active object * using the Last-In-First-Out (LIFO) policy. * @private @memberof QActive * * @details * The LIFO policy should be used only for self-posting and with caution, * because it alters order of events in the queue. * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] e pointer to the event to be posted * * @precondition{qf_actq,200} * - the queue must be able to accept the event (cannot overflow) * * @note * This function might be implemented differently in various QP/C++ * ports. The provided implementation assumes that the ::QEQueue * class is used for the QActive event queue. * * @sa * QActive_post() */ void QActive_postLIFO_(QActive * const me, QEvt const * const e); /*! Get an event from the event queue of an active object * @private @memberof QActive * * @details * The behavior of this function depends on the kernel used in the * QF port. For built-in kernels (Vanilla or QK) the function can be * called only when the queue is not empty, so it doesn't block. For * a blocking kernel/OS the function can block and wait for delivery * of an event. * * @param[in,out] me current instance pointer (see @ref oop) * * @returns * A pointer to the received event. The returned pointer is guaranteed * to be valid (can't be NULL). * * @note * This function might be implemented differently in various QP/C++ * ports. The provided implementation assumes that the ::QEQueue * class is used for the QActive event queue. */ QEvt const * QActive_get_(QActive * const me); /* public: */ /*! Subscribes for delivery of signal `sig` to the active object * @public @memberof QActive * * @details * This function is part of the Publish-Subscribe event delivery * mechanism available in QF. Subscribing to an event means that the * framework will start posting all published events with a given signal * `sig` to the event queue of the active object. * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] sig event signal to subscribe * * @precondition{qf_ps,300} * - signal must be in range of subscribe scignals * - subscriber AO priority must be in range * - the AO must be registered (started) * * The following example shows how the Table active object subscribes * to three signals in the initial transition: * @include qf_subscribe.cpp * * @sa * QActive_publish_(), QActive_unsubscribe(), and * QActive_unsubscribeAll() */ void QActive_subscribe(QActive const * const me, enum_t const sig); /*! Unsubscribes from the delivery of signal `sig` to the active object * @public @memberof QActive * * @details * This function is part of the Publish-Subscribe event delivery * mechanism available in QF. Un-subscribing from an event means that * the framework will stop posting published events with a given signal * `sig` to the event queue of the active object. * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] sig event signal to unsubscribe * * @precondition{qf_ps,400} * - signal must be in range of subscribe scignals * - subscriber AO priority must be in range * - the AO must be registered (started) * * @note * Due to the latency of event queues, an active object should NOT * assume that a given signal `sig` will never be dispatched to the * state machine of the active object after un-subscribing from that * signal. The event might be already in the queue, or just about to * be posted and the un-subscribe operation will not flush such events. * * @note * Un-subscribing from a signal that has never been subscribed in the * first place is considered an error and QF will raise an assertion. * * @sa * QActive_publish_(), QActive_subscribe(), and * QActive_unsubscribeAll() */ void QActive_unsubscribe(QActive const * const me, enum_t const sig); /*! Unsubscribes from the delivery of all signals to the active object * @public @memberof QActive * * @details * This function is part of the Publish-Subscribe event delivery * mechanism available in QF. Un-subscribing from all events means that * the framework will stop posting any published events to the event * queue of the active object. * * @param[in,out] me current instance pointer (see @ref oop) * * @precondition{qf_ps,500} * - subscriber AO priority must be in range * - the AO must be registered (started) * * @note * Due to the latency of event queues, an active object should NOT * assume that no events will ever be dispatched to the state machine of * the active object after un-subscribing from all events. * The events might be already in the queue, or just about to be posted * and the un-subscribe operation will not flush such events. Also, the * alternative event-delivery mechanisms, such as direct event posting or * time events, can be still delivered to the event queue of the active * object. * * @sa * QActive_publish_(), QActive_subscribe(), and QActive_unsubscribe() */ void QActive_unsubscribeAll(QActive const * const me); /*! Publish event to all subscribers of a given signal `e->sig` * @static @public @memberof QActive * * @details * This function posts (using the FIFO policy) the event @a e to **all** * active objects that have subscribed to the signal @a e->sig, which is * called _multicasting_. The multicasting performed in this function is * very efficient based on reference-counting inside the published event * ("zero-copy" event multicasting). This function is designed to be * callable from any part of the system, including ISRs, device drivers, * and active objects. * * @note * To avoid any unexpected re-ordering of events posted into AO queues, * the event multicasting is performed with scheduler **locked**. * However, the scheduler is locked only up to the priority level of * the highest-priority subscriber, so any AOs of even higher priority, * which did not subscribe to this event are *not* affected. */ void QActive_psInit( QSubscrList * const subscrSto, enum_t const maxSignal); /* private: */ /*! Publish event to all subscribers of a given signal `e->sig` * @static @private @memberof QActive * * @details * This function posts (using the FIFO policy) the event @a e to **all** * active objects that have subscribed to the signal @a e->sig, which is * called _multicasting_. The multicasting performed in this function is * very efficient based on reference-counting inside the published event * ("zero-copy" event multicasting). This function is designed to be * callable from any part of the system, including ISRs, device drivers, * and active objects. * * @precondition{qf_ps,200} * - the published signal must be within the configured range * * @note * To avoid any unexpected re-ordering of events posted into AO queues, * the event multicasting is performed with scheduler **locked**. * However, the scheduler is locked only up to the priority level of * the highest-priority subscriber, so any AOs of even higher priority, * which did not subscribe to this event are *not* affected. */ void QActive_publish_( QEvt const * const e, void const * const sender, uint_fast8_t const qs_id); /* protected: */ /*! Defer an event to a given separate event queue * @protected @memberof QActive * * @details * This function is part of the event deferral support. An active object * uses this function to defer an event `e` to the QF-supported native * event queue `eq`. QF correctly accounts for another outstanding * reference to the event and will not recycle the event at the end of * the RTC step. Later, the active object might recall one event at a * time from the event queue. * * @param[in] eq pointer to a "raw" thread-safe queue to recall * an event from. * @param[in] e pointer to the event to be deferred * * @returns * 'true' (success) when the event could be deferred and 'false' * (failure) if event deferral failed due to overflowing the queue. * * An active object can use multiple event queues to defer events of * different kinds. * * @sa * QActive_recall(), ::QEQueue, QActive_flushDeferred() */ bool QActive_defer(QActive const * const me, QEQueue * const eq, QEvt const * const e); /*! Recall a deferred event from a given event queue * @protected @memberof QActive * * @details * This function is part of the event deferral support. An active object * uses this function to recall a deferred event from a given QF * event queue. Recalling an event means that it is removed from the * deferred event queue `eq` and posted (LIFO) to the event queue of * the active object. * * @param[in] eq pointer to a "raw" thread-safe queue to recall * an event from. * * @returns * 'true' if an event has been recalled and 'false' if not. * * @note * An active object can use multiple event queues to defer events of * different kinds. * * @sa * QActive_recall(), QActive_postLIFO_(), ::QEQueue */ bool QActive_recall(QActive * const me, QEQueue * const eq); /*! Flush the specified deferred queue 'eq' * @protected @memberof QActive * * @details * This function is part of the event deferral support. An active object * can use this function to flush a given QF event queue. The function * makes sure that the events are not leaked. * * @param[in] eq pointer to a "raw" thread-safe queue to flush. * * @returns * the number of events actually flushed from the queue. * * @sa * QActive_defer(), QActive_recall(), ::QEQueue */ uint_fast16_t QActive_flushDeferred(QActive const * const me, QEQueue * const eq); /* public: */ /*! Generic setting of additional attributes (useful in QP ports) * @public @memberof QActive */ void QActive_setAttr(QActive * const me, uint32_t attr1, void const * attr2); /* private: */ /*! Thread routine for executing an active object `act` * @static @private @memberof QActive */ void QActive_thread_(QActive * act); /* protected: */ /*! Register this active object to be managed by the framework * @protected @memberof QActive * * @details * This function adds a given active object to the active objects * managed by the QF framework. It should not be called by the * application directly, only through the function QActive::start(). * * @param[in,out] me current instance pointer (see @ref oop) * * @precondition{qf_qact,100} * - the "QF-priority" of the AO must be in range (must be set * before calling QActive_register_()) * - the "QF-priority" must not be already in use (unique priority) * - the "QF-priority" must not exceed the "preemption-threshold" * * @postcondition{qf_qact,190} * - the preceding pre-thre must not exceed the preemption-threshold * - the preemption-threshold must not exceed the next pre-thre * * @sa QActive_unregister_() */ void QActive_register_(QActive * const me); /*! Un-register the active object from the framework * @protected @memberof QActive * * @details * This function un-registers a given active object from the active objects * managed by the QF framework. It should not be called by the QP ports. * * @param[in] me pointer to the active object to remove from the * framework. * * @precondition{qf_qact,200} * - the priority of the active object must not be zero and cannot * exceed the maximum #QF_MAX_ACTIVE * - the priority of the AO must be already registered. * * @note * The active object that is removed from the framework can no longer * participate in any event exchange. * * @sa QActive_register_() */ void QActive_unregister_(QActive * const me); /* private: */ #ifdef QF_ISR_API /*! the "FromISR" variant used in the QP port to "FreeRTOS" * @private @memberof QActive */ bool QActive_postFromISR_(QActive * const me, QEvt const * const e, uint_fast16_t const margin, void * par, void const * const sender); #endif /* def QF_ISR_API */ /* public: */ #ifdef QF_ISR_API /*! the "FromISR" variant used in the QP port to "FreeRTOS" * @static @private @memberof QActive */ void QActive_publishFromISR_( QEvt const * e, void * par, void const * sender); #endif /* def QF_ISR_API */ /*! Internal array of registered active objects * @static @private @memberof QActive */ extern QActive * QActive_active_[QF_MAX_ACTIVE + 1U]; /*! pointer to the array of all subscriber AOs for a given event signal. * @static @private @memberof QActive */ extern QSubscrList * QActive_subscrList_; /*! The maximum published signal (the size of the subscrList_ array) * @static @private @memberof QActive */ extern enum_t QActive_maxPubSignal_; /*! Internal array of registered active objects * @static @private @memberof QActive */ extern QActive * QActive_registry_[QF_MAX_ACTIVE + 1U]; /*$enddecl${QF::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*$declare${QF::QActiveVtable} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF::QActiveVtable} .....................................................*/ /*! @brief Virtual table for the QActive class */ typedef struct QActiveVtable { struct QHsmVtable super; /*!< @protected inherits ::QHsmVtable */ /*! @private virtual function to start the AO/thread * @sa QACTIVE_START() */ void (*start)(QActive * const me, QPrioSpec prio, QEvt const * * const qSto, uint_fast16_t const qLen, void * const stkSto, uint_fast16_t const stkSize, void const * const par); /*! @private virtual function to asynchronously post (FIFO) * an event to the AO * @sa QACTIVE_POST() and QACTIVE_POST_X() */ bool (*post)(QActive * const me, QEvt const * const e, uint_fast16_t const margin, void const * const sender); /*! @private virtual function to asynchronously post (LIFO) * an event to the AO * @sa QACTIVE_POST_LIFO() */ void (*postLIFO)(QActive * const me, QEvt const * const e); } QActiveVtable; /*$enddecl${QF::QActiveVtable} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*$declare${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF::QMActive} ..........................................................*/ /*! @brief Active object class (based on QMsm implementation strategy) * @class QMActive * @extends QActive * * @details * ::QMActive represents an active object that uses the ::QMsm style state * machine implementation strategy. This strategy requires the use of the * QM modeling tool to generate state machine code automatically, but the * code is faster than in the ::QHsm style implementation strategy and needs * less run-time support (smaller event-processor). * * @note * ::QMActive is not intended to be instantiated directly, but rather serves * as the base class for derivation of active objects in the application. * * @trace * @tr{AQP214} * * @usage * The following example illustrates how to derive an active object from * ::QMActive. Please note that the ::QActive member @c super is defined as * the **first** member of the derived struct (see @ref oop). * @include qf_qmactive.c */ typedef struct { /* protected: */ QActive super; } QMActive; /* protected: */ /*! Constructor of ::QMActive class. * @protected @memberof QMActive * * @details * Performs the first step of active object initialization by assigning * the virtual pointer and calling the superclass constructor. * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] initial pointer to the event to be dispatched to the MSM * * @note Must be called only ONCE before QHSM_INIT(). * * @sa QHsm_ctor() */ void QMActive_ctor(QMActive * const me, QStateHandler const initial); /*$enddecl${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*$declare${QF::QMActiveVtable} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF::QMActiveVtable} ....................................................*/ /*! @brief Virtual Table for the ::QMActive class (inherited * from ::QActiveVtable) * * @note * ::QMActive inherits ::QActive exactly, without adding any new virtual * functions and therefore, ::QMActiveVtable is typedef'ed as ::QActiveVtable. */ typedef QActiveVtable QMActiveVtable; /*$enddecl${QF::QMActiveVtable} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*$declare${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF::QTimeEvt} ..........................................................*/ /*! @brief Time Event class * @class QTimeEvt * @extends QEvt * * @details * Time events are special QF events equipped with the notion of time passage. * The basic usage model of the time events is as follows. An active object * allocates one or more ::QTimeEvt objects (provides the storage for them). * When the active object needs to arrange for a timeout, it arms one of its * time events to fire either just once (one-shot) or periodically. Each time * event times out independently from the others, so a QF application can make * multiple parallel timeout requests (from the same or different active * objects). When QF detects that the appropriate moment has arrived, it * inserts the time event directly into the recipient's event queue. The * recipient then processes the time event just like any other event. * * Time events, as any other QF events derive from the ::QEvt base class. * Typically, you will use a time event as-is, but you can also further * derive more specialized time events from it by adding some more data * members and/or specialized functions that operate on the specialized * time events. * * Internally, the armed time events are organized into linked lists--one * list for every supported ticking rate. These linked lists are scanned in * every invocation of the QTIMEEVT_TICK_X() macro. Only armed (timing out) * time events are in the list, so only armed time events consume CPU cycles. * * @sa ::QTimeEvt for the description of the data members * * @trace * @tr{AQP215} * * @note * QF manages the time events in the QTIMEEVT_TICK_X() macro, which must * be called periodically, from the clock tick ISR or from other periodic * source. QTIMEEVT_TICK_X() caYou might also use the special ::QTicker * active object. * * @note * Even though ::QTimeEvt is a subclass of ::QEvt, ::QTimeEvt instances can NOT * be allocated dynamically from event pools. In other words, it is illegal to * allocate ::QTimeEvt instances with the Q_NEW() or Q_NEW_X() macros. */ typedef struct QTimeEvt { /* protected: */ QEvt super; /* private: */ /*! link to the next time event in the list * @private @memberof QTimeEvt */ struct QTimeEvt * volatile next; /*! The active object that receives the time events * @private @memberof QTimeEvt */ void * volatile act; /*! Internal down-counter of the time event. * @private @memberof QTimeEvt * * @details * The down-counter is decremented by 1 in every QTimeEvt_tick_() call. * The time event fires (gets posted or published) when the down-counter * reaches zero. */ QTimeEvtCtr volatile ctr; /*! Interval for periodic time event (zero for one-shot time event) * @private @memberof QTimeEvt * * @details * The value of the interval is re-loaded to the internal down-counter * when the time event expires, so that the time event keeps timing out * periodically. */ QTimeEvtCtr interval; /* public: */ } QTimeEvt; /* public: */ /*! The "extended" constructor to initialize a Time Event. * @public @memberof QTimeEvt * * @details * When creating a time event, you must commit it to a specific active object * `act`, tick rate `tickRate` and event signal `sig`. You cannot change * these attributes later. * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] act pointer to the active object associated with this * time event. The time event will post itself to this AO. * @param[in] sig signal to associate with this time event. * @param[in] tickRate system clock tick rate to associate with this * time event in the range [0..15]. * * @precondition{qf_time,300} * - the signal `sig` must be valid * - the tick rate `tickRate` must be in range * * @note * You should call QTimeEvt_ctorX() exactly once for every Time Event * object **before** arming the Time Event. The ideal place for calling * QTimeEvt_ctorX() is the constructor of the associated AO. */ void QTimeEvt_ctorX(QTimeEvt * const me, QActive * const act, enum_t const sig, uint_fast8_t const tickRate); /*! Arm a time event (one shot or periodic) for direct event posting. * @public @memberof QTimeEvt * * @details * Arms a time event to fire in a specified number of clock ticks and with * a specified interval. If the interval is zero, the time event is armed for * one shot ('one-shot' time event). When the timeout expires, the time event * gets directly posted (using the FIFO policy) into the event queue of the * host active object. After posting, a one-shot time event gets automatically * disarmed while a periodic time event (interval != 0) is automatically * re-armed. * * A time event can be disarmed at any time by calling QTimeEvt_disarm(). * Also, a time event can be re-armed to fire in a different number of clock * ticks by calling the QTimeEvt_rearm(). * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] nTicks number of clock ticks (at the associated rate) * to rearm the time event with. * @param[in] interval interval (in clock ticks) for periodic time event. * * @precondition{qf_time,400} * - the host AO must be valid, * - the time eveht must be disarmed, * - the number of clock ticks cannot be zero, * - the signal must be valid. * * @attention * Arming an already armed time event is __not__ allowed and is considered * a programming error. The QP/C framework will assert if it detects an * attempt to arm an already armed time event. * * @usage * The following example shows how to arm a periodic time event as well as * one-shot time event from a state machine of an active object: * @include qf_tevt.c */ void QTimeEvt_armX(QTimeEvt * const me, QTimeEvtCtr const nTicks, QTimeEvtCtr const interval); /*! Disarm a time event. * @public @memberof QTimeEvt * * @details * Disarm the time event so it can be safely reused. * * @param[in,out] me current instance pointer (see @ref oop) * * @returns * 'true' if the time event was truly disarmed, that is, it was running. * The return of 'false' means that the time event was not truly disarmed, * because it was not running. The 'false' return is only possible for one- * shot time events that have been automatically disarmed upon expiration. * In this case the 'false' return means that the time event has already * been posted or published and should be expected in the active object's * state machine. * * @note * there is no harm in disarming an already disarmed time event */ bool QTimeEvt_disarm(QTimeEvt * const me); /*! Rearm a time event. * @public @memberof QTimeEvt * * @details * Rearms a time event with a new number of clock ticks. This function can * be used to adjust the current period of a periodic time event or to * prevent a one-shot time event from expiring (e.g., a watchdog time event). * Rearming a periodic timer leaves the interval unchanged and is a convenient * method to adjust the phasing of a periodic time event. * * @param[in,out] me current instance pointer (see @ref oop) * @param[in] nTicks number of clock ticks (at the associated rate) * to rearm the time event with. * * @returns * 'true' if the time event was running as it was re-armed. The 'false' * return means that the time event was not truly rearmed because it was * not running. The 'false' return is only possible for one-shot time events * that have been automatically disarmed upon expiration. In this case the * 'false' return means that the time event has already been posted or * published and should be expected in the active object's state machine. * * @precondition{qf_time,600} * - AO must be valid * - tick rate must be in range * - nTicks must not be zero, * - the signal of this time event must be valid */ bool QTimeEvt_rearm(QTimeEvt * const me, QTimeEvtCtr const nTicks); /*! Check the "was disarmed" status of a time event. * @public @memberof QTimeEvt * * @details * Useful for checking whether a one-shot time event was disarmed in the * QTimeEvt_disarm() operation. * * @param[in,out] me current instance pointer (see @ref oop) * * @returns * 'true' if the time event was truly disarmed in the last QTimeEvt_disarm() * operation. The 'false' return means that the time event was not truly * disarmed, because it was not running at that time. The 'false' return is * only possible for one-shot time events that have been automatically disarmed * upon expiration. In this case the 'false' return means that the time event * has already been posted or published and should be expected in the active * object's event queue. * * @note * This function has a **side effect** of setting the "was disarmed" status, * which means that the second and subsequent times this function is called * the function will return 'true'. */ bool QTimeEvt_wasDisarmed(QTimeEvt * const me); /*! Get the current value of the down-counter of a time event. * @public @memberof QTimeEvt * * @details * Useful for checking how many clock ticks (at the tick rate associated * with the time event) remain until the time event expires. * * @param[in,out] me current instance pointer (see @ref oop) * * @returns * For an armed time event, the function returns the current value of the * down-counter of the given time event. If the time event is not armed, * the function returns 0. * * @note * The function is thread-safe. */ QTimeEvtCtr QTimeEvt_currCtr(QTimeEvt const * const me); /*! Processes all armed time events at every clock tick. * @static @private @memberof QTimeEvt * * @details * This internal helper function processes all armed ::QTimeEvt objects * associated wit the tick rate `tickRate`. * * This function must be called periodically from a time-tick ISR or from * a task so that QF can manage the timeout events assigned to the given * system clock tick rate. * * @param[in] tickRate clock tick rate serviced in this call [1..15]. * @param[in] sender pointer to a sender object (only for QS tracing) * * @note * this function should be called only via the macro QTIMEEVT_TICK_X() * * @note * the calls to QTimeEvt_tick_() with different `tickRate` parameter can * preempt each other. For example, higher clock tick rates might be * serviced from interrupts while others from tasks (active objects). * * @sa ::QTimeEvt. */ void QTimeEvt_tick_( uint_fast8_t const tickRate, void const * const sender); #ifdef Q_UTEST /*! Processes one clock tick for QUTest * @static @private @memberof QTimeEvt */ void QTimeEvt_tick1_( uint_fast8_t const tickRate, void const * const sender); #endif /* def Q_UTEST */ /*! Returns 'true' if there are no armed time events at a given tick rate. * @static @public @memberof QTimeEvt * * @details * Find out if any time events are armed at the given clock tick rate. * * @param[in] tickRate system clock tick rate to find out about. * * @returns * 'true' if no time events are armed at the given tick rate and * 'false' otherwise. * * @precondition{qf_time,800} * - the tick rate must be in range * * @note * This function should be called in critical section. */ bool QTimeEvt_noActive(uint_fast8_t const tickRate); /*! heads of linked lists of time events, one for every clock tick rate */ extern QTimeEvt QTimeEvt_timeEvtHead_[QF_MAX_TICK_RATE]; /*$enddecl${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*$declare${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF::QTicker} ...........................................................*/ /*! @brief "Ticker" Active Object class * @class QTicker * @extends QActive * * @details * QTicker is an efficient active object specialized to process QF system * clock tick at a specified tick rate [0..#QF_MAX_TICK_RATE]. * Placing system clock tick processing in an active object allows you * to remove the non-deterministic QTIMEEVT_TICK_X() processing from the * interrupt level and move it into the thread-level, where you can prioritize * it as low as you wish. * * @usage * The following example illustrates use of QTicker active objects: * @include qf_ticker.c */ typedef struct { /* protected: */ QActive super; } QTicker; /* public: */ /*! Constructor of the QTicker Active Object class * @public @memberof QTicker */ void QTicker_ctor(QTicker * const me, uint_fast8_t const tickRate); /* private: */ /*! initialization (override) * @private @memberof QTicker */ void QTicker_init_( QHsm * const me, void const * const par, uint_fast8_t const qs_id); /*! dispatching (override) * @private @memberof QTicker */ void QTicker_dispatch_( QHsm * const me, QEvt const * const e, uint_fast8_t const qs_id); /* public: */ /*! post (override) * @private @memberof QTicker */ bool QTicker_post_( QActive * const me, QEvt const * const e, uint_fast16_t const margin, void const * const sender); /*! post-LIFO (override) * @private @memberof QTicker */ void QTicker_postLIFO_( QActive * const me, QEvt const * const e); /*$enddecl${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*$declare${QF::QF-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF::QF-base::Attr} .....................................................*/ /*! @brief QF active object framework * @class QF */ typedef struct QF_Attr { uint8_t dummy; /*< dummy attribute */ } QF; /*${QF::QF-base::intLock_} .................................................*/ /*! Interrupt lock up-down counter (used in some QF ports ) * @static @private @memberof QF */ extern uint_fast8_t volatile QF_intLock_; /*${QF::QF-base::intNest_} .................................................*/ /*! Interrupt nesting up-down counter (used in some QF ports ) * @static @private @memberof QF */ extern uint_fast8_t volatile QF_intNest_; /*${QF::QF-base::init} .....................................................*/ /*! QF initialization * @static @public @memberof QF * * @details * Initializes QF and must be called exactly once before any other QF * function. Typcially, QF_init() is called from main() even before * initializing the Board Support Package (BSP). * * @note * QF_init() clears the internal QF variables, so that the framework * can start correctly even if the startup code fails to clear the * uninitialized data (as is required by the C Standard). */ void QF_init(void); /*${QF::QF-base::stop} .....................................................*/ /*! Function invoked by the application layer to stop the QF * application and return control to the OS/Kernel. * @static @public @memberof QF * * @details * This function stops the QF application. After calling this function, * QF attempts to gracefully stop the application. This graceful shutdown * might take some time to complete. The typical use of this function is * for terminating the QF application to return back to the operating * system or for handling fatal errors that require shutting down * (and possibly re-setting) the system. * * @attention * After calling QF_stop() the application must terminate and cannot * continue. In particular, QF_stop() is **not** intended to be followed * by a call to QF_init() to "resurrect" the application. * * @sa QF_onCleanup() */ void QF_stop(void); /*${QF::QF-base::run} ......................................................*/ /*! Transfers control to QF to run the application. * @static @public @memberof QF * * @details * QF_run() is typically called from your startup code after you initialize * the QF and start at least one active object with QACTIVE_START(). * * @returns * In QK, the QF_run() does not return. */ int_t QF_run(void); /*${QF::QF-base::psInit} ...................................................*/ /*! initialization of publish-subscribe * * @deprecated * @sa QActive_psInit() */ static inline void QF_psInit( QSubscrList * const subscrSto, enum_t const maxSignal) { QActive_psInit(subscrSto, maxSignal); } /*${QF::QF-base::getQueueMin} ..............................................*/ /*! This function returns the minimum of free entries of * the given event queue. * @static @public @memberof QF * * @details * Queries the minimum of free ever present in the given event queue of * an active object with priority `prio`, since the active object * was started. * * @note * This function is available only when the native QF event queue * implementation is used. Requesting the queue minimum of an unused * priority level raises an assertion in the QF. (A priority level becomes * used in QF after the call to the QActive_register_() function.) * * @param[in] prio Priority of the active object, whose queue is queried * * @returns * the minimum of free ever present in the given event queue of an active * object with priority `prio`, since the active object was started. */ uint_fast16_t QF_getQueueMin(uint_fast8_t const prio); /*${QF::QF-base::onStartup} ................................................*/ /*! Startup QF callback. * @static @public @memberof QF * * @details * The purpose of the QF_onStartup() callback is to configure and enable * hardware interrupts. The callback is invoked from QF_run(), right before * starting the underlying real-time kernel. By that time, the application * is considered ready to receive and service interrupts. * * This function is application-specific and is not implemented in QF, but * rather in the Board Support Package (BSP) for the given application. */ void QF_onStartup(void); /*${QF::QF-base::onCleanup} ................................................*/ /*! Cleanup QF callback. * @static @public @memberof QF * * @details * QF_onCleanup() is called in some QF ports before QF returns to the * underlying real-time kernel or operating system. * * This function is strongly platform-specific and is not implemented in * the QF, but either in the QF port or in the Board Support Package (BSP) * for the given application. Some QF ports might not require implementing * QF_onCleanup() at all, because many embedded applications don't have * anything to exit to. * * @sa QF_stop() */ void QF_onCleanup(void); /*$enddecl${QF::QF-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*$declare${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF::QF-dyn::poolInit} ..................................................*/ /*! Event pool initialization for dynamic allocation of events. * @static @public @memberof QF * * @details * This function initializes one event pool at a time and must be called * exactly once for each event pool before the pool can be used. * * @param[in] poolSto pointer to the storage for the event pool * @param[in] poolSize size of the storage for the pool in bytes * @param[in] evtSize the block-size of the pool in bytes, which determines * the maximum size of events that can be allocated from the pool. * * @attention * You might initialize many event pools by making many consecutive calls * to the QF_poolInit() function. However, for the simplicity of the internal * implementation, you must initialize event pools in the **ascending order** * of the event size. * * Many RTOSes provide fixed block-size heaps, a.k.a. memory pools that can * be adapted for QF event pools. In case such support is missing, QF provides * a native QF event pool implementation. The macro #QF_EPOOL_TYPE_ determines * the type of event pool used by a particular QF port. See structure ::QMPool * for more information. * * @note The actual number of events available in the pool might be actually * less than (`poolSize` / `evtSize`) due to the internal alignment * of the blocks that the pool might perform. You can always check the * capacity of the pool by calling QF_getPoolMin(). * * @note The dynamic allocation of events is optional, meaning that you * might choose not to use dynamic events. In that case calling QF_poolInit() * and using up memory for the memory blocks is unnecessary. * * @sa QF initialization example for QF_init() */ void QF_poolInit( void * const poolSto, uint_fast32_t const poolSize, uint_fast16_t const evtSize); /*${QF::QF-dyn::poolGetMaxBlockSize} .......................................*/ /*! Obtain the block size of any registered event pools. * @static @public @memberof QF * * @details * Obtain the block size of any registered event pools */ uint_fast16_t QF_poolGetMaxBlockSize(void); /*${QF::QF-dyn::getPoolMin} ................................................*/ /*! Obtain the minimum of free entries of the given event pool. * @static @public @memberof QF * * @details * This function obtains the minimum number of free blocks in the given * event pool since this pool has been initialized by a call to QF_poolInit(). * * @param[in] poolId event pool ID in the range 1..QF_maxPool_, where * QF_maxPool_ is the number of event pools initialized * with the function QF_poolInit(). * * @returns * the minimum number of unused blocks in the given event pool. */ uint_fast16_t QF_getPoolMin(uint_fast8_t const poolId); /*${QF::QF-dyn::newX_} .....................................................*/ /*! Internal QF implementation of creating new dynamic event. * @static @private @memberof QF * * @details * Allocates an event dynamically from one of the QF event pools. * * @param[in] evtSize the size (in bytes) of the event to allocate * @param[in] margin the number of un-allocated events still available * in a given event pool after the allocation completes. * The special value ::QF_NO_MARGIN means that this function * will assert if allocation fails. * @param[in] sig the signal to be assigned to the allocated event * * @returns * pointer to the newly allocated event. This pointer can be NULL only if * margin != #QF_NO_MARGIN and the event cannot be allocated with the * specified margin still available in the given pool. * * @note * The internal QF function QF_newX_() raises an assertion when the * `margin` parameter is #QF_NO_MARGIN and allocation of the event turns * out to be impossible due to event pool depletion, or incorrect (too big) * size of the requested event. * * @note * The application code should not call this function directly. * The only allowed use is thorough the macros Q_NEW() or Q_NEW_X(). */ QEvt * QF_newX_( uint_fast16_t const evtSize, uint_fast16_t const margin, enum_t const sig); /*${QF::QF-dyn::gc} ........................................................*/ /*! Recycle a dynamic event * @static @public @memberof QF * * @details * This function implements a simple garbage collector for the dynamic events. * Only dynamic events are candidates for recycling. (A dynamic event is one * that is allocated from an event pool, which is determined as non-zero * e->poolId_ attribute.) Next, the function decrements the reference counter * of the event (e->refCtr_), and recycles the event only if the counter drops * to zero (meaning that no more references are outstanding for this event). * The dynamic event is recycled by returning it to the pool from which * it was originally allocated. * * @param[in] e pointer to the event to recycle * * @note * QF invokes the garbage collector at all appropriate contexts, when * an event can become garbage (automatic garbage collection), so the * application code should have no need to call QF_gc() directly. The QF_gc() * function is exposed only for special cases when your application sends * dynamic events to the "raw" thread-safe queues (see ::QEQueue). Such * queues are processed outside of QF and the automatic garbage collection * is **NOT** performed for these events. In this case you need to call * QF_gc() explicitly. */ void QF_gc(QEvt const * const e); /*${QF::QF-dyn::newRef_} ...................................................*/ /*! Internal QF implementation of creating new event reference. * @static @private @memberof QF * * @details * Creates and returns a new reference to the current event e * * @param[in] e pointer to the current event * @param[in] evtRef the event reference * * @returns * the newly created reference to the event `e` * * @note * The application code should not call this function directly. * The only allowed use is thorough the macro Q_NEW_REF(). */ QEvt const * QF_newRef_( QEvt const * const e, void const * const evtRef); /*${QF::QF-dyn::deleteRef_} ................................................*/ /*! Internal QF implementation of deleting event reference. * @static @private @memberof QF * * @details * Deletes an existing reference to the event e * * @param[in] evtRef the event reference * * @note * The application code should not call this function directly. * The only allowed use is thorough the macro Q_DELETE_REF(). */ void QF_deleteRef_(void const * const evtRef); /*$enddecl${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*$declare${QF::QF-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF::QF-extern-C::onContextSw} ..........................................*/ #ifdef QF_ON_CONTEXT_SW /*! QF context switch callback used in built-in kernels (QV, QK, QXK) * @static @public @memberof QF * * @details * This callback function provides a mechanism to perform additional * custom operations when one of the built-in kernels switches context * from one thread to another. * * @param[in] prev pointer to the previous thread (active object) * (prev==0 means that `prev` was the idle loop) * @param[in] next pointer to the next thread (active object) * (next==0) means that `next` is the idle loop) * @attention * QF_onContextSw() is invoked with interrupts **disabled** and must also * return with interrupts **disabled**. * * @note * This callback is enabled by defining the macro #QF_ON_CONTEXT_SW. * * @include qf_oncontextsw.c */ void QF_onContextSw( QActive * prev, QActive * next); #endif /* def QF_ON_CONTEXT_SW */ /*$enddecl${QF::QF-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*==========================================================================*/ /*$declare${QF-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*${QF-macros::QF_NO_MARGIN} ...............................................*/ /*! Special value of margin that causes asserting failure in case * event allocation or event posting fails */ #define QF_NO_MARGIN ((uint_fast16_t)0xFFFFU) /*${QF-macros::Q_PRIO} .....................................................*/ /*! Create a ::QPrioSpec object to specify priorty of an AO or a thread */ #define Q_PRIO(prio_, pthre_) ((QPrioSpec)((prio_) | ((pthre_) << 8U))) /*${QF-macros::Q_NEW} ......................................................*/ #ifndef Q_EVT_CTOR /*! Allocate a dynamic event (case when ::QEvt is a POD) * * @details * The macro calls the internal QF function QF::newX_() with * margin == ::QF_NO_MARGIN, which causes an assertion when the event * cannot be successfully allocated. * * @param[in] evtT_ event type (class name) of the event to allocate * @param[in] sig_ signal to assign to the newly allocated event * * @returns a valid event pointer cast to the type `evtT_`. * * @note * If #Q_EVT_CTOR is defined, the Q_NEW() macro becomes variadic and * takes all the arguments needed by the constructor of the event * class being allocated. The constructor is then called by means * of the placement-new operator. * * @usage * The following example illustrates dynamic allocation of an event: * @include qf_post.c */ #define Q_NEW(evtT_, sig_) ((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ QF_NO_MARGIN, (enum_t)(sig_))) #endif /* ndef Q_EVT_CTOR */ /*${QF-macros::Q_NEW} ......................................................*/ #ifdef Q_EVT_CTOR /*! Asserting allocate a dynamic event * (case when ::QEvt is not a POD) */ #define Q_NEW(evtT_, sig_, ...) \ (evtT_##_ctor((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ QF_NO_MARGIN, (sig_)), (enum_t)(sig_), ##__VA_ARGS__)) #endif /* def Q_EVT_CTOR */ /*${QF-macros::Q_NEW_X} ....................................................*/ #ifndef Q_EVT_CTOR /*! Non-asserting allocate a dynamic event (case when ::QEvt is a POD). * * @details * This macro allocates a new event and sets the pointer `e_`, while * leaving at least `margin_` of events still available in the pool * * @param[out] e_ pointer to the newly allocated event * @param[in] evtT_ event type (class name) of the event to allocate * @param[in] margin_ number of events that must remain available * in the given pool after this allocation. The * special value ::QF_NO_MARGIN causes asserting * failure in case event allocation fails. * @param[in] sig_ signal to assign to the newly allocated event * * @returns an event pointer cast to the type `evtT_` or NULL if the * event cannot be allocated with the specified `margin`. * * @note * If #Q_EVT_CTOR is defined, the Q_NEW_X() macro becomes variadic and * takes all the arguments needed by the constructor of the event * class being allocated. The constructor is then called by means * of the placement-new operator. * * @usage * The following example illustrates dynamic allocation of an event: * @include qf_postx.c */ #define Q_NEW_X(e_, evtT_, margin_, sig_) ((e_) = \ (evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ (margin_), (enum_t)(sig_))) #endif /* ndef Q_EVT_CTOR */ /*${QF-macros::Q_NEW_X} ....................................................*/ #ifdef Q_EVT_CTOR /*! Non-asserting allocate a dynamic event * (case when ::QEvt is not a POD) */ #define Q_NEW_X(e_, evtT_, margin_, sig_, ...) do { \ (e_) = (evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ (margin_), (enum_t)(sig_));\ if ((e_) != (evtT_ *)0) { \ evtT_##_ctor((e_), (enum_t)(sig_), ##__VA_ARGS__); \ } \ } while (false) #endif /* def Q_EVT_CTOR */ /*${QF-macros::Q_NEW_REF} ..................................................*/ /*! Create a new reference of the current event `e` * * @details * The current event processed by an active object is available only for * the duration of the run-to-completion (RTC) step. After that step, the * current event is no longer available and the framework might recycle * (garbage-collect) the event. The macro Q_NEW_REF() explicitly creates * a new reference to the current event that can be stored and used beyond * the current RTC step, until the reference is explicitly recycled by * means of the macro Q_DELETE_REF(). * * @param[in,out] evtRef_ event reference to create * @param[in] evtT_ event type (class name) of the event reference * * @usage * The example **defer** in the directory `examples/win32/defer` illustrates * the use of Q_NEW_REF() * * @sa Q_DELETE_REF() */ #define Q_NEW_REF(evtRef_, evtT_) \ ((evtRef_) = (evtT_ const *)QF_newRef_(e, (evtRef_))) /*${QF-macros::Q_DELETE_REF} ...............................................*/ /*! Delete the event reference * * @details * Every event reference created with the macro Q_NEW_REF() needs to be * eventually deleted by means of the macro Q_DELETE_REF() to avoid leaking * the event. * * @param[in,out] evtRef_ event reference to delete * * @usage * The example **defer** in the directory `examples/win32/defer` illustrates * the use of Q_DELETE_REF() * * @sa Q_NEW_REF() */ #define Q_DELETE_REF(evtRef_) do { \ QF_deleteRef_((evtRef_)); \ (evtRef_) = (void *)0; \ } while (false) /*${QF-macros::QACTIVE_START} ..............................................*/ /*! Virtual call to start an active object. * * @details * Starts execution of the AO and registers the AO with the framework. * * @param[in,out] me_ current instance pointer (see @ref oop) * @param[in] prioSpec_ priority specification for the Active Object * @param[in] qSto_ pointer to the storage for the ring buffer of the * event queue (used only with the built-in ::QEQueue) * @param[in] qLen_ length of the event queue (in events) * @param[in] stkSto_ pointer to the stack storage (used only when * per-AO stack is needed) * @param[in] stkSize_ stack size (in bytes) * @param[in] par_ pointer to the additional port-specific parameter(s) * (might be NULL). * @usage * @include qf_start.c */ #define QACTIVE_START(me_, prioSpec_, qSto_, qLen_, stkSto_, stkSize_, par_) do { \ Q_ASSERT((Q_HSM_UPCAST(me_))->vptr); \ (*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->start)( \ (QActive *)(me_), (prioSpec_), \ (qSto_), (qLen_), (stkSto_), (stkSize_), (par_)); \ } while (false) /*${QF-macros::QACTIVE_POST} ...............................................*/ #ifdef Q_SPY /*! Invoke the direct event posting facility QActive_post_() * * @details * This macro asserts if the queue overflows and cannot accept the event. * * @param[in,out] me_ current instance pointer (see @ref oop) * @param[in] e_ pointer to the event to post * @param[in] sender_ pointer to the sender object. * * @note * The `sendedr_` parameter is actually only used when QS tracing * is enabled (macro #Q_SPY is defined). When QS software tracing is * disenabled, the QACTIVE_POST() macro does not pass the `sender_` * parameter, so the overhead of passing this extra parameter is entirely * avoided. * * @note the pointer to the sender object is not necessarily a pointer * to an active object. In fact, if QACTIVE_POST() is called from an * interrupt or other context, you can create a unique object just to * unambiguously identify the sender of the event. * * @sa QActive_post_() */ #define QACTIVE_POST(me_, e_, sender_) \ ((void)(*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->post)(\ (me_), (e_), QF_NO_MARGIN, (sender_))) #endif /* def Q_SPY */ /*${QF-macros::QACTIVE_POST} ...............................................*/ #ifndef Q_SPY #define QACTIVE_POST(me_, e_, dummy) \ ((void)(*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->post)(\ (me_), (e_), QF_NO_MARGIN, (void *)0)) #endif /* ndef Q_SPY */ /*${QF-macros::QACTIVE_POST_X} .............................................*/ #ifdef Q_SPY /*! Invoke the direct event posting facility QActive_post_() * without delivery guarantee * * @details * This macro does not assert if the queue overflows and cannot accept * the event with the specified margin of free slots remaining. * * @param[in,out] me_ current instance pointer (see @ref oop) * @param[in] e_ pointer to the event to post * @param[in] margin_ the minimum free slots in the queue, which * must still be available after posting the event. * The special value ::QF_NO_MARGIN causes * asserting failure in case event posting fails. * @param[in] sender_ pointer to the sender object. * * @returns * 'true' if the posting succeeded, and 'false' if the posting * failed due to insufficient margin of free entries available in * the queue. * * @note * The `sender_` parameter is actually only used when QS tracing * is enabled (macro #Q_SPY is defined). When QS software tracing is * disabled, the POST_X() macro does not pass the `sender_` parameter, * so the overhead of passing this extra parameter is entirely avoided. * * @note * The pointer to the sender object is not necessarily a pointer * to an active object. In fact, if POST_X() is called from an * interrupt or other context, you can create a unique object just to * unambiguously identify the sender of the event. * * @usage * @include qf_postx.c */ #define QACTIVE_POST_X(me_, e_, margin_, sender_) \ ((*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->post)((me_),\ (e_), (margin_), (sender_))) #endif /* def Q_SPY */ /*${QF-macros::QACTIVE_POST_X} .............................................*/ #ifndef Q_SPY #define QACTIVE_POST_X(me_, e_, margin_, dummy) \ ((*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->post)((me_),\ (e_), (margin_), (void *)0)) #endif /* ndef Q_SPY */ /*${QF-macros::QACTIVE_POST_LIFO} ..........................................*/ /*! Virtual call to post an event to an active object using the * Last-In-First-Out (LIFO) policy. * * @param[in,out] me_ current instance pointer (see @ref oop) * @param[in] e_ pointer to the event to post */ #define QACTIVE_POST_LIFO(me_, e_) \ ((*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->postLIFO)( \ (me_), (e_))) /*${QF-macros::QACTIVE_PUBLISH} ............................................*/ #ifdef Q_SPY /*! Publish an event to all subscriber Active Objects. * * @details * If #Q_SPY is defined, this macro calls QActive_publish_() with * the `sender_` parameter to identify the publisher of the event. * Otherwise, `sender_` is not used. * * @param[in] e_ pointer to the posted event * @param[in] sender_ pointer to the sender object (actually used * only when #Q_SPY is defined) * * @note * The pointer to the `sender_` object is not necessarily a pointer * to an active object. In fact, if QACTIVE_PUBLISH() is called from an * interrupt or other context, you can create a unique object just to * unambiguously identify the sender of the event. * * @sa QActive_publish_() */ #define QACTIVE_PUBLISH(e_, sender_) \ (QActive_publish_((e_), (void const *)(sender_), (sender_)->prio)) #endif /* def Q_SPY */ /*${QF-macros::QACTIVE_PUBLISH} ............................................*/ #ifndef Q_SPY #define QACTIVE_PUBLISH(e_, dummy) (QActive_publish_((e_), (void *)0, 0U)) #endif /* ndef Q_SPY */ /*${QF-macros::QTIMEEVT_TICK_X} ............................................*/ #ifdef Q_SPY /*! Invoke the system clock tick processing QTimeEvt_tick_() * * @details * This macro is the recommended way of invoking clock tick processing, * because it provides the vital information for software tracing and * avoids any overhead when the tracing is disabled. * * @param[in] tickRate_ clock tick rate to be serviced through this call * @param[in] sender_ pointer to the sender object. This parameter * is actually only used when QS software tracing is enabled * (macro #Q_SPY is defined) * @note * When QS software tracing is disabled, the macro calls QTimeEvt_tick_() * without the `sender` parameter, so the overhead of passing this * extra parameter is entirely avoided. * * @note * The pointer to the sender object is not necessarily a pointer * to an active object. In fact, when QTIMEEVT_TICK_X() is called from * an interrupt, you would create a unique object just to unambiguously * identify the ISR as the sender of the time events. * * @sa QTimeEvt_tick_() */ #define QTIMEEVT_TICK_X(tickRate_, sender_) \ (QTimeEvt_tick_((tickRate_), (sender_))) #endif /* def Q_SPY */ /*${QF-macros::QTIMEEVT_TICK_X} ............................................*/ #ifndef Q_SPY #define QTIMEEVT_TICK_X(tickRate_, dummy) \ (QTimeEvt_tick_((tickRate_), (void *)0)) #endif /* ndef Q_SPY */ /*${QF-macros::QTIMEEVT_TICK} ..............................................*/ /*! Invoke the system clock tick processing * for tick rate 0 */ #define QTIMEEVT_TICK(sender_) QTIMEEVT_TICK_X(0U, (sender_)) /*${QF-macros::QF_CRIT_EXIT_NOP} ...........................................*/ #ifndef QF_CRIT_EXIT_NOP /*! No-operation for exiting a critical section * * @details * In some QF ports the critical section exit takes effect only on the * next machine instruction. If this next instruction is another entry * to a critical section, the critical section won't be really exited, * but rather the two adjecent critical sections would be merged. * The QF_CRIT_EXIT_NOP() macro contains minimal code required to * prevent such merging of critical sections in such merging of * critical sections in QF ports, in which it can occur. */ #define QF_CRIT_EXIT_NOP() ((void)0) #endif /* ndef QF_CRIT_EXIT_NOP */ /*${QF-macros::QF_TICK_X} ..................................................*/ /*! Invoke the system clock tick processing * * @deprecated * superseded by QTIMEEVT_TICK_X() */ #define QF_TICK_X(tickRate_, sender_) QTIMEEVT_TICK_X((tickRate_), (sender_)) /*${QF-macros::QF_TICK} ....................................................*/ /*! Invoke the system clock tick processing for tick rate 0 * * @deprecated * superseded by QTIMEEVT_TICK() */ #define QF_TICK(sender_) QTIMEEVT_TICK(sender_) /*${QF-macros::QF_PUBLISH} .................................................*/ /*! Publish an event to all subscriber Active Objects. * * @deprecated * superseded by QACTIVE_PUBLISH() */ #define QF_PUBLISH(e_, sender_) QACTIVE_PUBLISH((e_), (sender_)) /*$enddecl${QF-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #endif /* QF_H_ */