[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:
@@ -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);
|
@@ -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;
|
||||
}
|
@@ -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);
|
@@ -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;
|
||||
}
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
48
core/app.cpp
48
core/app.cpp
@@ -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;
|
||||
}
|
@@ -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)
|
||||
|
@@ -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
|
@@ -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;
|
@@ -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;
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
@@ -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
36
core/furi.c
Normal 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
28
core/furi.h
Normal 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
|
@@ -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) {
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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
|
@@ -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();
|
||||
}
|
@@ -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
|
@@ -1,5 +1,5 @@
|
||||
#include "pubsub.h"
|
||||
#include "flipper_v2.h"
|
||||
#include <furi.h>
|
||||
|
||||
bool init_pubsub(PubSub* pubsub) {
|
||||
// mutex without name,
|
@@ -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
123
core/furi/record.c
Normal 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
44
core/furi/record.h
Normal 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
38
core/furi/stdglue.c
Normal 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
13
core/furi/stdglue.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool furi_stdglue_init();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -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
|
@@ -1,4 +1,5 @@
|
||||
#include "valuemutex.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) {
|
@@ -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);
|
190
core/furi_ac.c
190
core/furi_ac.c
@@ -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;
|
||||
}
|
||||
}
|
25
core/log.c
25
core/log.c
@@ -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);
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "flipper.h"
|
||||
|
||||
FuriRecordSubscriber* get_default_log();
|
||||
void fuprintf(FuriRecordSubscriber* f, const char* format, ...);
|
138
core/ring.c
138
core/ring.c
@@ -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;
|
||||
}
|
22
core/ring.h
22
core/ring.h
@@ -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);
|
@@ -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;
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool register_tty_uart();
|
Reference in New Issue
Block a user