[FL-2460] Rpc: debug request logging and cli log command (#1114)
* Rpc: debug request logging * Furi, Cli: replace global thread callback with local ring buffers, fix log command
This commit is contained in:
parent
c078bbcb0e
commit
9b9edf2fbf
@ -6,6 +6,7 @@
|
||||
#include <time.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <loader/loader.h>
|
||||
#include <stream_buffer.h>
|
||||
|
||||
// Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'`
|
||||
#define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d"
|
||||
@ -126,11 +127,28 @@ void cli_command_date(Cli* cli, string_t args, void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
#define CLI_COMMAND_LOG_RING_SIZE 2048
|
||||
#define CLI_COMMAND_LOG_BUFFER_SIZE 64
|
||||
|
||||
void cli_command_log_tx_callback(const uint8_t* buffer, size_t size, void* context) {
|
||||
xStreamBufferSend(context, buffer, size, 0);
|
||||
}
|
||||
|
||||
void cli_command_log(Cli* cli, string_t args, void* context) {
|
||||
furi_stdglue_set_global_stdout_callback(cli_stdout_callback);
|
||||
printf("Press any key to stop...\r\n");
|
||||
cli_getc(cli);
|
||||
furi_stdglue_set_global_stdout_callback(NULL);
|
||||
StreamBufferHandle_t ring = xStreamBufferCreate(CLI_COMMAND_LOG_RING_SIZE, 1);
|
||||
uint8_t buffer[CLI_COMMAND_LOG_BUFFER_SIZE];
|
||||
|
||||
furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring);
|
||||
|
||||
printf("Press CTRL+C to stop...\r\n");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
size_t ret = xStreamBufferReceive(ring, buffer, CLI_COMMAND_LOG_BUFFER_SIZE, 50);
|
||||
cli_write(cli, buffer, ret);
|
||||
}
|
||||
|
||||
furi_hal_console_set_tx_callback(NULL, NULL);
|
||||
|
||||
vStreamBufferDelete(ring);
|
||||
}
|
||||
|
||||
void cli_command_vibro(Cli* cli, string_t args, void* context) {
|
||||
|
@ -4,12 +4,16 @@
|
||||
#include <furi.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
#define TAG "RpcSystemApp"
|
||||
|
||||
static void rpc_system_app_start_process(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_app_start_request_tag);
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
FURI_LOG_D(TAG, "Start");
|
||||
|
||||
PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START;
|
||||
|
||||
Loader* loader = furi_record_open("loader");
|
||||
@ -43,6 +47,8 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
FURI_LOG_D(TAG, "LockStatus");
|
||||
|
||||
Loader* loader = furi_record_open("loader");
|
||||
|
||||
PB_Main response = {
|
||||
|
@ -65,8 +65,10 @@ static int32_t rpc_system_gui_screen_stream_frame_transmit_thread(void* context)
|
||||
static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
RpcGuiSystem* rpc_gui = context;
|
||||
|
||||
FURI_LOG_D(TAG, "StartScreenStream");
|
||||
|
||||
RpcGuiSystem* rpc_gui = context;
|
||||
RpcSession* session = rpc_gui->session;
|
||||
furi_assert(session);
|
||||
|
||||
@ -103,6 +105,8 @@ static void rpc_system_gui_stop_screen_stream_process(const PB_Main* request, vo
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
FURI_LOG_D(TAG, "StopScreenStream");
|
||||
|
||||
RpcGuiSystem* rpc_gui = context;
|
||||
RpcSession* session = rpc_gui->session;
|
||||
furi_assert(session);
|
||||
@ -132,6 +136,8 @@ static void
|
||||
furi_assert(request->which_content == PB_Main_gui_send_input_event_request_tag);
|
||||
furi_assert(context);
|
||||
|
||||
FURI_LOG_D(TAG, "SendInputEvent");
|
||||
|
||||
RpcGuiSystem* rpc_gui = context;
|
||||
RpcSession* session = rpc_gui->session;
|
||||
furi_assert(session);
|
||||
@ -220,6 +226,8 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request,
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
FURI_LOG_D(TAG, "StartVirtualDisplay");
|
||||
|
||||
RpcGuiSystem* rpc_gui = context;
|
||||
RpcSession* session = rpc_gui->session;
|
||||
furi_assert(session);
|
||||
@ -259,6 +267,8 @@ static void rpc_system_gui_stop_virtual_display_process(const PB_Main* request,
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
FURI_LOG_D(TAG, "StopVirtualDisplay");
|
||||
|
||||
RpcGuiSystem* rpc_gui = context;
|
||||
RpcSession* session = rpc_gui->session;
|
||||
furi_assert(session);
|
||||
@ -282,6 +292,8 @@ static void rpc_system_gui_virtual_display_frame_process(const PB_Main* request,
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
FURI_LOG_D(TAG, "VirtualDisplayFrame");
|
||||
|
||||
RpcGuiSystem* rpc_gui = context;
|
||||
RpcSession* session = rpc_gui->session;
|
||||
furi_assert(session);
|
||||
|
@ -12,7 +12,8 @@
|
||||
#include <lib/toolbox/md5.h>
|
||||
#include <update_util/lfs_backup.h>
|
||||
|
||||
#define RPC_TAG "RPC_STORAGE"
|
||||
#define TAG "RpcStorage"
|
||||
|
||||
#define MAX_NAME_LENGTH 255
|
||||
|
||||
static const size_t MAX_DATA_SIZE = 512;
|
||||
@ -106,6 +107,8 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex
|
||||
furi_assert(context);
|
||||
furi_assert(request->which_content == PB_Main_storage_info_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "Info");
|
||||
|
||||
RpcStorageSystem* rpc_storage = context;
|
||||
RpcSession* session = rpc_storage->session;
|
||||
furi_assert(session);
|
||||
@ -140,6 +143,8 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex
|
||||
furi_assert(context);
|
||||
furi_assert(request->which_content == PB_Main_storage_stat_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "Stat");
|
||||
|
||||
RpcStorageSystem* rpc_storage = context;
|
||||
RpcSession* session = rpc_storage->session;
|
||||
furi_assert(session);
|
||||
@ -205,6 +210,8 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
|
||||
furi_assert(context);
|
||||
furi_assert(request->which_content == PB_Main_storage_list_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "List");
|
||||
|
||||
RpcStorageSystem* rpc_storage = context;
|
||||
RpcSession* session = rpc_storage->session;
|
||||
furi_assert(session);
|
||||
@ -273,6 +280,8 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex
|
||||
furi_assert(context);
|
||||
furi_assert(request->which_content == PB_Main_storage_read_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "Read");
|
||||
|
||||
RpcStorageSystem* rpc_storage = context;
|
||||
RpcSession* session = rpc_storage->session;
|
||||
furi_assert(session);
|
||||
@ -330,6 +339,8 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte
|
||||
furi_assert(context);
|
||||
furi_assert(request->which_content == PB_Main_storage_write_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "Write");
|
||||
|
||||
RpcStorageSystem* rpc_storage = context;
|
||||
RpcSession* session = rpc_storage->session;
|
||||
furi_assert(session);
|
||||
@ -395,6 +406,9 @@ static void rpc_system_storage_delete_process(const PB_Main* request, void* cont
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_storage_delete_request_tag);
|
||||
furi_assert(context);
|
||||
|
||||
FURI_LOG_D(TAG, "Delete");
|
||||
|
||||
RpcStorageSystem* rpc_storage = context;
|
||||
RpcSession* session = rpc_storage->session;
|
||||
furi_assert(session);
|
||||
@ -433,6 +447,9 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_storage_mkdir_request_tag);
|
||||
furi_assert(context);
|
||||
|
||||
FURI_LOG_D(TAG, "Mkdir");
|
||||
|
||||
RpcStorageSystem* rpc_storage = context;
|
||||
RpcSession* session = rpc_storage->session;
|
||||
furi_assert(session);
|
||||
@ -456,6 +473,9 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_storage_md5sum_request_tag);
|
||||
furi_assert(context);
|
||||
|
||||
FURI_LOG_D(TAG, "Md5sum");
|
||||
|
||||
RpcStorageSystem* rpc_storage = context;
|
||||
RpcSession* session = rpc_storage->session;
|
||||
furi_assert(session);
|
||||
@ -521,6 +541,9 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_storage_rename_request_tag);
|
||||
furi_assert(context);
|
||||
|
||||
FURI_LOG_D(TAG, "Rename");
|
||||
|
||||
RpcStorageSystem* rpc_storage = context;
|
||||
RpcSession* session = rpc_storage->session;
|
||||
furi_assert(session);
|
||||
@ -544,6 +567,8 @@ static void rpc_system_storage_backup_create_process(const PB_Main* request, voi
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_storage_backup_create_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "BackupCreate");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -567,6 +592,8 @@ static void rpc_system_storage_backup_restore_process(const PB_Main* request, vo
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_storage_backup_restore_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "BackupRestore");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#include "rpc_i.h"
|
||||
|
||||
#define TAG "RpcSystem"
|
||||
|
||||
typedef struct {
|
||||
RpcSession* session;
|
||||
PB_Main* response;
|
||||
@ -16,6 +18,8 @@ static void rpc_system_system_ping_process(const PB_Main* request, void* context
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_ping_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "Ping");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -46,6 +50,8 @@ static void rpc_system_system_reboot_process(const PB_Main* request, void* conte
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_reboot_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "Reboot");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -84,6 +90,8 @@ static void rpc_system_system_device_info_process(const PB_Main* request, void*
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_device_info_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "DeviceInfo");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -105,6 +113,8 @@ static void rpc_system_system_get_datetime_process(const PB_Main* request, void*
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_get_datetime_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "GetDatetime");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -132,6 +142,8 @@ static void rpc_system_system_set_datetime_process(const PB_Main* request, void*
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_set_datetime_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "SetDatetime");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -158,6 +170,8 @@ static void rpc_system_system_factory_reset_process(const PB_Main* request, void
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_factory_reset_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "Reset");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -172,6 +186,8 @@ static void
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_play_audiovisual_alert_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "Alert");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -186,6 +202,8 @@ static void rpc_system_system_protobuf_version_process(const PB_Main* request, v
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_protobuf_version_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "ProtobufVersion");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -226,6 +244,8 @@ static void rpc_system_system_get_power_info_process(const PB_Main* request, voi
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_power_info_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "GetPowerInfo");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -248,6 +268,8 @@ static void rpc_system_system_update_request_process(const PB_Main* request, voi
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_system_update_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "SystemUpdate");
|
||||
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
furi_assert(session);
|
||||
|
||||
@ -305,4 +327,4 @@ void* rpc_system_system_alloc(RpcSession* session) {
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ DICT_DEF2(
|
||||
|
||||
typedef struct {
|
||||
osMutexId_t mutex;
|
||||
FuriStdglueCallbackDict_t global_outputs;
|
||||
FuriStdglueCallbackDict_t thread_outputs;
|
||||
} FuriStdglue;
|
||||
|
||||
@ -31,17 +30,6 @@ static ssize_t stdout_write(void* _cookie, const char* data, size_t size) {
|
||||
if(state == osKernelRunning && thread_id &&
|
||||
osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK) {
|
||||
// We are in the thread context
|
||||
// Handle global callbacks
|
||||
FuriStdglueCallbackDict_it_t it;
|
||||
for(FuriStdglueCallbackDict_it(it, furi_stdglue->global_outputs);
|
||||
!FuriStdglueCallbackDict_end_p(it);
|
||||
FuriStdglueCallbackDict_next(it)) {
|
||||
osThreadId_t it_thread = (osThreadId_t)FuriStdglueCallbackDict_ref(it)->key;
|
||||
FuriStdglueWriteCallback it_callback = FuriStdglueCallbackDict_ref(it)->value;
|
||||
if(thread_id != it_thread) {
|
||||
it_callback(_cookie, data, size);
|
||||
}
|
||||
}
|
||||
// Handle thread callbacks
|
||||
FuriStdglueWriteCallback* callback_ptr =
|
||||
FuriStdglueCallbackDict_get(furi_stdglue->thread_outputs, (uint32_t)thread_id);
|
||||
@ -71,7 +59,6 @@ void furi_stdglue_init() {
|
||||
// Init outputs structures
|
||||
furi_stdglue->mutex = osMutexNew(NULL);
|
||||
furi_check(furi_stdglue->mutex);
|
||||
FuriStdglueCallbackDict_init(furi_stdglue->global_outputs);
|
||||
FuriStdglueCallbackDict_init(furi_stdglue->thread_outputs);
|
||||
// Prepare and set stdout descriptor
|
||||
FILE* fp = fopencookie(
|
||||
@ -87,24 +74,6 @@ void furi_stdglue_init() {
|
||||
stdout = fp;
|
||||
}
|
||||
|
||||
bool furi_stdglue_set_global_stdout_callback(FuriStdglueWriteCallback callback) {
|
||||
furi_assert(furi_stdglue);
|
||||
osThreadId_t thread_id = osThreadGetId();
|
||||
if(thread_id) {
|
||||
furi_check(osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK);
|
||||
if(callback) {
|
||||
FuriStdglueCallbackDict_set_at(
|
||||
furi_stdglue->global_outputs, (uint32_t)thread_id, callback);
|
||||
} else {
|
||||
FuriStdglueCallbackDict_erase(furi_stdglue->global_outputs, (uint32_t)thread_id);
|
||||
}
|
||||
furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback) {
|
||||
furi_assert(furi_stdglue);
|
||||
osThreadId_t thread_id = osThreadGetId();
|
||||
|
@ -22,15 +22,6 @@ typedef void (*FuriStdglueWriteCallback)(void* _cookie, const char* data, size_t
|
||||
/** Initialized std library glue code */
|
||||
void furi_stdglue_init();
|
||||
|
||||
/** Set global STDOUT callback
|
||||
*
|
||||
* @param callback callback or NULL to clear
|
||||
*
|
||||
* @return true on success, otherwise fail
|
||||
* @warning function is thread aware, use this API from the same thread
|
||||
*/
|
||||
bool furi_stdglue_set_global_stdout_callback(FuriStdglueWriteCallback callback);
|
||||
|
||||
/** Set STDOUT callback for your thread
|
||||
*
|
||||
* @param callback callback or NULL to clear
|
||||
|
@ -18,11 +18,21 @@
|
||||
#define CONSOLE_BAUDRATE 230400
|
||||
#endif
|
||||
|
||||
volatile bool furi_hal_console_alive = false;
|
||||
typedef struct {
|
||||
bool alive;
|
||||
FuriHalConsoleTxCallback tx_callback;
|
||||
void* tx_callback_context;
|
||||
} FuriHalConsole;
|
||||
|
||||
FuriHalConsole furi_hal_console = {
|
||||
.alive = false,
|
||||
.tx_callback = NULL,
|
||||
.tx_callback_context = NULL,
|
||||
};
|
||||
|
||||
void furi_hal_console_init() {
|
||||
furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
|
||||
furi_hal_console_alive = true;
|
||||
furi_hal_console.alive = true;
|
||||
}
|
||||
|
||||
void furi_hal_console_enable() {
|
||||
@ -30,20 +40,32 @@ void furi_hal_console_enable() {
|
||||
while(!LL_USART_IsActiveFlag_TC(USART1))
|
||||
;
|
||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
|
||||
furi_hal_console_alive = true;
|
||||
furi_hal_console.alive = true;
|
||||
}
|
||||
|
||||
void furi_hal_console_disable() {
|
||||
while(!LL_USART_IsActiveFlag_TC(USART1))
|
||||
;
|
||||
furi_hal_console_alive = false;
|
||||
furi_hal_console.alive = false;
|
||||
}
|
||||
|
||||
void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context) {
|
||||
FURI_CRITICAL_ENTER();
|
||||
furi_hal_console.tx_callback = callback;
|
||||
furi_hal_console.tx_callback_context = context;
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
|
||||
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
|
||||
if(!furi_hal_console_alive) return;
|
||||
if(!furi_hal_console.alive) return;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
// Transmit data
|
||||
|
||||
if(furi_hal_console.tx_callback) {
|
||||
furi_hal_console.tx_callback(buffer, buffer_size, furi_hal_console.tx_callback_context);
|
||||
}
|
||||
|
||||
furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size);
|
||||
// Wait for TC flag to be raised for last char
|
||||
while(!LL_USART_IsActiveFlag_TC(USART1))
|
||||
@ -52,7 +74,7 @@ void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
|
||||
}
|
||||
|
||||
void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) {
|
||||
if(!furi_hal_console_alive) return;
|
||||
if(!furi_hal_console.alive) return;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
// Transmit data
|
||||
|
@ -7,12 +7,16 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*FuriHalConsoleTxCallback)(const uint8_t* buffer, size_t size, void* context);
|
||||
|
||||
void furi_hal_console_init();
|
||||
|
||||
void furi_hal_console_enable();
|
||||
|
||||
void furi_hal_console_disable();
|
||||
|
||||
void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context);
|
||||
|
||||
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size);
|
||||
|
||||
void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size);
|
||||
|
Loading…
Reference in New Issue
Block a user