Rename furi to tactility-core (#10)
* renamed module * renamed code * more renames * cleanup
This commit is contained in:
parent
64a01df750
commit
069416eee5
@ -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")
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
|
||||||
@ -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);
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
|
|
||||||
} Context;
|
|
||||||
@ -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);
|
|
||||||
@ -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
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#define FURI_CONFIG_THREAD_MAX_PRIORITIES (32)
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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));
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
8
components/tactility-core/README.md
Normal file
8
components/tactility-core/README.md
Normal 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)
|
||||||
20
components/tactility-core/src/api_lock.h
Normal file
20
components/tactility-core/src/api_lock.h
Normal 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);
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
@ -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);
|
||||||
});
|
});
|
||||||
20
components/tactility-core/src/app_manifest_registry.h
Normal file
20
components/tactility-core/src/app_manifest_registry.h
Normal 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
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
34
components/tactility-core/src/bundle.h
Normal file
34
components/tactility-core/src/bundle.h
Normal 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
|
||||||
38
components/tactility-core/src/check.c
Normal file
38
components/tactility-core/src/check.c
Normal 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();
|
||||||
|
}
|
||||||
@ -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,23 +64,27 @@ 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)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
@ -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");
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
42
components/tactility-core/src/core_defines.h
Normal file
42
components/tactility-core/src/core_defines.h
Normal 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
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
44
components/tactility-core/src/core_types.h
Normal file
44
components/tactility-core/src/core_types.h
Normal 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
|
||||||
@ -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) {
|
||||||
22
components/tactility-core/src/critical.h
Normal file
22
components/tactility-core/src/critical.h
Normal 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);
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
67
components/tactility-core/src/event_flag.h
Normal file
67
components/tactility-core/src/event_flag.h
Normal 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
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
94
components/tactility-core/src/message_queue.h
Normal file
94
components/tactility-core/src/message_queue.h
Normal 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
|
||||||
@ -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 */
|
||||||
62
components/tactility-core/src/mutex.h
Normal file
62
components/tactility-core/src/mutex.h
Normal 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
|
||||||
93
components/tactility-core/src/pubsub.c
Normal file
93
components/tactility-core/src/pubsub.c
Normal 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);
|
||||||
|
}
|
||||||
68
components/tactility-core/src/pubsub.h
Normal file
68
components/tactility-core/src/pubsub.h
Normal 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
|
||||||
@ -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);
|
||||||
54
components/tactility-core/src/semaphore.h
Normal file
54
components/tactility-core/src/semaphore.h
Normal 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
|
||||||
62
components/tactility-core/src/service.c
Normal file
62
components/tactility-core/src/service.c
Normal 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
|
||||||
18
components/tactility-core/src/service.h
Normal file
18
components/tactility-core/src/service.h
Normal 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
|
||||||
@ -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);
|
||||||
@ -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" {
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
25
components/tactility-core/src/service_registry.h
Normal file
25
components/tactility-core/src/service_registry.h
Normal 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
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
@ -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"
|
||||||
3
components/tactility-core/src/tactility_core_config.h
Normal file
3
components/tactility-core/src/tactility_core_config.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define TT_CONFIG_THREAD_MAX_PRIORITIES (32)
|
||||||
599
components/tactility-core/src/thread.c
Normal file
599
components/tactility-core/src/thread.c
Normal 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;
|
||||||
|
}
|
||||||
334
components/tactility-core/src/thread.h
Normal file
334
components/tactility-core/src/thread.h
Normal 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
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
106
components/tactility-core/src/timer.h
Normal file
106
components/tactility-core/src/timer.h
Normal 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
|
||||||
304
components/tactility-core/src/tt_string.c
Normal file
304
components/tactility-core/src/tt_string.c
Normal 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;
|
||||||
|
}
|
||||||
@ -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 ch’entrate
|
// Lasciate ogne speranza, voi ch’entrate
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
|
|||||||
@ -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 = {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 = {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 = {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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
Loading…
x
Reference in New Issue
Block a user