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) cmake_minimum_required(VERSION 3.16)
add_definitions(-DFURI_DEBUG) add_definitions(-DTT_DEBUG)
set(COMPONENTS main) set(COMPONENTS main)
set(EXTRA_COMPONENT_DIRS "boards" "components") 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: 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) - 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 - [Tactility](./components/tactility): The main platform with default services and apps.
- [Furi](./components/furi/): the core platform code, based on Flipper Zero firmware - [Tactility Core](./components/tactility-core): The core platform code.
## Building Firmware ## Building Firmware

View File

@ -22,5 +22,5 @@ void lilygo_tdeck_bootstrap() {
tdeck_power_on(); tdeck_power_on();
// Give keyboard's ESP time to boot // Give keyboard's ESP time to boot
// It uses I2C and seems to interfere with the touch driver // 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> #include <stdio.h>
static AppFlags app_get_flags_default(AppType type); static AppFlags tt_app_get_flags_default(AppType type);
// region Alloc/free // 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)); AppData* data = malloc(sizeof(AppData));
*data = (AppData) { *data = (AppData) {
.mutex = furi_mutex_alloc(FuriMutexTypeRecursive), .mutex = tt_mutex_alloc(MutexTypeRecursive),
.state = APP_STATE_INITIAL, .state = APP_STATE_INITIAL,
.flags = app_get_flags_default(manifest->type), .flags = tt_app_get_flags_default(manifest->type),
.manifest = manifest, .manifest = manifest,
.parameters = parameters, .parameters = parameters,
.data = NULL .data = NULL
@ -19,12 +19,12 @@ App app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters) {
return (App*)data; return (App*)data;
} }
void app_free(App app) { void tt_app_free(App app) {
AppData* data = (AppData*)app; AppData* data = (AppData*)app;
if (data->parameters) { if (data->parameters) {
bundle_free(data->parameters); tt_bundle_free(data->parameters);
} }
furi_mutex_free(data->mutex); tt_mutex_free(data->mutex);
free(data); free(data);
} }
@ -32,15 +32,15 @@ void app_free(App app) {
// region Internal // region Internal
static void app_lock(AppData* data) { static void tt_app_lock(AppData* data) {
furi_mutex_acquire(data->mutex, FuriMutexTypeRecursive); tt_mutex_acquire(data->mutex, MutexTypeRecursive);
} }
static void app_unlock(AppData* data) { static void tt_app_unlock(AppData* data) {
furi_mutex_release(data->mutex); 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 = { static const AppFlags DEFAULT_DESKTOP_FLAGS = {
.show_toolbar = false, .show_toolbar = false,
.show_statusbar = true .show_statusbar = true
@ -60,55 +60,55 @@ static AppFlags app_get_flags_default(AppType type) {
// region Public getters & setters // 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; AppData* data = (AppData*)app;
app_lock(data); tt_app_lock(data);
data->state = state; 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; AppData* data = (AppData*)app;
app_lock(data); tt_app_lock(data);
AppState state = data->state; AppState state = data->state;
app_unlock(data); tt_app_unlock(data);
return state; return state;
} }
const AppManifest* app_get_manifest(App app) { const AppManifest* tt_app_get_manifest(App app) {
AppData* data = (AppData*)app; AppData* data = (AppData*)app;
// No need to lock const data; // No need to lock const data;
return data->manifest; return data->manifest;
} }
AppFlags app_get_flags(App app) { AppFlags tt_app_get_flags(App app) {
AppData* data = (AppData*)app; AppData* data = (AppData*)app;
app_lock(data); tt_app_lock(data);
AppFlags flags = data->flags; AppFlags flags = data->flags;
app_unlock(data); tt_app_unlock(data);
return flags; return flags;
} }
void app_set_flags(App app, AppFlags flags) { void tt_app_set_flags(App app, AppFlags flags) {
AppData* data = (AppData*)app; AppData* data = (AppData*)app;
app_lock(data); tt_app_lock(data);
data->flags = flags; 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; AppData* data = (AppData*)app;
app_lock(data); tt_app_lock(data);
void* value = data->data; void* value = data->data;
app_unlock(data); tt_app_unlock(data);
return value; return value;
} }
void app_set_data(App app, void* value) { void tt_app_set_data(App app, void* value) {
AppData* data = (AppData*)app; AppData* data = (AppData*)app;
app_lock(data); tt_app_lock(data);
data->data = value; data->data = value;
app_unlock(data); tt_app_unlock(data);
} }
/** TODO: Make this thread-safe. /** TODO: Make this thread-safe.
@ -117,11 +117,11 @@ void app_set_data(App app, void* value) {
* Consider creating MutableBundle vs Bundle. * Consider creating MutableBundle vs Bundle.
* Consider not exposing bundle, but expose `app_get_bundle_int(key)` methods with locking in it. * 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; AppData* data = (AppData*)app;
app_lock(data); tt_app_lock(data);
Bundle* bundle = data->parameters; Bundle* bundle = data->parameters;
app_unlock(data); tt_app_unlock(data);
return bundle; return bundle;
} }

View File

@ -30,21 +30,21 @@ typedef void* App;
* @param parameters optional bundle. memory ownership is transferred to App * @param parameters optional bundle. memory ownership is transferred to App
* @return * @return
*/ */
App app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters); App tt_app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters);
void app_free(App app); void tt_app_free(App app);
void app_set_state(App app, AppState state); void tt_app_set_state(App app, AppState state);
AppState app_get_state(App app); 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); AppFlags tt_app_get_flags(App app);
void app_set_flags(App app, AppFlags flags); void tt_app_set_flags(App app, AppFlags flags);
void* _Nullable app_get_data(App app); void* _Nullable tt_app_get_data(App app);
void app_set_data(App app, void* data); 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 #ifdef __cplusplus
} }

View File

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

View File

@ -1,9 +1,9 @@
#include "app_manifest_registry.h" #include "app_manifest_registry.h"
#include "furi_core.h"
#include "m-dict.h" #include "m-dict.h"
#include "m_cstr_dup.h" #include "m_cstr_dup.h"
#include "mutex.h" #include "mutex.h"
#include "tactility_core.h"
#define TAG "app_registry" #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; AppManifestDict_t app_manifest_dict;
FuriMutex* mutex = NULL; Mutex* mutex = NULL;
void app_manifest_registry_init() { void tt_app_manifest_registry_init() {
furi_assert(mutex == NULL); tt_assert(mutex == NULL);
mutex = furi_mutex_alloc(FuriMutexTypeNormal); mutex = tt_mutex_alloc(MutexTypeNormal);
AppManifestDict_init(app_manifest_dict); AppManifestDict_init(app_manifest_dict);
} }
void app_registry_lock() { void app_registry_lock() {
furi_assert(mutex != NULL); tt_assert(mutex != NULL);
furi_mutex_acquire(mutex, FuriWaitForever); tt_mutex_acquire(mutex, TtWaitForever);
} }
void app_registry_unlock() { void app_registry_unlock() {
furi_assert(mutex != NULL); tt_assert(mutex != NULL);
furi_mutex_release(mutex); tt_mutex_release(mutex);
} }
void app_manifest_registry_add(const AppManifest _Nonnull* manifest) { void tt_app_manifest_registry_add(const AppManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "adding %s", manifest->id); TT_LOG_I(TAG, "adding %s", manifest->id);
app_registry_lock(); app_registry_lock();
AppManifestDict_set_at(app_manifest_dict, manifest->id, manifest); AppManifestDict_set_at(app_manifest_dict, manifest->id, manifest);
app_registry_unlock(); app_registry_unlock();
} }
void app_manifest_registry_remove(const AppManifest _Nonnull* manifest) { void tt_app_manifest_registry_remove(const AppManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "removing %s", manifest->id); TT_LOG_I(TAG, "removing %s", manifest->id);
app_registry_lock(); app_registry_lock();
AppManifestDict_erase(app_manifest_dict, manifest->id); AppManifestDict_erase(app_manifest_dict, manifest->id);
app_registry_unlock(); 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(); app_registry_lock();
const AppManifest _Nullable** manifest = AppManifestDict_get(app_manifest_dict, id); const AppManifest _Nullable** manifest = AppManifestDict_get(app_manifest_dict, id);
app_registry_unlock(); app_registry_unlock();
return (manifest != NULL) ? *manifest : NULL; 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, { APP_REGISTRY_FOR_EACH(manifest, {
if (manifest->type == type) { if (manifest->type == type) {
callback(manifest, context); 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, { APP_REGISTRY_FOR_EACH(manifest, {
callback(manifest, context); 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; BundleDict_t dict;
} BundleData; } BundleData;
Bundle bundle_alloc() { Bundle tt_bundle_alloc() {
BundleData* bundle = malloc(sizeof(BundleData)); BundleData* bundle = malloc(sizeof(BundleData));
BundleDict_init(bundle->dict); BundleDict_init(bundle->dict);
return bundle; return bundle;
} }
Bundle bundle_alloc_copy(Bundle source) { Bundle tt_bundle_alloc_copy(Bundle source) {
BundleData* source_data = (BundleData*)source; BundleData* source_data = (BundleData*)source;
BundleData* target_data = bundle_alloc(); BundleData* target_data = tt_bundle_alloc();
BundleDict_it_t it; BundleDict_it_t it;
for (BundleDict_it(it, source_data->dict); !BundleDict_end_p(it); BundleDict_next(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; return target_data;
} }
void bundle_free(Bundle bundle) { void tt_bundle_free(Bundle bundle) {
BundleData* data = (BundleData*)bundle; BundleData* data = (BundleData*)bundle;
BundleDict_it_t it; BundleDict_it_t it;
@ -105,28 +105,28 @@ void bundle_free(Bundle bundle) {
free(data); 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; BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key); BundleEntry** entry = BundleDict_get(data->dict, key);
furi_check(entry != NULL); tt_check(entry != NULL);
return (*entry)->bool_value; 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; BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key); BundleEntry** entry = BundleDict_get(data->dict, key);
furi_check(entry != NULL); tt_check(entry != NULL);
return (*entry)->int_value; 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; BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key); BundleEntry** entry = BundleDict_get(data->dict, key);
furi_check(entry != NULL); tt_check(entry != NULL);
return (*entry)->string_ptr; 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; BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key); BundleEntry** entry = BundleDict_get(data->dict, key);
if (entry != NULL) { 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; BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key); BundleEntry** entry = BundleDict_get(data->dict, key);
if (entry != NULL) { 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; BundleData* data = (BundleData*)bundle;
BundleEntry** entry = BundleDict_get(data->dict, key); BundleEntry** entry = BundleDict_get(data->dict, key);
if (entry != NULL) { 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; BundleData* data = (BundleData*)bundle;
BundleEntry** entry_handle = BundleDict_get(data->dict, key); BundleEntry** entry_handle = BundleDict_get(data->dict, key);
if (entry_handle != NULL) { if (entry_handle != NULL) {
BundleEntry* entry = *entry_handle; BundleEntry* entry = *entry_handle;
furi_assert(entry->type == BUNDLE_ENTRY_TYPE_BOOL); tt_assert(entry->type == BUNDLE_ENTRY_TYPE_BOOL);
entry->bool_value = value; entry->bool_value = value;
} else { } else {
BundleEntry* entry = bundle_entry_alloc_bool(value); 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; BundleData* data = (BundleData*)bundle;
BundleEntry** entry_handle = BundleDict_get(data->dict, key); BundleEntry** entry_handle = BundleDict_get(data->dict, key);
if (entry_handle != NULL) { if (entry_handle != NULL) {
BundleEntry* entry = *entry_handle; BundleEntry* entry = *entry_handle;
furi_assert(entry->type == BUNDLE_ENTRY_TYPE_INT); tt_assert(entry->type == BUNDLE_ENTRY_TYPE_INT);
entry->int_value = value; entry->int_value = value;
} else { } else {
BundleEntry* entry = bundle_entry_alloc_int(value); 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; BundleData* data = (BundleData*)bundle;
BundleEntry** entry_handle = BundleDict_get(data->dict, key); BundleEntry** entry_handle = BundleDict_get(data->dict, key);
if (entry_handle != NULL) { if (entry_handle != NULL) {
BundleEntry* entry = *entry_handle; 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) { if (entry->string_ptr != NULL) {
free(entry->string_ptr); 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 * @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, * 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. * 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 #ifdef __cplusplus
extern "C" { extern "C" {
#define FURI_NORETURN [[noreturn]] #define TT_NORETURN [[noreturn]]
#else #else
#include <stdnoreturn.h> #include <stdnoreturn.h>
#define FURI_NORETURN noreturn #define TT_NORETURN noreturn
#endif #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 */ /** Crash system */
FURI_NORETURN void __furi_crash_implementation(); TT_NORETURN void tt_crash_implementation();
/** Halt system */
FURI_NORETURN void __furi_halt_implementation();
/** Crash system with message. */ /** Crash system with message. */
#define __furi_crash(message) \ #define __tt_crash(message) \
do { \ do { \
ESP_LOGE("crash", "%s\n\tat %s:%d", ((message) ? (message) : ""), __FILE__, __LINE__); \ ESP_LOGE("crash", "%s\n\tat %s:%d", ((message) ? (message) : ""), __FILE__, __LINE__); \
__furi_crash_implementation(); \ tt_crash_implementation(); \
} while (0) } while (0)
/** Crash system /** Crash system
* *
* @param optional message (const char*) * @param optional message (const char*)
*/ */
#define furi_crash(...) M_APPLY(__furi_crash, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) #define tt_crash(...) M_APPLY(__tt_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)
/** Halt system /** Halt system
* *
* @param optional message (const char*) * @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 */ /** Check condition and crash if check failed */
#define __furi_check(__e, __m) \ #define __tt_check(__e, __m) \
do { \ do { \
if (!(__e)) { \ if (!(__e)) { \
ESP_LOGE("check", "%s", #__e); \ ESP_LOGE("check", "%s", #__e); \
__furi_crash(#__m); \ if (__m) { \
__tt_crash(#__m); \
} else { \
__tt_crash(""); \
} \
} \ } \
} while (0) } while (0)
@ -74,20 +64,24 @@ FURI_NORETURN void __furi_halt_implementation();
* @param condition to check * @param condition to check
* @param optional message (const char*) * @param optional message (const char*)
*/ */
#define furi_check(...) \ #define tt_check(...) \
M_APPLY(__furi_check, M_DEFAULT_ARGS(2, (__FURI_CHECK_MESSAGE_FLAG), __VA_ARGS__)) M_APPLY(__tt_check, M_DEFAULT_ARGS(2, NULL, __VA_ARGS__))
/** Only in debug build: Assert condition and crash if assert failed */ /** Only in debug build: Assert condition and crash if assert failed */
#ifdef FURI_DEBUG #ifdef TT_DEBUG
#define __furi_assert(__e, __m) \ #define __tt_assert(__e, __m) \
do { \ do { \
if (!(__e)) { \ if (!(__e)) { \
ESP_LOGE("assert", "%s", #__e); \ ESP_LOGE("assert", "%s", #__e); \
__furi_crash(#__m); \ if (__m) { \
__tt_crash(#__m); \
} else { \
__tt_crash(""); \
} \
} \ } \
} while (0) } while (0)
#else #else
#define __furi_assert(__e, __m) \ #define __tt_assert(__e, __m) \
do { \ do { \
((void)(__e)); \ ((void)(__e)); \
((void)(__m)); \ ((void)(__m)); \
@ -101,8 +95,8 @@ FURI_NORETURN void __furi_halt_implementation();
* @param condition to check * @param condition to check
* @param optional message (const char*) * @param optional message (const char*)
*/ */
#define furi_assert(...) \ #define tt_assert(...) \
M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__)) M_APPLY(__tt_assert, M_DEFAULT_ARGS(2, NULL, __VA_ARGS__))
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

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

View File

@ -2,9 +2,8 @@
#include <stdlib.h> #include <stdlib.h>
#include "furi_core.h" #include "tactility_core.h"
#include "furi_string.h"
#include "event_flag.h" #include "event_flag.h"
#include "kernel.h" #include "kernel.h"
#include "message_queue.h" #include "message_queue.h"
@ -15,12 +14,13 @@
#include "string.h" #include "string.h"
#include "thread.h" #include "thread.h"
#include "timer.h" #include "timer.h"
#include "tt_string.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void furi_init(); void tt_core_init();
#ifdef __cplusplus #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" { extern "C" {
#endif #endif
#define FURI_RETURNS_NONNULL __attribute__((returns_nonnull))
#ifndef MAX #ifndef MAX
#define MAX(a, b) \ #define MAX(a, b) \
({ \ ({ \
@ -45,8 +43,8 @@ extern "C" {
#define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) #define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
#endif #endif
#ifndef FURI_SWAP #ifndef TT_SWAP
#define FURI_SWAP(x, y) \ #define TT_SWAP(x, y) \
do { \ do { \
typeof(x) SWAP = x; \ typeof(x) SWAP = x; \
x = y; \ x = y; \
@ -89,27 +87,27 @@ extern "C" {
(((x) & 0xFF000000) >> 24)) (((x) & 0xFF000000) >> 24))
#endif #endif
#ifndef FURI_BIT #ifndef TT_BIT
#define FURI_BIT(x, n) (((x) >> (n)) & 1) #define TT_BIT(x, n) (((x) >> (n)) & 1)
#endif #endif
#ifndef FURI_BIT_SET #ifndef TT_BIT_SET
#define FURI_BIT_SET(x, n) \ #define TT_BIT_SET(x, n) \
({ \ ({ \
__typeof__(x) _x = (1); \ __typeof__(x) _x = (1); \
(x) |= (_x << (n)); \ (x) |= (_x << (n)); \
}) })
#endif #endif
#ifndef FURI_BIT_CLEAR #ifndef TT_BIT_CLEAR
#define FURI_BIT_CLEAR(x, n) \ #define TT_BIT_CLEAR(x, n) \
({ \ ({ \
__typeof__(x) _x = (1); \ __typeof__(x) _x = (1); \
(x) &= ~(_x << (n)); \ (x) &= ~(_x << (n)); \
}) })
#endif #endif
#define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") #define TT_SW_MEMBARRIER() asm volatile("" : : : "memory")
#ifdef __cplusplus #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 "critical.h"
#include "furi_core_defines.h" #include "core_defines.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
static portMUX_TYPE critical_mutex; static portMUX_TYPE critical_mutex;
__FuriCriticalInfo __furi_critical_enter(void) { __TtCriticalInfo __tt_critical_enter(void) {
__FuriCriticalInfo info; __TtCriticalInfo info;
info.isrm = 0; info.isrm = 0;
info.from_isr = FURI_IS_ISR(); info.from_isr = TT_IS_ISR();
info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING);
if (info.from_isr) { if (info.from_isr) {
@ -24,7 +24,7 @@ __FuriCriticalInfo __furi_critical_enter(void) {
return info; return info;
} }
void __furi_critical_exit(__FuriCriticalInfo info) { void __tt_critical_exit(__TtCriticalInfo info) {
if (info.from_isr) { if (info.from_isr) {
taskEXIT_CRITICAL_FROM_ISR(info.isrm); taskEXIT_CRITICAL_FROM_ISR(info.isrm);
} else if (info.kernel_running) { } 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 "event_flag.h"
#include "check.h" #include "check.h"
#include "furi_core_defines.h" #include "core_defines.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h" #include "freertos/event_groups.h"
#define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U #define TT_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_INVALID_BITS (~((1UL << TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U))
FuriEventFlag* furi_event_flag_alloc() { EventFlag* tt_event_flag_alloc() {
furi_assert(!FURI_IS_IRQ_MODE()); tt_assert(!TT_IS_IRQ_MODE());
EventGroupHandle_t handle = xEventGroupCreate(); EventGroupHandle_t handle = xEventGroupCreate();
furi_check(handle); tt_check(handle);
return ((FuriEventFlag*)handle); return ((EventFlag*)handle);
} }
void furi_event_flag_free(FuriEventFlag* instance) { void tt_event_flag_free(EventFlag* instance) {
furi_assert(!FURI_IS_IRQ_MODE()); tt_assert(!TT_IS_IRQ_MODE());
vEventGroupDelete((EventGroupHandle_t)instance); vEventGroupDelete((EventGroupHandle_t)instance);
} }
uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { uint32_t tt_event_flag_set(EventFlag* instance, uint32_t flags) {
furi_assert(instance); tt_assert(instance);
furi_assert((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U); tt_assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
uint32_t rflags; uint32_t rflags;
BaseType_t yield; BaseType_t yield;
if (FURI_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
yield = pdFALSE; yield = pdFALSE;
if (xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { if (xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {
rflags = (uint32_t)FuriFlagErrorResource; rflags = (uint32_t)TtFlagErrorResource;
} else { } else {
rflags = flags; rflags = flags;
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
@ -46,18 +46,18 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {
return (rflags); return (rflags);
} }
uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) { uint32_t tt_event_flag_clear(EventFlag* instance, uint32_t flags) {
furi_assert(instance); tt_assert(instance);
furi_assert((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U); tt_assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
uint32_t rflags; uint32_t rflags;
if (FURI_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
rflags = xEventGroupGetBitsFromISR(hEventGroup); rflags = xEventGroupGetBitsFromISR(hEventGroup);
if (xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) { if (xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) {
rflags = (uint32_t)FuriStatusErrorResource; rflags = (uint32_t)TtStatusErrorResource;
} else { } else {
/* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */ /* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */
/* Yield is required here otherwise clear operation might not execute in the right order. */ /* 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); return (rflags);
} }
uint32_t furi_event_flag_get(FuriEventFlag* instance) { uint32_t tt_event_flag_get(EventFlag* instance) {
furi_assert(instance); tt_assert(instance);
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
uint32_t rflags; uint32_t rflags;
if (FURI_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
rflags = xEventGroupGetBitsFromISR(hEventGroup); rflags = xEventGroupGetBitsFromISR(hEventGroup);
} else { } else {
rflags = xEventGroupGetBits(hEventGroup); rflags = xEventGroupGetBits(hEventGroup);
@ -88,28 +88,28 @@ uint32_t furi_event_flag_get(FuriEventFlag* instance) {
return (rflags); return (rflags);
} }
uint32_t furi_event_flag_wait( uint32_t tt_event_flag_wait(
FuriEventFlag* instance, EventFlag* instance,
uint32_t flags, uint32_t flags,
uint32_t options, uint32_t options,
uint32_t timeout uint32_t timeout
) { ) {
furi_assert(!FURI_IS_IRQ_MODE()); tt_assert(!TT_IS_IRQ_MODE());
furi_assert(instance); tt_assert(instance);
furi_assert((flags & FURI_EVENT_FLAG_INVALID_BITS) == 0U); tt_assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
BaseType_t wait_all; BaseType_t wait_all;
BaseType_t exit_clr; BaseType_t exit_clr;
uint32_t rflags; uint32_t rflags;
if (options & FuriFlagWaitAll) { if (options & TtFlagWaitAll) {
wait_all = pdTRUE; wait_all = pdTRUE;
} else { } else {
wait_all = pdFAIL; wait_all = pdFAIL;
} }
if (options & FuriFlagNoClear) { if (options & TtFlagNoClear) {
exit_clr = pdFAIL; exit_clr = pdFAIL;
} else { } else {
exit_clr = pdTRUE; exit_clr = pdTRUE;
@ -123,20 +123,20 @@ uint32_t furi_event_flag_wait(
(TickType_t)timeout (TickType_t)timeout
); );
if (options & FuriFlagWaitAll) { if (options & TtFlagWaitAll) {
if ((flags & rflags) != flags) { if ((flags & rflags) != flags) {
if (timeout > 0U) { if (timeout > 0U) {
rflags = (uint32_t)FuriStatusErrorTimeout; rflags = (uint32_t)TtStatusErrorTimeout;
} else { } else {
rflags = (uint32_t)FuriStatusErrorResource; rflags = (uint32_t)TtStatusErrorResource;
} }
} }
} else { } else {
if ((flags & rflags) == 0U) { if ((flags & rflags) == 0U) {
if (timeout > 0U) { if (timeout > 0U) {
rflags = (uint32_t)FuriStatusErrorTimeout; rflags = (uint32_t)TtStatusErrorTimeout;
} else { } 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 "kernel.h"
#include "check.h" #include "check.h"
#include "furi_core_defines.h" #include "core_defines.h"
#include "furi_core_types.h" #include "core_types.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include <rom/ets_sys.h> #include <rom/ets_sys.h>
bool furi_kernel_is_irq() { bool tt_kernel_is_irq() {
return FURI_IS_IRQ_MODE(); return TT_IS_IRQ_MODE();
} }
bool furi_kernel_is_running() { bool tt_kernel_is_running() {
return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING; return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING;
} }
int32_t furi_kernel_lock() { int32_t tt_kernel_lock() {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
int32_t lock; int32_t lock;
@ -32,7 +32,7 @@ int32_t furi_kernel_lock() {
case taskSCHEDULER_NOT_STARTED: case taskSCHEDULER_NOT_STARTED:
default: default:
lock = (int32_t)FuriStatusError; lock = (int32_t)TtStatusError;
break; break;
} }
@ -40,8 +40,8 @@ int32_t furi_kernel_lock() {
return (lock); return (lock);
} }
int32_t furi_kernel_unlock() { int32_t tt_kernel_unlock() {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
int32_t lock; int32_t lock;
@ -51,7 +51,7 @@ int32_t furi_kernel_unlock() {
if (xTaskResumeAll() != pdTRUE) { if (xTaskResumeAll() != pdTRUE) {
if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) { if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) {
lock = (int32_t)FuriStatusError; lock = (int32_t)TtStatusError;
} }
} }
break; break;
@ -62,7 +62,7 @@ int32_t furi_kernel_unlock() {
case taskSCHEDULER_NOT_STARTED: case taskSCHEDULER_NOT_STARTED:
default: default:
lock = (int32_t)FuriStatusError; lock = (int32_t)TtStatusError;
break; break;
} }
@ -70,8 +70,8 @@ int32_t furi_kernel_unlock() {
return (lock); return (lock);
} }
int32_t furi_kernel_restore_lock(int32_t lock) { int32_t tt_kernel_restore_lock(int32_t lock) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
switch (xTaskGetSchedulerState()) { switch (xTaskGetSchedulerState()) {
case taskSCHEDULER_SUSPENDED: case taskSCHEDULER_SUSPENDED:
@ -80,11 +80,11 @@ int32_t furi_kernel_restore_lock(int32_t lock) {
vTaskSuspendAll(); vTaskSuspendAll();
} else { } else {
if (lock != 0) { if (lock != 0) {
lock = (int32_t)FuriStatusError; lock = (int32_t)TtStatusError;
} else { } else {
if (xTaskResumeAll() != pdTRUE) { if (xTaskResumeAll() != pdTRUE) {
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { 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: case taskSCHEDULER_NOT_STARTED:
default: default:
lock = (int32_t)FuriStatusError; lock = (int32_t)TtStatusError;
break; break;
} }
@ -101,13 +101,13 @@ int32_t furi_kernel_restore_lock(int32_t lock) {
return (lock); return (lock);
} }
uint32_t furi_kernel_get_tick_frequency() { uint32_t tt_kernel_get_tick_frequency() {
/* Return frequency in hertz */ /* Return frequency in hertz */
return (configTICK_RATE_HZ_RAW); return (configTICK_RATE_HZ_RAW);
} }
void furi_delay_tick(uint32_t ticks) { void tt_delay_tick(uint32_t ticks) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
if (ticks == 0U) { if (ticks == 0U) {
taskYIELD(); taskYIELD();
} else { } else {
@ -115,13 +115,13 @@ void furi_delay_tick(uint32_t ticks) {
} }
} }
FuriStatus furi_delay_until_tick(uint32_t tick) { TtStatus tt_delay_until_tick(uint32_t tick) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
TickType_t tcnt, delay; TickType_t tcnt, delay;
FuriStatus stat; TtStatus stat;
stat = FuriStatusOk; stat = TtStatusOk;
tcnt = xTaskGetTickCount(); tcnt = xTaskGetTickCount();
/* Determine remaining number of tick to delay */ /* 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 ((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) {
if (xTaskDelayUntil(&tcnt, delay) == pdFALSE) { if (xTaskDelayUntil(&tcnt, delay) == pdFALSE) {
/* Did not delay */ /* Did not delay */
stat = FuriStatusError; stat = TtStatusError;
} }
} else { } else {
/* No delay or already expired */ /* No delay or already expired */
stat = FuriStatusErrorParameter; stat = TtStatusErrorParameter;
} }
/* Return execution status */ /* Return execution status */
return (stat); return (stat);
} }
uint32_t furi_get_tick() { uint32_t tt_get_tick() {
TickType_t ticks; TickType_t ticks;
if (furi_kernel_is_irq() != 0U) { if (tt_kernel_is_irq() != 0U) {
ticks = xTaskGetTickCountFromISR(); ticks = xTaskGetTickCountFromISR();
} else { } else {
ticks = xTaskGetTickCount(); ticks = xTaskGetTickCount();
@ -154,7 +154,7 @@ uint32_t furi_get_tick() {
return ticks; 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 #if configTICK_RATE_HZ_RAW == 1000
return milliseconds; return milliseconds;
#else #else
@ -162,21 +162,21 @@ uint32_t furi_ms_to_ticks(uint32_t milliseconds) {
#endif #endif
} }
void furi_delay_ms(uint32_t milliseconds) { void tt_delay_ms(uint32_t milliseconds) {
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
if (milliseconds > 0 && milliseconds < portMAX_DELAY - 1) { if (milliseconds > 0 && milliseconds < portMAX_DELAY - 1) {
milliseconds += 1; milliseconds += 1;
} }
#if configTICK_RATE_HZ_RAW == 1000 #if configTICK_RATE_HZ_RAW == 1000
furi_delay_tick(milliseconds); tt_delay_tick(milliseconds);
#else #else
furi_delay_tick(furi_ms_to_ticks(milliseconds)); tt_delay_tick(tt_ms_to_ticks(milliseconds));
#endif #endif
} else if (milliseconds > 0) { } 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); ets_delay_us(microseconds);
} }

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "furi_core_types.h" #include "core_types.h"
#define configTICK_RATE_HZ_RAW CONFIG_FREERTOS_HZ #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 * @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 /** Check if kernel is running
* *
* @return true if running, false otherwise * @return true if running, false otherwise
*/ */
bool furi_kernel_is_running(); bool tt_kernel_is_running();
/** Lock kernel, pause process scheduling /** Lock kernel, pause process scheduling
* *
@ -37,7 +37,7 @@ bool furi_kernel_is_running();
* *
* @return previous lock state(0 - unlocked, 1 - locked) * @return previous lock state(0 - unlocked, 1 - locked)
*/ */
int32_t furi_kernel_lock(); int32_t tt_kernel_lock();
/** Unlock kernel, resume process scheduling /** Unlock kernel, resume process scheduling
* *
@ -45,7 +45,7 @@ int32_t furi_kernel_lock();
* *
* @return previous lock state(0 - unlocked, 1 - locked) * @return previous lock state(0 - unlocked, 1 - locked)
*/ */
int32_t furi_kernel_unlock(); int32_t tt_kernel_unlock();
/** Restore kernel lock state /** Restore kernel lock state
* *
@ -55,13 +55,13 @@ int32_t furi_kernel_unlock();
* *
* @return new lock state or error * @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 /** Get kernel systick frequency
* *
* @return systick counts per second * @return systick counts per second
*/ */
uint32_t furi_kernel_get_tick_frequency(); uint32_t tt_kernel_get_tick_frequency();
/** Delay execution /** Delay execution
* *
@ -71,7 +71,7 @@ uint32_t furi_kernel_get_tick_frequency();
* *
* @param[in] ticks The ticks count to pause * @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 /** 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 * @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 /** Get current tick counter
* *
@ -89,27 +89,27 @@ FuriStatus furi_delay_until_tick(uint32_t tick);
* *
* @return Current ticks in milliseconds * @return Current ticks in milliseconds
*/ */
uint32_t furi_get_tick(void); uint32_t tt_get_tick(void);
/** Convert milliseconds to ticks /** Convert milliseconds to ticks
* *
* @param[in] milliseconds time in milliseconds * @param[in] milliseconds time in milliseconds
* @return time in ticks * @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 /** Delay in milliseconds
* *
* This method uses kernel ticks on the inside, which causes delay to be aliased to scheduler timer intervals. * 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. * Real wait time will be between X+ milliseconds.
* Special value: 0, will cause task yield. * 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 * @warning Cannot be used from ISR
* *
* @param[in] milliseconds milliseconds to wait * @param[in] milliseconds milliseconds to wait
*/ */
void furi_delay_ms(uint32_t milliseconds); void tt_delay_ms(uint32_t milliseconds);
/** Delay in microseconds /** Delay in microseconds
* *
@ -117,7 +117,7 @@ void furi_delay_ms(uint32_t milliseconds);
* *
* @param[in] microseconds microseconds to wait * @param[in] microseconds microseconds to wait
*/ */
void furi_delay_us(uint32_t microseconds); void tt_delay_us(uint32_t microseconds);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

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

View File

@ -5,51 +5,50 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/queue.h" #include "freertos/queue.h"
FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { MessageQueue* tt_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) {
furi_assert((furi_kernel_is_irq() == 0U) && (msg_count > 0U) && (msg_size > 0U)); tt_assert((tt_kernel_is_irq() == 0U) && (msg_count > 0U) && (msg_size > 0U));
QueueHandle_t handle = xQueueCreate(msg_count, msg_size); 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) { void tt_message_queue_free(MessageQueue* instance) {
furi_assert(furi_kernel_is_irq() == 0U); tt_assert(tt_kernel_is_irq() == 0U);
furi_assert(instance); tt_assert(instance);
vQueueDelete((QueueHandle_t)instance); vQueueDelete((QueueHandle_t)instance);
} }
FuriStatus TtStatus tt_message_queue_put(MessageQueue* instance, const void* msg_ptr, uint32_t timeout) {
furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t timeout) {
QueueHandle_t hQueue = (QueueHandle_t)instance; QueueHandle_t hQueue = (QueueHandle_t)instance;
FuriStatus stat; TtStatus stat;
BaseType_t yield; 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)) { if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
stat = FuriStatusErrorParameter; stat = TtStatusErrorParameter;
} else { } else {
yield = pdFALSE; yield = pdFALSE;
if (xQueueSendToBackFromISR(hQueue, msg_ptr, &yield) != pdTRUE) { if (xQueueSendToBackFromISR(hQueue, msg_ptr, &yield) != pdTRUE) {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} else { } else {
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} }
} }
} else { } else {
if ((hQueue == NULL) || (msg_ptr == NULL)) { if ((hQueue == NULL) || (msg_ptr == NULL)) {
stat = FuriStatusErrorParameter; stat = TtStatusErrorParameter;
} else { } else {
if (xQueueSendToBack(hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) { if (xQueueSendToBack(hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) {
if (timeout != 0U) { if (timeout != 0U) {
stat = FuriStatusErrorTimeout; stat = TtStatusErrorTimeout;
} else { } else {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} }
} }
} }
@ -59,34 +58,34 @@ furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t
return (stat); 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; QueueHandle_t hQueue = (QueueHandle_t)instance;
FuriStatus stat; TtStatus stat;
BaseType_t yield; 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)) { if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout_ticks != 0U)) {
stat = FuriStatusErrorParameter; stat = TtStatusErrorParameter;
} else { } else {
yield = pdFALSE; yield = pdFALSE;
if (xQueueReceiveFromISR(hQueue, msg_ptr, &yield) != pdPASS) { if (xQueueReceiveFromISR(hQueue, msg_ptr, &yield) != pdPASS) {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} else { } else {
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} }
} }
} else { } else {
if ((hQueue == NULL) || (msg_ptr == NULL)) { if ((hQueue == NULL) || (msg_ptr == NULL)) {
stat = FuriStatusErrorParameter; stat = TtStatusErrorParameter;
} else { } else {
if (xQueueReceive(hQueue, msg_ptr, (TickType_t)timeout_ticks) != pdPASS) { if (xQueueReceive(hQueue, msg_ptr, (TickType_t)timeout_ticks) != pdPASS) {
if (timeout_ticks != 0U) { if (timeout_ticks != 0U) {
stat = FuriStatusErrorTimeout; stat = TtStatusErrorTimeout;
} else { } else {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} }
} }
} }
@ -96,7 +95,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin
return (stat); 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; StaticQueue_t* mq = (StaticQueue_t*)instance;
uint32_t capacity; uint32_t capacity;
@ -111,7 +110,7 @@ uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) {
return (capacity); 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; StaticQueue_t* mq = (StaticQueue_t*)instance;
uint32_t size; uint32_t size;
@ -126,13 +125,13 @@ uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) {
return (size); 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; QueueHandle_t hQueue = (QueueHandle_t)instance;
UBaseType_t count; UBaseType_t count;
if (hQueue == NULL) { if (hQueue == NULL) {
count = 0U; count = 0U;
} else if (furi_kernel_is_irq() != 0U) { } else if (tt_kernel_is_irq() != 0U) {
count = uxQueueMessagesWaitingFromISR(hQueue); count = uxQueueMessagesWaitingFromISR(hQueue);
} else { } else {
count = uxQueueMessagesWaiting(hQueue); count = uxQueueMessagesWaiting(hQueue);
@ -142,14 +141,14 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {
return ((uint32_t)count); 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; StaticQueue_t* mq = (StaticQueue_t*)instance;
uint32_t space; uint32_t space;
uint32_t isrm; uint32_t isrm;
if (mq == NULL) { if (mq == NULL) {
space = 0U; space = 0U;
} else if (furi_kernel_is_irq() != 0U) { } else if (tt_kernel_is_irq() != 0U) {
isrm = taskENTER_CRITICAL_FROM_ISR(); isrm = taskENTER_CRITICAL_FROM_ISR();
/* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */
@ -164,16 +163,16 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
return (space); return (space);
} }
FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { TtStatus tt_message_queue_reset(MessageQueue* instance) {
QueueHandle_t hQueue = (QueueHandle_t)instance; QueueHandle_t hQueue = (QueueHandle_t)instance;
FuriStatus stat; TtStatus stat;
if (furi_kernel_is_irq() != 0U) { if (tt_kernel_is_irq() != 0U) {
stat = FuriStatusErrorISR; stat = TtStatusErrorISR;
} else if (hQueue == NULL) { } else if (hQueue == NULL) {
stat = FuriStatusErrorParameter; stat = TtStatusErrorParameter;
} else { } else {
stat = FuriStatusOk; stat = TtStatusOk;
(void)xQueueReset(hQueue); (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 "mutex.h"
#include "check.h" #include "check.h"
#include "furi_core_defines.h" #include "core_defines.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "log.h" #include "log.h"
FuriMutex* furi_mutex_alloc(FuriMutexType type) { Mutex* tt_mutex_alloc(MutexType type) {
furi_assert(!FURI_IS_IRQ_MODE()); tt_assert(!TT_IS_IRQ_MODE());
SemaphoreHandle_t hMutex = NULL; SemaphoreHandle_t hMutex = NULL;
if (type == FuriMutexTypeNormal) { if (type == MutexTypeNormal) {
hMutex = xSemaphoreCreateMutex(); hMutex = xSemaphoreCreateMutex();
} else if (type == FuriMutexTypeRecursive) { } else if (type == MutexTypeRecursive) {
hMutex = xSemaphoreCreateRecursiveMutex(); hMutex = xSemaphoreCreateRecursiveMutex();
} else { } 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' */ /* Set LSB as 'recursive mutex flag' */
hMutex = (SemaphoreHandle_t)((uint32_t)hMutex | 1U); hMutex = (SemaphoreHandle_t)((uint32_t)hMutex | 1U);
} }
/* Return mutex ID */ /* Return mutex ID */
return ((FuriMutex*)hMutex); return ((Mutex*)hMutex);
} }
void furi_mutex_free(FuriMutex* instance) { void tt_mutex_free(Mutex* instance) {
furi_assert(!FURI_IS_IRQ_MODE()); tt_assert(!TT_IS_IRQ_MODE());
furi_assert(instance); tt_assert(instance);
vSemaphoreDelete((SemaphoreHandle_t)((uint32_t)instance & ~1U)); 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; SemaphoreHandle_t hMutex;
FuriStatus stat; TtStatus stat;
uint32_t rmtx; uint32_t rmtx;
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
@ -47,27 +47,27 @@ FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) {
/* Extract recursive mutex flag */ /* Extract recursive mutex flag */
rmtx = (uint32_t)instance & 1U; rmtx = (uint32_t)instance & 1U;
stat = FuriStatusOk; stat = TtStatusOk;
if (FURI_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
stat = FuriStatusErrorISR; stat = TtStatusErrorISR;
} else if (hMutex == NULL) { } else if (hMutex == NULL) {
stat = FuriStatusErrorParameter; stat = TtStatusErrorParameter;
} else { } else {
if (rmtx != 0U) { if (rmtx != 0U) {
if (xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) { if (xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) {
if (timeout != 0U) { if (timeout != 0U) {
stat = FuriStatusErrorTimeout; stat = TtStatusErrorTimeout;
} else { } else {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} }
} }
} else { } else {
if (xSemaphoreTake(hMutex, timeout) != pdPASS) { if (xSemaphoreTake(hMutex, timeout) != pdPASS) {
if (timeout != 0U) { if (timeout != 0U) {
stat = FuriStatusErrorTimeout; stat = TtStatusErrorTimeout;
} else { } else {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} }
} }
} }
@ -77,9 +77,9 @@ FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) {
return (stat); return (stat);
} }
FuriStatus furi_mutex_release(FuriMutex* instance) { TtStatus tt_mutex_release(Mutex* instance) {
SemaphoreHandle_t hMutex; SemaphoreHandle_t hMutex;
FuriStatus stat; TtStatus stat;
uint32_t rmtx; uint32_t rmtx;
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
@ -87,20 +87,20 @@ FuriStatus furi_mutex_release(FuriMutex* instance) {
/* Extract recursive mutex flag */ /* Extract recursive mutex flag */
rmtx = (uint32_t)instance & 1U; rmtx = (uint32_t)instance & 1U;
stat = FuriStatusOk; stat = TtStatusOk;
if (FURI_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
stat = FuriStatusErrorISR; stat = TtStatusErrorISR;
} else if (hMutex == NULL) { } else if (hMutex == NULL) {
stat = FuriStatusErrorParameter; stat = TtStatusErrorParameter;
} else { } else {
if (rmtx != 0U) { if (rmtx != 0U) {
if (xSemaphoreGiveRecursive(hMutex) != pdPASS) { if (xSemaphoreGiveRecursive(hMutex) != pdPASS) {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} }
} else { } else {
if (xSemaphoreGive(hMutex) != pdPASS) { if (xSemaphoreGive(hMutex) != pdPASS) {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} }
} }
} }
@ -109,16 +109,16 @@ FuriStatus furi_mutex_release(FuriMutex* instance) {
return (stat); return (stat);
} }
FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { ThreadId tt_mutex_get_owner(Mutex* instance) {
SemaphoreHandle_t hMutex; SemaphoreHandle_t hMutex;
FuriThreadId owner; ThreadId owner;
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
if ((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) { if ((TT_IS_IRQ_MODE()) || (hMutex == NULL)) {
owner = 0; owner = 0;
} else { } else {
owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); owner = (ThreadId)xSemaphoreGetMutexHolder(hMutex);
} }
/* Return owner thread ID */ /* 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 "semaphore.h"
#include "check.h" #include "check.h"
#include "furi_core_defines.h" #include "core_defines.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count) { Semaphore* tt_semaphore_alloc(uint32_t max_count, uint32_t initial_count) {
furi_assert(!FURI_IS_IRQ_MODE()); tt_assert(!TT_IS_IRQ_MODE());
furi_assert((max_count > 0U) && (initial_count <= max_count)); tt_assert((max_count > 0U) && (initial_count <= max_count));
SemaphoreHandle_t hSemaphore = NULL; SemaphoreHandle_t hSemaphore = NULL;
if (max_count == 1U) { 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); hSemaphore = xSemaphoreCreateCounting(max_count, initial_count);
} }
furi_check(hSemaphore); tt_check(hSemaphore);
return (FuriSemaphore*)hSemaphore; return (Semaphore*)hSemaphore;
} }
void furi_semaphore_free(FuriSemaphore* instance) { void tt_semaphore_free(Semaphore* instance) {
furi_assert(instance); tt_assert(instance);
furi_assert(!FURI_IS_IRQ_MODE()); tt_assert(!TT_IS_IRQ_MODE());
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
vSemaphoreDelete(hSemaphore); vSemaphoreDelete(hSemaphore);
} }
FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { TtStatus tt_semaphore_acquire(Semaphore* instance, uint32_t timeout) {
furi_assert(instance); tt_assert(instance);
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
FuriStatus status; TtStatus status;
BaseType_t yield; BaseType_t yield;
status = FuriStatusOk; status = TtStatusOk;
if (FURI_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
if (timeout != 0U) { if (timeout != 0U) {
status = FuriStatusErrorParameter; status = TtStatusErrorParameter;
} else { } else {
yield = pdFALSE; yield = pdFALSE;
if (xSemaphoreTakeFromISR(hSemaphore, &yield) != pdPASS) { if (xSemaphoreTakeFromISR(hSemaphore, &yield) != pdPASS) {
status = FuriStatusErrorResource; status = TtStatusErrorResource;
} else { } else {
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} }
@ -60,9 +60,9 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {
} else { } else {
if (xSemaphoreTake(hSemaphore, (TickType_t)timeout) != pdPASS) { if (xSemaphoreTake(hSemaphore, (TickType_t)timeout) != pdPASS) {
if (timeout != 0U) { if (timeout != 0U) {
status = FuriStatusErrorTimeout; status = TtStatusErrorTimeout;
} else { } else {
status = FuriStatusErrorResource; status = TtStatusErrorResource;
} }
} }
} }
@ -70,26 +70,26 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {
return status; return status;
} }
FuriStatus furi_semaphore_release(FuriSemaphore* instance) { TtStatus tt_semaphore_release(Semaphore* instance) {
furi_assert(instance); tt_assert(instance);
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
FuriStatus stat; TtStatus stat;
BaseType_t yield; BaseType_t yield;
stat = FuriStatusOk; stat = TtStatusOk;
if (FURI_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
yield = pdFALSE; yield = pdFALSE;
if (xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) { if (xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} else { } else {
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} }
} else { } else {
if (xSemaphoreGive(hSemaphore) != pdPASS) { if (xSemaphoreGive(hSemaphore) != pdPASS) {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} }
} }
@ -97,13 +97,13 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
return (stat); return (stat);
} }
uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { uint32_t tt_semaphore_get_count(Semaphore* instance) {
furi_assert(instance); tt_assert(instance);
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
uint32_t count; 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? // TODO: uxSemaphoreGetCountFromISR is not supported on esp-idf 5.1.2 - perhaps later on?
#ifdef uxSemaphoreGetCountFromISR #ifdef uxSemaphoreGetCountFromISR
count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore); 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 "service.h"
#include "context.h"
#include "mutex.h" #include "mutex.h"
#include "service_manifest.h" #include "service_manifest.h"
typedef struct { typedef struct {
FuriMutex* mutex; Mutex* mutex;
const ServiceManifest* manifest; const ServiceManifest* manifest;
void* data; void* data;
} ServiceData; } ServiceData;
Service service_alloc(const ServiceManifest* _Nonnull manifest); ServiceData* tt_service_alloc(const ServiceManifest* _Nonnull manifest);
void service_free(Service _Nonnull service); void tt_service_free(ServiceData* _Nonnull service);

View File

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

View File

@ -1,10 +1,10 @@
#include "service_registry.h" #include "service_registry.h"
#include "furi_core.h"
#include "m-dict.h" #include "m-dict.h"
#include "m_cstr_dup.h" #include "m_cstr_dup.h"
#include "mutex.h" #include "mutex.h"
#include "service_i.h" #include "service_i.h"
#include "tactility_core.h"
#define TAG "service_registry" #define TAG "service_registry"
@ -22,56 +22,56 @@ DICT_DEF2(ServiceInstanceDict, const char*, M_CSTR_DUP_OPLIST, const ServiceData
service_registry_manifest_unlock(); \ service_registry_manifest_unlock(); \
} }
ServiceManifestDict_t service_manifest_dict; static ServiceManifestDict_t service_manifest_dict;
ServiceInstanceDict_t service_instance_dict; static ServiceInstanceDict_t service_instance_dict;
FuriMutex* manifest_mutex = NULL; static Mutex* manifest_mutex = NULL;
FuriMutex* instance_mutex = NULL; static Mutex* instance_mutex = NULL;
void service_registry_init() { void tt_service_registry_init() {
furi_assert(manifest_mutex == NULL); tt_assert(manifest_mutex == NULL);
manifest_mutex = furi_mutex_alloc(FuriMutexTypeNormal); manifest_mutex = tt_mutex_alloc(MutexTypeNormal);
ServiceManifestDict_init(service_manifest_dict); ServiceManifestDict_init(service_manifest_dict);
furi_assert(instance_mutex == NULL); tt_assert(instance_mutex == NULL);
instance_mutex = furi_mutex_alloc(FuriMutexTypeNormal); instance_mutex = tt_mutex_alloc(MutexTypeNormal);
ServiceInstanceDict_init(service_instance_dict); ServiceInstanceDict_init(service_instance_dict);
} }
void service_registry_instance_lock() { void service_registry_instance_lock() {
furi_assert(instance_mutex != NULL); tt_assert(instance_mutex != NULL);
furi_mutex_acquire(instance_mutex, FuriWaitForever); tt_mutex_acquire(instance_mutex, TtWaitForever);
} }
void service_registry_instance_unlock() { void service_registry_instance_unlock() {
furi_assert(instance_mutex != NULL); tt_assert(instance_mutex != NULL);
furi_mutex_release(instance_mutex); tt_mutex_release(instance_mutex);
} }
void service_registry_manifest_lock() { void service_registry_manifest_lock() {
furi_assert(manifest_mutex != NULL); tt_assert(manifest_mutex != NULL);
furi_mutex_acquire(manifest_mutex, FuriWaitForever); tt_mutex_acquire(manifest_mutex, TtWaitForever);
} }
void service_registry_manifest_unlock() { void service_registry_manifest_unlock() {
furi_assert(manifest_mutex != NULL); tt_assert(manifest_mutex != NULL);
furi_mutex_release(manifest_mutex); tt_mutex_release(manifest_mutex);
} }
void service_registry_add(const ServiceManifest _Nonnull* manifest) { void tt_service_registry_add(const ServiceManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "adding %s", manifest->id); TT_LOG_I(TAG, "adding %s", manifest->id);
service_registry_manifest_lock(); service_registry_manifest_lock();
ServiceManifestDict_set_at(service_manifest_dict, manifest->id, manifest); ServiceManifestDict_set_at(service_manifest_dict, manifest->id, manifest);
service_registry_manifest_unlock(); service_registry_manifest_unlock();
} }
void service_registry_remove(const ServiceManifest _Nonnull* manifest) { void tt_service_registry_remove(const ServiceManifest _Nonnull* manifest) {
FURI_LOG_I(TAG, "removing %s", manifest->id); TT_LOG_I(TAG, "removing %s", manifest->id);
service_registry_manifest_lock(); service_registry_manifest_lock();
ServiceManifestDict_erase(service_manifest_dict, manifest->id); ServiceManifestDict_erase(service_manifest_dict, manifest->id);
service_registry_manifest_unlock(); 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(); service_registry_manifest_lock();
const ServiceManifest** _Nullable manifest = ServiceManifestDict_get(service_manifest_dict, id); const ServiceManifest** _Nullable manifest = ServiceManifestDict_get(service_manifest_dict, id);
service_registry_manifest_unlock(); service_registry_manifest_unlock();
@ -89,48 +89,48 @@ ServiceData* _Nullable service_registry_find_instance_by_id(const char* id) {
return service; 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, { APP_REGISTRY_FOR_EACH(manifest, {
callback(manifest, context); callback(manifest, context);
}); });
} }
// TODO: return proper error/status instead of BOOL // TODO: return proper error/status instead of BOOL
bool service_registry_start(const char* service_id) { bool tt_service_registry_start(const char* service_id) {
FURI_LOG_I(TAG, "starting %s", service_id); TT_LOG_I(TAG, "starting %s", service_id);
const ServiceManifest* manifest = service_registry_find_manifest_by_id(service_id); const ServiceManifest* manifest = tt_service_registry_find_manifest_by_id(service_id);
if (manifest == NULL) { 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; return false;
} }
Service service = service_alloc(manifest); Service service = tt_service_alloc(manifest);
manifest->on_start(service); manifest->on_start(service);
service_registry_instance_lock(); service_registry_instance_lock();
ServiceInstanceDict_set_at(service_instance_dict, manifest->id, service); ServiceInstanceDict_set_at(service_instance_dict, manifest->id, service);
service_registry_instance_unlock(); service_registry_instance_unlock();
FURI_LOG_I(TAG, "started %s", service_id); TT_LOG_I(TAG, "started %s", service_id);
return true; return true;
} }
bool service_registry_stop(const char* service_id) { bool tt_service_registry_stop(const char* service_id) {
FURI_LOG_I(TAG, "stopping %s", service_id); TT_LOG_I(TAG, "stopping %s", service_id);
ServiceData* service = service_registry_find_instance_by_id(service_id); ServiceData* service = service_registry_find_instance_by_id(service_id);
if (service == NULL) { 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; return false;
} }
service->manifest->on_stop(service); service->manifest->on_stop(service);
service_free(service); tt_service_free(service);
service_registry_instance_lock(); service_registry_instance_lock();
ServiceInstanceDict_erase(service_instance_dict, service_id); ServiceInstanceDict_erase(service_instance_dict, service_id);
service_registry_instance_unlock(); service_registry_instance_unlock();
FURI_LOG_I(TAG, "stopped %s", service_id); TT_LOG_I(TAG, "stopped %s", service_id);
return true; 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 "stream_buffer.h"
#include "check.h" #include "check.h"
#include "furi_core_defines.h" #include "core_defines.h"
#include "furi_core_types.h" #include "core_types.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/stream_buffer.h" #include "freertos/stream_buffer.h"
FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level) { StreamBuffer* tt_stream_buffer_alloc(size_t size, size_t trigger_level) {
furi_assert(size != 0); tt_assert(size != 0);
StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level); StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level);
furi_check(handle); tt_check(handle);
return handle; return handle;
}; };
void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer) { void tt_stream_buffer_free(StreamBuffer* stream_buffer) {
furi_assert(stream_buffer); tt_assert(stream_buffer);
vStreamBufferDelete(stream_buffer); vStreamBufferDelete(stream_buffer);
}; };
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) {
furi_assert(stream_buffer); tt_assert(stream_buffer);
return xStreamBufferSetTriggerLevel(stream_buffer, trigger_level) == pdTRUE; return xStreamBufferSetTriggerLevel(stream_buffer, trigger_level) == pdTRUE;
}; };
size_t furi_stream_buffer_send( size_t tt_stream_buffer_send(
FuriStreamBuffer* stream_buffer, StreamBuffer* stream_buffer,
const void* data, const void* data,
size_t length, size_t length,
uint32_t timeout uint32_t timeout
) { ) {
size_t ret; size_t ret;
if (FURI_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
BaseType_t yield; BaseType_t yield;
ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield); ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield);
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
@ -44,15 +44,15 @@ size_t furi_stream_buffer_send(
return ret; return ret;
}; };
size_t furi_stream_buffer_receive( size_t tt_stream_buffer_receive(
FuriStreamBuffer* stream_buffer, StreamBuffer* stream_buffer,
void* data, void* data,
size_t length, size_t length,
uint32_t timeout uint32_t timeout
) { ) {
size_t ret; size_t ret;
if (FURI_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
BaseType_t yield; BaseType_t yield;
ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield); ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield);
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
@ -63,26 +63,26 @@ size_t furi_stream_buffer_receive(
return ret; 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); 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); 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; 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); 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) { if (xStreamBufferReset(stream_buffer) == pdPASS) {
return FuriStatusOk; return TtStatusOk;
} else { } else {
return FuriStatusError; return TtStatusError;
} }
} }

View File

@ -1,6 +1,6 @@
/** /**
* @file stream_buffer.h * @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 * 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 * interrupt to another. Their implementation is light weight, making them
@ -12,7 +12,7 @@
* interrupt that will read from the buffer (the reader). * interrupt that will read from the buffer (the reader).
*/ */
#pragma once #pragma once
#include "furi_core_types.h" #include "core_types.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@ -21,7 +21,7 @@
extern "C" { extern "C" {
#endif #endif
typedef void FuriStreamBuffer; typedef void StreamBuffer;
/** /**
* @brief Allocate stream buffer instance. * @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. * 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. * @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 * @brief Free stream buffer instance
* *
* @param stream_buffer The 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. * @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 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). * @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. * @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 * @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. * Blocked state to wait for space to become available if the stream buffer is full.
* Will return immediately if timeout is zero. * 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. * Ignored if called from ISR.
* @return The number of bytes actually written to the stream buffer. * @return The number of bytes actually written to the stream buffer.
*/ */
size_t furi_stream_buffer_send( size_t tt_stream_buffer_send(
FuriStreamBuffer* stream_buffer, StreamBuffer* stream_buffer,
const void* data, const void* data,
size_t length, size_t length,
uint32_t timeout 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 * @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. * Blocked state to wait for data to become available if the stream buffer is empty.
* Will return immediately if timeout is zero. * 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. * Ignored if called from ISR.
* @return The number of bytes read from the stream buffer, if any. * @return The number of bytes read from the stream buffer, if any.
*/ */
size_t furi_stream_buffer_receive( size_t tt_stream_buffer_receive(
FuriStreamBuffer* stream_buffer, StreamBuffer* stream_buffer,
void* data, void* data,
size_t length, size_t length,
uint32_t timeout 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 * @return The number of bytes that can be read from the stream buffer before
* the stream buffer would be empty. * 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 * @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 * @return The number of bytes that can be written to the stream buffer before
* the stream buffer would be full. * 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. * @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 true if the stream buffer is full.
* @return false if the stream buffer is not 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. * @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 true if the stream buffer is empty.
* @return false if the stream buffer is not 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 * @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. * are no tasks blocked waiting to either send to or receive from the stream buffer.
* *
* @param stream_buffer The stream buffer instance. * @param stream_buffer The stream buffer instance.
* @return FuriStatusOk if the stream buffer is reset. * @return TtStatusOk if the stream buffer is reset.
* @return FuriStatusError if there was a task blocked waiting to send to or read * @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. * 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 #ifdef __cplusplus
} }

View File

@ -3,10 +3,12 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include "app.h"
#include "check.h" #include "check.h"
#include "core_defines.h"
#include "core_extra_defines.h"
#include "core_types.h"
#include "critical.h" #include "critical.h"
#include "event_flag.h" #include "event_flag.h"
#include "furi_core_defines.h"
#include "furi_core_types.h"
#include "furi_extra_defines.h"
#include "log.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" #include "freertos/timers.h"
typedef struct { typedef struct {
FuriTimerCallback func; TimerCallback func;
void* context; void* context;
} TimerCallback_t; } TimerCallback_t;
static void TimerCallback(TimerHandle_t hTimer) { static void timer_callback(TimerHandle_t hTimer) {
TimerCallback_t* callb; TimerCallback_t* callb;
/* Retrieve pointer to callback function and context */ /* 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) { Timer* tt_timer_alloc(TimerCallback func, TimerType type, void* context) {
furi_assert((furi_kernel_is_irq() == 0U) && (func != NULL)); tt_assert((tt_kernel_is_irq() == 0U) && (func != NULL));
TimerHandle_t hTimer; TimerHandle_t hTimer;
TimerCallback_t* callb; TimerCallback_t* callb;
@ -40,7 +40,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
callb->func = func; callb->func = func;
callb->context = context; callb->context = context;
if (type == FuriTimerTypeOnce) { if (type == TimerTypeOnce) {
reload = pdFALSE; reload = pdFALSE;
} else { } else {
reload = pdTRUE; reload = pdTRUE;
@ -50,25 +50,26 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
callb = (TimerCallback_t*)((uint32_t)callb | 1U); callb = (TimerCallback_t*)((uint32_t)callb | 1U);
// TimerCallback function is always provided as a callback and is used to call application // TimerCallback function is always provided as a callback and is used to call application
// specified function with its context both stored in structure callb. // specified function with its context both stored in structure callb.
hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, TimerCallback); // TODO: should we use pointer to function or function directly as-is?
furi_check(hTimer); hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, timer_callback);
tt_check(hTimer);
/* Return timer ID */ /* Return timer ID */
return ((FuriTimer*)hTimer); return ((Timer*)hTimer);
} }
void furi_timer_free(FuriTimer* instance) { void tt_timer_free(Timer* instance) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
furi_assert(instance); tt_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance; TimerHandle_t hTimer = (TimerHandle_t)instance;
TimerCallback_t* callb; TimerCallback_t* callb;
callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); 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) { if ((uint32_t)callb & 1U) {
/* Callback memory was allocated from dynamic pool, clear flag */ /* 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) { TtStatus tt_timer_start(Timer* instance, uint32_t ticks) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
furi_assert(instance); tt_assert(instance);
furi_assert(ticks < portMAX_DELAY); tt_assert(ticks < portMAX_DELAY);
TimerHandle_t hTimer = (TimerHandle_t)instance; TimerHandle_t hTimer = (TimerHandle_t)instance;
FuriStatus stat; TtStatus stat;
if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS) { if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS) {
stat = FuriStatusOk; stat = TtStatusOk;
} else { } else {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} }
/* Return execution status */ /* Return execution status */
return (stat); return (stat);
} }
FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) { TtStatus tt_timer_restart(Timer* instance, uint32_t ticks) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
furi_assert(instance); tt_assert(instance);
furi_assert(ticks < portMAX_DELAY); tt_assert(ticks < portMAX_DELAY);
TimerHandle_t hTimer = (TimerHandle_t)instance; TimerHandle_t hTimer = (TimerHandle_t)instance;
FuriStatus stat; TtStatus stat;
if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS && if (xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS &&
xTimerReset(hTimer, portMAX_DELAY) == pdPASS) { xTimerReset(hTimer, portMAX_DELAY) == pdPASS) {
stat = FuriStatusOk; stat = TtStatusOk;
} else { } else {
stat = FuriStatusErrorResource; stat = TtStatusErrorResource;
} }
/* Return execution status */ /* Return execution status */
return (stat); return (stat);
} }
FuriStatus furi_timer_stop(FuriTimer* instance) { TtStatus tt_timer_stop(Timer* instance) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
furi_assert(instance); tt_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)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) { uint32_t tt_timer_is_running(Timer* instance) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
furi_assert(instance); tt_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance; TimerHandle_t hTimer = (TimerHandle_t)instance;
@ -137,36 +138,36 @@ uint32_t furi_timer_is_running(FuriTimer* instance) {
return (uint32_t)xTimerIsTimerActive(hTimer); return (uint32_t)xTimerIsTimerActive(hTimer);
} }
uint32_t furi_timer_get_expire_time(FuriTimer* instance) { uint32_t tt_timer_get_expire_time(Timer* instance) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
furi_assert(instance); tt_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance; TimerHandle_t hTimer = (TimerHandle_t)instance;
return (uint32_t)xTimerGetExpiryTime(hTimer); 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; BaseType_t ret = pdFAIL;
if (furi_kernel_is_irq()) { if (tt_kernel_is_irq()) {
ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL); ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL);
} else { } 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) { void tt_timer_set_thread_priority(TimerThreadPriority priority) {
furi_assert(!furi_kernel_is_irq()); tt_assert(!tt_kernel_is_irq());
TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); 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); vTaskPrioritySet(task_handle, configTIMER_TASK_PRIORITY);
} else if (priority == FuriTimerThreadPriorityElevated) { } else if (priority == TimerThreadPriorityElevated) {
vTaskPrioritySet(task_handle, configMAX_PRIORITIES - 1); vTaskPrioritySet(task_handle, configMAX_PRIORITIES - 1);
} else { } 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 #pragma once
#include <m-core.h> #include <m-core.h>
@ -15,77 +11,77 @@ extern "C" {
#endif #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 // Constructors
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
/** /**
* @brief Allocate new FuriString. * @brief Allocate new TtString.
* @return FuriString* * @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. * Allocate & Set the string a to the string.
* @param source * @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. * Allocate & Set the string a to the C string.
* @param cstr_source * @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. * Initialize and set a string to the given formatted value.
* @param format * @param format
* @param ... * @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))); _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. * Initialize and set a string to the given formatted value.
* @param format * @param format
* @param args * @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. * Allocate the string, set it to the other one, and destroy the other one.
* @param source * @param source
* @return FuriString* * @return TtString*
*/ */
FuriString* furi_string_alloc_move(FuriString* source); TtString* tt_string_alloc_move(TtString* source);
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Destructors // Destructors
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
/** /**
* @brief Free FuriString. * @brief Free TtString.
* @param string * @param string
*/ */
void furi_string_free(FuriString* string); void tt_string_free(TtString* string);
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// String memory management // String memory management
@ -97,14 +93,14 @@ void furi_string_free(FuriString* string);
* @param string * @param string
* @param size * @param size
*/ */
void furi_string_reserve(FuriString* string, size_t size); void tt_string_reserve(TtString* string, size_t size);
/** /**
* @brief Reset string. * @brief Reset string.
* Make the string empty. * Make the string empty.
* @param s * @param s
*/ */
void furi_string_reset(FuriString* string); void tt_string_reset(TtString* string);
/** /**
* @brief Swap two strings. * @brief Swap two strings.
@ -112,7 +108,7 @@ void furi_string_reset(FuriString* string);
* @param string_1 * @param string_1
* @param string_2 * @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. * @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_1
* @param string_2 * @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. * @brief Compute a hash for the string.
* @param string * @param string
* @return size_t * @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) * @brief Get string size (usually length, but not for UTF-8)
* @param string * @param string
* @return size_t * @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 * @brief Check that string is empty or not
* @param string * @param string
* @return bool * @return bool
*/ */
bool furi_string_empty(const FuriString* string); bool tt_string_empty(const TtString* string);
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Getters // Getters
@ -154,14 +150,14 @@ bool furi_string_empty(const FuriString* string);
* @param index * @param index
* @return char * @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. * @brief Return the string view a classic C string.
* @param string * @param string
* @return const char* * @return const char*
*/ */
const char* furi_string_get_cstr(const FuriString* string); const char* tt_string_get_cstr(const TtString* string);
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Setters // Setters
@ -173,7 +169,7 @@ const char* furi_string_get_cstr(const FuriString* string);
* @param string * @param string
* @param source * @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. * @brief Set the string to the other C string.
@ -181,7 +177,7 @@ void furi_string_set(FuriString* string, FuriString* source);
* @param string * @param string
* @param source * @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. * @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 source
* @param length * @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. * @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 index
* @param c * @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. * @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 offset
* @param length * @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 * @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 ... * @param ...
* @return int * @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))); _ATTRIBUTE((__format__(__printf__, 2, 3)));
/** /**
@ -225,7 +221,7 @@ int furi_string_printf(FuriString* string, const char format[], ...)
* @param args * @param args
* @return int * @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 // Appending
@ -236,7 +232,7 @@ int furi_string_vprintf(FuriString* string, const char format[], va_list args);
* @param string * @param string
* @param c * @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. * @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_1
* @param string_2 * @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. * @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 string_1
* @param cstring_2 * @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. * @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 ... * @param ...
* @return int * @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))); _ATTRIBUTE((__format__(__printf__, 2, 3)));
/** /**
@ -271,7 +267,7 @@ int furi_string_cat_printf(FuriString* string, const char format[], ...)
* @param args * @param args
* @return int * @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 // Comparators
@ -283,7 +279,7 @@ int furi_string_cat_vprintf(FuriString* string, const char format[], va_list arg
* @param string_2 * @param string_2
* @return int * @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. * @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 * @param cstring_2
* @return int * @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. * @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 * @param string_2
* @return int * @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. * @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 * @param cstring_2
* @return int * @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 // Search
@ -324,7 +320,7 @@ int furi_string_cmpi_str(const FuriString* string_1, const char cstring_2[]);
* @param start * @param start
* @return size_t * @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. * @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 * @param start
* @return size_t * @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. * @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 * @param start
* @return size_t * @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. * @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 * @param start
* @return size_t * @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 // Equality
@ -368,7 +364,7 @@ size_t furi_string_search_rchar(const FuriString* string, char c, size_t start);
* @param string_2 * @param string_2
* @return bool * @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. * @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 * @param cstring_2
* @return bool * @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 // Replace
@ -389,7 +385,7 @@ bool furi_string_equal_str(const FuriString* string_1, const char cstring_2[]);
* @param len * @param len
* @param replace * @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. * @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 * @return size_t
*/ */
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. * @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 * @param start
* @return size_t * @return size_t
*/ */
size_t furi_string_replace_str( size_t tt_string_replace_str(
FuriString* string, TtString* string,
const char needle[], const char needle[],
const char replace[], const char replace[],
size_t start size_t start
@ -427,10 +423,10 @@ size_t furi_string_replace_str(
* @param needle * @param needle
* @param replace * @param replace
*/ */
void furi_string_replace_all( void tt_string_replace_all(
FuriString* string, TtString* string,
const FuriString* needle, const TtString* needle,
const FuriString* replace const TtString* replace
); );
/** /**
@ -439,7 +435,7 @@ void furi_string_replace_all(
* @param needle * @param needle
* @param replace * @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 // Start / End tests
@ -451,7 +447,7 @@ void furi_string_replace_all_str(FuriString* string, const char needle[], const
* @param start * @param start
* @return bool * @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. * @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 * @param start
* @return bool * @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. * @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 * @param end
* @return bool * @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. * @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 * @param end
* @return bool * @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 // Trim
@ -486,30 +482,30 @@ bool furi_string_end_with_str(const FuriString* string, const char end[]);
* @param string * @param string
* @param index * @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. * @brief Trim the string right from the 'index' position to the last position.
* @param string * @param string
* @param index * @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. * @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 string
* @param index * @param index
* @param size * @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"). * @brief Trim a string from the given set of characters (default is " \n\r\t").
* @param string * @param string
* @param chars * @param chars
*/ */
void furi_string_trim(FuriString* string, const char chars[]); void tt_string_trim(TtString* string, const char chars[]);
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// UTF8 // UTF8
@ -518,43 +514,43 @@ void furi_string_trim(FuriString* string, const char chars[]);
/** /**
* @brief An unicode value. * @brief An unicode value.
*/ */
typedef unsigned int FuriStringUnicodeValue; typedef unsigned int TtStringUnicodeValue;
/** /**
* @brief Compute the length in UTF8 characters in the string. * @brief Compute the length in UTF8 characters in the string.
* @param string * @param string
* @return size_t * @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. * @brief Push unicode into string, encoding it in UTF8.
* @param string * @param string
* @param unicode * @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. * @brief State of the UTF8 decoding machine state.
*/ */
typedef enum { typedef enum {
FuriStringUTF8StateStarting, TtStringUTF8StateStarting,
FuriStringUTF8StateDecoding1, TtStringUTF8StateDecoding1,
FuriStringUTF8StateDecoding2, TtStringUTF8StateDecoding2,
FuriStringUTF8StateDecoding3, TtStringUTF8StateDecoding3,
FuriStringUTF8StateError TtStringUTF8StateError
} FuriStringUTF8State; } TtStringUTF8State;
/** /**
* @brief Main generic UTF8 decoder. * @brief Main generic UTF8 decoder.
* It takes a character, and the previous state and the previous value of the unicode value. * 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. * 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 c
* @param state * @param state
* @param unicode * @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 // Lasciate ogne speranza, voi chentrate
@ -570,131 +566,131 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico
/** /**
* @brief Select for 1 argument * @brief Select for 1 argument
*/ */
#define FURI_STRING_SELECT1(func1, func2, a) \ #define TT_STRING_SELECT1(func1, func2, a) \
_Generic((a), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)(a) _Generic((a), char*: func2, const char*: func2, TtString*: func1, const TtString*: func1)(a)
/** /**
* @brief Select for 2 arguments * @brief Select for 2 arguments
*/ */
#define FURI_STRING_SELECT2(func1, func2, a, b) \ #define TT_STRING_SELECT2(func1, func2, a, b) \
_Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)(a, b) _Generic((b), char*: func2, const char*: func2, TtString*: func1, const TtString*: func1)(a, b)
/** /**
* @brief Select for 3 arguments * @brief Select for 3 arguments
*/ */
#define FURI_STRING_SELECT3(func1, func2, a, b, c) \ #define TT_STRING_SELECT3(func1, func2, a, b, c) \
_Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)(a, b, c) _Generic((b), char*: func2, const char*: func2, TtString*: func1, const TtString*: func1)(a, b, c)
/** /**
* @brief Select for 4 arguments * @brief Select for 4 arguments
*/ */
#define FURI_STRING_SELECT4(func1, func2, a, b, c, d) \ #define TT_STRING_SELECT4(func1, func2, a, b, c, d) \
_Generic((b), char*: func2, const char*: func2, FuriString*: func1, const FuriString*: func1)(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) * ([c]string)
*/ */
#define furi_string_alloc_set(a) \ #define tt_string_alloc_set(a) \
FURI_STRING_SELECT1(furi_string_alloc_set, furi_string_alloc_set_str, a) TT_STRING_SELECT1(tt_string_alloc_set, tt_string_alloc_set_str, a)
/** /**
* @brief Set the string content to string (or C string). * @brief Set the string content to string (or C string).
* (string, [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. * @brief Compare string with string (or C string) and return the sort order.
* Note: doesn't work with UTF-8 strings. * Note: doesn't work with UTF-8 strings.
* (string, [c]string) * (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. * @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. * Note: doesn't work with UTF-8 strings.
* (string, [c]string) * (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). * @brief Test if the string is equal to the string (or C string).
* (string, [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. * @brief Replace all occurrences of string into string (or C string to another C string) in a string.
* (string, [c]string, [c]string) * (string, [c]string, [c]string)
*/ */
#define furi_string_replace_all(a, b, c) \ #define tt_string_replace_all(a, b, c) \
FURI_STRING_SELECT3(furi_string_replace_all, furi_string_replace_all_str, 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 * @brief Search for a string (or C string) in a string
* (string, [c]string[, start=0]) * (string, [c]string[, start=0])
*/ */
#define furi_string_search(...) \ #define tt_string_search(...) \
M_APPLY( \ M_APPLY( \
FURI_STRING_SELECT3, \ TT_STRING_SELECT3, \
furi_string_search, \ tt_string_search, \
furi_string_search_str, \ tt_string_search_str, \
M_DEFAULT_ARGS(3, (0), __VA_ARGS__) \ M_DEFAULT_ARGS(3, (0), __VA_ARGS__) \
) )
/** /**
* @brief Search for a C string in a string * @brief Search for a C string in a string
* (string, cstring[, start=0]) * (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). * @brief Test if the string starts with the given string (or C string).
* (string, [c]string) * (string, [c]string)
*/ */
#define furi_string_start_with(a, b) \ #define tt_string_start_with(a, b) \
FURI_STRING_SELECT2(furi_string_start_with, furi_string_start_with_str, 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). * @brief Test if the string ends with the given string (or C string).
* (string, [c]string) * (string, [c]string)
*/ */
#define furi_string_end_with(a, b) \ #define tt_string_end_with(a, b) \
FURI_STRING_SELECT2(furi_string_end_with, furi_string_end_with_str, 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. * @brief Append a string (or C string) to the string.
* (string, [c]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"). * @brief Trim a string from the given set of characters (default is " \n\r\t").
* (string[, set=" \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. * @brief Search for a character in a string.
* (string, character[, start=0]) * (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. * @brief Reverse Search for a character in a string.
* (string, character[, start=0]) * (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. * @brief Replace a string to another string (or C string to another C string) in a string.
* (string, [c]string, [c]string[, start=0]) * (string, [c]string, [c]string[, start=0])
*/ */
#define furi_string_replace(...) \ #define tt_string_replace(...) \
M_APPLY( \ M_APPLY( \
FURI_STRING_SELECT4, \ TT_STRING_SELECT4, \
furi_string_replace, \ tt_string_replace, \
furi_string_replace_str, \ tt_string_replace_str, \
M_DEFAULT_ARGS(4, (0), __VA_ARGS__) \ 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. * @brief Replace a C string to another C string in a string.
* (string, cstring, cstring[, start=0]) * (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(F_STR_INIT), \
INIT_SET(F_STR_INIT_SET), \ INIT_SET(F_STR_INIT_SET), \
SET(furi_string_set), \ SET(tt_string_set), \
INIT_MOVE(F_STR_INIT_MOVE), \ INIT_MOVE(F_STR_INIT_MOVE), \
MOVE(furi_string_move), \ MOVE(tt_string_move), \
SWAP(furi_string_swap), \ SWAP(tt_string_swap), \
RESET(furi_string_reset), \ RESET(tt_string_reset), \
EMPTY_P(furi_string_empty), \ EMPTY_P(tt_string_empty), \
CLEAR(furi_string_free), \ CLEAR(tt_string_free), \
HASH(furi_string_hash), \ HASH(tt_string_hash), \
EQUAL(furi_string_equal), \ EQUAL(tt_string_equal), \
CMP(furi_string_cmp), \ CMP(tt_string_cmp), \
TYPE(FuriString*)) TYPE(TtString*))
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -19,7 +19,7 @@ idf_component_register(
esp_wifi esp_wifi
driver driver
fatfs fatfs
furi tactility-core
mlib mlib
nvs_flash nvs_flash
spiffs 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) { 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* list = (lv_obj_t*)parent;
lv_obj_t* btn = lv_list_add_btn(list, LV_SYMBOL_FILE, manifest->name); 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); 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_obj_center(list);
lv_list_add_text(list, "System"); 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"); 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 = { const AppManifest desktop_app = {

View File

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

View File

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

View File

@ -11,8 +11,8 @@ extern "C" {
#endif #endif
typedef struct { typedef struct {
FuriPubSubSubscription* wifi_subscription; PubSubSubscription* wifi_subscription;
FuriMutex* mutex; Mutex* mutex;
WifiConnectState state; WifiConnectState state;
WifiConnectView view; WifiConnectView view;
bool view_enabled; 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); lv_obj_add_event_cb(view->connect_button, &on_connect, LV_EVENT_CLICKED, wifi);
// Init from app parameters // Init from app parameters
Bundle* _Nullable bundle = app_get_parameters(app); Bundle* _Nullable bundle = tt_app_get_parameters(app);
if (bundle) { if (bundle) {
char* ssid; 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); lv_textarea_set_text(view->ssid_textarea, ssid);
} }
char* password; 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); lv_textarea_set_text(view->password_textarea, password);
} }
} }

View File

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

View File

@ -9,8 +9,8 @@ extern "C" {
#endif #endif
typedef struct { typedef struct {
FuriPubSubSubscription* wifi_subscription; PubSubSubscription* wifi_subscription;
FuriMutex* mutex; Mutex* mutex;
WifiManageState state; WifiManageState state;
WifiManageView view; WifiManageView view;
bool view_enabled; 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 // We get the SSID from the button label because it's safer than alloc'ing
// our own and passing it as the event data // our own and passing it as the event data
const char* ssid = lv_label_get_text(label); 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; WifiManageBindings* bindings = (WifiManageBindings*)event->user_data;
bindings->on_connect_ssid(ssid); bindings->on_connect_ssid(ssid);
} }

View File

@ -11,7 +11,7 @@ Hardware tt_hardware_init(const HardwareConfig _Nonnull* config) {
config->bootstrap(); 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(); DisplayDriver display_driver = config->display_driver();
ESP_LOGI(TAG, "display with driver %s", display_driver.name); ESP_LOGI(TAG, "display with driver %s", display_driver.name);
DisplayDevice* display = tt_display_device_alloc(&display_driver); 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* tt_display_device_alloc(DisplayDriver _Nonnull* driver) {
DisplayDevice _Nonnull* display = malloc(sizeof(DisplayDevice)); DisplayDevice _Nonnull* display = malloc(sizeof(DisplayDevice));
memset(display, 0, sizeof(DisplayDevice)); memset(display, 0, sizeof(DisplayDevice));
furi_check(driver->create_display_device(display), "failed to create display"); tt_check(driver->create_display_device(display), "failed to create display");
furi_check(display->io_handle != NULL); tt_check(display->io_handle != NULL);
furi_check(display->display_handle != NULL); tt_check(display->display_handle != NULL);
furi_check(display->horizontal_resolution != 0); tt_check(display->horizontal_resolution != 0);
furi_check(display->vertical_resolution != 0); tt_check(display->vertical_resolution != 0);
furi_check(display->draw_buffer_height > 0); tt_check(display->draw_buffer_height > 0);
furi_check(display->bits_per_pixel > 0); tt_check(display->bits_per_pixel > 0);
return display; return display;
} }

View File

@ -14,7 +14,7 @@ Lvgl tt_graphics_init(Hardware _Nonnull* hardware) {
.timer_period_ms = 5 .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; DisplayDevice _Nonnull* display = hardware->display;
// Add 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); 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; lv_indev_t _Nullable* touch_indev = NULL;
@ -49,7 +49,7 @@ Lvgl tt_graphics_init(Hardware _Nonnull* hardware) {
.handle = hardware->touch->touch_handle, .handle = hardware->touch->touch_handle,
}; };
touch_indev = lvgl_port_add_touch(&touch_cfg); 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) { 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); esp_err_t ret = esp_vfs_spiffs_register(conf);
if (ret != ESP_OK) { if (ret != ESP_OK) {
if (ret == ESP_FAIL) { 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) { } 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 { } 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; 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; size_t total = -1, used = 0;
ret = esp_spiffs_info(NULL, &total, &used); ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) { 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 { } 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; return ESP_OK;
} }

View File

@ -1,10 +1,10 @@
#include "gui_i.h" #include "gui_i.h"
#include "check.h" #include "check.h"
#include "core_extra_defines.h"
#include "esp_lvgl_port.h" #include "esp_lvgl_port.h"
#include "furi_extra_defines.h"
#include "log.h"
#include "kernel.h" #include "kernel.h"
#include "log.h"
#define TAG "gui" #define TAG "gui"
@ -17,16 +17,16 @@ static Gui* gui = NULL;
Gui* gui_alloc() { Gui* gui_alloc() {
Gui* instance = malloc(sizeof(Gui)); Gui* instance = malloc(sizeof(Gui));
memset(instance, 0, sizeof(Gui)); memset(instance, 0, sizeof(Gui));
furi_check(instance != NULL); tt_check(instance != NULL);
instance->thread = furi_thread_alloc_ex( instance->thread = tt_thread_alloc_ex(
"gui", "gui",
4096, // Last known minimum was 2800 for launching desktop 4096, // Last known minimum was 2800 for launching desktop
&gui_main, &gui_main,
NULL 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(); instance->lvgl_parent = lv_scr_act();
lvgl_port_unlock(); lvgl_port_unlock();
@ -34,33 +34,33 @@ Gui* gui_alloc() {
} }
void gui_free(Gui* instance) { void gui_free(Gui* instance) {
furi_assert(instance != NULL); tt_assert(instance != NULL);
furi_thread_free(instance->thread); tt_thread_free(instance->thread);
furi_mutex_free(instance->mutex); tt_mutex_free(instance->mutex);
free(instance); free(instance);
} }
void gui_lock() { void gui_lock() {
furi_assert(gui); tt_assert(gui);
furi_assert(gui->mutex); tt_assert(gui->mutex);
furi_check(furi_mutex_acquire(gui->mutex, 1000 / portTICK_PERIOD_MS) == FuriStatusOk); tt_check(tt_mutex_acquire(gui->mutex, 1000 / portTICK_PERIOD_MS) == TtStatusOk);
} }
void gui_unlock() { void gui_unlock() {
furi_assert(gui); tt_assert(gui);
furi_assert(gui->mutex); tt_assert(gui->mutex);
furi_check(furi_mutex_release(gui->mutex) == FuriStatusOk); tt_check(tt_mutex_release(gui->mutex) == TtStatusOk);
} }
void gui_request_draw() { void gui_request_draw() {
furi_assert(gui); tt_assert(gui);
FuriThreadId thread_id = furi_thread_get_id(gui->thread); ThreadId thread_id = tt_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW); tt_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
} }
void gui_show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) { void gui_show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) {
gui_lock(); 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->app_view_port = view_port_alloc(app, on_show, on_hide);
gui_unlock(); gui_unlock();
gui_request_draw(); gui_request_draw();
@ -69,7 +69,7 @@ void gui_show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on
void gui_hide_app() { void gui_hide_app() {
gui_lock(); gui_lock();
ViewPort* view_port = gui->app_view_port; ViewPort* view_port = gui->app_view_port;
furi_check(view_port != NULL); tt_check(view_port != NULL);
view_port_hide(view_port); view_port_hide(view_port);
view_port_free(view_port); view_port_free(view_port);
gui->app_view_port = NULL; gui->app_view_port = NULL;
@ -78,23 +78,23 @@ void gui_hide_app() {
static int32_t gui_main(void* p) { static int32_t gui_main(void* p) {
UNUSED(p); UNUSED(p);
furi_check(gui); tt_check(gui);
Gui* local_gui = gui; Gui* local_gui = gui;
while (1) { while (1) {
uint32_t flags = furi_thread_flags_wait( uint32_t flags = tt_thread_flags_wait(
GUI_THREAD_FLAG_ALL, GUI_THREAD_FLAG_ALL,
FuriFlagWaitAny, TtFlagWaitAny,
FuriWaitForever TtWaitForever
); );
// Process and dispatch draw call // Process and dispatch draw call
if (flags & GUI_THREAD_FLAG_DRAW) { 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); gui_redraw(local_gui);
} }
if (flags & GUI_THREAD_FLAG_EXIT) { if (flags & GUI_THREAD_FLAG_EXIT) {
furi_thread_flags_clear(GUI_THREAD_FLAG_EXIT); tt_thread_flags_clear(GUI_THREAD_FLAG_EXIT);
break; break;
} }
} }
@ -109,8 +109,8 @@ static void gui_start(Service service) {
gui = gui_alloc(); gui = gui_alloc();
furi_thread_set_priority(gui->thread, FuriThreadPriorityNormal); tt_thread_set_priority(gui->thread, ThreadPriorityNormal);
furi_thread_start(gui->thread); tt_thread_start(gui->thread);
} }
static void gui_stop(Service service) { static void gui_stop(Service service) {
@ -118,9 +118,9 @@ static void gui_stop(Service service) {
gui_lock(); gui_lock();
FuriThreadId thread_id = furi_thread_get_id(gui->thread); ThreadId thread_id = tt_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT); tt_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
furi_thread_join(gui->thread); tt_thread_join(gui->thread);
gui_unlock(); 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); tt_lv_obj_set_style_bg_blacken(vertical_container);
// TODO: Move statusbar into separate ViewPort // TODO: Move statusbar into separate ViewPort
AppFlags flags = app_get_flags(app); AppFlags flags = tt_app_get_flags(app);
if (flags.show_statusbar) { if (flags.show_statusbar) {
tt_lv_statusbar_create(vertical_container); tt_lv_statusbar_create(vertical_container);
} }
if (flags.show_toolbar) { if (flags.show_toolbar) {
const AppManifest* manifest = app_get_manifest(app); const AppManifest* manifest = tt_app_get_manifest(app);
if (manifest != NULL) { if (manifest != NULL) {
// TODO: Keep toolbar on app level so app can update it (app_set_toolbar() etc?) // TODO: Keep toolbar on app level so app can update it (app_set_toolbar() etc?)
Toolbar toolbar = { Toolbar toolbar = {
@ -51,22 +51,22 @@ static lv_obj_t* create_app_views(lv_obj_t* parent, App app) {
} }
void gui_redraw(Gui* gui) { void gui_redraw(Gui* gui) {
furi_assert(gui); tt_assert(gui);
// Lock GUI and LVGL // Lock GUI and LVGL
gui_lock(); gui_lock();
furi_check(lvgl_port_lock(100)); tt_check(lvgl_port_lock(100));
lv_obj_clean(gui->lvgl_parent); lv_obj_clean(gui->lvgl_parent);
if (gui->app_view_port != NULL) { if (gui->app_view_port != NULL) {
ViewPort* view_port = gui->app_view_port; ViewPort* view_port = gui->app_view_port;
furi_assert(view_port); tt_assert(view_port);
App app = gui->app_view_port->app; App app = gui->app_view_port->app;
lv_obj_t* container = create_app_views(gui->lvgl_parent, app); lv_obj_t* container = create_app_views(gui->lvgl_parent, app);
view_port_show(view_port, container); view_port_show(view_port, container);
} else { } else {
FURI_LOG_W(TAG, "nothing to draw"); TT_LOG_W(TAG, "nothing to draw");
} }
// Unlock GUI and LVGL // Unlock GUI and LVGL

View File

@ -16,8 +16,8 @@
/** Gui structure */ /** Gui structure */
struct Gui { struct Gui {
// Thread and lock // Thread and lock
FuriThread* thread; Thread* thread;
FuriMutex* mutex; Mutex* mutex;
// Layers and Canvas // Layers and Canvas
lv_obj_t* lvgl_parent; 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) { void view_port_free(ViewPort* view_port) {
furi_assert(view_port); tt_assert(view_port);
free(view_port); free(view_port);
} }
void view_port_show(ViewPort* view_port, lv_obj_t* parent) { void view_port_show(ViewPort* view_port, lv_obj_t* parent) {
furi_assert(view_port); tt_assert(view_port);
furi_assert(parent); tt_assert(parent);
if (view_port->on_show) { if (view_port->on_show) {
tt_lv_obj_set_style_no_padding(parent); tt_lv_obj_set_style_no_padding(parent);
view_port->on_show(view_port->app, 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) { void view_port_hide(ViewPort* view_port) {
furi_assert(view_port); tt_assert(view_port);
if (view_port->on_hide) { if (view_port->on_hide) {
view_port->on_hide(view_port->app); view_port->on_hide(view_port->app);
} }

View File

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

View File

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

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