[FL-140] Core api dynamic records (#296)

* SYSTEM: tickless mode with deep sleep.
* Move FreeRTOS ticks to lptim2
* API: move all sumbodules init routines to one place. Timebase: working lptim2 at tick source.
* API Timebase: lp-timer routines, timer access safe zones prediction and synchronization. FreeRTOS: adjust configuration for tickless mode.
* NFC: support for tickless mode.
* API Timebase: improve tick error handling in IRQ. Apploader: use insomnia mode to run applications.
* BLE: prevent sleep while core2 starting
* HAL: nap while in insomnia mode
* init records work
* try to implement record delete
* tests and flapp
* flapp subsystem
* new core functions to get app stat, simplify core code
* fix thread termination
* add strdup to core
* fix tests
* Refactoring: remove all unusued parts, update API usage, aggreagate API sources and headers, new record storage
* Refactoring: update furi record api usage, cleanup code
* Fix broken merge for freertos apps
* Core, Target: fix compilation warnings
* Drop firmware target local
* HAL Timebase, Power, Clock: semaphore guarded access to clock and power modes, better sleep mode.
* SD-Filesystem: wait for all deps to arrive before adding widget. Core, BLE: disable debug dump to serial.
* delete old app example-ipc
* delete old app fatfs list
* fix strobe app, add input header
* delete old display driver
* comment old app qr-code
* fix sd-card test, add forced widget update
* remove unused new core test
* increase heap to 128k
* comment and assert old core tests
* fix syntax

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
DrZlo13
2021-01-21 02:09:26 +10:00
committed by GitHub
parent 6c4983c6b6
commit 8f9b2513ff
169 changed files with 1009 additions and 4535 deletions

View File

@@ -1,47 +0,0 @@
#pragma once
#include "flipper.h"
// == Flipper Application control (flapp) ==
typedef FlappHandler uint32_t; // TODO
/*
simply starts application. It call `app` entrypoint with `param` passed as argument
Useful for daemon applications and pop-up.
*/
FlappHandler* flapp_start(void(app*)(void*), char* name, void* param);
/*
swtich to other application.
System **stop current app**, call `app` entrypoint with `param` passed
as argument and save current application entrypoint to `prev` field in
current application registry. Useful for UI or "active" application.
*/
FlappHandler* flapp_switch(void(app*)(void*), char* name, void* param);
/*
Exit application
stop current application (stop thread and clear application's stack),
start application from `prev` entry in current application registry,
cleanup current application registry.
*/
void flapp_exit(void* param);
/*
stop specified `app` without returning to `prev` application.
*/
bool flapp_kill(FlappHandler* app);
/*
If case one app depend on other, notify that app is ready.
*/
void flapp_ready();
/*
Register on-exit callback.
It called before app will be killed.
Not recommended to use in user scenario, only for system purpose
(unregister callbacks, release mutexes, etc.)
*/
bool flapp_on_exit(void(cb*)(void*), void* ctx);

View File

@@ -1,14 +0,0 @@
#include "furi.h"
#include "furi-deprecated.h"
bool furi_create(const char* name, void* ptr) {
return furi_create_deprecated(name, ptr, sizeof(size_t));
}
void* furi_open(const char* name) {
FuriRecordSubscriber* record = furi_open_deprecated(name, false, false, NULL, NULL, NULL);
void* res = furi_take(record);
furi_give(record);
return res;
}

View File

@@ -1,26 +0,0 @@
#pragma once
#include "flipper.h"
/*
== Flipper universal registry implementation (FURI) ==
## Requirements
* start daemon app
* kill app
* start child thread (kill when parent app was killed)
* switch between UI apps
*/
/*
Create record.
creates new record in registry and store pointer into it
*/
bool furi_create(const char* name, void* ptr);
/*
Open record.
get stored pointer by its name
*/
void* furi_open(const char* name);

View File

@@ -1,4 +1,6 @@
#include "api-gpio.h"
#include <cmsis_os2.h>
#include <furi/record.h>
osMutexId_t gpioInitMutex;
@@ -37,7 +39,7 @@ void gpio_disable(GpioDisableRecord* gpio_record) {
// get GPIO record
ValueMutex* gpio_open_mutex(const char* name) {
ValueMutex* gpio_mutex = (ValueMutex*)furi_open(name);
ValueMutex* gpio_mutex = (ValueMutex*)furi_record_open(name);
// TODO disable gpio on app exit
//if(gpio_mutex != NULL) flapp_on_exit(gpio_disable, gpio_mutex);
@@ -48,6 +50,6 @@ ValueMutex* gpio_open_mutex(const char* name) {
// get GPIO record and acquire mutex
GpioPin* gpio_open(const char* name) {
ValueMutex* gpio_mutex = gpio_open_mutex(name);
GpioPin* gpio_pin = acquire_mutex(gpio_mutex, FLIPPER_HELPER_TIMEOUT);
GpioPin* gpio_pin = acquire_mutex(gpio_mutex, osWaitForever);
return gpio_pin;
}

View File

@@ -1,7 +1,11 @@
#pragma once
#include "flipper.h"
#include "flipper_v2.h"
#include "api-hal-gpio.h"
#include <furi/valuemutex.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
ValueMutex* gpio_mutex;
@@ -38,4 +42,8 @@ void gpio_disable(GpioDisableRecord* gpio_record);
ValueMutex* gpio_open_mutex(const char* name);
// get GPIO record and acquire mutex
GpioPin* gpio_open(const char* name);
GpioPin* gpio_open(const char* name);
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,8 @@
#include "api-interrupt-mgr.h"
#include <m-list.h>
#include <cmsis_os2.h>
LIST_DEF(list_interrupt, InterruptCallbackItem, M_POD_OPLIST);
list_interrupt_t interrupts;
osMutexId_t interrupt_list_mutex;

View File

@@ -1,5 +1,10 @@
#pragma once
#include "flipper_v2.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*InterruptCallback)(void*, void*);
@@ -18,4 +23,8 @@ typedef struct {
bool api_interrupt_init();
void api_interrupt_add(InterruptCallback callback, InterruptType type, void* context);
void api_interrupt_remove(InterruptCallback callback);
void api_interrupt_call(InterruptType type, void* hw);
void api_interrupt_call(InterruptType type, void* hw);
#ifdef __cplusplus
}
#endif

View File

@@ -1,4 +1,8 @@
#include "flipper_v2.h"
#include <furi.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
struct used for handling SPI info.
@@ -133,4 +137,8 @@ void cc1101_example() {
}
}
```
*/
*/
#ifdef __cplusplus
}
#endif

View File

@@ -1,48 +0,0 @@
#include <stdio.h>
#include "flipper.h"
#include "flipper_v2.h"
extern "C" {
#include "log.h"
#include "applications.h"
#include "tty_uart.h"
}
// for testing purpose
uint32_t exitcode = 0;
extern "C" void set_exitcode(uint32_t _exitcode) {
exitcode = _exitcode;
}
extern "C" int app() {
init_flipper_api();
register_tty_uart();
FuriRecordSubscriber* log = get_default_log();
fuprintf(log, "\n=== Welcome to Flipper Zero! ===\n\n");
// FURI startup
const size_t flipper_app_count = sizeof(FLIPPER_STARTUP) / sizeof(FLIPPER_STARTUP[0]);
FuriApp* handlers[flipper_app_count];
for(size_t i = 0; i < flipper_app_count; i++) {
// TODO create a dependency tree and run tasks in the desired order
furiac_wait_libs(&FLIPPER_STARTUP[i].libs);
handlers[i] = furiac_start(FLIPPER_STARTUP[i].app, FLIPPER_STARTUP[i].name, NULL);
}
bool is_alive = false;
do {
is_alive = false;
for(size_t i = 0; i < flipper_app_count; i++) {
if(handlers[i]->handler != NULL) {
is_alive = true;
}
}
delay(500);
} while(is_alive);
fuprintf(log, "\n=== Bye from Flipper Zero! ===\n\n");
return (int)exitcode;
}

View File

@@ -1,8 +1,8 @@
CORE_DIR = $(PROJECT_ROOT)/core
CFLAGS += -I$(CORE_DIR)
CFLAGS += -I$(CORE_DIR) -D_GNU_SOURCE
ASM_SOURCES += $(wildcard $(CORE_DIR)/*.s)
C_SOURCES += $(wildcard $(CORE_DIR)/*.c)
C_SOURCES += $(wildcard $(CORE_DIR)/api-basic/*.c)
C_SOURCES += $(wildcard $(CORE_DIR)/furi/*.c)
C_SOURCES += $(wildcard $(CORE_DIR)/api-hal/*.c)
CPP_SOURCES += $(wildcard $(CORE_DIR)/*.cpp)

View File

@@ -1,24 +0,0 @@
#pragma once
#include "api-hal.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "cmsis_os.h"
#include "furi-deprecated.h"
#include "log.h"
#include "input/input.h"
#include <stdio.h>
void set_exitcode(uint32_t _exitcode);
#define FURI_LIB (const char*[])
#ifdef __cplusplus
}
#endif

View File

@@ -1,16 +0,0 @@
#pragma once
#include "stdint.h"
// Arduino defines
#define pinMode gpio_init
#define digitalWrite gpio_write
#define digitalRead gpio_read
#define delayMicroseconds delay_us
#define delay osDelay
#define OUTPUT GpioModeOutputPushPull
#define INPUT GpioModeInput
#define LOW false
#define HIGH true
typedef uint8_t byte;

View File

@@ -1,19 +0,0 @@
#include "flipper_v2.h"
bool init_flipper_api(void) {
bool no_errors = true;
if(!furi_init()) {
no_errors = false;
}
if(!gpio_api_init()) {
no_errors = false;
}
if(!api_interrupt_init()) {
no_errors = false;
}
return no_errors;
}

View File

@@ -1,33 +0,0 @@
#pragma once
#include "flipper.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "api-basic/furi.h"
//#include "api-basic/flapp.h"
#include "cmsis_os2.h"
#include "api-basic/valuemutex.h"
#include "api-basic/pubsub.h"
#include "api-basic/value-expanders.h"
#include "api-basic/event.h"
#include "api-basic/memmgr.h"
#include "api-basic/check.h"
#include "api-hal/api-gpio.h"
#include "api-hal/api-interrupt-mgr.h"
#include "api-hal-resources.h"
#include "gui/gui.h"
// tmeout for helper functions
#define FLIPPER_HELPER_TIMEOUT 10
bool init_flipper_api(void);
#ifdef __cplusplus
}
#endif

View File

@@ -1,295 +0,0 @@
#include "furi-deprecated.h"
#include <string.h>
// TODO: this file contains printf, that not implemented on uC target
#ifdef FURI_DEBUG
#include <stdio.h>
#endif
#define MAX_RECORD_COUNT 32
static FuriRecord records[MAX_RECORD_COUNT];
static size_t current_buffer_idx = 0;
osMutexId_t furi_core_mutex;
bool furi_init(void) {
furi_core_mutex = osMutexNew(NULL);
if(furi_core_mutex == NULL) return false;
return true;
}
// find record pointer by name
static FuriRecord* find_record(const char* name) {
if(name == NULL) return NULL;
FuriRecord* res = NULL;
for(size_t i = 0; i < MAX_RECORD_COUNT; i++) {
if(records[i].name != NULL && strcmp(name, records[i].name) == 0) {
res = &records[i];
}
}
return res;
}
// TODO: change open-create to only open
bool furi_create_deprecated(const char* name, void* value, size_t size) {
#ifdef FURI_DEBUG
printf("[FURI] creating %s record\n", name);
#endif
// acquire mutex to prevent simultaneous write to record with same index
if(osMutexAcquire(furi_core_mutex, osWaitForever) != osOK) {
return false;
}
FuriRecord* record = find_record(name);
if(record != NULL) {
#ifdef FURI_DEBUG
printf("[FURI] record already exist\n");
#endif
record->value = value;
record->size = size;
return true;
}
// record not exist, create new
if(current_buffer_idx >= MAX_RECORD_COUNT) {
// max record count exceed
#ifdef FURI_DEBUG
printf("[FURI] create: max record count exceed\n");
#endif
return NULL;
}
records[current_buffer_idx].mute_counter = 0;
records[current_buffer_idx].mutex =
xSemaphoreCreateMutexStatic(&records[current_buffer_idx].mutex_buffer);
records[current_buffer_idx].value = value;
records[current_buffer_idx].size = size;
records[current_buffer_idx].name = name;
for(size_t i = 0; i < MAX_RECORD_SUBSCRIBERS; i++) {
records[current_buffer_idx].subscribers[i].allocated = false;
records[current_buffer_idx].subscribers[i].ctx = NULL;
}
current_buffer_idx++;
osMutexRelease(furi_core_mutex);
return true;
}
FuriRecordSubscriber* furi_open_deprecated(
const char* name,
bool solo,
bool no_mute,
FlipperRecordCallback value_callback,
FlipperRecordStateCallback state_callback,
void* ctx) {
#ifdef FURI_DEBUG
printf("[FURI] opening %s record\n", name);
#endif
// get furi record by name
FuriRecord* record = find_record(name);
if(record == NULL) {
// cannot find record
#ifdef FURI_DEBUG
printf("[FURI] cannot find record %s\n", name);
#endif
// create record if not exist
if(!furi_create_deprecated(name, NULL, 0)) {
return NULL;
}
record = find_record(name);
if(record == NULL) {
return NULL;
}
}
// allocate subscriber
FuriRecordSubscriber* subscriber = NULL;
for(size_t i = 0; i < MAX_RECORD_SUBSCRIBERS; i++) {
if(!record->subscribers[i].allocated) {
subscriber = &record->subscribers[i];
break;
}
}
if(subscriber == NULL) {
// cannot add subscriber (full)
#ifdef FURI_DEBUG
printf("[FURI] open: cannot add subscriber (full)\n");
#endif
return NULL;
}
// increase mute_counter
if(solo) {
record->mute_counter++;
}
// set all parameters
subscriber->allocated = true;
subscriber->mute_counter = record->mute_counter;
subscriber->no_mute = no_mute;
subscriber->cb = value_callback;
subscriber->state_cb = state_callback;
subscriber->record = record;
subscriber->ctx = ctx;
// register record in application
FuriApp* current_task = find_task(xTaskGetCurrentTaskHandle());
if(current_task != NULL) {
current_task->records[current_task->records_count] = record;
current_task->records_count++;
} else {
#ifdef FURI_DEBUG
printf("[FURI] open: no current task\n");
#endif
}
return subscriber;
}
void furi_close(FuriRecordSubscriber* handler) {
#ifdef FURI_DEBUG
printf("[FURI] closing %s record\n", handler->record->name);
#endif
// deallocate subscriber
handler->allocated = false;
// set mute counter to next max value
uint8_t max_mute_counter = 0;
for(size_t i = 0; i < MAX_RECORD_SUBSCRIBERS; i++) {
if(handler->record->subscribers[i].allocated) {
if(handler->record->subscribers[i].mute_counter > max_mute_counter) {
max_mute_counter = handler->record->subscribers[i].mute_counter;
}
}
}
handler->record->mute_counter = max_mute_counter;
}
static void furi_notify(FuriRecordSubscriber* handler, const void* value, size_t size) {
for(size_t i = 0; i < MAX_RECORD_SUBSCRIBERS; i++) {
if(handler->record->subscribers[i].allocated) {
if(handler->record->subscribers[i].cb != NULL) {
handler->record->subscribers[i].cb(
value, size, handler->record->subscribers[i].ctx);
}
}
}
}
void* furi_take(FuriRecordSubscriber* handler) {
if(handler == NULL || handler->record == NULL) return NULL;
if(xSemaphoreTake(handler->record->mutex, portMAX_DELAY) == pdTRUE) {
return handler->record->value;
} else {
return NULL;
}
}
void furi_give(FuriRecordSubscriber* handler) {
if(handler == NULL || handler->record == NULL) return;
xSemaphoreGive(handler->record->mutex);
}
void furi_commit(FuriRecordSubscriber* handler) {
if(handler == NULL || handler->record == NULL) return;
furi_notify(handler, handler->record->value, handler->record->size);
furi_give(handler);
}
bool furi_read(FuriRecordSubscriber* handler, void* value, size_t size) {
#ifdef FURI_DEBUG
printf("[FURI] read from %s\n", handler->record->name);
#endif
if(handler == NULL || handler->record == NULL || value == NULL) return false;
if(size > handler->record->size) return false;
// return false if read from pipe
if(handler->record->value == NULL) return false;
furi_take(handler);
memcpy(value, handler->record->value, size);
furi_notify(handler, value, size);
furi_give(handler);
return true;
}
bool furi_write(FuriRecordSubscriber* handler, const void* value, size_t size) {
#ifdef FURI_DEBUG
printf("[FURI] write to %s\n", handler->record->name);
#endif
if(handler == NULL || handler->record == NULL || value == NULL) {
#ifdef FURI_DEBUG
printf(
"[FURI] write: null param %x %x\n",
(uint32_t)(size_t)handler,
(uint32_t)(size_t)value);
#endif
return false;
}
// check if closed
if(!handler->allocated) {
#ifdef FURI_DEBUG
printf("[FURI] write: handler closed\n");
#endif
return false;
}
if(handler->record->value != NULL && size > handler->record->size) {
#ifdef FURI_DEBUG
printf("[FURI] write: wrong size %d\n", (uint32_t)size);
#endif
return false;
}
// check mute
if(handler->record->mute_counter != handler->mute_counter && !handler->no_mute) {
#ifdef FURI_DEBUG
printf("[FURI] write: muted\n");
#endif
return false;
}
furi_take(handler);
if(handler->record->value != NULL) {
// real write to value
memcpy(handler->record->value, value, size);
// notify subscribers
furi_notify(handler, handler->record->value, handler->record->size);
} else {
furi_notify(handler, value, size);
}
furi_give(handler);
return true;
}

View File

@@ -1,207 +0,0 @@
#pragma once
#include "cmsis_os.h"
#ifdef HAVE_FREERTOS
#include <semphr.h>
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include "assets_icons.h"
#define MAX_TASK_RECORDS 8
#define MAX_RECORD_SUBSCRIBERS 8
inline static void* furi_alloc(size_t size) {
void* p = malloc(size);
assert(p);
return memset(p, 0, size);
}
/// application is just a function
typedef void (*FlipperApplication)(void*);
/// pointer to value callback function
typedef void (*FlipperRecordCallback)(const void*, size_t, void*);
typedef enum {
FlipperRecordStateMute, ///< record open and mute this handler
FlipperRecordStateUnmute, ///< record unmuted
FlipperRecordStateDeleted ///< record owner halt
} FlipperRecordState;
/// pointer to state callback function
typedef void (*FlipperRecordStateCallback)(FlipperRecordState, void*);
struct _FuriRecord;
typedef struct {
bool allocated;
FlipperRecordCallback cb; ///< value cb
FlipperRecordStateCallback state_cb; ///< state cb
uint8_t mute_counter; ///< see "wiki/FURI#mute-algorithm"
bool no_mute;
struct _FuriRecord* record; ///< parent record
void* ctx;
} FuriRecordSubscriber;
/// FURI record handler
struct _FuriRecord {
const char* name;
void* value;
size_t size;
StaticSemaphore_t mutex_buffer;
SemaphoreHandle_t mutex;
uint8_t mute_counter;
FuriRecordSubscriber subscribers[MAX_RECORD_SUBSCRIBERS];
};
typedef struct _FuriRecord FuriRecord;
/// store info about active task
typedef struct {
const char* name;
FlipperApplication application;
const char* prev_name;
FlipperApplication prev;
TaskHandle_t handler;
uint8_t records_count; ///< count of records which task open
FuriRecord* records[MAX_TASK_RECORDS]; ///< list of records which task open
bool ready;
} FuriApp;
// application dependency info
typedef struct {
uint8_t count;
const char** name;
} FlipperAppLibrary;
// application startup info
typedef struct {
FlipperApplication app;
const char* name;
FlipperAppLibrary libs;
IconName icon;
} FlipperStartupApp;
// Init core
bool furi_init(void);
/*!
Simply starts application.
It call app entrypoint with param passed as argument.
Useful for daemon applications and pop-up.
*/
FuriApp* furiac_start(FlipperApplication app, const char* name, void* param);
/*!
Swtich to other application.
FURI stop current app, call app entrypoint with param passed as
argument and save current application entrypoint to prev field
in current application registry.
Useful for UI or "active" application.
*/
void furiac_switch(FlipperApplication app, char* name, void* param);
/*!
Stop current application
(stop thread and clear application's stack), start application
from prev entry in current application registry, cleanup current
application registry.
*/
void furiac_exit(void* param);
/*!
Mark application as prepared and ready to perform actions
*/
void furiac_ready();
/*
Wait for the libraries we depend on
*/
void furiac_wait_libs(const FlipperAppLibrary* libs);
/*!
Stop specified app without returning to prev application.
*/
bool furiac_kill(FuriApp* app);
// find task pointer by handle
FuriApp* find_task(TaskHandle_t handler);
/*!
Creates named FURI record.
\param[in] name you can open this record anywhere
\param[in] value pointer to data.
\param[in] size size of data.
If NULL, create FURI Pipe (only callbacks management, no data/mutex)
Returns false if registry have not enough memory for creating.
*/
bool furi_create_deprecated(const char* name, void* value, size_t size);
/*!
Opens existing FURI record by name.
Returns NULL if record does not exist.
\param[in] solo if true another applications handlers set into "muted" state.
When appication has exited or record has closed, all handlers is unmuted.
It may be useful for concurrently acces to resources like framebuffer or beeper.
\param[in] no_mute if true, another applications cannot mute this handler.
*/
FuriRecordSubscriber* furi_open_deprecated(
const char* name,
bool solo,
bool no_mute,
FlipperRecordCallback value_callback,
FlipperRecordStateCallback state_callback,
void* ctx);
/*!
*/
void furi_close(FuriRecordSubscriber* handler);
/*!
read message from record.
Returns true if success, false otherwise (closed/non-existent record)
Also return false if you try to read from FURI pipe
TODO: enum return value with execution status
*/
bool furi_read(FuriRecordSubscriber* record, void* data, size_t size);
/*!
write message to record.
Returns true if success, false otherwise (closed/non-existent record or muted).
TODO: enum return value with execution status
*/
bool furi_write(FuriRecordSubscriber* record, const void* data, size_t size);
/*!
lock value mutex.
It can be useful if records contain pointer to buffer which you want to change.
You must call furi_give after operation on data and
you shouldn't block executing between take and give calls
Returns pointer to data, NULL if closed/non-existent record or muted
TODO: enum return value with execution status
*/
void* furi_take(FuriRecordSubscriber* record);
/*!
unlock value mutex.
*/
void furi_give(FuriRecordSubscriber* record);
/*!
unlock value mutex and notify subscribers that data is chaned.
*/
void furi_commit(FuriRecordSubscriber* handler);

36
core/furi.c Normal file
View File

@@ -0,0 +1,36 @@
#include "furi.h"
#include <applications.h>
// for testing purpose
uint32_t exitcode = 0;
void set_exitcode(uint32_t _exitcode) {
exitcode = _exitcode;
}
void furi_init() {
gpio_api_init();
api_interrupt_init();
furi_record_init();
furi_stdglue_init();
}
int systemd() {
furi_init();
// FURI startup
for(size_t i = 0; i < FLIPPER_SERVICES_size(); i++) {
osThreadAttr_t* attr = furi_alloc(sizeof(osThreadAttr_t));
attr->name = FLIPPER_SERVICES[i].name;
attr->stack_size = 1024;
osThreadNew(FLIPPER_SERVICES[i].app, NULL, attr);
}
while(1) {
osThreadSuspend(osThreadGetId());
}
printf("\n=== Bye from Flipper Zero! ===\n\n");
return (int)exitcode;
}

28
core/furi.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include <cmsis_os2.h>
#include <furi/check.h>
#include <furi/event.h>
#include <furi/memmgr.h>
#include <furi/pubsub.h>
#include <furi/record.h>
#include <furi/stdglue.h>
#include <furi/value-expanders.h>
#include <furi/valuemutex.h>
#include <api-hal/api-gpio.h>
#include <api-hal/api-interrupt-mgr.h>
#include <api-hal.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define furiac_exit(ptr) osThreadExit()
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,6 @@
#include "check.h"
#include "api-hal-task.h"
#include <stdio.h>
void __furi_abort(void);
@@ -22,20 +23,21 @@ void __furi_check_debug(const char* file, int line, const char* function, const
if(task_is_isr_context()) {
printf(" in [ISR] context");
} else {
FuriApp* app = find_task(xTaskGetCurrentTaskHandle());
// FuriApp* app = find_task(xTaskGetCurrentTaskHandle());
if(app == NULL) {
printf(", in [main] context");
} else {
printf(", in [%s] app context", app->name);
}
// if(app == NULL) {
// printf(", in [main] context");
// } else {
// printf(", in [%s] app context", app->name);
// }
}
__furi_abort();
}
void __furi_abort(void) {
taskDISABLE_INTERRUPTS();
__disable_irq();
asm("bkpt 1");
while(1) {
}
}

View File

@@ -1,6 +1,8 @@
#pragma once
#include "flipper.h"
#ifdef __cplusplus
extern "C" {
#endif
// Find how to how get function's pretty name
#ifndef __FURI_CHECK_FUNC
@@ -38,4 +40,8 @@
// !NDEBUG
void __furi_check(void);
void __furi_check_debug(const char* file, int line, const char* function, const char* condition);
void __furi_check_debug(const char* file, int line, const char* function, const char* condition);
#ifdef __cplusplus
}
#endif

View File

@@ -2,7 +2,11 @@
#include <stdbool.h>
#include <stdint.h>
#include "cmsis_os.h"
#include <cmsis_os2.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
osSemaphoreId_t semaphore_id;
@@ -34,3 +38,7 @@ void wait_event(Event* event);
Waits with a timeout until the event is signalled.
*/
bool wait_event_with_timeout(Event* event, uint32_t timeout_ms);
#ifdef __cplusplus
}
#endif

View File

@@ -42,6 +42,23 @@ void* calloc(size_t count, size_t size) {
return ptr;
}
char* strdup(const char* s) {
if(s == NULL) {
return NULL;
}
size_t siz = strlen(s) + 1;
char* y = malloc(siz);
if(y != NULL) {
memcpy(y, s, siz);
} else {
return NULL;
}
return y;
}
size_t memmgr_get_free_heap(void) {
return xPortGetFreeHeapSize();
}

View File

@@ -1,5 +1,12 @@
#pragma once
#include <stddef.h>
#include <string.h>
#include "check.h"
#ifdef __cplusplus
extern "C" {
#endif
// define for test case "link against furi memmgr"
#define FURI_MEMMGR_GUARD 1
@@ -11,3 +18,13 @@ void* calloc(size_t count, size_t size);
size_t memmgr_get_free_heap(void);
size_t memmgr_get_minimum_free_heap(void);
inline static void* furi_alloc(size_t size) {
void* p = malloc(size);
furi_check(p);
return memset(p, 0, size);
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
#include "pubsub.h"
#include "flipper_v2.h"
#include <furi.h>
bool init_pubsub(PubSub* pubsub) {
// mutex without name,

View File

@@ -3,6 +3,10 @@
#include "cmsis_os.h"
#include "m-list.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
== PubSub ==
@@ -53,6 +57,10 @@ Use `notify_pubsub` to notify subscribers.
*/
bool notify_pubsub(PubSub* pubsub, void* arg);
#ifdef __cplusplus
}
#endif
/*
```C
@@ -84,4 +92,4 @@ void pubsub_test() {
}
}
```
*/
*/

123
core/furi/record.c Normal file
View File

@@ -0,0 +1,123 @@
#include "record.h"
#include "check.h"
#include <cmsis_os2.h>
#include <m-string.h>
#include <m-dict.h>
#define FURI_RECORD_FLAG_UPDATED 0x00000001U
DICT_SET_DEF(osThreadIdSet, uint32_t)
typedef struct {
void* data;
osThreadId_t owner;
osThreadIdSet_t holders;
} FuriRecord;
DICT_DEF2(FuriRecordDict, string_t, STRING_OPLIST, FuriRecord, M_POD_OPLIST)
typedef struct {
osMutexId_t records_mutex;
FuriRecordDict_t records;
} FuriRecordData;
FuriRecordData furi_record_data;
void furi_record_init() {
furi_record_data.records_mutex = osMutexNew(NULL);
FuriRecordDict_init(furi_record_data.records);
}
FuriRecord* furi_record_get_or_create(string_t name_str) {
FuriRecord* record = FuriRecordDict_get(furi_record_data.records, name_str);
if(!record) {
FuriRecord new_record;
new_record.data = NULL;
new_record.owner = NULL;
osThreadIdSet_init(new_record.holders);
FuriRecordDict_set_at(furi_record_data.records, name_str, new_record);
record = FuriRecordDict_get(furi_record_data.records, name_str);
}
return record;
}
void furi_record_create(const char* name, void* data) {
osThreadId_t thread_id = osThreadGetId();
string_t name_str;
string_init_set_str(name_str, name);
// Acquire mutex
furi_check(osMutexAcquire(furi_record_data.records_mutex, osWaitForever) == osOK);
FuriRecord* record = furi_record_get_or_create(name_str);
record->data = data;
record->owner = thread_id;
// For each holder set event flag
osThreadIdSet_it_t it;
for(osThreadIdSet_it(it, record->holders); !osThreadIdSet_end_p(it); osThreadIdSet_next(it)) {
osThreadFlagsSet((osThreadId_t)*osThreadIdSet_ref(it), FURI_RECORD_FLAG_UPDATED);
}
// Release mutex
furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
string_clear(name_str);
}
bool furi_record_destroy(const char* name) {
osThreadId_t thread_id = osThreadGetId();
string_t name_str;
string_init_set_str(name_str, name);
bool destroyed = false;
furi_check(osMutexAcquire(furi_record_data.records_mutex, osWaitForever) == osOK);
FuriRecord* record = FuriRecordDict_get(furi_record_data.records, name_str);
if(record && record->owner == thread_id && osThreadIdSet_size(record->holders) == 0) {
osThreadIdSet_clear(record->holders);
FuriRecordDict_erase(furi_record_data.records, name_str);
}
furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
string_clear(name_str);
return destroyed;
}
void* furi_record_open(const char* name) {
osThreadId_t thread_id = osThreadGetId();
string_t name_str;
string_init_set_str(name_str, name);
FuriRecord* record = NULL;
while(1) {
furi_check(osMutexAcquire(furi_record_data.records_mutex, osWaitForever) == osOK);
record = furi_record_get_or_create(name_str);
osThreadIdSet_push(record->holders, (uint32_t)thread_id);
furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
// Check if owner is already arrived
if(record->owner) {
break;
}
// Wait for thread flag to appear
osThreadFlagsWait(FURI_RECORD_FLAG_UPDATED, osFlagsWaitAny, osWaitForever);
}
string_clear(name_str);
return record->data;
}
void furi_record_close(const char* name) {
osThreadId_t thread_id = osThreadGetId();
string_t name_str;
string_init_set_str(name_str, name);
furi_check(osMutexAcquire(furi_record_data.records_mutex, osWaitForever) == osOK);
FuriRecord* record = FuriRecordDict_get(furi_record_data.records, name_str);
osThreadIdSet_erase(record->holders, (uint32_t)thread_id);
furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
string_clear(name_str);
}

44
core/furi/record.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Initialize record storage
* For internal use only.
*/
void furi_record_init();
/* Create record
* @param name - record name
* @param data - data pointer
* @note Thread safe. Create and destroy must be executed from the same thread.
*/
void furi_record_create(const char* name, void* data);
/* Destroy record
* @param name - record name
* @return true if successful, false if still have holders or thread is not owner.
* @note Thread safe. Create and destroy must be executed from the same thread.
*/
bool furi_record_destroy(const char* name);
/* Open record
* @param name - record name
* @return pointer to the record
* @note Thread safe. Open and close must be executed from the same thread.
* Suspends caller thread till record appear
*/
void* furi_record_open(const char* name);
/* Close record
* @param name - record name
* @note Thread safe. Open and close must be executed from the same thread.
*/
void furi_record_close(const char* name);
#ifdef __cplusplus
}
#endif

38
core/furi/stdglue.c Normal file
View File

@@ -0,0 +1,38 @@
#include "stdglue.h"
#include <main.h>
#include <stdio.h>
#include <string.h>
extern UART_HandleTypeDef DEBUG_UART;
static ssize_t stdout_write(void* _cookie, const char* data, size_t size) {
if(data == 0) {
/*
* This means that we should flush internal buffers. Since we
* don't we just return. (Remember, "handle" == -1 means that all
* handles should be flushed.)
*/
return 0;
}
HAL_UART_Transmit(&DEBUG_UART, (uint8_t*)data, (uint16_t)size, HAL_MAX_DELAY);
return size;
}
bool furi_stdglue_init() {
FILE* fp = fopencookie(
NULL,
"w",
(cookie_io_functions_t){
.read = NULL,
.write = stdout_write,
.seek = NULL,
.close = NULL,
});
setvbuf(fp, NULL, _IONBF, 0);
stdout = fp;
return true;
}

13
core/furi/stdglue.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
bool furi_stdglue_init();
#ifdef __cplusplus
}
#endif

View File

@@ -1,10 +1,13 @@
#pragma once
#include "flipper.h"
#include "valuemutex.h"
#include "pubsub.h"
#include "event.h"
#include "m-list.h"
#include <m-list.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
== Value composer ==
@@ -106,3 +109,7 @@ bool write_managed(ValueManager* managed, void* data, size_t len, uint32_t timeo
commit_managed works as `release_mutex` but send notify with current value.
*/
bool commit_managed(ValueManager* managed, void* value);
#ifdef __cplusplus
}
#endif

View File

@@ -1,4 +1,5 @@
#include "valuemutex.h"
#include <string.h>
bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) {

View File

@@ -1,6 +1,11 @@
#pragma once
#include "flipper.h"
#include <cmsis_os2.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
== ValueMutex ==
@@ -76,6 +81,10 @@ inline static bool read_mutex_block(ValueMutex* valuemutex, void* data, size_t l
return read_mutex(valuemutex, data, len, osWaitForever);
}
#ifdef __cplusplus
}
#endif
/*
Usage example
@@ -95,10 +104,7 @@ void provider_app(void* _p) {
flapp_exit(NULL);
}
if(furi_create("provider/example", (void*)&example_mutex)) {
printf("critical error\n");
flapp_exit(NULL);
}
furi_record_create("provider/example", (void*)&example_mutex);
// we are ready to provide record to other apps
flapp_ready();
@@ -123,7 +129,7 @@ void consumer_app(void* _p) {
// this app run after flapp_ready call in all requirements app
// open mutex value
ValueMutex* counter_mutex = furi_open("provider/example");
ValueMutex* counter_mutex = furi_record_open("provider/example");
if(counter_mutex == NULL) {
printf("critical error\n");
flapp_exit(NULL);

View File

@@ -1,190 +0,0 @@
#include "flipper.h"
#include "api-hal-task.h"
// TODO: this file contains printf, that not implemented on uC target
#ifdef FURI_DEBUG
#include <stdio.h>
#endif
#include <string.h>
#define INVALID_TASK_ID UINT16_MAX
static StaticTask_t task_info_buffer[MAX_TASK_COUNT];
static StackType_t stack_buffer[MAX_TASK_COUNT][DEFAULT_STACK_SIZE / 4];
static FuriApp task_buffer[MAX_TASK_COUNT];
static size_t current_buffer_idx = 0;
uint16_t furiac_get_task_id_by_name(const char* app_name) {
for(size_t i = 0; i < MAX_TASK_RECORDS; i++) {
if(strcmp(task_buffer[i].name, app_name) == 0) return i;
}
return INVALID_TASK_ID;
}
void furiac_wait_libs(const FlipperAppLibrary* libs) {
for(uint8_t i = 0; i < libs->count; i++) {
uint16_t app_id = furiac_get_task_id_by_name(libs->name[i]);
if(app_id == INVALID_TASK_ID) {
#ifdef FURI_DEBUG
printf("[FURIAC] Invalid library name %s\n", libs->name[i]);
#endif
} else {
while(!task_buffer[app_id].ready) {
#ifdef FURI_DEBUG
printf("[FURIAC] waiting for library \"%s\"\n", libs->name[i]);
#endif
osDelay(50);
}
}
}
}
// find task pointer by handle
FuriApp* find_task(TaskHandle_t handler) {
FuriApp* res = NULL;
for(size_t i = 0; i < MAX_TASK_COUNT; i++) {
if(task_equal(task_buffer[i].handler, handler)) {
res = &task_buffer[i];
}
}
return res;
}
FuriApp* furiac_start(FlipperApplication app, const char* name, void* param) {
#ifdef FURI_DEBUG
printf("[FURIAC] start %s\n", name);
#endif
// TODO check first free item (.handler == NULL) and use it
if(current_buffer_idx >= MAX_TASK_COUNT) {
// max task count exceed
#ifdef FURI_DEBUG
printf("[FURIAC] max task count exceed\n");
#endif
return NULL;
}
// application ready
task_buffer[current_buffer_idx].ready = false;
// create task on static stack memory
task_buffer[current_buffer_idx].handler = xTaskCreateStatic(
(TaskFunction_t)app,
(const char* const)name,
DEFAULT_STACK_SIZE / 4, // freertos specify stack size in words
(void* const)param,
tskIDLE_PRIORITY + 3, // normal priority
stack_buffer[current_buffer_idx],
&task_info_buffer[current_buffer_idx]);
// save task
task_buffer[current_buffer_idx].application = app;
task_buffer[current_buffer_idx].prev_name = NULL;
task_buffer[current_buffer_idx].prev = NULL;
task_buffer[current_buffer_idx].records_count = 0;
task_buffer[current_buffer_idx].name = name;
current_buffer_idx++;
return &task_buffer[current_buffer_idx - 1];
}
bool furiac_kill(FuriApp* app) {
#ifdef FURI_DEBUG
printf("[FURIAC] kill %s\n", app->name);
#endif
// check handler
if(app == NULL || app->handler == NULL) return false;
// kill task
vTaskDelete(app->handler);
// cleanup its registry
// TODO realy free memory
app->handler = NULL;
return true;
}
void furiac_exit(void* param) {
// get current task handler
FuriApp* current_task = find_task(xTaskGetCurrentTaskHandle());
// run prev
if(current_task != NULL) {
#ifdef FURI_DEBUG
printf("[FURIAC] exit %s\n", current_task->name);
#endif
if(current_task->prev != NULL) {
furiac_start(current_task->prev, current_task->prev_name, param);
} else {
#ifdef FURI_DEBUG
printf("[FURIAC] no prev\n");
#endif
}
// cleanup registry
// TODO realy free memory
current_task->handler = NULL;
}
// kill itself
vTaskDelete(NULL);
}
void furiac_switch(FlipperApplication app, char* name, void* param) {
// get current task handler
FuriApp* current_task = find_task(xTaskGetCurrentTaskHandle());
if(current_task == NULL) {
#ifdef FURI_DEBUG
printf("[FURIAC] no current task found\n");
#endif
}
#ifdef FURI_DEBUG
printf("[FURIAC] switch %s to %s\n", current_task->name, name);
#endif
// run next
FuriApp* next = furiac_start(app, name, param);
if(next != NULL) {
// save current application pointer as prev
next->prev = current_task->application;
next->prev_name = current_task->name;
// kill itself
vTaskDelete(NULL);
}
}
// set task to ready state
void furiac_ready() {
/*
TODO:
Currently i think that better way is to use application name
and restrict applications to "one task per application"
*/
FuriApp* app = find_task(xTaskGetCurrentTaskHandle());
if(app == NULL) {
#ifdef FURI_DEBUG
printf("[FURIAC] cannot find task to set ready state\n");
#endif
} else {
#ifdef FURI_DEBUG
printf("[FURIAC] task is ready\n");
#endif
app->ready = true;
}
}

View File

@@ -1,25 +0,0 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "log.h"
#include "flipper.h"
#define PRINT_STR_SIZE 64
void fuprintf(FuriRecordSubscriber* f, const char* format, ...) {
char buffer[PRINT_STR_SIZE];
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
furi_write(f, buffer, strlen(buffer));
}
FuriRecordSubscriber* get_default_log() {
return furi_open_deprecated("tty", false, false, NULL, NULL, NULL);
}

View File

@@ -1,6 +0,0 @@
#pragma once
#include "flipper.h"
FuriRecordSubscriber* get_default_log();
void fuprintf(FuriRecordSubscriber* f, const char* format, ...);

View File

@@ -1,138 +0,0 @@
#include "ring.h"
#include <flipper_v2.h>
struct Ring {
uint8_t* data;
size_t size;
volatile size_t read_ptr;
volatile size_t write_ptr;
};
Ring* ring_alloc(size_t size) {
Ring* ring = furi_alloc(sizeof(Ring));
ring->size = size + 1;
ring->data = furi_alloc(ring->size);
ring_clear(ring);
return ring;
}
void ring_free(Ring* ring) {
furi_assert(ring);
free(ring->data);
free(ring);
}
size_t ring_size(Ring* ring) {
furi_assert(ring);
return ring->size - 1;
}
inline static size_t ring_read_calculate(Ring* ring, size_t r, size_t w) {
if(w >= r) {
return w - r;
} else {
return ring->size - (r - w);
}
}
size_t ring_read_space(Ring* ring) {
furi_assert(ring);
const size_t r = ring->read_ptr;
const size_t w = ring->write_ptr;
return ring_read_calculate(ring, r, w);
}
inline static size_t ring_write_calculate(Ring* ring, size_t r, size_t w) {
if(r > w) {
return r - w - 1;
} else {
return ring->size - (r - w);
}
}
size_t ring_write_space(Ring* ring) {
furi_assert(ring);
const size_t r = ring->read_ptr;
const size_t w = ring->write_ptr;
return ring_write_calculate(ring, r, w);
}
size_t ring_push(Ring* ring, const uint8_t* data, size_t size) {
furi_assert(ring);
furi_assert(data);
const size_t r = ring->read_ptr;
size_t w = ring->write_ptr;
const size_t write_space = ring_write_calculate(ring, r, w);
if(write_space == 0) return 0;
const size_t to_write = size > write_space ? write_space : size;
size_t end, first, second;
end = w + to_write;
if(end > ring->size) {
first = ring->size - w;
second = end % ring->size;
} else {
first = to_write;
second = 0;
}
memcpy(ring->data + w, data, first);
w = (w + first) % ring->size;
if(second) {
memcpy(ring->data + w, data + first, second);
w = (w + second) % ring->size;
}
ring->write_ptr = w;
return to_write;
}
size_t ring_pull(Ring* ring, uint8_t* data, size_t size) {
furi_assert(ring);
furi_assert(data);
size_t r = ring->read_ptr;
const size_t w = ring->write_ptr;
const size_t read_space = ring_read_calculate(ring, r, w);
if(read_space == 0) return 0;
size_t to_read = size > read_space ? read_space : size;
size_t end, first, second;
end = r + to_read;
if(end > ring->size) {
first = ring->size - r;
second = end % ring->size;
} else {
first = to_read;
second = 0;
}
memcpy(data, ring->data + r, first);
r = (r + first) % ring->size;
if(second) {
memcpy(data + first, ring->data + r, second);
r = (r + second) % ring->size;
}
ring->read_ptr = r;
return to_read;
}
void ring_clear(Ring* ring) {
furi_assert(ring);
ring->read_ptr = 0;
ring->write_ptr = 0;
}

View File

@@ -1,22 +0,0 @@
#pragma once
#include <stdint.h>
#include <string.h>
typedef struct Ring Ring;
Ring* ring_alloc(size_t size);
void ring_free(Ring* ring);
size_t ring_size(Ring* ring);
size_t ring_read_space(Ring* ring);
size_t ring_write_space(Ring* ring);
size_t ring_push(Ring* ring, const uint8_t* data, size_t size);
size_t ring_pull(Ring* ring, uint8_t* data, size_t size);
void ring_clear(Ring* ring);

View File

@@ -1,57 +0,0 @@
#define _GNU_SOURCE
#include <stdio.h>
#include "flipper.h"
#include "main.h"
extern UART_HandleTypeDef DEBUG_UART;
void handle_uart_write(const void* data, size_t size, void* ctx) {
HAL_UART_Transmit(&DEBUG_UART, (uint8_t*)data, (uint16_t)size, HAL_MAX_DELAY);
}
static ssize_t stdout_write(void* _cookie, const char* buf, size_t n) {
FuriRecordSubscriber* log = pvTaskGetThreadLocalStoragePointer(NULL, 0);
if(log == NULL) {
log = furi_open_deprecated("tty", false, false, NULL, NULL, NULL);
if(log == NULL) {
return -1;
}
vTaskSetThreadLocalStoragePointer(NULL, 0, log);
}
if(buf == 0) {
/*
* This means that we should flush internal buffers. Since we
* don't we just return. (Remember, "handle" == -1 means that all
* handles should be flushed.)
*/
return 0;
}
furi_write(log, buf, n);
return n;
}
bool register_tty_uart() {
if(!furi_create_deprecated("tty", NULL, 0)) {
return false;
}
if(furi_open_deprecated("tty", false, false, handle_uart_write, NULL, NULL) == NULL) {
return false;
}
FILE* fp = fopencookie(
NULL,
"w",
(cookie_io_functions_t){
.read = NULL,
.write = stdout_write,
.seek = NULL,
.close = NULL,
});
setvbuf(fp, NULL, _IONBF, 0);
stdout = fp;
return true;
}

View File

@@ -1,5 +0,0 @@
#pragma once
#include <stdbool.h>
bool register_tty_uart();