App Loading via Loader (#1)

* app loading wip

* various improvements

irq/isr stuff is now working
lvgl locking where needed
hello world now uses proper mutex for app unlocking
etc?

* various improvements

* cmsis_esp improvements

* implement interrupts
This commit is contained in:
Ken Van Hoeylandt 2023-12-30 12:39:07 +01:00 committed by GitHub
parent 60372076d5
commit b9427d4eba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
83 changed files with 1180 additions and 623 deletions

View File

@ -30,7 +30,7 @@ You might have to remove this setting if you're not using the Yellow Board descr
See below for the supported hardware. See below for the supported hardware.
Predefined configurations are available for: Predefined configurations are available for:
- Yellow Board / 2432S024 - Yellow Board / 2432S024 (capacitive touch variant)
- (more will follow) - (more will follow)
### Drivers ### Drivers

View File

@ -31,7 +31,7 @@ IRAM_ATTR static bool prv_on_color_trans_done(esp_lcd_panel_io_handle_t io_handl
return (need_yield == pdTRUE); return (need_yield == pdTRUE);
} }
static bool prv_create_display(DisplayDevice* display) { static bool prv_create_display_device(DisplayDevice* display) {
ESP_LOGI(TAG, "creating display"); ESP_LOGI(TAG, "creating display");
gpio_config_t io_conf = { gpio_config_t io_conf = {
@ -117,6 +117,7 @@ static bool prv_create_display(DisplayDevice* display) {
display->vertical_resolution = LCD_VERTICAL_RESOLUTION; display->vertical_resolution = LCD_VERTICAL_RESOLUTION;
display->draw_buffer_height = LCD_DRAW_BUFFER_HEIGHT; display->draw_buffer_height = LCD_DRAW_BUFFER_HEIGHT;
display->bits_per_pixel = LCD_BITS_PER_PIXEL; display->bits_per_pixel = LCD_BITS_PER_PIXEL;
display->monochrome = false;
return true; return true;
} }
@ -124,6 +125,6 @@ static bool prv_create_display(DisplayDevice* display) {
DisplayDriver board_2432s024_create_display_driver() { DisplayDriver board_2432s024_create_display_driver() {
return (DisplayDriver) { return (DisplayDriver) {
.name = "ili9341_2432s024", .name = "ili9341_2432s024",
.create_display = &prv_create_display .create_display_device = &prv_create_display_device
}; };
} }

View File

@ -9,7 +9,7 @@
#define TAG "2432s024_cst816" #define TAG "2432s024_cst816"
static bool prv_create_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) { static bool prv_create_touch_device(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) {
ESP_LOGI(TAG, "creating touch"); ESP_LOGI(TAG, "creating touch");
const i2c_config_t i2c_conf = { const i2c_config_t i2c_conf = {
@ -67,6 +67,6 @@ static bool prv_create_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch
TouchDriver board_2432s024_create_touch_driver() { TouchDriver board_2432s024_create_touch_driver() {
return (TouchDriver) { return (TouchDriver) {
.name = "cst816s_2432s024", .name = "cst816s_2432s024",
.create_touch = &prv_create_touch .create_touch_device = &prv_create_touch_device
}; };
} }

View File

@ -52,12 +52,13 @@
#elif defined (__ti__) #elif defined (__ti__)
#include "cmsis_tiarmclang.h" #include "cmsis_tiarmclang.h"
#elif defined (ESP_PLATFORM)
#include "cmsis_esp.h"
/* /*
* GNU Compiler * GNU Compiler
*/ */
#elif defined ( __GNUC__ ) #elif defined ( __GNUC__ )
#include "cmsis_gcc_esp32.h" #include "cmsis_gcc.h"
// #include "cmsis_gcc.h"
/* /*

View File

@ -1,5 +1,5 @@
/**************************************************************************//** /**************************************************************************//**
* @file cmsis_gcc.h * @file cmsis_esp.h
* @brief CMSIS compiler GCC header file * @brief CMSIS compiler GCC header file
* @version V5.4.2 * @version V5.4.2
* @date 17. December 2022 * @date 17. December 2022
@ -25,6 +25,8 @@
#ifndef __CMSIS_GCC_H #ifndef __CMSIS_GCC_H
#define __CMSIS_GCC_H #define __CMSIS_GCC_H
#include "freertos/portmacro.h"
/* ignore some GCC warnings */ /* ignore some GCC warnings */
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion" #pragma GCC diagnostic ignored "-Wsign-conversion"
@ -949,25 +951,19 @@ __STATIC_FORCEINLINE uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr)
/** /**
\brief Enable IRQ Interrupts \brief Enable IRQ Interrupts
\details Enables IRQ interrupts by clearing special-purpose register PRIMASK.
Can only be executed in Privileged modes.
*/ */
__STATIC_FORCEINLINE void __enable_irq(void) __STATIC_FORCEINLINE void __enable_irq(void)
{ {
// TODO esp portENABLE_INTERRUPTS();
// __ASM volatile ("cpsie i" : : : "memory");
} }
/** /**
\brief Disable IRQ Interrupts \brief Disable IRQ Interrupts
\details Disables IRQ interrupts by setting special-purpose register PRIMASK.
Can only be executed in Privileged modes.
*/ */
__STATIC_FORCEINLINE void __disable_irq(void) __STATIC_FORCEINLINE void __disable_irq(void)
{ {
// TODO esp portDISABLE_INTERRUPTS();
// __ASM volatile ("cpsid i" : : : "memory");
} }
@ -1034,11 +1030,8 @@ __STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control)
*/ */
__STATIC_FORCEINLINE uint32_t __get_IPSR(void) __STATIC_FORCEINLINE uint32_t __get_IPSR(void)
{ {
uint32_t result; // TODO esp
return 0;
result = 0; // TODO esp
// __ASM volatile ("MRS %0, ipsr" : "=r" (result) );
return(result);
} }
@ -1050,7 +1043,6 @@ __STATIC_FORCEINLINE uint32_t __get_IPSR(void)
__STATIC_FORCEINLINE uint32_t __get_APSR(void) __STATIC_FORCEINLINE uint32_t __get_APSR(void)
{ {
uint32_t result; uint32_t result;
__ASM volatile ("MRS %0, apsr" : "=r" (result) ); __ASM volatile ("MRS %0, apsr" : "=r" (result) );
return(result); return(result);
} }
@ -1212,11 +1204,8 @@ __STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack)
*/ */
__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) __STATIC_FORCEINLINE uint32_t __get_PRIMASK(void)
{ {
uint32_t result; // Not supported by ESP
return 0U;
result = 1U; // TODO esp
// __ASM volatile ("MRS %0, primask" : "=r" (result) );
return(result);
} }

View File

@ -0,0 +1,19 @@
#pragma once
#include "furi.h"
typedef FuriEventFlag* FuriApiLock;
#define API_LOCK_EVENT (1U << 0)
#define api_lock_alloc_locked() furi_event_flag_alloc()
#define api_lock_wait_unlock(_lock) \
furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever)
#define api_lock_free(_lock) furi_event_flag_free(_lock)
#define api_lock_unlock(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT)
#define api_lock_wait_unlock_and_free(_lock) \
api_lock_wait_unlock(_lock); \
api_lock_free(_lock);

102
components/furi/src/app.c Normal file
View File

@ -0,0 +1,102 @@
#include "app_i.h"
#include "furi_core.h"
#include "log.h"
#include "furi_string.h"
#define TAG "app"
const char* prv_type_service = "service";
const char* prv_type_system = "system";
const char* prv_type_user = "user";
static FuriThreadPriority get_thread_priority(AppType type) {
switch (type) {
case AppTypeService:
return FuriThreadPriorityHighest;
case AppTypeSystem:
return FuriThreadPriorityHigh;
case AppTypeUser:
return FuriThreadPriorityNormal;
default:
furi_crash("no priority defined for app type");
}
}
const char* furi_app_type_to_string(AppType type) {
switch (type) {
case AppTypeService:
return prv_type_service;
case AppTypeSystem:
return prv_type_system;
case AppTypeUser:
return prv_type_user;
default:
furi_crash();
}
}
App* furi_app_alloc(const AppManifest* _Nonnull manifest) {
App app = {
.manifest = manifest,
.thread = NULL,
.ep_thread_args = NULL
};
App* app_ptr = malloc(sizeof(App));
return memcpy(app_ptr, &app, sizeof(App));
}
void furi_app_free(App* app) {
furi_assert(app);
if(app->thread) {
furi_thread_join(app->thread);
furi_thread_free(app->thread);
}
if (app->ep_thread_args) {
free(app->ep_thread_args);
app->ep_thread_args = NULL;
}
free(app);
}
FuriThread* furi_app_alloc_thread(App _Nonnull* app, const char* args) {
FURI_LOG_I(
TAG,
"Starting %s app \"%s\"",
furi_app_type_to_string(app->manifest->type),
app->manifest->name
);
// Free any previous app launching arguments
if (app->ep_thread_args) {
free(app->ep_thread_args);
}
if (args) {
app->ep_thread_args = strdup(args);
} else {
app->ep_thread_args = NULL;
}
FuriThread* thread = furi_thread_alloc_ex(
app->manifest->name,
app->manifest->stack_size,
app->manifest->entry_point,
app
);
if (app->manifest->type == AppTypeService) {
furi_thread_mark_as_service(thread);
}
FuriString* app_name = furi_string_alloc();
furi_thread_set_appid(thread, furi_string_get_cstr(app_name));
furi_string_free(app_name);
FuriThreadPriority priority = get_thread_priority(app->manifest->type);
furi_thread_set_priority(thread, priority);
return thread;
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "app_manifest.h"
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
FuriThread* thread;
const AppManifest* manifest;
void* ep_thread_args;
} App;
const char* furi_app_type_to_string(AppType type);
FuriThread* furi_app_alloc_thread(App* _Nonnull app, const char* args);
App* furi_app_alloc(const AppManifest* _Nonnull manifest);
void furi_app_free(App* _Nonnull app);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,32 @@
#pragma once
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
AppTypeService,
AppTypeSystem,
AppTypeUser
} AppType;
typedef enum {
AppStackSizeNormal = 2048
} AppStackSize;
typedef int32_t (*AppEntryPoint)(void _Nonnull* parameter);
typedef struct {
const char* _Nonnull id;
const char* _Nonnull name;
const char* _Nullable icon;
const AppType type;
const AppEntryPoint _Nullable entry_point;
const AppStackSize stack_size;
} AppManifest;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,79 @@
#include "app_manifest_registry.h"
#include "furi_core.h"
#include "m-dict.h"
#include "m_cstr_dup.h"
#include "mutex.h"
#define TAG "app_registry"
typedef struct {
const AppManifest* manifest;
} AppEntry;
DICT_DEF2(AppManifestDict, const char*, M_CSTR_DUP_OPLIST, const AppManifest*, M_PTR_OPLIST)
#define APP_REGISTRY_FOR_EACH(manifest_var_name, code_to_execute) \
{ \
app_registry_lock(); \
AppManifestDict_it_t it; \
for (AppManifestDict_it(it, app_manifest_dict); !AppManifestDict_end_p(it); AppManifestDict_next(it)) { \
const AppManifest* (manifest_var_name) = AppManifestDict_cref(it)->value; \
code_to_execute; \
} \
app_registry_unlock(); \
}
AppManifestDict_t app_manifest_dict;
FuriMutex* mutex = NULL;
void app_manifest_registry_init() {
furi_assert(mutex == NULL);
mutex = furi_mutex_alloc(FuriMutexTypeNormal);
AppManifestDict_init(app_manifest_dict);
}
void app_registry_lock() {
furi_assert(mutex != NULL);
furi_mutex_acquire(mutex, FuriWaitForever);
}
void app_registry_unlock() {
furi_assert(mutex != NULL);
furi_mutex_release(mutex);
}
void app_manifest_registry_add(const AppManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "adding %s", manifest->id);
app_registry_lock();
AppManifestDict_set_at(app_manifest_dict, manifest->id, manifest);
app_registry_unlock();
}
void app_manifest_registry_remove(const AppManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "removing %s", manifest->id);
app_registry_lock();
AppManifestDict_erase(app_manifest_dict, manifest->id);
app_registry_unlock();
}
const AppManifest _Nullable* app_manifest_registry_find_by_id(const char* id) {
app_registry_lock();
const AppManifest _Nullable** manifest = AppManifestDict_get(app_manifest_dict, id);
app_registry_unlock();
return (manifest != NULL) ? *manifest : NULL;
}
void app_manifest_registry_for_each_of_type(AppType type, AppManifestCallback callback) {
APP_REGISTRY_FOR_EACH(manifest, {
if (manifest->type == type) {
callback(manifest);
}
});
}
void app_manifest_registry_for_each(AppManifestCallback callback) {
APP_REGISTRY_FOR_EACH(manifest, {
callback(manifest);
});
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "app_manifest.h"
#ifdef __cplusplus
extern "C" {
#endif
extern const AppManifest* const INTERNAL_APP_MANIFESTS[];
extern const size_t INTERNAL_APP_COUNT;
typedef void (*AppManifestCallback)(const AppManifest*);
void app_manifest_registry_init();
void app_manifest_registry_add(const AppManifest _Nonnull* manifest);
void app_manifest_registry_remove(const AppManifest _Nonnull* manifest);
const AppManifest _Nullable* app_manifest_registry_find_by_id(const char* id);
void app_manifest_registry_for_each(AppManifestCallback callback);
void app_manifest_registry_for_each_of_type(AppType type, AppManifestCallback callback);
#ifdef __cplusplus
}
#endif

View File

@ -1,110 +1,29 @@
#include "check.h" #include "check.h"
#include "common_defines.h" #include "furi_core_defines.h"
#include "furi_hal_console.h" #include "furi_hal_console.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h> #include <freertos/task.h>
#include <stdlib.h> #include <stdlib.h>
PLACE_IN_SECTION("MB_MEM2")
const char* __furi_check_message = NULL;
PLACE_IN_SECTION("MB_MEM2")
uint32_t __furi_check_registers[13] = {0};
/** Load r12 value to __furi_check_message and store registers to __furi_check_registers */
/*#define GET_MESSAGE_AND_STORE_REGISTERS() \
asm volatile("ldr r11, =__furi_check_message \n" \
"str r12, [r11] \n" \
"ldr r12, =__furi_check_registers \n" \
"stm r12, {r0-r11} \n" \
"str lr, [r12, #48] \n" \
: \
: \
: "memory");*/
/** Restore registers and halt MCU
*
* - Always use it with GET_MESSAGE_AND_STORE_REGISTERS
* - If debugger is(was) connected this routine will raise bkpt
* - If debugger is not connected then endless loop
*
*/
/*#define RESTORE_REGISTERS_AND_HALT_MCU(debug) \
register bool a0 asm("a0") = debug; \
asm volatile("cbnz a0, with_debugger%= \n" \
"ldr a12, =__furi_check_registers\n" \
"ldm a12, {a0-a11} \n" \
"loop%=: \n" \
"wfi \n" \
"b loop%= \n" \
"with_debugger%=: \n" \
"ldr a12, =__furi_check_registers\n" \
"ldm a12, {a0-a11} \n" \
"debug_loop%=: \n" \
"bkpt 0x00 \n" \
"wfi \n" \
"b debug_loop%= \n" \
: \
: "a"(a0) \
: "memory");*/
extern size_t xPortGetTotalHeapSize(void);
static void __furi_put_uint32_as_text(uint32_t data) { static void __furi_put_uint32_as_text(uint32_t data) {
char tmp_str[] = "-2147483648"; char tmp_str[] = "-2147483648";
itoa(data, tmp_str, 10); itoa(data, tmp_str, 10);
furi_hal_console_puts(tmp_str); furi_hal_console_puts(tmp_str);
} }
static void __furi_put_uint32_as_hex(uint32_t data) {
char tmp_str[] = "0xFFFFFFFF";
itoa(data, tmp_str, 16);
furi_hal_console_puts(tmp_str);
}
static void __furi_print_register_info() {
// Print registers
for (uint8_t i = 0; i < 12; i++) {
furi_hal_console_puts("\r\n\tr");
__furi_put_uint32_as_text(i);
furi_hal_console_puts(" : ");
__furi_put_uint32_as_hex(__furi_check_registers[i]);
}
furi_hal_console_puts("\r\n\tlr : ");
__furi_put_uint32_as_hex(__furi_check_registers[12]);
}
static void __furi_print_stack_info() { static void __furi_print_stack_info() {
furi_hal_console_puts("\r\n\tstack watermark: "); furi_hal_console_puts("\r\n\tstack watermark: ");
__furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4);
} }
static void __furi_print_bt_stack_info() {
/*
const FuriHalBtHardfaultInfo* fault_info = furi_hal_bt_get_hardfault_info();
if(fault_info == NULL) {
furi_hal_console_puts("\r\n\tcore2: not faulted");
} else {
furi_hal_console_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: ");
__furi_put_uint32_as_hex(fault_info->source_pc);
furi_hal_console_puts("\r\n\tLR: ");
__furi_put_uint32_as_hex(fault_info->source_lr);
furi_hal_console_puts("\r\n\tSP: ");
__furi_put_uint32_as_hex(fault_info->source_sp);
}
*/
}
static void __furi_print_heap_info() { static void __furi_print_heap_info() {
/* furi_hal_console_puts("\r\n\theap total: ");
furi_hal_console_puts("\r\n\t heap total: "); __furi_put_uint32_as_text(heap_caps_get_total_size(MALLOC_CAP_DEFAULT));
__furi_put_uint32_as_text(xPortGetTotalHeapSize()); furi_hal_console_puts("\r\n\theap free: ");
*/ __furi_put_uint32_as_text(heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
furi_hal_console_puts("\r\n\t heap free: "); furi_hal_console_puts("\r\n\theap min free: ");
__furi_put_uint32_as_text(xPortGetFreeHeapSize()); __furi_put_uint32_as_text(heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT));
furi_hal_console_puts("\r\n\t heap watermark: ");
__furi_put_uint32_as_text(xPortGetMinimumEverFreeHeapSize());
} }
static void __furi_print_name(bool isr) { static void __furi_print_name(bool isr) {
@ -130,24 +49,13 @@ FURI_NORETURN void __furi_crash_implementation() {
bool isr = FURI_IS_IRQ_MODE(); bool isr = FURI_IS_IRQ_MODE();
if (__furi_check_message == NULL) {
__furi_check_message = "Fatal Error";
} else if (__furi_check_message == (void*)__FURI_ASSERT_MESSAGE_FLAG) {
__furi_check_message = "furi_assert failed";
} else if (__furi_check_message == (void*)__FURI_CHECK_MESSAGE_FLAG) {
__furi_check_message = "furi_check failed";
}
furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); furi_hal_console_puts("\r\n\033[0;31m[CRASH]");
__furi_print_name(isr); __furi_print_name(isr);
furi_hal_console_puts(__furi_check_message);
__furi_print_register_info();
if (!isr) { if (!isr) {
__furi_print_stack_info(); __furi_print_stack_info();
} }
__furi_print_heap_info(); __furi_print_heap_info();
__furi_print_bt_stack_info();
// Check if debug enabled by DAP // Check if debug enabled by DAP
// https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en
@ -176,24 +84,13 @@ FURI_NORETURN void __furi_crash_implementation() {
} }
FURI_NORETURN void __furi_halt_implementation() { FURI_NORETURN void __furi_halt_implementation() {
__disable_irq(); __disable_irq();
// GET_MESSAGE_AND_STORE_REGISTERS();
bool isr = FURI_IS_IRQ_MODE(); bool isr = FURI_IS_IRQ_MODE();
if (__furi_check_message == NULL) {
__furi_check_message = "System halt requested.";
}
furi_hal_console_puts("\r\n\033[0;31m[HALT]"); furi_hal_console_puts("\r\n\033[0;31m[HALT]");
__furi_print_name(isr); __furi_print_name(isr);
furi_hal_console_puts(__furi_check_message);
furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n");
furi_hal_console_puts("\033[0m\r\n"); furi_hal_console_puts("\033[0m\r\n");
// Check if debug enabled by DAP
// https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en
// bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk;
// RESTORE_REGISTERS_AND_HALT_MCU(true);
__builtin_unreachable(); __builtin_unreachable();
} }

View File

@ -1,60 +0,0 @@
#pragma once
#include "core_defines.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <cmsis_compiler.h>
#ifndef FURI_WARN_UNUSED
#define FURI_WARN_UNUSED __attribute__((warn_unused_result))
#endif
#ifndef FURI_WEAK
#define FURI_WEAK __attribute__((weak))
#endif
#ifndef FURI_PACKED
#define FURI_PACKED __attribute__((packed))
#endif
#ifndef FURI_IS_IRQ_MASKED
#define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U)
#endif
#ifndef FURI_IS_IRQ_MODE
#define FURI_IS_IRQ_MODE() (__get_IPSR() != 0U)
#endif
#ifndef FURI_IS_ISR
#define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED())
#endif
typedef struct {
uint32_t isrm;
bool from_isr;
bool kernel_running;
} __FuriCriticalInfo;
__FuriCriticalInfo __furi_critical_enter(void);
void __furi_critical_exit(__FuriCriticalInfo info);
#ifndef FURI_CRITICAL_ENTER
#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter();
#endif
#ifndef FURI_CRITICAL_EXIT
#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info);
#endif
#ifndef FURI_CHECK_RETURN
#define FURI_CHECK_RETURN __attribute__((__warn_unused_result__))
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +1,5 @@
#include "common_defines.h" #include "critical.h"
#include "furi_core_defines.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h> #include <freertos/task.h>

View File

@ -0,0 +1,22 @@
#pragma once
#include <stdio.h>
#include <stdbool.h>
#ifndef FURI_CRITICAL_ENTER
#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter();
#endif
#ifndef FURI_CRITICAL_EXIT
#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info);
#endif
typedef struct {
uint32_t isrm;
bool from_isr;
bool kernel_running;
} __FuriCriticalInfo;
__FuriCriticalInfo __furi_critical_enter(void);
void __furi_critical_exit(__FuriCriticalInfo info);

View File

@ -1,6 +1,6 @@
#include "event_flag.h" #include "event_flag.h"
#include "check.h" #include "check.h"
#include "common_defines.h" #include "furi_core_defines.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h> #include <freertos/event_groups.h>

View File

@ -4,7 +4,7 @@
*/ */
#pragma once #pragma once
#include "base.h" #include "furi_core_types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View File

@ -0,0 +1,30 @@
#include "furi.h"
#include "app_manifest_registry.h"
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
static bool scheduler_was_running = false;
void furi_init() {
furi_assert(!furi_kernel_is_irq());
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
vTaskSuspendAll();
scheduler_was_running = true;
}
furi_record_init();
xTaskResumeAll();
#if defined(__ARM_ARCH_7A__) && (__ARM_ARCH_7A__ == 0U)
/* Service Call interrupt might be configured before kernel start */
/* and when its priority is lower or equal to BASEPRI, svc instruction */
/* causes a Hard Fault. */
NVIC_SetPriority(SVCall_IRQn, 0U);
#endif
app_manifest_registry_init();
}

View File

@ -0,0 +1,28 @@
#pragma once
#include <stdlib.h>
#include "furi_core.h"
#include "furi_string.h"
#include "event_flag.h"
#include "kernel.h"
#include "message_queue.h"
#include "mutex.h"
#include "pubsub.h"
#include "record.h"
#include "semaphore.h"
#include "stream_buffer.h"
#include "string.h"
#include "thread.h"
#include "timer.h"
#ifdef __cplusplus
extern "C" {
#endif
void furi_init();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,12 @@
#pragma once
#include <stdbool.h>
#include <stdio.h>
#include "check.h"
#include "critical.h"
#include "event_flag.h"
#include "furi_core_defines.h"
#include "furi_core_types.h"
#include "furi_extra_defines.h"
#include "log.h"

View File

@ -0,0 +1,40 @@
#pragma once
#include "freertos/portmacro.h"
#include "furi_extra_defines.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <cmsis_compiler.h>
#ifndef FURI_WARN_UNUSED
#define FURI_WARN_UNUSED __attribute__((warn_unused_result))
#endif
#ifndef FURI_WEAK
#define FURI_WEAK __attribute__((weak))
#endif
#ifndef FURI_PACKED
#define FURI_PACKED __attribute__((packed))
#endif
// Used by portENABLE_INTERRUPTS and portDISABLE_INTERRUPTS?
#ifndef FURI_IS_IRQ_MODE
#define FURI_IS_IRQ_MODE() (xPortInIsrContext() == pdTRUE)
#endif
#ifndef FURI_IS_ISR
#define FURI_IS_ISR() (FURI_IS_IRQ_MODE())
#endif
#ifndef FURI_CHECK_RETURN
#define FURI_CHECK_RETURN __attribute__((__warn_unused_result__))
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,10 +1,8 @@
#include "furi_hal_console.h" #include "furi_hal_console.h"
#include "common_defines.h" #include "furi_core.h"
#include "furi_string.h" #include "furi_string.h"
#include <esp_log.h> #include "esp_log.h" // TODO remove
#include <memory.h>
#include <stdbool.h>
#define TAG "FuriHalConsole" #define TAG "FuriHalConsole"

View File

@ -1,7 +1,5 @@
#pragma once #pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -1,34 +1,14 @@
#include "kernel.h" #include "kernel.h"
#include "base.h"
#include "check.h" #include "check.h"
#include "common_defines.h" #include "furi_core_defines.h"
#include "furi_core_types.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h> #include <freertos/task.h>
#include <rom/ets_sys.h> #include <rom/ets_sys.h>
bool furi_kernel_is_irq_or_masked() { bool furi_kernel_is_irq() {
bool irq = false; return FURI_IS_IRQ_MODE();
BaseType_t state;
if (FURI_IS_IRQ_MODE()) {
/* Called from interrupt context */
irq = true;
} else {
/* Get FreeRTOS scheduler state */
state = xTaskGetSchedulerState();
if (state != taskSCHEDULER_NOT_STARTED) {
/* Scheduler was started */
if (FURI_IS_IRQ_MASKED()) {
/* Interrupts are masked */
irq = true;
}
}
}
/* Return context, 0: thread context, 1: IRQ context */
return (irq);
} }
bool furi_kernel_is_running() { bool furi_kernel_is_running() {
@ -36,7 +16,7 @@ bool furi_kernel_is_running() {
} }
int32_t furi_kernel_lock() { int32_t furi_kernel_lock() {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
int32_t lock; int32_t lock;
@ -61,7 +41,7 @@ int32_t furi_kernel_lock() {
} }
int32_t furi_kernel_unlock() { int32_t furi_kernel_unlock() {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
int32_t lock; int32_t lock;
@ -91,7 +71,7 @@ int32_t furi_kernel_unlock() {
} }
int32_t furi_kernel_restore_lock(int32_t lock) { int32_t furi_kernel_restore_lock(int32_t lock) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
switch (xTaskGetSchedulerState()) { switch (xTaskGetSchedulerState()) {
case taskSCHEDULER_SUSPENDED: case taskSCHEDULER_SUSPENDED:
@ -127,7 +107,7 @@ uint32_t furi_kernel_get_tick_frequency() {
} }
void furi_delay_tick(uint32_t ticks) { void furi_delay_tick(uint32_t ticks) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
if (ticks == 0U) { if (ticks == 0U) {
taskYIELD(); taskYIELD();
} else { } else {
@ -136,7 +116,7 @@ void furi_delay_tick(uint32_t ticks) {
} }
FuriStatus furi_delay_until_tick(uint32_t tick) { FuriStatus furi_delay_until_tick(uint32_t tick) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
TickType_t tcnt, delay; TickType_t tcnt, delay;
FuriStatus stat; FuriStatus stat;
@ -165,7 +145,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) {
uint32_t furi_get_tick() { uint32_t furi_get_tick() {
TickType_t ticks; TickType_t ticks;
if (furi_kernel_is_irq_or_masked() != 0U) { if (furi_kernel_is_irq() != 0U) {
ticks = xTaskGetTickCountFromISR(); ticks = xTaskGetTickCountFromISR();
} else { } else {
ticks = xTaskGetTickCount(); ticks = xTaskGetTickCount();
@ -183,7 +163,7 @@ uint32_t furi_ms_to_ticks(uint32_t milliseconds) {
} }
void furi_delay_ms(uint32_t milliseconds) { void furi_delay_ms(uint32_t milliseconds) {
if (!FURI_IS_ISR() && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
if (milliseconds > 0 && milliseconds < portMAX_DELAY - 1) { if (milliseconds > 0 && milliseconds < portMAX_DELAY - 1) {
milliseconds += 1; milliseconds += 1;
} }

View File

@ -4,7 +4,7 @@
*/ */
#pragma once #pragma once
#include "base.h" #include "furi_core_types.h"
#define configTICK_RATE_HZ_RAW 1000 #define configTICK_RATE_HZ_RAW 1000
@ -27,7 +27,7 @@ extern "C" {
* *
* @return true if CPU is in IRQ or kernel running and IRQ is masked * @return true if CPU is in IRQ or kernel running and IRQ is masked
*/ */
bool furi_kernel_is_irq_or_masked(); bool furi_kernel_is_irq();
/** Check if kernel is running /** Check if kernel is running
* *

22
components/furi/src/log.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "esp_log.h"
#ifdef __cplusplus
extern "C" {
#endif
#define FURI_LOG_E(tag, format, ...) \
ESP_LOGE(tag, format, ##__VA_ARGS__)
#define FURI_LOG_W(tag, format, ...) \
ESP_LOGW(tag, format, ##__VA_ARGS__)
#define FURI_LOG_I(tag, format, ...) \
ESP_LOGI(tag, format, ##__VA_ARGS__)
#define FURI_LOG_D(tag, format, ...) \
ESP_LOGD(tag, format, ##__VA_ARGS__)
#define FURI_LOG_T(tag, format, ...) \
ESP_LOGT(tag, format, ##__VA_ARGS__)
#ifdef __cplusplus
}
#endif

View File

@ -6,7 +6,7 @@
#include <freertos/queue.h> #include <freertos/queue.h>
FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) {
furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U)); furi_assert((furi_kernel_is_irq() == 0U) && (msg_count > 0U) && (msg_size > 0U));
QueueHandle_t handle = xQueueCreate(msg_count, msg_size); QueueHandle_t handle = xQueueCreate(msg_count, msg_size);
furi_check(handle); furi_check(handle);
@ -15,7 +15,7 @@ FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size
} }
void furi_message_queue_free(FuriMessageQueue* instance) { void furi_message_queue_free(FuriMessageQueue* instance) {
furi_assert(furi_kernel_is_irq_or_masked() == 0U); furi_assert(furi_kernel_is_irq() == 0U);
furi_assert(instance); furi_assert(instance);
vQueueDelete((QueueHandle_t)instance); vQueueDelete((QueueHandle_t)instance);
@ -29,7 +29,7 @@ furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t
stat = FuriStatusOk; stat = FuriStatusOk;
if (furi_kernel_is_irq_or_masked() != 0U) { if (furi_kernel_is_irq() != 0U) {
if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
stat = FuriStatusErrorParameter; stat = FuriStatusErrorParameter;
} else { } else {
@ -66,7 +66,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin
stat = FuriStatusOk; stat = FuriStatusOk;
if (furi_kernel_is_irq_or_masked() != 0U) { if (furi_kernel_is_irq() != 0U) {
if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
stat = FuriStatusErrorParameter; stat = FuriStatusErrorParameter;
} else { } else {
@ -132,7 +132,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {
if (hQueue == NULL) { if (hQueue == NULL) {
count = 0U; count = 0U;
} else if (furi_kernel_is_irq_or_masked() != 0U) { } else if (furi_kernel_is_irq() != 0U) {
count = uxQueueMessagesWaitingFromISR(hQueue); count = uxQueueMessagesWaitingFromISR(hQueue);
} else { } else {
count = uxQueueMessagesWaiting(hQueue); count = uxQueueMessagesWaiting(hQueue);
@ -149,7 +149,7 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
if (mq == NULL) { if (mq == NULL) {
space = 0U; space = 0U;
} else if (furi_kernel_is_irq_or_masked() != 0U) { } else if (furi_kernel_is_irq() != 0U) {
isrm = taskENTER_CRITICAL_FROM_ISR(); isrm = taskENTER_CRITICAL_FROM_ISR();
/* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */
@ -168,7 +168,7 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) {
QueueHandle_t hQueue = (QueueHandle_t)instance; QueueHandle_t hQueue = (QueueHandle_t)instance;
FuriStatus stat; FuriStatus stat;
if (furi_kernel_is_irq_or_masked() != 0U) { if (furi_kernel_is_irq() != 0U) {
stat = FuriStatusErrorISR; stat = FuriStatusErrorISR;
} else if (hQueue == NULL) { } else if (hQueue == NULL) {
stat = FuriStatusErrorParameter; stat = FuriStatusErrorParameter;

View File

@ -4,7 +4,7 @@
*/ */
#pragma once #pragma once
#include "base.h" #include "furi_core_types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View File

@ -1,6 +1,6 @@
#include "mutex.h" #include "mutex.h"
#include "check.h" #include "check.h"
#include "common_defines.h" #include "furi_core_defines.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/semphr.h> #include <freertos/semphr.h>

View File

@ -4,7 +4,7 @@
*/ */
#pragma once #pragma once
#include "base.h" #include "furi_core_types.h"
#include "thread.h" #include "thread.h"
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -2,9 +2,8 @@
#include "check.h" #include "check.h"
#include "event_flag.h" #include "event_flag.h"
#include "mutex.h" #include "mutex.h"
#include "m-dict.h"
#include "m_cstr_dup.h" #include "m_cstr_dup.h"
#include <m-dict.h>
#define FURI_RECORD_FLAG_READY (0x1) #define FURI_RECORD_FLAG_READY (0x1)

View File

@ -5,7 +5,7 @@
#pragma once #pragma once
#include "core_defines.h" #include "furi_extra_defines.h"
#include <stdbool.h> #include <stdbool.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -18,11 +18,11 @@ extern "C" {
* @param variable_name the name of the variable that is used in the `code` * @param variable_name the name of the variable that is used in the `code`
* @param code the code to execute: consider putting it between {} * @param code the code to execute: consider putting it between {}
*/ */
#define FURI_RECORD_TRANSACTION(record_name, variable_name, code) \ #define FURI_RECORD_TRANSACTION(record_name, variable_type, variable_name, code) \
{ \ { \
Gui*(variable_name) = (Gui*)furi_record_open(record_name); \ variable_type (variable_name) = (variable_type)furi_record_open(record_name); \
code; \ code; \
furi_record_close(record_name); \ furi_record_close(record_name); \
} }
/** Initialize record storage For internal use only. /** Initialize record storage For internal use only.

View File

@ -1,6 +1,6 @@
#include "semaphore.h" #include "semaphore.h"
#include "check.h" #include "check.h"
#include "common_defines.h" #include "furi_core_defines.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/semphr.h> #include <freertos/semphr.h>
@ -99,18 +99,41 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
return (stat); return (stat);
} }
//uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { uint32_t furi_semaphore_get_count(FuriSemaphore* instance) {
// furi_assert(instance); furi_assert(instance);
//
// SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
// uint32_t count; uint32_t count;
//
// if(FURI_IS_IRQ_MODE()) { if(FURI_IS_IRQ_MODE()) {
furi_crash("not implemented");
// count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore); // count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore);
// } else { } else {
// count = (uint32_t)uxSemaphoreGetCount(hSemaphore); count = (uint32_t)uxSemaphoreGetCount(hSemaphore);
// } }
//
// /* Return number of tokens */ /* Return number of tokens */
// return (count); return (count);
//} }
bool furi_semaphore_take(FuriSemaphore* instance, TickType_t timeout) {
furi_assert(instance);
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
if(FURI_IS_IRQ_MODE()) {
furi_crash("not implemented");
} else {
return xSemaphoreTake(hSemaphore, timeout) == pdTRUE;
}
}
bool furi_semaphore_give(FuriSemaphore* instance) {
furi_assert(instance);
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
if(FURI_IS_IRQ_MODE()) {
furi_crash("not implemented");
} else {
return xSemaphoreGive(hSemaphore);
}
}

View File

@ -1,10 +1,6 @@
/**
* @file semaphore.h
* FuriSemaphore
*/
#pragma once #pragma once
#include "base.h" #include "furi_core_types.h"
#include "thread.h" #include "thread.h"
#ifdef __cplusplus #ifdef __cplusplus
@ -45,13 +41,30 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout);
*/ */
FuriStatus furi_semaphore_release(FuriSemaphore* instance); FuriStatus furi_semaphore_release(FuriSemaphore* instance);
///** Get semaphore count /** Get semaphore count
// * *
// * @param instance The pointer to FuriSemaphore instance * @param instance The pointer to FuriSemaphore instance
// * *
// * @return Semaphore count * @return Semaphore count
// */ */
//uint32_t furi_semaphore_get_count(FuriSemaphore* instance); uint32_t furi_semaphore_get_count(FuriSemaphore* instance);
/** Wait for the semaphore to become available
*
* @param instance The pointer to FuriSemaphore instance
* @param timeout The maximum amount of ticks to wait for the semaphore to become available
*
* @return True if the semaphore became available. False on timeout.
*/
bool furi_semaphore_take(FuriSemaphore* instance, TickType_t timeout);
/** Wait for the semaphore to become available
*
* @param instance The pointer to FuriSemaphore instance
*
* @return True if the semaphore became available. False on timeout.
*/
bool furi_semaphore_give(FuriSemaphore* instance);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,7 +1,7 @@
#include "stream_buffer.h" #include "stream_buffer.h"
#include "base.h"
#include "check.h" #include "check.h"
#include "common_defines.h" #include "furi_core_defines.h"
#include "furi_core_types.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/stream_buffer.h> #include <freertos/stream_buffer.h>

View File

@ -12,10 +12,10 @@
* interrupt that will read from the buffer (the reader). * interrupt that will read from the buffer (the reader).
*/ */
#pragma once #pragma once
#include "furi_core_types.h"
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "base.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View File

@ -1,6 +1,6 @@
#include "thread.h" #include "thread.h"
#include "check.h" #include "check.h"
#include "common_defines.h" #include "furi_core_defines.h"
#include "furi_string.h" #include "furi_string.h"
#include "kernel.h" #include "kernel.h"

View File

@ -5,8 +5,8 @@
#pragma once #pragma once
#include "base.h" #include "furi_core_defines.h"
#include "common_defines.h" #include "furi_core_types.h"
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>

View File

@ -25,7 +25,7 @@ static void TimerCallback(TimerHandle_t hTimer) {
} }
FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) {
furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL)); furi_assert((furi_kernel_is_irq() == 0U) && (func != NULL));
TimerHandle_t hTimer; TimerHandle_t hTimer;
TimerCallback_t* callb; TimerCallback_t* callb;
@ -58,7 +58,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
} }
void furi_timer_free(FuriTimer* instance) { void furi_timer_free(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
furi_assert(instance); furi_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance; TimerHandle_t hTimer = (TimerHandle_t)instance;
@ -80,7 +80,7 @@ void furi_timer_free(FuriTimer* instance) {
} }
FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
furi_assert(instance); furi_assert(instance);
furi_assert(ticks < portMAX_DELAY); furi_assert(ticks < portMAX_DELAY);
@ -98,7 +98,7 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
} }
FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) { FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
furi_assert(instance); furi_assert(instance);
furi_assert(ticks < portMAX_DELAY); furi_assert(ticks < portMAX_DELAY);
@ -117,7 +117,7 @@ FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) {
} }
FuriStatus furi_timer_stop(FuriTimer* instance) { FuriStatus furi_timer_stop(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
furi_assert(instance); furi_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance; TimerHandle_t hTimer = (TimerHandle_t)instance;
@ -128,7 +128,7 @@ FuriStatus furi_timer_stop(FuriTimer* instance) {
} }
uint32_t furi_timer_is_running(FuriTimer* instance) { uint32_t furi_timer_is_running(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
furi_assert(instance); furi_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance; TimerHandle_t hTimer = (TimerHandle_t)instance;
@ -138,7 +138,7 @@ uint32_t furi_timer_is_running(FuriTimer* instance) {
} }
uint32_t furi_timer_get_expire_time(FuriTimer* instance) { uint32_t furi_timer_get_expire_time(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
furi_assert(instance); furi_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance; TimerHandle_t hTimer = (TimerHandle_t)instance;
@ -148,7 +148,7 @@ uint32_t furi_timer_get_expire_time(FuriTimer* instance) {
void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg) { void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg) {
BaseType_t ret = pdFAIL; BaseType_t ret = pdFAIL;
if (furi_kernel_is_irq_or_masked()) { if (furi_kernel_is_irq()) {
ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL); ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL);
} else { } else {
ret = xTimerPendFunctionCall(callback, context, arg, FuriWaitForever); ret = xTimerPendFunctionCall(callback, context, arg, FuriWaitForever);
@ -157,7 +157,7 @@ void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context
} }
void furi_timer_set_thread_priority(FuriTimerThreadPriority priority) { void furi_timer_set_thread_priority(FuriTimerThreadPriority priority) {
furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(!furi_kernel_is_irq());
TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle();
furi_check(task_handle); // Don't call this method before timer task start furi_check(task_handle); // Don't call this method before timer task start

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "base.h" #include "furi_core_types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View File

@ -363,8 +363,8 @@ namespace m_lib {
function_name(name_t v \ function_name(name_t v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \ { \
M_CALL_CLEAR(oplist, v->name); \ M_CALL_CLEAR(oplist, v->id); \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, v->name, exp_emplace_type); \ M_EMPLACE_CALL_FUNC(a, init_func, oplist, v->id, exp_emplace_type); \
} }

View File

@ -1,10 +1,9 @@
idf_component_register( idf_component_register(
SRC_DIRS "src" SRC_DIRS "src"
"src/applications" "src/apps/system/system_info"
"src/applications/system/system_info" "src/apps/services/desktop"
"src/applications/services/desktop" "src/apps/services/loader"
"src/applications/services/loader" "src/apps/services/gui"
"src/applications/services/gui"
INCLUDE_DIRS "src" INCLUDE_DIRS "src"
REQUIRES esp_lvgl_port esp_lcd esp_lcd_touch driver mlib cmsis_core furi REQUIRES esp_lvgl_port esp_lcd esp_lcd_touch driver mlib cmsis_core furi
) )

View File

@ -1,19 +0,0 @@
#include "app_i.h"
#include "check.h"
const char* prv_type_service = "service";
const char* prv_type_system = "system";
const char* prv_type_user = "user";
const char* nb_app_type_to_string(AppType type) {
switch (type) {
case SERVICE:
return prv_type_service;
case SYSTEM:
return prv_type_system;
case USER:
return prv_type_user;
default:
furi_crash();
}
}

View File

@ -1,41 +0,0 @@
#pragma once
#include "esp_err.h"
#include "lvgl.h"
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#define NB_APP_ID_LENGTH 32
#define NB_APP_NAME_LENGTH 32
typedef enum {
SERVICE,
SYSTEM,
USER
} AppType;
typedef enum {
NB_TASK_PRIORITY_DEFAULT = 10
} AppPriority;
typedef enum {
NB_TASK_STACK_SIZE_DEFAULT = 2048
} AppStackSize;
typedef int32_t (*AppEntryPoint)(void _Nonnull* parameter);
typedef struct {
const char id[NB_APP_ID_LENGTH];
const char name[NB_APP_NAME_LENGTH];
const AppType type;
const AppEntryPoint _Nullable entry_point;
const AppStackSize stack_size;
const AppPriority priority;
} App;
#ifdef __cplusplus
}
#endif

View File

@ -1,13 +0,0 @@
#pragma once
#include "app.h"
#ifdef __cplusplus
extern "C" {
#endif
const char* nb_app_type_to_string(AppType type);
#ifdef __cplusplus
}
#endif

View File

@ -1,28 +0,0 @@
#include "applications_i.h"
// System services
extern const App desktop_app;
extern const App gui_app;
extern const App loader_app;
// System apps
extern const App system_info_app;
const App* const NANOBAKE_SERVICES[] = {
&desktop_app,
&gui_app,
&loader_app
};
const size_t NANOBAKE_SERVICES_COUNT = sizeof(NANOBAKE_SERVICES) / sizeof(App*);
const App* const NANOBAKE_SYSTEM_APPS[] = {
&system_info_app
};
const size_t NANOBAKE_SYSTEM_APPS_COUNT = sizeof(NANOBAKE_SYSTEM_APPS) / sizeof(App*);
const OnSystemStart NANOBAKE_ON_SYSTEM_START[] = {
};
const size_t NANOBAKE_ON_SYSTEM_START_COUNT = sizeof(NANOBAKE_ON_SYSTEM_START) / sizeof(OnSystemStart);

View File

@ -1,23 +0,0 @@
#pragma once
#include "app.h"
#include "devices.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*OnSystemStart)(Devices* hardware);
extern const App* const NANOBAKE_SERVICES[];
extern const size_t NANOBAKE_SERVICES_COUNT;
extern const App* const NANOBAKE_SYSTEM_APPS[];
extern const size_t NANOBAKE_SYSTEM_APPS_COUNT;
extern const OnSystemStart NANOBAKE_ON_SYSTEM_START[];
extern const size_t NANOBAKE_ON_SYSTEM_START_COUNT;
#ifdef __cplusplus
}
#endif

View File

@ -1,17 +0,0 @@
#include "loader.h"
#include "core_defines.h"
static int32_t prv_loader_main(void* param) {
UNUSED(param);
printf("loader app init\n");
return 0;
}
const App loader_app = {
.id = "loader",
.name = "Loader",
.type = SERVICE,
.entry_point = &prv_loader_main,
.stack_size = NB_TASK_STACK_SIZE_DEFAULT,
.priority = NB_TASK_PRIORITY_DEFAULT
};

View File

@ -1,13 +0,0 @@
#pragma once
#include "app.h"
#ifdef __cplusplus
extern "C" {
#endif
extern const App loader_app;
#ifdef __cplusplus
}
#endif

View File

@ -1,39 +0,0 @@
#include "system_info.h"
#include "core_defines.h"
#include "nanobake.h"
#include "thread.h"
static int32_t system_info_entry_point(void* param) {
UNUSED(param);
// Wait for all apps to start
vTaskDelay(1000 / portTICK_PERIOD_MS);
size_t system_service_count = nanobake_get_app_thread_count();
printf("Running apps:\n");
for (int i = 0; i < system_service_count; ++i) {
FuriThreadId thread_id = nanobake_get_app_thread_id(i);
const char* appid = furi_thread_get_appid(thread_id);
const char* name = furi_thread_get_name(thread_id);
bool is_suspended = furi_thread_is_suspended(thread_id);
const char* status = is_suspended ? "suspended" : "active";
printf(" - [%s] %s (%s)\n", status, name, appid);
}
printf(
"Heap memory available: %d / %d\n",
heap_caps_get_free_size(MALLOC_CAP_DEFAULT),
heap_caps_get_total_size(MALLOC_CAP_DEFAULT)
);
return 0;
}
App system_info_app = {
.id = "systeminfo",
.name = "System Info",
.type = SYSTEM,
.entry_point = &system_info_entry_point,
.stack_size = NB_TASK_STACK_SIZE_DEFAULT,
.priority = NB_TASK_PRIORITY_DEFAULT
};

View File

@ -1,6 +1,5 @@
#include "desktop.h" #include "desktop.h"
#include "core_defines.h" #include "furi_extra_defines.h"
#include "devices.h"
static int32_t prv_desktop_main(void* param) { static int32_t prv_desktop_main(void* param) {
UNUSED(param); UNUSED(param);
@ -8,11 +7,11 @@ static int32_t prv_desktop_main(void* param) {
return 0; return 0;
} }
const App desktop_app = { const AppManifest desktop_app = {
.id = "desktop", .id = "desktop",
.name = "Desktop", .name = "Desktop",
.type = SERVICE, .icon = NULL,
.type = AppTypeService,
.entry_point = &prv_desktop_main, .entry_point = &prv_desktop_main,
.stack_size = NB_TASK_STACK_SIZE_DEFAULT, .stack_size = AppStackSizeNormal
.priority = NB_TASK_PRIORITY_DEFAULT
}; };

View File

@ -1,12 +1,12 @@
#pragma once #pragma once
#include "app.h" #include "app_manifest.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
extern const App desktop_app; extern const AppManifest desktop_app;
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,5 +1,6 @@
#include "check.h" #include "check.h"
#include "core_defines.h" #include "esp_lvgl_port.h"
#include "furi_extra_defines.h"
#include "gui_i.h" #include "gui_i.h"
#include "record.h" #include "record.h"
@ -184,7 +185,11 @@ Gui* gui_alloc() {
Gui* gui = malloc(sizeof(Gui)); Gui* gui = malloc(sizeof(Gui));
gui->thread_id = furi_thread_get_current_id(); gui->thread_id = furi_thread_get_current_id();
gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal); gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
furi_check(lvgl_port_lock(100));
gui->lvgl_parent = lv_scr_act(); gui->lvgl_parent = lv_scr_act();
lvgl_port_unlock();
gui->lockdown = false; gui->lockdown = false;
furi_check(gui->mutex); furi_check(gui->mutex);
for (size_t i = 0; i < GuiLayerMAX; i++) { for (size_t i = 0; i < GuiLayerMAX; i++) {
@ -231,11 +236,11 @@ __attribute((__noreturn__)) int32_t prv_gui_main(void* parameter) {
} }
} }
const App gui_app = { const AppManifest gui_app = {
.id = "gui", .id = "gui",
.name = "GUI", .name = "GUI",
.type = SERVICE, .icon = NULL,
.type = AppTypeService,
.entry_point = &prv_gui_main, .entry_point = &prv_gui_main,
.stack_size = NB_TASK_STACK_SIZE_DEFAULT, .stack_size = AppStackSizeNormal
.priority = NB_TASK_PRIORITY_DEFAULT
}; };

View File

@ -1,22 +1,13 @@
#pragma once #pragma once
#include "app.h" #include "app_manifest.h"
#include "lvgl.h"
#include "view_port.h" #include "view_port.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
extern const App gui_app; extern const AppManifest gui_app;
/** Canvas Orientation */
typedef enum {
CanvasOrientationHorizontal,
CanvasOrientationHorizontalFlip,
CanvasOrientationVertical,
CanvasOrientationVerticalFlip,
} CanvasOrientation;
/** Gui layers */ /** Gui layers */
typedef enum { typedef enum {
@ -36,7 +27,6 @@ typedef enum {
typedef void (*GuiCanvasCommitCallback)( typedef void (*GuiCanvasCommitCallback)(
uint8_t* data, uint8_t* data,
size_t size, size_t size,
CanvasOrientation orientation,
void* context void* context
); );

View File

@ -0,0 +1,13 @@
#include "icon_i.h"
uint8_t icon_get_width(const Icon* instance) {
return instance->width;
}
uint8_t icon_get_height(const Icon* instance) {
return instance->height;
}
const uint8_t* icon_get_data(const Icon* instance) {
return instance->frames[0];
}

View File

@ -0,0 +1,11 @@
#pragma once
#include <stdio.h>
struct Icon {
const uint8_t width;
const uint8_t height;
const uint8_t frame_count;
const uint8_t frame_rate;
const uint8_t* const* frames;
};

View File

@ -0,0 +1,10 @@
#pragma once
#include "icon.h"
typedef struct {
const uint8_t width;
const uint8_t height;
const uint8_t frame_count;
const uint8_t frame_rate;
const uint8_t* const* frames;
} Icon;

View File

@ -2,6 +2,7 @@
#include "gui.h" #include "gui.h"
#include "gui_i.h" #include "gui_i.h"
#include "view_port_i.h" #include "view_port_i.h"
#include "esp_lvgl_port.h"
#define TAG "viewport" #define TAG "viewport"
@ -88,7 +89,10 @@ void view_port_draw(ViewPort* view_port, lv_obj_t* parent) {
furi_check(view_port->gui); furi_check(view_port->gui);
if (view_port->draw_callback) { if (view_port->draw_callback) {
furi_check(lvgl_port_lock(100));
lv_obj_clean(parent); lv_obj_clean(parent);
lvgl_port_unlock();
view_port->draw_callback(parent, view_port->draw_callback_context); view_port->draw_callback(parent, view_port->draw_callback_context);
} }

View File

@ -0,0 +1,290 @@
#include "loader.h"
#include "app_i.h"
#include "app_manifest.h"
#include "app_manifest_registry.h"
#include "loader_i.h"
#include <sys/cdefs.h>
#include "esp_heap_caps.h"
#define TAG "Loader"
#define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF
LoaderStatus loader_start(Loader* loader, const char* id, const char* args, FuriString* error_message) {
LoaderMessage message;
LoaderMessageLoaderStatusResult result;
message.type = LoaderMessageTypeStartByName;
message.start.id = id;
message.start.args = args;
message.start.error_message = error_message;
message.api_lock = api_lock_alloc_locked();
message.status_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return result.value;
}
bool loader_lock(Loader* loader) {
LoaderMessage message;
LoaderMessageBoolResult result;
message.type = LoaderMessageTypeLock;
message.api_lock = api_lock_alloc_locked();
message.bool_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return result.value;
}
void loader_unlock(Loader* loader) {
LoaderMessage message;
message.type = LoaderMessageTypeUnlock;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
bool loader_is_locked(Loader* loader) {
LoaderMessage message;
LoaderMessageBoolResult result;
message.type = LoaderMessageTypeIsLocked;
message.api_lock = api_lock_alloc_locked();
message.bool_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return result.value;
}
FuriPubSub* loader_get_pubsub(Loader* loader) {
furi_assert(loader);
// it's safe to return pubsub without locking
// because it's never freed and loader is never exited
// also the loader instance cannot be obtained until the pubsub is created
return loader->pubsub;
}
// callbacks
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
furi_assert(context);
Loader* loader = context;
if (thread_state == FuriThreadStateRunning) {
LoaderEvent event;
event.type = LoaderEventTypeApplicationStarted;
furi_pubsub_publish(loader->pubsub, &event);
} else if (thread_state == FuriThreadStateStopped) {
LoaderMessage message;
message.type = LoaderMessageTypeAppClosed;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
}
// implementation
static Loader* loader_alloc() {
Loader* loader = malloc(sizeof(Loader));
loader->pubsub = furi_pubsub_alloc();
loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
loader->app_data.args = NULL;
loader->app_data.thread = NULL;
loader->app_data.app = NULL;
return loader;
}
static void loader_start_app_thread(Loader* loader) {
// setup thread state callbacks
furi_thread_set_state_context(loader->app_data.thread, loader);
furi_thread_set_state_callback(loader->app_data.thread, loader_thread_state_callback);
// start app thread
furi_thread_start(loader->app_data.thread);
}
static void loader_log_status_error(
LoaderStatus status,
FuriString* error_message,
const char* format,
va_list args
) {
if (error_message) {
furi_string_vprintf(error_message, format, args);
FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(error_message));
} else {
FURI_LOG_E(TAG, "Status [%d]", status);
}
}
static LoaderStatus loader_make_status_error(
LoaderStatus status,
FuriString* error_message,
const char* format,
...
) {
va_list args;
va_start(args, format);
loader_log_status_error(status, error_message, format, args);
va_end(args);
return status;
}
static LoaderStatus loader_make_success_status(FuriString* error_message) {
if (error_message) {
furi_string_set(error_message, "App started");
}
return LoaderStatusOk;
}
static void loader_start_app(
Loader* loader,
const AppManifest* _Nonnull manifest,
const char* args
) {
FURI_LOG_I(TAG, "Starting %s", manifest->id);
App* _Nonnull app = furi_app_alloc(manifest);
loader->app_data.app = app;
FuriThread* thread = furi_app_alloc_thread(loader->app_data.app, args);
loader->app_data.app->thread = thread;
loader->app_data.thread = thread;
loader_start_app_thread(loader);
}
// process messages
static bool loader_do_is_locked(Loader* loader) {
return loader->app_data.thread != NULL;
}
static LoaderStatus loader_do_start_by_id(
Loader* loader,
const char* id,
const char* args,
FuriString* error_message
) {
// check lock
if (loader_do_is_locked(loader)) {
const char* current_thread_name =
furi_thread_get_name(furi_thread_get_id(loader->app_data.thread));
return loader_make_status_error(
LoaderStatusErrorAppStarted,
error_message,
"Loader is locked, please close the \"%s\" first",
current_thread_name
);
}
const AppManifest* manifest = app_manifest_registry_find_by_id(id);
if (manifest == NULL) {
return loader_make_status_error(
LoaderStatusErrorUnknownApp,
error_message,
"Application \"%s\" not found",
id
);
}
loader_start_app(loader, manifest, args);
return loader_make_success_status(error_message);
}
static bool loader_do_lock(Loader* loader) {
if (loader->app_data.thread) {
return false;
}
loader->app_data.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE;
return true;
}
static void loader_do_unlock(Loader* loader) {
furi_check(loader->app_data.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE);
loader->app_data.thread = NULL;
}
static void loader_do_app_closed(Loader* loader) {
furi_assert(loader->app_data.thread);
furi_thread_join(loader->app_data.thread);
FURI_LOG_I(
TAG,
"App %s returned: %li",
loader->app_data.app->manifest->id,
furi_thread_get_return_code(loader->app_data.thread)
);
if (loader->app_data.args) {
free(loader->app_data.args);
loader->app_data.args = NULL;
}
if (loader->app_data.app) {
furi_app_free(loader->app_data.app);
loader->app_data.app = NULL;
} else {
assert(loader->app_data.thread == NULL);
furi_thread_free(loader->app_data.thread);
}
loader->app_data.thread = NULL;
FURI_LOG_I(
TAG,
"Application stopped. Free heap: %zu",
heap_caps_get_free_size(MALLOC_CAP_DEFAULT)
);
LoaderEvent event;
event.type = LoaderEventTypeApplicationStopped;
furi_pubsub_publish(loader->pubsub, &event);
}
// app
_Noreturn int32_t loader_main(void* p) {
UNUSED(p);
Loader* loader = loader_alloc();
furi_record_create(RECORD_LOADER, loader);
LoaderMessage message;
while (true) {
if (furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) {
switch (message.type) {
case LoaderMessageTypeStartByName:
message.status_value->value = loader_do_start_by_id(
loader,
message.start.id,
message.start.args,
message.start.error_message
);
api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeIsLocked:
message.bool_value->value = loader_do_is_locked(loader);
api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeAppClosed:
loader_do_app_closed(loader);
break;
case LoaderMessageTypeLock:
message.bool_value->value = loader_do_lock(loader);
api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeUnlock:
loader_do_unlock(loader);
break;
}
}
}
}
const AppManifest loader_app = {
.id = "loader",
.name = "Loader",
.icon = NULL,
.type = AppTypeService,
.entry_point = &loader_main,
.stack_size = AppStackSizeNormal
};

View File

@ -0,0 +1,84 @@
#pragma once
#include "furi_core.h"
#include "furi_string.h"
#include "pubsub.h"
#ifdef __cplusplus
extern "C" {
#endif
#define RECORD_LOADER "loader"
typedef struct Loader Loader;
typedef enum {
LoaderStatusOk,
LoaderStatusErrorAppStarted,
LoaderStatusErrorUnknownApp,
LoaderStatusErrorInternal,
} LoaderStatus;
typedef enum {
LoaderEventTypeApplicationStarted,
LoaderEventTypeApplicationStopped
} LoaderEventType;
typedef struct {
LoaderEventType type;
} LoaderEvent;
/**
* @brief Start application
* @param[in] instance loader instance
* @param[in] id application name or id
* @param[in] args application arguments
* @param[out] error_message detailed error message, can be NULL
* @return LoaderStatus
*/
LoaderStatus loader_start(Loader* instance, const char* id, const char* args, FuriString* error_message);
/**
* @brief Start application with GUI error message
* @param[in] instance loader instance
* @param[in] name application name or id
* @param[in] args application arguments
* @return LoaderStatus
*/
//LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args);
/**
* @brief Lock application start
* @param[in] instance loader instance
* @return true on success
*/
bool loader_lock(Loader* instance);
/**
* @brief Unlock application start
* @param[in] instance loader instance
*/
void loader_unlock(Loader* instance);
/**
* @brief Check if loader is locked
* @param[in] instance loader instance
* @return true if locked
*/
bool loader_is_locked(Loader* instance);
/**
* @brief Show loader menu
* @param[in] instance loader instance
*/
void loader_show_menu(Loader* instance);
/**
* @brief Get loader pubsub
* @param[in] instance loader instance
* @return FuriPubSub*
*/
FuriPubSub* loader_get_pubsub(Loader* instance);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,55 @@
#pragma once
#include "api_lock.h"
#include "app_manifest.h"
#include "loader.h"
#include "message_queue.h"
#include "pubsub.h"
#include "thread.h"
typedef struct {
char* args;
FuriThread* thread;
App* app;
} LoaderAppData;
struct Loader {
FuriPubSub* pubsub;
FuriMessageQueue* queue;
LoaderAppData app_data;
};
typedef enum {
LoaderMessageTypeStartByName,
LoaderMessageTypeAppClosed,
LoaderMessageTypeLock,
LoaderMessageTypeUnlock,
LoaderMessageTypeIsLocked,
} LoaderMessageType;
typedef struct {
const char* id;
const char* args;
FuriString* error_message;
} LoaderMessageStartById;
typedef struct {
LoaderStatus value;
} LoaderMessageLoaderStatusResult;
typedef struct {
bool value;
} LoaderMessageBoolResult;
typedef struct {
FuriApiLock api_lock;
LoaderMessageType type;
union {
LoaderMessageStartById start;
};
union {
LoaderMessageLoaderStatusResult* status_value;
LoaderMessageBoolResult* bool_value;
};
} LoaderMessage;

View File

@ -0,0 +1,24 @@
#include "system_info.h"
#include "furi_extra_defines.h"
#include "thread.h"
static int32_t system_info_entry_point(void* param) {
UNUSED(param);
printf(
"Heap memory available: %d / %d\n",
heap_caps_get_free_size(MALLOC_CAP_DEFAULT),
heap_caps_get_total_size(MALLOC_CAP_DEFAULT)
);
return 0;
}
AppManifest system_info_app = {
.id = "systeminfo",
.name = "System Info",
.icon = NULL,
.type = AppTypeSystem,
.entry_point = &system_info_entry_point,
.stack_size = AppStackSizeNormal
};

View File

@ -1,12 +1,12 @@
#pragma once #pragma once
#include "app.h" #include "app_manifest.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
extern App system_info_app; extern AppManifest system_info_app;
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -9,7 +9,7 @@ Devices nb_devices_create(Config _Nonnull* config) {
furi_check(config->display_driver != NULL, "no display driver configured"); furi_check(config->display_driver != NULL, "no display driver configured");
DisplayDriver display_driver = config->display_driver(); DisplayDriver display_driver = config->display_driver();
ESP_LOGI(TAG, "display with driver %s", display_driver.name); ESP_LOGI(TAG, "display with driver %s", display_driver.name);
DisplayDevice* display = nb_display_alloc(&display_driver); DisplayDevice* display = nb_display_device_alloc(&display_driver);
TouchDevice* touch = NULL; TouchDevice* touch = NULL;
if (config->touch_driver != NULL) { if (config->touch_driver != NULL) {

View File

@ -1,10 +1,10 @@
#include "check.h" #include "check.h"
#include "display.h" #include "display.h"
DisplayDevice _Nonnull* nb_display_alloc(DisplayDriver _Nonnull* driver) { DisplayDevice _Nonnull* nb_display_device_alloc(DisplayDriver _Nonnull* driver) {
DisplayDevice _Nonnull* display = malloc(sizeof(DisplayDevice)); DisplayDevice _Nonnull* display = malloc(sizeof(DisplayDevice));
memset(display, 0, sizeof(DisplayDevice)); memset(display, 0, sizeof(DisplayDevice));
furi_check(driver->create_display(display), "failed to create display"); furi_check(driver->create_display_device(display), "failed to create display");
furi_check(display->io_handle != NULL); furi_check(display->io_handle != NULL);
furi_check(display->display_handle != NULL); furi_check(display->display_handle != NULL);
furi_check(display->horizontal_resolution != 0); furi_check(display->horizontal_resolution != 0);

View File

@ -16,20 +16,21 @@ typedef struct {
bool mirror_x; bool mirror_x;
bool mirror_y; bool mirror_y;
bool swap_xy; bool swap_xy;
bool monochrome;
} DisplayDevice; } DisplayDevice;
typedef bool (*CreateDisplay)(DisplayDevice* display); typedef bool (*CreateDisplay)(DisplayDevice* display);
typedef struct { typedef struct {
char name[32]; char name[32];
CreateDisplay create_display; CreateDisplay create_display_device;
} DisplayDriver; } DisplayDriver;
/** /**
* @param[in] driver * @param[in] driver
* @return allocated display object * @return allocated display object
*/ */
DisplayDevice _Nonnull* nb_display_alloc(DisplayDriver _Nonnull* driver); DisplayDevice _Nonnull* nb_display_device_alloc(DisplayDriver _Nonnull* driver);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -25,7 +25,7 @@ Lvgl nb_graphics_init(Devices _Nonnull* hardware) {
.double_buffer = 0, .double_buffer = 0,
.hres = display->horizontal_resolution, .hres = display->horizontal_resolution,
.vres = display->vertical_resolution, .vres = display->vertical_resolution,
.monochrome = false, .monochrome = display->monochrome,
.rotation = { .rotation = {
.swap_xy = display->swap_xy, .swap_xy = display->swap_xy,
.mirror_x = display->mirror_x, .mirror_x = display->mirror_x,
@ -37,6 +37,8 @@ Lvgl nb_graphics_init(Devices _Nonnull* hardware) {
}; };
lv_disp_t _Nonnull* disp = lvgl_port_add_disp(&disp_cfg); lv_disp_t _Nonnull* disp = lvgl_port_add_disp(&disp_cfg);
furi_check(disp != NULL, "failed to add display");
lv_indev_t _Nullable* touch_indev = NULL; lv_indev_t _Nullable* touch_indev = NULL;
// Add touch // Add touch

View File

@ -1,86 +1,61 @@
#include "nanobake.h" #include "nanobake.h"
#include "app_i.h" #include "app_i.h"
#include "applications/applications_i.h" #include "app_manifest_registry.h"
#include "devices_i.h" #include "devices_i.h"
#include "esp_log.h" #include "furi.h"
#include "graphics_i.h" #include "graphics_i.h"
#include "m-list.h"
// Furi
#include "kernel.h"
#include "record.h"
#include "thread.h"
M_LIST_DEF(thread_ids, FuriThreadId);
#define TAG "nanobake" #define TAG "nanobake"
thread_ids_t prv_thread_ids; // System services
extern const AppManifest desktop_app;
extern const AppManifest gui_app;
extern const AppManifest loader_app;
static void prv_furi_init() { // System apps
// TODO: can we remove the suspend-resume logic? extern const AppManifest system_info_app;
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
vTaskSuspendAll();
}
furi_record_init();
xTaskResumeAll();
}
FuriThreadId nanobake_get_app_thread_id(size_t index) {
return *thread_ids_get(prv_thread_ids, index);
}
size_t nanobake_get_app_thread_count() {
return thread_ids_size(prv_thread_ids);
}
static void prv_start_app(const App _Nonnull* app) {
ESP_LOGI(TAG, "Starting %s app \"%s\"", nb_app_type_to_string(app->type), app->name);
void start_service(const AppManifest* _Nonnull manifest) {
// TODO: keep track of running services
FURI_LOG_I(TAG, "Starting service %s", manifest->name);
FuriThread* thread = furi_thread_alloc_ex( FuriThread* thread = furi_thread_alloc_ex(
app->name, manifest->name,
app->stack_size, manifest->stack_size,
app->entry_point, manifest->entry_point,
NULL NULL
); );
furi_thread_mark_as_service(thread);
if (app->type == SERVICE) { furi_thread_set_appid(thread, manifest->id);
furi_thread_mark_as_service(thread);
}
furi_thread_set_appid(thread, app->id);
furi_thread_set_priority(thread, app->priority);
furi_thread_start(thread); furi_thread_start(thread);
FuriThreadId thread_id = furi_thread_get_id(thread);
thread_ids_push_back(prv_thread_ids, thread_id);
} }
__attribute__((unused)) extern void nanobake_start(Config _Nonnull* config) { static void register_apps(Config* _Nonnull config) {
prv_furi_init(); FURI_LOG_I(TAG, "Registering core apps");
app_manifest_registry_add(&desktop_app);
app_manifest_registry_add(&gui_app);
app_manifest_registry_add(&loader_app);
app_manifest_registry_add(&system_info_app);
FURI_LOG_I(TAG, "Registering user apps");
for (size_t i = 0; i < config->apps_count; i++) {
app_manifest_registry_add(config->apps[i]);
}
}
static void start_services() {
FURI_LOG_I(TAG, "Starting services");
app_manifest_registry_for_each_of_type(AppTypeService, start_service);
FURI_LOG_I(TAG, "Startup complete");
}
__attribute__((unused)) extern void nanobake_start(Config* _Nonnull config) {
furi_init();
Devices hardware = nb_devices_create(config); Devices hardware = nb_devices_create(config);
/*NbLvgl lvgl =*/nb_graphics_init(&hardware); /*NbLvgl lvgl =*/nb_graphics_init(&hardware);
thread_ids_init(prv_thread_ids); register_apps(config);
ESP_LOGI(TAG, "Starting apps"); start_services();
// TODO: option to await starting services?
// Services
for (size_t i = 0; i < NANOBAKE_SERVICES_COUNT; i++) {
prv_start_app(NANOBAKE_SERVICES[i]);
}
// System
for (size_t i = 0; i < NANOBAKE_SYSTEM_APPS_COUNT; i++) {
prv_start_app(NANOBAKE_SYSTEM_APPS[i]);
}
// User
for (size_t i = 0; i < config->apps_count; i++) {
prv_start_app(config->apps[i]);
}
ESP_LOGI(TAG, "Startup complete");
} }

View File

@ -1,9 +1,8 @@
#pragma once #pragma once
#include "app.h" #include "app_manifest.h"
#include "devices.h" #include "devices.h"
#include "core_defines.h" #include "furi_extra_defines.h"
#include "base.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -21,7 +20,7 @@ typedef struct {
const CreateTouchDriver _Nullable touch_driver; const CreateTouchDriver _Nullable touch_driver;
// List of user applications // List of user applications
const size_t apps_count; const size_t apps_count;
const App* const apps[]; const AppManifest* const apps[];
} Config; } Config;
__attribute__((unused)) extern void nanobake_start(Config _Nonnull* config); __attribute__((unused)) extern void nanobake_start(Config _Nonnull* config);

View File

@ -3,7 +3,7 @@
TouchDevice _Nonnull* nb_touch_alloc(TouchDriver _Nonnull* driver) { TouchDevice _Nonnull* nb_touch_alloc(TouchDriver _Nonnull* driver) {
TouchDevice _Nonnull* touch = malloc(sizeof(TouchDevice)); TouchDevice _Nonnull* touch = malloc(sizeof(TouchDevice));
bool success = driver->create_touch( bool success = driver->create_touch_device(
&(touch->io_handle), &(touch->io_handle),
&(touch->touch_handle) &(touch->touch_handle)
); );

View File

@ -11,7 +11,7 @@ typedef bool (*CreateTouch)(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_
typedef struct { typedef struct {
char name[32]; char name[32];
CreateTouch create_touch; CreateTouch create_touch_device;
} TouchDriver; } TouchDriver;
typedef struct { typedef struct {

View File

@ -1,22 +1,16 @@
#include "hello_world.h" #include "hello_world.h"
#include "applications/services/gui/gui.h" #include "furi.h"
#include "esp_log.h" #include "apps/services/gui/gui.h"
#include "esp_lvgl_port.h" #include "esp_lvgl_port.h"
#include "graphics.h"
#include "record.h"
static const char* TAG = "app_helloworld"; static const char* TAG = "app_hello_world";
ViewPort* view_port = NULL; ViewPort* view_port = NULL;
FuriSemaphore* quit_lock = NULL;
static void on_button_click(lv_event_t _Nonnull* event) { static void on_button_click(lv_event_t _Nonnull* event) {
ESP_LOGI(TAG, "button clicked"); ESP_LOGI(TAG, "button clicked");
furi_semaphore_give(quit_lock);
FURI_RECORD_TRANSACTION(RECORD_GUI, gui, {
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
view_port = NULL;
});
} }
// Main entry point for LVGL widget creation // Main entry point for LVGL widget creation
@ -48,18 +42,32 @@ static int32_t app_main(void* param) {
view_port_draw_callback_set(view_port, &app_lvgl, view_port); view_port_draw_callback_set(view_port, &app_lvgl, view_port);
// The transaction automatically calls furi_record_open() and furi_record_close() // The transaction automatically calls furi_record_open() and furi_record_close()
FURI_RECORD_TRANSACTION(RECORD_GUI, gui, { FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_add_view_port(gui, view_port, GuiLayerFullscreen); gui_add_view_port(gui, view_port, GuiLayerFullscreen);
}) })
// Wait for the button click to release the mutex (lock)
quit_lock = furi_semaphore_alloc(1, 0);
while (!furi_semaphore_take(quit_lock, UINT32_MAX)) {
// Do nothing
}
furi_semaphore_free(quit_lock);
quit_lock = NULL;
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
view_port = NULL;
});
return 0; return 0;
} }
const App hello_world_app = { const AppManifest hello_world_app = {
.id = "helloworld", .id = "helloworld",
.name = "Hello World", .name = "Hello World",
.type = USER, .icon = NULL,
.type = AppTypeUser,
.entry_point = &app_main, .entry_point = &app_main,
.stack_size = NB_TASK_STACK_SIZE_DEFAULT, .stack_size = AppStackSizeNormal,
.priority = NB_TASK_PRIORITY_DEFAULT
}; };

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include "app.h" #include "app_manifest.h"
extern const App hello_world_app; extern const AppManifest hello_world_app;

View File

@ -1,4 +1,6 @@
#include "nanobake.h" #include "nanobake.h"
#include "record.h"
#include "apps/services/loader/loader.h"
// Hardware // Hardware
#include "board_2432s024.h" #include "board_2432s024.h"
@ -17,4 +19,11 @@ __attribute__((unused)) void app_main(void) {
}; };
nanobake_start(&config); nanobake_start(&config);
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
FuriString* error_message = furi_string_alloc();
if (loader_start(loader, hello_world_app.id, NULL, error_message) != LoaderStatusOk) {
FURI_LOG_E(hello_world_app.id, "%s\r\n", furi_string_get_cstr(error_message));
}
});
} }