mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'feat/sys_event_in_freertos' into 'master'
feat(system_init): Introduce pre-scheduler and pre-app main handler registrations Closes IDF-16133 See merge request espressif/esp-idf!52296
This commit is contained in:
@@ -198,6 +198,11 @@ endif()
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_system_include_startup_funcs")
|
||||
|
||||
if(CONFIG_ESP_INT_WDT)
|
||||
# Pull in the PRE_SCHEDULER registrations from int_wdt.c.
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_int_wdt_init")
|
||||
endif()
|
||||
|
||||
# [refactor-todo] requirements due to init code, should be removable
|
||||
# once link-time registration of component init functions is used.
|
||||
if(NOT CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This file is used to check the order of execution of ESP_SYSTEM_INIT_FN functions.
|
||||
# This file checks the order of execution of system startup handlers.
|
||||
# It compares the priorities found in .c source files to the contents of system_init_fn.txt
|
||||
# In case of an inconsistency, the script prints the differences found and returns with a
|
||||
# non-zero exit code.
|
||||
@@ -15,12 +15,47 @@ import re
|
||||
import sys
|
||||
|
||||
COMMENT_REGEX = re.compile(r'//.*?$|/\*.*?\*/', re.DOTALL | re.MULTILINE)
|
||||
ESP_SYSTEM_INIT_FN_REGEX_SIMPLE = re.compile(r'\bESP_SYSTEM_INIT_FN\s*\(')
|
||||
ESP_SYSTEM_INIT_FN_REGEX = re.compile(
|
||||
r'ESP_SYSTEM_INIT_FN\(([a-zA-Z0-9_]+)\s*,\s*([a-zA-Z\ _0-9\(\)|]+)\s*,\s*([a-zA-Z\ _0-9\(\)|]+)\s*,\s*([0-9]+)\)'
|
||||
ESP_SYSTEM_INIT_FN_REGEX = (
|
||||
r'{macro}\((?P<func>[a-zA-Z0-9_]+)\s*,\s*'
|
||||
r'(?P<stage>[a-zA-Z\ _0-9\(\)|]+)\s*,\s*'
|
||||
r'(?P<affinity>[a-zA-Z\ _0-9\(\)|]+)\s*,\s*'
|
||||
r'(?P<priority>[0-9]+)\)'
|
||||
)
|
||||
APP_INIT_FN_REGEX = r'{macro}\s*\(\s*(?P<func>[a-zA-Z0-9_]+)\s*,\s*(?P<priority>[0-9]+)\s*\)'
|
||||
STARTUP_REGISTRATIONS = (
|
||||
(
|
||||
'ESP_SYSTEM_INIT_FN',
|
||||
re.compile(ESP_SYSTEM_INIT_FN_REGEX.format(macro='ESP_SYSTEM_INIT_FN')),
|
||||
'',
|
||||
'',
|
||||
),
|
||||
(
|
||||
'ESP_PRE_SCHEDULER_HANDLER_REGISTER',
|
||||
re.compile(APP_INIT_FN_REGEX.format(macro='ESP_PRE_SCHEDULER_HANDLER_REGISTER')),
|
||||
'PRE_SCHEDULER',
|
||||
'BIT(0)',
|
||||
),
|
||||
(
|
||||
'ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU',
|
||||
re.compile(APP_INIT_FN_REGEX.format(macro='ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU')),
|
||||
'PRE_SCHEDULER',
|
||||
'ESP_SYSTEM_INIT_ALL_CORES',
|
||||
),
|
||||
(
|
||||
'ESP_PRE_APP_MAIN_HANDLER_REGISTER',
|
||||
re.compile(APP_INIT_FN_REGEX.format(macro='ESP_PRE_APP_MAIN_HANDLER_REGISTER')),
|
||||
'PRE_APP_MAIN',
|
||||
'MAIN_TASK',
|
||||
),
|
||||
)
|
||||
STARTUP_ENTRIES_FILE = 'components/esp_system/system_init_fn.txt'
|
||||
EXCLUDED_SOURCE_DIRS = {'test_apps', 'host_test', 'host_tests'}
|
||||
STAGE_ORDER = {
|
||||
'CORE': 0,
|
||||
'SECONDARY': 1,
|
||||
'PRE_SCHEDULER': 2,
|
||||
'PRE_APP_MAIN': 3,
|
||||
}
|
||||
|
||||
|
||||
class StartupEntry:
|
||||
@@ -69,36 +104,36 @@ def main() -> None:
|
||||
file_contents = f_obj.read()
|
||||
|
||||
file_contents_no_comments = strip_comments(file_contents)
|
||||
if not ESP_SYSTEM_INIT_FN_REGEX_SIMPLE.search(file_contents_no_comments):
|
||||
continue
|
||||
for macro, pattern, default_stage, default_affinity in STARTUP_REGISTRATIONS:
|
||||
count_expected = len(re.findall(rf'\b{macro}\s*\(', file_contents_no_comments))
|
||||
found = list(pattern.finditer(file_contents_no_comments))
|
||||
if len(found) != count_expected:
|
||||
print(
|
||||
f'error: In {filename}, found {macro} {count_expected} time(s), '
|
||||
f'but regular expression matched {len(found)} time(s)',
|
||||
file=sys.stderr,
|
||||
)
|
||||
has_errors = True
|
||||
|
||||
count_expected = len(ESP_SYSTEM_INIT_FN_REGEX_SIMPLE.findall(file_contents_no_comments))
|
||||
found = ESP_SYSTEM_INIT_FN_REGEX.findall(file_contents_no_comments)
|
||||
if len(found) != count_expected:
|
||||
print(
|
||||
(
|
||||
f'error: In {filename}, found ESP_SYSTEM_INIT_FN {count_expected} time(s), '
|
||||
f'but regular expression matched {len(found)} time(s)'
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
has_errors = True
|
||||
|
||||
for match in found:
|
||||
entry = StartupEntry(
|
||||
filename=relpath, func=match[0], stage=match[1], affinity=match[2], priority=int(match[3])
|
||||
)
|
||||
startup_entries.append(entry)
|
||||
for match in found:
|
||||
startup_entries.append(
|
||||
StartupEntry(
|
||||
relpath,
|
||||
match.group('func'),
|
||||
match.groupdict().get('stage') or default_stage,
|
||||
match.groupdict().get('affinity') or default_affinity,
|
||||
int(match.group('priority')),
|
||||
)
|
||||
)
|
||||
|
||||
#
|
||||
# 2. Sort the ESP_SYSTEM_INIT_FN functions in C source files.
|
||||
# 2. Sort the startup handlers in C source files.
|
||||
# In addition to the stage and priority, we also add filename to the sort key,
|
||||
# to have a stable sorting order in case when the same startup function is defined in multiple files,
|
||||
# for example for different targets.
|
||||
#
|
||||
def sort_key(entry: StartupEntry) -> tuple[str, int, str]:
|
||||
# luckily 'core' and 'secondary' are in alphabetical order, so we can return the string
|
||||
return (entry.stage, entry.priority, entry.filename)
|
||||
def sort_key(entry: StartupEntry) -> tuple[int, int, str]:
|
||||
return (STAGE_ORDER[entry.stage], entry.priority, entry.filename)
|
||||
|
||||
startup_entries = list(sorted(startup_entries, key=sort_key))
|
||||
startup_entries_lines = [str(entry) for entry in startup_entries]
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_debug_helpers.h"
|
||||
#include "esp_private/crosscore_int.h"
|
||||
#include "esp_private/esp_sys_event_app_init.h"
|
||||
#include "soc/periph_defs.h"
|
||||
#include "soc/system_intr.h"
|
||||
#include "hal/crosscore_int_ll.h"
|
||||
@@ -113,6 +114,14 @@ void esp_crosscore_int_init(void)
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU(init_crosscore_interrupt, 120)
|
||||
{
|
||||
(void)user_arg;
|
||||
(void)ctx;
|
||||
esp_crosscore_int_init();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void ESP_SYSTEM_IRAM_ATTR esp_crosscore_int_send(int core_id, uint32_t reason_mask)
|
||||
{
|
||||
assert(core_id < CONFIG_FREERTOS_NUMBER_OF_CORES);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_private/esp_sys_event.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Register a handler to run on core 0 immediately before the scheduler starts
|
||||
*/
|
||||
#define ESP_PRE_SCHEDULER_HANDLER_REGISTER(fn, priority) \
|
||||
static esp_err_t fn(void *user_arg, void *ctx); \
|
||||
_ESP_SYS_EVENT_REGISTER(PRE_SCHEDULER, priority, __esp_pre_scheduler_##fn) \
|
||||
{ \
|
||||
if (esp_cpu_get_core_id() != 0) { \
|
||||
return ESP_OK; \
|
||||
} \
|
||||
return fn(user_arg, ctx); \
|
||||
} \
|
||||
static esp_err_t fn(void *user_arg, void *ctx)
|
||||
|
||||
/**
|
||||
* @brief Register a handler to run on every core immediately before its scheduler starts
|
||||
*/
|
||||
#define ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU(fn, priority) \
|
||||
_ESP_SYS_EVENT_REGISTER(PRE_SCHEDULER, priority, fn)
|
||||
|
||||
/**
|
||||
* @brief Register a handler to run in the main task immediately before app_main
|
||||
*
|
||||
* Handlers run in ascending priority order after all CPUs have completed
|
||||
* FreeRTOS initialization. The scheduler and heap allocator are available.
|
||||
*/
|
||||
#define ESP_PRE_APP_MAIN_HANDLER_REGISTER(fn, priority) \
|
||||
_ESP_SYS_EVENT_REGISTER(PRE_APP_MAIN, priority, fn)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -34,6 +34,8 @@
|
||||
#define _ESP_SYS_EVENT_SECTION_SHUTDOWN esysev_shdn
|
||||
#define _ESP_SYS_EVENT_SECTION_SYSTEM_INIT_CORE esysev_initc
|
||||
#define _ESP_SYS_EVENT_SECTION_SYSTEM_INIT_SECONDARY esysev_inits
|
||||
#define _ESP_SYS_EVENT_SECTION_PRE_SCHEDULER esysev_presc
|
||||
#define _ESP_SYS_EVENT_SECTION_PRE_APP_MAIN esysev_pream
|
||||
#define _ESP_SYS_EVENT_SECTION_PANIC esysev_panic
|
||||
#define _ESP_SYS_EVENT_SECTION_PANIC_EARLY_BREAK esysev_paneb
|
||||
#endif
|
||||
@@ -42,5 +44,7 @@
|
||||
ESP_SYS_EVENT_DEF(SHUTDOWN)
|
||||
ESP_SYS_EVENT_DEF(SYSTEM_INIT_CORE)
|
||||
ESP_SYS_EVENT_DEF(SYSTEM_INIT_SECONDARY)
|
||||
ESP_SYS_EVENT_DEF(PRE_SCHEDULER)
|
||||
ESP_SYS_EVENT_DEF(PRE_APP_MAIN)
|
||||
ESP_SYS_EVENT_DEF(PANIC)
|
||||
ESP_SYS_EVENT_DEF(PANIC_EARLY_BREAK)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "esp_freertos_hooks.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_private/esp_int_wdt.h"
|
||||
#include "esp_private/esp_sys_event_app_init.h"
|
||||
#include "mwdt_priv.h"
|
||||
|
||||
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_MWDT_SUPPORT_SLEEP_RETENTION
|
||||
@@ -194,6 +195,22 @@ void esp_int_wdt_cpu_init(void)
|
||||
esp_intr_enable_source(ETS_INT_WDT_INUM);
|
||||
}
|
||||
|
||||
ESP_PRE_SCHEDULER_HANDLER_REGISTER(init_int_wdt, 100)
|
||||
{
|
||||
(void)user_arg;
|
||||
(void)ctx;
|
||||
esp_int_wdt_init();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU(init_int_wdt_cpu, 110)
|
||||
{
|
||||
(void)user_arg;
|
||||
(void)ctx;
|
||||
esp_int_wdt_cpu_init();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ESP_SYSTEM_IRAM_ATTR esp_int_wdt_pause(void)
|
||||
{
|
||||
wdt_hal_write_protect_disable(&iwdt_context);
|
||||
|
||||
@@ -22,6 +22,16 @@ SECTIONS
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_inits.*)))
|
||||
PROVIDE(_esysev_inits_end = .);
|
||||
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_esysev_presc_start = .);
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_presc.*)))
|
||||
PROVIDE(_esysev_presc_end = .);
|
||||
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_esysev_pream_start = .);
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_pream.*)))
|
||||
PROVIDE(_esysev_pream_end = .);
|
||||
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_esysev_panic_start = .);
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_panic.*)))
|
||||
|
||||
@@ -12,6 +12,14 @@ entries:
|
||||
entries:
|
||||
.esysev_inits+
|
||||
|
||||
[sections:esysev_presc]
|
||||
entries:
|
||||
.esysev_presc+
|
||||
|
||||
[sections:esysev_pream]
|
||||
entries:
|
||||
.esysev_pream+
|
||||
|
||||
[sections:esysev_panic]
|
||||
entries:
|
||||
.esysev_panic+
|
||||
@@ -26,6 +34,8 @@ entries:
|
||||
esysev_shdn -> flash_rodata
|
||||
esysev_initc -> flash_rodata
|
||||
esysev_inits -> flash_rodata
|
||||
esysev_presc -> flash_rodata
|
||||
esysev_pream -> flash_rodata
|
||||
esysev_panic -> flash_rodata
|
||||
esysev_paneb -> flash_rodata
|
||||
|
||||
@@ -36,6 +46,8 @@ entries:
|
||||
esysev_shdn -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_shdn),
|
||||
esysev_initc -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_initc),
|
||||
esysev_inits -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_inits),
|
||||
esysev_presc -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_presc),
|
||||
esysev_pream -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_pream),
|
||||
esysev_panic -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_panic),
|
||||
esysev_paneb -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_paneb)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_attr.h"
|
||||
@@ -13,8 +14,10 @@
|
||||
#include "esp_check.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_xt_wdt.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_private/esp_sys_event_app_init.h"
|
||||
#include "esp_private/startup_internal.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "soc/soc_caps.h"
|
||||
@@ -50,6 +53,16 @@ void esp_system_include_startup_funcs(void)
|
||||
|
||||
// [refactor-todo] Most of these init functions should be moved to the respective components.
|
||||
|
||||
#if !CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
||||
ESP_PRE_SCHEDULER_HANDLER_REGISTER(check_cache_lock_bug, 90)
|
||||
{
|
||||
(void)user_arg;
|
||||
(void)ctx;
|
||||
assert(!soc_has_cache_lock_bug() && "ESP32 Rev 3 + Dual Core + PSRAM requires INT WDT enabled in project config!");
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_SYSTEM_INIT_FN(init_show_cpu_freq, CORE, BIT(0), 10)
|
||||
{
|
||||
ESP_EARLY_LOGI(TAG, "Pro cpu start user code");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This file documents the expected order of execution of ESP_SYSTEM_INIT_FN functions.
|
||||
# This file documents the expected order of execution of system startup handlers.
|
||||
#
|
||||
# When adding new ESP_SYSTEM_INIT_FN functions or changing init priorities of existing functions,
|
||||
# keep this file up to date. This is checked in CI.
|
||||
# When adding startup handlers or changing their priorities, keep this file up to date.
|
||||
# This is checked in CI.
|
||||
# When adding new functions or changing the priorities, please read the comments and see if
|
||||
# they need to be updated to be consistent with the changes you are making.
|
||||
#
|
||||
@@ -9,7 +9,7 @@
|
||||
# Each line has the following format:
|
||||
# stage: prio: function_name in path/to/source_file on affinity_expression
|
||||
# Where:
|
||||
# stage: which startup stage the function is executed in (CORE or SECONDARY)
|
||||
# stage: which startup stage the function is executed in (CORE, SECONDARY, PRE_SCHEDULER, or PRE_APP_MAIN)
|
||||
# prio: priority value (higher value means function is executed later)
|
||||
# affinity_expression: bit map of cores the function is executed on
|
||||
|
||||
@@ -148,4 +148,35 @@ SECONDARY: 240: psram_adjust_timing_point_via_temperature in components/esp_hw_s
|
||||
# Now that the application is about to start, disable boot watchdog
|
||||
SECONDARY: 999: init_disable_rtc_wdt in components/esp_system/startup_funcs.c on BIT(0)
|
||||
|
||||
# DO NOT add new init functions here. Add them to the correct stage above.
|
||||
# DO NOT add new init functions at the end of a stage. Add them at the correct priority.
|
||||
|
||||
|
||||
########### PRE_SCHEDULER startup stage ###########
|
||||
|
||||
# If the interrupt watchdog is disabled, validate the ESP32 ECO3 cache lock configuration instead.
|
||||
PRE_SCHEDULER: 90: check_cache_lock_bug in components/esp_system/startup_funcs.c on BIT(0)
|
||||
|
||||
# Initialize the interrupt watchdog globally, then enable it on each core.
|
||||
PRE_SCHEDULER: 100: init_int_wdt in components/esp_system/int_wdt.c on BIT(0)
|
||||
PRE_SCHEDULER: 110: init_int_wdt_cpu in components/esp_system/int_wdt.c on ESP_SYSTEM_INIT_ALL_CORES
|
||||
|
||||
# Cross-core interrupts must be available before each core starts its scheduler.
|
||||
PRE_SCHEDULER: 120: init_crosscore_interrupt in components/esp_system/crosscore_int.c on ESP_SYSTEM_INIT_ALL_CORES
|
||||
|
||||
# Runtime GDB stub initialization requires cross-core interrupts.
|
||||
PRE_SCHEDULER: 130: init_gdbstub in components/esp_gdbstub/src/gdbstub.c on BIT(0)
|
||||
|
||||
|
||||
########### PRE_APP_MAIN startup stage ###########
|
||||
|
||||
# Reclaim startup stacks only after every core has completed FreeRTOS initialization.
|
||||
PRE_APP_MAIN: 100: enable_nonos_stack_heaps in components/heap/heap_caps_init.c on MAIN_TASK
|
||||
|
||||
# Reserve the internal DMA pool after startup stack memory has been reclaimed.
|
||||
PRE_APP_MAIN: 110: reserve_dma_pool in components/esp_psram/system_layer/esp_psram.c on MAIN_TASK
|
||||
|
||||
# Initialize the task watchdog after the scheduler is available.
|
||||
PRE_APP_MAIN: 120: init_task_wdt in components/esp_system/task_wdt/task_wdt.c on MAIN_TASK
|
||||
|
||||
# Confirm the running OTA image immediately before calling app_main.
|
||||
PRE_APP_MAIN: 200: esp_ota_auto_confirm in components/app_update/ota_auto_confirm.c on MAIN_TASK
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp_private/system_internal.h"
|
||||
#include "esp_private/crosscore_int.h"
|
||||
#include "esp_private/esp_sys_event_app_init.h"
|
||||
#include "esp_private/esp_task_wdt.h"
|
||||
#include "esp_private/esp_task_wdt_impl.h"
|
||||
|
||||
@@ -58,6 +59,28 @@ extern bool g_panic_abort;
|
||||
/* Global flag marking whether the current ISR is a Task Watchdog ISR. */
|
||||
bool g_twdt_isr = false;
|
||||
|
||||
#if CONFIG_ESP_TASK_WDT_INIT
|
||||
ESP_PRE_APP_MAIN_HANDLER_REGISTER(init_task_wdt, 120)
|
||||
{
|
||||
(void)user_arg;
|
||||
(void)ctx;
|
||||
esp_task_wdt_config_t config = {
|
||||
.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000,
|
||||
.idle_core_mask = 0,
|
||||
#if CONFIG_ESP_TASK_WDT_PANIC
|
||||
.trigger_panic = true,
|
||||
#endif
|
||||
};
|
||||
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
|
||||
config.idle_core_mask |= BIT(0);
|
||||
#endif
|
||||
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
|
||||
config.idle_core_mask |= BIT(1);
|
||||
#endif
|
||||
return esp_task_wdt_init(&config);
|
||||
}
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------- Definitions -----------------------------------------------------
|
||||
|
||||
// ---------------------- Typedefs -------------------------
|
||||
|
||||
Reference in New Issue
Block a user