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.
Predefined configurations are available for:
- Yellow Board / 2432S024
- Yellow Board / 2432S024 (capacitive touch variant)
- (more will follow)
### 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);
}
static bool prv_create_display(DisplayDevice* display) {
static bool prv_create_display_device(DisplayDevice* display) {
ESP_LOGI(TAG, "creating display");
gpio_config_t io_conf = {
@ -117,6 +117,7 @@ static bool prv_create_display(DisplayDevice* display) {
display->vertical_resolution = LCD_VERTICAL_RESOLUTION;
display->draw_buffer_height = LCD_DRAW_BUFFER_HEIGHT;
display->bits_per_pixel = LCD_BITS_PER_PIXEL;
display->monochrome = false;
return true;
}
@ -124,6 +125,6 @@ static bool prv_create_display(DisplayDevice* display) {
DisplayDriver board_2432s024_create_display_driver() {
return (DisplayDriver) {
.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"
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");
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() {
return (TouchDriver) {
.name = "cst816s_2432s024",
.create_touch = &prv_create_touch
.create_touch_device = &prv_create_touch_device
};
}

View File

@ -52,12 +52,13 @@
#elif defined (__ti__)
#include "cmsis_tiarmclang.h"
#elif defined (ESP_PLATFORM)
#include "cmsis_esp.h"
/*
* GNU Compiler
*/
#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
* @version V5.4.2
* @date 17. December 2022
@ -25,6 +25,8 @@
#ifndef __CMSIS_GCC_H
#define __CMSIS_GCC_H
#include "freertos/portmacro.h"
/* ignore some GCC warnings */
#pragma GCC diagnostic push
#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
\details Enables IRQ interrupts by clearing special-purpose register PRIMASK.
Can only be executed in Privileged modes.
*/
__STATIC_FORCEINLINE void __enable_irq(void)
{
// TODO esp
// __ASM volatile ("cpsie i" : : : "memory");
portENABLE_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)
{
// TODO esp
// __ASM volatile ("cpsid i" : : : "memory");
portDISABLE_INTERRUPTS();
}
@ -1034,11 +1030,8 @@ __STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control)
*/
__STATIC_FORCEINLINE uint32_t __get_IPSR(void)
{
uint32_t result;
result = 0; // TODO esp
// __ASM volatile ("MRS %0, ipsr" : "=r" (result) );
return(result);
// TODO esp
return 0;
}
@ -1050,7 +1043,6 @@ __STATIC_FORCEINLINE uint32_t __get_IPSR(void)
__STATIC_FORCEINLINE uint32_t __get_APSR(void)
{
uint32_t result;
__ASM volatile ("MRS %0, apsr" : "=r" (result) );
return(result);
}
@ -1212,11 +1204,8 @@ __STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack)
*/
__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void)
{
uint32_t result;
result = 1U; // TODO esp
// __ASM volatile ("MRS %0, primask" : "=r" (result) );
return(result);
// Not supported by ESP
return 0U;
}

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 "common_defines.h"
#include "furi_core_defines.h"
#include "furi_hal_console.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.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) {
char tmp_str[] = "-2147483648";
itoa(data, tmp_str, 10);
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() {
furi_hal_console_puts("\r\n\tstack watermark: ");
__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() {
/*
furi_hal_console_puts("\r\n\t heap total: ");
__furi_put_uint32_as_text(xPortGetTotalHeapSize());
*/
furi_hal_console_puts("\r\n\t heap free: ");
__furi_put_uint32_as_text(xPortGetFreeHeapSize());
furi_hal_console_puts("\r\n\t heap watermark: ");
__furi_put_uint32_as_text(xPortGetMinimumEverFreeHeapSize());
furi_hal_console_puts("\r\n\theap total: ");
__furi_put_uint32_as_text(heap_caps_get_total_size(MALLOC_CAP_DEFAULT));
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\theap min free: ");
__furi_put_uint32_as_text(heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT));
}
static void __furi_print_name(bool isr) {
@ -130,24 +49,13 @@ FURI_NORETURN void __furi_crash_implementation() {
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_print_name(isr);
furi_hal_console_puts(__furi_check_message);
__furi_print_register_info();
if (!isr) {
__furi_print_stack_info();
}
__furi_print_heap_info();
__furi_print_bt_stack_info();
// 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
@ -176,24 +84,13 @@ FURI_NORETURN void __furi_crash_implementation() {
}
FURI_NORETURN void __furi_halt_implementation() {
__disable_irq();
// GET_MESSAGE_AND_STORE_REGISTERS();
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_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("\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();
}

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/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 "check.h"
#include "common_defines.h"
#include "furi_core_defines.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>

View File

@ -4,7 +4,7 @@
*/
#pragma once
#include "base.h"
#include "furi_core_types.h"
#ifdef __cplusplus
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 "common_defines.h"
#include "furi_core.h"
#include "furi_string.h"
#include <esp_log.h>
#include <memory.h>
#include <stdbool.h>
#include "esp_log.h" // TODO remove
#define TAG "FuriHalConsole"

View File

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

View File

@ -1,34 +1,14 @@
#include "kernel.h"
#include "base.h"
#include "check.h"
#include "common_defines.h"
#include "furi_core_defines.h"
#include "furi_core_types.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <rom/ets_sys.h>
bool furi_kernel_is_irq_or_masked() {
bool irq = false;
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_irq() {
return FURI_IS_IRQ_MODE();
}
bool furi_kernel_is_running() {
@ -36,7 +16,7 @@ bool furi_kernel_is_running() {
}
int32_t furi_kernel_lock() {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
int32_t lock;
@ -61,7 +41,7 @@ int32_t furi_kernel_lock() {
}
int32_t furi_kernel_unlock() {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
int32_t lock;
@ -91,7 +71,7 @@ int32_t furi_kernel_unlock() {
}
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()) {
case taskSCHEDULER_SUSPENDED:
@ -127,7 +107,7 @@ uint32_t furi_kernel_get_tick_frequency() {
}
void furi_delay_tick(uint32_t ticks) {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
if (ticks == 0U) {
taskYIELD();
} else {
@ -136,7 +116,7 @@ void furi_delay_tick(uint32_t ticks) {
}
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;
FuriStatus stat;
@ -165,7 +145,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) {
uint32_t furi_get_tick() {
TickType_t ticks;
if (furi_kernel_is_irq_or_masked() != 0U) {
if (furi_kernel_is_irq() != 0U) {
ticks = xTaskGetTickCountFromISR();
} else {
ticks = xTaskGetTickCount();
@ -183,7 +163,7 @@ uint32_t furi_ms_to_ticks(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) {
milliseconds += 1;
}

View File

@ -4,7 +4,7 @@
*/
#pragma once
#include "base.h"
#include "furi_core_types.h"
#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
*/
bool furi_kernel_is_irq_or_masked();
bool furi_kernel_is_irq();
/** 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>
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);
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) {
furi_assert(furi_kernel_is_irq_or_masked() == 0U);
furi_assert(furi_kernel_is_irq() == 0U);
furi_assert(instance);
vQueueDelete((QueueHandle_t)instance);
@ -29,7 +29,7 @@ furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t
stat = FuriStatusOk;
if (furi_kernel_is_irq_or_masked() != 0U) {
if (furi_kernel_is_irq() != 0U) {
if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
stat = FuriStatusErrorParameter;
} else {
@ -66,7 +66,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin
stat = FuriStatusOk;
if (furi_kernel_is_irq_or_masked() != 0U) {
if (furi_kernel_is_irq() != 0U) {
if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
stat = FuriStatusErrorParameter;
} else {
@ -132,7 +132,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {
if (hQueue == NULL) {
count = 0U;
} else if (furi_kernel_is_irq_or_masked() != 0U) {
} else if (furi_kernel_is_irq() != 0U) {
count = uxQueueMessagesWaitingFromISR(hQueue);
} else {
count = uxQueueMessagesWaiting(hQueue);
@ -149,7 +149,7 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
if (mq == NULL) {
space = 0U;
} else if (furi_kernel_is_irq_or_masked() != 0U) {
} else if (furi_kernel_is_irq() != 0U) {
isrm = taskENTER_CRITICAL_FROM_ISR();
/* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */
@ -168,7 +168,7 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) {
QueueHandle_t hQueue = (QueueHandle_t)instance;
FuriStatus stat;
if (furi_kernel_is_irq_or_masked() != 0U) {
if (furi_kernel_is_irq() != 0U) {
stat = FuriStatusErrorISR;
} else if (hQueue == NULL) {
stat = FuriStatusErrorParameter;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
#include "semaphore.h"
#include "check.h"
#include "common_defines.h"
#include "furi_core_defines.h"
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
@ -99,18 +99,41 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
return (stat);
}
//uint32_t furi_semaphore_get_count(FuriSemaphore* instance) {
// furi_assert(instance);
//
// SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
// uint32_t count;
//
// if(FURI_IS_IRQ_MODE()) {
uint32_t furi_semaphore_get_count(FuriSemaphore* instance) {
furi_assert(instance);
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
uint32_t count;
if(FURI_IS_IRQ_MODE()) {
furi_crash("not implemented");
// count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore);
// } else {
// count = (uint32_t)uxSemaphoreGetCount(hSemaphore);
// }
//
// /* Return number of tokens */
// return (count);
//}
} else {
count = (uint32_t)uxSemaphoreGetCount(hSemaphore);
}
/* Return number of tokens */
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
#include "base.h"
#include "furi_core_types.h"
#include "thread.h"
#ifdef __cplusplus
@ -45,13 +41,30 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout);
*/
FuriStatus furi_semaphore_release(FuriSemaphore* instance);
///** Get semaphore count
// *
// * @param instance The pointer to FuriSemaphore instance
// *
// * @return Semaphore count
// */
//uint32_t furi_semaphore_get_count(FuriSemaphore* instance);
/** Get semaphore count
*
* @param instance The pointer to FuriSemaphore instance
*
* @return Semaphore count
*/
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
}

View File

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

View File

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

View File

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

View File

@ -5,8 +5,8 @@
#pragma once
#include "base.h"
#include "common_defines.h"
#include "furi_core_defines.h"
#include "furi_core_types.h"
#include <stddef.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) {
furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL));
furi_assert((furi_kernel_is_irq() == 0U) && (func != NULL));
TimerHandle_t hTimer;
TimerCallback_t* callb;
@ -58,7 +58,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
}
void furi_timer_free(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
furi_assert(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) {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
furi_assert(instance);
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) {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
furi_assert(instance);
furi_assert(ticks < portMAX_DELAY);
@ -117,7 +117,7 @@ FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) {
}
FuriStatus furi_timer_stop(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
furi_assert(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) {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
furi_assert(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) {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
furi_assert(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) {
BaseType_t ret = pdFAIL;
if (furi_kernel_is_irq_or_masked()) {
if (furi_kernel_is_irq()) {
ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL);
} else {
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) {
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(!furi_kernel_is_irq());
TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle();
furi_check(task_handle); // Don't call this method before timer task start

View File

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

View File

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

View File

@ -1,10 +1,9 @@
idf_component_register(
SRC_DIRS "src"
"src/applications"
"src/applications/system/system_info"
"src/applications/services/desktop"
"src/applications/services/loader"
"src/applications/services/gui"
"src/apps/system/system_info"
"src/apps/services/desktop"
"src/apps/services/loader"
"src/apps/services/gui"
INCLUDE_DIRS "src"
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 "core_defines.h"
#include "devices.h"
#include "furi_extra_defines.h"
static int32_t prv_desktop_main(void* param) {
UNUSED(param);
@ -8,11 +7,11 @@ static int32_t prv_desktop_main(void* param) {
return 0;
}
const App desktop_app = {
const AppManifest desktop_app = {
.id = "desktop",
.name = "Desktop",
.type = SERVICE,
.icon = NULL,
.type = AppTypeService,
.entry_point = &prv_desktop_main,
.stack_size = NB_TASK_STACK_SIZE_DEFAULT,
.priority = NB_TASK_PRIORITY_DEFAULT
.stack_size = AppStackSizeNormal
};

View File

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

View File

@ -1,5 +1,6 @@
#include "check.h"
#include "core_defines.h"
#include "esp_lvgl_port.h"
#include "furi_extra_defines.h"
#include "gui_i.h"
#include "record.h"
@ -184,7 +185,11 @@ Gui* gui_alloc() {
Gui* gui = malloc(sizeof(Gui));
gui->thread_id = furi_thread_get_current_id();
gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
furi_check(lvgl_port_lock(100));
gui->lvgl_parent = lv_scr_act();
lvgl_port_unlock();
gui->lockdown = false;
furi_check(gui->mutex);
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",
.name = "GUI",
.type = SERVICE,
.icon = NULL,
.type = AppTypeService,
.entry_point = &prv_gui_main,
.stack_size = NB_TASK_STACK_SIZE_DEFAULT,
.priority = NB_TASK_PRIORITY_DEFAULT
.stack_size = AppStackSizeNormal
};

View File

@ -1,22 +1,13 @@
#pragma once
#include "app.h"
#include "lvgl.h"
#include "app_manifest.h"
#include "view_port.h"
#ifdef __cplusplus
extern "C" {
#endif
extern const App gui_app;
/** Canvas Orientation */
typedef enum {
CanvasOrientationHorizontal,
CanvasOrientationHorizontalFlip,
CanvasOrientationVertical,
CanvasOrientationVerticalFlip,
} CanvasOrientation;
extern const AppManifest gui_app;
/** Gui layers */
typedef enum {
@ -36,7 +27,6 @@ typedef enum {
typedef void (*GuiCanvasCommitCallback)(
uint8_t* data,
size_t size,
CanvasOrientation orientation,
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_i.h"
#include "view_port_i.h"
#include "esp_lvgl_port.h"
#define TAG "viewport"
@ -88,7 +89,10 @@ void view_port_draw(ViewPort* view_port, lv_obj_t* parent) {
furi_check(view_port->gui);
if (view_port->draw_callback) {
furi_check(lvgl_port_lock(100));
lv_obj_clean(parent);
lvgl_port_unlock();
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
#include "app.h"
#include "app_manifest.h"
#ifdef __cplusplus
extern "C" {
#endif
extern App system_info_app;
extern AppManifest system_info_app;
#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");
DisplayDriver display_driver = config->display_driver();
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;
if (config->touch_driver != NULL) {

View File

@ -1,10 +1,10 @@
#include "check.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));
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->display_handle != NULL);
furi_check(display->horizontal_resolution != 0);

View File

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

View File

@ -25,7 +25,7 @@ Lvgl nb_graphics_init(Devices _Nonnull* hardware) {
.double_buffer = 0,
.hres = display->horizontal_resolution,
.vres = display->vertical_resolution,
.monochrome = false,
.monochrome = display->monochrome,
.rotation = {
.swap_xy = display->swap_xy,
.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);
furi_check(disp != NULL, "failed to add display");
lv_indev_t _Nullable* touch_indev = NULL;
// Add touch

View File

@ -1,86 +1,61 @@
#include "nanobake.h"
#include "app_i.h"
#include "applications/applications_i.h"
#include "app_manifest_registry.h"
#include "devices_i.h"
#include "esp_log.h"
#include "furi.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"
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() {
// TODO: can we remove the suspend-resume logic?
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);
// System apps
extern const AppManifest system_info_app;
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(
app->name,
app->stack_size,
app->entry_point,
manifest->name,
manifest->stack_size,
manifest->entry_point,
NULL
);
if (app->type == SERVICE) {
furi_thread_mark_as_service(thread);
}
furi_thread_set_appid(thread, app->id);
furi_thread_set_priority(thread, app->priority);
furi_thread_mark_as_service(thread);
furi_thread_set_appid(thread, manifest->id);
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) {
prv_furi_init();
static void register_apps(Config* _Nonnull config) {
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);
/*NbLvgl lvgl =*/nb_graphics_init(&hardware);
thread_ids_init(prv_thread_ids);
register_apps(config);
ESP_LOGI(TAG, "Starting apps");
// 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");
start_services();
// TODO: option to await starting services?
}

View File

@ -1,9 +1,8 @@
#pragma once
#include "app.h"
#include "app_manifest.h"
#include "devices.h"
#include "core_defines.h"
#include "base.h"
#include "furi_extra_defines.h"
#ifdef __cplusplus
extern "C" {
@ -21,7 +20,7 @@ typedef struct {
const CreateTouchDriver _Nullable touch_driver;
// List of user applications
const size_t apps_count;
const App* const apps[];
const AppManifest* const apps[];
} 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* touch = malloc(sizeof(TouchDevice));
bool success = driver->create_touch(
bool success = driver->create_touch_device(
&(touch->io_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 {
char name[32];
CreateTouch create_touch;
CreateTouch create_touch_device;
} TouchDriver;
typedef struct {

View File

@ -1,22 +1,16 @@
#include "hello_world.h"
#include "applications/services/gui/gui.h"
#include "esp_log.h"
#include "furi.h"
#include "apps/services/gui/gui.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;
FuriSemaphore* quit_lock = NULL;
static void on_button_click(lv_event_t _Nonnull* event) {
ESP_LOGI(TAG, "button clicked");
FURI_RECORD_TRANSACTION(RECORD_GUI, gui, {
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
view_port = NULL;
});
furi_semaphore_give(quit_lock);
}
// 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);
// 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);
})
// 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;
}
const App hello_world_app = {
const AppManifest hello_world_app = {
.id = "helloworld",
.name = "Hello World",
.type = USER,
.icon = NULL,
.type = AppTypeUser,
.entry_point = &app_main,
.stack_size = NB_TASK_STACK_SIZE_DEFAULT,
.priority = NB_TASK_PRIORITY_DEFAULT
.stack_size = AppStackSizeNormal,
};

View File

@ -1,5 +1,5 @@
#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 "record.h"
#include "apps/services/loader/loader.h"
// Hardware
#include "board_2432s024.h"
@ -17,4 +19,11 @@ __attribute__((unused)) void app_main(void) {
};
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));
}
});
}