| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- /*
- * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- /**
- * This file provides an abstract OS API for entering and exiting critical sections.
- * It furthermore provides macros to define and initialize an optional spinlock
- * if the used chip is a multi-core chip. If a single-core chip is used, just disabling interrupts
- * is sufficient to guarantee consecutive, non-interrupted execution of a critical section.
- * Hence, the spinlock is unneccessary and will be automatically ommitted by the macros.
- */
- #pragma once
- #include "freertos/FreeRTOS.h"
- #include "spinlock.h"
- #ifdef __cplusplus
- extern "C" {
- #endif
- #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32P4
- /**
- * This macro also helps users switching between spinlock declarations/definitions for multi-/single core environments
- * if the macros below aren't sufficient.
- */
- #define OS_SPINLOCK 1
- #else
- #define OS_SPINLOCK 0
- #endif
- #if OS_SPINLOCK == 1
- typedef spinlock_t esp_os_spinlock_t;
- #endif
- /**
- * Define and initialize a static (internal linking) lock for entering critical sections.
- *
- * Use this when all the critical sections are local inside a file.
- * The lock will only be defined if built for a multi-core system, otherwise it is unnecessary.
- *
- * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
- * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core systems.
- *
- * @param lock_name Variable name of the lock. This will later be used to reference the declared lock.
- * @param optional_qualifiers Qualifiers such as DRAM_ATTR and other attributes. Can be omitted if no qualifiers are
- * required.
- *
- * Example usage:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
- * ...
- * esp_os_enter_critical(&my_lock);
- * ...
- * esp_os_exit_critical(&my_lock);
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define DEFINE_CRIT_SECTION_LOCK_STATIC(lock_name, optional_qualifiers...) static optional_qualifiers esp_os_spinlock_t lock_name = SPINLOCK_INITIALIZER
- #else
- #define DEFINE_CRIT_SECTION_LOCK_STATIC(lock_name, optional_qualifiers...)
- #endif
- /**
- * Define and initialize a non-static (external linking) lock for entering critical sections.
- *
- * Locks defined by this macro can be linked among object files but this rather exceptional.
- * Prefer the static lock definition whenever possible.
- * The lock will only be defined if built for a multi-core system, otherwise it is unnecessary.
- *
- * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
- * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core systems.
- *
- * @param lock_name Variable name of the lock. This will later be used to reference the declared lock.
- * @param optional_qualifiers Qualifiers such as DRAM_ATTR and other attributes. Can be omitted if no qualifiers are
- * required.
- *
- * Example usage:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * DEFINE_CRIT_SECTION_LOCK(my_lock); // will have external linking (non-static)
- * ...
- * esp_os_enter_critical(&my_lock);
- * ...
- * esp_os_exit_critical(&my_lock);
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define DEFINE_CRIT_SECTION_LOCK(lock_name, optional_qualifiers...) optional_qualifiers esp_os_spinlock_t lock_name = SPINLOCK_INITIALIZER
- #else
- #define DEFINE_CRIT_SECTION_LOCK(lock_name, optional_qualifiers...)
- #endif
- /**
- * @brief This macro initializes a critical section lock at runtime.
- *
- * This macro basically creates a member of the initialization list, including the trailing comma.
- * If the lock is unnecessary because the architecture is single-core, this macro will not do anything.
- * This is incompatible with a lock created by DEFINE_CRIT_SECTION_LOCK_STATIC from above.
- *
- * @param lock_name Pointer to the lock.
- *
- * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
- * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core
- * systems.
- *
- * Example usage:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * typedef struct protected_struct_t {
- * int member1;
- * DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock)
- * int another_member;
- * };
- * ...
- * protected_struct_t my_protected;
- * INIT_CRIT_SECTION_LOCK_IN_STRUCT(&(my_protected.my_lock));
- * };
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define INIT_CRIT_SECTION_LOCK_RUNTIME(lock_name) spinlock_initialize(lock_name)
- #else
- #define INIT_CRIT_SECTION_LOCK_RUNTIME(lock_name)
- #endif
- /**
- * @brief This macro declares a critical section lock as a member of a struct.
- *
- * The critical section lock member is only declared if built for multi-core systems, otherwise it is omitted.
- *
- * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
- * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core
- * systems.
- * @note Do NOT add any semicolon after declaring the member with this macro.
- * The trailing semicolon is included in the macro, otherwise -Wpedantic would complain about
- * superfluous ";" if OS_SPINLOCK == 0.
- *
- * Example usage:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * typedef struct protected_struct_t {
- * int member1;
- * DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) // no semicolon!
- * int another_member;
- * };
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) esp_os_spinlock_t lock_name;
- #else
- #define DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(lock_name)
- #endif
- /**
- * @brief This macro initializes a critical section lock as a member of a struct when using an list initialization.
- * It has to be used together with \c DECLARE_CRIT_SECTION_LOCK_IN_STRUCT() to work.
- *
- * This macro basically creates a member of the initialization list, including the trailing comma.
- * If the lock is unnecessary because the architecture is single-core, this macro will not do anything.
- * This means that if \c lock_name is still a member of the struct, \c lock_name will be uninitialized.
- * Hence, this macro has to be used together with \c DECLARE_CRIT_SECTION_LOCK_IN_STRUCT() to correctly to declare
- * or omit the struct member \c lock_name.
- *
- * @param lock_name The field name of the lock inside the struct.
- *
- * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
- * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core
- * systems.
- * @note Do NOT add any comma in the initializer list after using this macro.
- *
- * Example usage:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * typedef struct protected_struct_t {
- * int member1;
- * DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock)
- * int another_member;
- * };
- * ...
- * protected_struct_t my_protected = {
- * .member1 = 0,
- * INIT_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) // no comma!
- * another_member = 47,
- * };
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define INIT_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) .lock_name = portMUX_INITIALIZER_UNLOCKED,
- #else
- #define INIT_CRIT_SECTION_LOCK_IN_STRUCT(lock_name)
- #endif
- /**
- * @brief Enter a critical section, i.e., a section that will not be interrupted by any other task or interrupt.
- *
- * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a
- * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only.
- *
- * @note This macro MUST be used together with any of the initialization macros, e.g.
- * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
- *
- * @param lock Pointer to the critical section lock. Ignored if build for single core system.
- *
- * Example usage with static locks:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
- * ...
- * esp_os_enter_critical(&my_lock);
- * // code inside critical section
- * esp_os_exit_critical(&my_lock);
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define esp_os_enter_critical(lock) portENTER_CRITICAL(lock)
- #else
- #define esp_os_enter_critical(lock) vPortEnterCritical()
- #endif
- /**
- * @brief Exit a critical section.
- *
- * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a
- * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only.
- *
- * @note This macro MUST be used together with any of the initialization macros, e.g.
- * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
- *
- * @param lock Pointer to the critical section lock. Ignored if build for single core system.
- *
- * Example usage with static locks:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
- * ...
- * esp_os_enter_critical(&my_lock);
- * // code inside critical section
- * esp_os_exit_critical(&my_lock);
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define esp_os_exit_critical(lock) portEXIT_CRITICAL(lock)
- #else
- #define esp_os_exit_critical(lock) vPortExitCritical()
- #endif
- /**
- * @brief Enter a critical section while from ISR.
- *
- * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a
- * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only.
- *
- * @note This macro MUST be used together with any of the initialization macros, e.g.
- * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
- *
- * @param lock Pointer to the critical section lock. Ignored if build for single core system.
- *
- * Example usage with static locks:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
- * ...
- * esp_os_enter_critical(&my_lock);
- * // code inside critical section
- * esp_os_exit_critical(&my_lock);
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define esp_os_enter_critical_isr(lock) portENTER_CRITICAL_ISR(lock)
- #else
- #define esp_os_enter_critical_isr(lock) vPortEnterCritical()
- #endif
- /**
- * @brief Exit a critical section after entering from ISR.
- *
- * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a
- * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only.
- *
- * @note This macro MUST be used together with any of the initialization macros, e.g.
- * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
- *
- * @param lock Pointer to the critical section lock. Ignored if build for single core system.
- *
- * Example usage with static locks:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
- * ...
- * esp_os_enter_critical(&my_lock);
- * // code inside critical section
- * esp_os_exit_critical(&my_lock);
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define esp_os_exit_critical_isr(lock) portEXIT_CRITICAL_ISR(lock)
- #else
- #define esp_os_exit_critical_isr(lock) vPortExitCritical()
- #endif
- /**
- * @brief Enter a critical section from normal task or ISR. This macro will check if the current CPU is processing
- * an ISR or not and enter the critical section accordingly.
- *
- * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a
- * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only.
- *
- * @note This macro MUST be used together with any of the initialization macros, e.g.
- * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
- *
- * @param lock Pointer to the critical section lock. Ignored if build for single core system.
- *
- * Example usage with static locks:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
- * ...
- * esp_os_enter_critical(&my_lock);
- * // code inside critical section
- * esp_os_exit_critical(&my_lock);
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define esp_os_enter_critical_safe(lock) portENTER_CRITICAL_SAFE(lock)
- #else
- #define esp_os_enter_critical_safe(lock) vPortEnterCritical()
- #endif
- /**
- * @brief Exit a critical section after entering via esp_os_enter_critical_safe.
- *
- * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a
- * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only.
- *
- * @note This macro MUST be used together with any of the initialization macros, e.g.
- * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
- *
- * @param lock Pointer to the critical section lock. Ignored if build for single core system.
- *
- * Example usage with static locks:
- * @code{c}
- * ...
- * #include "os/critical_section.h"
- * ...
- * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
- * ...
- * esp_os_enter_critical(&my_lock);
- * // code inside critical section
- * esp_os_exit_critical(&my_lock);
- * @endcode
- */
- #if OS_SPINLOCK == 1
- #define esp_os_exit_critical_safe(lock) portEXIT_CRITICAL_SAFE(lock)
- #else
- #define esp_os_exit_critical_safe(lock) vPortExitCritical()
- #endif
- #ifdef __cplusplus
- }
- #endif
|