Merge branch 'UNLEASHED' into 420

This commit is contained in:
RogueMaster
2022-11-29 12:38:01 -05:00
47 changed files with 1870 additions and 450 deletions

View File

@@ -0,0 +1,10 @@
App(
appid="rpc_debug",
name="RPC Debug",
apptype=FlipperAppType.DEBUG,
entry_point="rpc_debug_app",
requires=["gui", "rpc_start", "notification"],
stack_size=2 * 1024,
order=10,
fap_category="Debug",
)

View File

@@ -0,0 +1,138 @@
#include "rpc_debug_app.h"
#include <core/log.h>
#include <string.h>
static bool rpc_debug_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
RpcDebugApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool rpc_debug_app_back_event_callback(void* context) {
furi_assert(context);
RpcDebugApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void rpc_debug_app_tick_event_callback(void* context) {
furi_assert(context);
RpcDebugApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) {
furi_assert(context);
RpcDebugApp* app = context;
furi_assert(app->rpc);
if(event == RpcAppEventSessionClose) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
rpc_system_app_set_callback(app->rpc, NULL, NULL);
app->rpc = NULL;
} else if(event == RpcAppEventAppExit) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true);
} else {
rpc_system_app_confirm(app->rpc, event, false);
}
}
static bool rpc_debug_app_rpc_init_rpc(RpcDebugApp* app, const char* args) {
bool ret = false;
if(args && strlen(args)) {
uint32_t rpc = 0;
if(sscanf(args, "RPC %lX", &rpc) == 1) {
app->rpc = (RpcAppSystem*)rpc;
rpc_system_app_set_callback(app->rpc, rpc_debug_app_rpc_command_callback, app);
rpc_system_app_send_started(app->rpc);
ret = true;
}
}
return ret;
}
static RpcDebugApp* rpc_debug_app_alloc() {
RpcDebugApp* app = malloc(sizeof(RpcDebugApp));
app->gui = furi_record_open(RECORD_GUI);
app->notifications = furi_record_open(RECORD_NOTIFICATION);
app->scene_manager = scene_manager_alloc(&rpc_debug_app_scene_handlers, app);
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, rpc_debug_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, rpc_debug_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, rpc_debug_app_tick_event_callback, 100);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_enable_queue(app->view_dispatcher);
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewWidget, widget_get_view(app->widget));
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewSubmenu, submenu_get_view(app->submenu));
app->text_box = text_box_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewTextBox, text_box_get_view(app->text_box));
app->text_input = text_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewTextInput, text_input_get_view(app->text_input));
app->byte_input = byte_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewByteInput, byte_input_get_view(app->byte_input));
return app;
}
static void rpc_debug_app_free(RpcDebugApp* app) {
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewByteInput);
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextInput);
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextBox);
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewWidget);
free(app->byte_input);
free(app->text_input);
free(app->text_box);
free(app->submenu);
free(app->widget);
free(app->scene_manager);
free(app->view_dispatcher);
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
furi_record_close(RECORD_GUI);
app->gui = NULL;
if(app->rpc) {
rpc_system_app_set_callback(app->rpc, NULL, NULL);
rpc_system_app_send_exited(app->rpc);
app->rpc = NULL;
}
free(app);
}
int32_t rpc_debug_app(void* args) {
RpcDebugApp* app = rpc_debug_app_alloc();
if(rpc_debug_app_rpc_init_rpc(app, args)) {
notification_message(app->notifications, &sequence_display_backlight_on);
scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStart);
} else {
scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStartDummy);
}
view_dispatcher_run(app->view_dispatcher);
rpc_debug_app_free(app);
return 0;
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include <furi.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/scene_manager.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/widget.h>
#include <gui/modules/submenu.h>
#include <gui/modules/text_box.h>
#include <gui/modules/text_input.h>
#include <gui/modules/byte_input.h>
#include <rpc/rpc_app.h>
#include <notification/notification_messages.h>
#include "scenes/rpc_debug_app_scene.h"
#define DATA_STORE_SIZE 64U
#define TEXT_STORE_SIZE 64U
typedef struct {
Gui* gui;
RpcAppSystem* rpc;
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
NotificationApp* notifications;
Widget* widget;
Submenu* submenu;
TextBox* text_box;
TextInput* text_input;
ByteInput* byte_input;
char text_store[TEXT_STORE_SIZE];
uint8_t data_store[DATA_STORE_SIZE];
} RpcDebugApp;
typedef enum {
RpcDebugAppViewWidget,
RpcDebugAppViewSubmenu,
RpcDebugAppViewTextBox,
RpcDebugAppViewTextInput,
RpcDebugAppViewByteInput,
} RpcDebugAppView;
typedef enum {
// Reserve first 100 events for button types and indexes, starting from 0
RpcDebugAppCustomEventInputErrorCode = 100,
RpcDebugAppCustomEventInputErrorText,
RpcDebugAppCustomEventInputDataExchange,
RpcDebugAppCustomEventRpcDataExchange,
} RpcDebugAppCustomEvent;

View File

@@ -0,0 +1,30 @@
#include "rpc_debug_app_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const rpc_debug_app_on_enter_handlers[])(void*) = {
#include "rpc_debug_app_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const rpc_debug_app_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "rpc_debug_app_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const rpc_debug_app_on_exit_handlers[])(void* context) = {
#include "rpc_debug_app_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers rpc_debug_app_scene_handlers = {
.on_enter_handlers = rpc_debug_app_on_enter_handlers,
.on_event_handlers = rpc_debug_app_on_event_handlers,
.on_exit_handlers = rpc_debug_app_on_exit_handlers,
.scene_num = RpcDebugAppSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) RpcDebugAppScene##id,
typedef enum {
#include "rpc_debug_app_scene_config.h"
RpcDebugAppSceneNum,
} RpcDebugAppScene;
#undef ADD_SCENE
extern const SceneManagerHandlers rpc_debug_app_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "rpc_debug_app_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "rpc_debug_app_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "rpc_debug_app_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,8 @@
ADD_SCENE(rpc_debug_app, start, Start)
ADD_SCENE(rpc_debug_app, start_dummy, StartDummy)
ADD_SCENE(rpc_debug_app, test_app_error, TestAppError)
ADD_SCENE(rpc_debug_app, test_data_exchange, TestDataExchange)
ADD_SCENE(rpc_debug_app, input_error_code, InputErrorCode)
ADD_SCENE(rpc_debug_app, input_error_text, InputErrorText)
ADD_SCENE(rpc_debug_app, input_data_exchange, InputDataExchange)
ADD_SCENE(rpc_debug_app, receive_data_exchange, ReceiveDataExchange)

View File

@@ -0,0 +1,40 @@
#include "../rpc_debug_app.h"
static void rpc_debug_app_scene_input_data_exchange_result_callback(void* context) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(
app->view_dispatcher, RpcDebugAppCustomEventInputDataExchange);
}
void rpc_debug_app_scene_input_data_exchange_on_enter(void* context) {
RpcDebugApp* app = context;
byte_input_set_header_text(app->byte_input, "Enter data to exchange");
byte_input_set_result_callback(
app->byte_input,
rpc_debug_app_scene_input_data_exchange_result_callback,
NULL,
app,
app->data_store,
DATA_STORE_SIZE);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewByteInput);
}
bool rpc_debug_app_scene_input_data_exchange_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventInputDataExchange) {
rpc_system_app_exchange_data(app->rpc, app->data_store, DATA_STORE_SIZE);
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_input_data_exchange_on_exit(void* context) {
RpcDebugApp* app = context;
UNUSED(app);
}

View File

@@ -0,0 +1,60 @@
#include "../rpc_debug_app.h"
static bool rpc_debug_app_scene_input_error_code_validator_callback(
const char* text,
FuriString* error,
void* context) {
UNUSED(context);
for(; *text; ++text) {
const char c = *text;
if(c < '0' || c > '9') {
furi_string_printf(error, "%s", "Please enter\na number!");
return false;
}
}
return true;
}
static void rpc_debug_app_scene_input_error_code_result_callback(void* context) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorCode);
}
void rpc_debug_app_scene_input_error_code_on_enter(void* context) {
RpcDebugApp* app = context;
strncpy(app->text_store, "666", TEXT_STORE_SIZE);
text_input_set_header_text(app->text_input, "Enter error code");
text_input_set_validator(
app->text_input, rpc_debug_app_scene_input_error_code_validator_callback, NULL);
text_input_set_result_callback(
app->text_input,
rpc_debug_app_scene_input_error_code_result_callback,
app,
app->text_store,
TEXT_STORE_SIZE,
true);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);
}
bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventInputErrorCode) {
rpc_system_app_set_error_code(app->rpc, (uint32_t)atol(app->text_store));
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_input_error_code_on_exit(void* context) {
RpcDebugApp* app = context;
text_input_reset(app->text_input);
text_input_set_validator(app->text_input, NULL, NULL);
}

View File

@@ -0,0 +1,40 @@
#include "../rpc_debug_app.h"
static void rpc_debug_app_scene_input_error_text_result_callback(void* context) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorText);
}
void rpc_debug_app_scene_input_error_text_on_enter(void* context) {
RpcDebugApp* app = context;
strncpy(app->text_store, "I'm a scary error message!", TEXT_STORE_SIZE);
text_input_set_header_text(app->text_input, "Enter error text");
text_input_set_result_callback(
app->text_input,
rpc_debug_app_scene_input_error_text_result_callback,
app,
app->text_store,
TEXT_STORE_SIZE,
true);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);
}
bool rpc_debug_app_scene_input_error_text_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventInputErrorText) {
rpc_system_app_set_error_text(app->rpc, app->text_store);
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_input_error_text_on_exit(void* context) {
RpcDebugApp* app = context;
text_input_reset(app->text_input);
}

View File

@@ -0,0 +1,70 @@
#include "../rpc_debug_app.h"
static void rpc_debug_app_scene_start_format_hex(
const uint8_t* data,
size_t data_size,
char* buf,
size_t buf_size) {
furi_assert(data);
furi_assert(buf);
const size_t byte_width = 3;
const size_t line_width = 7;
data_size = MIN(data_size, buf_size / (byte_width + 1));
for(size_t i = 0; i < data_size; ++i) {
char* p = buf + (i * byte_width);
char sep = !((i + 1) % line_width) ? '\n' : ' ';
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
}
buf[buf_size - 1] = '\0';
}
static void rpc_debug_app_scene_receive_data_exchange_callback(
const uint8_t* data,
size_t data_size,
void* context) {
RpcDebugApp* app = context;
if(data) {
rpc_debug_app_scene_start_format_hex(data, data_size, app->text_store, TEXT_STORE_SIZE);
} else {
strncpy(app->text_store, "<Data empty>", TEXT_STORE_SIZE);
}
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
}
void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
RpcDebugApp* app = context;
strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE);
text_box_set_text(app->text_box, app->text_store);
text_box_set_font(app->text_box, TextBoxFontHex);
rpc_system_app_set_data_exchange_callback(
app->rpc, rpc_debug_app_scene_receive_data_exchange_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox);
}
bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventRpcDataExchange) {
notification_message(app->notifications, &sequence_blink_cyan_100);
notification_message(app->notifications, &sequence_display_backlight_on);
text_box_set_text(app->text_box, app->text_store);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) {
RpcDebugApp* app = context;
text_box_reset(app->text_box);
rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL);
}

View File

@@ -0,0 +1,57 @@
#include "../rpc_debug_app.h"
enum SubmenuIndex {
SubmenuIndexTestAppError,
SubmenuIndexTestDataExchange,
};
static void rpc_debug_app_scene_start_submenu_callback(void* context, uint32_t index) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void rpc_debug_app_scene_start_on_enter(void* context) {
RpcDebugApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Test App Error",
SubmenuIndexTestAppError,
rpc_debug_app_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Test Data Exchange",
SubmenuIndexTestDataExchange,
rpc_debug_app_scene_start_submenu_callback,
app);
submenu_set_selected_item(submenu, SubmenuIndexTestAppError);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
}
bool rpc_debug_app_scene_start_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
const uint32_t submenu_index = event.event;
if(submenu_index == SubmenuIndexTestAppError) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestAppError);
consumed = true;
} else if(submenu_index == SubmenuIndexTestDataExchange) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestDataExchange);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_start_on_exit(void* context) {
RpcDebugApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,30 @@
#include "../rpc_debug_app.h"
void rpc_debug_app_scene_start_dummy_on_enter(void* context) {
RpcDebugApp* app = context;
widget_add_text_box_element(
app->widget,
0,
0,
128,
64,
AlignCenter,
AlignCenter,
"This application\nis meant to be run\nin \e#RPC\e# mode.",
false);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewWidget);
}
bool rpc_debug_app_scene_start_dummy_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
UNUSED(app);
UNUSED(event);
bool consumed = false;
return consumed;
}
void rpc_debug_app_scene_start_dummy_on_exit(void* context) {
RpcDebugApp* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,57 @@
#include "../rpc_debug_app.h"
typedef enum {
SubmenuIndexSetErrorCode,
SubmenuIndexSetErrorText,
} SubmenuIndex;
static void rpc_debug_app_scene_test_app_error_submenu_callback(void* context, uint32_t index) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void rpc_debug_app_scene_test_app_error_on_enter(void* context) {
RpcDebugApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Set Error Code",
SubmenuIndexSetErrorCode,
rpc_debug_app_scene_test_app_error_submenu_callback,
app);
submenu_add_item(
submenu,
"Set Error Text",
SubmenuIndexSetErrorText,
rpc_debug_app_scene_test_app_error_submenu_callback,
app);
submenu_set_selected_item(submenu, SubmenuIndexSetErrorCode);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
}
bool rpc_debug_app_scene_test_app_error_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
const uint32_t submenu_index = event.event;
if(submenu_index == SubmenuIndexSetErrorCode) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorCode);
consumed = true;
} else if(submenu_index == SubmenuIndexSetErrorText) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorText);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_test_app_error_on_exit(void* context) {
RpcDebugApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,58 @@
#include "../rpc_debug_app.h"
typedef enum {
SubmenuIndexSendData,
SubmenuIndexReceiveData,
} SubmenuIndex;
static void
rpc_debug_app_scene_test_data_exchange_submenu_callback(void* context, uint32_t index) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void rpc_debug_app_scene_test_data_exchange_on_enter(void* context) {
RpcDebugApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Send Data",
SubmenuIndexSendData,
rpc_debug_app_scene_test_data_exchange_submenu_callback,
app);
submenu_add_item(
submenu,
"Receive Data",
SubmenuIndexReceiveData,
rpc_debug_app_scene_test_data_exchange_submenu_callback,
app);
submenu_set_selected_item(submenu, SubmenuIndexSendData);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
}
bool rpc_debug_app_scene_test_data_exchange_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
const uint32_t submenu_index = event.event;
if(submenu_index == SubmenuIndexSendData) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputDataExchange);
consumed = true;
} else if(submenu_index == SubmenuIndexReceiveData) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneReceiveDataExchange);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_test_data_exchange_on_exit(void* context) {
RpcDebugApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -33,6 +33,7 @@ struct GpioApp {
UsbUartBridge* usb_uart_bridge; UsbUartBridge* usb_uart_bridge;
GpioI2CScanner* gpio_i2c_scanner; GpioI2CScanner* gpio_i2c_scanner;
GpioI2CSfp* gpio_i2c_sfp; GpioI2CSfp* gpio_i2c_sfp;
UsbUartConfig* usb_uart_cfg;
}; };
typedef enum { typedef enum {

View File

@@ -11,4 +11,5 @@ typedef enum {
GpioCustomEventErrorBack, GpioCustomEventErrorBack,
GpioUsbUartEventConfig, GpioUsbUartEventConfig,
GpioUsbUartEventConfigSet,
} GpioCustomEvent; } GpioCustomEvent;

View File

@@ -9,8 +9,6 @@ typedef enum {
UsbUartLineIndexFlow, UsbUartLineIndexFlow,
} LineIndex; } LineIndex;
static UsbUartConfig* cfg_set;
static const char* vcp_ch[] = {"0 (CLI)", "1"}; static const char* vcp_ch[] = {"0 (CLI)", "1"};
static const char* uart_ch[] = {"13,14", "15,16"}; static const char* uart_ch[] = {"13,14", "15,16"};
static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"}; static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
@@ -28,8 +26,14 @@ static const uint32_t baudrate_list[] = {
}; };
bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
UNUSED(context); GpioApp* app = context;
UNUSED(event); furi_assert(app);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GpioUsbUartEventConfigSet) {
usb_uart_set_config(app->usb_uart_bridge, app->usb_uart_cfg);
return true;
}
}
return false; return false;
} }
@@ -38,55 +42,59 @@ void line_ensure_flow_invariant(GpioApp* app) {
// selected. This function enforces that invariant by resetting flow_pins // selected. This function enforces that invariant by resetting flow_pins
// to None if it is configured to 16,15 when LPUART is selected. // to None if it is configured to 16,15 when LPUART is selected.
uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
VariableItem* item = app->var_item_flow; VariableItem* item = app->var_item_flow;
variable_item_set_values_count(item, available_flow_pins); variable_item_set_values_count(item, available_flow_pins);
if(cfg_set->flow_pins >= available_flow_pins) { if(app->usb_uart_cfg->flow_pins >= available_flow_pins) {
cfg_set->flow_pins = 0; app->usb_uart_cfg->flow_pins = 0;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
variable_item_set_current_value_index(item, cfg_set->flow_pins); variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
} }
} }
static void line_vcp_cb(VariableItem* item) { static void line_vcp_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, vcp_ch[index]); variable_item_set_current_value_text(item, vcp_ch[index]);
cfg_set->vcp_ch = index; app->usb_uart_cfg->vcp_ch = index;
usb_uart_set_config(app->usb_uart_bridge, cfg_set); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
static void line_port_cb(VariableItem* item) { static void line_port_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, uart_ch[index]); variable_item_set_current_value_text(item, uart_ch[index]);
if(index == 0) if(index == 0)
cfg_set->uart_ch = FuriHalUartIdUSART1; app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1;
else if(index == 1) else if(index == 1)
cfg_set->uart_ch = FuriHalUartIdLPUART1; app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
line_ensure_flow_invariant(app); line_ensure_flow_invariant(app);
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
static void line_flow_cb(VariableItem* item) { static void line_flow_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, flow_pins[index]); variable_item_set_current_value_text(item, flow_pins[index]);
cfg_set->flow_pins = index; app->usb_uart_cfg->flow_pins = index;
usb_uart_set_config(app->usb_uart_bridge, cfg_set); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
static void line_baudrate_cb(VariableItem* item) { static void line_baudrate_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
char br_text[8]; char br_text[8];
@@ -94,28 +102,29 @@ static void line_baudrate_cb(VariableItem* item) {
if(index > 0) { if(index > 0) {
snprintf(br_text, 7, "%lu", baudrate_list[index - 1]); snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
variable_item_set_current_value_text(item, br_text); variable_item_set_current_value_text(item, br_text);
cfg_set->baudrate = baudrate_list[index - 1]; app->usb_uart_cfg->baudrate = baudrate_list[index - 1];
} else { } else {
variable_item_set_current_value_text(item, baudrate_mode[index]); variable_item_set_current_value_text(item, baudrate_mode[index]);
cfg_set->baudrate = 0; app->usb_uart_cfg->baudrate = 0;
} }
cfg_set->baudrate_mode = index; app->usb_uart_cfg->baudrate_mode = index;
usb_uart_set_config(app->usb_uart_bridge, cfg_set); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
void gpio_scene_usb_uart_cfg_on_enter(void* context) { void gpio_scene_usb_uart_cfg_on_enter(void* context) {
GpioApp* app = context; GpioApp* app = context;
furi_assert(app);
VariableItemList* var_item_list = app->var_item_list; VariableItemList* var_item_list = app->var_item_list;
cfg_set = malloc(sizeof(UsbUartConfig)); app->usb_uart_cfg = malloc(sizeof(UsbUartConfig));
usb_uart_get_config(app->usb_uart_bridge, cfg_set); usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg);
VariableItem* item; VariableItem* item;
char br_text[8]; char br_text[8];
item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app); item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app);
variable_item_set_current_value_index(item, cfg_set->vcp_ch); variable_item_set_current_value_index(item, app->usb_uart_cfg->vcp_ch);
variable_item_set_current_value_text(item, vcp_ch[cfg_set->vcp_ch]); variable_item_set_current_value_text(item, vcp_ch[app->usb_uart_cfg->vcp_ch]);
item = variable_item_list_add( item = variable_item_list_add(
var_item_list, var_item_list,
@@ -123,22 +132,23 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) {
sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1, sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
line_baudrate_cb, line_baudrate_cb,
app); app);
variable_item_set_current_value_index(item, cfg_set->baudrate_mode); variable_item_set_current_value_index(item, app->usb_uart_cfg->baudrate_mode);
if(cfg_set->baudrate_mode > 0) { if(app->usb_uart_cfg->baudrate_mode > 0) {
snprintf(br_text, 7, "%lu", baudrate_list[cfg_set->baudrate_mode - 1]); snprintf(br_text, 7, "%lu", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]);
variable_item_set_current_value_text(item, br_text); variable_item_set_current_value_text(item, br_text);
} else { } else {
variable_item_set_current_value_text(item, baudrate_mode[cfg_set->baudrate_mode]); variable_item_set_current_value_text(
item, baudrate_mode[app->usb_uart_cfg->baudrate_mode]);
} }
item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app); item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app);
variable_item_set_current_value_index(item, cfg_set->uart_ch); variable_item_set_current_value_index(item, app->usb_uart_cfg->uart_ch);
variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]); variable_item_set_current_value_text(item, uart_ch[app->usb_uart_cfg->uart_ch]);
item = variable_item_list_add( item = variable_item_list_add(
var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app); var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app);
variable_item_set_current_value_index(item, cfg_set->flow_pins); variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
app->var_item_flow = item; app->var_item_flow = item;
line_ensure_flow_invariant(app); line_ensure_flow_invariant(app);
@@ -155,5 +165,5 @@ void gpio_scene_usb_uart_cfg_on_exit(void* context) {
GpioAppViewUsbUartCfg, GpioAppViewUsbUartCfg,
variable_item_list_get_selected_item_index(app->var_item_list)); variable_item_list_get_selected_item_index(app->var_item_list));
variable_item_list_reset(app->var_item_list); variable_item_list_reset(app->var_item_list);
free(cfg_set); free(app->usb_uart_cfg);
} }

View File

@@ -3,6 +3,7 @@
#include <furi_hal_usb_cdc.h> #include <furi_hal_usb_cdc.h>
#include "usb_cdc.h" #include "usb_cdc.h"
#include "cli/cli_vcp.h" #include "cli/cli_vcp.h"
#include <toolbox/api_lock.h>
#include "cli/cli.h" #include "cli/cli.h"
#define USB_CDC_PKT_LEN CDC_DATA_SZ #define USB_CDC_PKT_LEN CDC_DATA_SZ
@@ -51,6 +52,8 @@ struct UsbUartBridge {
UsbUartState st; UsbUartState st;
FuriApiLock cfg_lock;
uint8_t rx_buf[USB_CDC_PKT_LEN]; uint8_t rx_buf[USB_CDC_PKT_LEN];
}; };
@@ -244,6 +247,7 @@ static int32_t usb_uart_worker(void* context) {
usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins; usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
events |= WorkerEvtCtrlLineSet; events |= WorkerEvtCtrlLineSet;
} }
api_lock_unlock(usb_uart->cfg_lock);
} }
if(events & WorkerEvtLineCfgSet) { if(events & WorkerEvtLineCfgSet) {
if(usb_uart->cfg.baudrate == 0) if(usb_uart->cfg.baudrate == 0)
@@ -352,8 +356,10 @@ void usb_uart_disable(UsbUartBridge* usb_uart) {
void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
furi_assert(usb_uart); furi_assert(usb_uart);
furi_assert(cfg); furi_assert(cfg);
usb_uart->cfg_lock = api_lock_alloc_locked();
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange); furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange);
api_lock_wait_unlock_and_free(usb_uart->cfg_lock);
} }
void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {

View File

@@ -247,7 +247,6 @@ static int32_t dap_process(void* p) {
// deinit usb // deinit usb
furi_hal_usb_set_config(usb_config_prev, NULL); furi_hal_usb_set_config(usb_config_prev, NULL);
dap_common_wait_for_deinit();
dap_common_usb_free_name(); dap_common_usb_free_name();
dap_deinit_gpio(swd_pins_prev); dap_deinit_gpio(swd_pins_prev);
return 0; return 0;

View File

@@ -618,23 +618,12 @@ static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1); if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1);
if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1); if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1);
usb_hid.dev_descr->idVendor = DAP_HID_VID;
usb_hid.dev_descr->idProduct = DAP_HID_PID;
usbd_reg_config(dev, hid_ep_config); usbd_reg_config(dev, hid_ep_config);
usbd_reg_control(dev, hid_control); usbd_reg_control(dev, hid_control);
usbd_connect(dev, true); usbd_connect(dev, true);
} }
static bool deinit_flag = false;
void dap_common_wait_for_deinit() {
while(!deinit_flag) {
furi_delay_ms(50);
}
}
static void hid_deinit(usbd_device* dev) { static void hid_deinit(usbd_device* dev) {
dap_state.usb_dev = NULL; dap_state.usb_dev = NULL;
@@ -647,12 +636,6 @@ static void hid_deinit(usbd_device* dev) {
usbd_reg_config(dev, NULL); usbd_reg_config(dev, NULL);
usbd_reg_control(dev, NULL); usbd_reg_control(dev, NULL);
free(usb_hid.str_manuf_descr);
free(usb_hid.str_prod_descr);
FURI_SW_MEMBARRIER();
deinit_flag = true;
} }
static void hid_on_wakeup(usbd_device* dev) { static void hid_on_wakeup(usbd_device* dev) {

View File

@@ -51,5 +51,3 @@ void dap_common_usb_set_state_callback(DapStateCallback callback);
void dap_common_usb_alloc_name(const char* name); void dap_common_usb_alloc_name(const char* name);
void dap_common_usb_free_name(); void dap_common_usb_free_name();
void dap_common_wait_for_deinit();

View File

@@ -25,7 +25,7 @@ void cli_command_device_info_callback(const char* key, const char* value, bool l
void cli_command_device_info(Cli* cli, FuriString* args, void* context) { void cli_command_device_info(Cli* cli, FuriString* args, void* context) {
UNUSED(cli); UNUSED(cli);
UNUSED(args); UNUSED(args);
furi_hal_info_get(cli_command_device_info_callback, context); furi_hal_info_get(cli_command_device_info_callback, '_', context);
} }
void cli_command_help(Cli* cli, FuriString* args, void* context) { void cli_command_help(Cli* cli, FuriString* args, void* context) {

View File

@@ -1,6 +1,6 @@
#include "dialogs/dialogs_message.h" #include "dialogs/dialogs_message.h"
#include "dialogs_i.h" #include "dialogs_i.h"
#include "dialogs_api_lock.h" #include <toolbox/api_lock.h>
#include "dialogs_module_file_browser.h" #include "dialogs_module_file_browser.h"
#include "dialogs_module_message.h" #include "dialogs_module_message.h"
@@ -35,7 +35,7 @@ static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* mess
dialogs_app_process_module_message(&message->data->dialog); dialogs_app_process_module_message(&message->data->dialog);
break; break;
} }
API_LOCK_UNLOCK(message->lock); api_lock_unlock(message->lock);
} }
int32_t dialogs_srv(void* p) { int32_t dialogs_srv(void* p) {

View File

@@ -1,6 +1,6 @@
#include "dialogs/dialogs_message.h" #include "dialogs/dialogs_message.h"
#include "dialogs_i.h" #include "dialogs_i.h"
#include "dialogs_api_lock.h" #include <toolbox/api_lock.h>
#include <assets_icons.h> #include <assets_icons.h>
/****************** File browser ******************/ /****************** File browser ******************/
@@ -10,7 +10,7 @@ bool dialog_file_browser_show(
FuriString* result_path, FuriString* result_path,
FuriString* path, FuriString* path,
const DialogsFileBrowserOptions* options) { const DialogsFileBrowserOptions* options) {
FuriApiLock lock = API_LOCK_INIT_LOCKED(); FuriApiLock lock = api_lock_alloc_locked();
furi_check(lock != NULL); furi_check(lock != NULL);
DialogsAppData data = { DialogsAppData data = {
@@ -35,7 +35,7 @@ bool dialog_file_browser_show(
furi_check( furi_check(
furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk); furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock); api_lock_wait_unlock_and_free(lock);
return return_data.bool_value; return return_data.bool_value;
} }
@@ -43,7 +43,7 @@ bool dialog_file_browser_show(
/****************** Message ******************/ /****************** Message ******************/
DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) { DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) {
FuriApiLock lock = API_LOCK_INIT_LOCKED(); FuriApiLock lock = api_lock_alloc_locked();
furi_check(lock != NULL); furi_check(lock != NULL);
DialogsAppData data = { DialogsAppData data = {
@@ -61,7 +61,7 @@ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage
furi_check( furi_check(
furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk); furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock); api_lock_wait_unlock_and_free(lock);
return return_data.dialog_value; return return_data.dialog_value;
} }

View File

@@ -1,18 +0,0 @@
#pragma once
typedef FuriEventFlag* FuriApiLock;
#define API_LOCK_EVENT (1U << 0)
#define API_LOCK_INIT_LOCKED() furi_event_flag_alloc();
#define API_LOCK_WAIT_UNTIL_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_UNTIL_UNLOCK_AND_FREE(_lock) \
API_LOCK_WAIT_UNTIL_UNLOCK(_lock); \
API_LOCK_FREE(_lock);

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <furi.h> #include <furi.h>
#include "dialogs_i.h" #include "dialogs_i.h"
#include "dialogs_api_lock.h" #include <toolbox/api_lock.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View File

@@ -1,5 +1,5 @@
#include "dialogs_i.h" #include "dialogs_i.h"
#include "dialogs_api_lock.h" #include <toolbox/api_lock.h>
#include "gui/modules/file_browser.h" #include "gui/modules/file_browser.h"
typedef struct { typedef struct {
@@ -11,14 +11,14 @@ static void dialogs_app_file_browser_back_callback(void* context) {
furi_assert(context); furi_assert(context);
DialogsAppFileBrowserContext* file_browser_context = context; DialogsAppFileBrowserContext* file_browser_context = context;
file_browser_context->result = false; file_browser_context->result = false;
API_LOCK_UNLOCK(file_browser_context->lock); api_lock_unlock(file_browser_context->lock);
} }
static void dialogs_app_file_browser_callback(void* context) { static void dialogs_app_file_browser_callback(void* context) {
furi_assert(context); furi_assert(context);
DialogsAppFileBrowserContext* file_browser_context = context; DialogsAppFileBrowserContext* file_browser_context = context;
file_browser_context->result = true; file_browser_context->result = true;
API_LOCK_UNLOCK(file_browser_context->lock); api_lock_unlock(file_browser_context->lock);
} }
bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) { bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) {
@@ -27,7 +27,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
DialogsAppFileBrowserContext* file_browser_context = DialogsAppFileBrowserContext* file_browser_context =
malloc(sizeof(DialogsAppFileBrowserContext)); malloc(sizeof(DialogsAppFileBrowserContext));
file_browser_context->lock = API_LOCK_INIT_LOCKED(); file_browser_context->lock = api_lock_alloc_locked();
ViewHolder* view_holder = view_holder_alloc(); ViewHolder* view_holder = view_holder_alloc();
view_holder_attach_to_gui(view_holder, gui); view_holder_attach_to_gui(view_holder, gui);
@@ -44,7 +44,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
view_holder_set_view(view_holder, file_browser_get_view(file_browser)); view_holder_set_view(view_holder, file_browser_get_view(file_browser));
view_holder_start(view_holder); view_holder_start(view_holder);
API_LOCK_WAIT_UNTIL_UNLOCK(file_browser_context->lock); api_lock_wait_unlock(file_browser_context->lock);
ret = file_browser_context->result; ret = file_browser_context->result;
@@ -52,7 +52,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
view_holder_free(view_holder); view_holder_free(view_holder);
file_browser_stop(file_browser); file_browser_stop(file_browser);
file_browser_free(file_browser); file_browser_free(file_browser);
API_LOCK_FREE(file_browser_context->lock); api_lock_free(file_browser_context->lock);
free(file_browser_context); free(file_browser_context);
furi_record_close(RECORD_GUI); furi_record_close(RECORD_GUI);

View File

@@ -1,5 +1,5 @@
#include "dialogs_i.h" #include "dialogs_i.h"
#include "dialogs_api_lock.h" #include <toolbox/api_lock.h>
#include <gui/modules/dialog_ex.h> #include <gui/modules/dialog_ex.h>
typedef struct { typedef struct {
@@ -30,7 +30,7 @@ static void dialogs_app_message_back_callback(void* context) {
furi_assert(context); furi_assert(context);
DialogsAppMessageContext* message_context = context; DialogsAppMessageContext* message_context = context;
message_context->result = DialogMessageButtonBack; message_context->result = DialogMessageButtonBack;
API_LOCK_UNLOCK(message_context->lock); api_lock_unlock(message_context->lock);
} }
static void dialogs_app_message_callback(DialogExResult result, void* context) { static void dialogs_app_message_callback(DialogExResult result, void* context) {
@@ -49,7 +49,7 @@ static void dialogs_app_message_callback(DialogExResult result, void* context) {
default: default:
break; break;
} }
API_LOCK_UNLOCK(message_context->lock); api_lock_unlock(message_context->lock);
} }
DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) { DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) {
@@ -57,7 +57,7 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa
Gui* gui = furi_record_open(RECORD_GUI); Gui* gui = furi_record_open(RECORD_GUI);
const DialogMessage* message = data->message; const DialogMessage* message = data->message;
DialogsAppMessageContext* message_context = malloc(sizeof(DialogsAppMessageContext)); DialogsAppMessageContext* message_context = malloc(sizeof(DialogsAppMessageContext));
message_context->lock = API_LOCK_INIT_LOCKED(); message_context->lock = api_lock_alloc_locked();
ViewHolder* view_holder = view_holder_alloc(); ViewHolder* view_holder = view_holder_alloc();
view_holder_attach_to_gui(view_holder, gui); view_holder_attach_to_gui(view_holder, gui);
@@ -87,14 +87,14 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa
view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex)); view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex));
view_holder_start(view_holder); view_holder_start(view_holder);
API_LOCK_WAIT_UNTIL_UNLOCK(message_context->lock); api_lock_wait_unlock(message_context->lock);
ret = message_context->result; ret = message_context->result;
view_holder_stop(view_holder); view_holder_stop(view_holder);
view_holder_free(view_holder); view_holder_free(view_holder);
dialog_ex_free(dialog_ex); dialog_ex_free(dialog_ex);
API_LOCK_FREE(message_context->lock); api_lock_free(message_context->lock);
free(message_context); free(message_context);
furi_record_close(RECORD_GUI); furi_record_close(RECORD_GUI);

View File

@@ -26,7 +26,7 @@ void power_cli_reboot2dfu(Cli* cli, FuriString* args) {
power_reboot(PowerBootModeDfu); power_reboot(PowerBootModeDfu);
} }
static void power_cli_info_callback(const char* key, const char* value, bool last, void* context) { static void power_cli_callback(const char* key, const char* value, bool last, void* context) {
UNUSED(last); UNUSED(last);
UNUSED(context); UNUSED(context);
printf("%-24s: %s\r\n", key, value); printf("%-24s: %s\r\n", key, value);
@@ -35,13 +35,13 @@ static void power_cli_info_callback(const char* key, const char* value, bool las
void power_cli_info(Cli* cli, FuriString* args) { void power_cli_info(Cli* cli, FuriString* args) {
UNUSED(cli); UNUSED(cli);
UNUSED(args); UNUSED(args);
furi_hal_power_info_get(power_cli_info_callback, NULL); furi_hal_power_info_get(power_cli_callback, '_', NULL);
} }
void power_cli_debug(Cli* cli, FuriString* args) { void power_cli_debug(Cli* cli, FuriString* args) {
UNUSED(cli); UNUSED(cli);
UNUSED(args); UNUSED(args);
furi_hal_power_dump_state(); furi_hal_power_debug_get(power_cli_callback, NULL);
} }
void power_cli_5v(Cli* cli, FuriString* args) { void power_cli_5v(Cli* cli, FuriString* args) {

View File

@@ -52,7 +52,12 @@ static RpcSystemCallbacks rpc_systems[] = {
{ {
.alloc = rpc_system_gpio_alloc, .alloc = rpc_system_gpio_alloc,
.free = NULL, .free = NULL,
}}; },
{
.alloc = rpc_system_property_alloc,
.free = NULL,
},
};
struct RpcSession { struct RpcSession {
Rpc* rpc; Rpc* rpc;

View File

@@ -9,9 +9,15 @@
struct RpcAppSystem { struct RpcAppSystem {
RpcSession* session; RpcSession* session;
RpcAppSystemCallback app_callback; RpcAppSystemCallback app_callback;
void* app_context; void* app_context;
RpcAppSystemDataExchangeCallback data_exchange_callback;
void* data_exchange_context;
PB_Main* state_msg; PB_Main* state_msg;
PB_Main* error_msg;
uint32_t last_id; uint32_t last_id;
char* last_data; char* last_data;
@@ -195,6 +201,50 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context)
} }
} }
static void rpc_system_app_get_error_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(request->which_content == PB_Main_app_get_error_request_tag);
furi_assert(context);
RpcAppSystem* rpc_app = context;
RpcSession* session = rpc_app->session;
furi_assert(session);
rpc_app->error_msg->command_id = request->command_id;
FURI_LOG_D(TAG, "GetError");
rpc_send(session, rpc_app->error_msg);
}
static void rpc_system_app_data_exchange_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(request->which_content == PB_Main_app_data_exchange_request_tag);
furi_assert(context);
RpcAppSystem* rpc_app = context;
RpcSession* session = rpc_app->session;
furi_assert(session);
PB_CommandStatus command_status;
pb_bytes_array_t* data = request->content.app_data_exchange_request.data;
if(rpc_app->data_exchange_callback) {
uint8_t* data_bytes = NULL;
size_t data_size = 0;
if(data) {
data_bytes = data->bytes;
data_size = data->size;
}
rpc_app->data_exchange_callback(data_bytes, data_size, rpc_app->data_exchange_context);
command_status = PB_CommandStatus_OK;
} else {
command_status = PB_CommandStatus_ERROR_APP_CMD_ERROR;
}
FURI_LOG_D(TAG, "DataExchange");
rpc_send_and_release_empty(session, request->command_id, command_status);
}
void rpc_system_app_send_started(RpcAppSystem* rpc_app) { void rpc_system_app_send_started(RpcAppSystem* rpc_app) {
furi_assert(rpc_app); furi_assert(rpc_app);
RpcSession* session = rpc_app->session; RpcSession* session = rpc_app->session;
@@ -259,6 +309,58 @@ void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback cal
rpc_app->app_context = ctx; rpc_app->app_context = ctx;
} }
void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code) {
furi_assert(rpc_app);
PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response;
content->code = error_code;
}
void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text) {
furi_assert(rpc_app);
PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response;
if(content->text) {
free(content->text);
}
content->text = error_text ? strdup(error_text) : NULL;
}
void rpc_system_app_set_data_exchange_callback(
RpcAppSystem* rpc_app,
RpcAppSystemDataExchangeCallback callback,
void* ctx) {
furi_assert(rpc_app);
rpc_app->data_exchange_callback = callback;
rpc_app->data_exchange_context = ctx;
}
void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size) {
furi_assert(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
PB_Main message = {
.command_id = 0,
.command_status = PB_CommandStatus_OK,
.has_next = false,
.which_content = PB_Main_app_data_exchange_request_tag,
};
PB_App_DataExchangeRequest* content = &message.content.app_data_exchange_request;
if(data && data_size) {
content->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data_size));
content->data->size = data_size;
memcpy(content->data->bytes, data, data_size);
} else {
content->data = NULL;
}
rpc_send_and_release(session, &message);
}
void* rpc_system_app_alloc(RpcSession* session) { void* rpc_system_app_alloc(RpcSession* session) {
furi_assert(session); furi_assert(session);
@@ -270,6 +372,13 @@ void* rpc_system_app_alloc(RpcSession* session) {
rpc_app->state_msg->which_content = PB_Main_app_state_response_tag; rpc_app->state_msg->which_content = PB_Main_app_state_response_tag;
rpc_app->state_msg->command_status = PB_CommandStatus_OK; rpc_app->state_msg->command_status = PB_CommandStatus_OK;
// App error message
rpc_app->error_msg = malloc(sizeof(PB_Main));
rpc_app->error_msg->which_content = PB_Main_app_get_error_response_tag;
rpc_app->error_msg->command_status = PB_CommandStatus_OK;
rpc_app->error_msg->content.app_get_error_response.code = 0;
rpc_app->error_msg->content.app_get_error_response.text = NULL;
RpcHandler rpc_handler = { RpcHandler rpc_handler = {
.message_handler = NULL, .message_handler = NULL,
.decode_submessage = NULL, .decode_submessage = NULL,
@@ -294,6 +403,12 @@ void* rpc_system_app_alloc(RpcSession* session) {
rpc_handler.message_handler = rpc_system_app_button_release; rpc_handler.message_handler = rpc_system_app_button_release;
rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler); rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler);
rpc_handler.message_handler = rpc_system_app_get_error_process;
rpc_add_handler(session, PB_Main_app_get_error_request_tag, &rpc_handler);
rpc_handler.message_handler = rpc_system_app_data_exchange_process;
rpc_add_handler(session, PB_Main_app_data_exchange_request_tag, &rpc_handler);
return rpc_app; return rpc_app;
} }
@@ -311,8 +426,13 @@ void rpc_system_app_free(void* context) {
furi_delay_tick(1); furi_delay_tick(1);
} }
furi_assert(!rpc_app->data_exchange_callback);
if(rpc_app->last_data) free(rpc_app->last_data); if(rpc_app->last_data) free(rpc_app->last_data);
pb_release(&PB_Main_msg, rpc_app->error_msg);
free(rpc_app->error_msg);
free(rpc_app->state_msg); free(rpc_app->state_msg);
free(rpc_app); free(rpc_app);
} }

View File

@@ -14,6 +14,8 @@ typedef enum {
} RpcAppSystemEvent; } RpcAppSystemEvent;
typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context); typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context);
typedef void (
*RpcAppSystemDataExchangeCallback)(const uint8_t* data, size_t data_size, void* context);
typedef struct RpcAppSystem RpcAppSystem; typedef struct RpcAppSystem RpcAppSystem;
@@ -27,6 +29,17 @@ const char* rpc_system_app_get_data(RpcAppSystem* rpc_app);
void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result); void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result);
void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code);
void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text);
void rpc_system_app_set_data_exchange_callback(
RpcAppSystem* rpc_app,
RpcAppSystemDataExchangeCallback callback,
void* ctx);
void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -34,6 +34,7 @@ void* rpc_system_gui_alloc(RpcSession* session);
void rpc_system_gui_free(void* ctx); void rpc_system_gui_free(void* ctx);
void* rpc_system_gpio_alloc(RpcSession* session); void* rpc_system_gpio_alloc(RpcSession* session);
void rpc_system_gpio_free(void* ctx); void rpc_system_gpio_free(void* ctx);
void* rpc_system_property_alloc(RpcSession* session);
void rpc_debug_print_message(const PB_Main* message); void rpc_debug_print_message(const PB_Main* message);
void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size);

View File

@@ -0,0 +1,107 @@
#include <flipper.pb.h>
#include <furi_hal.h>
#include <furi_hal_info.h>
#include <furi_hal_power.h>
#include <core/core_defines.h>
#include "rpc_i.h"
#define TAG "RpcProperty"
#define PROPERTY_CATEGORY_DEVICE_INFO "devinfo"
#define PROPERTY_CATEGORY_POWER_INFO "pwrinfo"
#define PROPERTY_CATEGORY_POWER_DEBUG "pwrdebug"
typedef struct {
RpcSession* session;
PB_Main* response;
FuriString* subkey;
} RpcPropertyContext;
static void
rpc_system_property_get_callback(const char* key, const char* value, bool last, void* context) {
furi_assert(key);
furi_assert(value);
furi_assert(context);
furi_assert(key);
furi_assert(value);
RpcPropertyContext* ctx = context;
RpcSession* session = ctx->session;
PB_Main* response = ctx->response;
if(!strncmp(key, furi_string_get_cstr(ctx->subkey), furi_string_size(ctx->subkey))) {
response->content.system_device_info_response.key = strdup(key);
response->content.system_device_info_response.value = strdup(value);
rpc_send_and_release(session, response);
}
if(last) {
rpc_send_and_release_empty(session, response->command_id, PB_CommandStatus_OK);
}
}
static void rpc_system_property_get_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(request->which_content == PB_Main_property_get_request_tag);
FURI_LOG_D(TAG, "GetProperty");
RpcSession* session = (RpcSession*)context;
furi_assert(session);
FuriString* topkey = furi_string_alloc();
FuriString* subkey = furi_string_alloc_set_str(request->content.property_get_request.key);
const size_t sep_idx = furi_string_search_char(subkey, '.');
if(sep_idx == FURI_STRING_FAILURE) {
furi_string_swap(topkey, subkey);
} else {
furi_string_set_n(topkey, subkey, 0, sep_idx);
furi_string_right(subkey, sep_idx + 1);
}
PB_Main* response = malloc(sizeof(PB_Main));
response->command_id = request->command_id;
response->command_status = PB_CommandStatus_OK;
response->has_next = true;
response->which_content = PB_Main_property_get_response_tag;
RpcPropertyContext property_context = {
.session = session,
.response = response,
.subkey = subkey,
};
if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_DEVICE_INFO)) {
furi_hal_info_get(rpc_system_property_get_callback, '.', &property_context);
} else if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_POWER_INFO)) {
furi_hal_power_info_get(rpc_system_property_get_callback, '.', &property_context);
} else if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_POWER_DEBUG)) {
furi_hal_power_debug_get(rpc_system_property_get_callback, &property_context);
} else {
rpc_send_and_release_empty(
session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);
}
furi_string_free(subkey);
furi_string_free(topkey);
free(response);
}
void* rpc_system_property_alloc(RpcSession* session) {
furi_assert(session);
RpcHandler rpc_handler = {
.message_handler = NULL,
.decode_submessage = NULL,
.context = session,
};
rpc_handler.message_handler = rpc_system_property_get_process;
rpc_add_handler(session, PB_Main_property_get_request_tag, &rpc_handler);
return NULL;
}

View File

@@ -109,7 +109,7 @@ static void rpc_system_system_device_info_process(const PB_Main* request, void*
.session = session, .session = session,
.response = response, .response = response,
}; };
furi_hal_info_get(rpc_system_system_device_info_callback, &device_info_context); furi_hal_info_get(rpc_system_system_device_info_callback, '_', &device_info_context);
free(response); free(response);
} }
@@ -266,7 +266,7 @@ static void rpc_system_system_get_power_info_process(const PB_Main* request, voi
.session = session, .session = session,
.response = response, .response = response,
}; };
furi_hal_power_info_get(rpc_system_system_power_info_callback, &power_info_context); furi_hal_power_info_get(rpc_system_system_power_info_callback, '_', &power_info_context);
free(response); free(response);
} }

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,7.6,, Version,+,8.1,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/cli/cli_vcp.h,,
@@ -1147,7 +1147,7 @@ Function,+,furi_hal_ibutton_start_drive_in_isr,void,
Function,+,furi_hal_ibutton_start_interrupt,void, Function,+,furi_hal_ibutton_start_interrupt,void,
Function,+,furi_hal_ibutton_start_interrupt_in_isr,void, Function,+,furi_hal_ibutton_start_interrupt_in_isr,void,
Function,+,furi_hal_ibutton_stop,void, Function,+,furi_hal_ibutton_stop,void,
Function,+,furi_hal_info_get,void,"FuriHalInfoValueCallback, void*" Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*"
Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*" Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*"
Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t
Function,+,furi_hal_infrared_async_rx_set_timeout_isr_callback,void,"FuriHalInfraredRxTimeoutCallback, void*" Function,+,furi_hal_infrared_async_rx_set_timeout_isr_callback,void,"FuriHalInfraredRxTimeoutCallback, void*"
@@ -1212,10 +1212,10 @@ Function,+,furi_hal_nfc_tx_rx_full,_Bool,FuriHalNfcTxRxContext*
Function,-,furi_hal_os_init,void, Function,-,furi_hal_os_init,void,
Function,+,furi_hal_os_tick,void, Function,+,furi_hal_os_tick,void,
Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_check_otg_status,void,
Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*"
Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_deep_sleep_available,_Bool,
Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_external_3_3v,void,
Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_disable_otg,void,
Function,+,furi_hal_power_dump_state,void,
Function,+,furi_hal_power_enable_external_3_3v,void, Function,+,furi_hal_power_enable_external_3_3v,void,
Function,+,furi_hal_power_enable_otg,void, Function,+,furi_hal_power_enable_otg,void,
Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_gauge_is_ok,_Bool,
@@ -1228,7 +1228,7 @@ Function,+,furi_hal_power_get_battery_temperature,float,FuriHalPowerIC
Function,+,furi_hal_power_get_battery_voltage,float,FuriHalPowerIC Function,+,furi_hal_power_get_battery_voltage,float,FuriHalPowerIC
Function,+,furi_hal_power_get_pct,uint8_t, Function,+,furi_hal_power_get_pct,uint8_t,
Function,+,furi_hal_power_get_usb_voltage,float, Function,+,furi_hal_power_get_usb_voltage,float,
Function,+,furi_hal_power_info_get,void,"FuriHalPowerInfoCallback, void*" Function,+,furi_hal_power_info_get,void,"PropertyValueCallback, char, void*"
Function,-,furi_hal_power_init,void, Function,-,furi_hal_power_init,void,
Function,+,furi_hal_power_insomnia_enter,void, Function,+,furi_hal_power_insomnia_enter,void,
Function,+,furi_hal_power_insomnia_exit,void, Function,+,furi_hal_power_insomnia_exit,void,
@@ -2113,6 +2113,7 @@ Function,+,powf,float,"float, float"
Function,-,powl,long double,"long double, long double" Function,-,powl,long double,"long double, long double"
Function,-,printf,int,"const char*, ..." Function,-,printf,int,"const char*, ..."
Function,-,prng_successor,uint32_t,"uint32_t, uint32_t" Function,-,prng_successor,uint32_t,"uint32_t, uint32_t"
Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..."
Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t"
Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t"
Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t" Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t"
@@ -2366,10 +2367,14 @@ Function,+,rpc_session_set_context,void,"RpcSession*, void*"
Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback" Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback"
Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback" Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback"
Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool" Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool"
Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t"
Function,+,rpc_system_app_get_data,const char*,RpcAppSystem* Function,+,rpc_system_app_get_data,const char*,RpcAppSystem*
Function,+,rpc_system_app_send_exited,void,RpcAppSystem* Function,+,rpc_system_app_send_exited,void,RpcAppSystem*
Function,+,rpc_system_app_send_started,void,RpcAppSystem* Function,+,rpc_system_app_send_started,void,RpcAppSystem*
Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*" Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*"
Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcAppSystemDataExchangeCallback, void*"
Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t"
Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*"
Function,-,rpmatch,int,const char* Function,-,rpmatch,int,const char*
Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t"
Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t"
1 entry status name type params
2 Version + 7.6 8.1
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
1147 Function + furi_hal_ibutton_start_interrupt void
1148 Function + furi_hal_ibutton_start_interrupt_in_isr void
1149 Function + furi_hal_ibutton_stop void
1150 Function + furi_hal_info_get void FuriHalInfoValueCallback, void* PropertyValueCallback, char, void*
1151 Function + furi_hal_infrared_async_rx_set_capture_isr_callback void FuriHalInfraredRxCaptureCallback, void*
1152 Function + furi_hal_infrared_async_rx_set_timeout void uint32_t
1153 Function + furi_hal_infrared_async_rx_set_timeout_isr_callback void FuriHalInfraredRxTimeoutCallback, void*
1212 Function - furi_hal_os_init void
1213 Function + furi_hal_os_tick void
1214 Function + furi_hal_power_check_otg_status void
1215 Function + furi_hal_power_debug_get void PropertyValueCallback, void*
1216 Function + furi_hal_power_deep_sleep_available _Bool
1217 Function + furi_hal_power_disable_external_3_3v void
1218 Function + furi_hal_power_disable_otg void
Function + furi_hal_power_dump_state void
1219 Function + furi_hal_power_enable_external_3_3v void
1220 Function + furi_hal_power_enable_otg void
1221 Function + furi_hal_power_gauge_is_ok _Bool
1228 Function + furi_hal_power_get_battery_voltage float FuriHalPowerIC
1229 Function + furi_hal_power_get_pct uint8_t
1230 Function + furi_hal_power_get_usb_voltage float
1231 Function + furi_hal_power_info_get void FuriHalPowerInfoCallback, void* PropertyValueCallback, char, void*
1232 Function - furi_hal_power_init void
1233 Function + furi_hal_power_insomnia_enter void
1234 Function + furi_hal_power_insomnia_exit void
2113 Function - powl long double long double, long double
2114 Function - printf int const char*, ...
2115 Function - prng_successor uint32_t uint32_t, uint32_t
2116 Function + property_value_out void PropertyValueContext*, const char*, unsigned int, ...
2117 Function + protocol_dict_alloc ProtocolDict* const ProtocolBase**, size_t
2118 Function + protocol_dict_decoders_feed ProtocolId ProtocolDict*, _Bool, uint32_t
2119 Function + protocol_dict_decoders_feed_by_feature ProtocolId ProtocolDict*, uint32_t, _Bool, uint32_t
2367 Function + rpc_session_set_send_bytes_callback void RpcSession*, RpcSendBytesCallback
2368 Function + rpc_session_set_terminated_callback void RpcSession*, RpcSessionTerminatedCallback
2369 Function + rpc_system_app_confirm void RpcAppSystem*, RpcAppSystemEvent, _Bool
2370 Function + rpc_system_app_exchange_data void RpcAppSystem*, const uint8_t*, size_t
2371 Function + rpc_system_app_get_data const char* RpcAppSystem*
2372 Function + rpc_system_app_send_exited void RpcAppSystem*
2373 Function + rpc_system_app_send_started void RpcAppSystem*
2374 Function + rpc_system_app_set_callback void RpcAppSystem*, RpcAppSystemCallback, void*
2375 Function + rpc_system_app_set_data_exchange_callback void RpcAppSystem*, RpcAppSystemDataExchangeCallback, void*
2376 Function + rpc_system_app_set_error_code void RpcAppSystem*, uint32_t
2377 Function + rpc_system_app_set_error_text void RpcAppSystem*, const char*
2378 Function - rpmatch int const char*
2379 Function + saved_struct_load _Bool const char*, void*, size_t, uint8_t, uint8_t
2380 Function + saved_struct_save _Bool const char*, void*, size_t, uint8_t, uint8_t

View File

@@ -8,16 +8,25 @@
#include <furi.h> #include <furi.h>
#include <protobuf_version.h> #include <protobuf_version.h>
void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) {
FuriString* value; FuriString* key = furi_string_alloc();
value = furi_string_alloc(); FuriString* value = furi_string_alloc();
PropertyValueContext property_context = {
.key = key, .value = value, .out = out, .sep = sep, .last = false, .context = context};
// Device Info version // Device Info version
out("device_info_major", "2", false, context); if(sep == '.') {
out("device_info_minor", "0", false, context); property_value_out(&property_context, NULL, 2, "format", "major", "3");
property_value_out(&property_context, NULL, 2, "format", "minor", "0");
} else {
property_value_out(&property_context, NULL, 3, "device", "info", "major", "2");
property_value_out(&property_context, NULL, 3, "device", "info", "minor", "0");
}
// Model name // Model name
out("hardware_model", furi_hal_version_get_model_name(), false, context); property_value_out(
&property_context, NULL, 2, "hardware", "model", furi_hal_version_get_model_name());
// Unique ID // Unique ID
furi_string_reset(value); furi_string_reset(value);
@@ -25,93 +34,211 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) {
for(size_t i = 0; i < furi_hal_version_uid_size(); i++) { for(size_t i = 0; i < furi_hal_version_uid_size(); i++) {
furi_string_cat_printf(value, "%02X", uid[i]); furi_string_cat_printf(value, "%02X", uid[i]);
} }
out("hardware_uid", furi_string_get_cstr(value), false, context); property_value_out(&property_context, NULL, 2, "hardware", "uid", furi_string_get_cstr(value));
// OTP Revision // OTP Revision
furi_string_printf(value, "%d", furi_hal_version_get_otp_version()); property_value_out(
out("hardware_otp_ver", furi_string_get_cstr(value), false, context); &property_context, "%d", 3, "hardware", "otp", "ver", furi_hal_version_get_otp_version());
furi_string_printf(value, "%lu", furi_hal_version_get_hw_timestamp()); property_value_out(
out("hardware_timestamp", furi_string_get_cstr(value), false, context); &property_context, "%lu", 2, "hardware", "timestamp", furi_hal_version_get_hw_timestamp());
// Board Revision // Board Revision
furi_string_printf(value, "%d", furi_hal_version_get_hw_version()); property_value_out(
out("hardware_ver", furi_string_get_cstr(value), false, context); &property_context, "%d", 2, "hardware", "ver", furi_hal_version_get_hw_version());
furi_string_printf(value, "%d", furi_hal_version_get_hw_target()); property_value_out(
out("hardware_target", furi_string_get_cstr(value), false, context); &property_context, "%d", 2, "hardware", "target", furi_hal_version_get_hw_target());
furi_string_printf(value, "%d", furi_hal_version_get_hw_body()); property_value_out(
out("hardware_body", furi_string_get_cstr(value), false, context); &property_context, "%d", 2, "hardware", "body", furi_hal_version_get_hw_body());
furi_string_printf(value, "%d", furi_hal_version_get_hw_connect()); property_value_out(
out("hardware_connect", furi_string_get_cstr(value), false, context); &property_context, "%d", 2, "hardware", "connect", furi_hal_version_get_hw_connect());
furi_string_printf(value, "%d", furi_hal_version_get_hw_display()); property_value_out(
out("hardware_display", furi_string_get_cstr(value), false, context); &property_context, "%d", 2, "hardware", "display", furi_hal_version_get_hw_display());
// Board Personification // Board Personification
furi_string_printf(value, "%d", furi_hal_version_get_hw_color()); property_value_out(
out("hardware_color", furi_string_get_cstr(value), false, context); &property_context, "%d", 2, "hardware", "color", furi_hal_version_get_hw_color());
furi_string_printf(value, "%d", furi_hal_version_get_hw_region());
out("hardware_region", furi_string_get_cstr(value), false, context); if(sep == '.') {
out("hardware_region_provisioned", furi_hal_region_get_name(), false, context); property_value_out(
&property_context,
"%d",
3,
"hardware",
"region",
"builtin",
furi_hal_version_get_hw_region());
} else {
property_value_out(
&property_context, "%d", 2, "hardware", "region", furi_hal_version_get_hw_region());
}
property_value_out(
&property_context,
NULL,
3,
"hardware",
"region",
"provisioned",
furi_hal_region_get_name());
const char* name = furi_hal_version_get_name_ptr(); const char* name = furi_hal_version_get_name_ptr();
if(name) { if(name) {
out("hardware_name", name, false, context); property_value_out(&property_context, NULL, 2, "hardware", "name", name);
} }
// Firmware version // Firmware version
const Version* firmware_version = furi_hal_version_get_firmware_version(); const Version* firmware_version = furi_hal_version_get_firmware_version();
if(firmware_version) { if(firmware_version) {
out("firmware_commit", version_get_githash(firmware_version), false, context); if(sep == '.') {
out("firmware_commit_dirty", property_value_out(
version_get_dirty_flag(firmware_version) ? "true" : "false", &property_context,
false, NULL,
context); 3,
out("firmware_branch", version_get_gitbranch(firmware_version), false, context); "firmware",
out("firmware_branch_num", version_get_gitbranchnum(firmware_version), false, context); "commit",
out("firmware_version", version_get_version(firmware_version), false, context); "hash",
out("firmware_build_date", version_get_builddate(firmware_version), false, context); version_get_githash(firmware_version));
furi_string_printf(value, "%d", version_get_target(firmware_version)); } else {
out("firmware_target", furi_string_get_cstr(value), false, context); property_value_out(
&property_context,
NULL,
2,
"firmware",
"commit",
version_get_githash(firmware_version));
}
property_value_out(
&property_context,
NULL,
3,
"firmware",
"commit",
"dirty",
version_get_dirty_flag(firmware_version) ? "true" : "false");
if(sep == '.') {
property_value_out(
&property_context,
NULL,
3,
"firmware",
"branch",
"name",
version_get_gitbranch(firmware_version));
} else {
property_value_out(
&property_context,
NULL,
2,
"firmware",
"branch",
version_get_gitbranch(firmware_version));
}
property_value_out(
&property_context,
NULL,
3,
"firmware",
"branch",
"num",
version_get_gitbranchnum(firmware_version));
property_value_out(
&property_context,
NULL,
2,
"firmware",
"version",
version_get_version(firmware_version));
property_value_out(
&property_context,
NULL,
3,
"firmware",
"build",
"date",
version_get_builddate(firmware_version));
property_value_out(
&property_context, "%d", 2, "firmware", "target", version_get_target(firmware_version));
} }
if(furi_hal_bt_is_alive()) { if(furi_hal_bt_is_alive()) {
const BleGlueC2Info* ble_c2_info = ble_glue_get_c2_info(); const BleGlueC2Info* ble_c2_info = ble_glue_get_c2_info();
out("radio_alive", "true", false, context); property_value_out(&property_context, NULL, 2, "radio", "alive", "true");
out("radio_mode", ble_c2_info->mode == BleGlueC2ModeFUS ? "FUS" : "Stack", false, context); property_value_out(
&property_context,
NULL,
2,
"radio",
"mode",
ble_c2_info->mode == BleGlueC2ModeFUS ? "FUS" : "Stack");
// FUS Info // FUS Info
furi_string_printf(value, "%d", ble_c2_info->FusVersionMajor); property_value_out(
out("radio_fus_major", furi_string_get_cstr(value), false, context); &property_context, "%d", 3, "radio", "fus", "major", ble_c2_info->FusVersionMajor);
furi_string_printf(value, "%d", ble_c2_info->FusVersionMinor); property_value_out(
out("radio_fus_minor", furi_string_get_cstr(value), false, context); &property_context, "%d", 3, "radio", "fus", "minor", ble_c2_info->FusVersionMinor);
furi_string_printf(value, "%d", ble_c2_info->FusVersionSub); property_value_out(
out("radio_fus_sub", furi_string_get_cstr(value), false, context); &property_context, "%d", 3, "radio", "fus", "sub", ble_c2_info->FusVersionSub);
furi_string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2B); property_value_out(
out("radio_fus_sram2b", furi_string_get_cstr(value), false, context); &property_context,
furi_string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2A); "%dK",
out("radio_fus_sram2a", furi_string_get_cstr(value), false, context); 3,
furi_string_printf(value, "%dK", ble_c2_info->FusMemorySizeFlash * 4); "radio",
out("radio_fus_flash", furi_string_get_cstr(value), false, context); "fus",
"sram2b",
ble_c2_info->FusMemorySizeSram2B);
property_value_out(
&property_context,
"%dK",
3,
"radio",
"fus",
"sram2a",
ble_c2_info->FusMemorySizeSram2A);
property_value_out(
&property_context,
"%dK",
3,
"radio",
"fus",
"flash",
ble_c2_info->FusMemorySizeFlash * 4);
// Stack Info // Stack Info
furi_string_printf(value, "%d", ble_c2_info->StackType); property_value_out(
out("radio_stack_type", furi_string_get_cstr(value), false, context); &property_context, "%d", 3, "radio", "stack", "type", ble_c2_info->StackType);
furi_string_printf(value, "%d", ble_c2_info->VersionMajor); property_value_out(
out("radio_stack_major", furi_string_get_cstr(value), false, context); &property_context, "%d", 3, "radio", "stack", "major", ble_c2_info->VersionMajor);
furi_string_printf(value, "%d", ble_c2_info->VersionMinor); property_value_out(
out("radio_stack_minor", furi_string_get_cstr(value), false, context); &property_context, "%d", 3, "radio", "stack", "minor", ble_c2_info->VersionMinor);
furi_string_printf(value, "%d", ble_c2_info->VersionSub); property_value_out(
out("radio_stack_sub", furi_string_get_cstr(value), false, context); &property_context, "%d", 3, "radio", "stack", "sub", ble_c2_info->VersionSub);
furi_string_printf(value, "%d", ble_c2_info->VersionBranch); property_value_out(
out("radio_stack_branch", furi_string_get_cstr(value), false, context); &property_context, "%d", 3, "radio", "stack", "branch", ble_c2_info->VersionBranch);
furi_string_printf(value, "%d", ble_c2_info->VersionReleaseType); property_value_out(
out("radio_stack_release", furi_string_get_cstr(value), false, context); &property_context,
furi_string_printf(value, "%dK", ble_c2_info->MemorySizeSram2B); "%d",
out("radio_stack_sram2b", furi_string_get_cstr(value), false, context); 3,
furi_string_printf(value, "%dK", ble_c2_info->MemorySizeSram2A); "radio",
out("radio_stack_sram2a", furi_string_get_cstr(value), false, context); "stack",
furi_string_printf(value, "%dK", ble_c2_info->MemorySizeSram1); "release",
out("radio_stack_sram1", furi_string_get_cstr(value), false, context); ble_c2_info->VersionReleaseType);
furi_string_printf(value, "%dK", ble_c2_info->MemorySizeFlash * 4); property_value_out(
out("radio_stack_flash", furi_string_get_cstr(value), false, context); &property_context, "%dK", 3, "radio", "stack", "sram2b", ble_c2_info->MemorySizeSram2B);
property_value_out(
&property_context, "%dK", 3, "radio", "stack", "sram2a", ble_c2_info->MemorySizeSram2A);
property_value_out(
&property_context, "%dK", 3, "radio", "stack", "sram1", ble_c2_info->MemorySizeSram1);
property_value_out(
&property_context,
"%dK",
3,
"radio",
"stack",
"flash",
ble_c2_info->MemorySizeFlash * 4);
// Mac address // Mac address
furi_string_reset(value); furi_string_reset(value);
@@ -119,23 +246,33 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) {
for(size_t i = 0; i < 6; i++) { for(size_t i = 0; i < 6; i++) {
furi_string_cat_printf(value, "%02X", ble_mac[i]); furi_string_cat_printf(value, "%02X", ble_mac[i]);
} }
out("radio_ble_mac", furi_string_get_cstr(value), false, context); property_value_out(
&property_context, NULL, 3, "radio", "ble", "mac", furi_string_get_cstr(value));
// Signature verification // Signature verification
uint8_t enclave_keys = 0; uint8_t enclave_keys = 0;
uint8_t enclave_valid_keys = 0; uint8_t enclave_valid_keys = 0;
bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys); bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys);
furi_string_printf(value, "%d", enclave_valid_keys); if(sep == '.') {
out("enclave_valid_keys", furi_string_get_cstr(value), false, context); property_value_out(
out("enclave_valid", enclave_valid ? "true" : "false", false, context); &property_context, "%d", 3, "enclave", "keys", "valid", enclave_valid_keys);
} else {
property_value_out(
&property_context, "%d", 3, "enclave", "valid", "keys", enclave_valid_keys);
}
property_value_out(
&property_context, NULL, 2, "enclave", "valid", enclave_valid ? "true" : "false");
} else { } else {
out("radio_alive", "false", false, context); property_value_out(&property_context, NULL, 2, "radio", "alive", "false");
} }
furi_string_printf(value, "%u", PROTOBUF_MAJOR_VERSION); property_value_out(
out("protobuf_version_major", furi_string_get_cstr(value), false, context); &property_context, "%u", 3, "protobuf", "version", "major", PROTOBUF_MAJOR_VERSION);
furi_string_printf(value, "%u", PROTOBUF_MINOR_VERSION); property_context.last = true;
out("protobuf_version_minor", furi_string_get_cstr(value), true, context); property_value_out(
&property_context, "%u", 3, "protobuf", "version", "minor", PROTOBUF_MINOR_VERSION);
furi_string_free(key);
furi_string_free(value); furi_string_free(value);
} }

View File

@@ -81,6 +81,10 @@ void furi_hal_memory_init() {
} }
void* furi_hal_memory_alloc(size_t size) { void* furi_hal_memory_alloc(size_t size) {
if(FURI_IS_IRQ_MODE()) {
furi_crash("memmgt in ISR");
}
if(furi_hal_memory == NULL) { if(furi_hal_memory == NULL) {
return NULL; return NULL;
} }

View File

@@ -422,76 +422,6 @@ float furi_hal_power_get_usb_voltage() {
return ret; return ret;
} }
void furi_hal_power_dump_state() {
BatteryStatus battery_status;
OperationStatus operation_status;
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR ||
bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) ==
BQ27220_ERROR) {
printf("Failed to get bq27220 status. Communication error.\r\n");
} else {
// Operation status register
printf(
"bq27220: CALMD: %d, SEC: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n",
operation_status.CALMD,
operation_status.SEC,
operation_status.EDV2,
operation_status.VDQ,
operation_status.INITCOMP,
operation_status.SMTH,
operation_status.BTPINT,
operation_status.CFGUPDATE);
// Battery status register, part 1
printf(
"bq27220: CHGINH: %d, FC: %d, OTD: %d, OTC: %d, SLEEP: %d, OCVFAIL: %d, OCVCOMP: %d, FD: %d\r\n",
battery_status.CHGINH,
battery_status.FC,
battery_status.OTD,
battery_status.OTC,
battery_status.SLEEP,
battery_status.OCVFAIL,
battery_status.OCVCOMP,
battery_status.FD);
// Battery status register, part 2
printf(
"bq27220: DSG: %d, SYSDWN: %d, TDA: %d, BATTPRES: %d, AUTH_GD: %d, OCVGD: %d, TCA: %d, RSVD: %d\r\n",
battery_status.DSG,
battery_status.SYSDWN,
battery_status.TDA,
battery_status.BATTPRES,
battery_status.AUTH_GD,
battery_status.OCVGD,
battery_status.TCA,
battery_status.RSVD);
// Voltage and current info
printf(
"bq27220: Full capacity: %dmAh, Design capacity: %dmAh, Remaining capacity: %dmAh, State of Charge: %d%%, State of health: %d%%\r\n",
bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power),
bq27220_get_design_capacity(&furi_hal_i2c_handle_power),
bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power),
bq27220_get_state_of_charge(&furi_hal_i2c_handle_power),
bq27220_get_state_of_health(&furi_hal_i2c_handle_power));
printf(
"bq27220: Voltage: %dmV, Current: %dmA, Temperature: %dC\r\n",
bq27220_get_voltage(&furi_hal_i2c_handle_power),
bq27220_get_current(&furi_hal_i2c_handle_power),
(int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge));
}
printf(
"bq25896: VBUS: %d, VSYS: %d, VBAT: %d, Current: %d, NTC: %ldm%%\r\n",
bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power),
bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power),
bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power),
bq25896_get_vbat_current(&furi_hal_i2c_handle_power),
bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power));
furi_hal_i2c_release(&furi_hal_i2c_handle_power);
}
void furi_hal_power_enable_external_3_3v() { void furi_hal_power_enable_external_3_3v() {
furi_hal_gpio_write(&periph_power, 1); furi_hal_gpio_write(&periph_power, 1);
} }
@@ -526,57 +456,227 @@ void furi_hal_power_suppress_charge_exit() {
} }
} }
void furi_hal_power_info_get(FuriHalPowerInfoCallback out, void* context) { void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) {
furi_assert(out); furi_assert(out);
FuriString* value; FuriString* value = furi_string_alloc();
value = furi_string_alloc(); FuriString* key = furi_string_alloc();
// Power Info version PropertyValueContext property_context = {
out("power_info_major", "1", false, context); .key = key, .value = value, .out = out, .sep = sep, .last = false, .context = context};
out("power_info_minor", "0", false, context);
if(sep == '.') {
property_value_out(&property_context, NULL, 2, "format", "major", "2");
property_value_out(&property_context, NULL, 2, "format", "minor", "0");
} else {
property_value_out(&property_context, NULL, 3, "power", "info", "major", "1");
property_value_out(&property_context, NULL, 3, "power", "info", "minor", "0");
}
uint8_t charge = furi_hal_power_get_pct(); uint8_t charge = furi_hal_power_get_pct();
property_value_out(&property_context, "%u", 2, "charge", "level", charge);
furi_string_printf(value, "%u", charge); const char* charge_state;
out("charge_level", furi_string_get_cstr(value), false, context);
if(furi_hal_power_is_charging()) { if(furi_hal_power_is_charging()) {
if(charge < 100) { if(charge < 100) {
furi_string_printf(value, "charging"); charge_state = "charging";
} else { } else {
furi_string_printf(value, "charged"); charge_state = "charged";
} }
} else { } else {
furi_string_printf(value, "discharging"); charge_state = "discharging";
} }
out("charge_state", furi_string_get_cstr(value), false, context);
property_value_out(&property_context, NULL, 2, "charge", "state", charge_state);
uint16_t voltage = uint16_t voltage =
(uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f); (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f);
furi_string_printf(value, "%u", voltage); property_value_out(&property_context, "%u", 2, "battery", "voltage", voltage);
out("battery_voltage", furi_string_get_cstr(value), false, context);
int16_t current = int16_t current =
(int16_t)(furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000.f); (int16_t)(furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000.f);
furi_string_printf(value, "%d", current); property_value_out(&property_context, "%d", 2, "battery", "current", current);
out("battery_current", furi_string_get_cstr(value), false, context);
int16_t temperature = (int16_t)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); int16_t temperature = (int16_t)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge);
furi_string_printf(value, "%d", temperature); property_value_out(&property_context, "%d", 2, "battery", "temp", temperature);
out("gauge_temp", furi_string_get_cstr(value), false, context); property_value_out(
&property_context, "%u", 2, "battery", "health", furi_hal_power_get_bat_health_pct());
furi_string_printf(value, "%u", furi_hal_power_get_bat_health_pct()); property_value_out(
out("battery_health", furi_string_get_cstr(value), false, context); &property_context,
"%lu",
furi_string_printf(value, "%lu", furi_hal_power_get_battery_remaining_capacity()); 2,
out("capacity_remain", furi_string_get_cstr(value), false, context); "capacity",
"remain",
furi_string_printf(value, "%lu", furi_hal_power_get_battery_full_capacity()); furi_hal_power_get_battery_remaining_capacity());
out("capacity_full", furi_string_get_cstr(value), false, context); property_value_out(
&property_context,
furi_string_printf(value, "%lu", furi_hal_power_get_battery_design_capacity()); "%lu",
out("capacity_design", furi_string_get_cstr(value), true, context); 2,
"capacity",
"full",
furi_hal_power_get_battery_full_capacity());
property_context.last = true;
property_value_out(
&property_context,
"%lu",
2,
"capacity",
"design",
furi_hal_power_get_battery_design_capacity());
furi_string_free(key);
furi_string_free(value); furi_string_free(value);
} }
void furi_hal_power_debug_get(PropertyValueCallback out, void* context) {
furi_assert(out);
FuriString* value = furi_string_alloc();
FuriString* key = furi_string_alloc();
PropertyValueContext property_context = {
.key = key, .value = value, .out = out, .sep = '.', .last = false, .context = context};
BatteryStatus battery_status;
OperationStatus operation_status;
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
// Power Debug version
property_value_out(&property_context, NULL, 2, "format", "major", "1");
property_value_out(&property_context, NULL, 2, "format", "minor", "0");
property_value_out(
&property_context,
"%d",
2,
"charger",
"vbus",
bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power));
property_value_out(
&property_context,
"%d",
2,
"charger",
"vsys",
bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power));
property_value_out(
&property_context,
"%d",
2,
"charger",
"vbat",
bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power));
property_value_out(
&property_context,
"%d",
2,
"charger",
"current",
bq25896_get_vbat_current(&furi_hal_i2c_handle_power));
const uint32_t ntc_mpct = bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power);
if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) != BQ27220_ERROR &&
bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) !=
BQ27220_ERROR) {
property_value_out(&property_context, "%lu", 2, "charger", "ntc", ntc_mpct);
property_value_out(&property_context, "%d", 2, "gauge", "calmd", operation_status.CALMD);
property_value_out(&property_context, "%d", 2, "gauge", "sec", operation_status.SEC);
property_value_out(&property_context, "%d", 2, "gauge", "edv2", operation_status.EDV2);
property_value_out(&property_context, "%d", 2, "gauge", "vdq", operation_status.VDQ);
property_value_out(
&property_context, "%d", 2, "gauge", "initcomp", operation_status.INITCOMP);
property_value_out(&property_context, "%d", 2, "gauge", "smth", operation_status.SMTH);
property_value_out(&property_context, "%d", 2, "gauge", "btpint", operation_status.BTPINT);
property_value_out(
&property_context, "%d", 2, "gauge", "cfgupdate", operation_status.CFGUPDATE);
// Battery status register, part 1
property_value_out(&property_context, "%d", 2, "gauge", "chginh", battery_status.CHGINH);
property_value_out(&property_context, "%d", 2, "gauge", "fc", battery_status.FC);
property_value_out(&property_context, "%d", 2, "gauge", "otd", battery_status.OTD);
property_value_out(&property_context, "%d", 2, "gauge", "otc", battery_status.OTC);
property_value_out(&property_context, "%d", 2, "gauge", "sleep", battery_status.SLEEP);
property_value_out(&property_context, "%d", 2, "gauge", "ocvfail", battery_status.OCVFAIL);
property_value_out(&property_context, "%d", 2, "gauge", "ocvcomp", battery_status.OCVCOMP);
property_value_out(&property_context, "%d", 2, "gauge", "fd", battery_status.FD);
// Battery status register, part 2
property_value_out(&property_context, "%d", 2, "gauge", "dsg", battery_status.DSG);
property_value_out(&property_context, "%d", 2, "gauge", "sysdwn", battery_status.SYSDWN);
property_value_out(&property_context, "%d", 2, "gauge", "tda", battery_status.TDA);
property_value_out(
&property_context, "%d", 2, "gauge", "battpres", battery_status.BATTPRES);
property_value_out(&property_context, "%d", 2, "gauge", "authgd", battery_status.AUTH_GD);
property_value_out(&property_context, "%d", 2, "gauge", "ocvgd", battery_status.OCVGD);
property_value_out(&property_context, "%d", 2, "gauge", "tca", battery_status.TCA);
property_value_out(&property_context, "%d", 2, "gauge", "rsvd", battery_status.RSVD);
// Voltage and current info
property_value_out(
&property_context,
"%d",
3,
"gauge",
"capacity",
"full",
bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power));
property_value_out(
&property_context,
"%d",
3,
"gauge",
"capacity",
"design",
bq27220_get_design_capacity(&furi_hal_i2c_handle_power));
property_value_out(
&property_context,
"%d",
3,
"gauge",
"capacity",
"remain",
bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power));
property_value_out(
&property_context,
"%d",
3,
"gauge",
"state",
"charge",
bq27220_get_state_of_charge(&furi_hal_i2c_handle_power));
property_value_out(
&property_context,
"%d",
3,
"gauge",
"state",
"health",
bq27220_get_state_of_health(&furi_hal_i2c_handle_power));
property_value_out(
&property_context,
"%d",
2,
"gauge",
"voltage",
bq27220_get_voltage(&furi_hal_i2c_handle_power));
property_value_out(
&property_context,
"%d",
2,
"gauge",
"current",
bq27220_get_current(&furi_hal_i2c_handle_power));
property_context.last = true;
const int battery_temp =
(int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge);
property_value_out(&property_context, "%d", 2, "gauge", "temperature", battery_temp);
} else {
property_context.last = true;
property_value_out(&property_context, "%lu", 2, "charger", "ntc", ntc_mpct);
}
furi_string_free(key);
furi_string_free(value);
furi_hal_i2c_release(&furi_hal_i2c_handle_power);
}

View File

@@ -4,6 +4,7 @@
#include <furi_hal_power.h> #include <furi_hal_power.h>
#include <stm32wbxx_ll_pwr.h> #include <stm32wbxx_ll_pwr.h>
#include <furi.h> #include <furi.h>
#include <toolbox/api_lock.h>
#include "usb.h" #include "usb.h"
@@ -11,35 +12,67 @@
#define USB_RECONNECT_DELAY 500 #define USB_RECONNECT_DELAY 500
typedef enum {
UsbApiEventTypeSetConfig,
UsbApiEventTypeGetConfig,
UsbApiEventTypeLock,
UsbApiEventTypeUnlock,
UsbApiEventTypeIsLocked,
UsbApiEventTypeEnable,
UsbApiEventTypeDisable,
UsbApiEventTypeReinit,
UsbApiEventTypeSetStateCallback,
} UsbApiEventType;
typedef struct {
FuriHalUsbStateCallback callback;
void* context;
} UsbApiEventDataStateCallback;
typedef struct {
FuriHalUsbInterface* interface;
void* context;
} UsbApiEventDataInterface;
typedef union {
UsbApiEventDataStateCallback state_callback;
UsbApiEventDataInterface interface;
} UsbApiEventData;
typedef union {
bool bool_value;
void* void_value;
} UsbApiEventReturnData;
typedef struct {
FuriApiLock lock;
UsbApiEventType type;
UsbApiEventData data;
UsbApiEventReturnData* return_data;
} UsbApiEventMessage;
typedef struct { typedef struct {
FuriThread* thread; FuriThread* thread;
FuriMessageQueue* queue;
bool enabled; bool enabled;
bool connected; bool connected;
bool mode_lock; bool mode_lock;
FuriHalUsbInterface* if_cur; bool request_pending;
FuriHalUsbInterface* if_next; FuriHalUsbInterface* interface;
void* if_ctx; void* interface_context;
FuriHalUsbStateCallback callback; FuriHalUsbStateCallback callback;
void* cb_ctx; void* callback_context;
} UsbSrv; } UsbSrv;
typedef enum { typedef enum {
EventModeChange = (1 << 0), UsbEventReset = (1 << 0),
EventEnable = (1 << 1), UsbEventRequest = (1 << 1),
EventDisable = (1 << 2), UsbEventMessage = (1 << 2),
EventReinit = (1 << 3),
EventReset = (1 << 4),
EventRequest = (1 << 5),
EventModeChangeStart = (1 << 6),
} UsbEvent; } UsbEvent;
#define USB_SRV_ALL_EVENTS \ #define USB_SRV_ALL_EVENTS (UsbEventReset | UsbEventRequest | UsbEventMessage)
(EventModeChange | EventEnable | EventDisable | EventReinit | EventReset | EventRequest | \
EventModeChangeStart)
static UsbSrv usb; PLACE_IN_SECTION("MB_MEM2") static UsbSrv usb = {0};
static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
@@ -74,12 +107,13 @@ void furi_hal_usb_init(void) {
// Reset callback will be enabled after first mode change to avoid getting false reset events // Reset callback will be enabled after first mode change to avoid getting false reset events
usb.enabled = false; usb.enabled = false;
usb.if_cur = NULL; usb.interface = NULL;
NVIC_SetPriority(USB_LP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_SetPriority(USB_LP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
NVIC_SetPriority(USB_HP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); NVIC_SetPriority(USB_HP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
NVIC_EnableIRQ(USB_LP_IRQn); NVIC_EnableIRQ(USB_LP_IRQn);
NVIC_EnableIRQ(USB_HP_IRQn); NVIC_EnableIRQ(USB_HP_IRQn);
usb.queue = furi_message_queue_alloc(1, sizeof(UsbApiEventMessage));
usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL); usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL);
furi_thread_mark_as_service(usb.thread); furi_thread_mark_as_service(usb.thread);
furi_thread_start(usb.thread); furi_thread_start(usb.thread);
@@ -87,53 +121,119 @@ void furi_hal_usb_init(void) {
FURI_LOG_I(TAG, "Init OK"); FURI_LOG_I(TAG, "Init OK");
} }
bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) { static void furi_hal_usb_send_message(UsbApiEventMessage* message) {
if(usb.mode_lock) { furi_message_queue_put(usb.queue, message, FuriWaitForever);
return false; furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventMessage);
} api_lock_wait_unlock_and_free(message->lock);
}
usb.if_next = new_if; bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) {
usb.if_ctx = ctx; UsbApiEventReturnData return_data = {
if(usb.thread == NULL) { .bool_value = false,
// Service thread hasn't started yet, so just save interface mode };
return true;
} UsbApiEventMessage msg = {
furi_assert(usb.thread); .lock = api_lock_alloc_locked(),
furi_thread_flags_set(furi_thread_get_id(usb.thread), EventModeChange); .type = UsbApiEventTypeSetConfig,
return true; .data.interface =
{
.interface = new_if,
.context = ctx,
},
.return_data = &return_data,
};
furi_hal_usb_send_message(&msg);
return return_data.bool_value;
} }
FuriHalUsbInterface* furi_hal_usb_get_config() { FuriHalUsbInterface* furi_hal_usb_get_config() {
return usb.if_cur; UsbApiEventReturnData return_data = {
.void_value = NULL,
};
UsbApiEventMessage msg = {
.lock = api_lock_alloc_locked(),
.type = UsbApiEventTypeGetConfig,
.return_data = &return_data,
};
furi_hal_usb_send_message(&msg);
return return_data.void_value;
} }
void furi_hal_usb_lock() { void furi_hal_usb_lock() {
FURI_LOG_I(TAG, "Mode lock"); UsbApiEventMessage msg = {
usb.mode_lock = true; .lock = api_lock_alloc_locked(),
.type = UsbApiEventTypeLock,
};
furi_hal_usb_send_message(&msg);
} }
void furi_hal_usb_unlock() { void furi_hal_usb_unlock() {
FURI_LOG_I(TAG, "Mode unlock"); UsbApiEventMessage msg = {
usb.mode_lock = false; .lock = api_lock_alloc_locked(),
.type = UsbApiEventTypeUnlock,
};
furi_hal_usb_send_message(&msg);
} }
bool furi_hal_usb_is_locked() { bool furi_hal_usb_is_locked() {
return usb.mode_lock; UsbApiEventReturnData return_data = {
.bool_value = false,
};
UsbApiEventMessage msg = {
.lock = api_lock_alloc_locked(),
.type = UsbApiEventTypeIsLocked,
.return_data = &return_data,
};
furi_hal_usb_send_message(&msg);
return return_data.bool_value;
} }
void furi_hal_usb_disable() { void furi_hal_usb_disable() {
furi_assert(usb.thread); UsbApiEventMessage msg = {
furi_thread_flags_set(furi_thread_get_id(usb.thread), EventDisable); .lock = api_lock_alloc_locked(),
.type = UsbApiEventTypeDisable,
};
furi_hal_usb_send_message(&msg);
} }
void furi_hal_usb_enable() { void furi_hal_usb_enable() {
furi_assert(usb.thread); UsbApiEventMessage msg = {
furi_thread_flags_set(furi_thread_get_id(usb.thread), EventEnable); .lock = api_lock_alloc_locked(),
.type = UsbApiEventTypeEnable,
};
furi_hal_usb_send_message(&msg);
} }
void furi_hal_usb_reinit() { void furi_hal_usb_reinit() {
furi_assert(usb.thread); UsbApiEventMessage msg = {
furi_thread_flags_set(furi_thread_get_id(usb.thread), EventReinit); .lock = api_lock_alloc_locked(),
.type = UsbApiEventTypeReinit,
};
furi_hal_usb_send_message(&msg);
}
void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) {
UsbApiEventMessage msg = {
.lock = api_lock_alloc_locked(),
.type = UsbApiEventTypeSetStateCallback,
.data.state_callback =
{
.callback = cb,
.context = ctx,
},
};
furi_hal_usb_send_message(&msg);
} }
/* Get device / configuration descriptors */ /* Get device / configuration descriptors */
@@ -142,29 +242,29 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
const uint8_t dnumber = req->wValue & 0xFF; const uint8_t dnumber = req->wValue & 0xFF;
const void* desc; const void* desc;
uint16_t len = 0; uint16_t len = 0;
if(usb.if_cur == NULL) return usbd_fail; if(usb.interface == NULL) return usbd_fail;
switch(dtype) { switch(dtype) {
case USB_DTYPE_DEVICE: case USB_DTYPE_DEVICE:
furi_thread_flags_set(furi_thread_get_id(usb.thread), EventRequest); furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventRequest);
if(usb.callback != NULL) { if(usb.callback != NULL) {
usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx); usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.callback_context);
} }
desc = usb.if_cur->dev_descr; desc = usb.interface->dev_descr;
break; break;
case USB_DTYPE_CONFIGURATION: case USB_DTYPE_CONFIGURATION:
desc = usb.if_cur->cfg_descr; desc = usb.interface->cfg_descr;
len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0]; len = ((struct usb_string_descriptor*)(usb.interface->cfg_descr))->wString[0];
break; break;
case USB_DTYPE_STRING: case USB_DTYPE_STRING:
if(dnumber == UsbDevLang) { if(dnumber == UsbDevLang) {
desc = &dev_lang_desc; desc = &dev_lang_desc;
} else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) { } else if((dnumber == UsbDevManuf) && (usb.interface->str_manuf_descr != NULL)) {
desc = usb.if_cur->str_manuf_descr; desc = usb.interface->str_manuf_descr;
} else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) { } else if((dnumber == UsbDevProduct) && (usb.interface->str_prod_descr != NULL)) {
desc = usb.if_cur->str_prod_descr; desc = usb.interface->str_prod_descr;
} else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) { } else if((dnumber == UsbDevSerial) && (usb.interface->str_serial_descr != NULL)) {
desc = usb.if_cur->str_serial_descr; desc = usb.interface->str_serial_descr;
} else } else
return usbd_fail; return usbd_fail;
break; break;
@@ -181,18 +281,13 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
return usbd_ack; return usbd_ack;
} }
void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) {
usb.callback = cb;
usb.cb_ctx = ctx;
}
static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
UNUSED(dev); UNUSED(dev);
UNUSED(event); UNUSED(event);
UNUSED(ep); UNUSED(ep);
furi_thread_flags_set(furi_thread_get_id(usb.thread), EventReset); furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventReset);
if(usb.callback != NULL) { if(usb.callback != NULL) {
usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx); usb.callback(FuriHalUsbStateEventReset, usb.callback_context);
} }
} }
@@ -200,14 +295,14 @@ static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
UNUSED(dev); UNUSED(dev);
UNUSED(event); UNUSED(event);
UNUSED(ep); UNUSED(ep);
if((usb.if_cur != NULL) && (usb.connected == true)) { if((usb.interface != NULL) && (usb.connected == true)) {
usb.connected = false; usb.connected = false;
usb.if_cur->suspend(&udev); usb.interface->suspend(&udev);
furi_hal_power_insomnia_exit(); furi_hal_power_insomnia_exit();
} }
if(usb.callback != NULL) { if(usb.callback != NULL) {
usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx); usb.callback(FuriHalUsbStateEventSuspend, usb.callback_context);
} }
} }
@@ -215,101 +310,161 @@ static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
UNUSED(dev); UNUSED(dev);
UNUSED(event); UNUSED(event);
UNUSED(ep); UNUSED(ep);
if((usb.if_cur != NULL) && (usb.connected == false)) { if((usb.interface != NULL) && (usb.connected == false)) {
usb.connected = true; usb.connected = true;
usb.if_cur->wakeup(&udev); usb.interface->wakeup(&udev);
furi_hal_power_insomnia_enter(); furi_hal_power_insomnia_enter();
} }
if(usb.callback != NULL) { if(usb.callback != NULL) {
usb.callback(FuriHalUsbStateEventWakeup, usb.cb_ctx); usb.callback(FuriHalUsbStateEventWakeup, usb.callback_context);
} }
} }
static void usb_process_mode_start(FuriHalUsbInterface* interface, void* context) {
if(usb.interface != NULL) {
usb.interface->deinit(&udev);
}
__disable_irq();
usb.interface = interface;
usb.interface_context = context;
__enable_irq();
if(interface != NULL) {
interface->init(&udev, interface, context);
usbd_reg_event(&udev, usbd_evt_reset, reset_evt);
FURI_LOG_I(TAG, "USB Mode change done");
usb.enabled = true;
}
}
static void usb_process_mode_change(FuriHalUsbInterface* interface, void* context) {
if(interface != usb.interface) {
if(usb.enabled) {
// Disable current interface
susp_evt(&udev, 0, 0);
usbd_connect(&udev, false);
usb.enabled = false;
furi_delay_ms(USB_RECONNECT_DELAY);
}
usb_process_mode_start(interface, context);
}
}
static void usb_process_mode_reinit() {
// Temporary disable callback to avoid getting false reset events
usbd_reg_event(&udev, usbd_evt_reset, NULL);
FURI_LOG_I(TAG, "USB Reinit");
susp_evt(&udev, 0, 0);
usbd_connect(&udev, false);
usb.enabled = false;
usbd_enable(&udev, false);
usbd_enable(&udev, true);
furi_delay_ms(USB_RECONNECT_DELAY);
usb_process_mode_start(usb.interface, usb.interface_context);
}
static bool usb_process_set_config(FuriHalUsbInterface* interface, void* context) {
if(usb.mode_lock) {
return false;
} else {
usb_process_mode_change(interface, context);
return true;
}
}
static void usb_process_enable(bool enable) {
if(enable) {
if((!usb.enabled) && (usb.interface != NULL)) {
usbd_connect(&udev, true);
usb.enabled = true;
FURI_LOG_I(TAG, "USB Enable");
}
} else {
if(usb.enabled) {
susp_evt(&udev, 0, 0);
usbd_connect(&udev, false);
usb.enabled = false;
usb.request_pending = false;
FURI_LOG_I(TAG, "USB Disable");
}
}
}
static void usb_process_message(UsbApiEventMessage* message) {
switch(message->type) {
case UsbApiEventTypeSetConfig:
message->return_data->bool_value = usb_process_set_config(
message->data.interface.interface, message->data.interface.context);
break;
case UsbApiEventTypeGetConfig:
message->return_data->void_value = usb.interface;
break;
case UsbApiEventTypeLock:
FURI_LOG_I(TAG, "Mode lock");
usb.mode_lock = true;
break;
case UsbApiEventTypeUnlock:
FURI_LOG_I(TAG, "Mode unlock");
usb.mode_lock = false;
break;
case UsbApiEventTypeIsLocked:
message->return_data->bool_value = usb.mode_lock;
break;
case UsbApiEventTypeDisable:
usb_process_enable(false);
break;
case UsbApiEventTypeEnable:
usb_process_enable(true);
break;
case UsbApiEventTypeReinit:
usb_process_mode_reinit();
break;
case UsbApiEventTypeSetStateCallback:
usb.callback = message->data.state_callback.callback;
usb.callback_context = message->data.state_callback.context;
break;
}
api_lock_unlock(message->lock);
}
static int32_t furi_hal_usb_thread(void* context) { static int32_t furi_hal_usb_thread(void* context) {
UNUSED(context); UNUSED(context);
bool usb_request_pending = false;
uint8_t usb_wait_time = 0; uint8_t usb_wait_time = 0;
FuriHalUsbInterface* if_new = NULL;
FuriHalUsbInterface* if_ctx_new = NULL;
if(usb.if_next != NULL) { if(furi_message_queue_get_count(usb.queue) > 0) {
furi_thread_flags_set(furi_thread_get_id(usb.thread), EventModeChange); furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventMessage);
} }
while(true) { while(true) {
uint32_t flags = furi_thread_flags_wait(USB_SRV_ALL_EVENTS, FuriFlagWaitAny, 500); uint32_t flags = furi_thread_flags_wait(USB_SRV_ALL_EVENTS, FuriFlagWaitAny, 500);
{
UsbApiEventMessage message;
if(furi_message_queue_get(usb.queue, &message, 0) == FuriStatusOk) {
usb_process_message(&message);
}
}
if((flags & FuriFlagError) == 0) { if((flags & FuriFlagError) == 0) {
if(flags & EventModeChange) { if(flags & UsbEventReset) {
if(usb.if_next != usb.if_cur) {
if_new = usb.if_next;
if_ctx_new = usb.if_ctx;
if(usb.enabled) { // Disable current interface
susp_evt(&udev, 0, 0);
usbd_connect(&udev, false);
usb.enabled = false;
furi_delay_ms(USB_RECONNECT_DELAY);
}
flags |= EventModeChangeStart;
}
}
if(flags & EventReinit) {
// Temporary disable callback to avoid getting false reset events
usbd_reg_event(&udev, usbd_evt_reset, NULL);
FURI_LOG_I(TAG, "USB Reinit");
susp_evt(&udev, 0, 0);
usbd_connect(&udev, false);
usb.enabled = false;
usbd_enable(&udev, false);
usbd_enable(&udev, true);
if_new = usb.if_cur;
furi_delay_ms(USB_RECONNECT_DELAY);
flags |= EventModeChangeStart;
}
if(flags & EventModeChangeStart) { // Second stage of mode change process
if(usb.if_cur != NULL) {
usb.if_cur->deinit(&udev);
}
if(if_new != NULL) {
if_new->init(&udev, if_new, if_ctx_new);
usbd_reg_event(&udev, usbd_evt_reset, reset_evt);
FURI_LOG_I(TAG, "USB Mode change done");
usb.enabled = true;
}
usb.if_cur = if_new;
}
if(flags & EventEnable) {
if((!usb.enabled) && (usb.if_cur != NULL)) {
usbd_connect(&udev, true);
usb.enabled = true;
FURI_LOG_I(TAG, "USB Enable");
}
}
if(flags & EventDisable) {
if(usb.enabled) { if(usb.enabled) {
susp_evt(&udev, 0, 0); usb.request_pending = true;
usbd_connect(&udev, false);
usb.enabled = false;
usb_request_pending = false;
FURI_LOG_I(TAG, "USB Disable");
}
}
if(flags & EventReset) {
if(usb.enabled) {
usb_request_pending = true;
usb_wait_time = 0; usb_wait_time = 0;
} }
} }
if(flags & EventRequest) { if(flags & UsbEventRequest) {
usb_request_pending = false; usb.request_pending = false;
} }
} else if(usb_request_pending) { } else if(usb.request_pending) {
usb_wait_time++; usb_wait_time++;
if(usb_wait_time > 4) { if(usb_wait_time > 4) {
furi_hal_usb_reinit(); usb_process_mode_reinit();
usb_request_pending = false; usb.request_pending = false;
} }
} }
} }

View File

@@ -7,27 +7,20 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <core/string.h>
#include <toolbox/property.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/** Callback type called every time another key-value pair of device information is ready
*
* @param key[in] device information type identifier
* @param value[in] device information value
* @param last[in] whether the passed key-value pair is the last one
* @param context[in] to pass to callback
*/
typedef void (
*FuriHalInfoValueCallback)(const char* key, const char* value, bool last, void* context);
/** Get device information /** Get device information
* *
* @param[in] callback callback to provide with new data * @param[in] callback callback to provide with new data
* @param[in] sep category separator character
* @param[in] context context to pass to callback * @param[in] context context to pass to callback
*/ */
void furi_hal_info_get(FuriHalInfoValueCallback callback, void* context); void furi_hal_info_get(PropertyValueCallback callback, char sep, void* context);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -7,6 +7,8 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <core/string.h>
#include <toolbox/property.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -167,10 +169,6 @@ float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic);
*/ */
float furi_hal_power_get_usb_voltage(); float furi_hal_power_get_usb_voltage();
/** Get power system component state
*/
void furi_hal_power_dump_state();
/** Enable 3.3v on external gpio and sd card /** Enable 3.3v on external gpio and sd card
*/ */
void furi_hal_power_enable_external_3_3v(); void furi_hal_power_enable_external_3_3v();
@@ -189,22 +187,20 @@ void furi_hal_power_suppress_charge_enter();
*/ */
void furi_hal_power_suppress_charge_exit(); void furi_hal_power_suppress_charge_exit();
/** Callback type called by furi_hal_power_info_get every time another key-value pair of information is ready
*
* @param key[in] power information type identifier
* @param value[in] power information value
* @param last[in] whether the passed key-value pair is the last one
* @param context[in] to pass to callback
*/
typedef void (
*FuriHalPowerInfoCallback)(const char* key, const char* value, bool last, void* context);
/** Get power information /** Get power information
*
* @param[in] callback callback to provide with new data
* @param[in] sep category separator character
* @param[in] context context to pass to callback
*/
void furi_hal_power_info_get(PropertyValueCallback callback, char sep, void* context);
/** Get power debug information
* *
* @param[in] callback callback to provide with new data * @param[in] callback callback to provide with new data
* @param[in] context context to pass to callback * @param[in] context context to pass to callback
*/ */
void furi_hal_power_info_get(FuriHalPowerInfoCallback callback, void* context); void furi_hal_power_debug_get(PropertyValueCallback callback, void* context);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -340,6 +340,10 @@ void* pvPortMalloc(size_t xWantedSize) {
void* pvReturn = NULL; void* pvReturn = NULL;
size_t to_wipe = xWantedSize; size_t to_wipe = xWantedSize;
if(FURI_IS_IRQ_MODE()) {
furi_crash("memmgt in ISR");
}
#ifdef HEAP_PRINT_DEBUG #ifdef HEAP_PRINT_DEBUG
BlockLink_t* print_heap_block = NULL; BlockLink_t* print_heap_block = NULL;
#endif #endif
@@ -486,6 +490,10 @@ void vPortFree(void* pv) {
uint8_t* puc = (uint8_t*)pv; uint8_t* puc = (uint8_t*)pv;
BlockLink_t* pxLink; BlockLink_t* pxLink;
if(FURI_IS_IRQ_MODE()) {
furi_crash("memmgt in ISR");
}
if(pv != NULL) { if(pv != NULL) {
/* The memory being freed will have an BlockLink_t structure immediately /* The memory being freed will have an BlockLink_t structure immediately
before it. */ before it. */

43
lib/toolbox/api_lock.h Normal file
View File

@@ -0,0 +1,43 @@
#pragma once
#include <furi/furi.h>
/*
Testing 10000 api calls
No lock
Time diff: 445269.218750 us
Time per call: 44.526920 us
furi_thread_flags
Time diff: 430279.875000 us // lol wtf? smaller than no lock?
Time per call: 43.027988 us // I tested it many times, it's always smaller
FuriEventFlag
Time diff: 831523.625000 us
Time per call: 83.152359 us
FuriSemaphore
Time diff: 999807.125000 us
Time per call: 99.980713 us
FuriMutex
Time diff: 1071417.500000 us
Time per call: 107.141747 us
*/
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);

33
lib/toolbox/property.c Normal file
View File

@@ -0,0 +1,33 @@
#include "property.h"
#include <core/check.h>
void property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...) {
furi_assert(ctx);
furi_string_reset(ctx->key);
va_list args;
va_start(args, nparts);
for(size_t i = 0; i < nparts; ++i) {
const char* keypart = va_arg(args, const char*);
furi_string_cat(ctx->key, keypart);
if(i < nparts - 1) {
furi_string_push_back(ctx->key, ctx->sep);
}
}
const char* value_str;
if(fmt) {
furi_string_vprintf(ctx->value, fmt, args);
value_str = furi_string_get_cstr(ctx->value);
} else {
// C string passthrough (no formatting)
value_str = va_arg(args, const char*);
}
va_end(args);
ctx->out(furi_string_get_cstr(ctx->key), value_str, ctx->last, ctx->context);
}

39
lib/toolbox/property.h Normal file
View File

@@ -0,0 +1,39 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <core/string.h>
/** Callback type called every time another key-value pair of device information is ready
*
* @param key[in] device information type identifier
* @param value[in] device information value
* @param last[in] whether the passed key-value pair is the last one
* @param context[in] to pass to callback
*/
typedef void (*PropertyValueCallback)(const char* key, const char* value, bool last, void* context);
typedef struct {
FuriString* key; /**< key string buffer, must be initialised before use */
FuriString* value; /**< value string buffer, must be initialised before use */
PropertyValueCallback out; /**< output callback function */
char sep; /**< separator character between key parts */
bool last; /**< flag to indicate last element */
void* context; /**< user-defined context, passed through to out callback */
} PropertyValueContext;
/** Builds key and value strings and outputs them via a callback function
*
* @param ctx[in] local property context
* @param fmt[in] value format, set to NULL to bypass formatting
* @param nparts[in] number of key parts (separated by character)
* @param ...[in] list of key parts followed by value
*/
void property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...);
#ifdef __cplusplus
}
#endif