[FL-781] FURI, CLI, stdlib: stdout hooks, integration between subsystems, uniform printf usage (#311)

* FURI stdglue: stdout hooks, local and global, ISR safe printf. Uniform newlines for terminal/debug output. Power: prevent sleep while core 2 has not started.
* Furi record, stdglue: check mutex allocation
* remove unused test
* Furi stdglue: buferized output, dynamically allocated state. Furi record: dynamically allocated state. Input dump: proper line ending. Hal VCP: dynamically allocated state.
* Interrupt manager: explicitly init list.
* Makefile: cleanup rules, fix broken dfu upload. F4: add compiler stack protection options.
* BLE: call debug uart callback on transmission complete
* FreeRTOS: add configUSE_NEWLIB_REENTRANT
* API HAL Timebase: fix issue with idle thread stack corruption caused by systick interrupt. BT: cleanup debug info output. FreeRTOS: disable reentry for newlib.
* F4: update stack protection CFLAGS to match used compiller
* F4: disable compiller stack protection because of incompatibility with current compiller
* Makefile: return openocd logs to gdb
* BLE: fixed pin, moar power, ble trace info.
* Prevent sleep when connection is active
* Makefile: return serial port to upload rule, add workaround for mac os
* Furi: prevent usage of stack for cmsis functions.
* F4: add missing includes, add debugger breakpoints
* Applications: per app stack size.
* Furi: honor kernel state in stdglue
* FreeRTOS: remove unused hooks
* Cleanup and format sources

Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
This commit is contained in:
あく
2021-01-29 03:09:33 +03:00
committed by GitHub
parent fd5f694758
commit 584c0962d8
52 changed files with 517 additions and 389 deletions

View File

@@ -120,4 +120,4 @@ void api_interrupt_call(InterruptType type, void* hw) {
}
}
}
}
}

View File

@@ -17,20 +17,22 @@ void furi_init() {
int systemd() {
furi_init();
printf("[systemd] furi initialized\r\n");
// FURI startup
for(size_t i = 0; i < FLIPPER_SERVICES_size(); i++) {
printf("[systemd] starting service %s\r\n", FLIPPER_SERVICES[i].name);
osThreadAttr_t* attr = furi_alloc(sizeof(osThreadAttr_t));
attr->name = FLIPPER_SERVICES[i].name;
attr->stack_size = 1024;
attr->stack_size = FLIPPER_SERVICES[i].stack_size;
osThreadNew(FLIPPER_SERVICES[i].app, NULL, attr);
}
printf("[systemd] all services started\r\n");
while(1) {
osThreadSuspend(osThreadGetId());
}
printf("\n=== Bye from Flipper Zero! ===\n\n");
printf("[systemd] === Bye from Flipper Zero! ===\r\n");
return (int)exitcode;
}

View File

@@ -5,10 +5,7 @@ bool init_pubsub(PubSub* pubsub) {
// mutex without name,
// no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
// with dynamic memory allocation
const osMutexAttr_t value_mutex_attr = {
.name = NULL, .attr_bits = 0, .cb_mem = NULL, .cb_size = 0U};
pubsub->mutex = osMutexNew(&value_mutex_attr);
pubsub->mutex = osMutexNew(NULL);
if(pubsub->mutex == NULL) return false;
// construct list

View File

@@ -1,5 +1,6 @@
#include "record.h"
#include "check.h"
#include "memmgr.h"
#include <cmsis_os2.h>
#include <m-string.h>
@@ -22,34 +23,38 @@ typedef struct {
FuriRecordDict_t records;
} FuriRecordData;
FuriRecordData furi_record_data;
static FuriRecordData* furi_record_data = NULL;
void furi_record_init() {
furi_record_data.records_mutex = osMutexNew(NULL);
FuriRecordDict_init(furi_record_data.records);
furi_record_data = furi_alloc(sizeof(FuriRecordData));
furi_record_data->records_mutex = osMutexNew(NULL);
furi_check(furi_record_data->records_mutex);
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);
assert(furi_record_data);
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);
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) {
assert(furi_record_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);
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;
@@ -60,31 +65,34 @@ void furi_record_create(const char* name, void* data) {
osThreadFlagsSet((osThreadId_t)*osThreadIdSet_ref(it), FURI_RECORD_FLAG_UPDATED);
}
// Release mutex
furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
furi_check(osMutexRelease(furi_record_data->records_mutex) == osOK);
string_clear(name_str);
}
bool furi_record_destroy(const char* name) {
assert(furi_record_data);
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);
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);
FuriRecordDict_erase(furi_record_data->records, name_str);
destroyed = true;
}
furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
furi_check(osMutexRelease(furi_record_data->records_mutex) == osOK);
string_clear(name_str);
return destroyed;
}
void* furi_record_open(const char* name) {
assert(furi_record_data);
osThreadId_t thread_id = osThreadGetId();
string_t name_str;
@@ -92,10 +100,10 @@ void* furi_record_open(const char* name) {
FuriRecord* record = NULL;
while(1) {
furi_check(osMutexAcquire(furi_record_data.records_mutex, osWaitForever) == osOK);
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);
furi_check(osMutexRelease(furi_record_data->records_mutex) == osOK);
// Check if owner is already arrived
if(record->owner) {
break;
@@ -109,15 +117,16 @@ void* furi_record_open(const char* name) {
}
void furi_record_close(const char* name) {
assert(furi_record_data);
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);
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);
furi_check(osMutexRelease(furi_record_data->records_mutex) == osOK);
string_clear(name_str);
}

View File

@@ -1,12 +1,57 @@
#include "stdglue.h"
#include <main.h>
#include "check.h"
#include "memmgr.h"
#include <main.h>
#include <cmsis_os2.h>
#include <stdio.h>
#include <string.h>
#include <m-dict.h>
extern UART_HandleTypeDef DEBUG_UART;
DICT_DEF2(
FuriStdglueCallbackDict,
uint32_t,
M_DEFAULT_OPLIST,
FuriStdglueWriteCallback,
M_PTR_OPLIST)
typedef struct {
osMutexId_t mutex;
FuriStdglueCallbackDict_t global_outputs;
FuriStdglueCallbackDict_t thread_outputs;
} FuriStdglue;
static FuriStdglue* furi_stdglue = NULL;
static ssize_t stdout_write(void* _cookie, const char* data, size_t size) {
assert(furi_stdglue);
osKernelState_t state = osKernelGetState();
osThreadId_t thread_id = osThreadGetId();
if(state == osKernelRunning && thread_id &&
osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK) {
// We are in the thread context
// Handle global callbacks
FuriStdglueCallbackDict_it_t it;
for(FuriStdglueCallbackDict_it(it, furi_stdglue->global_outputs);
!FuriStdglueCallbackDict_end_p(it);
FuriStdglueCallbackDict_next(it)) {
osThreadId_t it_thread = (osThreadId_t)FuriStdglueCallbackDict_ref(it)->key;
FuriStdglueWriteCallback it_callback = FuriStdglueCallbackDict_ref(it)->value;
if(thread_id != it_thread) {
it_callback(_cookie, data, size);
}
}
// Handle thread callbacks
FuriStdglueWriteCallback* callback_ptr =
FuriStdglueCallbackDict_get(furi_stdglue->thread_outputs, (uint32_t)thread_id);
if(callback_ptr) {
(*callback_ptr)(_cookie, data, size);
}
furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
}
// Flush
if(data == 0) {
/*
* This means that we should flush internal buffers. Since we
@@ -15,13 +60,20 @@ static ssize_t stdout_write(void* _cookie, const char* data, size_t size) {
*/
return 0;
}
// Debug uart
HAL_UART_Transmit(&DEBUG_UART, (uint8_t*)data, (uint16_t)size, HAL_MAX_DELAY);
// All data consumed
return size;
}
bool furi_stdglue_init() {
void furi_stdglue_init() {
furi_stdglue = furi_alloc(sizeof(FuriStdglue));
// Init outputs structures
furi_stdglue->mutex = osMutexNew(NULL);
furi_check(furi_stdglue->mutex);
FuriStdglueCallbackDict_init(furi_stdglue->global_outputs);
FuriStdglueCallbackDict_init(furi_stdglue->thread_outputs);
// Prepare and set stdout descriptor
FILE* fp = fopencookie(
NULL,
"w",
@@ -31,8 +83,42 @@ bool furi_stdglue_init() {
.seek = NULL,
.close = NULL,
});
setvbuf(fp, NULL, _IONBF, 0);
setvbuf(fp, NULL, _IOLBF, 0);
stdout = fp;
return true;
}
bool furi_stdglue_set_global_stdout_callback(FuriStdglueWriteCallback callback) {
assert(furi_stdglue);
osThreadId_t thread_id = osThreadGetId();
if(thread_id) {
furi_check(osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK);
if(callback) {
FuriStdglueCallbackDict_set_at(
furi_stdglue->global_outputs, (uint32_t)thread_id, callback);
} else {
FuriStdglueCallbackDict_erase(furi_stdglue->global_outputs, (uint32_t)thread_id);
}
furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
return true;
} else {
return false;
}
}
bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback) {
assert(furi_stdglue);
osThreadId_t thread_id = osThreadGetId();
if(thread_id) {
furi_check(osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK);
if(callback) {
FuriStdglueCallbackDict_set_at(
furi_stdglue->thread_outputs, (uint32_t)thread_id, callback);
} else {
FuriStdglueCallbackDict_erase(furi_stdglue->thread_outputs, (uint32_t)thread_id);
}
furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
return true;
} else {
return false;
}
}

View File

@@ -1,12 +1,36 @@
#pragma once
#include <stdbool.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
bool furi_stdglue_init();
/* Write callback
* @param _cookie - pointer to cookie (see stdio gnu extension)
* @param data - pointer to data
* @param size - data size
* @warnign your handler must consume everything
*/
typedef void (*FuriStdglueWriteCallback)(void* _cookie, const char* data, size_t size);
/* Initialized std library glue code */
void furi_stdglue_init();
/* Set global STDOUT callback
* @param callback - callback or NULL to clear
* @return true on success, otherwise fail
* @warning function is thread aware, use this API from the same thread
*/
bool furi_stdglue_set_global_stdout_callback(FuriStdglueWriteCallback callback);
/* Set STDOUT callback for your thread
* @param callback - callback or NULL to clear
* @return true on success, otherwise fail
* @warning function is thread aware, use this API from the same thread
*/
bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback);
#ifdef __cplusplus
}

View File

@@ -10,10 +10,7 @@ bool init_composer(ValueComposer* composer, void* value) {
// mutex without name,
// no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
// with dynamic memory allocation
const osMutexAttr_t value_mutex_attr = {
.name = NULL, .attr_bits = 0, .cb_mem = NULL, .cb_size = 0U};
composer->mutex = osMutexNew(&value_mutex_attr);
composer->mutex = osMutexNew(NULL);
if(composer->mutex == NULL) return false;
if(!init_event(&composer->request)) return false;

View File

@@ -6,10 +6,7 @@ bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) {
// mutex without name,
// no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
// with dynamic memory allocation
const osMutexAttr_t value_mutext_attr = {
.name = NULL, .attr_bits = 0, .cb_mem = NULL, .cb_size = 0U};
valuemutex->mutex = osMutexNew(&value_mutext_attr);
valuemutex->mutex = osMutexNew(NULL);
if(valuemutex->mutex == NULL) return false;
valuemutex->value = value;