* furiac start and thread create implementation"

* create and kill task

* rename debug, add header

* remove write.c

* kill itself

* furi exit/switch

* success switch and exit

* WIP furi records

* add furi record interface

* rename furi app control file

* record implementation in progress

* wip furi implementation

* add automatic tests for FURI AC

* differ build tests

* small changes

* FURI record tests description

* change furi statuses

* FURI record test blank

* exit after all application ends

* delay: print then wait

* fix FURI implementatnion building

* pipe record test

* concurrent access

* uncomplete mute-test

* update FURI documentation
This commit is contained in:
coreglitch
2020-08-24 21:31:22 +06:00
committed by GitHub
parent 04035ce52d
commit 1759787334
21 changed files with 1448 additions and 47 deletions

View File

@@ -2,23 +2,28 @@
#include <stdio.h>
extern "C" {
FILE* get_debug();
#include "startup.h"
#include "furi.h"
#include "debug.h"
}
extern "C" void app() {
FILE* debug_uart = get_debug();
// FURI startup
FuriApp* handlers[sizeof(FLIPPER_STARTUP)/sizeof(FLIPPER_STARTUP[0])];
fprintf(debug_uart, "hello Flipper!\n");
GpioPin red_led = {LED_RED_GPIO_Port, LED_RED_Pin};
app_gpio_init(red_led, GpioModeOutput);
while(1) {
delay(100);
app_gpio_write(red_led, true);
delay(100);
app_gpio_write(red_led, false);
for(size_t i = 0; i < sizeof(FLIPPER_STARTUP)/sizeof(FLIPPER_STARTUP[0]); i++) {
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 < sizeof(FLIPPER_STARTUP)/sizeof(FLIPPER_STARTUP[0]); i++) {
if(handlers[i]->handler != NULL) {
is_alive = true;
}
}
delay(500);
// TODO add deferred event queue here
} while(is_alive);
}

View File

@@ -18,7 +18,7 @@ ssize_t uart_write(void* cookie, const char * buffer, size_t size) {
}
FILE* get_debug() {
FILE* fp = fopencookie(NULL,"w+", (cookie_io_functions_t){
FILE* fp = fopencookie(NULL, "w+", (cookie_io_functions_t){
.read = NULL,
.write = uart_write,
.seek = NULL,

1
core/debug.h Normal file
View File

@@ -0,0 +1 @@
FILE* get_debug();

View File

@@ -1,8 +1,15 @@
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "flipper_hal.h"
#include "cmsis_os.h"
#include "furi.h"
#ifdef __cplusplus
}
#endif
// Arduino defines

View File

@@ -0,0 +1,216 @@
#include "furi.h"
#include "cmsis_os.h"
#include <string.h>
#define DEBUG
#ifdef DEBUG
#include <stdio.h>
#endif
#define MAX_RECORD_COUNT 32
static FuriRecord records[MAX_RECORD_COUNT];
static size_t current_buffer_idx = 0;
// 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;
}
bool furi_create(const char* name, void* value, size_t size) {
#ifdef DEBUG
printf("[FURI] creating %s record\n", name);
#endif
if(current_buffer_idx >= MAX_RECORD_COUNT) {
// max record count exceed
#ifdef DEBUG
printf("[FURI] 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;
}
return true;
}
FuriRecordHandler furi_open(
const char* name,
bool solo,
bool no_mute,
FlipperRecordCallback value_callback,
FlipperRecordStateCallback state_callback
) {
#ifdef 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 DEBUG
printf("[FURI] cannot find record %s\n", name);
#endif
FuriRecordHandler res = {.record = NULL, .subscriber = NULL};
return res;
}
// allocate subscriber
FuriRecordSubscriber* subscriber = NULL;
for(size_t i = 0; i < MAX_RECORD_SUBSCRIBERS; i++) {
if(!records[current_buffer_idx].subscribers[i].allocated) {
subscriber = &records[current_buffer_idx].subscribers[i];
break;
}
}
if(subscriber == NULL) {
// cannot add subscriber (full)
#ifdef DEBUG
printf("[FURI] cannot add subscriber (full)\n");
#endif
FuriRecordHandler res = {.record = NULL, .subscriber = NULL};
return res;
}
// 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;
// register record in application
FuriApp* current_task = find_task(xTaskGetCurrentTaskHandle());
current_task->records[current_task->records_count] = record;
current_task->records_count++;
FuriRecordHandler res = {.record = record, .subscriber = subscriber};
return res;
}
void furi_close(FuriRecordHandler* handler) {
#ifdef DEBUG
printf("[FURI] closing %s record\n", handler->record->name);
#endif
// deallocate subscriber
handler->subscriber->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(FuriRecordHandler* 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);
}
}
}
}
void* furi_take(FuriRecordHandler* handler) {
// take mutex
return handler->record->value;
}
void furi_give(FuriRecordHandler* handler) {
// release mutex
}
bool furi_read(FuriRecordHandler* handler, void* value, size_t size) {
#ifdef 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_give(handler);
furi_notify(handler, value, size);
return true;
}
bool furi_write(FuriRecordHandler* handler, const void* value, size_t size) {
#ifdef DEBUG
printf("[FURI] write to %s\n", handler->record->name);
#endif
if(handler == NULL || handler->record == NULL || value == NULL) return false;
// check if closed
if(!handler->subscriber->allocated) return false;
if(handler->record->value != NULL && size > handler->record->size) return false;
// check mute
if(
handler->record->mute_counter != handler->subscriber->mute_counter
&& !handler->subscriber->no_mute
) return false;
if(handler->record->value != NULL) {
// real write to value
furi_take(handler);
memcpy(handler->record->value, value, size);
furi_give(handler);
// notify subscribers
furi_notify(handler, handler->record->value, handler->record->size);
} else {
furi_notify(handler, value, size);
}
return true;
}

View File

@@ -0,0 +1,158 @@
#pragma once
#include "cmsis_os.h"
#include <stdbool.h>
#include <stdint.h>
#define MAX_TASK_RECORDS 8
#define MAX_RECORD_SUBSCRIBERS 8
/// application is just a function
typedef void(*FlipperApplication)(void*);
/// pointer to value callback function
typedef void(*FlipperRecordCallback)(const void*, size_t);
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);
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;
} FuriRecordSubscriber;
/// FURI record handler
typedef struct {
const char* name;
void* value;
size_t size;
StaticSemaphore_t mutex_buffer;
SemaphoreHandle_t mutex;
uint8_t mute_counter;
FuriRecordSubscriber subscribers[MAX_RECORD_SUBSCRIBERS];
} FuriRecord;
/// FURI record handler for use after open
typedef struct {
FuriRecord* record; ///< full record (for read/write/take/give value)
FuriRecordSubscriber* subscriber; ///< current handler info
} FuriRecordHandler;
/// 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
} FuriApp;
/*!
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);
/*!
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(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.
*/
FuriRecordHandler furi_open(
const char* name,
bool solo,
bool no_mute,
FlipperRecordCallback value_callback,
FlipperRecordStateCallback state_callback
);
/*!
*/
void furi_close(FuriRecordHandler* 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(FuriRecordHandler* 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(FuriRecordHandler* 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(FuriRecordHandler* record);
/*!
unlock value mutex.
*/
void furi_give(FuriRecordHandler* record);

139
core/furi_ac.c Normal file
View File

@@ -0,0 +1,139 @@
#include "furi.h"
#include "cmsis_os.h"
#define DEBUG
#ifdef DEBUG
#include <stdio.h>
#endif
#define DEFAULT_STACK_SIZE 1024 // Stack size in bytes
#define MAX_TASK_COUNT 8
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;
// 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 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 DEBUG
printf("[FURIAC] max task count exceed\n");
#endif
return NULL;
}
// 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 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 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 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 DEBUG
printf("[FURIAC] no current task found\n");
#endif
}
#ifdef 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);
}
}