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:
parent
60372076d5
commit
b9427d4eba
@ -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
|
||||
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
19
components/furi/src/api_lock.h
Normal file
19
components/furi/src/api_lock.h
Normal 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
102
components/furi/src/app.c
Normal 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;
|
||||
}
|
||||
23
components/furi/src/app_i.h
Normal file
23
components/furi/src/app_i.h
Normal 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
|
||||
32
components/furi/src/app_manifest.h
Normal file
32
components/furi/src/app_manifest.h
Normal 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
|
||||
79
components/furi/src/app_manifest_registry.c
Normal file
79
components/furi/src/app_manifest_registry.c
Normal 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);
|
||||
});
|
||||
}
|
||||
23
components/furi/src/app_manifest_registry.h
Normal file
23
components/furi/src/app_manifest_registry.h
Normal 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
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -1,4 +1,5 @@
|
||||
#include "common_defines.h"
|
||||
#include "critical.h"
|
||||
#include "furi_core_defines.h"
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
22
components/furi/src/critical.h
Normal file
22
components/furi/src/critical.h
Normal 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);
|
||||
@ -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>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "furi_core_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
30
components/furi/src/furi.c
Normal file
30
components/furi/src/furi.c
Normal 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();
|
||||
}
|
||||
28
components/furi/src/furi.h
Normal file
28
components/furi/src/furi.h
Normal 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
|
||||
12
components/furi/src/furi_core.h
Normal file
12
components/furi/src/furi_core.h
Normal 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"
|
||||
40
components/furi/src/furi_core_defines.h
Normal file
40
components/furi/src/furi_core_defines.h
Normal 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
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
22
components/furi/src/log.h
Normal 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
|
||||
@ -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;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "furi_core_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "furi_core_types.h"
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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" {
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "furi_core_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@ -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); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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);
|
||||
@ -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
|
||||
@ -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
|
||||
};
|
||||
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "app.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const App loader_app;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -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
|
||||
};
|
||||
@ -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
|
||||
};
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
};
|
||||
@ -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
|
||||
);
|
||||
|
||||
13
components/nanobake/src/apps/services/gui/icon.c
Normal file
13
components/nanobake/src/apps/services/gui/icon.c
Normal 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];
|
||||
}
|
||||
11
components/nanobake/src/apps/services/gui/icon.h
Normal file
11
components/nanobake/src/apps/services/gui/icon.h
Normal 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;
|
||||
};
|
||||
10
components/nanobake/src/apps/services/gui/icon_i.h
Normal file
10
components/nanobake/src/apps/services/gui/icon_i.h
Normal 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;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
290
components/nanobake/src/apps/services/loader/loader.c
Normal file
290
components/nanobake/src/apps/services/loader/loader.c
Normal 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
|
||||
};
|
||||
84
components/nanobake/src/apps/services/loader/loader.h
Normal file
84
components/nanobake/src/apps/services/loader/loader.h
Normal 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
|
||||
55
components/nanobake/src/apps/services/loader/loader_i.h
Normal file
55
components/nanobake/src/apps/services/loader/loader_i.h
Normal 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;
|
||||
@ -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
|
||||
};
|
||||
@ -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
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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?
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user