Wifi support and much more (#9)
* add wifi service * updates for service/app registry changes * wifi wip * basic wifi functionality radio on/off is working scanning state is working * fix for wifi switch state * reduce singleton usage * various improvements * improved error handling for low memory issues * working scanning * various improvements * various improvements and fixes + added auto-start support in Config * allow hardwareconfig customizations * fix for rgb format * increased lvgl fps 17ms works but 16ms makes the touch events hang for some reason * layout improvements * wip on multi-screen view * basic connection dialog * more connection logic * created proper app stack and lifecycle * cleanup * cleanup * cleanup lv widgets * proper toolbar implementation * split up wifi apps * wip * revert naming * wip * temp fix for internal disconnect * added bundle * app/service vs appdata/servicedata * working wifi connect parameters
This commit is contained in:
committed by
GitHub
parent
83e226f696
commit
64a01df750
+120
-14
@@ -1,22 +1,128 @@
|
||||
#include "app_i.h"
|
||||
#include "furi_core.h"
|
||||
#include "log.h"
|
||||
#include "furi_string.h"
|
||||
|
||||
#define TAG "app"
|
||||
#include <stdio.h>
|
||||
|
||||
App* furi_app_alloc(const AppManifest* _Nonnull manifest) {
|
||||
App app = {
|
||||
static AppFlags app_get_flags_default(AppType type);
|
||||
|
||||
// region Alloc/free
|
||||
|
||||
App app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters) {
|
||||
AppData* data = malloc(sizeof(AppData));
|
||||
*data = (AppData) {
|
||||
.mutex = furi_mutex_alloc(FuriMutexTypeRecursive),
|
||||
.state = APP_STATE_INITIAL,
|
||||
.flags = app_get_flags_default(manifest->type),
|
||||
.manifest = manifest,
|
||||
.context = {
|
||||
.data = NULL
|
||||
}
|
||||
.parameters = parameters,
|
||||
.data = NULL
|
||||
};
|
||||
App* app_ptr = malloc(sizeof(App));
|
||||
return memcpy(app_ptr, &app, sizeof(App));
|
||||
return (App*)data;
|
||||
}
|
||||
|
||||
void furi_app_free(App* app) {
|
||||
furi_assert(app);
|
||||
free(app);
|
||||
void app_free(App app) {
|
||||
AppData* data = (AppData*)app;
|
||||
if (data->parameters) {
|
||||
bundle_free(data->parameters);
|
||||
}
|
||||
furi_mutex_free(data->mutex);
|
||||
free(data);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Internal
|
||||
|
||||
static void app_lock(AppData* data) {
|
||||
furi_mutex_acquire(data->mutex, FuriMutexTypeRecursive);
|
||||
}
|
||||
|
||||
static void app_unlock(AppData* data) {
|
||||
furi_mutex_release(data->mutex);
|
||||
}
|
||||
|
||||
static AppFlags app_get_flags_default(AppType type) {
|
||||
static const AppFlags DEFAULT_DESKTOP_FLAGS = {
|
||||
.show_toolbar = false,
|
||||
.show_statusbar = true
|
||||
};
|
||||
|
||||
static const AppFlags DEFAULT_APP_FLAGS = {
|
||||
.show_toolbar = true,
|
||||
.show_statusbar = true
|
||||
};
|
||||
|
||||
return type == AppTypeDesktop
|
||||
? DEFAULT_DESKTOP_FLAGS
|
||||
: DEFAULT_APP_FLAGS;
|
||||
}
|
||||
|
||||
// endregion Internal
|
||||
|
||||
// region Public getters & setters
|
||||
|
||||
void app_set_state(App app, AppState state) {
|
||||
AppData* data = (AppData*)app;
|
||||
app_lock(data);
|
||||
data->state = state;
|
||||
app_unlock(data);
|
||||
}
|
||||
|
||||
AppState app_get_state(App app) {
|
||||
AppData* data = (AppData*)app;
|
||||
app_lock(data);
|
||||
AppState state = data->state;
|
||||
app_unlock(data);
|
||||
return state;
|
||||
}
|
||||
|
||||
const AppManifest* app_get_manifest(App app) {
|
||||
AppData* data = (AppData*)app;
|
||||
// No need to lock const data;
|
||||
return data->manifest;
|
||||
}
|
||||
|
||||
AppFlags app_get_flags(App app) {
|
||||
AppData* data = (AppData*)app;
|
||||
app_lock(data);
|
||||
AppFlags flags = data->flags;
|
||||
app_unlock(data);
|
||||
return flags;
|
||||
}
|
||||
|
||||
void app_set_flags(App app, AppFlags flags) {
|
||||
AppData* data = (AppData*)app;
|
||||
app_lock(data);
|
||||
data->flags = flags;
|
||||
app_unlock(data);
|
||||
}
|
||||
|
||||
void* app_get_data(App app) {
|
||||
AppData* data = (AppData*)app;
|
||||
app_lock(data);
|
||||
void* value = data->data;
|
||||
app_unlock(data);
|
||||
return value;
|
||||
}
|
||||
|
||||
void app_set_data(App app, void* value) {
|
||||
AppData* data = (AppData*)app;
|
||||
app_lock(data);
|
||||
data->data = value;
|
||||
app_unlock(data);
|
||||
}
|
||||
|
||||
/** TODO: Make this thread-safe.
|
||||
* In practice, the bundle is writeable, so someone could be writing to it
|
||||
* while it is being accessed from another thread.
|
||||
* Consider creating MutableBundle vs Bundle.
|
||||
* Consider not exposing bundle, but expose `app_get_bundle_int(key)` methods with locking in it.
|
||||
*/
|
||||
Bundle* _Nullable app_get_parameters(App app) {
|
||||
AppData* data = (AppData*)app;
|
||||
app_lock(data);
|
||||
Bundle* bundle = data->parameters;
|
||||
app_unlock(data);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
// endregion Public getters & setters
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "app_manifest.h"
|
||||
#include "bundle.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
APP_STATE_INITIAL, // App is being activated in loader
|
||||
APP_STATE_STARTED, // App is in memory
|
||||
APP_STATE_SHOWING, // App view is created
|
||||
APP_STATE_HIDING, // App view is destroyed
|
||||
APP_STATE_STOPPED // App is not in memory
|
||||
} AppState;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
bool show_statusbar : 1;
|
||||
bool show_toolbar : 1;
|
||||
};
|
||||
unsigned char flags;
|
||||
} AppFlags;
|
||||
|
||||
typedef void* App;
|
||||
|
||||
/** @brief Create an app
|
||||
* @param manifest
|
||||
* @param parameters optional bundle. memory ownership is transferred to App
|
||||
* @return
|
||||
*/
|
||||
App app_alloc(const AppManifest* manifest, Bundle* _Nullable parameters);
|
||||
void app_free(App app);
|
||||
|
||||
void app_set_state(App app, AppState state);
|
||||
AppState app_get_state(App app);
|
||||
|
||||
const AppManifest* app_get_manifest(App app);
|
||||
|
||||
AppFlags app_get_flags(App app);
|
||||
void app_set_flags(App app, AppFlags flags);
|
||||
|
||||
void* _Nullable app_get_data(App app);
|
||||
void app_set_data(App app, void* data);
|
||||
|
||||
Bundle* _Nullable app_get_parameters(App app);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,19 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "app.h"
|
||||
|
||||
#include "app_manifest.h"
|
||||
#include "thread.h"
|
||||
#include "context.h"
|
||||
#include "mutex.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
const AppManifest* manifest;
|
||||
Context context;
|
||||
} App;
|
||||
|
||||
App* furi_app_alloc(const AppManifest* _Nonnull manifest);
|
||||
void furi_app_free(App* _Nonnull app);
|
||||
AppState state;
|
||||
AppFlags flags;
|
||||
/** @brief Optional parameters to start the app with
|
||||
* When these are stored in the app struct, the struct takes ownership.
|
||||
* Do not mutate after app creation.
|
||||
*/
|
||||
Bundle* _Nullable parameters;
|
||||
/** @brief @brief Contextual data related to the running app's instance
|
||||
* The app can attach its data to this.
|
||||
* The lifecycle is determined by the on_start and on_stop methods in the AppManifest.
|
||||
* These manifest methods can optionally allocate/free data that is attached here.
|
||||
*/
|
||||
void* _Nullable data;
|
||||
} AppData;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "context.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -9,16 +8,19 @@ extern "C" {
|
||||
|
||||
// Forward declarations
|
||||
typedef struct _lv_obj_t lv_obj_t;
|
||||
typedef void* App;
|
||||
|
||||
typedef enum {
|
||||
AppTypeDesktop,
|
||||
AppTypeSystem,
|
||||
AppTypeSettings,
|
||||
AppTypeUser
|
||||
} AppType;
|
||||
|
||||
typedef void (*AppOnStart)(Context* context);
|
||||
typedef void (*AppOnStop)(Context* context);
|
||||
typedef void (*AppOnShow)(Context* context, lv_obj_t* parent);
|
||||
typedef void (*AppOnStart)(App app);
|
||||
typedef void (*AppOnStop)(App app);
|
||||
typedef void (*AppOnShow)(App app, lv_obj_t* parent);
|
||||
typedef void (*AppOnHide)(App app);
|
||||
|
||||
typedef struct {
|
||||
/**
|
||||
@@ -55,6 +57,11 @@ typedef struct {
|
||||
* Non-blocking method to create the GUI
|
||||
*/
|
||||
const AppOnShow _Nullable on_show;
|
||||
|
||||
/**
|
||||
* Non-blocking method, called before gui is destroyed
|
||||
*/
|
||||
const AppOnHide _Nullable on_hide;
|
||||
} AppManifest;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "app_manifest_registry.h"
|
||||
|
||||
#include "furi_core.h"
|
||||
#include "m-dict.h"
|
||||
#include "m_cstr_dup.h"
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "bundle.h"
|
||||
|
||||
#include "m-dict.h"
|
||||
#include "m_cstr_dup.h"
|
||||
#include "check.h"
|
||||
|
||||
// region BundleEntry
|
||||
|
||||
typedef enum {
|
||||
BUNDLE_ENTRY_TYPE_BOOL,
|
||||
BUNDLE_ENTRY_TYPE_INT,
|
||||
BUNDLE_ENTRY_TYPE_STRING,
|
||||
} BundleEntryType;
|
||||
|
||||
typedef struct {
|
||||
BundleEntryType type;
|
||||
union {
|
||||
bool bool_value;
|
||||
int int_value;
|
||||
char* string_ptr;
|
||||
};
|
||||
} BundleEntry;
|
||||
|
||||
BundleEntry* bundle_entry_alloc_bool(bool value) {
|
||||
BundleEntry* entry = malloc(sizeof(BundleEntry));
|
||||
entry->type = BUNDLE_ENTRY_TYPE_BOOL;
|
||||
entry->bool_value = value;
|
||||
return entry;
|
||||
}
|
||||
|
||||
BundleEntry* bundle_entry_alloc_int(int value) {
|
||||
BundleEntry* entry = malloc(sizeof(BundleEntry));
|
||||
entry->type = BUNDLE_ENTRY_TYPE_INT;
|
||||
entry->int_value = value;
|
||||
return entry;
|
||||
}
|
||||
|
||||
BundleEntry* bundle_entry_alloc_string(const char* text) {
|
||||
BundleEntry* entry = malloc(sizeof(BundleEntry));
|
||||
entry->type = BUNDLE_ENTRY_TYPE_STRING;
|
||||
entry->string_ptr = malloc(strlen(text) + 1);
|
||||
strcpy(entry->string_ptr, text);
|
||||
return entry;
|
||||
}
|
||||
|
||||
BundleEntry* bundle_entry_alloc_copy(BundleEntry* source) {
|
||||
BundleEntry* entry = malloc(sizeof(BundleEntry));
|
||||
entry->type = source->type;
|
||||
if (source->type == BUNDLE_ENTRY_TYPE_STRING) {
|
||||
entry->string_ptr = malloc(strlen(source->string_ptr) + 1);
|
||||
strcpy(entry->string_ptr, source->string_ptr);
|
||||
} else {
|
||||
entry->int_value = source->int_value;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
void bundle_entry_free(BundleEntry* entry) {
|
||||
if (entry->type == BUNDLE_ENTRY_TYPE_STRING) {
|
||||
free(entry->string_ptr);
|
||||
}
|
||||
free(entry);
|
||||
}
|
||||
|
||||
// endregion BundleEntry
|
||||
|
||||
// region Bundle
|
||||
|
||||
DICT_DEF2(BundleDict, const char*, M_CSTR_DUP_OPLIST, BundleEntry*, M_PTR_OPLIST)
|
||||
|
||||
typedef struct {
|
||||
BundleDict_t dict;
|
||||
} BundleData;
|
||||
|
||||
Bundle bundle_alloc() {
|
||||
BundleData* bundle = malloc(sizeof(BundleData));
|
||||
BundleDict_init(bundle->dict);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
Bundle bundle_alloc_copy(Bundle source) {
|
||||
BundleData* source_data = (BundleData*)source;
|
||||
BundleData* target_data = bundle_alloc();
|
||||
|
||||
BundleDict_it_t it;
|
||||
for (BundleDict_it(it, source_data->dict); !BundleDict_end_p(it); BundleDict_next(it)) {
|
||||
const char* key = BundleDict_cref(it)->key;
|
||||
BundleEntry* entry = BundleDict_cref(it)->value;
|
||||
BundleEntry* entry_copy = bundle_entry_alloc_copy(entry);
|
||||
BundleDict_set_at(target_data->dict, key, entry_copy);
|
||||
}
|
||||
|
||||
return target_data;
|
||||
}
|
||||
|
||||
void bundle_free(Bundle bundle) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
|
||||
BundleDict_it_t it;
|
||||
for (BundleDict_it(it, data->dict); !BundleDict_end_p(it); BundleDict_next(it)) {
|
||||
bundle_entry_free(BundleDict_cref(it)->value);
|
||||
}
|
||||
|
||||
BundleDict_clear(data->dict);
|
||||
free(data);
|
||||
}
|
||||
|
||||
bool bundle_get_bool(Bundle bundle, const char* key) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
BundleEntry** entry = BundleDict_get(data->dict, key);
|
||||
furi_check(entry != NULL);
|
||||
return (*entry)->bool_value;
|
||||
}
|
||||
|
||||
int bundle_get_int(Bundle bundle, const char* key) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
BundleEntry** entry = BundleDict_get(data->dict, key);
|
||||
furi_check(entry != NULL);
|
||||
return (*entry)->int_value;
|
||||
}
|
||||
|
||||
const char* bundle_get_string(Bundle bundle, const char* key) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
BundleEntry** entry = BundleDict_get(data->dict, key);
|
||||
furi_check(entry != NULL);
|
||||
return (*entry)->string_ptr;
|
||||
}
|
||||
|
||||
bool bundle_opt_bool(Bundle bundle, const char* key, bool* out) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
BundleEntry** entry = BundleDict_get(data->dict, key);
|
||||
if (entry != NULL) {
|
||||
*out = (*entry)->bool_value;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool bundle_opt_int(Bundle bundle, const char* key, int* out) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
BundleEntry** entry = BundleDict_get(data->dict, key);
|
||||
if (entry != NULL) {
|
||||
*out = (*entry)->int_value;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool bundle_opt_string(Bundle bundle, const char* key, char** out) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
BundleEntry** entry = BundleDict_get(data->dict, key);
|
||||
if (entry != NULL) {
|
||||
*out = (*entry)->string_ptr;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void bundle_put_bool(Bundle bundle, const char* key, bool value) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
BundleEntry** entry_handle = BundleDict_get(data->dict, key);
|
||||
if (entry_handle != NULL) {
|
||||
BundleEntry* entry = *entry_handle;
|
||||
furi_assert(entry->type == BUNDLE_ENTRY_TYPE_BOOL);
|
||||
entry->bool_value = value;
|
||||
} else {
|
||||
BundleEntry* entry = bundle_entry_alloc_bool(value);
|
||||
BundleDict_set_at(data->dict, key, entry);
|
||||
}
|
||||
}
|
||||
|
||||
void bundle_put_int(Bundle bundle, const char* key, int value) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
BundleEntry** entry_handle = BundleDict_get(data->dict, key);
|
||||
if (entry_handle != NULL) {
|
||||
BundleEntry* entry = *entry_handle;
|
||||
furi_assert(entry->type == BUNDLE_ENTRY_TYPE_INT);
|
||||
entry->int_value = value;
|
||||
} else {
|
||||
BundleEntry* entry = bundle_entry_alloc_int(value);
|
||||
BundleDict_set_at(data->dict, key, entry);
|
||||
}
|
||||
}
|
||||
|
||||
void bundle_put_string(Bundle bundle, const char* key, const char* value) {
|
||||
BundleData* data = (BundleData*)bundle;
|
||||
BundleEntry** entry_handle = BundleDict_get(data->dict, key);
|
||||
if (entry_handle != NULL) {
|
||||
BundleEntry* entry = *entry_handle;
|
||||
furi_assert(entry->type == BUNDLE_ENTRY_TYPE_STRING);
|
||||
if (entry->string_ptr != NULL) {
|
||||
free(entry->string_ptr);
|
||||
}
|
||||
entry->string_ptr = malloc(strlen(value) + 1);
|
||||
strcpy(entry->string_ptr, value);
|
||||
} else {
|
||||
BundleEntry* entry = bundle_entry_alloc_string(value);
|
||||
BundleDict_set_at(data->dict, key, entry);
|
||||
}
|
||||
}
|
||||
|
||||
// endregion Bundle
|
||||
@@ -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 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,11 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct {
|
||||
/** Contextual data related to the running app's instance
|
||||
*
|
||||
* The app can attach its data to this.
|
||||
* The lifecycle is determined by the on_start and on_stop methods in the AppManifest.
|
||||
* These manifest methods can optionally allocate/free data that is attached here.
|
||||
*/
|
||||
void* data;
|
||||
|
||||
} Context;
|
||||
|
||||
@@ -11,12 +11,6 @@ void furi_init() {
|
||||
FURI_LOG_I(TAG, "init start");
|
||||
furi_assert(!furi_kernel_is_irq());
|
||||
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
||||
vTaskSuspendAll();
|
||||
}
|
||||
|
||||
xTaskResumeAll();
|
||||
|
||||
#if defined(__ARM_ARCH_7A__) && (__ARM_ARCH_7A__ == 0U)
|
||||
/* Service Call interrupt might be configured before kernel start */
|
||||
/* and when its priority is lower or equal to BASEPRI, svc instruction */
|
||||
|
||||
@@ -59,7 +59,7 @@ furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t
|
||||
return (stat);
|
||||
}
|
||||
|
||||
FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout) {
|
||||
FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout_ticks) {
|
||||
QueueHandle_t hQueue = (QueueHandle_t)instance;
|
||||
FuriStatus stat;
|
||||
BaseType_t yield;
|
||||
@@ -67,7 +67,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin
|
||||
stat = FuriStatusOk;
|
||||
|
||||
if (furi_kernel_is_irq() != 0U) {
|
||||
if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
|
||||
if ((hQueue == NULL) || (msg_ptr == NULL) || (timeout_ticks != 0U)) {
|
||||
stat = FuriStatusErrorParameter;
|
||||
} else {
|
||||
yield = pdFALSE;
|
||||
@@ -82,8 +82,8 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin
|
||||
if ((hQueue == NULL) || (msg_ptr == NULL)) {
|
||||
stat = FuriStatusErrorParameter;
|
||||
} else {
|
||||
if (xQueueReceive(hQueue, msg_ptr, (TickType_t)timeout) != pdPASS) {
|
||||
if (timeout != 0U) {
|
||||
if (xQueueReceive(hQueue, msg_ptr, (TickType_t)timeout_ticks) != pdPASS) {
|
||||
if (timeout_ticks != 0U) {
|
||||
stat = FuriStatusErrorTimeout;
|
||||
} else {
|
||||
stat = FuriStatusErrorResource;
|
||||
|
||||
@@ -44,11 +44,11 @@ furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t
|
||||
* @param instance pointer to FuriMessageQueue instance
|
||||
* @param msg_ptr The message pointer
|
||||
* @param msg_prio The message prioority
|
||||
* @param[in] timeout The timeout
|
||||
* @param[in] timeout_ticks The timeout
|
||||
*
|
||||
* @return The furi status.
|
||||
*/
|
||||
FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout);
|
||||
FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout_ticks);
|
||||
|
||||
/** Get queue capacity
|
||||
*
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "log.h"
|
||||
|
||||
FuriMutex* furi_mutex_alloc(FuriMutexType type) {
|
||||
furi_assert(!FURI_IS_IRQ_MODE());
|
||||
|
||||
@@ -2,20 +2,62 @@
|
||||
#include "furi_core.h"
|
||||
#include "log.h"
|
||||
|
||||
#define TAG "service"
|
||||
// region Alloc/free
|
||||
|
||||
Service* furi_service_alloc(const ServiceManifest* _Nonnull manifest) {
|
||||
Service app = {
|
||||
Service service_alloc(const ServiceManifest* _Nonnull manifest) {
|
||||
ServiceData* data = malloc(sizeof(ServiceData));
|
||||
*data = (ServiceData) {
|
||||
.manifest = manifest,
|
||||
.context = {
|
||||
.data = NULL
|
||||
}
|
||||
.mutex = furi_mutex_alloc(FuriMutexTypeRecursive),
|
||||
.data = NULL
|
||||
};
|
||||
Service* app_ptr = malloc(sizeof(Service));
|
||||
return memcpy(app_ptr, &app, sizeof(Service));
|
||||
return data;
|
||||
}
|
||||
|
||||
void furi_service_free(Service* app) {
|
||||
furi_assert(app);
|
||||
free(app);
|
||||
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
|
||||
@@ -0,0 +1,18 @@
|
||||
#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,12 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "service_manifest.h"
|
||||
#include "service.h"
|
||||
|
||||
#include "context.h"
|
||||
#include "mutex.h"
|
||||
#include "service_manifest.h"
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
const ServiceManifest* manifest;
|
||||
Context context;
|
||||
} Service;
|
||||
void* data;
|
||||
} ServiceData;
|
||||
|
||||
Service* furi_service_alloc(const ServiceManifest* _Nonnull manifest);
|
||||
void furi_service_free(Service* _Nonnull service);
|
||||
Service service_alloc(const ServiceManifest* _Nonnull manifest);
|
||||
void service_free(Service _Nonnull service);
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*ServiceOnStart)(Context* context);
|
||||
typedef void (*ServiceOnStop)(Context* context);
|
||||
typedef void* Service;
|
||||
|
||||
typedef void (*ServiceOnStart)(Service service);
|
||||
typedef void (*ServiceOnStop)(Service service);
|
||||
|
||||
typedef struct {
|
||||
/**
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
#define TAG "service_registry"
|
||||
|
||||
DICT_DEF2(ServiceManifestDict, const char*, M_CSTR_DUP_OPLIST, const ServiceManifest*, M_PTR_OPLIST)
|
||||
DICT_DEF2(ServiceInstanceDict, const char*, M_CSTR_DUP_OPLIST, const Service*, M_PTR_OPLIST)
|
||||
DICT_DEF2(ServiceInstanceDict, const char*, M_CSTR_DUP_OPLIST, const ServiceData*, M_PTR_OPLIST)
|
||||
|
||||
#define APP_REGISTRY_FOR_EACH(manifest_var_name, code_to_execute) \
|
||||
{ \
|
||||
service_registry_manifest_lock(); \
|
||||
ServiceManifestDict_it_t it; \
|
||||
#define APP_REGISTRY_FOR_EACH(manifest_var_name, code_to_execute) \
|
||||
{ \
|
||||
service_registry_manifest_lock(); \
|
||||
ServiceManifestDict_it_t it; \
|
||||
for (ServiceManifestDict_it(it, service_manifest_dict); !ServiceManifestDict_end_p(it); ServiceManifestDict_next(it)) { \
|
||||
const ServiceManifest*(manifest_var_name) = ServiceManifestDict_cref(it)->value; \
|
||||
code_to_execute; \
|
||||
} \
|
||||
service_registry_manifest_unlock(); \
|
||||
const ServiceManifest*(manifest_var_name) = ServiceManifestDict_cref(it)->value; \
|
||||
code_to_execute; \
|
||||
} \
|
||||
service_registry_manifest_unlock(); \
|
||||
}
|
||||
|
||||
ServiceManifestDict_t service_manifest_dict;
|
||||
@@ -78,13 +78,13 @@ const ServiceManifest* _Nullable service_registry_find_manifest_by_id(const char
|
||||
return (manifest != NULL) ? *manifest : NULL;
|
||||
}
|
||||
|
||||
Service* _Nullable service_registry_find_instance_by_id(const char* id) {
|
||||
ServiceData* _Nullable service_registry_find_instance_by_id(const char* id) {
|
||||
service_registry_instance_lock();
|
||||
const Service** _Nullable service_ptr = ServiceInstanceDict_get(service_instance_dict, id);
|
||||
const ServiceData** _Nullable service_ptr = ServiceInstanceDict_get(service_instance_dict, id);
|
||||
if (service_ptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
Service* service = (Service*) *service_ptr;
|
||||
ServiceData* service = (ServiceData*)*service_ptr;
|
||||
service_registry_instance_unlock();
|
||||
return service;
|
||||
}
|
||||
@@ -100,12 +100,12 @@ bool service_registry_start(const char* service_id) {
|
||||
FURI_LOG_I(TAG, "starting %s", service_id);
|
||||
const ServiceManifest* manifest = service_registry_find_manifest_by_id(service_id);
|
||||
if (manifest == NULL) {
|
||||
FURI_LOG_I(TAG, "manifest not found for %s", service_id);
|
||||
FURI_LOG_E(TAG, "manifest not found for service %s", service_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
Service* service = furi_service_alloc(manifest);
|
||||
service->manifest->on_start(&service->context);
|
||||
Service service = service_alloc(manifest);
|
||||
manifest->on_start(service);
|
||||
|
||||
service_registry_instance_lock();
|
||||
ServiceInstanceDict_set_at(service_instance_dict, manifest->id, service);
|
||||
@@ -117,14 +117,14 @@ bool service_registry_start(const char* service_id) {
|
||||
|
||||
bool service_registry_stop(const char* service_id) {
|
||||
FURI_LOG_I(TAG, "stopping %s", service_id);
|
||||
Service* service = service_registry_find_instance_by_id(service_id);
|
||||
ServiceData* service = service_registry_find_instance_by_id(service_id);
|
||||
if (service == NULL) {
|
||||
FURI_LOG_I(TAG, "service not running: %s", service_id);
|
||||
FURI_LOG_W(TAG, "service not running: %s", service_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
service->manifest->on_stop(&service->context);
|
||||
furi_service_free(service);
|
||||
service->manifest->on_stop(service);
|
||||
service_free(service);
|
||||
|
||||
service_registry_instance_lock();
|
||||
ServiceInstanceDict_erase(service_instance_dict, service_id);
|
||||
|
||||
Reference in New Issue
Block a user