Rename furi to tactility-core (#10)

* renamed module

* renamed code

* more renames

* cleanup
This commit is contained in:
Ken Van Hoeylandt 2024-01-13 22:12:40 +01:00 committed by GitHub
parent 64a01df750
commit 069416eee5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
106 changed files with 2992 additions and 3347 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
add_definitions(-DFURI_DEBUG)
add_definitions(-DTT_DEBUG)
set(COMPONENTS main)
set(EXTRA_COMPONENT_DIRS "boards" "components")

View File

@ -40,8 +40,8 @@ Other configurations can be supported, but they require you to set up the driver
Until there is proper documentation, here are some pointers:
- Sample application: [bootstrap](main/src/main.c) and [app](main/src/hello_world/hello_world.c)
- [Tactility](./components/tactility/): the main platform with default services and apps
- [Furi](./components/furi/): the core platform code, based on Flipper Zero firmware
- [Tactility](./components/tactility): The main platform with default services and apps.
- [Tactility Core](./components/tactility-core): The core platform code.
## Building Firmware

View File

@ -22,5 +22,5 @@ void lilygo_tdeck_bootstrap() {
tdeck_power_on();
// Give keyboard's ESP time to boot
// It uses I2C and seems to interfere with the touch driver
furi_delay_ms(500);
tt_delay_ms(500);
}

View File

@ -1,7 +0,0 @@
## Description
This code is derived from the "Furi" code in the [Flipper Zero firmware](https://github.com/flipperdevices/flipperzero-firmware/).
## License
[GNU General Public License Version 3](LICENSE.md)

View File

@ -1,19 +0,0 @@
#pragma once
#include "furi.h"
typedef FuriEventFlag* FuriApiLock;
#define API_LOCK_EVENT (1U << 0)
#define api_lock_alloc_locked() furi_event_flag_alloc()
#define api_lock_wait_unlock(_lock) \
furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever)
#define api_lock_free(_lock) furi_event_flag_free(_lock)
#define api_lock_unlock(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT)
#define api_lock_wait_unlock_and_free(_lock) \
api_lock_wait_unlock(_lock); \
api_lock_free(_lock);

View File

@ -1,23 +0,0 @@
#pragma once
#include "app_manifest.h"
#ifdef __cplusplus
extern "C" {
#endif
extern const AppManifest* const INTERNAL_APP_MANIFESTS[];
extern const size_t INTERNAL_APP_COUNT;
typedef void (*AppManifestCallback)(const AppManifest*, void* context);
void app_manifest_registry_init();
void app_manifest_registry_add(const AppManifest _Nonnull* manifest);
void app_manifest_registry_remove(const AppManifest _Nonnull* manifest);
const AppManifest _Nullable* app_manifest_registry_find_by_id(const char* id);
void app_manifest_registry_for_each(AppManifestCallback callback, void* _Nullable context);
void app_manifest_registry_for_each_of_type(AppType type, void* _Nullable context, AppManifestCallback callback);
#ifdef __cplusplus
}
#endif

View File

@ -1,34 +0,0 @@
/**
* @brief key-value storage for general purpose.
* Maps strings on a fixed set of data types.
*/
#pragma once
#include <stdio.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Bundle;
Bundle bundle_alloc();
Bundle bundle_alloc_copy(Bundle source);
void bundle_free(Bundle bundle);
bool bundle_get_bool(Bundle bundle, const char* key);
int bundle_get_int(Bundle bundle, const char* key);
const char* bundle_get_string(Bundle bundle, const char* key);
bool bundle_opt_bool(Bundle bundle, const char* key, bool* out);
bool bundle_opt_int(Bundle bundle, const char* key, int* out);
bool bundle_opt_string(Bundle bundle, const char* key, char** out);
void bundle_put_bool(Bundle bundle, const char* key, bool value);
void bundle_put_int(Bundle bundle, const char* key, int value);
void bundle_put_string(Bundle bundle, const char* key, const char* value);
#ifdef __cplusplus
}
#endif

View File

@ -1,96 +0,0 @@
#include "check.h"
#include "furi_core_defines.h"
#include "furi_hal_console.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <stdlib.h>
static void __furi_put_uint32_as_text(uint32_t data) {
char tmp_str[] = "-2147483648";
itoa(data, tmp_str, 10);
furi_hal_console_puts(tmp_str);
}
static void __furi_print_stack_info() {
furi_hal_console_puts("\r\n\tstack watermark: ");
__furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4);
}
static void __furi_print_heap_info() {
furi_hal_console_puts("\r\n\theap total: ");
__furi_put_uint32_as_text(heap_caps_get_total_size(MALLOC_CAP_DEFAULT));
furi_hal_console_puts("\r\n\theap free: ");
__furi_put_uint32_as_text(heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
furi_hal_console_puts("\r\n\theap min free: ");
__furi_put_uint32_as_text(heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT));
}
static void __furi_print_name(bool isr) {
if (isr) {
furi_hal_console_puts("[ISR ");
__furi_put_uint32_as_text(__get_IPSR());
furi_hal_console_puts("] ");
} else {
const char* name = pcTaskGetName(NULL);
if (name == NULL) {
furi_hal_console_puts("[main] ");
} else {
furi_hal_console_puts("[");
furi_hal_console_puts(name);
furi_hal_console_puts("] ");
}
}
}
FURI_NORETURN void __furi_crash_implementation() {
__disable_irq();
// GET_MESSAGE_AND_STORE_REGISTERS();
bool isr = FURI_IS_IRQ_MODE();
furi_hal_console_puts("\r\n\033[0;31m[CRASH]");
__furi_print_name(isr);
if (!isr) {
__furi_print_stack_info();
}
__furi_print_heap_info();
// Check if debug enabled by DAP
// https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en
// bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk;
#ifdef FURI_NDEBUG
if (debug) {
#endif
furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n");
furi_hal_console_puts("\033[0m\r\n");
// furi_hal_debug_enable();
esp_system_abort("crash");
#ifdef FURI_NDEBUG
} else {
uint32_t ptr = (uint32_t)__furi_check_message;
if (ptr < FLASH_BASE || ptr > (FLASH_BASE + FLASH_SIZE)) {
ptr = (uint32_t) "Check serial logs";
}
furi_hal_rtc_set_fault_data(ptr);
furi_hal_console_puts("\r\nRebooting system.\r\n");
furi_hal_console_puts("\033[0m\r\n");
esp_system_abort("crash");
}
#endif
__builtin_unreachable();
}
FURI_NORETURN void __furi_halt_implementation() {
__disable_irq();
bool isr = FURI_IS_IRQ_MODE();
furi_hal_console_puts("\r\n\033[0;31m[HALT]");
__furi_print_name(isr);
furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n");
furi_hal_console_puts("\033[0m\r\n");
__builtin_unreachable();
}

View File

@ -1,5 +0,0 @@
#pragma once
typedef struct {
} Context;

View File

@ -1,22 +0,0 @@
#pragma once
#include <stdio.h>
#include <stdbool.h>
#ifndef FURI_CRITICAL_ENTER
#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter();
#endif
#ifndef FURI_CRITICAL_EXIT
#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info);
#endif
typedef struct {
uint32_t isrm;
bool from_isr;
bool kernel_running;
} __FuriCriticalInfo;
__FuriCriticalInfo __furi_critical_enter(void);
void __furi_critical_exit(__FuriCriticalInfo info);

View File

@ -1,71 +0,0 @@
/**
* @file event_flag.h
* Furi Event Flag
*/
#pragma once
#include "furi_core_types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void FuriEventFlag;
/** Allocate FuriEventFlag
*
* @return pointer to FuriEventFlag
*/
FuriEventFlag* furi_event_flag_alloc();
/** Deallocate FuriEventFlag
*
* @param instance pointer to FuriEventFlag
*/
void furi_event_flag_free(FuriEventFlag* instance);
/** Set flags
*
* @param instance pointer to FuriEventFlag
* @param[in] flags The flags
*
* @return Resulting flags or error (FuriStatus)
*/
uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags);
/** Clear flags
*
* @param instance pointer to FuriEventFlag
* @param[in] flags The flags
*
* @return Resulting flags or error (FuriStatus)
*/
uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags);
/** Get flags
*
* @param instance pointer to FuriEventFlag
*
* @return Resulting flags
*/
uint32_t furi_event_flag_get(FuriEventFlag* instance);
/** Wait flags
*
* @param instance pointer to FuriEventFlag
* @param[in] flags The flags
* @param[in] options The option flags
* @param[in] timeout The timeout
*
* @return Resulting flags or error (FuriStatus)
*/
uint32_t furi_event_flag_wait(
FuriEventFlag* instance,
uint32_t flags,
uint32_t options,
uint32_t timeout
);
#ifdef __cplusplus
}
#endif

View File

@ -1,3 +0,0 @@
#pragma once
#define FURI_CONFIG_THREAD_MAX_PRIORITIES (32)

View File

@ -1,40 +0,0 @@
#pragma once
#include "freertos/portmacro.h"
#include "furi_extra_defines.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <cmsis_compiler.h>
#ifndef FURI_WARN_UNUSED
#define FURI_WARN_UNUSED __attribute__((warn_unused_result))
#endif
#ifndef FURI_WEAK
#define FURI_WEAK __attribute__((weak))
#endif
#ifndef FURI_PACKED
#define FURI_PACKED __attribute__((packed))
#endif
// Used by portENABLE_INTERRUPTS and portDISABLE_INTERRUPTS?
#ifndef FURI_IS_IRQ_MODE
#define FURI_IS_IRQ_MODE() (xPortInIsrContext() == pdTRUE)
#endif
#ifndef FURI_IS_ISR
#define FURI_IS_ISR() (FURI_IS_IRQ_MODE())
#endif
#ifndef FURI_CHECK_RETURN
#define FURI_CHECK_RETURN __attribute__((__warn_unused_result__))
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,44 +0,0 @@
#pragma once
#include <furi_config.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
FuriWaitForever = 0xFFFFFFFFU,
} FuriWait;
typedef enum {
FuriFlagWaitAny = 0x00000000U, ///< Wait for any flag (default).
FuriFlagWaitAll = 0x00000001U, ///< Wait for all flags.
FuriFlagNoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for.
FuriFlagError = 0x80000000U, ///< Error indicator.
FuriFlagErrorUnknown = 0xFFFFFFFFU, ///< FuriStatusError (-1).
FuriFlagErrorTimeout = 0xFFFFFFFEU, ///< FuriStatusErrorTimeout (-2).
FuriFlagErrorResource = 0xFFFFFFFDU, ///< FuriStatusErrorResource (-3).
FuriFlagErrorParameter = 0xFFFFFFFCU, ///< FuriStatusErrorParameter (-4).
FuriFlagErrorISR = 0xFFFFFFFAU, ///< FuriStatusErrorISR (-6).
} FuriFlag;
typedef enum {
FuriStatusOk = 0, ///< Operation completed successfully.
FuriStatusError =
-1, ///< Unspecified RTOS error: run-time error but no other error message fits.
FuriStatusErrorTimeout = -2, ///< Operation not completed within the timeout period.
FuriStatusErrorResource = -3, ///< Resource not available.
FuriStatusErrorParameter = -4, ///< Parameter error.
FuriStatusErrorNoMemory =
-5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.
FuriStatusErrorISR =
-6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
FuriStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
} FuriStatus;
#ifdef __cplusplus
}
#endif

View File

@ -1,81 +0,0 @@
#include "furi_hal_console.h"
#include "furi_core.h"
#include "furi_string.h"
#include "esp_log.h" // TODO remove
#define TAG "FuriHalConsole"
typedef struct {
bool alive;
FuriHalConsoleTxCallback tx_callback;
void* tx_callback_context;
} FuriHalConsole;
FuriHalConsole furi_hal_console = {
.alive = false,
.tx_callback = NULL,
.tx_callback_context = NULL,
};
void furi_hal_console_init() {
furi_hal_console.alive = true;
}
void furi_hal_console_enable() {
furi_hal_console.alive = true;
}
void furi_hal_console_disable() {
furi_hal_console.alive = false;
}
void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context) {
FURI_CRITICAL_ENTER();
furi_hal_console.tx_callback = callback;
furi_hal_console.tx_callback_context = context;
FURI_CRITICAL_EXIT();
}
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
if (!furi_hal_console.alive) return;
FURI_CRITICAL_ENTER();
if (furi_hal_console.tx_callback) {
furi_hal_console.tx_callback(buffer, buffer_size, furi_hal_console.tx_callback_context);
}
char safe_buffer[buffer_size + 1];
memcpy(safe_buffer, buffer, buffer_size);
safe_buffer[buffer_size] = 0;
ESP_LOGI(TAG, "%s", safe_buffer);
FURI_CRITICAL_EXIT();
}
void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) {
if (!furi_hal_console.alive) return;
FURI_CRITICAL_ENTER();
char safe_buffer[buffer_size + 1];
memcpy(safe_buffer, buffer, buffer_size);
safe_buffer[buffer_size] = 0;
ESP_LOGI(TAG, "%s", safe_buffer);
FURI_CRITICAL_EXIT();
}
void furi_hal_console_printf(const char format[], ...) {
FuriString* string;
va_list args;
va_start(args, format);
string = furi_string_alloc_vprintf(format, args);
va_end(args);
furi_hal_console_tx((const uint8_t*)furi_string_get_cstr(string), furi_string_size(string));
furi_string_free(string);
}
void furi_hal_console_puts(const char* data) {
furi_hal_console_tx((const uint8_t*)data, strlen(data));
}

View File

@ -1,35 +0,0 @@
#pragma once
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*FuriHalConsoleTxCallback)(const uint8_t* buffer, size_t size, void* context);
void furi_hal_console_init();
void furi_hal_console_enable();
void furi_hal_console_disable();
void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context);
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size);
void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size);
/**
* Printf-like plain uart interface
* @warning Will not work in ISR context
* @param format
* @param ...
*/
void furi_hal_console_printf(const char format[], ...) _ATTRIBUTE((__format__(__printf__, 1, 2)));
void furi_hal_console_puts(const char* data);
#ifdef __cplusplus
}
#endif

View File

@ -1,304 +0,0 @@
#include "furi_string.h"
#include <m-string.h>
struct FuriString {
string_t string;
};
#undef furi_string_alloc_set
#undef furi_string_set
#undef furi_string_cmp
#undef furi_string_cmpi
#undef furi_string_search
#undef furi_string_search_str
#undef furi_string_equal
#undef furi_string_replace
#undef furi_string_replace_str
#undef furi_string_replace_all
#undef furi_string_start_with
#undef furi_string_end_with
#undef furi_string_search_char
#undef furi_string_search_rchar
#undef furi_string_trim
#undef furi_string_cat
FuriString* furi_string_alloc() {
FuriString* string = malloc(sizeof(FuriString));
string_init(string->string);
return string;
}
FuriString* furi_string_alloc_set(const FuriString* s) {
FuriString* string = malloc(sizeof(FuriString)); //-V799
string_init_set(string->string, s->string);
return string;
} //-V773
FuriString* furi_string_alloc_set_str(const char cstr[]) {
FuriString* string = malloc(sizeof(FuriString)); //-V799
string_init_set(string->string, cstr);
return string;
} //-V773
FuriString* furi_string_alloc_printf(const char format[], ...) {
va_list args;
va_start(args, format);
FuriString* string = furi_string_alloc_vprintf(format, args);
va_end(args);
return string;
}
FuriString* furi_string_alloc_vprintf(const char format[], va_list args) {
FuriString* string = malloc(sizeof(FuriString));
string_init_vprintf(string->string, format, args);
return string;
}
FuriString* furi_string_alloc_move(FuriString* s) {
FuriString* string = malloc(sizeof(FuriString));
string_init_move(string->string, s->string);
free(s);
return string;
}
void furi_string_free(FuriString* s) {
string_clear(s->string);
free(s);
}
void furi_string_reserve(FuriString* s, size_t alloc) {
string_reserve(s->string, alloc);
}
void furi_string_reset(FuriString* s) {
string_reset(s->string);
}
void furi_string_swap(FuriString* v1, FuriString* v2) {
string_swap(v1->string, v2->string);
}
void furi_string_move(FuriString* v1, FuriString* v2) {
string_clear(v1->string);
string_init_move(v1->string, v2->string);
free(v2);
}
size_t furi_string_hash(const FuriString* v) {
return string_hash(v->string);
}
char furi_string_get_char(const FuriString* v, size_t index) {
return string_get_char(v->string, index);
}
const char* furi_string_get_cstr(const FuriString* s) {
return string_get_cstr(s->string);
}
void furi_string_set(FuriString* s, FuriString* source) {
string_set(s->string, source->string);
}
void furi_string_set_str(FuriString* s, const char cstr[]) {
string_set(s->string, cstr);
}
void furi_string_set_strn(FuriString* s, const char str[], size_t n) {
string_set_strn(s->string, str, n);
}
void furi_string_set_char(FuriString* s, size_t index, const char c) {
string_set_char(s->string, index, c);
}
int furi_string_cmp(const FuriString* s1, const FuriString* s2) {
return string_cmp(s1->string, s2->string);
}
int furi_string_cmp_str(const FuriString* s1, const char str[]) {
return string_cmp(s1->string, str);
}
int furi_string_cmpi(const FuriString* v1, const FuriString* v2) {
return string_cmpi(v1->string, v2->string);
}
int furi_string_cmpi_str(const FuriString* v1, const char p2[]) {
return string_cmpi_str(v1->string, p2);
}
size_t furi_string_search(const FuriString* v, const FuriString* needle, size_t start) {
return string_search(v->string, needle->string, start);
}
size_t furi_string_search_str(const FuriString* v, const char needle[], size_t start) {
return string_search(v->string, needle, start);
}
bool furi_string_equal(const FuriString* v1, const FuriString* v2) {
return string_equal_p(v1->string, v2->string);
}
bool furi_string_equal_str(const FuriString* v1, const char v2[]) {
return string_equal_p(v1->string, v2);
}
void furi_string_push_back(FuriString* v, char c) {
string_push_back(v->string, c);
}
size_t furi_string_size(const FuriString* s) {
return string_size(s->string);
}
int furi_string_printf(FuriString* v, const char format[], ...) {
va_list args;
va_start(args, format);
int result = furi_string_vprintf(v, format, args);
va_end(args);
return result;
}
int furi_string_vprintf(FuriString* v, const char format[], va_list args) {
return string_vprintf(v->string, format, args);
}
int furi_string_cat_printf(FuriString* v, const char format[], ...) {
va_list args;
va_start(args, format);
int result = furi_string_cat_vprintf(v, format, args);
va_end(args);
return result;
}
int furi_string_cat_vprintf(FuriString* v, const char format[], va_list args) {
FuriString* string = furi_string_alloc();
int ret = furi_string_vprintf(string, format, args);
furi_string_cat(v, string);
furi_string_free(string);
return ret;
}
bool furi_string_empty(const FuriString* v) {
return string_empty_p(v->string);
}
void furi_string_replace_at(FuriString* v, size_t pos, size_t len, const char str2[]) {
string_replace_at(v->string, pos, len, str2);
}
size_t
furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace, size_t start) {
return string_replace(string->string, needle->string, replace->string, start);
}
size_t furi_string_replace_str(FuriString* v, const char str1[], const char str2[], size_t start) {
return string_replace_str(v->string, str1, str2, start);
}
void furi_string_replace_all_str(FuriString* v, const char str1[], const char str2[]) {
string_replace_all_str(v->string, str1, str2);
}
void furi_string_replace_all(FuriString* v, const FuriString* str1, const FuriString* str2) {
string_replace_all(v->string, str1->string, str2->string);
}
bool furi_string_start_with(const FuriString* v, const FuriString* v2) {
return string_start_with_string_p(v->string, v2->string);
}
bool furi_string_start_with_str(const FuriString* v, const char str[]) {
return string_start_with_str_p(v->string, str);
}
bool furi_string_end_with(const FuriString* v, const FuriString* v2) {
return string_end_with_string_p(v->string, v2->string);
}
bool furi_string_end_with_str(const FuriString* v, const char str[]) {
return string_end_with_str_p(v->string, str);
}
size_t furi_string_search_char(const FuriString* v, char c, size_t start) {
return string_search_char(v->string, c, start);
}
size_t furi_string_search_rchar(const FuriString* v, char c, size_t start) {
return string_search_rchar(v->string, c, start);
}
void furi_string_left(FuriString* v, size_t index) {
string_left(v->string, index);
}
void furi_string_right(FuriString* v, size_t index) {
string_right(v->string, index);
}
void furi_string_mid(FuriString* v, size_t index, size_t size) {
string_mid(v->string, index, size);
}
void furi_string_trim(FuriString* v, const char charac[]) {
string_strim(v->string, charac);
}
void furi_string_cat(FuriString* v, const FuriString* v2) {
string_cat(v->string, v2->string);
}
void furi_string_cat_str(FuriString* v, const char str[]) {
string_cat(v->string, str);
}
void furi_string_set_n(FuriString* v, const FuriString* ref, size_t offset, size_t length) {
string_set_n(v->string, ref->string, offset, length);
}
size_t furi_string_utf8_length(FuriString* str) {
return string_length_u(str->string);
}
void furi_string_utf8_push(FuriString* str, FuriStringUnicodeValue u) {
string_push_u(str->string, u);
}
static m_str1ng_utf8_state_e furi_state_to_state(FuriStringUTF8State state) {
switch (state) {
case FuriStringUTF8StateStarting:
return M_STR1NG_UTF8_STARTING;
case FuriStringUTF8StateDecoding1:
return M_STR1NG_UTF8_DECODING_1;
case FuriStringUTF8StateDecoding2:
return M_STR1NG_UTF8_DECODING_2;
case FuriStringUTF8StateDecoding3:
return M_STR1NG_UTF8_DECODING_3;
default:
return M_STR1NG_UTF8_ERROR;
}
}
static FuriStringUTF8State state_to_furi_state(m_str1ng_utf8_state_e state) {
switch (state) {
case M_STR1NG_UTF8_STARTING:
return FuriStringUTF8StateStarting;
case M_STR1NG_UTF8_DECODING_1:
return FuriStringUTF8StateDecoding1;
case M_STR1NG_UTF8_DECODING_2:
return FuriStringUTF8StateDecoding2;
case M_STR1NG_UTF8_DECODING_3:
return FuriStringUTF8StateDecoding3;
default:
return FuriStringUTF8StateError;
}
}
void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode) {
string_unicode_t m_u = *unicode;
m_str1ng_utf8_state_e m_state = furi_state_to_state(*state);
m_str1ng_utf8_decode(c, &m_state, &m_u);
*state = state_to_furi_state(m_state);
*unicode = m_u;
}

View File

@ -1,95 +0,0 @@
/**
* @file message_queue.h
* FuriMessageQueue
*/
#pragma once
#include "furi_core_types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void FuriMessageQueue;
/** Allocate furi message queue
*
* @param[in] msg_count The message count
* @param[in] msg_size The message size
*
* @return pointer to FuriMessageQueue instance
*/
FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size);
/** Free queue
*
* @param instance pointer to FuriMessageQueue instance
*/
void furi_message_queue_free(FuriMessageQueue* instance);
/** Put message into queue
*
* @param instance pointer to FuriMessageQueue instance
* @param[in] msg_ptr The message pointer
* @param[in] timeout The timeout
* @param[in] msg_prio The message prio
*
* @return The furi status.
*/
FuriStatus
furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t timeout);
/** Get message from queue
*
* @param instance pointer to FuriMessageQueue instance
* @param msg_ptr The message pointer
* @param msg_prio The message prioority
* @param[in] timeout_ticks The timeout
*
* @return The furi status.
*/
FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout_ticks);
/** Get queue capacity
*
* @param instance pointer to FuriMessageQueue instance
*
* @return capacity in object count
*/
uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance);
/** Get message size
*
* @param instance pointer to FuriMessageQueue instance
*
* @return Message size in bytes
*/
uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance);
/** Get message count in queue
*
* @param instance pointer to FuriMessageQueue instance
*
* @return Message count
*/
uint32_t furi_message_queue_get_count(FuriMessageQueue* instance);
/** Get queue available space
*
* @param instance pointer to FuriMessageQueue instance
*
* @return Message count
*/
uint32_t furi_message_queue_get_space(FuriMessageQueue* instance);
/** Reset queue
*
* @param instance pointer to FuriMessageQueue instance
*
* @return The furi status.
*/
FuriStatus furi_message_queue_reset(FuriMessageQueue* instance);
#ifdef __cplusplus
}
#endif

View File

@ -1,62 +0,0 @@
/**
* @file mutex.h
* FuriMutex
*/
#pragma once
#include "furi_core_types.h"
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
FuriMutexTypeNormal,
FuriMutexTypeRecursive,
} FuriMutexType;
typedef void FuriMutex;
/** Allocate FuriMutex
*
* @param[in] type The mutex type
*
* @return pointer to FuriMutex instance
*/
FuriMutex* furi_mutex_alloc(FuriMutexType type);
/** Free FuriMutex
*
* @param instance The pointer to FuriMutex instance
*/
void furi_mutex_free(FuriMutex* instance);
/** Acquire mutex
*
* @param instance The pointer to FuriMutex instance
* @param[in] timeout The timeout
*
* @return The furi status.
*/
FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout);
/** Release mutex
*
* @param instance The pointer to FuriMutex instance
*
* @return The furi status.
*/
FuriStatus furi_mutex_release(FuriMutex* instance);
/** Get mutex owner thread id
*
* @param instance The pointer to FuriMutex instance
*
* @return The furi thread identifier.
*/
FuriThreadId furi_mutex_get_owner(FuriMutex* instance);
#ifdef __cplusplus
}
#endif

View File

@ -1,94 +0,0 @@
#include "pubsub.h"
#include "check.h"
#include "mutex.h"
#include <m-list.h>
struct FuriPubSubSubscription {
FuriPubSubCallback callback;
void* callback_context;
};
LIST_DEF(FuriPubSubSubscriptionList, FuriPubSubSubscription, M_POD_OPLIST);
struct FuriPubSub {
FuriPubSubSubscriptionList_t items;
FuriMutex* mutex;
};
FuriPubSub* furi_pubsub_alloc() {
FuriPubSub* pubsub = malloc(sizeof(FuriPubSub));
pubsub->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
furi_assert(pubsub->mutex);
FuriPubSubSubscriptionList_init(pubsub->items);
return pubsub;
}
void furi_pubsub_free(FuriPubSub* pubsub) {
furi_assert(pubsub);
furi_check(FuriPubSubSubscriptionList_size(pubsub->items) == 0);
FuriPubSubSubscriptionList_clear(pubsub->items);
furi_mutex_free(pubsub->mutex);
free(pubsub);
}
FuriPubSubSubscription*
furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context) {
furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk);
// put uninitialized item to the list
FuriPubSubSubscription* item = FuriPubSubSubscriptionList_push_raw(pubsub->items);
// initialize item
item->callback = callback;
item->callback_context = callback_context;
furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk);
return item;
}
void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription) {
furi_assert(pubsub);
furi_assert(pubsub_subscription);
furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk);
bool result = false;
// iterate over items
FuriPubSubSubscriptionList_it_t it;
for (FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it);
FuriPubSubSubscriptionList_next(it)) {
const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it);
// if the iterator is equal to our element
if (item == pubsub_subscription) {
FuriPubSubSubscriptionList_remove(pubsub->items, it);
result = true;
break;
}
}
furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk);
furi_check(result);
}
void furi_pubsub_publish(FuriPubSub* pubsub, void* message) {
furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk);
// iterate over subscribers
FuriPubSubSubscriptionList_it_t it;
for (FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it);
FuriPubSubSubscriptionList_next(it)) {
const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it);
item->callback(message, item->callback_context);
}
furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk);
}

View File

@ -1,68 +0,0 @@
/**
* @file pubsub.h
* FuriPubSub
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/** FuriPubSub Callback type */
typedef void (*FuriPubSubCallback)(const void* message, void* context);
/** FuriPubSub type */
typedef struct FuriPubSub FuriPubSub;
/** FuriPubSubSubscription type */
typedef struct FuriPubSubSubscription FuriPubSubSubscription;
/** Allocate FuriPubSub
*
* Reentrable, Not threadsafe, one owner
*
* @return pointer to FuriPubSub instance
*/
FuriPubSub* furi_pubsub_alloc();
/** Free FuriPubSub
*
* @param pubsub FuriPubSub instance
*/
void furi_pubsub_free(FuriPubSub* pubsub);
/** Subscribe to FuriPubSub
*
* Threadsafe, Reentrable
*
* @param pubsub pointer to FuriPubSub instance
* @param[in] callback The callback
* @param callback_context The callback context
*
* @return pointer to FuriPubSubSubscription instance
*/
FuriPubSubSubscription*
furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context);
/** Unsubscribe from FuriPubSub
*
* No use of `pubsub_subscription` allowed after call of this method
* Threadsafe, Reentrable.
*
* @param pubsub pointer to FuriPubSub instance
* @param pubsub_subscription pointer to FuriPubSubSubscription instance
*/
void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription);
/** Publish message to FuriPubSub
*
* Threadsafe, Reentrable.
*
* @param pubsub pointer to FuriPubSub instance
* @param message message pointer to publish
*/
void furi_pubsub_publish(FuriPubSub* pubsub, void* message);
#ifdef __cplusplus
}
#endif

View File

@ -1,54 +0,0 @@
#pragma once
#include "furi_core_types.h"
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void FuriSemaphore;
/** Allocate semaphore
*
* @param[in] max_count The maximum count
* @param[in] initial_count The initial count
*
* @return pointer to FuriSemaphore instance
*/
FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count);
/** Free semaphore
*
* @param instance The pointer to FuriSemaphore instance
*/
void furi_semaphore_free(FuriSemaphore* instance);
/** Acquire semaphore
*
* @param instance The pointer to FuriSemaphore instance
* @param[in] timeout The timeout
*
* @return The furi status.
*/
FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout);
/** Release semaphore
*
* @param instance The pointer to FuriSemaphore instance
*
* @return The furi status.
*/
FuriStatus furi_semaphore_release(FuriSemaphore* instance);
/** Get semaphore count
*
* @param instance The pointer to FuriSemaphore instance
*
* @return Semaphore count
*/
uint32_t furi_semaphore_get_count(FuriSemaphore* instance);
#ifdef __cplusplus
}
#endif

View File

@ -1,63 +0,0 @@
#include "service_i.h"
#include "furi_core.h"
#include "log.h"
// region Alloc/free
Service service_alloc(const ServiceManifest* _Nonnull manifest) {
ServiceData* data = malloc(sizeof(ServiceData));
*data = (ServiceData) {
.manifest = manifest,
.mutex = furi_mutex_alloc(FuriMutexTypeRecursive),
.data = NULL
};
return data;
}
void service_free(Service service) {
ServiceData* data = (ServiceData*)service;
furi_assert(service);
furi_mutex_free(data->mutex);
free(data);
}
// endregion Alloc/free
// region Internal
static void service_lock(ServiceData * data) {
furi_mutex_acquire(data->mutex, FuriMutexTypeRecursive);
}
static void service_unlock(ServiceData* data) {
furi_mutex_release(data->mutex);
}
// endregion Internal
// region Getters & Setters
const ServiceManifest* service_get_manifest(Service service) {
ServiceData* data = (ServiceData*)service;
service_lock(data);
const ServiceManifest* manifest = data->manifest;
service_unlock(data);
return manifest;
}
void service_set_data(Service service, void* value) {
ServiceData* data = (ServiceData*)service;
service_lock(data);
data->data = value;
service_unlock(data);
}
void* _Nullable service_get_data(Service service) {
ServiceData* data = (ServiceData*)service;
service_lock(data);
void* value = data->data;
service_unlock(data);
return value;
}
// endregion Getters & Setters

View File

@ -1,18 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "service_manifest.h"
typedef void* Service;
const ServiceManifest* service_get_manifest(Service service);
void service_set_data(Service service, void* value);
void* _Nullable service_get_data(Service service);
#ifdef __cplusplus
}
#endif

View File

@ -1,25 +0,0 @@
#pragma once
#include "service_manifest.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
typedef void (*ServiceManifestCallback)(const ServiceManifest*, void* context);
void service_registry_init();
void service_registry_add(const ServiceManifest* manifest);
void service_registry_remove(const ServiceManifest* manifest);
const ServiceManifest _Nullable* service_registry_find_manifest_by_id(const char* id);
void service_registry_for_each_manifest(ServiceManifestCallback callback, void* _Nullable context);
bool service_registry_start(const char* service_id);
bool service_registry_stop(const char* service_id);
#ifdef __cplusplus
}
#endif

View File

@ -1,665 +0,0 @@
#include "thread.h"
#include "check.h"
#include "furi_core_defines.h"
#include "furi_string.h"
#include "kernel.h"
#include <esp_log.h>
#include <furi_hal_console.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define TAG "FuriThread"
#define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers
// Limits
#define MAX_BITS_TASK_NOTIFY 31U
#define MAX_BITS_EVENT_GROUPS 24U
#define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U))
#define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U))
typedef struct FuriThreadStdout FuriThreadStdout;
struct FuriThreadStdout {
FuriThreadStdoutWriteCallback write_callback;
FuriString* buffer;
};
struct FuriThread {
FuriThreadState state;
int32_t ret;
FuriThreadCallback callback;
void* context;
FuriThreadStateCallback state_callback;
void* state_context;
char* name;
char* appid;
FuriThreadPriority priority;
TaskHandle_t task_handle;
size_t heap_size;
FuriThreadStdout output;
// Keep all non-alignable byte types in one place,
// this ensures that the size of this structure is minimal
bool is_static;
bool heap_trace_enabled;
configSTACK_DEPTH_TYPE stack_size;
};
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size);
static int32_t __furi_thread_stdout_flush(FuriThread* thread);
/** Catch threads that are trying to exit wrong way */
__attribute__((__noreturn__)) void furi_thread_catch() { //-V1082
// If you're here it means you're probably doing something wrong
// with critical sections or with scheduler state
asm volatile("nop"); // extra magic
furi_crash("You are doing it wrong"); //-V779
__builtin_unreachable();
}
static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) {
furi_assert(thread);
thread->state = state;
if (thread->state_callback) {
thread->state_callback(state, thread->state_context);
}
}
static void furi_thread_body(void* context) {
furi_assert(context);
FuriThread* thread = context;
// store thread instance to thread local storage
furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) == NULL);
vTaskSetThreadLocalStoragePointer(NULL, 0, thread);
furi_assert(thread->state == FuriThreadStateStarting);
furi_thread_set_state(thread, FuriThreadStateRunning);
/*
TaskHandle_t task_handle = xTaskGetCurrentTaskHandle();
if(thread->heap_trace_enabled == true) {
memmgr_heap_enable_thread_trace((FuriThreadId)task_handle);
}
*/
thread->ret = thread->callback(thread->context);
/*
if(thread->heap_trace_enabled == true) {
furi_delay_ms(33);
thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle);
furi_log_print_format(
thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo,
TAG,
"%s allocation balance: %zu",
thread->name ? thread->name : "Thread",
thread->heap_size);
memmgr_heap_disable_thread_trace((FuriThreadId)task_handle);
}
*/
furi_assert(thread->state == FuriThreadStateRunning);
if (thread->is_static) {
ESP_LOGI(
TAG,
"%s service thread TCB memory will not be reclaimed",
thread->name ? thread->name : "<unnamed service>"
);
}
// flush stdout
__furi_thread_stdout_flush(thread);
furi_thread_set_state(thread, FuriThreadStateStopped);
vTaskDelete(NULL);
furi_thread_catch();
}
FuriThread* furi_thread_alloc() {
FuriThread* thread = malloc(sizeof(FuriThread));
// TODO: create default struct instead of using memset()
memset(thread, 0, sizeof(FuriThread));
thread->output.buffer = furi_string_alloc();
thread->is_static = false;
FuriThread* parent = NULL;
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
// TLS is not available, if we called not from thread context
parent = pvTaskGetThreadLocalStoragePointer(NULL, 0);
if (parent && parent->appid) {
furi_thread_set_appid(thread, parent->appid);
} else {
furi_thread_set_appid(thread, "unknown");
}
} else {
// if scheduler is not started, we are starting driver thread
furi_thread_set_appid(thread, "driver");
}
/*FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();
if(mode == FuriHalRtcHeapTrackModeAll) {
thread->heap_trace_enabled = true;
} else if(mode == FuriHalRtcHeapTrackModeTree && furi_thread_get_current_id()) {
if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled;
} else */
{
thread->heap_trace_enabled = false;
}
return thread;
}
FuriThread* furi_thread_alloc_ex(
const char* name,
uint32_t stack_size,
FuriThreadCallback callback,
void* context
) {
FuriThread* thread = furi_thread_alloc();
furi_thread_set_name(thread, name);
furi_thread_set_stack_size(thread, stack_size);
furi_thread_set_callback(thread, callback);
furi_thread_set_context(thread, context);
return thread;
}
void furi_thread_free(FuriThread* thread) {
furi_assert(thread);
// Ensure that use join before free
furi_assert(thread->state == FuriThreadStateStopped);
furi_assert(thread->task_handle == NULL);
if (thread->name) free(thread->name);
if (thread->appid) free(thread->appid);
furi_string_free(thread->output.buffer);
free(thread);
}
void furi_thread_set_name(FuriThread* thread, const char* name) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
if (thread->name) free(thread->name);
thread->name = name ? strdup(name) : NULL;
}
void furi_thread_set_appid(FuriThread* thread, const char* appid) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
if (thread->appid) free(thread->appid);
thread->appid = appid ? strdup(appid) : NULL;
}
void furi_thread_mark_as_static(FuriThread* thread) {
thread->is_static = true;
}
bool furi_thread_mark_is_service(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
assert(!FURI_IS_IRQ_MODE() && (hTask != NULL));
FuriThread* thread = (FuriThread*)pvTaskGetThreadLocalStoragePointer(hTask, 0);
assert(thread != NULL);
return thread->is_static;
}
void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
furi_assert(stack_size % 4 == 0);
thread->stack_size = stack_size;
}
void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
thread->callback = callback;
}
void furi_thread_set_context(FuriThread* thread, void* context) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
thread->context = context;
}
void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
furi_assert(priority >= FuriThreadPriorityIdle && priority <= FuriThreadPriorityIsr);
thread->priority = priority;
}
void furi_thread_set_current_priority(FuriThreadPriority priority) {
UBaseType_t new_priority = priority ? priority : FuriThreadPriorityNormal;
vTaskPrioritySet(NULL, new_priority);
}
FuriThreadPriority furi_thread_get_current_priority() {
return (FuriThreadPriority)uxTaskPriorityGet(NULL);
}
void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
thread->state_callback = callback;
}
void furi_thread_set_state_context(FuriThread* thread, void* context) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
thread->state_context = context;
}
FuriThreadState furi_thread_get_state(FuriThread* thread) {
furi_assert(thread);
return thread->state;
}
void furi_thread_start(FuriThread* thread) {
furi_assert(thread);
furi_assert(thread->callback);
furi_assert(thread->state == FuriThreadStateStopped);
furi_assert(thread->stack_size > 0 && thread->stack_size < (UINT16_MAX * sizeof(StackType_t)));
furi_thread_set_state(thread, FuriThreadStateStarting);
uint32_t stack = thread->stack_size / sizeof(StackType_t);
UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal;
if (thread->is_static) {
thread->task_handle = xTaskCreateStatic(
furi_thread_body,
thread->name,
stack,
thread,
priority,
malloc(sizeof(StackType_t) * stack),
malloc(sizeof(StaticTask_t))
);
} else {
BaseType_t ret = xTaskCreate(
furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle
);
furi_check(ret == pdPASS);
}
furi_check(thread->task_handle);
}
void furi_thread_cleanup_tcb_event(TaskHandle_t task) {
FuriThread* thread = pvTaskGetThreadLocalStoragePointer(task, 0);
if (thread) {
// clear thread local storage
vTaskSetThreadLocalStoragePointer(task, 0, NULL);
furi_assert(thread->task_handle == task);
thread->task_handle = NULL;
}
}
bool furi_thread_join(FuriThread* thread) {
furi_assert(thread);
furi_check(furi_thread_get_current() != thread);
// !!! IMPORTANT NOTICE !!!
//
// If your thread exited, but your app stuck here: some other thread uses
// all cpu time, which delays kernel from releasing task handle
while (thread->task_handle) {
furi_delay_ms(10);
}
return true;
}
FuriThreadId furi_thread_get_id(FuriThread* thread) {
furi_assert(thread);
return thread->task_handle;
}
void furi_thread_enable_heap_trace(FuriThread* thread) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
thread->heap_trace_enabled = true;
}
void furi_thread_disable_heap_trace(FuriThread* thread) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
thread->heap_trace_enabled = false;
}
size_t furi_thread_get_heap_size(FuriThread* thread) {
furi_assert(thread);
furi_assert(thread->heap_trace_enabled == true);
return thread->heap_size;
}
int32_t furi_thread_get_return_code(FuriThread* thread) {
furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
return thread->ret;
}
FuriThreadId furi_thread_get_current_id() {
return xTaskGetCurrentTaskHandle();
}
FuriThread* furi_thread_get_current() {
FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0);
return thread;
}
void furi_thread_yield() {
furi_assert(!FURI_IS_IRQ_MODE());
taskYIELD();
}
uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
uint32_t rflags;
BaseType_t yield;
if ((hTask == NULL) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) {
rflags = (uint32_t)FuriStatusErrorParameter;
} else {
rflags = (uint32_t)FuriStatusError;
if (FURI_IS_IRQ_MODE()) {
yield = pdFALSE;
(void)xTaskNotifyIndexedFromISR(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits, &yield);
(void)xTaskNotifyAndQueryIndexedFromISR(
hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags, NULL
);
portYIELD_FROM_ISR(yield);
} else {
(void)xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits);
(void)xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags);
}
}
/* Return flags after setting */
return (rflags);
}
uint32_t furi_thread_flags_clear(uint32_t flags) {
TaskHandle_t hTask;
uint32_t rflags, cflags;
if (FURI_IS_IRQ_MODE()) {
rflags = (uint32_t)FuriStatusErrorISR;
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
rflags = (uint32_t)FuriStatusErrorParameter;
} else {
hTask = xTaskGetCurrentTaskHandle();
if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &cflags) ==
pdPASS) {
rflags = cflags;
cflags &= ~flags;
if (xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) !=
pdPASS) {
rflags = (uint32_t)FuriStatusError;
}
} else {
rflags = (uint32_t)FuriStatusError;
}
}
/* Return flags before clearing */
return (rflags);
}
uint32_t furi_thread_flags_get(void) {
TaskHandle_t hTask;
uint32_t rflags;
if (FURI_IS_IRQ_MODE()) {
rflags = (uint32_t)FuriStatusErrorISR;
} else {
hTask = xTaskGetCurrentTaskHandle();
if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) !=
pdPASS) {
rflags = (uint32_t)FuriStatusError;
}
}
return (rflags);
}
uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
uint32_t rflags, nval;
uint32_t clear;
TickType_t t0, td, tout;
BaseType_t rval;
if (FURI_IS_IRQ_MODE()) {
rflags = (uint32_t)FuriStatusErrorISR;
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
rflags = (uint32_t)FuriStatusErrorParameter;
} else {
if ((options & FuriFlagNoClear) == FuriFlagNoClear) {
clear = 0U;
} else {
clear = flags;
}
rflags = 0U;
tout = timeout;
t0 = xTaskGetTickCount();
do {
rval = xTaskNotifyWaitIndexed(THREAD_NOTIFY_INDEX, 0, clear, &nval, tout);
if (rval == pdPASS) {
rflags &= flags;
rflags |= nval;
if ((options & FuriFlagWaitAll) == FuriFlagWaitAll) {
if ((flags & rflags) == flags) {
break;
} else {
if (timeout == 0U) {
rflags = (uint32_t)FuriStatusErrorResource;
break;
}
}
} else {
if ((flags & rflags) != 0) {
break;
} else {
if (timeout == 0U) {
rflags = (uint32_t)FuriStatusErrorResource;
break;
}
}
}
/* Update timeout */
td = xTaskGetTickCount() - t0;
if (td > tout) {
tout = 0;
} else {
tout -= td;
}
} else {
if (timeout == 0) {
rflags = (uint32_t)FuriStatusErrorResource;
} else {
rflags = (uint32_t)FuriStatusErrorTimeout;
}
}
} while (rval != pdFAIL);
}
/* Return flags before clearing */
return (rflags);
}
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_items) {
uint32_t i, count;
TaskStatus_t* task;
if (FURI_IS_IRQ_MODE() || (thread_array == NULL) || (array_items == 0U)) {
count = 0U;
} else {
vTaskSuspendAll();
count = uxTaskGetNumberOfTasks();
task = pvPortMalloc(count * sizeof(TaskStatus_t));
if (task != NULL) {
count = uxTaskGetSystemState(task, count, NULL);
for (i = 0U; (i < count) && (i < array_items); i++) {
thread_array[i] = (FuriThreadId)task[i].xHandle;
}
count = i;
}
(void)xTaskResumeAll();
vPortFree(task);
}
return (count);
}
const char* furi_thread_get_name(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
const char* name;
if (FURI_IS_IRQ_MODE() || (hTask == NULL)) {
name = NULL;
} else {
name = pcTaskGetName(hTask);
}
return (name);
}
const char* furi_thread_get_appid(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
const char* appid = "system";
if (!FURI_IS_IRQ_MODE() && (hTask != NULL)) {
FuriThread* thread = (FuriThread*)pvTaskGetThreadLocalStoragePointer(hTask, 0);
if (thread) {
appid = thread->appid;
}
}
return (appid);
}
uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
uint32_t sz;
if (FURI_IS_IRQ_MODE() || (hTask == NULL)) {
sz = 0U;
} else {
sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t));
}
return (sz);
}
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) {
if (thread->output.write_callback != NULL) {
thread->output.write_callback(data, size);
} else {
furi_hal_console_tx((const uint8_t*)data, size);
}
return size;
}
static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
FuriString* buffer = thread->output.buffer;
size_t size = furi_string_size(buffer);
if (size > 0) {
__furi_thread_stdout_write(thread, furi_string_get_cstr(buffer), size);
furi_string_reset(buffer);
}
return 0;
}
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) {
FuriThread* thread = furi_thread_get_current();
furi_assert(thread);
__furi_thread_stdout_flush(thread);
thread->output.write_callback = callback;
}
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback() {
FuriThread* thread = furi_thread_get_current();
furi_assert(thread);
return thread->output.write_callback;
}
size_t furi_thread_stdout_write(const char* data, size_t size) {
FuriThread* thread = furi_thread_get_current();
furi_assert(thread);
if (size == 0 || data == NULL) {
return __furi_thread_stdout_flush(thread);
} else {
if (data[size - 1] == '\n') {
// if the last character is a newline, we can flush buffer and write data as is, wo buffers
__furi_thread_stdout_flush(thread);
__furi_thread_stdout_write(thread, data, size);
} else {
// string_cat doesn't work here because we need to write the exact size data
for (size_t i = 0; i < size; i++) {
furi_string_push_back(thread->output.buffer, data[i]);
if (data[i] == '\n') {
__furi_thread_stdout_flush(thread);
}
}
}
}
return size;
}
int32_t furi_thread_stdout_flush() {
FuriThread* thread = furi_thread_get_current();
furi_assert(thread);
return __furi_thread_stdout_flush(thread);
}
void furi_thread_suspend(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
vTaskSuspend(hTask);
}
void furi_thread_resume(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
if (FURI_IS_IRQ_MODE()) {
xTaskResumeFromISR(hTask);
} else {
vTaskResume(hTask);
}
}
bool furi_thread_is_suspended(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
return eTaskGetState(hTask) == eSuspended;
}

View File

@ -1,339 +0,0 @@
/**
* @file thread.h
* Furi: Furi Thread API
*/
#pragma once
#include "furi_core_defines.h"
#include "furi_core_types.h"
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** FuriThreadState */
typedef enum {
FuriThreadStateStopped,
FuriThreadStateStarting,
FuriThreadStateRunning,
} FuriThreadState;
/** FuriThreadPriority */
typedef enum {
FuriThreadPriorityNone = 0, /**< Uninitialized, choose system default */
FuriThreadPriorityIdle = 1, /**< Idle priority */
FuriThreadPriorityLowest = 14, /**< Lowest */
FuriThreadPriorityLow = 15, /**< Low */
FuriThreadPriorityNormal = 16, /**< Normal */
FuriThreadPriorityHigh = 17, /**< High */
FuriThreadPriorityHighest = 18, /**< Highest */
FuriThreadPriorityIsr =
(FURI_CONFIG_THREAD_MAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */
} FuriThreadPriority;
/** FuriThread anonymous structure */
typedef struct FuriThread FuriThread;
/** FuriThreadId proxy type to OS low level functions */
typedef void* FuriThreadId;
/** FuriThreadCallback Your callback to run in new thread
* @warning never use osThreadExit in FuriThread
*/
typedef int32_t (*FuriThreadCallback)(void* context);
/** Write to stdout callback
* @param data pointer to data
* @param size data size @warning your handler must consume everything
*/
typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size);
/** FuriThread state change callback called upon thread state change
* @param state new thread state
* @param context callback context
*/
typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context);
/** Allocate FuriThread
*
* @return FuriThread instance
*/
FuriThread* furi_thread_alloc();
/** Allocate FuriThread, shortcut version
*
* @param name
* @param stack_size
* @param callback
* @param context
* @return FuriThread*
*/
FuriThread* furi_thread_alloc_ex(
const char* name,
uint32_t stack_size,
FuriThreadCallback callback,
void* context
);
/** Release FuriThread
*
* @warning see furi_thread_join
*
* @param thread FuriThread instance
*/
void furi_thread_free(FuriThread* thread);
/** Set FuriThread name
*
* @param thread FuriThread instance
* @param name string
*/
void furi_thread_set_name(FuriThread* thread, const char* name);
/**
* @brief Set FuriThread appid
* Technically, it is like a "process id", but it is not a system-wide unique identifier.
* All threads spawned by the same app will have the same appid.
*
* @param thread
* @param appid
*/
void furi_thread_set_appid(FuriThread* thread, const char* appid);
/** Mark thread as service
* The service cannot be stopped or removed, and cannot exit from the thread body
*
* @param thread
*/
void furi_thread_mark_as_static(FuriThread* thread);
/** Set FuriThread stack size
*
* @param thread FuriThread instance
* @param stack_size stack size in bytes
*/
void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size);
/** Set FuriThread callback
*
* @param thread FuriThread instance
* @param callback FuriThreadCallback, called upon thread run
*/
void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback);
/** Set FuriThread context
*
* @param thread FuriThread instance
* @param context pointer to context for thread callback
*/
void furi_thread_set_context(FuriThread* thread, void* context);
/** Set FuriThread priority
*
* @param thread FuriThread instance
* @param priority FuriThreadPriority value
*/
void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority);
/** Set current thread priority
*
* @param priority FuriThreadPriority value
*/
void furi_thread_set_current_priority(FuriThreadPriority priority);
/** Get current thread priority
*
* @return FuriThreadPriority value
*/
FuriThreadPriority furi_thread_get_current_priority();
/** Set FuriThread state change callback
*
* @param thread FuriThread instance
* @param callback state change callback
*/
void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback);
/** Set FuriThread state change context
*
* @param thread FuriThread instance
* @param context pointer to context
*/
void furi_thread_set_state_context(FuriThread* thread, void* context);
/** Get FuriThread state
*
* @param thread FuriThread instance
*
* @return thread state from FuriThreadState
*/
FuriThreadState furi_thread_get_state(FuriThread* thread);
/** Start FuriThread
*
* @param thread FuriThread instance
*/
void furi_thread_start(FuriThread* thread);
/** Join FuriThread
*
* @warning Use this method only when CPU is not busy(Idle task receives
* control), otherwise it will wait forever.
*
* @param thread FuriThread instance
*
* @return bool
*/
bool furi_thread_join(FuriThread* thread);
/** Get FreeRTOS FuriThreadId for FuriThread instance
*
* @param thread FuriThread instance
*
* @return FuriThreadId or NULL
*/
FuriThreadId furi_thread_get_id(FuriThread* thread);
/** Enable heap tracing
*
* @param thread FuriThread instance
*/
void furi_thread_enable_heap_trace(FuriThread* thread);
/** Disable heap tracing
*
* @param thread FuriThread instance
*/
void furi_thread_disable_heap_trace(FuriThread* thread);
/** Get thread heap size
*
* @param thread FuriThread instance
*
* @return size in bytes
*/
size_t furi_thread_get_heap_size(FuriThread* thread);
/** Get thread return code
*
* @param thread FuriThread instance
*
* @return return code
*/
int32_t furi_thread_get_return_code(FuriThread* thread);
/** Thread related methods that doesn't involve FuriThread directly */
/** Get FreeRTOS FuriThreadId for current thread
*
* @param thread FuriThread instance
*
* @return FuriThreadId or NULL
*/
FuriThreadId furi_thread_get_current_id();
/** Get FuriThread instance for current thread
*
* @return pointer to FuriThread or NULL if this thread doesn't belongs to Furi
*/
FuriThread* furi_thread_get_current();
/** Return control to scheduler */
void furi_thread_yield();
uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags);
uint32_t furi_thread_flags_clear(uint32_t flags);
uint32_t furi_thread_flags_get(void);
uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout);
/**
* @brief Enumerate threads
*
* @param thread_array array of FuriThreadId, where thread ids will be stored
* @param array_items array size
* @return uint32_t threads count
*/
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_items);
/**
* @brief Get thread name
*
* @param thread_id
* @return const char* name or NULL
*/
const char* furi_thread_get_name(FuriThreadId thread_id);
/**
* @brief Get thread appid
*
* @param thread_id
* @return const char* appid
*/
const char* furi_thread_get_appid(FuriThreadId thread_id);
/**
* @brief Get thread stack watermark
*
* @param thread_id
* @return uint32_t
*/
uint32_t furi_thread_get_stack_space(FuriThreadId thread_id);
/** Get STDOUT callback for thead
*
* @return STDOUT callback
*/
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback();
/** Set STDOUT callback for thread
*
* @param callback callback or NULL to clear
*/
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback);
/** Write data to buffered STDOUT
*
* @param data input data
* @param size input data size
*
* @return size_t written data size
*/
size_t furi_thread_stdout_write(const char* data, size_t size);
/** Flush data to STDOUT
*
* @return int32_t error code
*/
int32_t furi_thread_stdout_flush();
/** Suspend thread
*
* @param thread_id thread id
*/
void furi_thread_suspend(FuriThreadId thread_id);
/** Resume thread
*
* @param thread_id thread id
*/
void furi_thread_resume(FuriThreadId thread_id);
/** Get thread suspended state
*
* @param thread_id thread id
* @return true if thread is suspended
*/
bool furi_thread_is_suspended(FuriThreadId thread_id);
bool furi_thread_mark_is_service(FuriThreadId thread_id);
#ifdef __cplusplus
}
#endif

View File

@ -1,106 +0,0 @@
#pragma once
#include "furi_core_types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*FuriTimerCallback)(void* context);
typedef enum {
FuriTimerTypeOnce = 0, ///< One-shot timer.
FuriTimerTypePeriodic = 1 ///< Repeating timer.
} FuriTimerType;
typedef void FuriTimer;
/** Allocate timer
*
* @param[in] func The callback function
* @param[in] type The timer type
* @param context The callback context
*
* @return The pointer to FuriTimer instance
*/
FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context);
/** Free timer
*
* @param instance The pointer to FuriTimer instance
*/
void furi_timer_free(FuriTimer* instance);
/** Start timer
*
* @warning This is asynchronous call, real operation will happen as soon as
* timer service process this request.
*
* @param instance The pointer to FuriTimer instance
* @param[in] ticks The interval in ticks
*
* @return The furi status.
*/
FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks);
/** Restart timer with previous timeout value
*
* @warning This is asynchronous call, real operation will happen as soon as
* timer service process this request.
*
* @param instance The pointer to FuriTimer instance
* @param[in] ticks The interval in ticks
*
* @return The furi status.
*/
FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks);
/** Stop timer
*
* @warning This is asynchronous call, real operation will happen as soon as
* timer service process this request.
*
* @param instance The pointer to FuriTimer instance
*
* @return The furi status.
*/
FuriStatus furi_timer_stop(FuriTimer* instance);
/** Is timer running
*
* @warning This cal may and will return obsolete timer state if timer
* commands are still in the queue. Please read FreeRTOS timer
* documentation first.
*
* @param instance The pointer to FuriTimer instance
*
* @return 0: not running, 1: running
*/
uint32_t furi_timer_is_running(FuriTimer* instance);
/** Get timer expire time
*
* @param instance The Timer instance
*
* @return expire tick
*/
uint32_t furi_timer_get_expire_time(FuriTimer* instance);
typedef void (*FuriTimerPendigCallback)(void* context, uint32_t arg);
void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg);
typedef enum {
FuriTimerThreadPriorityNormal, /**< Lower then other threads */
FuriTimerThreadPriorityElevated, /**< Same as other threads */
} FuriTimerThreadPriority;
/** Set Timer thread priority
*
* @param[in] priority The priority
*/
void furi_timer_set_thread_priority(FuriTimerThreadPriority priority);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,8 @@
## Description
A good portion of this code is derived from the "Furi" code in the [Flipper Zero firmware](https://github.com/flipperdevices/flipperzero-firmware/).
Some of it is also inspired by the Android operating system.
## License
[GNU General Public License Version 3](LICENSE.md)

View File

@ -0,0 +1,20 @@
#pragma once
#include "core.h"
typedef EventFlag* ApiLock;
#define TT_API_LOCK_EVENT (1U << 0)
#define tt_api_lock_alloc_locked() tt_event_flag_alloc()
#define tt_api_lock_wait_unlock(_lock) \
tt_event_flag_wait(_lock, TT_API_LOCK_EVENT, TtFlagWaitAny, TtWaitForever)
#define tt_api_lock_free(_lock) tt_event_flag_free(_lock)
#define tt_api_lock_unlock(_lock) tt_event_flag_set(_lock, TT_API_LOCK_EVENT)
#define tt_api_lock_wait_unlock_and_free(_lock) \
tt_api_lock_wait_unlock(_lock); \
tt_api_lock_free(_lock);

View File

@ -2,16 +2,16 @@
#include <stdio.h>
static AppFlags app_get_flags_default(AppType type);
static AppFlags tt_app_get_flags_default(AppType type);
// region Alloc/free
App app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters) {
App tt_app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters) {
AppData* data = malloc(sizeof(AppData));
*data = (AppData) {
.mutex = furi_mutex_alloc(FuriMutexTypeRecursive),
.mutex = tt_mutex_alloc(MutexTypeRecursive),
.state = APP_STATE_INITIAL,
.flags = app_get_flags_default(manifest->type),
.flags = tt_app_get_flags_default(manifest->type),
.manifest = manifest,
.parameters = parameters,
.data = NULL
@ -19,12 +19,12 @@ App app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters) {
return (App*)data;
}
void app_free(App app) {
void tt_app_free(App app) {
AppData* data = (AppData*)app;
if (data->parameters) {
bundle_free(data->parameters);
tt_bundle_free(data->parameters);
}
furi_mutex_free(data->mutex);
tt_mutex_free(data->mutex);
free(data);
}
@ -32,15 +32,15 @@ void app_free(App app) {
// region Internal
static void app_lock(AppData* data) {
furi_mutex_acquire(data->mutex, FuriMutexTypeRecursive);
static void tt_app_lock(AppData* data) {
tt_mutex_acquire(data->mutex, MutexTypeRecursive);
}
static void app_unlock(AppData* data) {
furi_mutex_release(data->mutex);
static void tt_app_unlock(AppData* data) {
tt_mutex_release(data->mutex);
}
static AppFlags app_get_flags_default(AppType type) {
static AppFlags tt_app_get_flags_default(AppType type) {
static const AppFlags DEFAULT_DESKTOP_FLAGS = {
.show_toolbar = false,
.show_statusbar = true
@ -60,55 +60,55 @@ static AppFlags app_get_flags_default(AppType type) {
// region Public getters & setters
void app_set_state(App app, AppState state) {
void tt_app_set_state(App app, AppState state) {
AppData* data = (AppData*)app;
app_lock(data);
tt_app_lock(data);
data->state = state;
app_unlock(data);
tt_app_unlock(data);
}
AppState app_get_state(App app) {
AppState tt_app_get_state(App app) {
AppData* data = (AppData*)app;
app_lock(data);
tt_app_lock(data);
AppState state = data->state;
app_unlock(data);
tt_app_unlock(data);
return state;
}
const AppManifest* app_get_manifest(App app) {
const AppManifest* tt_app_get_manifest(App app) {
AppData* data = (AppData*)app;
// No need to lock const data;
return data->manifest;
}
AppFlags app_get_flags(App app) {
AppFlags tt_app_get_flags(App app) {
AppData* data = (AppData*)app;
app_lock(data);
tt_app_lock(data);
AppFlags flags = data->flags;
app_unlock(data);
tt_app_unlock(data);
return flags;
}
void app_set_flags(App app, AppFlags flags) {
void tt_app_set_flags(App app, AppFlags flags) {
AppData* data = (AppData*)app;
app_lock(data);
tt_app_lock(data);
data->flags = flags;
app_unlock(data);
tt_app_unlock(data);
}
void* app_get_data(App app) {
void* tt_app_get_data(App app) {
AppData* data = (AppData*)app;
app_lock(data);
tt_app_lock(data);
void* value = data->data;
app_unlock(data);
tt_app_unlock(data);
return value;
}
void app_set_data(App app, void* value) {
void tt_app_set_data(App app, void* value) {
AppData* data = (AppData*)app;
app_lock(data);
tt_app_lock(data);
data->data = value;
app_unlock(data);
tt_app_unlock(data);
}
/** TODO: Make this thread-safe.
@ -117,11 +117,11 @@ void app_set_data(App app, void* value) {
* Consider creating MutableBundle vs Bundle.
* Consider not exposing bundle, but expose `app_get_bundle_int(key)` methods with locking in it.
*/
Bundle* _Nullable app_get_parameters(App app) {
Bundle* _Nullable tt_app_get_parameters(App app) {
AppData* data = (AppData*)app;
app_lock(data);
tt_app_lock(data);
Bundle* bundle = data->parameters;
app_unlock(data);
tt_app_unlock(data);
return bundle;
}

View File

@ -30,21 +30,21 @@ typedef void* App;
* @param parameters optional bundle. memory ownership is transferred to App
* @return
*/
App app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters);
void app_free(App app);
App tt_app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters);
void tt_app_free(App app);
void app_set_state(App app, AppState state);
AppState app_get_state(App app);
void tt_app_set_state(App app, AppState state);
AppState tt_app_get_state(App app);
const AppManifest* app_get_manifest(App app);
const AppManifest* tt_app_get_manifest(App app);
AppFlags app_get_flags(App app);
void app_set_flags(App app, AppFlags flags);
AppFlags tt_app_get_flags(App app);
void tt_app_set_flags(App app, AppFlags flags);
void* _Nullable app_get_data(App app);
void app_set_data(App app, void* data);
void* _Nullable tt_app_get_data(App app);
void tt_app_set_data(App app, void* data);
Bundle* _Nullable app_get_parameters(App app);
Bundle* _Nullable tt_app_get_parameters(App app);
#ifdef __cplusplus
}

View File

@ -3,7 +3,6 @@
#include "app.h"
#include "app_manifest.h"
#include "context.h"
#include "mutex.h"
#include <stdbool.h>
@ -12,7 +11,7 @@ extern "C" {
#endif
typedef struct {
FuriMutex* mutex;
Mutex* mutex;
const AppManifest* manifest;
AppState state;
AppFlags flags;

View File

@ -1,9 +1,9 @@
#include "app_manifest_registry.h"
#include "furi_core.h"
#include "m-dict.h"
#include "m_cstr_dup.h"
#include "mutex.h"
#include "tactility_core.h"
#define TAG "app_registry"
@ -21,47 +21,47 @@ DICT_DEF2(AppManifestDict, const char*, M_CSTR_DUP_OPLIST, const AppManifest*, M
}
AppManifestDict_t app_manifest_dict;
FuriMutex* mutex = NULL;
Mutex* mutex = NULL;
void app_manifest_registry_init() {
furi_assert(mutex == NULL);
mutex = furi_mutex_alloc(FuriMutexTypeNormal);
void tt_app_manifest_registry_init() {
tt_assert(mutex == NULL);
mutex = tt_mutex_alloc(MutexTypeNormal);
AppManifestDict_init(app_manifest_dict);
}
void app_registry_lock() {
furi_assert(mutex != NULL);
furi_mutex_acquire(mutex, FuriWaitForever);
tt_assert(mutex != NULL);
tt_mutex_acquire(mutex, TtWaitForever);
}
void app_registry_unlock() {
furi_assert(mutex != NULL);
furi_mutex_release(mutex);
tt_assert(mutex != NULL);
tt_mutex_release(mutex);
}
void app_manifest_registry_add(const AppManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "adding %s", manifest->id);
void tt_app_manifest_registry_add(const AppManifest _Nonnull* manifest) {
TT_LOG_I(TAG, "adding %s", manifest->id);
app_registry_lock();
AppManifestDict_set_at(app_manifest_dict, manifest->id, manifest);
app_registry_unlock();
}
void app_manifest_registry_remove(const AppManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "removing %s", manifest->id);
void tt_app_manifest_registry_remove(const AppManifest _Nonnull* manifest) {
TT_LOG_I(TAG, "removing %s", manifest->id);
app_registry_lock();
AppManifestDict_erase(app_manifest_dict, manifest->id);
app_registry_unlock();
}
const AppManifest _Nullable* app_manifest_registry_find_by_id(const char* id) {
const AppManifest _Nullable* tt_app_manifest_registry_find_by_id(const char* id) {
app_registry_lock();
const AppManifest _Nullable** manifest = AppManifestDict_get(app_manifest_dict, id);
app_registry_unlock();
return (manifest != NULL) ? *manifest : NULL;
}
void app_manifest_registry_for_each_of_type(AppType type, void* _Nullable context, AppManifestCallback callback) {
void tt_app_manifest_registry_for_each_of_type(AppType type, void* _Nullable context, AppManifestCallback callback) {
APP_REGISTRY_FOR_EACH(manifest, {
if (manifest->type == type) {
callback(manifest, context);
@ -69,7 +69,7 @@ void app_manifest_registry_for_each_of_type(AppType type, void* _Nullable contex
});
}
void app_manifest_registry_for_each(AppManifestCallback callback, void* _Nullable context) {
void tt_app_manifest_registry_for_each(AppManifestCallback callback, void* _Nullable context) {
APP_REGISTRY_FOR_EACH(manifest, {
callback(manifest, context);
});

View File

@ -0,0 +1,20 @@
#pragma once
#include "app_manifest.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*AppManifestCallback)(const AppManifest*, void* context);
void tt_app_manifest_registry_init();
void tt_app_manifest_registry_add(const AppManifest _Nonnull* manifest);
void tt_app_manifest_registry_remove(const AppManifest _Nonnull* manifest);
const AppManifest _Nullable* tt_app_manifest_registry_find_by_id(const char* id);
void tt_app_manifest_registry_for_each(AppManifestCallback callback, void* _Nullable context);
void tt_app_manifest_registry_for_each_of_type(AppType type, void* _Nullable context, AppManifestCallback callback);
#ifdef __cplusplus
}
#endif

View File

@ -72,15 +72,15 @@ typedef struct {
BundleDict_t dict;
} BundleData;
Bundle bundle_alloc() {
Bundle tt_bundle_alloc() {
BundleData* bundle = malloc(sizeof(BundleData));
BundleDict_init(bundle->dict);
return bundle;
}
Bundle bundle_alloc_copy(Bundle source) {
Bundle tt_bundle_alloc_copy(Bundle source) {
BundleData* source_data = (BundleData*)source;
BundleData* target_data = bundle_alloc();
BundleData* target_data = tt_bundle_alloc();
BundleDict_it_t it;
for (BundleDict_it(it, source_data->dict); !BundleDict_end_p(it); BundleDict_next(it)) {
@ -93,7 +93,7 @@ Bundle bundle_alloc_copy(Bundle source) {
return target_data;
}
void bundle_free(Bundle bundle) {
void tt_bundle_free(Bundle bundle) {
BundleData* data = (BundleData*)bundle;
BundleDict_it_t it;
@ -105,28 +105,28 @@ void bundle_free(Bundle bundle) {
free(data);
}
bool bundle_get_bool(Bundle bundle, const char* key) {
bool tt_bundle_get_bool(Bundle bundle, const char* key) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key);
furi_check(entry != NULL);
tt_check(entry != NULL);
return (*entry)->bool_value;
}
int bundle_get_int(Bundle bundle, const char* key) {
int tt_bundle_get_int(Bundle bundle, const char* key) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key);
furi_check(entry != NULL);
tt_check(entry != NULL);
return (*entry)->int_value;
}
const char* bundle_get_string(Bundle bundle, const char* key) {
const char* tt_bundle_get_string(Bundle bundle, const char* key) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key);
furi_check(entry != NULL);
tt_check(entry != NULL);
return (*entry)->string_ptr;
}
bool bundle_opt_bool(Bundle bundle, const char* key, bool* out) {
bool tt_bundle_opt_bool(Bundle bundle, const char* key, bool* out) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key);
if (entry != NULL) {
@ -137,7 +137,7 @@ bool bundle_opt_bool(Bundle bundle, const char* key, bool* out) {
}
}
bool bundle_opt_int(Bundle bundle, const char* key, int* out) {
bool tt_bundle_opt_int(Bundle bundle, const char* key, int* out) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key);
if (entry != NULL) {
@ -148,7 +148,7 @@ bool bundle_opt_int(Bundle bundle, const char* key, int* out) {
}
}
bool bundle_opt_string(Bundle bundle, const char* key, char** out) {
bool tt_bundle_opt_string(Bundle bundle, const char* key, char** out) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key);
if (entry != NULL) {
@ -159,12 +159,12 @@ bool bundle_opt_string(Bundle bundle, const char* key, char** out) {
}
}
void bundle_put_bool(Bundle bundle, const char* key, bool value) {
void tt_bundle_put_bool(Bundle bundle, const char* key, bool value) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry_handle = BundleDict_get(data->dict, key);
if (entry_handle != NULL) {
BundleEntry* entry = *entry_handle;
furi_assert(entry->type == BUNDLE_ENTRY_TYPE_BOOL);
tt_assert(entry->type == BUNDLE_ENTRY_TYPE_BOOL);
entry->bool_value = value;
} else {
BundleEntry* entry = bundle_entry_alloc_bool(value);
@ -172,12 +172,12 @@ void bundle_put_bool(Bundle bundle, const char* key, bool value) {
}
}
void bundle_put_int(Bundle bundle, const char* key, int value) {
void tt_bundle_put_int(Bundle bundle, const char* key, int value) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry_handle = BundleDict_get(data->dict, key);
if (entry_handle != NULL) {
BundleEntry* entry = *entry_handle;
furi_assert(entry->type == BUNDLE_ENTRY_TYPE_INT);
tt_assert(entry->type == BUNDLE_ENTRY_TYPE_INT);
entry->int_value = value;
} else {
BundleEntry* entry = bundle_entry_alloc_int(value);
@ -185,12 +185,12 @@ void bundle_put_int(Bundle bundle, const char* key, int value) {
}
}
void bundle_put_string(Bundle bundle, const char* key, const char* value) {
void tt_bundle_put_string(Bundle bundle, const char* key, const char* value) {
BundleData* data = (BundleData*)bundle;
BundleEntry** entry_handle = BundleDict_get(data->dict, key);
if (entry_handle != NULL) {
BundleEntry* entry = *entry_handle;
furi_assert(entry->type == BUNDLE_ENTRY_TYPE_STRING);
tt_assert(entry->type == BUNDLE_ENTRY_TYPE_STRING);
if (entry->string_ptr != NULL) {
free(entry->string_ptr);
}

View File

@ -0,0 +1,34 @@
/**
* @brief key-value storage for general purpose.
* Maps strings on a fixed set of data types.
*/
#pragma once
#include <stdio.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Bundle;
Bundle tt_bundle_alloc();
Bundle tt_bundle_alloc_copy(Bundle source);
void tt_bundle_free(Bundle bundle);
bool tt_bundle_get_bool(Bundle bundle, const char* key);
int tt_bundle_get_int(Bundle bundle, const char* key);
const char* tt_bundle_get_string(Bundle bundle, const char* key);
bool tt_bundle_opt_bool(Bundle bundle, const char* key, bool* out);
bool tt_bundle_opt_int(Bundle bundle, const char* key, int* out);
bool tt_bundle_opt_string(Bundle bundle, const char* key, char** out);
void tt_bundle_put_bool(Bundle bundle, const char* key, bool value);
void tt_bundle_put_int(Bundle bundle, const char* key, int value);
void tt_bundle_put_string(Bundle bundle, const char* key, const char* value);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,38 @@
#include "check.h"
#include "core_defines.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "log.h"
#define TAG "kernel"
static void tt_print_memory_info() {
TT_LOG_E(TAG, "default caps:");
TT_LOG_E(TAG, " total: %u", heap_caps_get_total_size(MALLOC_CAP_DEFAULT));
TT_LOG_E(TAG, " free: %u", heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
TT_LOG_E(TAG, " min free: %u", heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT));
TT_LOG_E(TAG, "internal caps:");
TT_LOG_E(TAG, " total: %u", heap_caps_get_total_size(MALLOC_CAP_INTERNAL));
TT_LOG_E(TAG, " free: %u", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
TT_LOG_E(TAG, " min free: %u", heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL));
}
static void tt_print_task_info() {
if (TT_IS_IRQ_MODE()) {
TT_LOG_E(TAG, "Task: ISR %lu", __get_IPSR());
} else {
const char* name = pcTaskGetName(NULL);
const char* safe_name = name ? name : "main";
TT_LOG_E(TAG, "Task: %s", safe_name);
TT_LOG_E(TAG, "Stack watermark: %u", uxTaskGetStackHighWaterMark(NULL) * 4);
}
}
TT_NORETURN void tt_crash_implementation() {
tt_print_task_info();
tt_print_memory_info();
// TODO: Add breakpoint when debugger is attached.
esp_system_abort("System halted. Connect debugger for more info.");
__builtin_unreachable();
}

View File

@ -1,7 +1,7 @@
/**
* @file check.h
*
* Furi crash and assert functions.
* Tactility crash and assert functions.
*
* The main problem with crashing is that you can't do anything without disturbing registers,
* and if you disturb registers, you won't be able to see the correct register values in the debugger.
@ -18,54 +18,44 @@
#ifdef __cplusplus
extern "C" {
#define FURI_NORETURN [[noreturn]]
#define TT_NORETURN [[noreturn]]
#else
#include <stdnoreturn.h>
#define FURI_NORETURN noreturn
#define TT_NORETURN noreturn
#endif
// Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls.
#define __FURI_ASSERT_MESSAGE_FLAG (0x01)
#define __FURI_CHECK_MESSAGE_FLAG (0x02)
/** Crash system */
FURI_NORETURN void __furi_crash_implementation();
/** Halt system */
FURI_NORETURN void __furi_halt_implementation();
TT_NORETURN void tt_crash_implementation();
/** Crash system with message. */
#define __furi_crash(message) \
#define __tt_crash(message) \
do { \
ESP_LOGE("crash", "%s\n\tat %s:%d", ((message) ? (message) : ""), __FILE__, __LINE__); \
__furi_crash_implementation(); \
tt_crash_implementation(); \
} while (0)
/** Crash system
*
* @param optional message (const char*)
*/
#define furi_crash(...) M_APPLY(__furi_crash, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__)))
/** Halt system with message. */
#define __furi_halt(message) \
do { \
ESP_LOGE("halt", "%s\n\tat %s:%d", ((message) ? (message) : ""), __FILE__, __LINE__); \
__furi_halt_implementation(); \
} while (0)
#define tt_crash(...) M_APPLY(__tt_crash, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__)))
/** Halt system
*
* @param optional message (const char*)
*/
#define furi_halt(...) M_APPLY(__furi_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__)))
#define tt_halt(...) M_APPLY(__tt_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__)))
/** Check condition and crash if check failed */
#define __furi_check(__e, __m) \
#define __tt_check(__e, __m) \
do { \
if (!(__e)) { \
ESP_LOGE("check", "%s", #__e); \
__furi_crash(#__m); \
if (__m) { \
__tt_crash(#__m); \
} else { \
__tt_crash(""); \
} \
} \
} while (0)
@ -74,20 +64,24 @@ FURI_NORETURN void __furi_halt_implementation();
* @param condition to check
* @param optional message (const char*)
*/
#define furi_check(...) \
M_APPLY(__furi_check, M_DEFAULT_ARGS(2, (__FURI_CHECK_MESSAGE_FLAG), __VA_ARGS__))
#define tt_check(...) \
M_APPLY(__tt_check, M_DEFAULT_ARGS(2, NULL, __VA_ARGS__))
/** Only in debug build: Assert condition and crash if assert failed */
#ifdef FURI_DEBUG
#define __furi_assert(__e, __m) \
#ifdef TT_DEBUG
#define __tt_assert(__e, __m) \
do { \
if (!(__e)) { \
ESP_LOGE("assert", "%s", #__e); \
__furi_crash(#__m); \
if (__m) { \
__tt_crash(#__m); \
} else { \
__tt_crash(""); \
} \
} \
} while (0)
#else
#define __furi_assert(__e, __m) \
#define __tt_assert(__e, __m) \
do { \
((void)(__e)); \
((void)(__m)); \
@ -101,8 +95,8 @@ FURI_NORETURN void __furi_halt_implementation();
* @param condition to check
* @param optional message (const char*)
*/
#define furi_assert(...) \
M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__))
#define tt_assert(...) \
M_APPLY(__tt_assert, M_DEFAULT_ARGS(2, NULL, __VA_ARGS__))
#ifdef __cplusplus
}

View File

@ -1,15 +1,13 @@
#include "furi.h"
#include "core.h"
#include "app_manifest_registry.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "service_registry.h"
#define TAG "furi"
#define TAG "tactility"
void furi_init() {
FURI_LOG_I(TAG, "init start");
furi_assert(!furi_kernel_is_irq());
void tt_core_init() {
TT_LOG_I(TAG, "core init start");
tt_assert(!tt_kernel_is_irq());
#if defined(__ARM_ARCH_7A__) && (__ARM_ARCH_7A__ == 0U)
/* Service Call interrupt might be configured before kernel start */
@ -18,7 +16,7 @@ void furi_init() {
NVIC_SetPriority(SVCall_IRQn, 0U);
#endif
service_registry_init();
app_manifest_registry_init();
FURI_LOG_I(TAG, "init complete");
tt_service_registry_init();
tt_app_manifest_registry_init();
TT_LOG_I(TAG, "core init complete");
}

View File

@ -2,9 +2,8 @@
#include <stdlib.h>
#include "furi_core.h"
#include "tactility_core.h"
#include "furi_string.h"
#include "event_flag.h"
#include "kernel.h"
#include "message_queue.h"
@ -15,12 +14,13 @@
#include "string.h"
#include "thread.h"
#include "timer.h"
#include "tt_string.h"
#ifdef __cplusplus
extern "C" {
#endif
void furi_init();
void tt_core_init();
#ifdef __cplusplus
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "core_extra_defines.h"
#include "freertos/portmacro.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <cmsis_compiler.h>
#define TT_RETURNS_NONNULL __attribute__((returns_nonnull))
#ifndef TT_WARN_UNUSED
#define TT_WARN_UNUSED __attribute__((warn_unused_result))
#endif
#ifndef TT_WEAK
#define TT_WEAK __attribute__((weak))
#endif
#ifndef TT_PACKED
#define TT_PACKED __attribute__((packed))
#endif
// Used by portENABLE_INTERRUPTS and portDISABLE_INTERRUPTS?
#ifndef TT_IS_IRQ_MODE
#define TT_IS_IRQ_MODE() (xPortInIsrContext() == pdTRUE)
#endif
#ifndef TT_IS_ISR
#define TT_IS_ISR() (TT_IS_IRQ_MODE())
#endif
#ifndef TT_CHECK_RETURN
#define TT_CHECK_RETURN __attribute__((__warn_unused_result__))
#endif
#ifdef __cplusplus
}
#endif

View File

@ -4,8 +4,6 @@
extern "C" {
#endif
#define FURI_RETURNS_NONNULL __attribute__((returns_nonnull))
#ifndef MAX
#define MAX(a, b) \
({ \
@ -45,8 +43,8 @@ extern "C" {
#define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
#endif
#ifndef FURI_SWAP
#define FURI_SWAP(x, y) \
#ifndef TT_SWAP
#define TT_SWAP(x, y) \
do { \
typeof(x) SWAP = x; \
x = y; \
@ -89,27 +87,27 @@ extern "C" {
(((x) & 0xFF000000) >> 24))
#endif
#ifndef FURI_BIT
#define FURI_BIT(x, n) (((x) >> (n)) & 1)
#ifndef TT_BIT
#define TT_BIT(x, n) (((x) >> (n)) & 1)
#endif
#ifndef FURI_BIT_SET
#define FURI_BIT_SET(x, n) \
#ifndef TT_BIT_SET
#define TT_BIT_SET(x, n) \
({ \
__typeof__(x) _x = (1); \
(x) |= (_x << (n)); \
})
#endif
#ifndef FURI_BIT_CLEAR
#define FURI_BIT_CLEAR(x, n) \
#ifndef TT_BIT_CLEAR
#define TT_BIT_CLEAR(x, n) \
({ \
__typeof__(x) _x = (1); \
(x) &= ~(_x << (n)); \
})
#endif
#define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory")
#define TT_SW_MEMBARRIER() asm volatile("" : : : "memory")
#ifdef __cplusplus
}

View File

@ -0,0 +1,44 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <tactility_core_config.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
TtWaitForever = 0xFFFFFFFFU,
} TtWait;
typedef enum {
TtFlagWaitAny = 0x00000000U, ///< Wait for any flag (default).
TtFlagWaitAll = 0x00000001U, ///< Wait for all flags.
TtFlagNoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for.
TtFlagError = 0x80000000U, ///< Error indicator.
TtFlagErrorUnknown = 0xFFFFFFFFU, ///< TtStatusError (-1).
TtFlagErrorTimeout = 0xFFFFFFFEU, ///< TtStatusErrorTimeout (-2).
TtFlagErrorResource = 0xFFFFFFFDU, ///< TtStatusErrorResource (-3).
TtFlagErrorParameter = 0xFFFFFFFCU, ///< TtStatusErrorParameter (-4).
TtFlagErrorISR = 0xFFFFFFFAU, ///< TtStatusErrorISR (-6).
} TtFlag;
typedef enum {
TtStatusOk = 0, ///< Operation completed successfully.
TtStatusError =
-1, ///< Unspecified RTOS error: run-time error but no other error message fits.
TtStatusErrorTimeout = -2, ///< Operation not completed within the timeout period.
TtStatusErrorResource = -3, ///< Resource not available.
TtStatusErrorParameter = -4, ///< Parameter error.
TtStatusErrorNoMemory =
-5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.
TtStatusErrorISR =
-6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
TtStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
} TtStatus;
#ifdef __cplusplus
}
#endif

View File

@ -1,16 +1,16 @@
#include "critical.h"
#include "furi_core_defines.h"
#include "core_defines.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static portMUX_TYPE critical_mutex;
__FuriCriticalInfo __furi_critical_enter(void) {
__FuriCriticalInfo info;
__TtCriticalInfo __tt_critical_enter(void) {
__TtCriticalInfo info;
info.isrm = 0;
info.from_isr = FURI_IS_ISR();
info.from_isr = TT_IS_ISR();
info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING);
if (info.from_isr) {
@ -24,7 +24,7 @@ __FuriCriticalInfo __furi_critical_enter(void) {
return info;
}
void __furi_critical_exit(__FuriCriticalInfo info) {
void __tt_critical_exit(__TtCriticalInfo info) {
if (info.from_isr) {
taskEXIT_CRITICAL_FROM_ISR(info.isrm);
} else if (info.kernel_running) {

View File

@ -0,0 +1,22 @@
#pragma once
#include <stdio.h>
#include <stdbool.h>
#ifndef TT_CRITICAL_ENTER
#define TT_CRITICAL_ENTER() __TtCriticalInfo __tt_critical_info = __tt_critical_enter();
#endif
#ifndef TT_CRITICAL_EXIT
#define TT_CRITICAL_EXIT() __tt_critical_exit(__tt_critical_info);
#endif
typedef struct {
uint32_t isrm;
bool from_isr;
bool kernel_running;
} __TtCriticalInfo;
__TtCriticalInfo __tt_critical_enter(void);
void __tt_critical_exit(__TtCriticalInfo info);

View File

@ -1,39 +1,39 @@
#include "event_flag.h"
#include "check.h"
#include "furi_core_defines.h"
#include "core_defines.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U
#define FURI_EVENT_FLAG_INVALID_BITS (~((1UL << FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U))
#define TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U
#define TT_EVENT_FLAG_INVALID_BITS (~((1UL << TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U))
FuriEventFlag* furi_event_flag_alloc() {
furi_assert(!FURI_IS_IRQ_MODE());
EventFlag* tt_event_flag_alloc() {
tt_assert(!TT_IS_IRQ_MODE());
EventGroupHandle_t handle = xEventGroupCreate();
furi_check(handle);
tt_check(handle);
return ((FuriEventFlag*)handle);
return ((EventFlag*)handle);
}
void furi_event_flag_free(FuriEventFlag* instance) {
furi_assert(!FURI_IS_IRQ_MODE());
void tt_event_flag_free(EventFlag* instance) {
tt_assert(!TT_IS_IRQ_MODE());
vEventGroupDelete((EventGroupHandle_t)instance);
}
uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {
furi_assert(instance);
furi_assert((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U);
uint32_t tt_event_flag_set(EventFlag* instance, uint32_t flags) {
tt_assert(instance);
tt_assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
uint32_t rflags;
BaseType_t yield;
if (FURI_IS_IRQ_MODE()) {
if (TT_IS_IRQ_MODE()) {
yield = pdFALSE;
if (xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {
rflags = (uint32_t)FuriFlagErrorResource;
rflags = (uint32_t)TtFlagErrorResource;
} else {
rflags = flags;
portYIELD_FROM_ISR(yield);
@ -46,18 +46,18 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {
return (rflags);
}
uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) {
furi_assert(instance);
furi_assert((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U);
uint32_t tt_event_flag_clear(EventFlag* instance, uint32_t flags) {
tt_assert(instance);
tt_assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
uint32_t rflags;
if (FURI_IS_IRQ_MODE()) {
if (TT_IS_IRQ_MODE()) {
rflags = xEventGroupGetBitsFromISR(hEventGroup);
if (xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) {
rflags = (uint32_t)FuriStatusErrorResource;
rflags = (uint32_t)TtStatusErrorResource;
} else {
/* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */
/* Yield is required here otherwise clear operation might not execute in the right order. */
@ -72,13 +72,13 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) {
return (rflags);
}
uint32_t furi_event_flag_get(FuriEventFlag* instance) {
furi_assert(instance);
uint32_t tt_event_flag_get(EventFlag* instance) {
tt_assert(instance);
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
uint32_t rflags;
if (FURI_IS_IRQ_MODE()) {
if (TT_IS_IRQ_MODE()) {
rflags = xEventGroupGetBitsFromISR(hEventGroup);
} else {
rflags = xEventGroupGetBits(hEventGroup);
@ -88,28 +88,28 @@ uint32_t furi_event_flag_get(FuriEventFlag* instance) {
return (rflags);
}
uint32_t furi_event_flag_wait(
FuriEventFlag* instance,
uint32_t tt_event_flag_wait(
EventFlag* instance,
uint32_t flags,
uint32_t options,
uint32_t timeout
) {
furi_assert(!FURI_IS_IRQ_MODE());
furi_assert(instance);
furi_assert((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U);
tt_assert(!TT_IS_IRQ_MODE());
tt_assert(instance);
tt_assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
BaseType_t wait_all;
BaseType_t exit_clr;
uint32_t rflags;
if (options & FuriFlagWaitAll) {
if (options & TtFlagWaitAll) {
wait_all = pdTRUE;
} else {
wait_all = pdFAIL;
}
if (options & FuriFlagNoClear) {
if (options & TtFlagNoClear) {
exit_clr = pdFAIL;
} else {
exit_clr = pdTRUE;
@ -123,20 +123,20 @@ uint32_t furi_event_flag_wait(
(TickType_t)timeout
);
if (options & FuriFlagWaitAll) {
if (options & TtFlagWaitAll) {
if ((flags & rflags) != flags) {
if (timeout > 0U) {
rflags = (uint32_t)FuriStatusErrorTimeout;
rflags = (uint32_t)TtStatusErrorTimeout;
} else {
rflags = (uint32_t)FuriStatusErrorResource;
rflags = (uint32_t)TtStatusErrorResource;
}
}
} else {
if ((flags & rflags) == 0U) {
if (timeout > 0U) {
rflags = (uint32_t)FuriStatusErrorTimeout;
rflags = (uint32_t)TtStatusErrorTimeout;
} else {
rflags = (uint32_t)FuriStatusErrorResource;
rflags = (uint32_t)TtStatusErrorResource;
}
}
}

View File

@ -0,0 +1,67 @@
#pragma once
#include "core_types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void EventFlag;
/** Allocate EventFlag
*
* @return pointer to EventFlag
*/
EventFlag* tt_event_flag_alloc();
/** Deallocate EventFlag
*
* @param instance pointer to EventFlag
*/
void tt_event_flag_free(EventFlag* instance);
/** Set flags
*
* @param instance pointer to EventFlag
* @param[in] flags The flags
*
* @return Resulting flags or error (TtStatus)
*/
uint32_t tt_event_flag_set(EventFlag* instance, uint32_t flags);
/** Clear flags
*
* @param instance pointer to EventFlag
* @param[in] flags The flags
*
* @return Resulting flags or error (TtStatus)
*/
uint32_t tt_event_flag_clear(EventFlag* instance, uint32_t flags);
/** Get flags
*
* @param instance pointer to EventFlag
*
* @return Resulting flags
*/
uint32_t tt_event_flag_get(EventFlag* instance);
/** Wait flags
*
* @param instance pointer to EventFlag
* @param[in] flags The flags
* @param[in] options The option flags
* @param[in] timeout The timeout
*
* @return Resulting flags or error (TtStatus)
*/
uint32_t tt_event_flag_wait(
EventFlag* instance,
uint32_t flags,
uint32_t options,
uint32_t timeout
);
#ifdef __cplusplus
}
#endif

View File

@ -1,22 +1,22 @@
#include "kernel.h"
#include "check.h"
#include "furi_core_defines.h"
#include "furi_core_types.h"
#include "core_defines.h"
#include "core_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <rom/ets_sys.h>
bool furi_kernel_is_irq() {
return FURI_IS_IRQ_MODE();
bool tt_kernel_is_irq() {
return TT_IS_IRQ_MODE();
}
bool furi_kernel_is_running() {
bool tt_kernel_is_running() {
return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING;
}
int32_t furi_kernel_lock() {
furi_assert(!furi_kernel_is_irq());
int32_t tt_kernel_lock() {
tt_assert(!tt_kernel_is_irq());
int32_t lock;
@ -32,7 +32,7 @@ int32_t furi_kernel_lock() {
case taskSCHEDULER_NOT_STARTED:
default:
lock = (int32_t)FuriStatusError;
lock = (int32_t)TtStatusError;
break;
}
@ -40,8 +40,8 @@ int32_t furi_kernel_lock() {
return (lock);
}
int32_t furi_kernel_unlock() {
furi_assert(!furi_kernel_is_irq());
int32_t tt_kernel_unlock() {
tt_assert(!tt_kernel_is_irq());
int32_t lock;
@ -51,7 +51,7 @@ int32_t furi_kernel_unlock() {
if (xTaskResumeAll() != pdTRUE) {
if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) {
lock = (int32_t)FuriStatusError;
lock = (int32_t)TtStatusError;
}
}
break;
@ -62,7 +62,7 @@ int32_t furi_kernel_unlock() {
case taskSCHEDULER_NOT_STARTED:
default:
lock = (int32_t)FuriStatusError;
lock = (int32_t)TtStatusError;
break;
}
@ -70,8 +70,8 @@ int32_t furi_kernel_unlock() {
return (lock);
}
int32_t furi_kernel_restore_lock(int32_t lock) {
furi_assert(!furi_kernel_is_irq());
int32_t tt_kernel_restore_lock(int32_t lock) {
tt_assert(!tt_kernel_is_irq());
switch (xTaskGetSchedulerState()) {
case taskSCHEDULER_SUSPENDED:
@ -80,11 +80,11 @@ int32_t furi_kernel_restore_lock(int32_t lock) {
vTaskSuspendAll();
} else {
if (lock != 0) {
lock = (int32_t)FuriStatusError;
lock = (int32_t)TtStatusError;
} else {
if (xTaskResumeAll() != pdTRUE) {
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
lock = (int32_t)FuriStatusError;
lock = (int32_t)TtStatusError;
}
}
}
@ -93,7 +93,7 @@ int32_t furi_kernel_restore_lock(int32_t lock) {
case taskSCHEDULER_NOT_STARTED:
default:
lock = (int32_t)FuriStatusError;
lock = (int32_t)TtStatusError;
break;
}
@ -101,13 +101,13 @@ int32_t furi_kernel_restore_lock(int32_t lock) {
return (lock);
}
uint32_t furi_kernel_get_tick_frequency() {
uint32_t tt_kernel_get_tick_frequency() {
/* Return frequency in hertz */
return (configTICK_RATE_HZ_RAW);
}
void furi_delay_tick(uint32_t ticks) {
furi_assert(!furi_kernel_is_irq());
void tt_delay_tick(uint32_t ticks) {
tt_assert(!tt_kernel_is_irq());
if (ticks == 0U) {
taskYIELD();
} else {
@ -115,13 +115,13 @@ void furi_delay_tick(uint32_t ticks) {
}
}
FuriStatus furi_delay_until_tick(uint32_t tick) {
furi_assert(!furi_kernel_is_irq());
TtStatus tt_delay_until_tick(uint32_t tick) {
tt_assert(!tt_kernel_is_irq());
TickType_t tcnt, delay;
FuriStatus stat;
TtStatus stat;
stat = FuriStatusOk;
stat = TtStatusOk;
tcnt = xTaskGetTickCount();
/* Determine remaining number of tick to delay */
@ -131,21 +131,21 @@ FuriStatus furi_delay_until_tick(uint32_t tick) {
if ((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) {
if (xTaskDelayUntil(&tcnt, delay) == pdFALSE) {
/* Did not delay */
stat = FuriStatusError;
stat = TtStatusError;
}
} else {
/* No delay or already expired */
stat = FuriStatusErrorParameter;
stat = TtStatusErrorParameter;
}
/* Return execution status */
return (stat);
}
uint32_t furi_get_tick() {
uint32_t tt_get_tick() {
TickType_t ticks;
if (furi_kernel_is_irq() != 0U) {
if (tt_kernel_is_irq() != 0U) {
ticks = xTaskGetTickCountFromISR();
} else {
ticks = xTaskGetTickCount();
@ -154,7 +154,7 @@ uint32_t furi_get_tick() {
return ticks;
}
uint32_t furi_ms_to_ticks(uint32_t milliseconds) {
uint32_t tt_ms_to_ticks(uint32_t milliseconds) {
#if configTICK_RATE_HZ_RAW == 1000
return milliseconds;
#else
@ -162,21 +162,21 @@ uint32_t furi_ms_to_ticks(uint32_t milliseconds) {
#endif
}
void furi_delay_ms(uint32_t milliseconds) {
void tt_delay_ms(uint32_t milliseconds) {
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
if (milliseconds > 0 && milliseconds < portMAX_DELAY - 1) {
milliseconds += 1;
}
#if configTICK_RATE_HZ_RAW == 1000
furi_delay_tick(milliseconds);
tt_delay_tick(milliseconds);
#else
furi_delay_tick(furi_ms_to_ticks(milliseconds));
tt_delay_tick(tt_ms_to_ticks(milliseconds));
#endif
} else if (milliseconds > 0) {
furi_delay_us(milliseconds * 1000);
tt_delay_us(milliseconds * 1000);
}
}
void furi_delay_us(uint32_t microseconds) {
void tt_delay_us(uint32_t microseconds) {
ets_delay_us(microseconds);
}

View File

@ -1,6 +1,6 @@
#pragma once
#include "furi_core_types.h"
#include "core_types.h"
#define configTICK_RATE_HZ_RAW CONFIG_FREERTOS_HZ
@ -23,13 +23,13 @@ extern "C" {
*
* @return true if CPU is in IRQ or kernel running and IRQ is masked
*/
bool furi_kernel_is_irq();
bool tt_kernel_is_irq();
/** Check if kernel is running
*
* @return true if running, false otherwise
*/
bool furi_kernel_is_running();
bool tt_kernel_is_running();
/** Lock kernel, pause process scheduling
*
@ -37,7 +37,7 @@ bool furi_kernel_is_running();
*
* @return previous lock state(0 - unlocked, 1 - locked)
*/
int32_t furi_kernel_lock();
int32_t tt_kernel_lock();
/** Unlock kernel, resume process scheduling
*
@ -45,7 +45,7 @@ int32_t furi_kernel_lock();
*
* @return previous lock state(0 - unlocked, 1 - locked)
*/
int32_t furi_kernel_unlock();
int32_t tt_kernel_unlock();
/** Restore kernel lock state
*
@ -55,13 +55,13 @@ int32_t furi_kernel_unlock();
*
* @return new lock state or error
*/
int32_t furi_kernel_restore_lock(int32_t lock);
int32_t tt_kernel_restore_lock(int32_t lock);
/** Get kernel systick frequency
*
* @return systick counts per second
*/
uint32_t furi_kernel_get_tick_frequency();
uint32_t tt_kernel_get_tick_frequency();
/** Delay execution
*
@ -71,7 +71,7 @@ uint32_t furi_kernel_get_tick_frequency();
*
* @param[in] ticks The ticks count to pause
*/
void furi_delay_tick(uint32_t ticks);
void tt_delay_tick(uint32_t ticks);
/** Delay until tick
*
@ -79,9 +79,9 @@ void furi_delay_tick(uint32_t ticks);
*
* @param[in] ticks The tick until which kerel should delay task execution
*
* @return The furi status.
* @return The status.
*/
FuriStatus furi_delay_until_tick(uint32_t tick);
TtStatus tt_delay_until_tick(uint32_t tick);
/** Get current tick counter
*
@ -89,27 +89,27 @@ FuriStatus furi_delay_until_tick(uint32_t tick);
*
* @return Current ticks in milliseconds
*/
uint32_t furi_get_tick(void);
uint32_t tt_get_tick(void);
/** Convert milliseconds to ticks
*
* @param[in] milliseconds time in milliseconds
* @return time in ticks
*/
uint32_t furi_ms_to_ticks(uint32_t milliseconds);
uint32_t tt_ms_to_ticks(uint32_t milliseconds);
/** Delay in milliseconds
*
* This method uses kernel ticks on the inside, which causes delay to be aliased to scheduler timer intervals.
* Real wait time will be between X+ milliseconds.
* Special value: 0, will cause task yield.
* Also if used when kernel is not running will fall back to `furi_delay_us`.
* Also if used when kernel is not running will fall back to `tt_delay_us`.
*
* @warning Cannot be used from ISR
*
* @param[in] milliseconds milliseconds to wait
*/
void furi_delay_ms(uint32_t milliseconds);
void tt_delay_ms(uint32_t milliseconds);
/** Delay in microseconds
*
@ -117,7 +117,7 @@ void furi_delay_ms(uint32_t milliseconds);
*
* @param[in] microseconds microseconds to wait
*/
void furi_delay_us(uint32_t microseconds);
void tt_delay_us(uint32_t microseconds);
#ifdef __cplusplus
}

View File

@ -6,15 +6,15 @@
extern "C" {
#endif
#define FURI_LOG_E(tag, format, ...) \
#define TT_LOG_E(tag, format, ...) \
ESP_LOGE(tag, format, ##__VA_ARGS__)
#define FURI_LOG_W(tag, format, ...) \
#define TT_LOG_W(tag, format, ...) \
ESP_LOGW(tag, format, ##__VA_ARGS__)
#define FURI_LOG_I(tag, format, ...) \
#define TT_LOG_I(tag, format, ...) \
ESP_LOGI(tag, format, ##__VA_ARGS__)
#define FURI_LOG_D(tag, format, ...) \
#define TT_LOG_D(tag, format, ...) \
ESP_LOGD(tag, format, ##__VA_ARGS__)
#define FURI_LOG_T(tag, format, ...) \
#define TT_LOG_T(tag, format, ...) \
ESP_LOGT(tag, format, ##__VA_ARGS__)
#ifdef __cplusplus

View File

@ -5,51 +5,50 @@
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) {
furi_assert((furi_kernel_is_irq() == 0U) && (msg_count > 0U) && (msg_size > 0U));
MessageQueue* tt_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) {
tt_assert((tt_kernel_is_irq() == 0U) && (msg_count > 0U) && (msg_size > 0U));
QueueHandle_t handle = xQueueCreate(msg_count, msg_size);
furi_check(handle);
tt_check(handle);
return ((FuriMessageQueue*)handle);
return ((MessageQueue*)handle);
}
void furi_message_queue_free(FuriMessageQueue* instance) {
furi_assert(furi_kernel_is_irq() == 0U);
furi_assert(instance);
void tt_message_queue_free(MessageQueue* instance) {
tt_assert(tt_kernel_is_irq() == 0U);
tt_assert(instance);
vQueueDelete((QueueHandle_t)instance);
}
FuriStatus
furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t timeout) {
TtStatus tt_message_queue_put(MessageQueue* instance, const void* msg_ptr, uint32_t timeout) {
QueueHandle_t hQueue = (QueueHandle_t)instance;
FuriStatus stat;
TtStatus stat;
BaseType_t yield;
stat = FuriStatusOk;
stat = TtStatusOk;
if (furi_kernel_is_irq() != 0U) {
if (tt_kernel_is_irq() != 0U) {
if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
stat = FuriStatusErrorParameter;
stat = TtStatusErrorParameter;
} else {
yield = pdFALSE;
if (xQueueSendToBackFromISR(hQueue, msg_ptr, &yield) != pdTRUE) {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
} else {
portYIELD_FROM_ISR(yield);
}
}
} else {
if ((hQueue == NULL) || (msg_ptr == NULL)) {
stat = FuriStatusErrorParameter;
stat = TtStatusErrorParameter;
} else {
if (xQueueSendToBack(hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) {
if (timeout != 0U) {
stat = FuriStatusErrorTimeout;
stat = TtStatusErrorTimeout;
} else {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
}
}
}
@ -59,34 +58,34 @@ furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t
return (stat);
}
FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout_ticks) {
TtStatus tt_message_queue_get(MessageQueue* instance, void* msg_ptr, uint32_t timeout_ticks) {
QueueHandle_t hQueue = (QueueHandle_t)instance;
FuriStatus stat;
TtStatus stat;
BaseType_t yield;
stat = FuriStatusOk;
stat = TtStatusOk;
if (furi_kernel_is_irq() != 0U) {
if (tt_kernel_is_irq() != 0U) {
if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout_ticks != 0U)) {
stat = FuriStatusErrorParameter;
stat = TtStatusErrorParameter;
} else {
yield = pdFALSE;
if (xQueueReceiveFromISR(hQueue, msg_ptr, &yield) != pdPASS) {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
} else {
portYIELD_FROM_ISR(yield);
}
}
} else {
if ((hQueue == NULL) || (msg_ptr == NULL)) {
stat = FuriStatusErrorParameter;
stat = TtStatusErrorParameter;
} else {
if (xQueueReceive(hQueue, msg_ptr, (TickType_t)timeout_ticks) != pdPASS) {
if (timeout_ticks != 0U) {
stat = FuriStatusErrorTimeout;
stat = TtStatusErrorTimeout;
} else {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
}
}
}
@ -96,7 +95,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin
return (stat);
}
uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) {
uint32_t tt_message_queue_get_capacity(MessageQueue* instance) {
StaticQueue_t* mq = (StaticQueue_t*)instance;
uint32_t capacity;
@ -111,7 +110,7 @@ uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) {
return (capacity);
}
uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) {
uint32_t tt_message_queue_get_message_size(MessageQueue* instance) {
StaticQueue_t* mq = (StaticQueue_t*)instance;
uint32_t size;
@ -126,13 +125,13 @@ uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) {
return (size);
}
uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {
uint32_t tt_message_queue_get_count(MessageQueue* instance) {
QueueHandle_t hQueue = (QueueHandle_t)instance;
UBaseType_t count;
if (hQueue == NULL) {
count = 0U;
} else if (furi_kernel_is_irq() != 0U) {
} else if (tt_kernel_is_irq() != 0U) {
count = uxQueueMessagesWaitingFromISR(hQueue);
} else {
count = uxQueueMessagesWaiting(hQueue);
@ -142,14 +141,14 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {
return ((uint32_t)count);
}
uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
uint32_t tt_message_queue_get_space(MessageQueue* instance) {
StaticQueue_t* mq = (StaticQueue_t*)instance;
uint32_t space;
uint32_t isrm;
if (mq == NULL) {
space = 0U;
} else if (furi_kernel_is_irq() != 0U) {
} else if (tt_kernel_is_irq() != 0U) {
isrm = taskENTER_CRITICAL_FROM_ISR();
/* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */
@ -164,16 +163,16 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
return (space);
}
FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) {
TtStatus tt_message_queue_reset(MessageQueue* instance) {
QueueHandle_t hQueue = (QueueHandle_t)instance;
FuriStatus stat;
TtStatus stat;
if (furi_kernel_is_irq() != 0U) {
stat = FuriStatusErrorISR;
if (tt_kernel_is_irq() != 0U) {
stat = TtStatusErrorISR;
} else if (hQueue == NULL) {
stat = FuriStatusErrorParameter;
stat = TtStatusErrorParameter;
} else {
stat = FuriStatusOk;
stat = TtStatusOk;
(void)xQueueReset(hQueue);
}

View File

@ -0,0 +1,94 @@
/**
* @file message_queue.h
* MessageQueue
*/
#pragma once
#include "core_types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void MessageQueue;
/** Allocate message queue
*
* @param[in] msg_count The message count
* @param[in] msg_size The message size
*
* @return pointer to MessageQueue instance
*/
MessageQueue* tt_message_queue_alloc(uint32_t msg_count, uint32_t msg_size);
/** Free queue
*
* @param instance pointer to MessageQueue instance
*/
void tt_message_queue_free(MessageQueue* instance);
/** Put message into queue
*
* @param instance pointer to MessageQueue instance
* @param[in] msg_ptr The message pointer
* @param[in] timeout The timeout
* @param[in] msg_prio The message prio
*
* @return The status.
*/
TtStatus tt_message_queue_put(MessageQueue* instance, const void* msg_ptr, uint32_t timeout);
/** Get message from queue
*
* @param instance pointer to MessageQueue instance
* @param msg_ptr The message pointer
* @param msg_prio The message prioority
* @param[in] timeout_ticks The timeout
*
* @return The status.
*/
TtStatus tt_message_queue_get(MessageQueue* instance, void* msg_ptr, uint32_t timeout_ticks);
/** Get queue capacity
*
* @param instance pointer to MessageQueue instance
*
* @return capacity in object count
*/
uint32_t tt_message_queue_get_capacity(MessageQueue* instance);
/** Get message size
*
* @param instance pointer to MessageQueue instance
*
* @return Message size in bytes
*/
uint32_t tt_message_queue_get_message_size(MessageQueue* instance);
/** Get message count in queue
*
* @param instance pointer to MessageQueue instance
*
* @return Message count
*/
uint32_t tt_message_queue_get_count(MessageQueue* instance);
/** Get queue available space
*
* @param instance pointer to MessageQueue instance
*
* @return Message count
*/
uint32_t tt_message_queue_get_space(MessageQueue* instance);
/** Reset queue
*
* @param instance pointer to MessageQueue instance
*
* @return The status.
*/
TtStatus tt_message_queue_reset(MessageQueue* instance);
#ifdef __cplusplus
}
#endif

View File

@ -1,45 +1,45 @@
#include "mutex.h"
#include "check.h"
#include "furi_core_defines.h"
#include "core_defines.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "log.h"
FuriMutex* furi_mutex_alloc(FuriMutexType type) {
furi_assert(!FURI_IS_IRQ_MODE());
Mutex* tt_mutex_alloc(MutexType type) {
tt_assert(!TT_IS_IRQ_MODE());
SemaphoreHandle_t hMutex = NULL;
if (type == FuriMutexTypeNormal) {
if (type == MutexTypeNormal) {
hMutex = xSemaphoreCreateMutex();
} else if (type == FuriMutexTypeRecursive) {
} else if (type == MutexTypeRecursive) {
hMutex = xSemaphoreCreateRecursiveMutex();
} else {
furi_crash("Programming error");
tt_crash("Programming error");
}
furi_check(hMutex != NULL);
tt_check(hMutex != NULL);
if (type == FuriMutexTypeRecursive) {
if (type == MutexTypeRecursive) {
/* Set LSB as 'recursive mutex flag' */
hMutex = (SemaphoreHandle_t)((uint32_t)hMutex | 1U);
}
/* Return mutex ID */
return ((FuriMutex*)hMutex);
return ((Mutex*)hMutex);
}
void furi_mutex_free(FuriMutex* instance) {
furi_assert(!FURI_IS_IRQ_MODE());
furi_assert(instance);
void tt_mutex_free(Mutex* instance) {
tt_assert(!TT_IS_IRQ_MODE());
tt_assert(instance);
vSemaphoreDelete((SemaphoreHandle_t)((uint32_t)instance & ~1U));
}
FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) {
TtStatus tt_mutex_acquire(Mutex* instance, uint32_t timeout) {
SemaphoreHandle_t hMutex;
FuriStatus stat;
TtStatus stat;
uint32_t rmtx;
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
@ -47,27 +47,27 @@ FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) {
/* Extract recursive mutex flag */
rmtx = (uint32_t)instance & 1U;
stat = FuriStatusOk;
stat = TtStatusOk;
if (FURI_IS_IRQ_MODE()) {
stat = FuriStatusErrorISR;
if (TT_IS_IRQ_MODE()) {
stat = TtStatusErrorISR;
} else if (hMutex == NULL) {
stat = FuriStatusErrorParameter;
stat = TtStatusErrorParameter;
} else {
if (rmtx != 0U) {
if (xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) {
if (timeout != 0U) {
stat = FuriStatusErrorTimeout;
stat = TtStatusErrorTimeout;
} else {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
}
}
} else {
if (xSemaphoreTake(hMutex, timeout) != pdPASS) {
if (timeout != 0U) {
stat = FuriStatusErrorTimeout;
stat = TtStatusErrorTimeout;
} else {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
}
}
}
@ -77,9 +77,9 @@ FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) {
return (stat);
}
FuriStatus furi_mutex_release(FuriMutex* instance) {
TtStatus tt_mutex_release(Mutex* instance) {
SemaphoreHandle_t hMutex;
FuriStatus stat;
TtStatus stat;
uint32_t rmtx;
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
@ -87,20 +87,20 @@ FuriStatus furi_mutex_release(FuriMutex* instance) {
/* Extract recursive mutex flag */
rmtx = (uint32_t)instance & 1U;
stat = FuriStatusOk;
stat = TtStatusOk;
if (FURI_IS_IRQ_MODE()) {
stat = FuriStatusErrorISR;
if (TT_IS_IRQ_MODE()) {
stat = TtStatusErrorISR;
} else if (hMutex == NULL) {
stat = FuriStatusErrorParameter;
stat = TtStatusErrorParameter;
} else {
if (rmtx != 0U) {
if (xSemaphoreGiveRecursive(hMutex) != pdPASS) {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
}
} else {
if (xSemaphoreGive(hMutex) != pdPASS) {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
}
}
}
@ -109,16 +109,16 @@ FuriStatus furi_mutex_release(FuriMutex* instance) {
return (stat);
}
FuriThreadId furi_mutex_get_owner(FuriMutex* instance) {
ThreadId tt_mutex_get_owner(Mutex* instance) {
SemaphoreHandle_t hMutex;
FuriThreadId owner;
ThreadId owner;
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
if ((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) {
if ((TT_IS_IRQ_MODE()) || (hMutex == NULL)) {
owner = 0;
} else {
owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex);
owner = (ThreadId)xSemaphoreGetMutexHolder(hMutex);
}
/* Return owner thread ID */

View File

@ -0,0 +1,62 @@
/**
* @file mutex.h
* Mutex
*/
#pragma once
#include "core_types.h"
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
MutexTypeNormal,
MutexTypeRecursive,
} MutexType;
typedef void Mutex;
/** Allocate Mutex
*
* @param[in] type The mutex type
*
* @return pointer to Mutex instance
*/
Mutex* tt_mutex_alloc(MutexType type);
/** Free Mutex
*
* @param instance The pointer to Mutex instance
*/
void tt_mutex_free(Mutex* instance);
/** Acquire mutex
*
* @param instance The pointer to Mutex instance
* @param[in] timeout The timeout
*
* @return The status.
*/
TtStatus tt_mutex_acquire(Mutex* instance, uint32_t timeout);
/** Release mutex
*
* @param instance The pointer to Mutex instance
*
* @return The status.
*/
TtStatus tt_mutex_release(Mutex* instance);
/** Get mutex owner thread id
*
* @param instance The pointer to Mutex instance
*
* @return The thread identifier.
*/
ThreadId tt_mutex_get_owner(Mutex* instance);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,93 @@
#include "pubsub.h"
#include "check.h"
#include "mutex.h"
#include <m-list.h>
struct PubSubSubscription {
PubSubCallback callback;
void* callback_context;
};
LIST_DEF(PubSubSubscriptionList, PubSubSubscription, M_POD_OPLIST);
struct PubSub {
PubSubSubscriptionList_t items;
Mutex* mutex;
};
PubSub* tt_pubsub_alloc() {
PubSub* pubsub = malloc(sizeof(PubSub));
pubsub->mutex = tt_mutex_alloc(MutexTypeRecursive);
tt_assert(pubsub->mutex);
PubSubSubscriptionList_init(pubsub->items);
return pubsub;
}
void tt_pubsub_free(PubSub* pubsub) {
tt_assert(pubsub);
tt_check(PubSubSubscriptionList_size(pubsub->items) == 0);
PubSubSubscriptionList_clear(pubsub->items);
tt_mutex_free(pubsub->mutex);
free(pubsub);
}
PubSubSubscription* tt_pubsub_subscribe(PubSub* pubsub, PubSubCallback callback, void* callback_context) {
tt_check(tt_mutex_acquire(pubsub->mutex, TtWaitForever) == TtStatusOk);
// put uninitialized item to the list
PubSubSubscription* item = PubSubSubscriptionList_push_raw(pubsub->items);
// initialize item
item->callback = callback;
item->callback_context = callback_context;
tt_check(tt_mutex_release(pubsub->mutex) == TtStatusOk);
return item;
}
void tt_pubsub_unsubscribe(PubSub* pubsub, PubSubSubscription* pubsub_subscription) {
tt_assert(pubsub);
tt_assert(pubsub_subscription);
tt_check(tt_mutex_acquire(pubsub->mutex, TtWaitForever) == TtStatusOk);
bool result = false;
// iterate over items
PubSubSubscriptionList_it_t it;
for (PubSubSubscriptionList_it(it, pubsub->items); !PubSubSubscriptionList_end_p(it);
PubSubSubscriptionList_next(it)) {
const PubSubSubscription* item = PubSubSubscriptionList_cref(it);
// if the iterator is equal to our element
if (item == pubsub_subscription) {
PubSubSubscriptionList_remove(pubsub->items, it);
result = true;
break;
}
}
tt_check(tt_mutex_release(pubsub->mutex) == TtStatusOk);
tt_check(result);
}
void tt_pubsub_publish(PubSub* pubsub, void* message) {
tt_check(tt_mutex_acquire(pubsub->mutex, TtWaitForever) == TtStatusOk);
// iterate over subscribers
PubSubSubscriptionList_it_t it;
for (PubSubSubscriptionList_it(it, pubsub->items); !PubSubSubscriptionList_end_p(it);
PubSubSubscriptionList_next(it)) {
const PubSubSubscription* item = PubSubSubscriptionList_cref(it);
item->callback(message, item->callback_context);
}
tt_check(tt_mutex_release(pubsub->mutex) == TtStatusOk);
}

View File

@ -0,0 +1,68 @@
/**
* @file pubsub.h
* PubSub
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/** PubSub Callback type */
typedef void (*PubSubCallback)(const void* message, void* context);
/** PubSub type */
typedef struct PubSub PubSub;
/** PubSubSubscription type */
typedef struct PubSubSubscription PubSubSubscription;
/** Allocate PubSub
*
* Reentrable, Not threadsafe, one owner
*
* @return pointer to PubSub instance
*/
PubSub* tt_pubsub_alloc();
/** Free PubSub
*
* @param pubsub PubSub instance
*/
void tt_pubsub_free(PubSub* pubsub);
/** Subscribe to PubSub
*
* Threadsafe, Reentrable
*
* @param pubsub pointer to PubSub instance
* @param[in] callback The callback
* @param callback_context The callback context
*
* @return pointer to PubSubSubscription instance
*/
PubSubSubscription*
tt_pubsub_subscribe(PubSub* pubsub, PubSubCallback callback, void* callback_context);
/** Unsubscribe from PubSub
*
* No use of `pubsub_subscription` allowed after call of this method
* Threadsafe, Reentrable.
*
* @param pubsub pointer to PubSub instance
* @param pubsub_subscription pointer to PubSubSubscription instance
*/
void tt_pubsub_unsubscribe(PubSub* pubsub, PubSubSubscription* pubsub_subscription);
/** Publish message to PubSub
*
* Threadsafe, Reentrable.
*
* @param pubsub pointer to PubSub instance
* @param message message pointer to publish
*/
void tt_pubsub_publish(PubSub* pubsub, void* message);
#ifdef __cplusplus
}
#endif

View File

@ -1,13 +1,13 @@
#include "semaphore.h"
#include "check.h"
#include "furi_core_defines.h"
#include "core_defines.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count) {
furi_assert(!FURI_IS_IRQ_MODE());
furi_assert((max_count > 0U) && (initial_count <= max_count));
Semaphore* tt_semaphore_alloc(uint32_t max_count, uint32_t initial_count) {
tt_assert(!TT_IS_IRQ_MODE());
tt_assert((max_count > 0U) && (initial_count <= max_count));
SemaphoreHandle_t hSemaphore = NULL;
if (max_count == 1U) {
@ -22,37 +22,37 @@ FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count)
hSemaphore = xSemaphoreCreateCounting(max_count, initial_count);
}
furi_check(hSemaphore);
tt_check(hSemaphore);
return (FuriSemaphore*)hSemaphore;
return (Semaphore*)hSemaphore;
}
void furi_semaphore_free(FuriSemaphore* instance) {
furi_assert(instance);
furi_assert(!FURI_IS_IRQ_MODE());
void tt_semaphore_free(Semaphore* instance) {
tt_assert(instance);
tt_assert(!TT_IS_IRQ_MODE());
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
vSemaphoreDelete(hSemaphore);
}
FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {
furi_assert(instance);
TtStatus tt_semaphore_acquire(Semaphore* instance, uint32_t timeout) {
tt_assert(instance);
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
FuriStatus status;
TtStatus status;
BaseType_t yield;
status = FuriStatusOk;
status = TtStatusOk;
if (FURI_IS_IRQ_MODE()) {
if (TT_IS_IRQ_MODE()) {
if (timeout != 0U) {
status = FuriStatusErrorParameter;
status = TtStatusErrorParameter;
} else {
yield = pdFALSE;
if (xSemaphoreTakeFromISR(hSemaphore, &yield) != pdPASS) {
status = FuriStatusErrorResource;
status = TtStatusErrorResource;
} else {
portYIELD_FROM_ISR(yield);
}
@ -60,9 +60,9 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {
} else {
if (xSemaphoreTake(hSemaphore, (TickType_t)timeout) != pdPASS) {
if (timeout != 0U) {
status = FuriStatusErrorTimeout;
status = TtStatusErrorTimeout;
} else {
status = FuriStatusErrorResource;
status = TtStatusErrorResource;
}
}
}
@ -70,26 +70,26 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {
return status;
}
FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
furi_assert(instance);
TtStatus tt_semaphore_release(Semaphore* instance) {
tt_assert(instance);
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
FuriStatus stat;
TtStatus stat;
BaseType_t yield;
stat = FuriStatusOk;
stat = TtStatusOk;
if (FURI_IS_IRQ_MODE()) {
if (TT_IS_IRQ_MODE()) {
yield = pdFALSE;
if (xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
} else {
portYIELD_FROM_ISR(yield);
}
} else {
if (xSemaphoreGive(hSemaphore) != pdPASS) {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
}
}
@ -97,13 +97,13 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
return (stat);
}
uint32_t furi_semaphore_get_count(FuriSemaphore* instance) {
furi_assert(instance);
uint32_t tt_semaphore_get_count(Semaphore* instance) {
tt_assert(instance);
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
uint32_t count;
if (FURI_IS_IRQ_MODE()) {
if (TT_IS_IRQ_MODE()) {
// TODO: uxSemaphoreGetCountFromISR is not supported on esp-idf 5.1.2 - perhaps later on?
#ifdef uxSemaphoreGetCountFromISR
count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore);

View File

@ -0,0 +1,54 @@
#pragma once
#include "core_types.h"
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void Semaphore;
/** Allocate semaphore
*
* @param[in] max_count The maximum count
* @param[in] initial_count The initial count
*
* @return pointer to Semaphore instance
*/
Semaphore* tt_semaphore_alloc(uint32_t max_count, uint32_t initial_count);
/** Free semaphore
*
* @param instance The pointer to Semaphore instance
*/
void tt_semaphore_free(Semaphore* instance);
/** Acquire semaphore
*
* @param instance The pointer to Semaphore instance
* @param[in] timeout The timeout
*
* @return The status.
*/
TtStatus tt_semaphore_acquire(Semaphore* instance, uint32_t timeout);
/** Release semaphore
*
* @param instance The pointer to Semaphore instance
*
* @return The status.
*/
TtStatus tt_semaphore_release(Semaphore* instance);
/** Get semaphore count
*
* @param instance The pointer to Semaphore instance
*
* @return Semaphore count
*/
uint32_t tt_semaphore_get_count(Semaphore* instance);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,62 @@
#include "log.h"
#include "service_i.h"
#include "tactility_core.h"
// region Alloc/free
ServiceData* tt_service_alloc(const ServiceManifest* _Nonnull manifest) {
ServiceData* data = malloc(sizeof(ServiceData));
*data = (ServiceData) {
.manifest = manifest,
.mutex = tt_mutex_alloc(MutexTypeRecursive),
.data = NULL
};
return data;
}
void tt_service_free(ServiceData* data) {
tt_assert(data);
tt_mutex_free(data->mutex);
free(data);
}
// endregion Alloc/free
// region Internal
static void tt_service_lock(ServiceData * data) {
tt_mutex_acquire(data->mutex, MutexTypeRecursive);
}
static void tt_service_unlock(ServiceData* data) {
tt_mutex_release(data->mutex);
}
// endregion Internal
// region Getters & Setters
const ServiceManifest* tt_service_get_manifest(Service service) {
ServiceData* data = (ServiceData*)service;
tt_service_lock(data);
const ServiceManifest* manifest = data->manifest;
tt_service_unlock(data);
return manifest;
}
void tt_service_set_data(Service service, void* value) {
ServiceData* data = (ServiceData*)service;
tt_service_lock(data);
data->data = value;
tt_service_unlock(data);
}
void* _Nullable tt_service_get_data(Service service) {
ServiceData* data = (ServiceData*)service;
tt_service_lock(data);
void* value = data->data;
tt_service_unlock(data);
return value;
}
// endregion Getters & Setters

View File

@ -0,0 +1,18 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "service_manifest.h"
typedef void* Service;
const ServiceManifest* tt_service_get_manifest(Service service);
void tt_service_set_data(Service service, void* value);
void* _Nullable tt_service_get_data(Service service);
#ifdef __cplusplus
}
#endif

View File

@ -2,15 +2,14 @@
#include "service.h"
#include "context.h"
#include "mutex.h"
#include "service_manifest.h"
typedef struct {
FuriMutex* mutex;
Mutex* mutex;
const ServiceManifest* manifest;
void* data;
} ServiceData;
Service service_alloc(const ServiceManifest* _Nonnull manifest);
void service_free(Service _Nonnull service);
ServiceData* tt_service_alloc(const ServiceManifest* _Nonnull manifest);
void tt_service_free(ServiceData* _Nonnull service);

View File

@ -1,7 +1,6 @@
#pragma once
#include <stdio.h>
#include "context.h"
#ifdef __cplusplus
extern "C" {

View File

@ -1,10 +1,10 @@
#include "service_registry.h"
#include "furi_core.h"
#include "m-dict.h"
#include "m_cstr_dup.h"
#include "mutex.h"
#include "service_i.h"
#include "tactility_core.h"
#define TAG "service_registry"
@ -22,56 +22,56 @@ DICT_DEF2(ServiceInstanceDict, const char*, M_CSTR_DUP_OPLIST, const ServiceData
service_registry_manifest_unlock(); \
}
ServiceManifestDict_t service_manifest_dict;
ServiceInstanceDict_t service_instance_dict;
FuriMutex* manifest_mutex = NULL;
FuriMutex* instance_mutex = NULL;
static ServiceManifestDict_t service_manifest_dict;
static ServiceInstanceDict_t service_instance_dict;
static Mutex* manifest_mutex = NULL;
static Mutex* instance_mutex = NULL;
void service_registry_init() {
furi_assert(manifest_mutex == NULL);
manifest_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
void tt_service_registry_init() {
tt_assert(manifest_mutex == NULL);
manifest_mutex = tt_mutex_alloc(MutexTypeNormal);
ServiceManifestDict_init(service_manifest_dict);
furi_assert(instance_mutex == NULL);
instance_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
tt_assert(instance_mutex == NULL);
instance_mutex = tt_mutex_alloc(MutexTypeNormal);
ServiceInstanceDict_init(service_instance_dict);
}
void service_registry_instance_lock() {
furi_assert(instance_mutex != NULL);
furi_mutex_acquire(instance_mutex, FuriWaitForever);
tt_assert(instance_mutex != NULL);
tt_mutex_acquire(instance_mutex, TtWaitForever);
}
void service_registry_instance_unlock() {
furi_assert(instance_mutex != NULL);
furi_mutex_release(instance_mutex);
tt_assert(instance_mutex != NULL);
tt_mutex_release(instance_mutex);
}
void service_registry_manifest_lock() {
furi_assert(manifest_mutex != NULL);
furi_mutex_acquire(manifest_mutex, FuriWaitForever);
tt_assert(manifest_mutex != NULL);
tt_mutex_acquire(manifest_mutex, TtWaitForever);
}
void service_registry_manifest_unlock() {
furi_assert(manifest_mutex != NULL);
furi_mutex_release(manifest_mutex);
tt_assert(manifest_mutex != NULL);
tt_mutex_release(manifest_mutex);
}
void service_registry_add(const ServiceManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "adding %s", manifest->id);
void tt_service_registry_add(const ServiceManifest _Nonnull* manifest) {
TT_LOG_I(TAG, "adding %s", manifest->id);
service_registry_manifest_lock();
ServiceManifestDict_set_at(service_manifest_dict, manifest->id, manifest);
service_registry_manifest_unlock();
}
void service_registry_remove(const ServiceManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "removing %s", manifest->id);
void tt_service_registry_remove(const ServiceManifest _Nonnull* manifest) {
TT_LOG_I(TAG, "removing %s", manifest->id);
service_registry_manifest_lock();
ServiceManifestDict_erase(service_manifest_dict, manifest->id);
service_registry_manifest_unlock();
}
const ServiceManifest* _Nullable service_registry_find_manifest_by_id(const char* id) {
const ServiceManifest* _Nullable tt_service_registry_find_manifest_by_id(const char* id) {
service_registry_manifest_lock();
const ServiceManifest** _Nullable manifest = ServiceManifestDict_get(service_manifest_dict, id);
service_registry_manifest_unlock();
@ -89,48 +89,48 @@ ServiceData* _Nullable service_registry_find_instance_by_id(const char* id) {
return service;
}
void service_registry_for_each_manifest(ServiceManifestCallback callback, void* _Nullable context) {
void tt_service_registry_for_each_manifest(ServiceManifestCallback callback, void* _Nullable context) {
APP_REGISTRY_FOR_EACH(manifest, {
callback(manifest, context);
});
}
// TODO: return proper error/status instead of BOOL
bool service_registry_start(const char* service_id) {
FURI_LOG_I(TAG, "starting %s", service_id);
const ServiceManifest* manifest = service_registry_find_manifest_by_id(service_id);
bool tt_service_registry_start(const char* service_id) {
TT_LOG_I(TAG, "starting %s", service_id);
const ServiceManifest* manifest = tt_service_registry_find_manifest_by_id(service_id);
if (manifest == NULL) {
FURI_LOG_E(TAG, "manifest not found for service %s", service_id);
TT_LOG_E(TAG, "manifest not found for service %s", service_id);
return false;
}
Service service = service_alloc(manifest);
Service service = tt_service_alloc(manifest);
manifest->on_start(service);
service_registry_instance_lock();
ServiceInstanceDict_set_at(service_instance_dict, manifest->id, service);
service_registry_instance_unlock();
FURI_LOG_I(TAG, "started %s", service_id);
TT_LOG_I(TAG, "started %s", service_id);
return true;
}
bool service_registry_stop(const char* service_id) {
FURI_LOG_I(TAG, "stopping %s", service_id);
bool tt_service_registry_stop(const char* service_id) {
TT_LOG_I(TAG, "stopping %s", service_id);
ServiceData* service = service_registry_find_instance_by_id(service_id);
if (service == NULL) {
FURI_LOG_W(TAG, "service not running: %s", service_id);
TT_LOG_W(TAG, "service not running: %s", service_id);
return false;
}
service->manifest->on_stop(service);
service_free(service);
tt_service_free(service);
service_registry_instance_lock();
ServiceInstanceDict_erase(service_instance_dict, service_id);
service_registry_instance_unlock();
FURI_LOG_I(TAG, "stopped %s", service_id);
TT_LOG_I(TAG, "stopped %s", service_id);
return true;
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "service_manifest.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
typedef void (*ServiceManifestCallback)(const ServiceManifest*, void* context);
void tt_service_registry_init();
void tt_service_registry_add(const ServiceManifest* manifest);
void tt_service_registry_remove(const ServiceManifest* manifest);
const ServiceManifest _Nullable* tt_service_registry_find_manifest_by_id(const char* id);
void tt_service_registry_for_each_manifest(ServiceManifestCallback callback, void* _Nullable context);
bool tt_service_registry_start(const char* service_id);
bool tt_service_registry_stop(const char* service_id);
#ifdef __cplusplus
}
#endif

View File

@ -1,39 +1,39 @@
#include "stream_buffer.h"
#include "check.h"
#include "furi_core_defines.h"
#include "furi_core_types.h"
#include "core_defines.h"
#include "core_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/stream_buffer.h"
FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level) {
furi_assert(size != 0);
StreamBuffer* tt_stream_buffer_alloc(size_t size, size_t trigger_level) {
tt_assert(size != 0);
StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level);
furi_check(handle);
tt_check(handle);
return handle;
};
void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer) {
furi_assert(stream_buffer);
void tt_stream_buffer_free(StreamBuffer* stream_buffer) {
tt_assert(stream_buffer);
vStreamBufferDelete(stream_buffer);
};
bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level) {
furi_assert(stream_buffer);
bool tt_stream_set_trigger_level(StreamBuffer* stream_buffer, size_t trigger_level) {
tt_assert(stream_buffer);
return xStreamBufferSetTriggerLevel(stream_buffer, trigger_level) == pdTRUE;
};
size_t furi_stream_buffer_send(
FuriStreamBuffer* stream_buffer,
size_t tt_stream_buffer_send(
StreamBuffer* stream_buffer,
const void* data,
size_t length,
uint32_t timeout
) {
size_t ret;
if (FURI_IS_IRQ_MODE()) {
if (TT_IS_IRQ_MODE()) {
BaseType_t yield;
ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield);
portYIELD_FROM_ISR(yield);
@ -44,15 +44,15 @@ size_t furi_stream_buffer_send(
return ret;
};
size_t furi_stream_buffer_receive(
FuriStreamBuffer* stream_buffer,
size_t tt_stream_buffer_receive(
StreamBuffer* stream_buffer,
void* data,
size_t length,
uint32_t timeout
) {
size_t ret;
if (FURI_IS_IRQ_MODE()) {
if (TT_IS_IRQ_MODE()) {
BaseType_t yield;
ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield);
portYIELD_FROM_ISR(yield);
@ -63,26 +63,26 @@ size_t furi_stream_buffer_receive(
return ret;
}
size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer) {
size_t tt_stream_buffer_bytes_available(StreamBuffer* stream_buffer) {
return xStreamBufferBytesAvailable(stream_buffer);
};
size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer) {
size_t tt_stream_buffer_spaces_available(StreamBuffer* stream_buffer) {
return xStreamBufferSpacesAvailable(stream_buffer);
};
bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer) {
bool tt_stream_buffer_is_full(StreamBuffer* stream_buffer) {
return xStreamBufferIsFull(stream_buffer) == pdTRUE;
};
bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer) {
bool tt_stream_buffer_is_empty(StreamBuffer* stream_buffer) {
return (xStreamBufferIsEmpty(stream_buffer) == pdTRUE);
};
FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer) {
TtStatus tt_stream_buffer_reset(StreamBuffer* stream_buffer) {
if (xStreamBufferReset(stream_buffer) == pdPASS) {
return FuriStatusOk;
return TtStatusOk;
} else {
return FuriStatusError;
return TtStatusError;
}
}

View File

@ -1,6 +1,6 @@
/**
* @file stream_buffer.h
* Furi stream buffer primitive.
* Tactility stream buffer primitive.
*
* Stream buffers are used to send a continuous stream of data from one task or
* interrupt to another. Their implementation is light weight, making them
@ -12,7 +12,7 @@
* interrupt that will read from the buffer (the reader).
*/
#pragma once
#include "furi_core_types.h"
#include "core_types.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@ -21,7 +21,7 @@
extern "C" {
#endif
typedef void FuriStreamBuffer;
typedef void StreamBuffer;
/**
* @brief Allocate stream buffer instance.
@ -34,14 +34,14 @@ typedef void FuriStreamBuffer;
* before a task that is blocked on the stream buffer to wait for data is moved out of the blocked state.
* @return The stream buffer instance.
*/
FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level);
StreamBuffer* tt_stream_buffer_alloc(size_t size, size_t trigger_level);
/**
* @brief Free stream buffer instance
*
* @param stream_buffer The stream buffer instance.
*/
void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer);
void tt_stream_buffer_free(StreamBuffer* stream_buffer);
/**
* @brief Set trigger level for stream buffer.
@ -54,7 +54,7 @@ void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer);
* @return true if trigger level can be be updated (new trigger level was less than or equal to the stream buffer's length).
* @return false if trigger level can't be be updated (new trigger level was greater than the stream buffer's length).
*/
bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level);
bool tt_stream_set_trigger_level(StreamBuffer* stream_buffer, size_t trigger_level);
/**
* @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer.
@ -66,12 +66,12 @@ bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigg
* @param timeout The maximum amount of time the task should remain in the
* Blocked state to wait for space to become available if the stream buffer is full.
* Will return immediately if timeout is zero.
* Setting timeout to FuriWaitForever will cause the task to wait indefinitely.
* Setting timeout to TtWaitForever will cause the task to wait indefinitely.
* Ignored if called from ISR.
* @return The number of bytes actually written to the stream buffer.
*/
size_t furi_stream_buffer_send(
FuriStreamBuffer* stream_buffer,
size_t tt_stream_buffer_send(
StreamBuffer* stream_buffer,
const void* data,
size_t length,
uint32_t timeout
@ -88,12 +88,12 @@ size_t furi_stream_buffer_send(
* @param timeout The maximum amount of time the task should remain in the
* Blocked state to wait for data to become available if the stream buffer is empty.
* Will return immediately if timeout is zero.
* Setting timeout to FuriWaitForever will cause the task to wait indefinitely.
* Setting timeout to TtWaitForever will cause the task to wait indefinitely.
* Ignored if called from ISR.
* @return The number of bytes read from the stream buffer, if any.
*/
size_t furi_stream_buffer_receive(
FuriStreamBuffer* stream_buffer,
size_t tt_stream_buffer_receive(
StreamBuffer* stream_buffer,
void* data,
size_t length,
uint32_t timeout
@ -108,7 +108,7 @@ size_t furi_stream_buffer_receive(
* @return The number of bytes that can be read from the stream buffer before
* the stream buffer would be empty.
*/
size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer);
size_t tt_stream_buffer_bytes_available(StreamBuffer* stream_buffer);
/**
* @brief Queries a stream buffer to see how much free space it contains, which is
@ -119,7 +119,7 @@ size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer);
* @return The number of bytes that can be written to the stream buffer before
* the stream buffer would be full.
*/
size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer);
size_t tt_stream_buffer_spaces_available(StreamBuffer* stream_buffer);
/**
* @brief Queries a stream buffer to see if it is full.
@ -128,7 +128,7 @@ size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer);
* @return true if the stream buffer is full.
* @return false if the stream buffer is not full.
*/
bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer);
bool tt_stream_buffer_is_full(StreamBuffer* stream_buffer);
/**
* @brief Queries a stream buffer to see if it is empty.
@ -137,7 +137,7 @@ bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer);
* @return true if the stream buffer is empty.
* @return false if the stream buffer is not empty.
*/
bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer);
bool tt_stream_buffer_is_empty(StreamBuffer* stream_buffer);
/**
* @brief Resets a stream buffer to its initial, empty, state. Any data that was
@ -145,11 +145,11 @@ bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer);
* are no tasks blocked waiting to either send to or receive from the stream buffer.
*
* @param stream_buffer The stream buffer instance.
* @return FuriStatusOk if the stream buffer is reset.
* @return FuriStatusError if there was a task blocked waiting to send to or read
* @return TtStatusOk if the stream buffer is reset.
* @return TtStatusError if there was a task blocked waiting to send to or read
* from the stream buffer then the stream buffer is not reset.
*/
FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer);
TtStatus tt_stream_buffer_reset(StreamBuffer* stream_buffer);
#ifdef __cplusplus
}

View File

@ -3,10 +3,12 @@
#include <stdbool.h>
#include <stdio.h>
#include "app.h"
#include "check.h"
#include "core_defines.h"
#include "core_extra_defines.h"
#include "core_types.h"
#include "critical.h"
#include "event_flag.h"
#include "furi_core_defines.h"
#include "furi_core_types.h"
#include "furi_extra_defines.h"
#include "log.h"
#include "service.h"

View File

@ -0,0 +1,3 @@
#pragma once
#define TT_CONFIG_THREAD_MAX_PRIORITIES (32)

View File

@ -0,0 +1,599 @@
#include "thread.h"
#include "check.h"
#include "core_defines.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "kernel.h"
#include "log.h"
#include "tt_string.h"
#include <esp_log.h>
#define TAG "Thread"
#define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers
// Limits
#define MAX_BITS_TASK_NOTIFY 31U
#define MAX_BITS_EVENT_GROUPS 24U
#define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U))
#define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U))
typedef struct ThreadStdout ThreadStdout;
struct ThreadStdout {
ThreadStdoutWriteCallback write_callback;
TtString* buffer;
};
struct Thread {
ThreadState state;
int32_t ret;
ThreadCallback callback;
void* context;
ThreadStateCallback state_callback;
void* state_context;
char* name;
char* appid;
ThreadPriority priority;
TaskHandle_t task_handle;
size_t heap_size;
ThreadStdout output;
// Keep all non-alignable byte types in one place,
// this ensures that the size of this structure is minimal
bool is_static;
bool heap_trace_enabled;
configSTACK_DEPTH_TYPE stack_size;
};
static size_t __tt_thread_stdout_write(Thread* thread, const char* data, size_t size);
static int32_t __tt_thread_stdout_flush(Thread* thread);
/** Catch threads that are trying to exit wrong way */
__attribute__((__noreturn__)) void tt_thread_catch() { //-V1082
// If you're here it means you're probably doing something wrong
// with critical sections or with scheduler state
asm volatile("nop"); // extra magic
tt_crash("You are doing it wrong"); //-V779
__builtin_unreachable();
}
static void tt_thread_set_state(Thread* thread, ThreadState state) {
tt_assert(thread);
thread->state = state;
if (thread->state_callback) {
thread->state_callback(state, thread->state_context);
}
}
static void tt_thread_body(void* context) {
tt_assert(context);
Thread* thread = context;
// store thread instance to thread local storage
tt_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) == NULL);
vTaskSetThreadLocalStoragePointer(NULL, 0, thread);
tt_assert(thread->state == ThreadStateStarting);
tt_thread_set_state(thread, ThreadStateRunning);
thread->ret = thread->callback(thread->context);
tt_assert(thread->state == ThreadStateRunning);
if (thread->is_static) {
ESP_LOGI(
TAG,
"%s service thread TCB memory will not be reclaimed",
thread->name ? thread->name : "<unnamed service>"
);
}
// flush stdout
__tt_thread_stdout_flush(thread);
tt_thread_set_state(thread, ThreadStateStopped);
vTaskDelete(NULL);
tt_thread_catch();
}
Thread* tt_thread_alloc() {
Thread* thread = malloc(sizeof(Thread));
// TODO: create default struct instead of using memset()
memset(thread, 0, sizeof(Thread));
thread->output.buffer = tt_string_alloc();
thread->is_static = false;
Thread* parent = NULL;
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
// TLS is not available, if we called not from thread context
parent = pvTaskGetThreadLocalStoragePointer(NULL, 0);
if (parent && parent->appid) {
tt_thread_set_appid(thread, parent->appid);
} else {
tt_thread_set_appid(thread, "unknown");
}
} else {
// if scheduler is not started, we are starting driver thread
tt_thread_set_appid(thread, "driver");
}
/*HalRtcHeapTrackMode mode = tt_hal_rtc_get_heap_track_mode();
if(mode == HalRtcHeapTrackModeAll) {
thread->heap_trace_enabled = true;
} else if(mode == HalRtcHeapTrackModeTree && tt_thread_get_current_id()) {
if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled;
} else */
{
thread->heap_trace_enabled = false;
}
return thread;
}
Thread* tt_thread_alloc_ex(
const char* name,
uint32_t stack_size,
ThreadCallback callback,
void* context
) {
Thread* thread = tt_thread_alloc();
tt_thread_set_name(thread, name);
tt_thread_set_stack_size(thread, stack_size);
tt_thread_set_callback(thread, callback);
tt_thread_set_context(thread, context);
return thread;
}
void tt_thread_free(Thread* thread) {
tt_assert(thread);
// Ensure that use join before free
tt_assert(thread->state == ThreadStateStopped);
tt_assert(thread->task_handle == NULL);
if (thread->name) free(thread->name);
if (thread->appid) free(thread->appid);
tt_string_free(thread->output.buffer);
free(thread);
}
void tt_thread_set_name(Thread* thread, const char* name) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
if (thread->name) free(thread->name);
thread->name = name ? strdup(name) : NULL;
}
void tt_thread_set_appid(Thread* thread, const char* appid) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
if (thread->appid) free(thread->appid);
thread->appid = appid ? strdup(appid) : NULL;
}
void tt_thread_mark_as_static(Thread* thread) {
thread->is_static = true;
}
bool tt_thread_mark_is_service(ThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
assert(!TT_IS_IRQ_MODE() && (hTask != NULL));
Thread* thread = (Thread*)pvTaskGetThreadLocalStoragePointer(hTask, 0);
assert(thread != NULL);
return thread->is_static;
}
void tt_thread_set_stack_size(Thread* thread, size_t stack_size) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
tt_assert(stack_size % 4 == 0);
thread->stack_size = stack_size;
}
void tt_thread_set_callback(Thread* thread, ThreadCallback callback) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
thread->callback = callback;
}
void tt_thread_set_context(Thread* thread, void* context) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
thread->context = context;
}
void tt_thread_set_priority(Thread* thread, ThreadPriority priority) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
tt_assert(priority >= ThreadPriorityIdle && priority <= ThreadPriorityIsr);
thread->priority = priority;
}
void tt_thread_set_current_priority(ThreadPriority priority) {
UBaseType_t new_priority = priority ? priority : ThreadPriorityNormal;
vTaskPrioritySet(NULL, new_priority);
}
ThreadPriority tt_thread_get_current_priority() {
return (ThreadPriority)uxTaskPriorityGet(NULL);
}
void tt_thread_set_state_callback(Thread* thread, ThreadStateCallback callback) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
thread->state_callback = callback;
}
void tt_thread_set_state_context(Thread* thread, void* context) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
thread->state_context = context;
}
ThreadState tt_thread_get_state(Thread* thread) {
tt_assert(thread);
return thread->state;
}
void tt_thread_start(Thread* thread) {
tt_assert(thread);
tt_assert(thread->callback);
tt_assert(thread->state == ThreadStateStopped);
tt_assert(thread->stack_size > 0 && thread->stack_size < (UINT16_MAX * sizeof(StackType_t)));
tt_thread_set_state(thread, ThreadStateStarting);
uint32_t stack = thread->stack_size / sizeof(StackType_t);
UBaseType_t priority = thread->priority ? thread->priority : ThreadPriorityNormal;
if (thread->is_static) {
thread->task_handle = xTaskCreateStatic(
tt_thread_body,
thread->name,
stack,
thread,
priority,
malloc(sizeof(StackType_t) * stack),
malloc(sizeof(StaticTask_t))
);
} else {
BaseType_t ret = xTaskCreate(
tt_thread_body, thread->name, stack, thread, priority, &thread->task_handle
);
tt_check(ret == pdPASS);
}
tt_check(thread->task_handle);
}
void tt_thread_cleanup_tcb_event(TaskHandle_t task) {
Thread* thread = pvTaskGetThreadLocalStoragePointer(task, 0);
if (thread) {
// clear thread local storage
vTaskSetThreadLocalStoragePointer(task, 0, NULL);
tt_assert(thread->task_handle == task);
thread->task_handle = NULL;
}
}
bool tt_thread_join(Thread* thread) {
tt_assert(thread);
tt_check(tt_thread_get_current() != thread);
// !!! IMPORTANT NOTICE !!!
//
// If your thread exited, but your app stuck here: some other thread uses
// all cpu time, which delays kernel from releasing task handle
while (thread->task_handle) {
tt_delay_ms(10);
}
return true;
}
ThreadId tt_thread_get_id(Thread* thread) {
tt_assert(thread);
return thread->task_handle;
}
void tt_thread_enable_heap_trace(Thread* thread) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
thread->heap_trace_enabled = true;
}
void tt_thread_disable_heap_trace(Thread* thread) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
thread->heap_trace_enabled = false;
}
size_t tt_thread_get_heap_size(Thread* thread) {
tt_assert(thread);
tt_assert(thread->heap_trace_enabled == true);
return thread->heap_size;
}
int32_t tt_thread_get_return_code(Thread* thread) {
tt_assert(thread);
tt_assert(thread->state == ThreadStateStopped);
return thread->ret;
}
ThreadId tt_thread_get_current_id() {
return xTaskGetCurrentTaskHandle();
}
Thread* tt_thread_get_current() {
Thread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0);
return thread;
}
void tt_thread_yield() {
tt_assert(!TT_IS_IRQ_MODE());
taskYIELD();
}
uint32_t tt_thread_flags_set(ThreadId thread_id, uint32_t flags) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
uint32_t rflags;
BaseType_t yield;
if ((hTask == NULL) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) {
rflags = (uint32_t)TtStatusErrorParameter;
} else {
rflags = (uint32_t)TtStatusError;
if (TT_IS_IRQ_MODE()) {
yield = pdFALSE;
(void)xTaskNotifyIndexedFromISR(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits, &yield);
(void)xTaskNotifyAndQueryIndexedFromISR(
hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags, NULL
);
portYIELD_FROM_ISR(yield);
} else {
(void)xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits);
(void)xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags);
}
}
/* Return flags after setting */
return (rflags);
}
uint32_t tt_thread_flags_clear(uint32_t flags) {
TaskHandle_t hTask;
uint32_t rflags, cflags;
if (TT_IS_IRQ_MODE()) {
rflags = (uint32_t)TtStatusErrorISR;
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
rflags = (uint32_t)TtStatusErrorParameter;
} else {
hTask = xTaskGetCurrentTaskHandle();
if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &cflags) ==
pdPASS) {
rflags = cflags;
cflags &= ~flags;
if (xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) !=
pdPASS) {
rflags = (uint32_t)TtStatusError;
}
} else {
rflags = (uint32_t)TtStatusError;
}
}
/* Return flags before clearing */
return (rflags);
}
uint32_t tt_thread_flags_get(void) {
TaskHandle_t hTask;
uint32_t rflags;
if (TT_IS_IRQ_MODE()) {
rflags = (uint32_t)TtStatusErrorISR;
} else {
hTask = xTaskGetCurrentTaskHandle();
if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) !=
pdPASS) {
rflags = (uint32_t)TtStatusError;
}
}
return (rflags);
}
uint32_t tt_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
uint32_t rflags, nval;
uint32_t clear;
TickType_t t0, td, tout;
BaseType_t rval;
if (TT_IS_IRQ_MODE()) {
rflags = (uint32_t)TtStatusErrorISR;
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
rflags = (uint32_t)TtStatusErrorParameter;
} else {
if ((options & TtFlagNoClear) == TtFlagNoClear) {
clear = 0U;
} else {
clear = flags;
}
rflags = 0U;
tout = timeout;
t0 = xTaskGetTickCount();
do {
rval = xTaskNotifyWaitIndexed(THREAD_NOTIFY_INDEX, 0, clear, &nval, tout);
if (rval == pdPASS) {
rflags &= flags;
rflags |= nval;
if ((options & TtFlagWaitAll) == TtFlagWaitAll) {
if ((flags & rflags) == flags) {
break;
} else {
if (timeout == 0U) {
rflags = (uint32_t)TtStatusErrorResource;
break;
}
}
} else {
if ((flags & rflags) != 0) {
break;
} else {
if (timeout == 0U) {
rflags = (uint32_t)TtStatusErrorResource;
break;
}
}
}
/* Update timeout */
td = xTaskGetTickCount() - t0;
if (td > tout) {
tout = 0;
} else {
tout -= td;
}
} else {
if (timeout == 0) {
rflags = (uint32_t)TtStatusErrorResource;
} else {
rflags = (uint32_t)TtStatusErrorTimeout;
}
}
} while (rval != pdFAIL);
}
/* Return flags before clearing */
return (rflags);
}
uint32_t tt_thread_enumerate(ThreadId* thread_array, uint32_t array_items) {
uint32_t i, count;
TaskStatus_t* task;
if (TT_IS_IRQ_MODE() || (thread_array == NULL) || (array_items == 0U)) {
count = 0U;
} else {
vTaskSuspendAll();
count = uxTaskGetNumberOfTasks();
task = pvPortMalloc(count * sizeof(TaskStatus_t));
if (task != NULL) {
count = uxTaskGetSystemState(task, count, NULL);
for (i = 0U; (i < count) && (i < array_items); i++) {
thread_array[i] = (ThreadId)task[i].xHandle;
}
count = i;
}
(void)xTaskResumeAll();
vPortFree(task);
}
return (count);
}
const char* tt_thread_get_name(ThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
const char* name;
if (TT_IS_IRQ_MODE() || (hTask == NULL)) {
name = NULL;
} else {
name = pcTaskGetName(hTask);
}
return (name);
}
const char* tt_thread_get_appid(ThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
const char* appid = "system";
if (!TT_IS_IRQ_MODE() && (hTask != NULL)) {
Thread* thread = (Thread*)pvTaskGetThreadLocalStoragePointer(hTask, 0);
if (thread) {
appid = thread->appid;
}
}
return (appid);
}
uint32_t tt_thread_get_stack_space(ThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
uint32_t sz;
if (TT_IS_IRQ_MODE() || (hTask == NULL)) {
sz = 0U;
} else {
sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t));
}
return (sz);
}
static size_t __tt_thread_stdout_write(Thread* thread, const char* data, size_t size) {
if (thread->output.write_callback != NULL) {
thread->output.write_callback(data, size);
} else {
TT_LOG_I(thread->name, "%s", data);
}
return size;
}
static int32_t __tt_thread_stdout_flush(Thread* thread) {
TtString* buffer = thread->output.buffer;
size_t size = tt_string_size(buffer);
if (size > 0) {
__tt_thread_stdout_write(thread, tt_string_get_cstr(buffer), size);
tt_string_reset(buffer);
}
return 0;
}
void tt_thread_suspend(ThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
vTaskSuspend(hTask);
}
void tt_thread_resume(ThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
if (TT_IS_IRQ_MODE()) {
xTaskResumeFromISR(hTask);
} else {
vTaskResume(hTask);
}
}
bool tt_thread_is_suspended(ThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
return eTaskGetState(hTask) == eSuspended;
}

View File

@ -0,0 +1,334 @@
#pragma once
#include "core_defines.h"
#include "core_types.h"
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** ThreadState */
typedef enum {
ThreadStateStopped,
ThreadStateStarting,
ThreadStateRunning,
} ThreadState;
/** ThreadPriority */
typedef enum {
ThreadPriorityNone = 0, /**< Uninitialized, choose system default */
ThreadPriorityIdle = 1, /**< Idle priority */
ThreadPriorityLowest = 14, /**< Lowest */
ThreadPriorityLow = 15, /**< Low */
ThreadPriorityNormal = 16, /**< Normal */
ThreadPriorityHigh = 17, /**< High */
ThreadPriorityHighest = 18, /**< Highest */
ThreadPriorityIsr =
(TT_CONFIG_THREAD_MAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */
} ThreadPriority;
/** Thread anonymous structure */
typedef struct Thread Thread;
/** ThreadId proxy type to OS low level functions */
typedef void* ThreadId;
/** ThreadCallback Your callback to run in new thread
* @warning never use osThreadExit in Thread
*/
typedef int32_t (*ThreadCallback)(void* context);
/** Write to stdout callback
* @param data pointer to data
* @param size data size @warning your handler must consume everything
*/
typedef void (*ThreadStdoutWriteCallback)(const char* data, size_t size);
/** Thread state change callback called upon thread state change
* @param state new thread state
* @param context callback context
*/
typedef void (*ThreadStateCallback)(ThreadState state, void* context);
/** Allocate Thread
*
* @return Thread instance
*/
Thread* tt_thread_alloc();
/** Allocate Thread, shortcut version
*
* @param name
* @param stack_size
* @param callback
* @param context
* @return Thread*
*/
Thread* tt_thread_alloc_ex(
const char* name,
uint32_t stack_size,
ThreadCallback callback,
void* context
);
/** Release Thread
*
* @warning see tt_thread_join
*
* @param thread Thread instance
*/
void tt_thread_free(Thread* thread);
/** Set Thread name
*
* @param thread Thread instance
* @param name string
*/
void tt_thread_set_name(Thread* thread, const char* name);
/**
* @brief Set Thread appid
* Technically, it is like a "process id", but it is not a system-wide unique identifier.
* All threads spawned by the same app will have the same appid.
*
* @param thread
* @param appid
*/
void tt_thread_set_appid(Thread* thread, const char* appid);
/** Mark thread as service
* The service cannot be stopped or removed, and cannot exit from the thread body
*
* @param thread
*/
void tt_thread_mark_as_static(Thread* thread);
/** Set Thread stack size
*
* @param thread Thread instance
* @param stack_size stack size in bytes
*/
void tt_thread_set_stack_size(Thread* thread, size_t stack_size);
/** Set Thread callback
*
* @param thread Thread instance
* @param callback ThreadCallback, called upon thread run
*/
void tt_thread_set_callback(Thread* thread, ThreadCallback callback);
/** Set Thread context
*
* @param thread Thread instance
* @param context pointer to context for thread callback
*/
void tt_thread_set_context(Thread* thread, void* context);
/** Set Thread priority
*
* @param thread Thread instance
* @param priority ThreadPriority value
*/
void tt_thread_set_priority(Thread* thread, ThreadPriority priority);
/** Set current thread priority
*
* @param priority ThreadPriority value
*/
void tt_thread_set_current_priority(ThreadPriority priority);
/** Get current thread priority
*
* @return ThreadPriority value
*/
ThreadPriority tt_thread_get_current_priority();
/** Set Thread state change callback
*
* @param thread Thread instance
* @param callback state change callback
*/
void tt_thread_set_state_callback(Thread* thread, ThreadStateCallback callback);
/** Set Thread state change context
*
* @param thread Thread instance
* @param context pointer to context
*/
void tt_thread_set_state_context(Thread* thread, void* context);
/** Get Thread state
*
* @param thread Thread instance
*
* @return thread state from ThreadState
*/
ThreadState tt_thread_get_state(Thread* thread);
/** Start Thread
*
* @param thread Thread instance
*/
void tt_thread_start(Thread* thread);
/** Join Thread
*
* @warning Use this method only when CPU is not busy(Idle task receives
* control), otherwise it will wait forever.
*
* @param thread Thread instance
*
* @return bool
*/
bool tt_thread_join(Thread* thread);
/** Get FreeRTOS ThreadId for Thread instance
*
* @param thread Thread instance
*
* @return ThreadId or NULL
*/
ThreadId tt_thread_get_id(Thread* thread);
/** Enable heap tracing
*
* @param thread Thread instance
*/
void tt_thread_enable_heap_trace(Thread* thread);
/** Disable heap tracing
*
* @param thread Thread instance
*/
void tt_thread_disable_heap_trace(Thread* thread);
/** Get thread heap size
*
* @param thread Thread instance
*
* @return size in bytes
*/
size_t tt_thread_get_heap_size(Thread* thread);
/** Get thread return code
*
* @param thread Thread instance
*
* @return return code
*/
int32_t tt_thread_get_return_code(Thread* thread);
/** Thread related methods that doesn't involve Thread directly */
/** Get FreeRTOS ThreadId for current thread
*
* @param thread Thread instance
*
* @return ThreadId or NULL
*/
ThreadId tt_thread_get_current_id();
/** Get Thread instance for current thread
*
* @return pointer to Thread or NULL if this thread doesn't belongs to Tactility
*/
Thread* tt_thread_get_current();
/** Return control to scheduler */
void tt_thread_yield();
uint32_t tt_thread_flags_set(ThreadId thread_id, uint32_t flags);
uint32_t tt_thread_flags_clear(uint32_t flags);
uint32_t tt_thread_flags_get(void);
uint32_t tt_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout);
/**
* @brief Enumerate threads
*
* @param thread_array array of ThreadId, where thread ids will be stored
* @param array_items array size
* @return uint32_t threads count
*/
uint32_t tt_thread_enumerate(ThreadId* thread_array, uint32_t array_items);
/**
* @brief Get thread name
*
* @param thread_id
* @return const char* name or NULL
*/
const char* tt_thread_get_name(ThreadId thread_id);
/**
* @brief Get thread appid
*
* @param thread_id
* @return const char* appid
*/
const char* tt_thread_get_appid(ThreadId thread_id);
/**
* @brief Get thread stack watermark
*
* @param thread_id
* @return uint32_t
*/
uint32_t tt_thread_get_stack_space(ThreadId thread_id);
/** Get STDOUT callback for thead
*
* @return STDOUT callback
*/
ThreadStdoutWriteCallback tt_thread_get_stdout_callback();
/** Set STDOUT callback for thread
*
* @param callback callback or NULL to clear
*/
void tt_thread_set_stdout_callback(ThreadStdoutWriteCallback callback);
/** Write data to buffered STDOUT
*
* @param data input data
* @param size input data size
*
* @return size_t written data size
*/
size_t tt_thread_stdout_write(const char* data, size_t size);
/** Flush data to STDOUT
*
* @return int32_t error code
*/
int32_t tt_thread_stdout_flush();
/** Suspend thread
*
* @param thread_id thread id
*/
void tt_thread_suspend(ThreadId thread_id);
/** Resume thread
*
* @param thread_id thread id
*/
void tt_thread_resume(ThreadId thread_id);
/** Get thread suspended state
*
* @param thread_id thread id
* @return true if thread is suspended
*/
bool tt_thread_is_suspended(ThreadId thread_id);
bool tt_thread_mark_is_service(ThreadId thread_id);
#ifdef __cplusplus
}
#endif

View File

@ -6,11 +6,11 @@
#include "freertos/timers.h"
typedef struct {
FuriTimerCallback func;
TimerCallback func;
void* context;
} TimerCallback_t;
static void TimerCallback(TimerHandle_t hTimer) {
static void timer_callback(TimerHandle_t hTimer) {
TimerCallback_t* callb;
/* Retrieve pointer to callback function and context */
@ -24,8 +24,8 @@ static void TimerCallback(TimerHandle_t hTimer) {
}
}
FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) {
furi_assert((furi_kernel_is_irq() == 0U) && (func != NULL));
Timer* tt_timer_alloc(TimerCallback func, TimerType type, void* context) {
tt_assert((tt_kernel_is_irq() == 0U) && (func != NULL));
TimerHandle_t hTimer;
TimerCallback_t* callb;
@ -40,7 +40,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
callb->func = func;
callb->context = context;
if (type == FuriTimerTypeOnce) {
if (type == TimerTypeOnce) {
reload = pdFALSE;
} else {
reload = pdTRUE;
@ -50,25 +50,26 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
callb = (TimerCallback_t*)((uint32_t)callb | 1U);
// TimerCallback function is always provided as a callback and is used to call application
// specified function with its context both stored in structure callb.
hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, TimerCallback);
furi_check(hTimer);
// TODO: should we use pointer to function or function directly as-is?
hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, timer_callback);
tt_check(hTimer);
/* Return timer ID */
return ((FuriTimer*)hTimer);
return ((Timer*)hTimer);
}
void furi_timer_free(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq());
furi_assert(instance);
void tt_timer_free(Timer* instance) {
tt_assert(!tt_kernel_is_irq());
tt_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance;
TimerCallback_t* callb;
callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer);
furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS);
tt_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS);
while (furi_timer_is_running(instance)) furi_delay_tick(2);
while (tt_timer_is_running(instance)) tt_delay_tick(2);
if ((uint32_t)callb & 1U) {
/* Callback memory was allocated from dynamic pool, clear flag */
@ -79,57 +80,57 @@ void furi_timer_free(FuriTimer* instance) {
}
}
FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
furi_assert(!furi_kernel_is_irq());
furi_assert(instance);
furi_assert(ticks < portMAX_DELAY);
TtStatus tt_timer_start(Timer* instance, uint32_t ticks) {
tt_assert(!tt_kernel_is_irq());
tt_assert(instance);
tt_assert(ticks < portMAX_DELAY);
TimerHandle_t hTimer = (TimerHandle_t)instance;
FuriStatus stat;
TtStatus stat;
if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS) {
stat = FuriStatusOk;
stat = TtStatusOk;
} else {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
}
/* Return execution status */
return (stat);
}
FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) {
furi_assert(!furi_kernel_is_irq());
furi_assert(instance);
furi_assert(ticks < portMAX_DELAY);
TtStatus tt_timer_restart(Timer* instance, uint32_t ticks) {
tt_assert(!tt_kernel_is_irq());
tt_assert(instance);
tt_assert(ticks < portMAX_DELAY);
TimerHandle_t hTimer = (TimerHandle_t)instance;
FuriStatus stat;
TtStatus stat;
if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS &&
xTimerReset(hTimer, portMAX_DELAY) == pdPASS) {
stat = FuriStatusOk;
stat = TtStatusOk;
} else {
stat = FuriStatusErrorResource;
stat = TtStatusErrorResource;
}
/* Return execution status */
return (stat);
}
FuriStatus furi_timer_stop(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq());
furi_assert(instance);
TtStatus tt_timer_stop(Timer* instance) {
tt_assert(!tt_kernel_is_irq());
tt_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance;
furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS);
tt_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS);
return FuriStatusOk;
return TtStatusOk;
}
uint32_t furi_timer_is_running(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq());
furi_assert(instance);
uint32_t tt_timer_is_running(Timer* instance) {
tt_assert(!tt_kernel_is_irq());
tt_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance;
@ -137,36 +138,36 @@ uint32_t furi_timer_is_running(FuriTimer* instance) {
return (uint32_t)xTimerIsTimerActive(hTimer);
}
uint32_t furi_timer_get_expire_time(FuriTimer* instance) {
furi_assert(!furi_kernel_is_irq());
furi_assert(instance);
uint32_t tt_timer_get_expire_time(Timer* instance) {
tt_assert(!tt_kernel_is_irq());
tt_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance;
return (uint32_t)xTimerGetExpiryTime(hTimer);
}
void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg) {
void tt_timer_pending_callback(TimerPendigCallback callback, void* context, uint32_t arg) {
BaseType_t ret = pdFAIL;
if (furi_kernel_is_irq()) {
if (tt_kernel_is_irq()) {
ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL);
} else {
ret = xTimerPendFunctionCall(callback, context, arg, FuriWaitForever);
ret = xTimerPendFunctionCall(callback, context, arg, TtWaitForever);
}
furi_check(ret == pdPASS);
tt_check(ret == pdPASS);
}
void furi_timer_set_thread_priority(FuriTimerThreadPriority priority) {
furi_assert(!furi_kernel_is_irq());
void tt_timer_set_thread_priority(TimerThreadPriority priority) {
tt_assert(!tt_kernel_is_irq());
TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle();
furi_check(task_handle); // Don't call this method before timer task start
tt_check(task_handle); // Don't call this method before timer task start
if (priority == FuriTimerThreadPriorityNormal) {
if (priority == TimerThreadPriorityNormal) {
vTaskPrioritySet(task_handle, configTIMER_TASK_PRIORITY);
} else if (priority == FuriTimerThreadPriorityElevated) {
} else if (priority == TimerThreadPriorityElevated) {
vTaskPrioritySet(task_handle, configMAX_PRIORITIES - 1);
} else {
furi_crash();
tt_crash();
}
}

View File

@ -0,0 +1,106 @@
#pragma once
#include "core_types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*TimerCallback)(void* context);
typedef enum {
TimerTypeOnce = 0, ///< One-shot timer.
TimerTypePeriodic = 1 ///< Repeating timer.
} TimerType;
typedef void Timer;
/** Allocate timer
*
* @param[in] func The callback function
* @param[in] type The timer type
* @param context The callback context
*
* @return The pointer to Timer instance
*/
Timer* tt_timer_alloc(TimerCallback func, TimerType type, void* context);
/** Free timer
*
* @param instance The pointer to Timer instance
*/
void tt_timer_free(Timer* instance);
/** Start timer
*
* @warning This is asynchronous call, real operation will happen as soon as
* timer service process this request.
*
* @param instance The pointer to Timer instance
* @param[in] ticks The interval in ticks
*
* @return The status.
*/
TtStatus tt_timer_start(Timer* instance, uint32_t ticks);
/** Restart timer with previous timeout value
*
* @warning This is asynchronous call, real operation will happen as soon as
* timer service process this request.
*
* @param instance The pointer to Timer instance
* @param[in] ticks The interval in ticks
*
* @return The status.
*/
TtStatus tt_timer_restart(Timer* instance, uint32_t ticks);
/** Stop timer
*
* @warning This is asynchronous call, real operation will happen as soon as
* timer service process this request.
*
* @param instance The pointer to Timer instance
*
* @return The status.
*/
TtStatus tt_timer_stop(Timer* instance);
/** Is timer running
*
* @warning This cal may and will return obsolete timer state if timer
* commands are still in the queue. Please read FreeRTOS timer
* documentation first.
*
* @param instance The pointer to Timer instance
*
* @return 0: not running, 1: running
*/
uint32_t tt_timer_is_running(Timer* instance);
/** Get timer expire time
*
* @param instance The Timer instance
*
* @return expire tick
*/
uint32_t tt_timer_get_expire_time(Timer* instance);
typedef void (*TimerPendigCallback)(void* context, uint32_t arg);
void tt_timer_pending_callback(TimerPendigCallback callback, void* context, uint32_t arg);
typedef enum {
TimerThreadPriorityNormal, /**< Lower then other threads */
TimerThreadPriorityElevated, /**< Same as other threads */
} TimerThreadPriority;
/** Set Timer thread priority
*
* @param[in] priority The priority
*/
void tt_timer_set_thread_priority(TimerThreadPriority priority);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,304 @@
#include "tt_string.h"
#include <m-string.h>
struct TtString {
string_t string;
};
#undef tt_string_alloc_set
#undef tt_string_set
#undef tt_string_cmp
#undef tt_string_cmpi
#undef tt_string_search
#undef tt_string_search_str
#undef tt_string_equal
#undef tt_string_replace
#undef tt_string_replace_str
#undef tt_string_replace_all
#undef tt_string_start_with
#undef tt_string_end_with
#undef tt_string_search_char
#undef tt_string_search_rchar
#undef tt_string_trim
#undef tt_string_cat
TtString* tt_string_alloc() {
TtString* string = malloc(sizeof(TtString));
string_init(string->string);
return string;
}
TtString* tt_string_alloc_set(const TtString* s) {
TtString* string = malloc(sizeof(TtString)); //-V799
string_init_set(string->string, s->string);
return string;
} //-V773
TtString* tt_string_alloc_set_str(const char cstr[]) {
TtString* string = malloc(sizeof(TtString)); //-V799
string_init_set(string->string, cstr);
return string;
} //-V773
TtString* tt_string_alloc_printf(const char format[], ...) {
va_list args;
va_start(args, format);
TtString* string = tt_string_alloc_vprintf(format, args);
va_end(args);
return string;
}
TtString* tt_string_alloc_vprintf(const char format[], va_list args) {
TtString* string = malloc(sizeof(TtString));
string_init_vprintf(string->string, format, args);
return string;
}
TtString* tt_string_alloc_move(TtString* s) {
TtString* string = malloc(sizeof(TtString));
string_init_move(string->string, s->string);
free(s);
return string;
}
void tt_string_free(TtString* s) {
string_clear(s->string);
free(s);
}
void tt_string_reserve(TtString* s, size_t alloc) {
string_reserve(s->string, alloc);
}
void tt_string_reset(TtString* s) {
string_reset(s->string);
}
void tt_string_swap(TtString* v1, TtString* v2) {
string_swap(v1->string, v2->string);
}
void tt_string_move(TtString* v1, TtString* v2) {
string_clear(v1->string);
string_init_move(v1->string, v2->string);
free(v2);
}
size_t tt_string_hash(const TtString* v) {
return string_hash(v->string);
}
char tt_string_get_char(const TtString* v, size_t index) {
return string_get_char(v->string, index);
}
const char* tt_string_get_cstr(const TtString* s) {
return string_get_cstr(s->string);
}
void tt_string_set(TtString* s, TtString* source) {
string_set(s->string, source->string);
}
void tt_string_set_str(TtString* s, const char cstr[]) {
string_set(s->string, cstr);
}
void tt_string_set_strn(TtString* s, const char str[], size_t n) {
string_set_strn(s->string, str, n);
}
void tt_string_set_char(TtString* s, size_t index, const char c) {
string_set_char(s->string, index, c);
}
int tt_string_cmp(const TtString* s1, const TtString* s2) {
return string_cmp(s1->string, s2->string);
}
int tt_string_cmp_str(const TtString* s1, const char str[]) {
return string_cmp(s1->string, str);
}
int tt_string_cmpi(const TtString* v1, const TtString* v2) {
return string_cmpi(v1->string, v2->string);
}
int tt_string_cmpi_str(const TtString* v1, const char p2[]) {
return string_cmpi_str(v1->string, p2);
}
size_t tt_string_search(const TtString* v, const TtString* needle, size_t start) {
return string_search(v->string, needle->string, start);
}
size_t tt_string_search_str(const TtString* v, const char needle[], size_t start) {
return string_search(v->string, needle, start);
}
bool tt_string_equal(const TtString* v1, const TtString* v2) {
return string_equal_p(v1->string, v2->string);
}
bool tt_string_equal_str(const TtString* v1, const char v2[]) {
return string_equal_p(v1->string, v2);
}
void tt_string_push_back(TtString* v, char c) {
string_push_back(v->string, c);
}
size_t tt_string_size(const TtString* s) {
return string_size(s->string);
}
int tt_string_printf(TtString* v, const char format[], ...) {
va_list args;
va_start(args, format);
int result = tt_string_vprintf(v, format, args);
va_end(args);
return result;
}
int tt_string_vprintf(TtString* v, const char format[], va_list args) {
return string_vprintf(v->string, format, args);
}
int tt_string_cat_printf(TtString* v, const char format[], ...) {
va_list args;
va_start(args, format);
int result = tt_string_cat_vprintf(v, format, args);
va_end(args);
return result;
}
int tt_string_cat_vprintf(TtString* v, const char format[], va_list args) {
TtString* string = tt_string_alloc();
int ret = tt_string_vprintf(string, format, args);
tt_string_cat(v, string);
tt_string_free(string);
return ret;
}
bool tt_string_empty(const TtString* v) {
return string_empty_p(v->string);
}
void tt_string_replace_at(TtString* v, size_t pos, size_t len, const char str2[]) {
string_replace_at(v->string, pos, len, str2);
}
size_t
tt_string_replace(TtString* string, TtString* needle, TtString* replace, size_t start) {
return string_replace(string->string, needle->string, replace->string, start);
}
size_t tt_string_replace_str(TtString* v, const char str1[], const char str2[], size_t start) {
return string_replace_str(v->string, str1, str2, start);
}
void tt_string_replace_all_str(TtString* v, const char str1[], const char str2[]) {
string_replace_all_str(v->string, str1, str2);
}
void tt_string_replace_all(TtString* v, const TtString* str1, const TtString* str2) {
string_replace_all(v->string, str1->string, str2->string);
}
bool tt_string_start_with(const TtString* v, const TtString* v2) {
return string_start_with_string_p(v->string, v2->string);
}
bool tt_string_start_with_str(const TtString* v, const char str[]) {
return string_start_with_str_p(v->string, str);
}
bool tt_string_end_with(const TtString* v, const TtString* v2) {
return string_end_with_string_p(v->string, v2->string);
}
bool tt_string_end_with_str(const TtString* v, const char str[]) {
return string_end_with_str_p(v->string, str);
}
size_t tt_string_search_char(const TtString* v, char c, size_t start) {
return string_search_char(v->string, c, start);
}
size_t tt_string_search_rchar(const TtString* v, char c, size_t start) {
return string_search_rchar(v->string, c, start);
}
void tt_string_left(TtString* v, size_t index) {
string_left(v->string, index);
}
void tt_string_right(TtString* v, size_t index) {
string_right(v->string, index);
}
void tt_string_mid(TtString* v, size_t index, size_t size) {
string_mid(v->string, index, size);
}
void tt_string_trim(TtString* v, const char charac[]) {
string_strim(v->string, charac);
}
void tt_string_cat(TtString* v, const TtString* v2) {
string_cat(v->string, v2->string);
}
void tt_string_cat_str(TtString* v, const char str[]) {
string_cat(v->string, str);
}
void tt_string_set_n(TtString* v, const TtString* ref, size_t offset, size_t length) {
string_set_n(v->string, ref->string, offset, length);
}
size_t tt_string_utf8_length(TtString* str) {
return string_length_u(str->string);
}
void tt_string_utf8_push(TtString* str, TtStringUnicodeValue u) {
string_push_u(str->string, u);
}
static m_str1ng_utf8_state_e tt_state_to_state(TtStringUTF8State state) {
switch (state) {
case TtStringUTF8StateStarting:
return M_STR1NG_UTF8_STARTING;
case TtStringUTF8StateDecoding1:
return M_STR1NG_UTF8_DECODING_1;
case TtStringUTF8StateDecoding2:
return M_STR1NG_UTF8_DECODING_2;
case TtStringUTF8StateDecoding3:
return M_STR1NG_UTF8_DECODING_3;
default:
return M_STR1NG_UTF8_ERROR;
}
}
static TtStringUTF8State state_to_tt_state(m_str1ng_utf8_state_e state) {
switch (state) {
case M_STR1NG_UTF8_STARTING:
return TtStringUTF8StateStarting;
case M_STR1NG_UTF8_DECODING_1:
return TtStringUTF8StateDecoding1;
case M_STR1NG_UTF8_DECODING_2:
return TtStringUTF8StateDecoding2;
case M_STR1NG_UTF8_DECODING_3:
return TtStringUTF8StateDecoding3;
default:
return TtStringUTF8StateError;
}
}
void tt_string_utf8_decode(char c, TtStringUTF8State* state, TtStringUnicodeValue* unicode) {
string_unicode_t m_u = *unicode;
m_str1ng_utf8_state_e m_state = tt_state_to_state(*state);
m_str1ng_utf8_decode(c, &m_state, &m_u);
*state = state_to_tt_state(m_state);
*unicode = m_u;
}

View File

@ -1,7 +1,3 @@
/**
* @file string.h
* Furi string primitive
*/
#pragma once
#include <m-core.h>
@ -15,77 +11,77 @@ extern "C" {
#endif
/**
* @brief Furi string failure constant.
* @brief String failure constant.
*/
#define FURI_STRING_FAILURE ((size_t)-1)
#define TT_STRING_FAILURE ((size_t)-1)
/**
* @brief Furi string primitive.
* @brief Tactility string primitive.
*/
typedef struct FuriString FuriString;
typedef struct TtString TtString;
//---------------------------------------------------------------------------
// Constructors
//---------------------------------------------------------------------------
/**
* @brief Allocate new FuriString.
* @return FuriString*
* @brief Allocate new TtString.
* @return TtString*
*/
FuriString* furi_string_alloc();
TtString* tt_string_alloc();
/**
* @brief Allocate new FuriString and set it to string.
* @brief Allocate new TtString and set it to string.
* Allocate & Set the string a to the string.
* @param source
* @return FuriString*
* @return TtString*
*/
FuriString* furi_string_alloc_set(const FuriString* source);
TtString* tt_string_alloc_set(const TtString* source);
/**
* @brief Allocate new FuriString and set it to C string.
* @brief Allocate new TtString and set it to C string.
* Allocate & Set the string a to the C string.
* @param cstr_source
* @return FuriString*
* @return TtString*
*/
FuriString* furi_string_alloc_set_str(const char cstr_source[]);
TtString* tt_string_alloc_set_str(const char cstr_source[]);
/**
* @brief Allocate new FuriString and printf to it.
* @brief Allocate new TtString and printf to it.
* Initialize and set a string to the given formatted value.
* @param format
* @param ...
* @return FuriString*
* @return TtString*
*/
FuriString* furi_string_alloc_printf(const char format[], ...)
TtString* tt_string_alloc_printf(const char format[], ...)
_ATTRIBUTE((__format__(__printf__, 1, 2)));
/**
* @brief Allocate new FuriString and printf to it.
* @brief Allocate new TtString and printf to it.
* Initialize and set a string to the given formatted value.
* @param format
* @param args
* @return FuriString*
* @return TtString*
*/
FuriString* furi_string_alloc_vprintf(const char format[], va_list args);
TtString* tt_string_alloc_vprintf(const char format[], va_list args);
/**
* @brief Allocate new FuriString and move source string content to it.
* @brief Allocate new TtString and move source string content to it.
* Allocate the string, set it to the other one, and destroy the other one.
* @param source
* @return FuriString*
* @return TtString*
*/
FuriString* furi_string_alloc_move(FuriString* source);
TtString* tt_string_alloc_move(TtString* source);
//---------------------------------------------------------------------------
// Destructors
//---------------------------------------------------------------------------
/**
* @brief Free FuriString.
* @brief Free TtString.
* @param string
*/
void furi_string_free(FuriString* string);
void tt_string_free(TtString* string);
//---------------------------------------------------------------------------
// String memory management
@ -97,14 +93,14 @@ void furi_string_free(FuriString* string);
* @param string
* @param size
*/
void furi_string_reserve(FuriString* string, size_t size);
void tt_string_reserve(TtString* string, size_t size);
/**
* @brief Reset string.
* Make the string empty.
* @param s
*/
void furi_string_reset(FuriString* string);
void tt_string_reset(TtString* string);
/**
* @brief Swap two strings.
@ -112,7 +108,7 @@ void furi_string_reset(FuriString* string);
* @param string_1
* @param string_2
*/
void furi_string_swap(FuriString* string_1, FuriString* string_2);
void tt_string_swap(TtString* string_1, TtString* string_2);
/**
* @brief Move string_2 content to string_1.
@ -120,28 +116,28 @@ void furi_string_swap(FuriString* string_1, FuriString* string_2);
* @param string_1
* @param string_2
*/
void furi_string_move(FuriString* string_1, FuriString* string_2);
void tt_string_move(TtString* string_1, TtString* string_2);
/**
* @brief Compute a hash for the string.
* @param string
* @return size_t
*/
size_t furi_string_hash(const FuriString* string);
size_t tt_string_hash(const TtString* string);
/**
* @brief Get string size (usually length, but not for UTF-8)
* @param string
* @return size_t
*/
size_t furi_string_size(const FuriString* string);
size_t tt_string_size(const TtString* string);
/**
* @brief Check that string is empty or not
* @param string
* @return bool
*/
bool furi_string_empty(const FuriString* string);
bool tt_string_empty(const TtString* string);
//---------------------------------------------------------------------------
// Getters
@ -154,14 +150,14 @@ bool furi_string_empty(const FuriString* string);
* @param index
* @return char
*/
char furi_string_get_char(const FuriString* string, size_t index);
char tt_string_get_char(const TtString* string, size_t index);
/**
* @brief Return the string view a classic C string.
* @param string
* @return const char*
*/
const char* furi_string_get_cstr(const FuriString* string);
const char* tt_string_get_cstr(const TtString* string);
//---------------------------------------------------------------------------
// Setters
@ -173,7 +169,7 @@ const char* furi_string_get_cstr(const FuriString* string);
* @param string
* @param source
*/
void furi_string_set(FuriString* string, FuriString* source);
void tt_string_set(TtString* string, TtString* source);
/**
* @brief Set the string to the other C string.
@ -181,7 +177,7 @@ void furi_string_set(FuriString* string, FuriString* source);
* @param string
* @param source
*/
void furi_string_set_str(FuriString* string, const char source[]);
void tt_string_set_str(TtString* string, const char source[]);
/**
* @brief Set the string to the n first characters of the C string.
@ -189,7 +185,7 @@ void furi_string_set_str(FuriString* string, const char source[]);
* @param source
* @param length
*/
void furi_string_set_strn(FuriString* string, const char source[], size_t length);
void tt_string_set_strn(TtString* string, const char source[], size_t length);
/**
* @brief Set the character at the given index.
@ -197,7 +193,7 @@ void furi_string_set_strn(FuriString* string, const char source[], size_t length
* @param index
* @param c
*/
void furi_string_set_char(FuriString* string, size_t index, const char c);
void tt_string_set_char(TtString* string, size_t index, const char c);
/**
* @brief Set the string to the n first characters of other one.
@ -206,7 +202,7 @@ void furi_string_set_char(FuriString* string, size_t index, const char c);
* @param offset
* @param length
*/
void furi_string_set_n(FuriString* string, const FuriString* source, size_t offset, size_t length);
void tt_string_set_n(TtString* string, const TtString* source, size_t offset, size_t length);
/**
* @brief Format in the string the given printf format
@ -215,7 +211,7 @@ void furi_string_set_n(FuriString* string, const FuriString* source, size_t offs
* @param ...
* @return int
*/
int furi_string_printf(FuriString* string, const char format[], ...)
int tt_string_printf(TtString* string, const char format[], ...)
_ATTRIBUTE((__format__(__printf__, 2, 3)));
/**
@ -225,7 +221,7 @@ int furi_string_printf(FuriString* string, const char format[], ...)
* @param args
* @return int
*/
int furi_string_vprintf(FuriString* string, const char format[], va_list args);
int tt_string_vprintf(TtString* string, const char format[], va_list args);
//---------------------------------------------------------------------------
// Appending
@ -236,7 +232,7 @@ int furi_string_vprintf(FuriString* string, const char format[], va_list args);
* @param string
* @param c
*/
void furi_string_push_back(FuriString* string, char c);
void tt_string_push_back(TtString* string, char c);
/**
* @brief Append a string to the string.
@ -244,7 +240,7 @@ void furi_string_push_back(FuriString* string, char c);
* @param string_1
* @param string_2
*/
void furi_string_cat(FuriString* string_1, const FuriString* string_2);
void tt_string_cat(TtString* string_1, const TtString* string_2);
/**
* @brief Append a C string to the string.
@ -252,7 +248,7 @@ void furi_string_cat(FuriString* string_1, const FuriString* string_2);
* @param string_1
* @param cstring_2
*/
void furi_string_cat_str(FuriString* string_1, const char cstring_2[]);
void tt_string_cat_str(TtString* string_1, const char cstring_2[]);
/**
* @brief Append to the string the formatted string of the given printf format.
@ -261,7 +257,7 @@ void furi_string_cat_str(FuriString* string_1, const char cstring_2[]);
* @param ...
* @return int
*/
int furi_string_cat_printf(FuriString* string, const char format[], ...)
int tt_string_cat_printf(TtString* string, const char format[], ...)
_ATTRIBUTE((__format__(__printf__, 2, 3)));
/**
@ -271,7 +267,7 @@ int furi_string_cat_printf(FuriString* string, const char format[], ...)
* @param args
* @return int
*/
int furi_string_cat_vprintf(FuriString* string, const char format[], va_list args);
int tt_string_cat_vprintf(TtString* string, const char format[], va_list args);
//---------------------------------------------------------------------------
// Comparators
@ -283,7 +279,7 @@ int furi_string_cat_vprintf(FuriString* string, const char format[], va_list arg
* @param string_2
* @return int
*/
int furi_string_cmp(const FuriString* string_1, const FuriString* string_2);
int tt_string_cmp(const TtString* string_1, const TtString* string_2);
/**
* @brief Compare string with C string and return the sort order.
@ -291,7 +287,7 @@ int furi_string_cmp(const FuriString* string_1, const FuriString* string_2);
* @param cstring_2
* @return int
*/
int furi_string_cmp_str(const FuriString* string_1, const char cstring_2[]);
int tt_string_cmp_str(const TtString* string_1, const char cstring_2[]);
/**
* @brief Compare two strings (case insensitive according to the current locale) and return the sort order.
@ -300,7 +296,7 @@ int furi_string_cmp_str(const FuriString* string_1, const char cstring_2[]);
* @param string_2
* @return int
*/
int furi_string_cmpi(const FuriString* string_1, const FuriString* string_2);
int tt_string_cmpi(const TtString* string_1, const TtString* string_2);
/**
* @brief Compare string with C string (case insensitive according to the current locale) and return the sort order.
@ -309,7 +305,7 @@ int furi_string_cmpi(const FuriString* string_1, const FuriString* string_2);
* @param cstring_2
* @return int
*/
int furi_string_cmpi_str(const FuriString* string_1, const char cstring_2[]);
int tt_string_cmpi_str(const TtString* string_1, const char cstring_2[]);
//---------------------------------------------------------------------------
// Search
@ -324,7 +320,7 @@ int furi_string_cmpi_str(const FuriString* string_1, const char cstring_2[]);
* @param start
* @return size_t
*/
size_t furi_string_search(const FuriString* string, const FuriString* needle, size_t start);
size_t tt_string_search(const TtString* string, const TtString* needle, size_t start);
/**
* @brief Search the first occurrence of the needle in the string from the position start.
@ -334,7 +330,7 @@ size_t furi_string_search(const FuriString* string, const FuriString* needle, si
* @param start
* @return size_t
*/
size_t furi_string_search_str(const FuriString* string, const char needle[], size_t start);
size_t tt_string_search_str(const TtString* string, const char needle[], size_t start);
/**
* @brief Search for the position of the character c from the position start (include) in the string.
@ -345,7 +341,7 @@ size_t furi_string_search_str(const FuriString* string, const char needle[], siz
* @param start
* @return size_t
*/
size_t furi_string_search_char(const FuriString* string, char c, size_t start);
size_t tt_string_search_char(const TtString* string, char c, size_t start);
/**
* @brief Reverse search for the position of the character c from the position start (include) in the string.
@ -356,7 +352,7 @@ size_t furi_string_search_char(const FuriString* string, char c, size_t start);
* @param start
* @return size_t
*/
size_t furi_string_search_rchar(const FuriString* string, char c, size_t start);
size_t tt_string_search_rchar(const TtString* string, char c, size_t start);
//---------------------------------------------------------------------------
// Equality
@ -368,7 +364,7 @@ size_t furi_string_search_rchar(const FuriString* string, char c, size_t start);
* @param string_2
* @return bool
*/
bool furi_string_equal(const FuriString* string_1, const FuriString* string_2);
bool tt_string_equal(const TtString* string_1, const TtString* string_2);
/**
* @brief Test if the string is equal to the C string.
@ -376,7 +372,7 @@ bool furi_string_equal(const FuriString* string_1, const FuriString* string_2);
* @param cstring_2
* @return bool
*/
bool furi_string_equal_str(const FuriString* string_1, const char cstring_2[]);
bool tt_string_equal_str(const TtString* string_1, const char cstring_2[]);
//---------------------------------------------------------------------------
// Replace
@ -389,7 +385,7 @@ bool furi_string_equal_str(const FuriString* string_1, const char cstring_2[]);
* @param len
* @param replace
*/
void furi_string_replace_at(FuriString* string, size_t pos, size_t len, const char replace[]);
void tt_string_replace_at(TtString* string, size_t pos, size_t len, const char replace[]);
/**
* @brief Replace a string 'needle' to string 'replace' in a string from 'start' position.
@ -402,7 +398,7 @@ void furi_string_replace_at(FuriString* string, size_t pos, size_t len, const ch
* @return size_t
*/
size_t
furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace, size_t start);
tt_string_replace(TtString* string, TtString* needle, TtString* replace, size_t start);
/**
* @brief Replace a C string 'needle' to C string 'replace' in a string from 'start' position.
@ -414,8 +410,8 @@ furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace,
* @param start
* @return size_t
*/
size_t furi_string_replace_str(
FuriString* string,
size_t tt_string_replace_str(
TtString* string,
const char needle[],
const char replace[],
size_t start
@ -427,10 +423,10 @@ size_t furi_string_replace_str(
* @param needle
* @param replace
*/
void furi_string_replace_all(
FuriString* string,
const FuriString* needle,
const FuriString* replace
void tt_string_replace_all(
TtString* string,
const TtString* needle,
const TtString* replace
);
/**
@ -439,7 +435,7 @@ void furi_string_replace_all(
* @param needle
* @param replace
*/
void furi_string_replace_all_str(FuriString* string, const char needle[], const char replace[]);
void tt_string_replace_all_str(TtString* string, const char needle[], const char replace[]);
//---------------------------------------------------------------------------
// Start / End tests
@ -451,7 +447,7 @@ void furi_string_replace_all_str(FuriString* string, const char needle[], const
* @param start
* @return bool
*/
bool furi_string_start_with(const FuriString* string, const FuriString* start);
bool tt_string_start_with(const TtString* string, const TtString* start);
/**
* @brief Test if the string starts with the given C string.
@ -459,7 +455,7 @@ bool furi_string_start_with(const FuriString* string, const FuriString* start);
* @param start
* @return bool
*/
bool furi_string_start_with_str(const FuriString* string, const char start[]);
bool tt_string_start_with_str(const TtString* string, const char start[]);
/**
* @brief Test if the string ends with the given string.
@ -467,7 +463,7 @@ bool furi_string_start_with_str(const FuriString* string, const char start[]);
* @param end
* @return bool
*/
bool furi_string_end_with(const FuriString* string, const FuriString* end);
bool tt_string_end_with(const TtString* string, const TtString* end);
/**
* @brief Test if the string ends with the given C string.
@ -475,7 +471,7 @@ bool furi_string_end_with(const FuriString* string, const FuriString* end);
* @param end
* @return bool
*/
bool furi_string_end_with_str(const FuriString* string, const char end[]);
bool tt_string_end_with_str(const TtString* string, const char end[]);
//---------------------------------------------------------------------------
// Trim
@ -486,30 +482,30 @@ bool furi_string_end_with_str(const FuriString* string, const char end[]);
* @param string
* @param index
*/
void furi_string_left(FuriString* string, size_t index);
void tt_string_left(TtString* string, size_t index);
/**
* @brief Trim the string right from the 'index' position to the last position.
* @param string
* @param index
*/
void furi_string_right(FuriString* string, size_t index);
void tt_string_right(TtString* string, size_t index);
/**
* @brief Trim the string from position index to size bytes.
* See also furi_string_set_n.
* See also tt_string_set_n.
* @param string
* @param index
* @param size
*/
void furi_string_mid(FuriString* string, size_t index, size_t size);
void tt_string_mid(TtString* string, size_t index, size_t size);
/**
* @brief Trim a string from the given set of characters (default is " \n\r\t").
* @param string
* @param chars
*/
void furi_string_trim(FuriString* string, const char chars[]);
void tt_string_trim(TtString* string, const char chars[]);
//---------------------------------------------------------------------------
// UTF8
@ -518,43 +514,43 @@ void furi_string_trim(FuriString* string, const char chars[]);
/**
* @brief An unicode value.
*/
typedef unsigned int FuriStringUnicodeValue;
typedef unsigned int TtStringUnicodeValue;
/**
* @brief Compute the length in UTF8 characters in the string.
* @param string
* @return size_t
*/
size_t furi_string_utf8_length(FuriString* string);
size_t tt_string_utf8_length(TtString* string);
/**
* @brief Push unicode into string, encoding it in UTF8.
* @param string
* @param unicode
*/
void furi_string_utf8_push(FuriString* string, FuriStringUnicodeValue unicode);
void tt_string_utf8_push(TtString* string, TtStringUnicodeValue unicode);
/**
* @brief State of the UTF8 decoding machine state.
*/
typedef enum {
FuriStringUTF8StateStarting,
FuriStringUTF8StateDecoding1,
FuriStringUTF8StateDecoding2,
FuriStringUTF8StateDecoding3,
FuriStringUTF8StateError
} FuriStringUTF8State;
TtStringUTF8StateStarting,
TtStringUTF8StateDecoding1,
TtStringUTF8StateDecoding2,
TtStringUTF8StateDecoding3,
TtStringUTF8StateError
} TtStringUTF8State;
/**
* @brief Main generic UTF8 decoder.
* It takes a character, and the previous state and the previous value of the unicode value.
* It updates the state and the decoded unicode value.
* A decoded unicode encoded value is valid only when the state is FuriStringUTF8StateStarting.
* A decoded unicode encoded value is valid only when the state is TtStringUTF8StateStarting.
* @param c
* @param state
* @param unicode
*/
void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode);
void tt_string_utf8_decode(char c, TtStringUTF8State* state, TtStringUnicodeValue* unicode);
//---------------------------------------------------------------------------
// Lasciate ogne speranza, voi chentrate
@ -570,131 +566,131 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico
/**
* @brief Select for 1 argument
*/
#define FURI_STRING_SELECT1(func1, func2, a) \
_Generic((a), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)(a)
#define TT_STRING_SELECT1(func1, func2, a) \
_Generic((a), char*: func2, const char*: func2, TtString*: func1, const TtString*: func1)(a)
/**
* @brief Select for 2 arguments
*/
#define FURI_STRING_SELECT2(func1, func2, a, b) \
_Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)(a, b)
#define TT_STRING_SELECT2(func1, func2, a, b) \
_Generic((b), char*: func2, const char*: func2, TtString*: func1, const TtString*: func1)(a, b)
/**
* @brief Select for 3 arguments
*/
#define FURI_STRING_SELECT3(func1, func2, a, b, c) \
_Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)(a, b, c)
#define TT_STRING_SELECT3(func1, func2, a, b, c) \
_Generic((b), char*: func2, const char*: func2, TtString*: func1, const TtString*: func1)(a, b, c)
/**
* @brief Select for 4 arguments
*/
#define FURI_STRING_SELECT4(func1, func2, a, b, c, d) \
_Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)(a, b, c, d)
#define TT_STRING_SELECT4(func1, func2, a, b, c, d) \
_Generic((b), char*: func2, const char*: func2, TtString*: func1, const TtString*: func1)(a, b, c, d)
/**
* @brief Allocate new FuriString and set it content to string (or C string).
* @brief Allocate new TtString and set it content to string (or C string).
* ([c]string)
*/
#define furi_string_alloc_set(a) \
FURI_STRING_SELECT1(furi_string_alloc_set, furi_string_alloc_set_str, a)
#define tt_string_alloc_set(a) \
TT_STRING_SELECT1(tt_string_alloc_set, tt_string_alloc_set_str, a)
/**
* @brief Set the string content to string (or C string).
* (string, [c]string)
*/
#define furi_string_set(a, b) FURI_STRING_SELECT2(furi_string_set, furi_string_set_str, a, b)
#define tt_string_set(a, b) TT_STRING_SELECT2(tt_string_set, tt_string_set_str, a, b)
/**
* @brief Compare string with string (or C string) and return the sort order.
* Note: doesn't work with UTF-8 strings.
* (string, [c]string)
*/
#define furi_string_cmp(a, b) FURI_STRING_SELECT2(furi_string_cmp, furi_string_cmp_str, a, b)
#define tt_string_cmp(a, b) TT_STRING_SELECT2(tt_string_cmp, tt_string_cmp_str, a, b)
/**
* @brief Compare string with string (or C string) (case insensitive according to the current locale) and return the sort order.
* Note: doesn't work with UTF-8 strings.
* (string, [c]string)
*/
#define furi_string_cmpi(a, b) FURI_STRING_SELECT2(furi_string_cmpi, furi_string_cmpi_str, a, b)
#define tt_string_cmpi(a, b) TT_STRING_SELECT2(tt_string_cmpi, tt_string_cmpi_str, a, b)
/**
* @brief Test if the string is equal to the string (or C string).
* (string, [c]string)
*/
#define furi_string_equal(a, b) FURI_STRING_SELECT2(furi_string_equal, furi_string_equal_str, a, b)
#define tt_string_equal(a, b) TT_STRING_SELECT2(tt_string_equal, tt_string_equal_str, a, b)
/**
* @brief Replace all occurrences of string into string (or C string to another C string) in a string.
* (string, [c]string, [c]string)
*/
#define furi_string_replace_all(a, b, c) \
FURI_STRING_SELECT3(furi_string_replace_all, furi_string_replace_all_str, a, b, c)
#define tt_string_replace_all(a, b, c) \
TT_STRING_SELECT3(tt_string_replace_all, tt_string_replace_all_str, a, b, c)
/**
* @brief Search for a string (or C string) in a string
* (string, [c]string[, start=0])
*/
#define furi_string_search(...) \
#define tt_string_search(...) \
M_APPLY( \
FURI_STRING_SELECT3, \
furi_string_search, \
furi_string_search_str, \
TT_STRING_SELECT3, \
tt_string_search, \
tt_string_search_str, \
M_DEFAULT_ARGS(3, (0), __VA_ARGS__) \
)
/**
* @brief Search for a C string in a string
* (string, cstring[, start=0])
*/
#define furi_string_search_str(...) furi_string_search_str(M_DEFAULT_ARGS(3, (0), __VA_ARGS__))
#define tt_string_search_str(...) tt_string_search_str(M_DEFAULT_ARGS(3, (0), __VA_ARGS__))
/**
* @brief Test if the string starts with the given string (or C string).
* (string, [c]string)
*/
#define furi_string_start_with(a, b) \
FURI_STRING_SELECT2(furi_string_start_with, furi_string_start_with_str, a, b)
#define tt_string_start_with(a, b) \
TT_STRING_SELECT2(tt_string_start_with, tt_string_start_with_str, a, b)
/**
* @brief Test if the string ends with the given string (or C string).
* (string, [c]string)
*/
#define furi_string_end_with(a, b) \
FURI_STRING_SELECT2(furi_string_end_with, furi_string_end_with_str, a, b)
#define tt_string_end_with(a, b) \
TT_STRING_SELECT2(tt_string_end_with, tt_string_end_with_str, a, b)
/**
* @brief Append a string (or C string) to the string.
* (string, [c]string)
*/
#define furi_string_cat(a, b) FURI_STRING_SELECT2(furi_string_cat, furi_string_cat_str, a, b)
#define tt_string_cat(a, b) TT_STRING_SELECT2(tt_string_cat, tt_string_cat_str, a, b)
/**
* @brief Trim a string from the given set of characters (default is " \n\r\t").
* (string[, set=" \n\r\t"])
*/
#define furi_string_trim(...) furi_string_trim(M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__))
#define tt_string_trim(...) tt_string_trim(M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__))
/**
* @brief Search for a character in a string.
* (string, character[, start=0])
*/
#define furi_string_search_char(...) furi_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__))
#define tt_string_search_char(...) tt_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__))
/**
* @brief Reverse Search for a character in a string.
* (string, character[, start=0])
*/
#define furi_string_search_rchar(...) furi_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__))
#define tt_string_search_rchar(...) tt_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__))
/**
* @brief Replace a string to another string (or C string to another C string) in a string.
* (string, [c]string, [c]string[, start=0])
*/
#define furi_string_replace(...) \
#define tt_string_replace(...) \
M_APPLY( \
FURI_STRING_SELECT4, \
furi_string_replace, \
furi_string_replace_str, \
TT_STRING_SELECT4, \
tt_string_replace, \
tt_string_replace_str, \
M_DEFAULT_ARGS(4, (0), __VA_ARGS__) \
)
@ -702,40 +698,40 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico
* @brief Replace a C string to another C string in a string.
* (string, cstring, cstring[, start=0])
*/
#define furi_string_replace_str(...) furi_string_replace_str(M_DEFAULT_ARGS(4, (0), __VA_ARGS__))
#define tt_string_replace_str(...) tt_string_replace_str(M_DEFAULT_ARGS(4, (0), __VA_ARGS__))
/**
* @brief INIT OPLIST for FuriString.
* @brief INIT OPLIST for TtString.
*/
#define F_STR_INIT(a) ((a) = furi_string_alloc())
#define F_STR_INIT(a) ((a) = tt_string_alloc())
/**
* @brief INIT SET OPLIST for FuriString.
* @brief INIT SET OPLIST for TtString.
*/
#define F_STR_INIT_SET(a, b) ((a) = furi_string_alloc_set(b))
#define F_STR_INIT_SET(a, b) ((a) = tt_string_alloc_set(b))
/**
* @brief INIT MOVE OPLIST for FuriString.
* @brief INIT MOVE OPLIST for TtString.
*/
#define F_STR_INIT_MOVE(a, b) ((a) = furi_string_alloc_move(b))
#define F_STR_INIT_MOVE(a, b) ((a) = tt_string_alloc_move(b))
/**
* @brief OPLIST for FuriString.
* @brief OPLIST for TtString.
*/
#define FURI_STRING_OPLIST \
#define TT_STRING_OPLIST \
(INIT(F_STR_INIT), \
INIT_SET(F_STR_INIT_SET), \
SET(furi_string_set), \
SET(tt_string_set), \
INIT_MOVE(F_STR_INIT_MOVE), \
MOVE(furi_string_move), \
SWAP(furi_string_swap), \
RESET(furi_string_reset), \
EMPTY_P(furi_string_empty), \
CLEAR(furi_string_free), \
HASH(furi_string_hash), \
EQUAL(furi_string_equal), \
CMP(furi_string_cmp), \
TYPE(FuriString*))
MOVE(tt_string_move), \
SWAP(tt_string_swap), \
RESET(tt_string_reset), \
EMPTY_P(tt_string_empty), \
CLEAR(tt_string_free), \
HASH(tt_string_hash), \
EQUAL(tt_string_equal), \
CMP(tt_string_cmp), \
TYPE(TtString*))
#ifdef __cplusplus
}

View File

@ -19,7 +19,7 @@ idf_component_register(
esp_wifi
driver
fatfs
furi
tactility-core
mlib
nvs_flash
spiffs

View File

@ -12,7 +12,7 @@ static void on_app_pressed(lv_event_t* e) {
}
static void create_app_widget(const AppManifest* manifest, void* _Nullable parent) {
furi_check(parent);
tt_check(parent);
lv_obj_t* list = (lv_obj_t*)parent;
lv_obj_t* btn = lv_list_add_btn(list, LV_SYMBOL_FILE, manifest->name);
lv_obj_add_event_cb(btn, &on_app_pressed, LV_EVENT_CLICKED, (void*)manifest);
@ -26,9 +26,9 @@ static void desktop_show(App app, lv_obj_t* parent) {
lv_obj_center(list);
lv_list_add_text(list, "System");
app_manifest_registry_for_each_of_type(AppTypeSystem, list, create_app_widget);
tt_app_manifest_registry_for_each_of_type(AppTypeSystem, list, create_app_widget);
lv_list_add_text(list, "User");
app_manifest_registry_for_each_of_type(AppTypeUser, list, create_app_widget);
tt_app_manifest_registry_for_each_of_type(AppTypeUser, list, create_app_widget);
}
const AppManifest desktop_app = {

View File

@ -1,8 +1,8 @@
#include "app_manifest.h"
#include "furi_extra_defines.h"
#include "thread.h"
#include "lvgl.h"
#include "core_extra_defines.h"
#include "esp_wifi.h"
#include "lvgl.h"
#include "thread.h"
static void app_show(App app, lv_obj_t* parent) {
UNUSED(app);

View File

@ -2,8 +2,8 @@
#include "app.h"
#include "esp_lvgl_port.h"
#include "furi_core.h"
#include "services/wifi/wifi.h"
#include "tactility_core.h"
#include "wifi_connect_state_updating.h"
// Forward declarations
@ -17,9 +17,9 @@ static void on_connect(const char* ssid, const char* password, void* parameter)
static WifiConnect* wifi_connect_alloc() {
WifiConnect* wifi = malloc(sizeof(WifiConnect));
FuriPubSub* wifi_pubsub = wifi_get_pubsub();
wifi->wifi_subscription = furi_pubsub_subscribe(wifi_pubsub, &wifi_connect_event_callback, wifi);
wifi->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
PubSub* wifi_pubsub = wifi_get_pubsub();
wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &wifi_connect_event_callback, wifi);
wifi->mutex = tt_mutex_alloc(MutexTypeNormal);
wifi->state = (WifiConnectState) {
.radio_state = wifi_get_radio_state()
};
@ -33,23 +33,23 @@ static WifiConnect* wifi_connect_alloc() {
}
static void wifi_connect_free(WifiConnect* wifi) {
FuriPubSub* wifi_pubsub = wifi_get_pubsub();
furi_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription);
furi_mutex_free(wifi->mutex);
PubSub* wifi_pubsub = wifi_get_pubsub();
tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription);
tt_mutex_free(wifi->mutex);
free(wifi);
}
void wifi_connect_lock(WifiConnect* wifi) {
furi_assert(wifi);
furi_assert(wifi->mutex);
furi_mutex_acquire(wifi->mutex, FuriWaitForever);
tt_assert(wifi);
tt_assert(wifi->mutex);
tt_mutex_acquire(wifi->mutex, TtWaitForever);
}
void wifi_connect_unlock(WifiConnect* wifi) {
furi_assert(wifi);
furi_assert(wifi->mutex);
furi_mutex_release(wifi->mutex);
tt_assert(wifi);
tt_assert(wifi->mutex);
tt_mutex_release(wifi->mutex);
}
void wifi_connect_request_view_update(WifiConnect* wifi) {
@ -76,7 +76,7 @@ static void wifi_connect_event_callback(const void* message, void* context) {
}
static void app_show(App app, lv_obj_t* parent) {
WifiConnect* wifi = (WifiConnect*)app_get_data(app);
WifiConnect* wifi = (WifiConnect*)tt_app_get_data(app);
wifi_connect_lock(wifi);
wifi->view_enabled = true;
@ -86,7 +86,7 @@ static void app_show(App app, lv_obj_t* parent) {
}
static void app_hide(App app) {
WifiConnect* wifi = (WifiConnect*)app_get_data(app);
WifiConnect* wifi = (WifiConnect*)tt_app_get_data(app);
wifi_connect_lock(wifi);
wifi->view_enabled = false;
wifi_connect_unlock(wifi);
@ -94,14 +94,14 @@ static void app_hide(App app) {
static void app_start(App app) {
WifiConnect* wifi_connect = wifi_connect_alloc(app);
app_set_data(app, wifi_connect);
tt_app_set_data(app, wifi_connect);
}
static void app_stop(App app) {
WifiConnect* wifi = app_get_data(app);
furi_assert(wifi != NULL);
WifiConnect* wifi = tt_app_get_data(app);
tt_assert(wifi != NULL);
wifi_connect_free(wifi);
app_set_data(app, NULL);
tt_app_set_data(app, NULL);
}
AppManifest wifi_connect_app = {

View File

@ -11,8 +11,8 @@ extern "C" {
#endif
typedef struct {
FuriPubSubSubscription* wifi_subscription;
FuriMutex* mutex;
PubSubSubscription* wifi_subscription;
Mutex* mutex;
WifiConnectState state;
WifiConnectView view;
bool view_enabled;

View File

@ -64,15 +64,15 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) {
lv_obj_add_event_cb(view->connect_button, &on_connect, LV_EVENT_CLICKED, wifi);
// Init from app parameters
Bundle* _Nullable bundle = app_get_parameters(app);
Bundle* _Nullable bundle = tt_app_get_parameters(app);
if (bundle) {
char* ssid;
if (bundle_opt_string(bundle, WIFI_CONNECT_PARAM_SSID, &ssid)) {
if (tt_bundle_opt_string(bundle, WIFI_CONNECT_PARAM_SSID, &ssid)) {
lv_textarea_set_text(view->ssid_textarea, ssid);
}
char* password;
if (bundle_opt_string(bundle, WIFI_CONNECT_PARAM_PASSWORD, &password)) {
if (tt_bundle_opt_string(bundle, WIFI_CONNECT_PARAM_PASSWORD, &password)) {
lv_textarea_set_text(view->password_textarea, password);
}
}

View File

@ -3,8 +3,8 @@
#include "app.h"
#include "apps/system/wifi_connect/wifi_connect_bundle.h"
#include "esp_lvgl_port.h"
#include "furi_core.h"
#include "services/loader/loader.h"
#include "tactility_core.h"
#include "wifi_manage_state_updating.h"
#include "wifi_manage_view.h"
@ -12,9 +12,9 @@
static void wifi_manage_event_callback(const void* message, void* context);
static void on_connect(const char* ssid) {
Bundle bundle = bundle_alloc();
bundle_put_string(bundle, WIFI_CONNECT_PARAM_SSID, ssid);
bundle_put_string(bundle, WIFI_CONNECT_PARAM_PASSWORD, ""); // TODO: Implement from cache
Bundle bundle = tt_bundle_alloc();
tt_bundle_put_string(bundle, WIFI_CONNECT_PARAM_SSID, ssid);
tt_bundle_put_string(bundle, WIFI_CONNECT_PARAM_PASSWORD, ""); // TODO: Implement from cache
loader_start_app("wifi_connect", false, bundle);
}
@ -29,9 +29,9 @@ static void on_wifi_toggled(bool enabled) {
static WifiManage* wifi_manage_alloc() {
WifiManage* wifi = malloc(sizeof(WifiManage));
FuriPubSub* wifi_pubsub = wifi_get_pubsub();
wifi->wifi_subscription = furi_pubsub_subscribe(wifi_pubsub, &wifi_manage_event_callback, wifi);
wifi->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
PubSub* wifi_pubsub = wifi_get_pubsub();
wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &wifi_manage_event_callback, wifi);
wifi->mutex = tt_mutex_alloc(MutexTypeNormal);
wifi->state = (WifiManageState) {
.scanning = wifi_is_scanning(),
.radio_state = wifi_get_radio_state()
@ -47,23 +47,23 @@ static WifiManage* wifi_manage_alloc() {
}
static void wifi_manage_free(WifiManage* wifi) {
FuriPubSub* wifi_pubsub = wifi_get_pubsub();
furi_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription);
furi_mutex_free(wifi->mutex);
PubSub* wifi_pubsub = wifi_get_pubsub();
tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription);
tt_mutex_free(wifi->mutex);
free(wifi);
}
void wifi_manage_lock(WifiManage* wifi) {
furi_assert(wifi);
furi_assert(wifi->mutex);
furi_mutex_acquire(wifi->mutex, FuriWaitForever);
tt_assert(wifi);
tt_assert(wifi->mutex);
tt_mutex_acquire(wifi->mutex, TtWaitForever);
}
void wifi_manage_unlock(WifiManage* wifi) {
furi_assert(wifi);
furi_assert(wifi->mutex);
furi_mutex_release(wifi->mutex);
tt_assert(wifi);
tt_assert(wifi->mutex);
tt_mutex_release(wifi->mutex);
}
void wifi_manage_request_view_update(WifiManage* wifi) {
@ -99,7 +99,7 @@ static void wifi_manage_event_callback(const void* message, void* context) {
}
static void app_show(App app, lv_obj_t* parent) {
WifiManage* wifi = (WifiManage*)app_get_data(app);
WifiManage* wifi = (WifiManage*)tt_app_get_data(app);
// State update (it has its own locking)
wifi_manage_state_set_radio_state(wifi, wifi_get_radio_state());
@ -120,7 +120,7 @@ static void app_show(App app, lv_obj_t* parent) {
}
static void app_hide(App app) {
WifiManage* wifi = (WifiManage*)app_get_data(app);
WifiManage* wifi = (WifiManage*)tt_app_get_data(app);
wifi_manage_lock(wifi);
wifi->view_enabled = false;
wifi_manage_unlock(wifi);
@ -128,14 +128,14 @@ static void app_hide(App app) {
static void app_start(App app) {
WifiManage* wifi = wifi_manage_alloc();
app_set_data(app, wifi);
tt_app_set_data(app, wifi);
}
static void app_stop(App app) {
WifiManage* wifi = (WifiManage*)app_get_data(app);
furi_assert(wifi != NULL);
WifiManage* wifi = (WifiManage*)tt_app_get_data(app);
tt_assert(wifi != NULL);
wifi_manage_free(wifi);
app_set_data(app, NULL);
tt_app_set_data(app, NULL);
}
AppManifest wifi_manage_app = {

View File

@ -9,8 +9,8 @@ extern "C" {
#endif
typedef struct {
FuriPubSubSubscription* wifi_subscription;
FuriMutex* mutex;
PubSubSubscription* wifi_subscription;
Mutex* mutex;
WifiManageState state;
WifiManageView view;
bool view_enabled;

View File

@ -56,7 +56,7 @@ static void connect(lv_event_t* event) {
// We get the SSID from the button label because it's safer than alloc'ing
// our own and passing it as the event data
const char* ssid = lv_label_get_text(label);
FURI_LOG_I(TAG, "Clicked AP: %s", ssid);
TT_LOG_I(TAG, "Clicked AP: %s", ssid);
WifiManageBindings* bindings = (WifiManageBindings*)event->user_data;
bindings->on_connect_ssid(ssid);
}

View File

@ -11,7 +11,7 @@ Hardware tt_hardware_init(const HardwareConfig _Nonnull* config) {
config->bootstrap();
}
furi_check(config->display_driver != NULL, "no display driver configured");
tt_check(config->display_driver != NULL, "no display driver configured");
DisplayDriver display_driver = config->display_driver();
ESP_LOGI(TAG, "display with driver %s", display_driver.name);
DisplayDevice* display = tt_display_device_alloc(&display_driver);

View File

@ -4,12 +4,12 @@
DisplayDevice _Nonnull* tt_display_device_alloc(DisplayDriver _Nonnull* driver) {
DisplayDevice _Nonnull* display = malloc(sizeof(DisplayDevice));
memset(display, 0, sizeof(DisplayDevice));
furi_check(driver->create_display_device(display), "failed to create display");
furi_check(display->io_handle != NULL);
furi_check(display->display_handle != NULL);
furi_check(display->horizontal_resolution != 0);
furi_check(display->vertical_resolution != 0);
furi_check(display->draw_buffer_height > 0);
furi_check(display->bits_per_pixel > 0);
tt_check(driver->create_display_device(display), "failed to create display");
tt_check(display->io_handle != NULL);
tt_check(display->display_handle != NULL);
tt_check(display->horizontal_resolution != 0);
tt_check(display->vertical_resolution != 0);
tt_check(display->draw_buffer_height > 0);
tt_check(display->bits_per_pixel > 0);
return display;
}

View File

@ -14,7 +14,7 @@ Lvgl tt_graphics_init(Hardware _Nonnull* hardware) {
.timer_period_ms = 5
};
furi_check(lvgl_port_init(&lvgl_cfg) == ESP_OK, "lvgl port init failed");
tt_check(lvgl_port_init(&lvgl_cfg) == ESP_OK, "lvgl port init failed");
DisplayDevice _Nonnull* display = hardware->display;
// Add display
@ -38,7 +38,7 @@ Lvgl tt_graphics_init(Hardware _Nonnull* hardware) {
};
lv_disp_t _Nonnull* disp = lvgl_port_add_disp(&disp_cfg);
furi_check(disp != NULL, "failed to add display");
tt_check(disp != NULL, "failed to add display");
lv_indev_t _Nullable* touch_indev = NULL;
@ -49,7 +49,7 @@ Lvgl tt_graphics_init(Hardware _Nonnull* hardware) {
.handle = hardware->touch->touch_handle,
};
touch_indev = lvgl_port_add_touch(&touch_cfg);
furi_check(touch_indev != NULL, "failed to add touch to lvgl");
tt_check(touch_indev != NULL, "failed to add touch to lvgl");
}
return (Lvgl) {

View File

@ -18,11 +18,11 @@ static esp_err_t spiffs_init(esp_vfs_spiffs_conf_t* conf) {
esp_err_t ret = esp_vfs_spiffs_register(conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
FURI_LOG_E(TAG, "Failed to mount or format filesystem %s", conf->base_path);
TT_LOG_E(TAG, "Failed to mount or format filesystem %s", conf->base_path);
} else if (ret == ESP_ERR_NOT_FOUND) {
FURI_LOG_E(TAG, "Failed to find SPIFFS partition %s", conf->base_path);
TT_LOG_E(TAG, "Failed to find SPIFFS partition %s", conf->base_path);
} else {
FURI_LOG_E(TAG, "Failed to initialize SPIFFS %s (%s)", conf->base_path, esp_err_to_name(ret));
TT_LOG_E(TAG, "Failed to initialize SPIFFS %s (%s)", conf->base_path, esp_err_to_name(ret));
}
return ESP_FAIL;
}
@ -30,9 +30,9 @@ static esp_err_t spiffs_init(esp_vfs_spiffs_conf_t* conf) {
size_t total = -1, used = 0;
ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) {
FURI_LOG_E(TAG, "Failed to get SPIFFS partition information for %s (%s)", conf->base_path, esp_err_to_name(ret));
TT_LOG_E(TAG, "Failed to get SPIFFS partition information for %s (%s)", conf->base_path, esp_err_to_name(ret));
} else {
FURI_LOG_I(TAG, "Partition size for %s: total: %d, used: %d", conf->base_path, total, used);
TT_LOG_I(TAG, "Partition size for %s: total: %d, used: %d", conf->base_path, total, used);
}
return ESP_OK;
}

View File

@ -1,10 +1,10 @@
#include "gui_i.h"
#include "check.h"
#include "core_extra_defines.h"
#include "esp_lvgl_port.h"
#include "furi_extra_defines.h"
#include "log.h"
#include "kernel.h"
#include "log.h"
#define TAG "gui"
@ -17,16 +17,16 @@ static Gui* gui = NULL;
Gui* gui_alloc() {
Gui* instance = malloc(sizeof(Gui));
memset(instance, 0, sizeof(Gui));
furi_check(instance != NULL);
instance->thread = furi_thread_alloc_ex(
tt_check(instance != NULL);
instance->thread = tt_thread_alloc_ex(
"gui",
4096, // Last known minimum was 2800 for launching desktop
&gui_main,
NULL
);
instance->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
instance->mutex = tt_mutex_alloc(MutexTypeRecursive);
furi_check(lvgl_port_lock(100));
tt_check(lvgl_port_lock(100));
instance->lvgl_parent = lv_scr_act();
lvgl_port_unlock();
@ -34,33 +34,33 @@ Gui* gui_alloc() {
}
void gui_free(Gui* instance) {
furi_assert(instance != NULL);
furi_thread_free(instance->thread);
furi_mutex_free(instance->mutex);
tt_assert(instance != NULL);
tt_thread_free(instance->thread);
tt_mutex_free(instance->mutex);
free(instance);
}
void gui_lock() {
furi_assert(gui);
furi_assert(gui->mutex);
furi_check(furi_mutex_acquire(gui->mutex, 1000 / portTICK_PERIOD_MS) == FuriStatusOk);
tt_assert(gui);
tt_assert(gui->mutex);
tt_check(tt_mutex_acquire(gui->mutex, 1000 / portTICK_PERIOD_MS) == TtStatusOk);
}
void gui_unlock() {
furi_assert(gui);
furi_assert(gui->mutex);
furi_check(furi_mutex_release(gui->mutex) == FuriStatusOk);
tt_assert(gui);
tt_assert(gui->mutex);
tt_check(tt_mutex_release(gui->mutex) == TtStatusOk);
}
void gui_request_draw() {
furi_assert(gui);
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
tt_assert(gui);
ThreadId thread_id = tt_thread_get_id(gui->thread);
tt_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
}
void gui_show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) {
gui_lock();
furi_check(gui->app_view_port == NULL);
tt_check(gui->app_view_port == NULL);
gui->app_view_port = view_port_alloc(app, on_show, on_hide);
gui_unlock();
gui_request_draw();
@ -69,7 +69,7 @@ void gui_show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on
void gui_hide_app() {
gui_lock();
ViewPort* view_port = gui->app_view_port;
furi_check(view_port != NULL);
tt_check(view_port != NULL);
view_port_hide(view_port);
view_port_free(view_port);
gui->app_view_port = NULL;
@ -78,23 +78,23 @@ void gui_hide_app() {
static int32_t gui_main(void* p) {
UNUSED(p);
furi_check(gui);
tt_check(gui);
Gui* local_gui = gui;
while (1) {
uint32_t flags = furi_thread_flags_wait(
uint32_t flags = tt_thread_flags_wait(
GUI_THREAD_FLAG_ALL,
FuriFlagWaitAny,
FuriWaitForever
TtFlagWaitAny,
TtWaitForever
);
// Process and dispatch draw call
if (flags & GUI_THREAD_FLAG_DRAW) {
furi_thread_flags_clear(GUI_THREAD_FLAG_DRAW);
tt_thread_flags_clear(GUI_THREAD_FLAG_DRAW);
gui_redraw(local_gui);
}
if (flags & GUI_THREAD_FLAG_EXIT) {
furi_thread_flags_clear(GUI_THREAD_FLAG_EXIT);
tt_thread_flags_clear(GUI_THREAD_FLAG_EXIT);
break;
}
}
@ -109,8 +109,8 @@ static void gui_start(Service service) {
gui = gui_alloc();
furi_thread_set_priority(gui->thread, FuriThreadPriorityNormal);
furi_thread_start(gui->thread);
tt_thread_set_priority(gui->thread, ThreadPriorityNormal);
tt_thread_start(gui->thread);
}
static void gui_stop(Service service) {
@ -118,9 +118,9 @@ static void gui_stop(Service service) {
gui_lock();
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
furi_thread_join(gui->thread);
ThreadId thread_id = tt_thread_get_id(gui->thread);
tt_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
tt_thread_join(gui->thread);
gui_unlock();

View File

@ -20,13 +20,13 @@ static lv_obj_t* create_app_views(lv_obj_t* parent, App app) {
tt_lv_obj_set_style_bg_blacken(vertical_container);
// TODO: Move statusbar into separate ViewPort
AppFlags flags = app_get_flags(app);
AppFlags flags = tt_app_get_flags(app);
if (flags.show_statusbar) {
tt_lv_statusbar_create(vertical_container);
}
if (flags.show_toolbar) {
const AppManifest* manifest = app_get_manifest(app);
const AppManifest* manifest = tt_app_get_manifest(app);
if (manifest != NULL) {
// TODO: Keep toolbar on app level so app can update it (app_set_toolbar() etc?)
Toolbar toolbar = {
@ -51,22 +51,22 @@ static lv_obj_t* create_app_views(lv_obj_t* parent, App app) {
}
void gui_redraw(Gui* gui) {
furi_assert(gui);
tt_assert(gui);
// Lock GUI and LVGL
gui_lock();
furi_check(lvgl_port_lock(100));
tt_check(lvgl_port_lock(100));
lv_obj_clean(gui->lvgl_parent);
if (gui->app_view_port != NULL) {
ViewPort* view_port = gui->app_view_port;
furi_assert(view_port);
tt_assert(view_port);
App app = gui->app_view_port->app;
lv_obj_t* container = create_app_views(gui->lvgl_parent, app);
view_port_show(view_port, container);
} else {
FURI_LOG_W(TAG, "nothing to draw");
TT_LOG_W(TAG, "nothing to draw");
}
// Unlock GUI and LVGL

View File

@ -16,8 +16,8 @@
/** Gui structure */
struct Gui {
// Thread and lock
FuriThread* thread;
FuriMutex* mutex;
Thread* thread;
Mutex* mutex;
// Layers and Canvas
lv_obj_t* lvgl_parent;

View File

@ -1,81 +0,0 @@
#include "gui_i.h"
/*
void gui_input_events_callback(const void* value, void* ctx) {
furi_assert(value);
furi_assert(ctx);
Gui* gui = ctx;
furi_message_queue_put(gui->input_queue, value, FuriWaitForever);
furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_INPUT);
}
static void gui_input(Gui* gui, InputEvent* input_event) {
furi_assert(gui);
furi_assert(input_event);
// Check input complementarity
uint8_t key_bit = (1 << input_event->key);
if(input_event->type == InputTypeRelease) {
gui->ongoing_input &= ~key_bit;
} else if(input_event->type == InputTypePress) {
gui->ongoing_input |= key_bit;
} else if(!(gui->ongoing_input & key_bit)) {
FURI_LOG_D(
TAG,
"non-complementary input, discarding key: %s type: %s, sequence: %p",
input_get_key_name(input_event->key),
input_get_type_name(input_event->type),
(void*)input_event->sequence);
return;
}
gui_lock(gui);
do {
if(gui->direct_draw && !gui->ongoing_input_view_port) {
break;
}
ViewPort* view_port = NULL;
if(gui->lockdown) {
view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
} else {
view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
}
if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) {
gui->ongoing_input_view_port = view_port;
}
if(view_port && view_port == gui->ongoing_input_view_port) {
view_port_input(view_port, input_event);
} else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) {
FURI_LOG_D(
TAG,
"ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
gui->ongoing_input_view_port,
view_port,
input_get_key_name(input_event->key),
input_get_type_name(input_event->type),
(void*)input_event->sequence);
view_port_input(gui->ongoing_input_view_port, input_event);
} else {
FURI_LOG_D(
TAG,
"ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p",
gui->ongoing_input_view_port,
view_port,
input_get_key_name(input_event->key),
input_get_type_name(input_event->type),
(void*)input_event->sequence);
}
} while(false);
gui_unlock(gui);
}
*/

View File

@ -19,13 +19,13 @@ ViewPort* view_port_alloc(
}
void view_port_free(ViewPort* view_port) {
furi_assert(view_port);
tt_assert(view_port);
free(view_port);
}
void view_port_show(ViewPort* view_port, lv_obj_t* parent) {
furi_assert(view_port);
furi_assert(parent);
tt_assert(view_port);
tt_assert(parent);
if (view_port->on_show) {
tt_lv_obj_set_style_no_padding(parent);
view_port->on_show(view_port->app, parent);
@ -33,7 +33,7 @@ void view_port_show(ViewPort* view_port, lv_obj_t* parent) {
}
void view_port_hide(ViewPort* view_port) {
furi_assert(view_port);
tt_assert(view_port);
if (view_port->on_hide) {
view_port->on_hide(view_port->app);
}

View File

@ -6,7 +6,6 @@ extern "C" {
#include "app.h"
#include "lvgl.h"
#include "context.h"
/** ViewPort Draw callback
* @warning called from GUI thread

View File

@ -17,11 +17,11 @@ static int32_t loader_main(void* p);
static Loader* loader_singleton = NULL;
static Loader* loader_alloc() {
furi_check(loader_singleton == NULL);
tt_check(loader_singleton == NULL);
loader_singleton = malloc(sizeof(Loader));
loader_singleton->pubsub = furi_pubsub_alloc();
loader_singleton->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
loader_singleton->thread = furi_thread_alloc_ex(
loader_singleton->pubsub = tt_pubsub_alloc();
loader_singleton->queue = tt_message_queue_alloc(1, sizeof(LoaderMessage));
loader_singleton->thread = tt_thread_alloc_ex(
"loader",
4096, // Last known minimum was 2400 for starting Hello World app
&loader_main,
@ -34,24 +34,24 @@ static Loader* loader_alloc() {
}
static void loader_free() {
furi_check(loader_singleton != NULL);
furi_thread_free(loader_singleton->thread);
furi_pubsub_free(loader_singleton->pubsub);
furi_message_queue_free(loader_singleton->queue);
furi_mutex_free(loader_singleton->mutex);
tt_check(loader_singleton != NULL);
tt_thread_free(loader_singleton->thread);
tt_pubsub_free(loader_singleton->pubsub);
tt_message_queue_free(loader_singleton->queue);
tt_mutex_free(loader_singleton->mutex);
free(loader_singleton);
}
void loader_lock() {
furi_assert(loader_singleton);
furi_assert(loader_singleton->mutex);
furi_check(xSemaphoreTakeRecursive(loader_singleton->mutex, portMAX_DELAY) == pdPASS);
tt_assert(loader_singleton);
tt_assert(loader_singleton->mutex);
tt_check(xSemaphoreTakeRecursive(loader_singleton->mutex, portMAX_DELAY) == pdPASS);
}
void loader_unlock() {
furi_assert(loader_singleton);
furi_assert(loader_singleton->mutex);
furi_check(xSemaphoreGiveRecursive(loader_singleton->mutex) == pdPASS);
tt_assert(loader_singleton);
tt_assert(loader_singleton->mutex);
tt_check(xSemaphoreGiveRecursive(loader_singleton->mutex) == pdPASS);
}
LoaderStatus loader_start_app(const char* id, bool blocking, Bundle* _Nullable bundle) {
@ -64,13 +64,13 @@ LoaderStatus loader_start_app(const char* id, bool blocking, Bundle* _Nullable b
.start.id = id,
.start.bundle = bundle,
.status_value = &result,
.api_lock = blocking ? api_lock_alloc_locked() : NULL
.api_lock = blocking ? tt_api_lock_alloc_locked() : NULL
};
furi_message_queue_put(loader_singleton->queue, &message, FuriWaitForever);
tt_message_queue_put(loader_singleton->queue, &message, TtWaitForever);
if (blocking) {
api_lock_wait_unlock_and_free(message.api_lock);
tt_api_lock_wait_unlock_and_free(message.api_lock);
}
return result.value;
@ -78,7 +78,7 @@ LoaderStatus loader_start_app(const char* id, bool blocking, Bundle* _Nullable b
void loader_stop_app() {
LoaderMessage message = {.type = LoaderMessageTypeAppStop};
furi_message_queue_put(loader_singleton->queue, &message, FuriWaitForever);
tt_message_queue_put(loader_singleton->queue, &message, TtWaitForever);
}
App _Nullable loader_get_current_app() {
@ -90,8 +90,8 @@ App _Nullable loader_get_current_app() {
return app;
}
FuriPubSub* loader_get_pubsub() {
furi_assert(loader_singleton);
PubSub* loader_get_pubsub() {
tt_assert(loader_singleton);
// it's safe to return pubsub without locking
// because it's never freed and loader is never exited
// also the loader instance cannot be obtained until the pubsub is created
@ -116,10 +116,10 @@ static const char* app_state_to_string(AppState state) {
}
static void app_transition_to_state(App app, AppState state) {
const AppManifest* manifest = app_get_manifest(app);
const AppState old_state = app_get_state(app);
const AppManifest* manifest = tt_app_get_manifest(app);
const AppState old_state = tt_app_get_state(app);
FURI_LOG_I(
TT_LOG_I(
TAG,
"app \"%s\" state: %s -> %s",
manifest->id,
@ -129,13 +129,13 @@ static void app_transition_to_state(App app, AppState state) {
switch (state) {
case APP_STATE_INITIAL:
app_set_state(app, APP_STATE_INITIAL);
tt_app_set_state(app, APP_STATE_INITIAL);
break;
case APP_STATE_STARTED:
if (manifest->on_start != NULL) {
manifest->on_start(app);
}
app_set_state(app, APP_STATE_STARTED);
tt_app_set_state(app, APP_STATE_STARTED);
break;
case APP_STATE_SHOWING:
gui_show_app(
@ -143,18 +143,18 @@ static void app_transition_to_state(App app, AppState state) {
manifest->on_show,
manifest->on_hide
);
app_set_state(app, APP_STATE_SHOWING);
tt_app_set_state(app, APP_STATE_SHOWING);
break;
case APP_STATE_HIDING:
gui_hide_app();
app_set_state(app, APP_STATE_HIDING);
tt_app_set_state(app, APP_STATE_HIDING);
break;
case APP_STATE_STOPPED:
if (manifest->on_stop) {
manifest->on_stop(app);
}
app_set_data(app, NULL);
app_set_state(app, APP_STATE_STOPPED);
tt_app_set_data(app, NULL);
tt_app_set_state(app, APP_STATE_STOPPED);
break;
}
}
@ -163,20 +163,20 @@ LoaderStatus loader_do_start_app_with_manifest(
const AppManifest* _Nonnull manifest,
Bundle* _Nullable bundle
) {
FURI_LOG_I(TAG, "start with manifest %s", manifest->id);
TT_LOG_I(TAG, "start with manifest %s", manifest->id);
loader_lock();
if (loader_singleton->app_stack_index >= (APP_STACK_SIZE - 1)) {
FURI_LOG_E(TAG, "failed to start app: stack limit of %d reached", APP_STACK_SIZE);
TT_LOG_E(TAG, "failed to start app: stack limit of %d reached", APP_STACK_SIZE);
return LoaderStatusErrorInternal;
}
int8_t previous_index = loader_singleton->app_stack_index;
loader_singleton->app_stack_index++;
App app = app_alloc(manifest, bundle);
furi_assert(loader_singleton->app_stack[loader_singleton->app_stack_index] == NULL);
App app = tt_app_alloc(manifest, bundle);
tt_assert(loader_singleton->app_stack[loader_singleton->app_stack_index] == NULL);
loader_singleton->app_stack[loader_singleton->app_stack_index] = app;
app_transition_to_state(app, APP_STATE_INITIAL);
app_transition_to_state(app, APP_STATE_STARTED);
@ -192,7 +192,7 @@ LoaderStatus loader_do_start_app_with_manifest(
loader_unlock();
LoaderEvent event = {.type = LoaderEventTypeApplicationStarted};
furi_pubsub_publish(loader_singleton->pubsub, &event);
tt_pubsub_publish(loader_singleton->pubsub, &event);
return LoaderStatusOk;
}
@ -201,9 +201,9 @@ static LoaderStatus loader_do_start_by_id(
const char* id,
Bundle* _Nullable bundle
) {
FURI_LOG_I(TAG, "Start by id %s", id);
TT_LOG_I(TAG, "Start by id %s", id);
const AppManifest* manifest = app_manifest_registry_find_by_id(id);
const AppManifest* manifest = tt_app_manifest_registry_find_by_id(id);
if (manifest == NULL) {
return LoaderStatusErrorUnknownApp;
} else {
@ -219,13 +219,13 @@ static void loader_do_stop_app() {
if (current_app_index == -1) {
loader_unlock();
FURI_LOG_E(TAG, "Stop app: no app running");
TT_LOG_E(TAG, "Stop app: no app running");
return;
}
if (current_app_index == 0) {
loader_unlock();
FURI_LOG_E(TAG, "Stop app: can't stop root app");
TT_LOG_E(TAG, "Stop app: can't stop root app");
return;
}
@ -234,21 +234,21 @@ static void loader_do_stop_app() {
app_transition_to_state(app_to_stop, APP_STATE_HIDING);
app_transition_to_state(app_to_stop, APP_STATE_STOPPED);
app_free(app_to_stop);
tt_app_free(app_to_stop);
loader_singleton->app_stack[current_app_index] = NULL;
loader_singleton->app_stack_index--;
FURI_LOG_I(TAG, "Free heap: %zu", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
TT_LOG_I(TAG, "Free heap: %zu", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
// Resume previous app
furi_assert(loader_singleton->app_stack[loader_singleton->app_stack_index] != NULL);
tt_assert(loader_singleton->app_stack[loader_singleton->app_stack_index] != NULL);
App app_to_resume = loader_singleton->app_stack[loader_singleton->app_stack_index];
app_transition_to_state(app_to_resume, APP_STATE_SHOWING);
loader_unlock();
LoaderEvent event = {.type = LoaderEventTypeApplicationStopped};
furi_pubsub_publish(loader_singleton->pubsub, &event);
tt_pubsub_publish(loader_singleton->pubsub, &event);
}
@ -258,9 +258,9 @@ static int32_t loader_main(void* p) {
LoaderMessage message;
bool exit_requested = false;
while (!exit_requested) {
furi_check(loader_singleton != NULL);
if (furi_message_queue_get(loader_singleton->queue, &message, FuriWaitForever) == FuriStatusOk) {
FURI_LOG_I(TAG, "Processing message of type %d", message.type);
tt_check(loader_singleton != NULL);
if (tt_message_queue_get(loader_singleton->queue, &message, TtWaitForever) == TtStatusOk) {
TT_LOG_I(TAG, "Processing message of type %d", message.type);
switch (message.type) {
case LoaderMessageTypeAppStart:
// TODO: add bundle
@ -269,7 +269,7 @@ static int32_t loader_main(void* p) {
message.start.bundle
);
if (message.api_lock) {
api_lock_unlock(message.api_lock);
tt_api_lock_unlock(message.api_lock);
}
break;
case LoaderMessageTypeAppStop:
@ -289,24 +289,24 @@ static int32_t loader_main(void* p) {
static void loader_start(Service service) {
UNUSED(service);
furi_check(loader_singleton == NULL);
tt_check(loader_singleton == NULL);
loader_singleton = loader_alloc();
furi_thread_set_priority(loader_singleton->thread, FuriThreadPriorityNormal);
furi_thread_start(loader_singleton->thread);
tt_thread_set_priority(loader_singleton->thread, ThreadPriorityNormal);
tt_thread_start(loader_singleton->thread);
}
static void loader_stop(Service service) {
UNUSED(service);
furi_check(loader_singleton != NULL);
tt_check(loader_singleton != NULL);
// Send stop signal to thread and wait for thread to finish
LoaderMessage message = {
.api_lock = NULL,
.type = LoaderMessageTypeServiceStop
};
furi_message_queue_put(loader_singleton->queue, &message, FuriWaitForever);
furi_thread_join(loader_singleton->thread);
tt_message_queue_put(loader_singleton->queue, &message, TtWaitForever);
tt_thread_join(loader_singleton->thread);
loader_free();
loader_singleton = NULL;

View File

@ -2,10 +2,10 @@
#include "app_manifest.h"
#include "bundle.h"
#include "furi_core.h"
#include "furi_string.h"
#include "pubsub.h"
#include "service_manifest.h"
#include "tactility_core.h"
#include "tt_string.h"
#ifdef __cplusplus
extern "C" {
@ -44,9 +44,9 @@ App _Nullable loader_get_current_app();
/**
* @brief Get loader pubsub
* @return FuriPubSub*
* @return PubSub*
*/
FuriPubSub* loader_get_pubsub();
PubSub* loader_get_pubsub();
#ifdef __cplusplus
}

Some files were not shown because too many files have changed in this diff Show More