mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 05:12:57 +01:00
cpu/esp*: common parts of syscalls in cpu/esp_common
This commit is contained in:
parent
4c466e54ec
commit
90dc2ce846
@ -21,32 +21,15 @@
|
||||
#ifndef SYSCALLS_H
|
||||
#define SYSCALLS_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "esp_common.h"
|
||||
#include "syscalls_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Necessary initializations of system call functions */
|
||||
void syscalls_init (void);
|
||||
|
||||
/** Determine free heap size */
|
||||
unsigned int get_free_heap_size (void);
|
||||
|
||||
/** Time since boot in us (32bit version) */
|
||||
uint32_t system_get_time (void);
|
||||
|
||||
/** Time since boot in us (64bit version) */
|
||||
uint64_t system_get_time_64 (void);
|
||||
|
||||
/** Time since boot in ms (32bit version) */
|
||||
uint32_t system_get_time_ms (void);
|
||||
|
||||
/** initialize system watchdog timer and start it */
|
||||
void system_wdt_init (void);
|
||||
|
||||
@ -59,9 +42,6 @@ void system_wdt_stop (void);
|
||||
/** reset the system watchdog timer */
|
||||
void system_wdt_feed (void);
|
||||
|
||||
/** memset version that the compiler should not be allowed to optimize this */
|
||||
void *system_secure_memset(void *s, int c, size_t n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -293,13 +293,6 @@ static NORETURN void IRAM system_init (void)
|
||||
/* Disable the hold flag of all RTC GPIO pins */
|
||||
RTCCNTL.hold_force.val = 0;
|
||||
|
||||
/*
|
||||
* initialization of newlib, includes the ctors initialization and
|
||||
* and the execution of stdio_init in _init of newlib_syscalls_default
|
||||
*/
|
||||
extern void __libc_init_array(void);
|
||||
__libc_init_array();
|
||||
|
||||
/* set log levels for SDK library outputs */
|
||||
extern void esp_log_level_set(const char* tag, esp_log_level_t level);
|
||||
esp_log_level_set("wifi", LOG_DEBUG);
|
||||
|
@ -18,35 +18,15 @@
|
||||
* @}
|
||||
*/
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/reent.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/times.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include "esp_common.h"
|
||||
#include "cpu_conf.h"
|
||||
#include "irq.h"
|
||||
#include "esp/common_macros.h"
|
||||
#include "irq_arch.h"
|
||||
#include "kernel_defines.h"
|
||||
#include "log.h"
|
||||
#include "mutex.h"
|
||||
#include "rmutex.h"
|
||||
#include "sched.h"
|
||||
#include "periph_cpu.h"
|
||||
#include "periph/pm.h"
|
||||
|
||||
#include "syscalls.h"
|
||||
#include "timex.h"
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "esp/xtensa_ops.h"
|
||||
#include "esp/common_macros.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "rom/libc_stubs.h"
|
||||
#include "soc/rtc.h"
|
||||
@ -55,231 +35,45 @@
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "xtensa/xtensa_api.h"
|
||||
|
||||
#include "periph_cpu.h"
|
||||
#include "syscalls.h"
|
||||
|
||||
#ifdef MODULE_ESP_IDF_HEAP
|
||||
#include "heap/esp_heap_caps.h"
|
||||
#include "heap/include/multi_heap.h"
|
||||
#else
|
||||
#include "malloc.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define MHZ 1000000UL
|
||||
|
||||
#ifndef MODULE_PTHREAD
|
||||
|
||||
#define PTHREAD_CANCEL_DISABLE 1
|
||||
/*
|
||||
* This is a dummy function to avoid undefined references when linking
|
||||
* against newlib and module pthread is not used.
|
||||
*/
|
||||
int pthread_setcancelstate(int state, int *oldstate)
|
||||
{
|
||||
if (oldstate) {
|
||||
*oldstate = PTHREAD_CANCEL_DISABLE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* MODULE_PTHREAD*/
|
||||
|
||||
/**
|
||||
* @name Locking functions
|
||||
*
|
||||
* Following function implements the lock mechanism in newlib. The only static
|
||||
* mutex defined here is the _malloc_rmtx to avoid that memory management
|
||||
* functions try to lock before RIOT's threads are running. All other mutexes
|
||||
* are allocated dynamically.
|
||||
*/
|
||||
|
||||
static rmutex_t _malloc_rmtx = RMUTEX_INIT;
|
||||
|
||||
void IRAM _lock_init(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM (sched_active_thread != 0);
|
||||
CHECK_PARAM (lock != NULL);
|
||||
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
|
||||
|
||||
mutex_t* mtx = malloc (sizeof(mutex_t));
|
||||
|
||||
if (mtx) {
|
||||
memset (mtx, 0, sizeof(mutex_t));
|
||||
*lock = (_lock_t)mtx;
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM _lock_init_recursive(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM (sched_active_thread != 0);
|
||||
CHECK_PARAM (lock != NULL);
|
||||
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
|
||||
|
||||
rmutex_t* rmtx = malloc (sizeof(rmutex_t));
|
||||
|
||||
if (rmtx) {
|
||||
memset (rmtx, 0, sizeof(rmutex_t));
|
||||
*lock = (_lock_t)rmtx;
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM _lock_close(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM (lock != NULL);
|
||||
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
|
||||
|
||||
free ((void*)*lock);
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void IRAM _lock_close_recursive(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM (lock != NULL);
|
||||
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
|
||||
|
||||
free ((void*)*lock);
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void IRAM _lock_acquire(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM (sched_active_thread != 0);
|
||||
CHECK_PARAM (lock != NULL && *lock != 0);
|
||||
|
||||
mutex_lock ((mutex_t*)*lock);
|
||||
}
|
||||
|
||||
void IRAM _lock_acquire_recursive(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM (sched_active_thread != 0);
|
||||
CHECK_PARAM (lock != NULL && *lock != 0);
|
||||
|
||||
rmutex_lock ((rmutex_t*)*lock);
|
||||
}
|
||||
|
||||
int IRAM _lock_try_acquire(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM_RET (sched_active_thread != 0, 0);
|
||||
CHECK_PARAM_RET (lock != NULL && *lock != 0, 0);
|
||||
|
||||
return rmutex_trylock ((rmutex_t*)*lock);
|
||||
}
|
||||
|
||||
int IRAM _lock_try_acquire_recursive(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM_RET (sched_active_thread != 0, 0);
|
||||
CHECK_PARAM_RET (lock != NULL && *lock != 0, 0);
|
||||
|
||||
return mutex_trylock ((mutex_t*)*lock);
|
||||
}
|
||||
|
||||
void IRAM _lock_release(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM (sched_active_thread != 0);
|
||||
CHECK_PARAM (lock != NULL && *lock != 0);
|
||||
|
||||
mutex_unlock ((mutex_t*)*lock);
|
||||
}
|
||||
|
||||
void IRAM _lock_release_recursive(_lock_t *lock)
|
||||
{
|
||||
CHECK_PARAM (sched_active_thread != 0);
|
||||
CHECK_PARAM (lock != NULL && *lock != 0);
|
||||
|
||||
rmutex_unlock ((rmutex_t*)*lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Memory allocation functions
|
||||
*/
|
||||
|
||||
#ifdef MODULE_ESP_IDF_HEAP
|
||||
|
||||
extern void *heap_caps_malloc_default( size_t size );
|
||||
extern void *heap_caps_realloc_default( void *ptr, size_t size );
|
||||
|
||||
void* IRAM_ATTR __wrap__malloc_r(struct _reent *r, size_t size)
|
||||
/* if module esp_idf_heap is used, this function has to be defined for ESP32 */
|
||||
unsigned int get_free_heap_size(void)
|
||||
{
|
||||
return heap_caps_malloc_default( size );
|
||||
return heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
|
||||
}
|
||||
|
||||
void IRAM_ATTR __wrap__free_r(struct _reent *r, void* ptr)
|
||||
{
|
||||
heap_caps_free( ptr );
|
||||
}
|
||||
|
||||
void* IRAM_ATTR __wrap__realloc_r(struct _reent *r, void* ptr, size_t size)
|
||||
{
|
||||
return heap_caps_realloc_default( ptr, size );
|
||||
}
|
||||
|
||||
void* IRAM_ATTR __wrap__calloc_r(struct _reent *r, size_t count, size_t size)
|
||||
{
|
||||
void* result = heap_caps_malloc_default(count * size);
|
||||
if (result) {
|
||||
bzero(result, count * size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned int IRAM get_free_heap_size (void)
|
||||
{
|
||||
return heap_caps_get_free_size( MALLOC_CAP_DEFAULT );
|
||||
}
|
||||
/* alias for compatibility with espressif/wifi_libs */
|
||||
uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size")));
|
||||
|
||||
/* this function is platform specific if module esp_idf_heap is used */
|
||||
void heap_stats(void)
|
||||
{
|
||||
size_t _free = 0;
|
||||
size_t _alloc = 0;
|
||||
|
||||
multi_heap_info_t hinfo;
|
||||
|
||||
heap_caps_get_info(&hinfo, MALLOC_CAP_DEFAULT);
|
||||
|
||||
size_t _free = hinfo.total_free_bytes;
|
||||
size_t _alloc = hinfo.total_allocated_bytes;
|
||||
_free = hinfo.total_free_bytes;
|
||||
_alloc = hinfo.total_allocated_bytes;
|
||||
|
||||
printf("heap: %u (used %u free %u) [bytes]\n",
|
||||
(unsigned)(_free + _alloc), (unsigned)_alloc, (unsigned)_free);
|
||||
}
|
||||
|
||||
#else /* MODULE_ESP_IDF_HEAP */
|
||||
|
||||
/* for compatibility with ESP-IDF heap functions */
|
||||
void* IRAM heap_caps_malloc( size_t size, uint32_t caps )
|
||||
{
|
||||
(void)caps;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void* IRAM heap_caps_calloc( size_t n, size_t size, uint32_t caps)
|
||||
{
|
||||
(void)caps;
|
||||
return calloc(n, size);
|
||||
}
|
||||
|
||||
void* IRAM heap_caps_realloc( void *ptr, size_t size )
|
||||
{
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
extern uint8_t _eheap; /* end of heap (defined in esp32.common.ld) */
|
||||
extern uint8_t _sheap; /* start of heap (defined in esp32.common.ld) */
|
||||
extern uint8_t *heap_top; /* current top of heap as defined in newlib_syscalls_default */
|
||||
|
||||
unsigned int IRAM get_free_heap_size (void)
|
||||
{
|
||||
struct mallinfo minfo = mallinfo();
|
||||
return &_eheap - &_sheap - minfo.uordblks;
|
||||
}
|
||||
|
||||
void heap_stats(void)
|
||||
{
|
||||
printf("heap: %u (used %u, free %u) [bytes]\n", (unsigned)(&_eheap - &_sheap),
|
||||
&_eheap - &_sheap - get_free_heap_size(), get_free_heap_size());
|
||||
ets_printf("heap: %u (used %u, free %u) [bytes]\n",
|
||||
_alloc + _free, _alloc, _free);
|
||||
}
|
||||
|
||||
#endif /* MODULE_ESP_IDF_HEAP */
|
||||
|
||||
/* alias for compatibility with espressif/wifi_libs */
|
||||
uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size")));
|
||||
|
||||
/**
|
||||
* @name Other system functions
|
||||
*/
|
||||
@ -288,7 +82,7 @@ void _abort(void)
|
||||
{
|
||||
ets_printf("#! abort called: powering off\n");
|
||||
pm_off();
|
||||
while(1);
|
||||
while (1) { };
|
||||
}
|
||||
|
||||
void _exit_r(struct _reent *r, int status)
|
||||
@ -296,19 +90,13 @@ void _exit_r(struct _reent *r, int status)
|
||||
_exit(status);
|
||||
}
|
||||
|
||||
struct _reent* __getreent(void) {
|
||||
return _GLOBAL_REENT;
|
||||
}
|
||||
|
||||
static int _no_sys_func (struct _reent *r)
|
||||
static int _no_sys_func(struct _reent *r)
|
||||
{
|
||||
DEBUG("%s: system function does not exist\n", __func__);
|
||||
r->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct _reent s_reent;
|
||||
|
||||
static struct syscall_stub_table s_stub_table =
|
||||
{
|
||||
.__getreent = &__getreent,
|
||||
@ -360,42 +148,35 @@ static struct syscall_stub_table s_stub_table =
|
||||
#endif /* CONFIG_NEWLIB_NANO_FORMAT */
|
||||
};
|
||||
|
||||
void IRAM syscalls_init (void)
|
||||
void IRAM syscalls_init_arch(void)
|
||||
{
|
||||
/* enable the system timer in us (TMG0 is enabled by default) */
|
||||
TIMER_SYSTEM.config.divider = rtc_clk_apb_freq_get()/MHZ;
|
||||
TIMER_SYSTEM.config.autoreload = 0;
|
||||
TIMER_SYSTEM.config.enable = 1;
|
||||
|
||||
syscall_table_ptr_pro = &s_stub_table;
|
||||
syscall_table_ptr_app = &s_stub_table;
|
||||
|
||||
_GLOBAL_REENT = &s_reent;
|
||||
|
||||
environ = malloc(sizeof(char*));
|
||||
environ[0] = NULL;
|
||||
}
|
||||
|
||||
uint32_t system_get_time (void)
|
||||
uint32_t system_get_time(void)
|
||||
{
|
||||
/* latch 64 bit timer value before read */
|
||||
TIMER_SYSTEM.update = 0;
|
||||
/* wait until instructions have been finished */
|
||||
__asm__ volatile ("isync");
|
||||
/* read the current timer value */
|
||||
return TIMER_SYSTEM.cnt_low;
|
||||
}
|
||||
|
||||
uint32_t system_get_time_ms (void)
|
||||
uint32_t system_get_time_ms(void)
|
||||
{
|
||||
return system_get_time_64() / USEC_PER_MSEC;
|
||||
}
|
||||
|
||||
uint64_t system_get_time_64 (void)
|
||||
uint64_t system_get_time_64(void)
|
||||
{
|
||||
uint64_t ret;
|
||||
/* latch 64 bit timer value before read */
|
||||
TIMER_SYSTEM.update = 0;
|
||||
/* wait until instructions have been finished */
|
||||
__asm__ volatile ("isync");
|
||||
/* read the current timer value */
|
||||
ret = TIMER_SYSTEM.cnt_low;
|
||||
ret += ((uint64_t)TIMER_SYSTEM.cnt_high) << 32;
|
||||
@ -411,7 +192,7 @@ static IRAM void system_wdt_int_handler(void *arg)
|
||||
system_wdt_feed();
|
||||
}
|
||||
|
||||
void IRAM system_wdt_feed (void)
|
||||
void IRAM system_wdt_feed(void)
|
||||
{
|
||||
DEBUG("%s\n", __func__);
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */
|
||||
@ -419,7 +200,7 @@ void IRAM system_wdt_feed (void)
|
||||
TIMERG0.wdt_wprotect=0; /* enable write protection */
|
||||
}
|
||||
|
||||
void system_wdt_init (void)
|
||||
void system_wdt_init(void)
|
||||
{
|
||||
/* disable boot watchdogs */
|
||||
TIMERG0.wdt_config0.flashboot_mod_en = 0;
|
||||
@ -456,7 +237,7 @@ void system_wdt_init (void)
|
||||
xt_ints_on(BIT(CPU_INUM_WDT));
|
||||
}
|
||||
|
||||
void system_wdt_stop (void)
|
||||
void system_wdt_stop(void)
|
||||
{
|
||||
xt_ints_off(BIT(CPU_INUM_WDT));
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */
|
||||
@ -465,7 +246,7 @@ void system_wdt_stop (void)
|
||||
TIMERG0.wdt_wprotect = 0; /* enable write protection */
|
||||
}
|
||||
|
||||
void system_wdt_start (void)
|
||||
void system_wdt_start(void)
|
||||
{
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */
|
||||
TIMERG0.wdt_config0.en = 1; /* disable MWDT */
|
||||
@ -473,18 +254,3 @@ void system_wdt_start (void)
|
||||
TIMERG0.wdt_wprotect = 0; /* enable write protection */
|
||||
xt_ints_on(BIT(CPU_INUM_WDT));
|
||||
}
|
||||
|
||||
__attribute__((weak)) void
|
||||
_system_prevent_memset_lto(void *const s, int c, const size_t n)
|
||||
{
|
||||
(void) s;
|
||||
(void) c;
|
||||
(void) n;
|
||||
}
|
||||
|
||||
void *system_secure_memset(void *s, int c, size_t n)
|
||||
{
|
||||
memset(s, c, n);
|
||||
_system_prevent_memset_lto(s, c, n);
|
||||
return s;
|
||||
}
|
||||
|
@ -21,38 +21,17 @@
|
||||
#ifndef SYSCALLS_H
|
||||
#define SYSCALLS_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "syscalls_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Necessary initializations of system call functions */
|
||||
extern void syscalls_init (void);
|
||||
#ifndef DOXYGEN
|
||||
|
||||
/** System standard printf function */
|
||||
extern int printf(const char* format, ...);
|
||||
#define MALLOC_CAP_DEFAULT MALLOC_CAP_8BIT
|
||||
|
||||
/** Determine free heap size */
|
||||
unsigned int get_free_heap_size (void);
|
||||
|
||||
/** System standard puts function */
|
||||
extern int puts(const char * str);
|
||||
|
||||
/** Determine free heap size */
|
||||
extern unsigned int get_free_heap_size (void);
|
||||
|
||||
/** Time since boot in us (32bit version) */
|
||||
uint32_t system_get_time (void);
|
||||
|
||||
/** Time since boot in ms (32bit version) */
|
||||
uint32_t system_get_time_ms (void);
|
||||
|
||||
/** memset version that the compiler should not be allowed to optimize this */
|
||||
void *system_secure_memset(void *s, int c, size_t n);
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -18,368 +18,36 @@
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "periph/pm.h"
|
||||
#include "timex.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/times.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include "mutex.h"
|
||||
#include "rmutex.h"
|
||||
|
||||
#include "esp_common.h"
|
||||
#include "esp/common_macros.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_common.h"
|
||||
#include "sdk/sdk.h"
|
||||
#include "syscalls.h"
|
||||
|
||||
#ifdef MODULE_STDIO_UART
|
||||
#include "stdio_uart.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_ESP_IDF_HEAP
|
||||
#include "esp_heap_caps.h"
|
||||
#else
|
||||
#include "malloc.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define MHZ 1000000UL
|
||||
|
||||
#ifndef MODULE_PTHREAD
|
||||
|
||||
#define PTHREAD_CANCEL_DISABLE 1
|
||||
/*
|
||||
* This is a dummy function to avoid undefined references when linking
|
||||
* against newlib and module pthread is not used.
|
||||
*/
|
||||
int pthread_setcancelstate(int state, int *oldstate)
|
||||
{
|
||||
if (oldstate) {
|
||||
*oldstate = PTHREAD_CANCEL_DISABLE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* MODULE_PTHREAD */
|
||||
|
||||
/*
|
||||
* TODO: When the lock functions in this section are enabled, an application
|
||||
* crashes when an ISR calls a `newlib` function that uses `_lock_acquire`
|
||||
* or `_log_acquire_recursive` to be thread-safe, for example, `puts` in
|
||||
* `tests/isr_yield_higher`. The reason is that the implementation of these
|
||||
* functions uses `mutex` and `rmutex` that do not work in the interrupt
|
||||
* context. Therefore, the lock functions are disabled for the moment, and
|
||||
* instead `newlib`'s dummy lock functions are used which do not guarantee
|
||||
* thread safety.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
|
||||
/**
|
||||
* @name Locking functions
|
||||
*
|
||||
* Following functions implement the lock mechanism for newlib.
|
||||
*/
|
||||
|
||||
/**
|
||||
* _malloc_rmtx is defined as static variable to avoid recursive calls of
|
||||
* malloc when _malloc_r tries to lock __malloc_lock_object the first
|
||||
* time. All other mutexes that are used for the lock mechanism are allocated
|
||||
* dynamically.
|
||||
*/
|
||||
static rmutex_t _malloc_rmtx = RMUTEX_INIT;
|
||||
|
||||
/**
|
||||
* To properly handle the static rmutex _malloc_rmtx, we have to know
|
||||
* the address of newlib's static variable __malloc_lock_object.
|
||||
*/
|
||||
static _lock_t *__malloc_static_object = NULL;
|
||||
|
||||
void IRAM _lock_init(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
mutex_t* mtx = malloc(sizeof(mutex_t));
|
||||
|
||||
if (mtx) {
|
||||
memset(mtx, 0, sizeof(mutex_t));
|
||||
*lock = (_lock_t)mtx;
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM _lock_init_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/**
|
||||
* Since we don't have direct access to newlib's static variable
|
||||
* __malloc_lock_object, we have to rely on the fact that function
|
||||
* _lock_aqcuire_recursive, and thus function _lock_init_recursive
|
||||
* is called for the first time with newlib's static variable
|
||||
* __malloc_lock_object as parameter. This is ensured by calling
|
||||
* malloc in the function syscalls_init.
|
||||
*/
|
||||
if (__malloc_static_object == NULL) {
|
||||
*lock = (_lock_t)&_malloc_rmtx;
|
||||
__malloc_static_object = lock;
|
||||
return;
|
||||
}
|
||||
|
||||
/* _malloc_rmtx is static and has not to be allocated */
|
||||
if (lock == __malloc_static_object) {
|
||||
return;
|
||||
}
|
||||
|
||||
rmutex_t* rmtx = malloc(sizeof(rmutex_t));
|
||||
|
||||
if (rmtx) {
|
||||
memset(rmtx, 0, sizeof(rmutex_t));
|
||||
*lock = (_lock_t)rmtx;
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM _lock_close(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
assert(lock != __malloc_static_object);
|
||||
|
||||
free((void*)*lock);
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void IRAM _lock_close_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
assert(lock != __malloc_static_object);
|
||||
|
||||
free((void*)*lock);
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void IRAM _lock_acquire(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/* if the lock data structure is still not allocated, initialize it first */
|
||||
if (*lock == 0) {
|
||||
_lock_init(lock);
|
||||
}
|
||||
|
||||
/* if scheduler is not running, we have not to lock the mutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!irq_is_in());
|
||||
mutex_lock((mutex_t*)*lock);
|
||||
}
|
||||
|
||||
void IRAM _lock_acquire_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/* if the lock data structure is still not allocated, initialize it first */
|
||||
if (*lock == 0) {
|
||||
_lock_init_recursive(lock);
|
||||
}
|
||||
|
||||
/* if scheduler is not running, we have not to lock the rmutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!irq_is_in());
|
||||
rmutex_lock((rmutex_t*)*lock);
|
||||
}
|
||||
|
||||
int IRAM _lock_try_acquire(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/* if the lock data structure is still not allocated, initialize it first */
|
||||
if (*lock == 0) {
|
||||
_lock_init(lock);
|
||||
}
|
||||
|
||||
/* if scheduler is not running, we have not to lock the mutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (irq_is_in()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mutex_trylock((mutex_t*)*lock);
|
||||
}
|
||||
|
||||
int IRAM _lock_try_acquire_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/* if the lock data structure is still not allocated, initialize it first */
|
||||
if (*lock == 0) {
|
||||
_lock_init_recursive(lock);
|
||||
}
|
||||
|
||||
/* if scheduler is not running, we have not to lock the rmutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (irq_is_in()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return rmutex_trylock((rmutex_t*)*lock);
|
||||
}
|
||||
|
||||
void IRAM _lock_release(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL && *lock != 0);
|
||||
|
||||
/* if scheduler is not running, we have not to unlock the mutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_unlock((mutex_t*)*lock);
|
||||
}
|
||||
|
||||
void IRAM _lock_release_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL && *lock != 0);
|
||||
|
||||
/* if scheduler is not running, we have not to unlock the rmutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
rmutex_unlock((rmutex_t*)*lock);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Memory allocation functions
|
||||
*/
|
||||
|
||||
#ifdef MODULE_ESP_IDF_HEAP
|
||||
|
||||
#define MALLOC_CAP_DEFAULT MALLOC_CAP_8BIT
|
||||
#define heap_caps_malloc_default(s) heap_caps_malloc(s, MALLOC_CAP_DEFAULT)
|
||||
#define heap_caps_realloc_default(p,s) heap_caps_realloc(p, s, MALLOC_CAP_DEFAULT)
|
||||
|
||||
void* IRAM_ATTR __wrap__malloc_r(struct _reent *r, size_t size)
|
||||
{
|
||||
return heap_caps_malloc_default( size );
|
||||
}
|
||||
|
||||
void IRAM_ATTR __wrap__free_r(struct _reent *r, void *ptr)
|
||||
{
|
||||
heap_caps_free( ptr );
|
||||
}
|
||||
|
||||
void* IRAM_ATTR __wrap__realloc_r(struct _reent *r, void* ptr, size_t size)
|
||||
{
|
||||
return heap_caps_realloc_default( ptr, size );
|
||||
}
|
||||
|
||||
void* IRAM_ATTR __wrap__calloc_r(struct _reent *r, size_t count, size_t size)
|
||||
{
|
||||
void *result = heap_caps_malloc_default(count * size);
|
||||
if (result) {
|
||||
bzero(result, count * size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned int get_free_heap_size(void)
|
||||
{
|
||||
return heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
|
||||
}
|
||||
|
||||
/* this function is platform specific if module esp_idf_heap is used */
|
||||
void heap_stats(void)
|
||||
{
|
||||
size_t _free = 0;
|
||||
size_t _alloc = 0;
|
||||
|
||||
extern heap_region_t g_heap_region[HEAP_REGIONS_MAX];
|
||||
|
||||
for (int i = 0; i < HEAP_REGIONS_MAX; i++) {
|
||||
ets_printf("Heap region %u @%p: %u (used %u, free %u) [bytes]\n", i,
|
||||
g_heap_region[i].start_addr,
|
||||
g_heap_region[i].total_size,
|
||||
g_heap_region[i].total_size - g_heap_region[i].free_bytes,
|
||||
g_heap_region[i].free_bytes);
|
||||
_free += g_heap_region[i].free_bytes;
|
||||
_alloc += g_heap_region[i].total_size - g_heap_region[i].free_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* MODULE_ESP_IDF_HEAP */
|
||||
|
||||
/* for compatibility with ESP-IDF heap functions */
|
||||
|
||||
void* _heap_caps_malloc(size_t size, uint32_t caps, const char *file, size_t line)
|
||||
{
|
||||
(void)caps;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void* _heap_caps_calloc(size_t n, size_t size, uint32_t caps, const char *file, size_t line)
|
||||
{
|
||||
(void)caps;
|
||||
return calloc(n, size);
|
||||
}
|
||||
|
||||
void* _heap_caps_realloc(void *ptr, size_t size, uint32_t caps, const char *file, size_t line)
|
||||
{
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
void *_heap_caps_zalloc(size_t size, uint32_t caps, const char *file, size_t line)
|
||||
{
|
||||
void *ptr = malloc(size);
|
||||
if (ptr) {
|
||||
memset(ptr, 0, size);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void _heap_caps_free(void *ptr, const char *file, size_t line)
|
||||
{
|
||||
(void)file;
|
||||
(void)line;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void heap_caps_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
extern uint8_t _eheap; /* end of heap (defined in esp32.common.ld) */
|
||||
extern uint8_t _sheap; /* start of heap (defined in esp32.common.ld) */
|
||||
|
||||
unsigned int IRAM get_free_heap_size(void)
|
||||
{
|
||||
struct mallinfo minfo = mallinfo();
|
||||
return &_eheap - &_sheap - minfo.uordblks;
|
||||
}
|
||||
|
||||
void heap_stats(void)
|
||||
{
|
||||
ets_printf("heap: %u (used %u, free %u) [bytes]\n",
|
||||
&_eheap - &_sheap, &_eheap - &_sheap - get_free_heap_size(),
|
||||
get_free_heap_size());
|
||||
_alloc + _free, _alloc, _free);
|
||||
}
|
||||
|
||||
/* alias for compatibility with espressif/wifi_libs */
|
||||
uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size")));
|
||||
|
||||
#endif /* MODULE_ESP_IDF_HEAP */
|
||||
|
||||
/**
|
||||
@ -391,108 +59,6 @@ int _rename_r(struct _reent *r, const char *from, const char *to)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _abort(void)
|
||||
{
|
||||
ets_printf("#! abort called: powering off\n");
|
||||
pm_off();
|
||||
while (1) { };
|
||||
}
|
||||
|
||||
void _exit_r(struct _reent *r, int status)
|
||||
{
|
||||
_exit(status);
|
||||
}
|
||||
|
||||
struct _reent* __getreent(void) {
|
||||
return _GLOBAL_REENT;
|
||||
}
|
||||
|
||||
#ifdef MCU_ESP32
|
||||
static int _no_sys_func(struct _reent *r)
|
||||
{
|
||||
DEBUG("%s: system function does not exist\n", __func__);
|
||||
r->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct _reent s_reent;
|
||||
|
||||
#ifdef MCU_ESP32
|
||||
static struct syscall_stub_table s_stub_table =
|
||||
{
|
||||
.__getreent = &__getreent,
|
||||
|
||||
._malloc_r = &_malloc_r,
|
||||
._free_r = &_free_r,
|
||||
._realloc_r = &_realloc_r,
|
||||
._calloc_r = &_calloc_r,
|
||||
._sbrk_r = &_sbrk_r,
|
||||
|
||||
._system_r = (int (*)(struct _reent *, const char*))&_no_sys_func,
|
||||
._raise_r = (void (*)(struct _reent *))&_no_sys_func,
|
||||
._abort = &_abort,
|
||||
._exit_r = &_exit_r,
|
||||
._getpid_r = &_getpid_r,
|
||||
._kill_r = &_kill_r,
|
||||
|
||||
._times_r = &_times_r,
|
||||
._gettimeofday_r = _gettimeofday_r,
|
||||
|
||||
._open_r = &_open_r,
|
||||
._close_r = &_close_r,
|
||||
._lseek_r = (int (*)(struct _reent *r, int, int, int))&_lseek_r,
|
||||
._fstat_r = &_fstat_r,
|
||||
._stat_r = &_stat_r,
|
||||
._write_r = (int (*)(struct _reent *r, int, const void *, int))&_write_r,
|
||||
._read_r = (int (*)(struct _reent *r, int, void *, int))&_read_r,
|
||||
._unlink_r = &_unlink_r,
|
||||
._link_r = (int (*)(struct _reent *r, const char*, const char*))&_no_sys_func,
|
||||
._rename_r = (int (*)(struct _reent *r, const char*, const char*))&_no_sys_func,
|
||||
|
||||
._lock_init = &_lock_init,
|
||||
._lock_init_recursive = &_lock_init_recursive,
|
||||
._lock_close = &_lock_close,
|
||||
._lock_close_recursive = &_lock_close_recursive,
|
||||
._lock_acquire = &_lock_acquire,
|
||||
._lock_acquire_recursive = &_lock_acquire_recursive,
|
||||
._lock_try_acquire = &_lock_try_acquire,
|
||||
._lock_try_acquire_recursive = &_lock_try_acquire_recursive,
|
||||
._lock_release = &_lock_release,
|
||||
._lock_release_recursive = &_lock_release_recursive,
|
||||
|
||||
#if CONFIG_NEWLIB_NANO_FORMAT
|
||||
._printf_float = &_printf_float,
|
||||
._scanf_float = &_scanf_float,
|
||||
#else /* CONFIG_NEWLIB_NANO_FORMAT */
|
||||
._printf_float = NULL,
|
||||
._scanf_float = NULL,
|
||||
#endif /* CONFIG_NEWLIB_NANO_FORMAT */
|
||||
};
|
||||
#endif
|
||||
|
||||
void syscalls_init(void)
|
||||
{
|
||||
#ifdef MCU_ESP32
|
||||
/* enable the system timer in us (TMG0 is enabled by default) */
|
||||
TIMER_SYSTEM.config.divider = rtc_clk_apb_freq_get()/MHZ;
|
||||
TIMER_SYSTEM.config.autoreload = 0;
|
||||
TIMER_SYSTEM.config.enable = 1;
|
||||
syscall_table_ptr_pro = &s_stub_table;
|
||||
syscall_table_ptr_app = &s_stub_table;
|
||||
#endif
|
||||
_GLOBAL_REENT = &s_reent;
|
||||
|
||||
environ = malloc(sizeof(char*));
|
||||
environ[0] = NULL;
|
||||
|
||||
/*
|
||||
* initialization of newlib, includes the ctors initialization
|
||||
*/
|
||||
extern void __libc_init_array(void);
|
||||
__libc_init_array();
|
||||
}
|
||||
|
||||
uint32_t system_get_time(void)
|
||||
{
|
||||
return phy_get_mactime();
|
||||
@ -500,107 +66,9 @@ uint32_t system_get_time(void)
|
||||
|
||||
uint32_t system_get_time_ms(void)
|
||||
{
|
||||
return phy_get_mactime() / US_PER_MS;
|
||||
return system_get_time() / USEC_PER_MSEC;
|
||||
}
|
||||
|
||||
#ifdef MCU_ESP32
|
||||
uint64_t system_get_time_64(void)
|
||||
void IRAM_ATTR syscalls_init_arch(void)
|
||||
{
|
||||
uint64_t ret;
|
||||
/* latch 64 bit timer value before read */
|
||||
TIMER_SYSTEM.update = 0;
|
||||
/* wait until instructions have been finished */
|
||||
__asm__ volatile ("isync");
|
||||
/* read the current timer value */
|
||||
ret = TIMER_SYSTEM.cnt_low;
|
||||
ret += ((uint64_t)TIMER_SYSTEM.cnt_high) << 32;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* alias for compatibility with espressif/wifi_libs */
|
||||
int64_t esp_timer_get_time(void) __attribute__((alias("system_get_time_64")));
|
||||
|
||||
static IRAM void system_wdt_int_handler(void *arg)
|
||||
{
|
||||
TIMERG0.int_clr_timers.wdt=1; /* clear interrupt */
|
||||
system_wdt_feed();
|
||||
}
|
||||
|
||||
void IRAM system_wdt_feed(void)
|
||||
{
|
||||
DEBUG("%s\n", __func__);
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */
|
||||
TIMERG0.wdt_feed=1; /* reset MWDT */
|
||||
TIMERG0.wdt_wprotect=0; /* enable write protection */
|
||||
}
|
||||
|
||||
void system_wdt_init(void)
|
||||
{
|
||||
/* disable boot watchdogs */
|
||||
TIMERG0.wdt_config0.flashboot_mod_en = 0;
|
||||
RTCCNTL.wdt_config0.flashboot_mod_en = 0;
|
||||
|
||||
/* enable system watchdog */
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */
|
||||
TIMERG0.wdt_config0.stg0 = TIMG_WDT_STG_SEL_INT; /* stage0 timeout: interrupt */
|
||||
TIMERG0.wdt_config0.stg1 = TIMG_WDT_STG_SEL_RESET_SYSTEM; /* stage1 timeout: sys reset */
|
||||
TIMERG0.wdt_config0.sys_reset_length = 7; /* sys reset signal length: 3.2 us */
|
||||
TIMERG0.wdt_config0.cpu_reset_length = 7; /* sys reset signal length: 3.2 us */
|
||||
TIMERG0.wdt_config0.edge_int_en = 0;
|
||||
TIMERG0.wdt_config0.level_int_en = 1;
|
||||
|
||||
/* MWDT clock = 80 * 12,5 ns = 1 us */
|
||||
TIMERG0.wdt_config1.clk_prescale = 80;
|
||||
|
||||
/* define stage timeouts */
|
||||
TIMERG0.wdt_config2 = 2 * US_PER_SEC; /* stage 0: 2 s (interrupt) */
|
||||
TIMERG0.wdt_config3 = 4 * US_PER_SEC; /* stage 1: 4 s (sys reset) */
|
||||
|
||||
TIMERG0.wdt_config0.en = 1; /* enable MWDT */
|
||||
TIMERG0.wdt_feed = 1; /* reset MWDT */
|
||||
TIMERG0.wdt_wprotect = 0; /* enable write protection */
|
||||
|
||||
DEBUG("%s TIMERG0 wdt_config0=%08x wdt_config1=%08x wdt_config2=%08x\n",
|
||||
__func__, TIMERG0.wdt_config0.val, TIMERG0.wdt_config1.val,
|
||||
TIMERG0.wdt_config2);
|
||||
|
||||
/* route WDT peripheral interrupt source to CPU_INUM_WDT */
|
||||
intr_matrix_set(PRO_CPU_NUM, ETS_TG0_WDT_LEVEL_INTR_SOURCE, CPU_INUM_WDT);
|
||||
/* set the interrupt handler and activate the interrupt */
|
||||
xt_set_interrupt_handler(CPU_INUM_WDT, system_wdt_int_handler, NULL);
|
||||
xt_ints_on(BIT(CPU_INUM_WDT));
|
||||
}
|
||||
|
||||
void system_wdt_stop(void)
|
||||
{
|
||||
xt_ints_off(BIT(CPU_INUM_WDT));
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */
|
||||
TIMERG0.wdt_config0.en = 0; /* disable MWDT */
|
||||
TIMERG0.wdt_feed = 1; /* reset MWDT */
|
||||
TIMERG0.wdt_wprotect = 0; /* enable write protection */
|
||||
}
|
||||
|
||||
void system_wdt_start(void)
|
||||
{
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */
|
||||
TIMERG0.wdt_config0.en = 1; /* disable MWDT */
|
||||
TIMERG0.wdt_feed = 1; /* reset MWDT */
|
||||
TIMERG0.wdt_wprotect = 0; /* enable write protection */
|
||||
xt_ints_on(BIT(CPU_INUM_WDT));
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__((weak)) void
|
||||
_system_prevent_memset_lto(void *const s, int c, const size_t n)
|
||||
{
|
||||
(void)s;
|
||||
(void)c;
|
||||
(void)n;
|
||||
}
|
||||
|
||||
void *system_secure_memset(void *s, int c, size_t n)
|
||||
{
|
||||
memset(s, c, n);
|
||||
_system_prevent_memset_lto(s, c, n);
|
||||
return s;
|
||||
}
|
||||
|
54
cpu/esp_common/include/syscalls_common.h
Normal file
54
cpu/esp_common/include/syscalls_common.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_esp_common
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of required system calls for ESP SoCs
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifndef SYSCALLS_COMMON_H
|
||||
#define SYSCALLS_COMMON_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "esp_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Necessary initializations of system call functions */
|
||||
void syscalls_init(void);
|
||||
|
||||
/** Determine free heap size */
|
||||
unsigned int get_free_heap_size(void);
|
||||
|
||||
/** Time since boot in us (32bit version) */
|
||||
uint32_t system_get_time(void);
|
||||
|
||||
/** Time since boot in ms (32bit version) */
|
||||
uint32_t system_get_time_ms(void);
|
||||
|
||||
/** memset version that the compiler should not be allowed to optimize this */
|
||||
void *system_secure_memset(void *s, int c, size_t n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SYSCALLS_COMMON_H */
|
395
cpu/esp_common/syscalls.c
Normal file
395
cpu/esp_common/syscalls.c
Normal file
@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_esp_common
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of required system calls
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio_ext.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include "irq_arch.h"
|
||||
#include "mutex.h"
|
||||
#include "rmutex.h"
|
||||
#include "timex.h"
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "syscalls.h"
|
||||
|
||||
#ifdef MODULE_ESP_IDF_HEAP
|
||||
#include "esp_heap_caps.h"
|
||||
#else
|
||||
#include "malloc.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef MODULE_PTHREAD
|
||||
|
||||
#define PTHREAD_CANCEL_DISABLE 1
|
||||
/*
|
||||
* This is a dummy function to avoid undefined references when linking
|
||||
* against newlib and module pthread is not used.
|
||||
*/
|
||||
int pthread_setcancelstate(int state, int *oldstate)
|
||||
{
|
||||
if (oldstate) {
|
||||
*oldstate = PTHREAD_CANCEL_DISABLE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* MODULE_PTHREAD */
|
||||
|
||||
/*
|
||||
* TODO: When the lock functions in this section are enabled, an application
|
||||
* crashes when an ISR calls a `newlib` function that uses `_lock_acquire`
|
||||
* or `_log_acquire_recursive` to be thread-safe, for example, `puts` in
|
||||
* `tests/isr_yield_higher`. The reason is that the implementation of these
|
||||
* functions uses `mutex` and `rmutex` that do not work in the interrupt
|
||||
* context. Therefore, the lock functions are disabled for the moment, and
|
||||
* instead `newlib`'s dummy lock functions are used which do not guarantee
|
||||
* thread safety.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Locking functions
|
||||
*
|
||||
* Following functions implement the lock mechanism for newlib.
|
||||
*/
|
||||
|
||||
/**
|
||||
* _malloc_rmtx is defined as static variable to avoid recursive calls of
|
||||
* malloc when _malloc_r tries to lock __malloc_lock_object the first
|
||||
* time. All other mutexes that are used for the lock mechanism are allocated
|
||||
* dynamically.
|
||||
*/
|
||||
static rmutex_t _malloc_rmtx = RMUTEX_INIT;
|
||||
|
||||
/**
|
||||
* To properly handle the static rmutex _malloc_rmtx, we have to know
|
||||
* the address of newlib's static variable __malloc_lock_object.
|
||||
*/
|
||||
static _lock_t *__malloc_static_object = NULL;
|
||||
|
||||
void IRAM_ATTR _lock_init(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
mutex_t* mtx = malloc(sizeof(mutex_t));
|
||||
|
||||
if (mtx) {
|
||||
memset(mtx, 0, sizeof(mutex_t));
|
||||
*lock = (_lock_t)mtx;
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_init_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/**
|
||||
* Since we don't have direct access to newlib's static variable
|
||||
* __malloc_lock_object, we have to rely on the fact that function
|
||||
* _lock_aqcuire_recursive, and thus function _lock_init_recursive
|
||||
* is called for the first time with newlib's static variable
|
||||
* __malloc_lock_object as parameter. This is ensured by calling
|
||||
* malloc in the function syscalls_init.
|
||||
*/
|
||||
if (__malloc_static_object == NULL) {
|
||||
*lock = (_lock_t)&_malloc_rmtx;
|
||||
__malloc_static_object = lock;
|
||||
return;
|
||||
}
|
||||
|
||||
/* _malloc_rmtx is static and has not to be allocated */
|
||||
if (lock == __malloc_static_object) {
|
||||
return;
|
||||
}
|
||||
|
||||
rmutex_t* rmtx = malloc(sizeof(rmutex_t));
|
||||
|
||||
if (rmtx) {
|
||||
memset(rmtx, 0, sizeof(rmutex_t));
|
||||
*lock = (_lock_t)rmtx;
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_close(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
assert(lock != __malloc_static_object);
|
||||
|
||||
free((void*)*lock);
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_close_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
assert(lock != __malloc_static_object);
|
||||
|
||||
free((void*)*lock);
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_acquire(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/* if the lock data structure is still not allocated, initialize it first */
|
||||
if (*lock == 0) {
|
||||
_lock_init(lock);
|
||||
}
|
||||
|
||||
/* if scheduler is not running, we have not to lock the mutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!irq_is_in());
|
||||
mutex_lock((mutex_t*)*lock);
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_acquire_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/* if the lock data structure is still not allocated, initialize it first */
|
||||
if (*lock == 0) {
|
||||
_lock_init_recursive(lock);
|
||||
}
|
||||
|
||||
/* if scheduler is not running, we have not to lock the rmutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!irq_is_in());
|
||||
rmutex_lock((rmutex_t*)*lock);
|
||||
}
|
||||
|
||||
int IRAM_ATTR _lock_try_acquire(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/* if the lock data structure is still not allocated, initialize it first */
|
||||
if (*lock == 0) {
|
||||
_lock_init(lock);
|
||||
}
|
||||
|
||||
/* if scheduler is not running, we have not to lock the mutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (irq_is_in()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mutex_trylock((mutex_t*)*lock);
|
||||
}
|
||||
|
||||
int IRAM_ATTR _lock_try_acquire_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL);
|
||||
|
||||
/* if the lock data structure is still not allocated, initialize it first */
|
||||
if (*lock == 0) {
|
||||
_lock_init_recursive(lock);
|
||||
}
|
||||
|
||||
/* if scheduler is not running, we have not to lock the rmutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (irq_is_in()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return rmutex_trylock((rmutex_t*)*lock);
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_release(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL && *lock != 0);
|
||||
|
||||
/* if scheduler is not running, we have not to unlock the mutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_unlock((mutex_t*)*lock);
|
||||
}
|
||||
|
||||
void IRAM_ATTR _lock_release_recursive(_lock_t *lock)
|
||||
{
|
||||
assert(lock != NULL && *lock != 0);
|
||||
|
||||
/* if scheduler is not running, we have not to unlock the rmutex */
|
||||
if (sched_active_thread == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
rmutex_unlock((rmutex_t*)*lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Memory allocation functions
|
||||
*/
|
||||
|
||||
#ifdef MODULE_ESP_IDF_HEAP
|
||||
|
||||
#define heap_caps_malloc_default(s) heap_caps_malloc(s, MALLOC_CAP_DEFAULT)
|
||||
#define heap_caps_realloc_default(p,s) heap_caps_realloc(p, s, MALLOC_CAP_DEFAULT)
|
||||
|
||||
void* IRAM_ATTR __wrap__malloc_r(struct _reent *r, size_t size)
|
||||
{
|
||||
return heap_caps_malloc_default( size );
|
||||
}
|
||||
|
||||
void IRAM_ATTR __wrap__free_r(struct _reent *r, void *ptr)
|
||||
{
|
||||
heap_caps_free( ptr );
|
||||
}
|
||||
|
||||
void* IRAM_ATTR __wrap__realloc_r(struct _reent *r, void* ptr, size_t size)
|
||||
{
|
||||
return heap_caps_realloc_default( ptr, size );
|
||||
}
|
||||
|
||||
void* IRAM_ATTR __wrap__calloc_r(struct _reent *r, size_t count, size_t size)
|
||||
{
|
||||
void *result = heap_caps_malloc_default(count * size);
|
||||
if (result) {
|
||||
bzero(result, count * size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#else /* MODULE_ESP_IDF_HEAP */
|
||||
|
||||
/* for compatibility with ESP-IDF heap functions */
|
||||
|
||||
void* _heap_caps_malloc(size_t size, uint32_t caps, const char *file, size_t line)
|
||||
{
|
||||
(void)caps;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void* _heap_caps_calloc(size_t n, size_t size, uint32_t caps, const char *file, size_t line)
|
||||
{
|
||||
(void)caps;
|
||||
return calloc(n, size);
|
||||
}
|
||||
|
||||
void* _heap_caps_realloc(void *ptr, size_t size, uint32_t caps, const char *file, size_t line)
|
||||
{
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
void *_heap_caps_zalloc(size_t size, uint32_t caps, const char *file, size_t line)
|
||||
{
|
||||
void *ptr = malloc(size);
|
||||
if (ptr) {
|
||||
memset(ptr, 0, size);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void _heap_caps_free(void *ptr, const char *file, size_t line)
|
||||
{
|
||||
(void)file;
|
||||
(void)line;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void heap_caps_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
extern uint8_t _eheap; /* end of heap (defined in ld script) */
|
||||
extern uint8_t _sheap; /* start of heap (defined in ld script) */
|
||||
|
||||
unsigned int IRAM_ATTR get_free_heap_size(void)
|
||||
{
|
||||
struct mallinfo minfo = mallinfo();
|
||||
return &_eheap - &_sheap - minfo.uordblks;
|
||||
}
|
||||
|
||||
void heap_stats(void)
|
||||
{
|
||||
ets_printf("heap: %u (used %u, free %u) [bytes]\n",
|
||||
&_eheap - &_sheap, &_eheap - &_sheap - get_free_heap_size(),
|
||||
get_free_heap_size());
|
||||
}
|
||||
|
||||
/* alias for compatibility with espressif/wifi_libs */
|
||||
uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size")));
|
||||
|
||||
#endif /* MODULE_ESP_IDF_HEAP */
|
||||
|
||||
/**
|
||||
* @name Other system functions
|
||||
*/
|
||||
|
||||
struct _reent* __getreent(void) {
|
||||
return _GLOBAL_REENT;
|
||||
}
|
||||
|
||||
static struct _reent s_reent;
|
||||
|
||||
void syscalls_init(void)
|
||||
{
|
||||
extern void syscalls_init_arch(void);
|
||||
syscalls_init_arch();
|
||||
|
||||
_GLOBAL_REENT = &s_reent;
|
||||
|
||||
environ = malloc(sizeof(char*));
|
||||
environ[0] = NULL;
|
||||
|
||||
/* initialization of newlib, includes the ctors initialization */
|
||||
extern void __libc_init_array(void);
|
||||
__libc_init_array();
|
||||
|
||||
/* initialization of global reent data structure */
|
||||
_REENT_SMALL_CHECK_INIT(_GLOBAL_REENT);
|
||||
|
||||
/*
|
||||
* disable the locking for stdout/stderr to avoid rmutex based locking
|
||||
* when puts/printf are called from an ISR
|
||||
*/
|
||||
__fsetlocking(_GLOBAL_REENT->_stdout, FSETLOCKING_BYCALLER);
|
||||
__fsetlocking(_GLOBAL_REENT->_stderr, FSETLOCKING_BYCALLER);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void
|
||||
_system_prevent_memset_lto(void *const s, int c, const size_t n)
|
||||
{
|
||||
(void)s;
|
||||
(void)c;
|
||||
(void)n;
|
||||
}
|
||||
|
||||
void *system_secure_memset(void *s, int c, size_t n)
|
||||
{
|
||||
memset(s, c, n);
|
||||
_system_prevent_memset_lto(s, c, n);
|
||||
return s;
|
||||
}
|
Loading…
Reference in New Issue
Block a user