Merge branch 'ofw_dev' into nfcrefactoring

This commit is contained in:
MX
2023-11-11 20:18:55 +04:00
29 changed files with 669 additions and 412 deletions

View File

@@ -477,12 +477,15 @@ void rpc_send_and_release(RpcSession* session, PB_Main* message) {
}
void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_CommandStatus status) {
furi_assert(session);
PB_Main message = {
.command_id = command_id,
.command_status = status,
.has_next = false,
.which_content = PB_Main_empty_tag,
};
rpc_send_and_release(session, &message);
pb_release(&PB_Main_msg, &message);
}

View File

@@ -10,49 +10,85 @@
struct RpcAppSystem {
RpcSession* session;
RpcAppSystemCallback app_callback;
void* app_context;
RpcAppSystemCallback callback;
void* callback_context;
RpcAppSystemDataExchangeCallback data_exchange_callback;
void* data_exchange_context;
uint32_t error_code;
char* error_text;
PB_Main* state_msg;
PB_Main* error_msg;
uint32_t last_id;
char* last_data;
uint32_t last_command_id;
RpcAppSystemEventType last_event_type;
};
#define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16
static void rpc_system_app_send_state_response(
RpcAppSystem* rpc_app,
PB_App_AppState state,
const char* name) {
PB_Main* response = malloc(sizeof(PB_Main));
response->which_content = PB_Main_app_state_response_tag;
response->content.app_state_response.state = state;
FURI_LOG_D(TAG, "%s", name);
rpc_send(rpc_app->session, response);
free(response);
}
static void rpc_system_app_send_error_response(
RpcAppSystem* rpc_app,
uint32_t command_id,
PB_CommandStatus status,
const char* name) {
// Not describing all possible errors as only APP_NOT_RUNNING is used
const char* status_str = status == PB_CommandStatus_ERROR_APP_NOT_RUNNING ? "APP_NOT_RUNNING" :
"UNKNOWN";
FURI_LOG_E(TAG, "%s: %s, id %lu, status: %d", name, status_str, command_id, status);
rpc_send_and_release_empty(rpc_app->session, command_id, status);
}
static void rpc_system_app_set_last_command(
RpcAppSystem* rpc_app,
uint32_t command_id,
const RpcAppSystemEvent* event) {
furi_assert(rpc_app->last_command_id == 0);
furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid);
rpc_app->last_command_id = command_id;
rpc_app->last_event_type = event->type;
}
static void rpc_system_app_start_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_app_start_request_tag);
RpcAppSystem* rpc_app = context;
RpcSession* session = rpc_app->session;
rpc_system_app_error_reset(rpc_app);
furi_assert(session);
char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE];
furi_assert(!rpc_app->last_id);
furi_assert(!rpc_app->last_data);
RpcAppSystem* rpc_app = context;
furi_assert(rpc_app);
furi_assert(rpc_app->last_command_id == 0);
furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid);
FURI_LOG_D(TAG, "StartProcess: id %lu", request->command_id);
PB_CommandStatus result;
Loader* loader = furi_record_open(RECORD_LOADER);
const char* app_name = request->content.app_start_request.name;
PB_CommandStatus result;
if(app_name) {
rpc_system_app_error_reset(rpc_app);
char app_args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE];
const char* app_args = request->content.app_start_request.args;
if(app_args && strcmp(app_args, "RPC") == 0) {
// If app is being started in RPC mode - pass RPC context via args string
snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app);
app_args = args_temp;
snprintf(app_args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app);
app_args = app_args_temp;
}
LoaderStatus status = loader_start(loader, app_name, app_args, NULL);
const LoaderStatus status = loader_start(loader, app_name, app_args, NULL);
if(status == LoaderStatusErrorAppStarted) {
result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED;
} else if(status == LoaderStatusErrorInternal) {
@@ -71,266 +107,271 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context)
furi_record_close(RECORD_LOADER);
FURI_LOG_D(TAG, "StartProcess: response id %lu, result %d", request->command_id, result);
rpc_send_and_release_empty(session, request->command_id, result);
rpc_send_and_release_empty(rpc_app->session, request->command_id, result);
}
static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_app_lock_status_request_tag);
RpcAppSystem* rpc_app = context;
furi_assert(rpc_app);
rpc_system_app_error_reset(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
FURI_LOG_D(TAG, "LockStatus");
PB_Main* response = malloc(sizeof(PB_Main));
response->command_id = request->command_id;
response->which_content = PB_Main_app_lock_status_response_tag;
Loader* loader = furi_record_open(RECORD_LOADER);
PB_Main response = {
.has_next = false,
.command_status = PB_CommandStatus_OK,
.command_id = request->command_id,
.which_content = PB_Main_app_lock_status_response_tag,
};
response.content.app_lock_status_response.locked = loader_is_locked(loader);
response->content.app_lock_status_response.locked = loader_is_locked(loader);
furi_record_close(RECORD_LOADER);
FURI_LOG_D(TAG, "LockStatus: response");
rpc_send_and_release(session, &response);
pb_release(&PB_Main_msg, &response);
rpc_send_and_release(rpc_app->session, response);
free(response);
}
static void rpc_system_app_exit_request(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_app_exit_request_tag);
RpcAppSystem* rpc_app = context;
rpc_system_app_error_reset(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
furi_assert(rpc_app);
PB_CommandStatus status;
if(rpc_app->app_callback) {
if(rpc_app->callback) {
FURI_LOG_D(TAG, "ExitRequest: id %lu", request->command_id);
furi_assert(!rpc_app->last_id);
furi_assert(!rpc_app->last_data);
rpc_app->last_id = request->command_id;
rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context);
const RpcAppSystemEvent event = {
.type = RpcAppEventTypeAppExit,
.data =
{
.type = RpcAppSystemEventDataTypeNone,
{0},
},
};
rpc_system_app_error_reset(rpc_app);
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
rpc_app->callback(&event, rpc_app->callback_context);
} else {
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
FURI_LOG_E(
TAG, "ExitRequest: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
rpc_send_and_release_empty(session, request->command_id, status);
rpc_system_app_send_error_response(
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ExitRequest");
}
}
static void rpc_system_app_load_file(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_app_load_file_request_tag);
RpcAppSystem* rpc_app = context;
rpc_system_app_error_reset(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
PB_CommandStatus status;
if(rpc_app->app_callback) {
RpcAppSystem* rpc_app = context;
furi_assert(rpc_app);
if(rpc_app->callback) {
FURI_LOG_D(TAG, "LoadFile: id %lu", request->command_id);
furi_assert(!rpc_app->last_id);
furi_assert(!rpc_app->last_data);
rpc_app->last_id = request->command_id;
rpc_app->last_data = strdup(request->content.app_load_file_request.path);
rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context);
const RpcAppSystemEvent event = {
.type = RpcAppEventTypeLoadFile,
.data =
{
.type = RpcAppSystemEventDataTypeString,
.string = request->content.app_load_file_request.path,
},
};
rpc_system_app_error_reset(rpc_app);
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
rpc_app->callback(&event, rpc_app->callback_context);
} else {
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
FURI_LOG_E(
TAG, "LoadFile: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
rpc_send_and_release_empty(session, request->command_id, status);
rpc_system_app_send_error_response(
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "LoadFile");
}
}
static void rpc_system_app_button_press(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_app_button_press_request_tag);
RpcAppSystem* rpc_app = context;
rpc_system_app_error_reset(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
PB_CommandStatus status;
if(rpc_app->app_callback) {
RpcAppSystem* rpc_app = context;
furi_assert(rpc_app);
if(rpc_app->callback) {
FURI_LOG_D(TAG, "ButtonPress");
furi_assert(!rpc_app->last_id);
furi_assert(!rpc_app->last_data);
rpc_app->last_id = request->command_id;
rpc_app->last_data = strdup(request->content.app_button_press_request.args);
rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context);
RpcAppSystemEvent event;
event.type = RpcAppEventTypeButtonPress;
if(strlen(request->content.app_button_press_request.args) != 0) {
event.data.type = RpcAppSystemEventDataTypeString;
event.data.string = request->content.app_button_press_request.args;
} else {
event.data.type = RpcAppSystemEventDataTypeInt32;
event.data.i32 = request->content.app_button_press_request.index;
}
rpc_system_app_error_reset(rpc_app);
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
rpc_app->callback(&event, rpc_app->callback_context);
} else {
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
FURI_LOG_E(
TAG, "ButtonPress: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
rpc_send_and_release_empty(session, request->command_id, status);
rpc_system_app_send_error_response(
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ButtonPress");
}
}
static void rpc_system_app_button_release(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(request->which_content == PB_Main_app_button_release_request_tag);
furi_assert(context);
RpcAppSystem* rpc_app = context;
rpc_system_app_error_reset(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
furi_assert(rpc_app);
PB_CommandStatus status;
if(rpc_app->app_callback) {
if(rpc_app->callback) {
FURI_LOG_D(TAG, "ButtonRelease");
furi_assert(!rpc_app->last_id);
furi_assert(!rpc_app->last_data);
rpc_app->last_id = request->command_id;
rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context);
const RpcAppSystemEvent event = {
.type = RpcAppEventTypeButtonRelease,
.data =
{
.type = RpcAppSystemEventDataTypeNone,
{0},
},
};
rpc_system_app_error_reset(rpc_app);
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
rpc_app->callback(&event, rpc_app->callback_context);
} else {
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
FURI_LOG_E(
TAG, "ButtonRelease: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
rpc_send_and_release_empty(session, request->command_id, status);
rpc_system_app_send_error_response(
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ButtonRelease");
}
}
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);
furi_assert(rpc_app);
rpc_app->error_msg->command_id = request->command_id;
PB_Main* response = malloc(sizeof(PB_Main));
response->command_id = request->command_id;
response->which_content = PB_Main_app_get_error_response_tag;
response->content.app_get_error_response.code = rpc_app->error_code;
response->content.app_get_error_response.text = rpc_app->error_text;
FURI_LOG_D(TAG, "GetError");
rpc_send(session, rpc_app->error_msg);
rpc_send(rpc_app->session, response);
free(response);
}
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;
rpc_system_app_error_reset(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
furi_assert(rpc_app);
PB_CommandStatus command_status;
pb_bytes_array_t* data = request->content.app_data_exchange_request.data;
if(rpc_app->callback) {
FURI_LOG_D(TAG, "DataExchange");
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;
const pb_bytes_array_t* data = request->content.app_data_exchange_request.data;
const RpcAppSystemEvent event = {
.type = RpcAppEventTypeDataExchange,
.data =
{
.type = RpcAppSystemEventDataTypeBytes,
.bytes =
{
.ptr = data ? data->bytes : NULL,
.size = data ? data->size : 0,
},
},
};
rpc_system_app_error_reset(rpc_app);
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
rpc_app->callback(&event, rpc_app->callback_context);
} else {
command_status = PB_CommandStatus_ERROR_APP_CMD_ERROR;
rpc_system_app_send_error_response(
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "DataExchange");
}
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) {
furi_assert(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED;
FURI_LOG_D(TAG, "SendStarted");
rpc_send(session, rpc_app->state_msg);
rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_STARTED, "SendStarted");
}
void rpc_system_app_send_exited(RpcAppSystem* rpc_app) {
furi_assert(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED;
FURI_LOG_D(TAG, "SendExit");
rpc_send(session, rpc_app->state_msg);
rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_CLOSED, "SendExit");
}
const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) {
void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result) {
furi_assert(rpc_app);
furi_assert(rpc_app->last_data);
return rpc_app->last_data;
}
furi_assert(rpc_app->last_command_id != 0);
/* Ensure that only commands of these types can be confirmed */
furi_assert(
rpc_app->last_event_type == RpcAppEventTypeAppExit ||
rpc_app->last_event_type == RpcAppEventTypeLoadFile ||
rpc_app->last_event_type == RpcAppEventTypeButtonPress ||
rpc_app->last_event_type == RpcAppEventTypeButtonRelease ||
rpc_app->last_event_type == RpcAppEventTypeDataExchange);
void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) {
furi_assert(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
furi_assert(rpc_app->last_id);
const uint32_t last_command_id = rpc_app->last_command_id;
const RpcAppSystemEventType last_event_type = rpc_app->last_event_type;
PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR;
rpc_app->last_command_id = 0;
rpc_app->last_event_type = RpcAppEventTypeInvalid;
uint32_t last_id = 0;
switch(event) {
case RpcAppEventAppExit:
case RpcAppEventLoadFile:
case RpcAppEventButtonPress:
case RpcAppEventButtonRelease:
last_id = rpc_app->last_id;
rpc_app->last_id = 0;
if(rpc_app->last_data) {
free(rpc_app->last_data);
rpc_app->last_data = NULL;
}
FURI_LOG_D(TAG, "AppConfirm: event %d last_id %lu status %d", event, last_id, status);
rpc_send_and_release_empty(session, last_id, status);
break;
default:
furi_crash("RPC App state programming Error");
break;
}
const PB_CommandStatus status = result ? PB_CommandStatus_OK :
PB_CommandStatus_ERROR_APP_CMD_ERROR;
FURI_LOG_D(
TAG,
"AppConfirm: event %d last_id %lu status %d",
last_event_type,
last_command_id,
status);
rpc_send_and_release_empty(rpc_app->session, last_command_id, status);
}
void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) {
furi_assert(rpc_app);
rpc_app->app_callback = callback;
rpc_app->app_context = ctx;
rpc_app->callback = callback;
rpc_app->callback_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;
rpc_app->error_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);
if(rpc_app->error_text) {
free(rpc_app->error_text);
}
content->text = error_text ? strdup(error_text) : NULL;
rpc_app->error_text = error_text ? strdup(error_text) : NULL;
}
void rpc_system_app_error_reset(RpcAppSystem* rpc_app) {
@@ -340,29 +381,13 @@ void rpc_system_app_error_reset(RpcAppSystem* rpc_app) {
rpc_system_app_set_error_text(rpc_app, 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_Main* request = malloc(sizeof(PB_Main));
PB_App_DataExchangeRequest* content = &message.content.app_data_exchange_request;
request->which_content = PB_Main_app_data_exchange_request_tag;
PB_App_DataExchangeRequest* content = &request->content.app_data_exchange_request;
if(data && data_size) {
content->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data_size));
@@ -372,7 +397,9 @@ void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, si
content->data = NULL;
}
rpc_send_and_release(session, &message);
rpc_send_and_release(rpc_app->session, request);
free(request);
}
void* rpc_system_app_alloc(RpcSession* session) {
@@ -381,18 +408,6 @@ void* rpc_system_app_alloc(RpcSession* session) {
RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem));
rpc_app->session = session;
// App exit message
rpc_app->state_msg = malloc(sizeof(PB_Main));
rpc_app->state_msg->which_content = PB_Main_app_state_response_tag;
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 = {
.message_handler = NULL,
.decode_submessage = NULL,
@@ -429,24 +444,24 @@ void* rpc_system_app_alloc(RpcSession* session) {
void rpc_system_app_free(void* context) {
RpcAppSystem* rpc_app = context;
furi_assert(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
furi_assert(rpc_app->session);
if(rpc_app->app_callback) {
rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context);
if(rpc_app->callback) {
const RpcAppSystemEvent event = {
.type = RpcAppEventTypeSessionClose,
.data =
{
.type = RpcAppSystemEventDataTypeNone,
{0},
},
};
rpc_app->callback(&event, rpc_app->callback_context);
}
while(rpc_app->app_callback) {
while(rpc_app->callback) {
furi_delay_tick(1);
}
furi_assert(!rpc_app->data_exchange_callback);
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);
}

View File

@@ -1,45 +1,213 @@
/**
* @file rpc_app.h
* @brief Application RPC subsystem interface.
*
* The application RPC subsystem provides facilities for interacting with applications,
* such as starting/stopping, passing parameters, sending commands and exchanging arbitrary data.
*
* All commands are handled asynchronously via a user-settable callback.
*
* For a complete description of message types handled in this subsystem,
* see https://github.com/flipperdevices/flipperzero-protobuf/blob/dev/application.proto
*/
#pragma once
#include "rpc.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Enumeration of possible event data types.
*/
typedef enum {
RpcAppEventSessionClose,
RpcAppEventAppExit,
RpcAppEventLoadFile,
RpcAppEventButtonPress,
RpcAppEventButtonRelease,
RpcAppSystemEventDataTypeNone, /**< No data is provided by the event. */
RpcAppSystemEventDataTypeString, /**< Event data contains a zero-terminated string. */
RpcAppSystemEventDataTypeInt32, /**< Event data contains a signed 32-bit integer. */
RpcAppSystemEventDataTypeBytes, /**< Event data contains zero or more bytes. */
} RpcAppSystemEventDataType;
/**
* @brief Event data structure, containing the type and associated data.
*
* All below fields except for type are valid only if the respective type is set.
*/
typedef struct {
RpcAppSystemEventDataType
type; /**< Type of the data. The meaning of other fields depends on this one. */
union {
const char* string; /**< Pointer to a zero-terminated character string. */
int32_t i32; /**< Signed 32-bit integer value. */
struct {
const uint8_t* ptr; /**< Pointer to the byte array data. */
size_t size; /**< Size of the byte array, in bytes. */
} bytes; /**< Byte array of arbitrary length. */
};
} RpcAppSystemEventData;
/**
* @brief Enumeration of possible event types.
*/
typedef enum {
/**
* @brief Denotes an invalid state.
*
* An event of this type shall never be passed into the callback.
*/
RpcAppEventTypeInvalid,
/**
* @brief The client side has closed the session.
*
* After receiving this event, the RPC context is no more valid.
*/
RpcAppEventTypeSessionClose,
/**
* @brief The client has requested the application to exit.
*
* The application must exit after receiving this command.
*/
RpcAppEventTypeAppExit,
/**
* @brief The client has requested the application to load a file.
*
* This command's meaning is application-specific, i.e. the application might or
* might not require additional commands after loading a file to do anything useful.
*/
RpcAppEventTypeLoadFile,
/**
* @brief The client has informed the application that a button has been pressed.
*
* This command's meaning is application-specific, e.g. to select a part of the
* previously loaded file or to invoke a particular function within the application.
*/
RpcAppEventTypeButtonPress,
/**
* @brief The client has informed the application that a button has been released.
*
* This command's meaning is application-specific, e.g. to cease
* all activities to be conducted while a button is being pressed.
*/
RpcAppEventTypeButtonRelease,
/**
* @brief The client has sent a byte array of arbitrary size.
*
* This command's purpose is bi-directional exchange of arbitrary raw data.
* Useful for implementing higher-level protocols while using the RPC as a transport layer.
*/
RpcAppEventTypeDataExchange,
} RpcAppSystemEventType;
/**
* @brief RPC application subsystem event structure.
*/
typedef struct {
RpcAppSystemEventType type; /**< Type of the event. */
RpcAppSystemEventData data; /**< Data associated with the event. */
} RpcAppSystemEvent;
typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context);
typedef void (
*RpcAppSystemDataExchangeCallback)(const uint8_t* data, size_t data_size, void* context);
/**
* @brief Callback function type.
*
* A function of this type must be passed to rpc_system_app_set_callback() by the user code.
*
* @warning The event pointer is valid ONLY inside the callback function.
*
* @param[in] event pointer to the event object. Valid only inside the callback function.
* @param[in,out] context pointer to the user-defined context object.
*/
typedef void (*RpcAppSystemCallback)(const RpcAppSystemEvent* event, void* context);
/**
* @brief RPC application subsystem opaque type declaration.
*/
typedef struct RpcAppSystem RpcAppSystem;
void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx);
/**
* @brief Set the callback function for use by an RpcAppSystem instance.
*
* @param[in,out] rpc_app pointer to the instance to be configured.
* @param[in] callback pointer to the function to be called upon message reception.
* @param[in,out] context pointer to the user-defined context object. Will be passed to the callback.
*/
void rpc_system_app_set_callback(
RpcAppSystem* rpc_app,
RpcAppSystemCallback callback,
void* context);
/**
* @brief Send a notification that an RpcAppSystem instance has been started and is ready.
*
* Call this function once right after acquiring an RPC context and setting the callback.
*
* @param[in,out] rpc_app pointer to the instance to be used.
*/
void rpc_system_app_send_started(RpcAppSystem* rpc_app);
/**
* @brief Send a notification that the application using an RpcAppSystem instance is about to exit.
*
* Call this function when the application is about to exit (usually in the *_free() function).
*
* @param[in,out] rpc_app pointer to the instance to be used.
*/
void rpc_system_app_send_exited(RpcAppSystem* rpc_app);
const char* rpc_system_app_get_data(RpcAppSystem* rpc_app);
void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result);
/**
* @brief Send a confirmation that the application using an RpcAppSystem instance has handled the event.
*
* An explicit confirmation is required for the following event types:
* - RpcAppEventTypeAppExit
* - RpcAppEventTypeLoadFile
* - RpcAppEventTypeButtonPress
* - RpcAppEventTypeButtonRelease
* - RpcAppEventTypeDataExchange
*
* Not confirming these events will result in a client-side timeout.
*
* @param[in,out] rpc_app pointer to the instance to be used.
* @param[in] result whether the command was successfully handled or not (true for success).
*/
void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result);
/**
* @brief Set the error code stored in an RpcAppSystem instance.
*
* The error code can be retrieved by the client at any time by using the GetError request.
* The error code value has no meaning within the subsystem, i.e. it is only passed through to the client.
*
* @param[in,out] rpc_app pointer to the instance to be modified.
* @param[in] error_code arbitrary error code to be set.
*/
void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code);
/**
* @brief Set the error text stored in an RpcAppSystem instance.
*
* The error text can be retrieved by the client at any time by using the GetError request.
* The text has no meaning within the subsystem, i.e. it is only passed through to the client.
*
* @param[in,out] rpc_app pointer to the instance to be modified.
* @param[in] error_text Pointer to a zero-terminated string containing the error text.
*/
void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text);
/**
* @brief Reset the error code and text stored in an RpcAppSystem instance.
*
* Resets the error code to 0 and error text to "" (empty string).
*
* @param[in,out] rpc_app pointer to the instance to be reset.
*/
void rpc_system_app_error_reset(RpcAppSystem* rpc_app);
void rpc_system_app_set_data_exchange_callback(
RpcAppSystem* rpc_app,
RpcAppSystemDataExchangeCallback callback,
void* ctx);
/**
* @brief Send a byte array of arbitrary data to the client using an RpcAppSystem instance.
*
* @param[in,out] rpc_app pointer to the instance to be used.
* @param[in] data pointer to the data buffer to be sent.
* @param[in] data_size size of the data buffer, in bytes.
*/
void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size);
#ifdef __cplusplus