Merge remote-tracking branch 'ofw/dev' into mntm-dev

This commit is contained in:
Willy-JL
2024-09-05 18:13:22 +02:00
19 changed files with 389 additions and 117 deletions

View File

@@ -105,7 +105,7 @@ void ccid_test_app_free(CcidTestApp* app) {
furi_record_close(RECORD_GUI);
app->gui = NULL;
free(app->iso7816_handler);
iso7816_handler_free(app->iso7816_handler);
// Free rest
free(app);
@@ -121,8 +121,7 @@ int32_t ccid_test_app(void* p) {
furi_hal_usb_unlock();
furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true);
furi_hal_usb_ccid_set_callbacks(
(CcidCallbacks*)&app->iso7816_handler->ccid_callbacks, app->iso7816_handler);
iso7816_handler_set_usb_ccid_callbacks();
furi_hal_usb_ccid_insert_smartcard();
//handle button events
@@ -142,7 +141,7 @@ int32_t ccid_test_app(void* p) {
}
//tear down USB
furi_hal_usb_ccid_set_callbacks(NULL, NULL);
iso7816_handler_reset_usb_ccid_callbacks();
furi_hal_usb_set_config(usb_mode_prev, NULL);
//teardown view

View File

@@ -6,11 +6,17 @@
#include <furi.h>
#include <furi_hal.h>
#include "iso7816_handler.h"
#include "iso7816_t0_apdu.h"
#include "iso7816_atr.h"
#include "iso7816_handler.h"
#include "iso7816_response.h"
static Iso7816Handler* iso7816_handler;
static CcidCallbacks* ccid_callbacks;
static uint8_t* command_apdu_buffer;
static uint8_t* response_apdu_buffer;
void iso7816_icc_power_on_callback(uint8_t* atr_data, uint32_t* atr_data_len, void* context) {
furi_check(context);
@@ -40,12 +46,11 @@ void iso7816_xfr_datablock_callback(
Iso7816Handler* handler = (Iso7816Handler*)context;
ISO7816_Response_APDU* response_apdu = (ISO7816_Response_APDU*)&handler->response_apdu_buffer;
ISO7816_Command_APDU* command_apdu = (ISO7816_Command_APDU*)&handler->command_apdu_buffer;
ISO7816_Response_APDU* response_apdu = (ISO7816_Response_APDU*)response_apdu_buffer;
ISO7816_Command_APDU* command_apdu = (ISO7816_Command_APDU*)command_apdu_buffer;
uint8_t result = iso7816_read_command_apdu(
command_apdu, pc_to_reader_datablock, pc_to_reader_datablock_len);
command_apdu, pc_to_reader_datablock, pc_to_reader_datablock_len, CCID_SHORT_APDU_SIZE);
if(result == ISO7816_READ_COMMAND_APDU_OK) {
handler->iso7816_process_command(command_apdu, response_apdu);
@@ -61,8 +66,31 @@ void iso7816_xfr_datablock_callback(
}
Iso7816Handler* iso7816_handler_alloc() {
Iso7816Handler* handler = malloc(sizeof(Iso7816Handler));
handler->ccid_callbacks.icc_power_on_callback = iso7816_icc_power_on_callback;
handler->ccid_callbacks.xfr_datablock_callback = iso7816_xfr_datablock_callback;
return handler;
iso7816_handler = malloc(sizeof(Iso7816Handler));
command_apdu_buffer = malloc(sizeof(ISO7816_Command_APDU) + CCID_SHORT_APDU_SIZE);
response_apdu_buffer = malloc(sizeof(ISO7816_Response_APDU) + CCID_SHORT_APDU_SIZE);
ccid_callbacks = malloc(sizeof(CcidCallbacks));
ccid_callbacks->icc_power_on_callback = iso7816_icc_power_on_callback;
ccid_callbacks->xfr_datablock_callback = iso7816_xfr_datablock_callback;
return iso7816_handler;
}
void iso7816_handler_set_usb_ccid_callbacks() {
furi_hal_usb_ccid_set_callbacks(ccid_callbacks, iso7816_handler);
}
void iso7816_handler_reset_usb_ccid_callbacks() {
furi_hal_usb_ccid_set_callbacks(NULL, NULL);
}
void iso7816_handler_free(Iso7816Handler* handler) {
free(ccid_callbacks);
free(command_apdu_buffer);
free(response_apdu_buffer);
free(handler);
}

View File

@@ -5,14 +5,14 @@
#include "iso7816_t0_apdu.h"
typedef struct {
CcidCallbacks ccid_callbacks;
void (*iso7816_answer_to_reset)(Iso7816Atr* atr);
void (*iso7816_process_command)(
const ISO7816_Command_APDU* command,
ISO7816_Response_APDU* response);
uint8_t command_apdu_buffer[sizeof(ISO7816_Command_APDU) + CCID_SHORT_APDU_SIZE];
uint8_t response_apdu_buffer[sizeof(ISO7816_Response_APDU) + CCID_SHORT_APDU_SIZE];
} Iso7816Handler;
Iso7816Handler* iso7816_handler_alloc();
void iso7816_handler_free(Iso7816Handler* handler);
void iso7816_handler_set_usb_ccid_callbacks();
void iso7816_handler_reset_usb_ccid_callbacks();

View File

@@ -1,49 +1,48 @@
/* Implements rudimentary iso7816-3 support for APDU (T=0) */
#include <stdint.h>
#include <string.h>
#include <furi.h>
#include <furi_hal.h>
#include "iso7816_t0_apdu.h"
//reads dataBuffer with dataLen size, translate it into a ISO7816_Command_APDU type
//reads pc_to_reader_datablock_len with pc_to_reader_datablock_len size, translate it into a ISO7816_Command_APDU type
//extra data will be pointed to commandDataBuffer
uint8_t iso7816_read_command_apdu(
ISO7816_Command_APDU* command,
const uint8_t* dataBuffer,
uint32_t dataLen) {
command->CLA = dataBuffer[0];
command->INS = dataBuffer[1];
command->P1 = dataBuffer[2];
command->P2 = dataBuffer[3];
const uint8_t* pc_to_reader_datablock,
uint32_t pc_to_reader_datablock_len,
uint32_t max_apdu_size) {
command->CLA = pc_to_reader_datablock[0];
command->INS = pc_to_reader_datablock[1];
command->P1 = pc_to_reader_datablock[2];
command->P2 = pc_to_reader_datablock[3];
if(dataLen == 4) {
if(pc_to_reader_datablock_len == 4) {
command->Lc = 0;
command->Le = 0;
command->LePresent = false;
return ISO7816_READ_COMMAND_APDU_OK;
} else if(dataLen == 5) {
} else if(pc_to_reader_datablock_len == 5) {
//short le
command->Lc = 0;
command->Le = dataBuffer[4];
command->Le = pc_to_reader_datablock[4];
command->LePresent = true;
return ISO7816_READ_COMMAND_APDU_OK;
} else if(dataLen > 5 && dataBuffer[4] != 0x00) {
} else if(pc_to_reader_datablock_len > 5 && pc_to_reader_datablock[4] != 0x00) {
//short lc
command->Lc = dataBuffer[4];
if(command->Lc > 0 && command->Lc < CCID_SHORT_APDU_SIZE) { //-V560
memcpy(command->Data, &dataBuffer[5], command->Lc);
command->Lc = pc_to_reader_datablock[4];
if(command->Lc > 0 && command->Lc < max_apdu_size) { //-V560
memcpy(command->Data, &pc_to_reader_datablock[5], command->Lc);
//does it have a short le too?
if(dataLen == (uint32_t)(command->Lc + 5)) {
if(pc_to_reader_datablock_len == (uint32_t)(command->Lc + 5)) {
command->Le = 0;
command->LePresent = false;
return ISO7816_READ_COMMAND_APDU_OK;
} else if(dataLen == (uint32_t)(command->Lc + 6)) {
command->Le = dataBuffer[dataLen - 1];
} else if(pc_to_reader_datablock_len == (uint32_t)(command->Lc + 6)) {
command->Le = pc_to_reader_datablock[pc_to_reader_datablock_len - 1];
command->LePresent = true;
return ISO7816_READ_COMMAND_APDU_OK;

View File

@@ -34,7 +34,8 @@ typedef struct {
uint8_t iso7816_read_command_apdu(
ISO7816_Command_APDU* command,
const uint8_t* pc_to_reader_datablock,
uint32_t pc_to_reader_datablock_len);
uint32_t pc_to_reader_datablock_len,
uint32_t max_apdu_size);
void iso7816_write_response_apdu(
const ISO7816_Response_APDU* response,
uint8_t* reader_to_pc_datablock,

View File

@@ -6,18 +6,27 @@
* 13 -> 16 (USART TX to LPUART RX)
* 14 -> 15 (USART RX to LPUART TX)
*
* Optional: Connect an LED with an appropriate series resistor
* between pins 1 and 8. It will always be on if the device is
* connected to USB power, so unplug it before running the app.
*
* What this application does:
*
* - Enables module support and emulates the module on a single device
* (hence the above connection),
* - Connects to the expansion module service, sets baud rate,
* - Enables OTG (5V) on GPIO via plain expansion protocol,
* - Waits 5 cycles of idle loop (1 second),
* - Starts the RPC session,
* - Disables OTG (5V) on GPIO via RPC messages,
* - Waits 5 cycles of idle loop (1 second),
* - Creates a directory at `/ext/ExpansionTest` and writes a file
* named `test.txt` under it,
* - Plays an audiovisual alert (sound and blinking display),
* - Waits 10 cycles of idle loop,
* - Enables OTG (5V) on GPIO via RPC messages,
* - Waits 5 cycles of idle loop (1 second),
* - Stops the RPC session,
* - Waits another 10 cycles of idle loop,
* - Disables OTG (5V) on GPIO via plain expansion protocol,
* - Exits (plays a sound if any of the above steps failed).
*/
#include <furi.h>
@@ -302,6 +311,22 @@ static bool expansion_test_app_handshake(ExpansionTestApp* instance) {
return success;
}
static bool expansion_test_app_enable_otg(ExpansionTestApp* instance, bool enable) {
bool success = false;
do {
const ExpansionFrameControlCommand command = enable ?
ExpansionFrameControlCommandEnableOtg :
ExpansionFrameControlCommandDisableOtg;
if(!expansion_test_app_send_control_request(instance, command)) break;
if(!expansion_test_app_receive_frame(instance, &instance->frame)) break;
if(!expansion_test_app_is_success_response(&instance->frame)) break;
success = true;
} while(false);
return success;
}
static bool expansion_test_app_start_rpc(ExpansionTestApp* instance) {
bool success = false;
@@ -396,6 +421,27 @@ static bool expansion_test_app_rpc_alert(ExpansionTestApp* instance) {
return success;
}
static bool expansion_test_app_rpc_enable_otg(ExpansionTestApp* instance, bool enable) {
bool success = false;
instance->msg.command_id++;
instance->msg.command_status = PB_CommandStatus_OK;
instance->msg.which_content = PB_Main_gpio_set_otg_mode_tag;
instance->msg.content.gpio_set_otg_mode.mode = enable ? PB_Gpio_GpioOtgMode_ON :
PB_Gpio_GpioOtgMode_OFF;
instance->msg.has_next = false;
do {
if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break;
if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break;
if(instance->msg.which_content != PB_Main_empty_tag) break;
if(instance->msg.command_status != PB_CommandStatus_OK) break;
success = true;
} while(false);
return success;
}
static bool expansion_test_app_idle(ExpansionTestApp* instance, uint32_t num_cycles) {
uint32_t num_cycles_done;
for(num_cycles_done = 0; num_cycles_done < num_cycles; ++num_cycles_done) {
@@ -434,13 +480,18 @@ int32_t expansion_test_app(void* p) {
if(!expansion_test_app_send_presence(instance)) break;
if(!expansion_test_app_wait_ready(instance)) break;
if(!expansion_test_app_handshake(instance)) break;
if(!expansion_test_app_enable_otg(instance, true)) break;
if(!expansion_test_app_idle(instance, 5)) break;
if(!expansion_test_app_start_rpc(instance)) break;
if(!expansion_test_app_rpc_enable_otg(instance, false)) break;
if(!expansion_test_app_idle(instance, 5)) break;
if(!expansion_test_app_rpc_mkdir(instance)) break;
if(!expansion_test_app_rpc_write(instance)) break;
if(!expansion_test_app_rpc_alert(instance)) break;
if(!expansion_test_app_idle(instance, 10)) break;
if(!expansion_test_app_rpc_enable_otg(instance, true)) break;
if(!expansion_test_app_idle(instance, 5)) break;
if(!expansion_test_app_stop_rpc(instance)) break;
if(!expansion_test_app_idle(instance, 10)) break;
if(!expansion_test_app_enable_otg(instance, false)) break;
success = true;
} while(false);

View File

@@ -13,8 +13,7 @@ static void example_number_input_scene_update_view(void* context) {
dialog_ex_set_header(dialog_ex, "The number is", 64, 0, AlignCenter, AlignTop);
static char buffer[12]; //needs static for extended lifetime
char buffer[12] = {};
snprintf(buffer, sizeof(buffer), "%ld", app->current_number);
dialog_ex_set_text(dialog_ex, buffer, 64, 29, AlignCenter, AlignCenter);

View File

@@ -64,8 +64,28 @@ typedef enum {
* @brief Enumeration of suported control commands.
*/
typedef enum {
ExpansionFrameControlCommandStartRpc = 0x00, /**< Start an RPC session. */
ExpansionFrameControlCommandStopRpc = 0x01, /**< Stop an open RPC session. */
/** @brief Start an RPC session.
*
* Must only be used while the RPC session is NOT active.
*/
ExpansionFrameControlCommandStartRpc = 0x00,
/** @brief Stop an open RPC session.
*
* Must only be used while the RPC session IS active.
*/
ExpansionFrameControlCommandStopRpc = 0x01,
/** @brief Enable OTG (5V) on external GPIO.
*
* Must only be used while the RPC session is NOT active,
* otherwise OTG is to be controlled via RPC messages.
*/
ExpansionFrameControlCommandEnableOtg = 0x02,
/** @brief Disable OTG (5V) on external GPIO.
*
* Must only be used while the RPC session is NOT active,
* otherwise OTG is to be controlled via RPC messages.
*/
ExpansionFrameControlCommandDisableOtg = 0x03,
} ExpansionFrameControlCommand;
#pragma pack(push, 1)

View File

@@ -246,9 +246,18 @@ static bool expansion_worker_handle_state_connected(
do {
if(rx_frame->header.type == ExpansionFrameTypeControl) {
if(rx_frame->content.control.command != ExpansionFrameControlCommandStartRpc) break;
instance->state = ExpansionWorkerStateRpcActive;
if(!expansion_worker_rpc_session_open(instance)) break;
const uint8_t command = rx_frame->content.control.command;
if(command == ExpansionFrameControlCommandStartRpc) {
if(!expansion_worker_rpc_session_open(instance)) break;
instance->state = ExpansionWorkerStateRpcActive;
} else if(command == ExpansionFrameControlCommandEnableOtg) {
furi_hal_power_enable_otg();
} else if(command == ExpansionFrameControlCommandDisableOtg) {
furi_hal_power_disable_otg();
} else {
break;
}
if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break;
} else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) {
@@ -280,9 +289,14 @@ static bool expansion_worker_handle_state_rpc_active(
if(size_consumed != rx_frame->content.data.size) break;
} else if(rx_frame->header.type == ExpansionFrameTypeControl) {
if(rx_frame->content.control.command != ExpansionFrameControlCommandStopRpc) break;
instance->state = ExpansionWorkerStateConnected;
expansion_worker_rpc_session_close(instance);
const uint8_t command = rx_frame->content.control.command;
if(command == ExpansionFrameControlCommandStopRpc) {
instance->state = ExpansionWorkerStateConnected;
expansion_worker_rpc_session_close(instance);
} else {
break;
}
if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break;
} else if(rx_frame->header.type == ExpansionFrameTypeStatus) {

View File

@@ -211,6 +211,70 @@ void elements_button_right(Canvas* canvas, const char* str) {
canvas_invert_color(canvas);
}
void elements_button_up(Canvas* canvas, const char* str) {
furi_check(canvas);
const Icon* icon = &I_ButtonUp_7x4;
const size_t button_height = 12;
const size_t vertical_offset = 3;
const size_t horizontal_offset = 3;
const size_t string_width = canvas_string_width(canvas, str);
const int32_t icon_h_offset = 3;
const int32_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
const int32_t icon_v_offset = icon_get_height(icon) + (int32_t)vertical_offset;
const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
const int32_t x = 0;
const int32_t y = 0 + button_height;
int32_t line_x = x + button_width;
int32_t line_y = y - button_height;
canvas_draw_box(canvas, x, line_y, button_width, button_height);
canvas_draw_line(canvas, line_x + 0, line_y, line_x + 0, y - 1);
canvas_draw_line(canvas, line_x + 1, line_y, line_x + 1, y - 2);
canvas_draw_line(canvas, line_x + 2, line_y, line_x + 2, y - 3);
canvas_invert_color(canvas);
canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, icon);
canvas_draw_str(
canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
canvas_invert_color(canvas);
}
void elements_button_down(Canvas* canvas, const char* str) {
furi_check(canvas);
const Icon* icon = &I_ButtonDown_7x4;
const size_t button_height = 12;
const size_t vertical_offset = 3;
const size_t horizontal_offset = 3;
const size_t string_width = canvas_string_width(canvas, str);
const int32_t icon_h_offset = 3;
const int32_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
const int32_t icon_v_offset = icon_get_height(icon) + vertical_offset + 1;
const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
const int32_t x = canvas_width(canvas);
const int32_t y = button_height;
int32_t line_x = x - button_width;
int32_t line_y = y - button_height;
canvas_draw_box(canvas, line_x, line_y, button_width, button_height);
canvas_draw_line(canvas, line_x - 1, line_y, line_x - 1, y - 1);
canvas_draw_line(canvas, line_x - 2, line_y, line_x - 2, y - 2);
canvas_draw_line(canvas, line_x - 3, line_y, line_x - 3, y - 3);
canvas_invert_color(canvas);
canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str);
canvas_draw_icon(
canvas, x - horizontal_offset - icon_get_width(icon), y - icon_v_offset, icon);
canvas_invert_color(canvas);
}
void elements_button_center(Canvas* canvas, const char* str) {
furi_check(canvas);

View File

@@ -113,6 +113,28 @@ void elements_button_left(Canvas* canvas, const char* str);
*/
void elements_button_right(Canvas* canvas, const char* str);
/**
* @brief This function draws a button in the top left corner of the canvas with icon and string.
*
* The design and layout of the button is defined within this function.
*
* @param[in] canvas This is a pointer to the @c Canvas structure where the button will be drawn.
* @param[in] str This is a pointer to the character string that will be drawn within the button.
*
*/
void elements_button_up(Canvas* canvas, const char* str);
/**
* @brief This function draws a button in the top right corner of the canvas with icon and string.
*
* The design and layout of the button is defined within this function.
*
* @param[in] canvas This is a pointer to the @c Canvas structure where the button will be drawn.
* @param[in] str This is a pointer to the character string that will be drawn within the button.
*
*/
void elements_button_down(Canvas* canvas, const char* str);
/** Draw button in center
*
* @param canvas Canvas instance

View File

@@ -33,7 +33,7 @@ typedef struct {
static const uint8_t keyboard_origin_x = 7;
static const uint8_t keyboard_origin_y = 31;
static const uint8_t keyboard_row_count = 2;
static const int8_t keyboard_row_count = 2;
static const uint8_t enter_symbol = '\r';
static const uint8_t backspace_symbol = '\b';
static const uint8_t max_drawable_bytes = 8;
@@ -613,11 +613,11 @@ static void byte_input_view_draw_callback(Canvas* canvas, void* _model) {
}
canvas_set_font(canvas, FontKeyboard);
// Draw keyboard
for(uint8_t row = 0; row < keyboard_row_count; row++) {
for(int8_t row = 0; row < keyboard_row_count; row++) {
const uint8_t column_count = byte_input_get_row_size(row);
const ByteInputKey* keys = byte_input_get_row(row);
for(size_t column = 0; column < column_count; column++) {
for(uint8_t column = 0; column < column_count; column++) {
bool selected = model->selected_row == row && model->selected_column == column;
const Icon* icon = NULL;
if(keys[column].value == enter_symbol) {

View File

@@ -10,7 +10,7 @@ struct DialogEx {
};
typedef struct {
const char* text;
FuriString* text;
uint8_t x;
uint8_t y;
Align horizontal;
@@ -28,16 +28,15 @@ typedef struct {
TextElement text;
IconElement icon;
const char* left_text;
const char* center_text;
const char* right_text;
FuriString* left_text;
FuriString* center_text;
FuriString* right_text;
} DialogExModel;
static void dialog_ex_view_draw_callback(Canvas* canvas, void* _model) {
DialogExModel* model = _model;
// Prepare canvas
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
if(model->icon.icon != NULL) {
@@ -46,94 +45,94 @@ static void dialog_ex_view_draw_callback(Canvas* canvas, void* _model) {
// Draw header
canvas_set_font(canvas, FontPrimary);
if(model->header.text != NULL) {
if(furi_string_size(model->header.text)) {
elements_multiline_text_aligned(
canvas,
model->header.x,
model->header.y,
model->header.horizontal,
model->header.vertical,
model->header.text);
furi_string_get_cstr(model->header.text));
}
// Draw text
canvas_set_font(canvas, FontSecondary);
if(model->text.text != NULL) {
if(furi_string_size(model->text.text)) {
elements_multiline_text_aligned(
canvas,
model->text.x,
model->text.y,
model->text.horizontal,
model->text.vertical,
model->text.text);
furi_string_get_cstr(model->text.text));
}
// Draw buttons
if(model->left_text != NULL) {
elements_button_left(canvas, model->left_text);
if(furi_string_size(model->left_text)) {
elements_button_left(canvas, furi_string_get_cstr(model->left_text));
}
if(model->center_text != NULL) {
elements_button_center(canvas, model->center_text);
if(furi_string_size(model->center_text)) {
elements_button_center(canvas, furi_string_get_cstr(model->center_text));
}
if(model->right_text != NULL) {
elements_button_right(canvas, model->right_text);
if(furi_string_size(model->right_text)) {
elements_button_right(canvas, furi_string_get_cstr(model->right_text));
}
}
static bool dialog_ex_view_input_callback(InputEvent* event, void* context) {
DialogEx* dialog_ex = context;
bool consumed = false;
const char* left_text = NULL;
const char* center_text = NULL;
const char* right_text = NULL;
bool left_text_present = false;
bool center_text_present = false;
bool right_text_present = false;
with_view_model(
dialog_ex->view,
DialogExModel * model,
{
left_text = model->left_text;
center_text = model->center_text;
right_text = model->right_text;
left_text_present = furi_string_size(model->left_text);
center_text_present = furi_string_size(model->center_text);
right_text_present = furi_string_size(model->right_text);
},
true);
false);
if(dialog_ex->callback) {
if(event->type == InputTypeShort) {
if(event->key == InputKeyLeft && left_text != NULL) {
if(event->key == InputKeyLeft && left_text_present) {
dialog_ex->callback(DialogExResultLeft, dialog_ex->context);
consumed = true;
} else if(event->key == InputKeyOk && center_text != NULL) {
} else if(event->key == InputKeyOk && center_text_present) {
dialog_ex->callback(DialogExResultCenter, dialog_ex->context);
consumed = true;
} else if(event->key == InputKeyRight && right_text != NULL) {
} else if(event->key == InputKeyRight && right_text_present) {
dialog_ex->callback(DialogExResultRight, dialog_ex->context);
consumed = true;
}
}
if(event->type == InputTypePress && dialog_ex->enable_extended_events) {
if(event->key == InputKeyLeft && left_text != NULL) {
if(event->key == InputKeyLeft && left_text_present) {
dialog_ex->callback(DialogExPressLeft, dialog_ex->context);
consumed = true;
} else if(event->key == InputKeyOk && center_text != NULL) {
} else if(event->key == InputKeyOk && center_text_present) {
dialog_ex->callback(DialogExPressCenter, dialog_ex->context);
consumed = true;
} else if(event->key == InputKeyRight && right_text != NULL) {
} else if(event->key == InputKeyRight && right_text_present) {
dialog_ex->callback(DialogExPressRight, dialog_ex->context);
consumed = true;
}
}
if(event->type == InputTypeRelease && dialog_ex->enable_extended_events) {
if(event->key == InputKeyLeft && left_text != NULL) {
if(event->key == InputKeyLeft && left_text_present) {
dialog_ex->callback(DialogExReleaseLeft, dialog_ex->context);
consumed = true;
} else if(event->key == InputKeyOk && center_text != NULL) {
} else if(event->key == InputKeyOk && center_text_present) {
dialog_ex->callback(DialogExReleaseCenter, dialog_ex->context);
consumed = true;
} else if(event->key == InputKeyRight && right_text != NULL) {
} else if(event->key == InputKeyRight && right_text_present) {
dialog_ex->callback(DialogExReleaseRight, dialog_ex->context);
consumed = true;
}
@@ -154,13 +153,13 @@ DialogEx* dialog_ex_alloc(void) {
dialog_ex->view,
DialogExModel * model,
{
model->header.text = NULL;
model->header.text = furi_string_alloc();
model->header.x = 0;
model->header.y = 0;
model->header.horizontal = AlignLeft;
model->header.vertical = AlignBottom;
model->text.text = NULL;
model->text.text = furi_string_alloc();
model->text.x = 0;
model->text.y = 0;
model->text.horizontal = AlignLeft;
@@ -170,17 +169,28 @@ DialogEx* dialog_ex_alloc(void) {
model->icon.y = 0;
model->icon.icon = NULL;
model->left_text = NULL;
model->center_text = NULL;
model->right_text = NULL;
model->left_text = furi_string_alloc();
model->center_text = furi_string_alloc();
model->right_text = furi_string_alloc();
},
true);
false);
dialog_ex->enable_extended_events = false;
return dialog_ex;
}
void dialog_ex_free(DialogEx* dialog_ex) {
furi_check(dialog_ex);
with_view_model(
dialog_ex->view,
DialogExModel * model,
{
furi_string_free(model->header.text);
furi_string_free(model->text.text);
furi_string_free(model->left_text);
furi_string_free(model->center_text);
furi_string_free(model->right_text);
},
false);
view_free(dialog_ex->view);
free(dialog_ex);
}
@@ -212,7 +222,7 @@ void dialog_ex_set_header(
dialog_ex->view,
DialogExModel * model,
{
model->header.text = text;
furi_string_set(model->header.text, text);
model->header.x = x;
model->header.y = y;
model->header.horizontal = horizontal;
@@ -233,7 +243,7 @@ void dialog_ex_set_text(
dialog_ex->view,
DialogExModel * model,
{
model->text.text = text;
furi_string_set(model->text.text, text);
model->text.x = x;
model->text.y = y;
model->text.horizontal = horizontal;
@@ -257,34 +267,41 @@ void dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* i
void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text) {
furi_check(dialog_ex);
with_view_model(dialog_ex->view, DialogExModel * model, { model->left_text = text; }, true);
with_view_model(
dialog_ex->view, DialogExModel * model, { furi_string_set(model->left_text, text); }, true);
}
void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text) {
furi_check(dialog_ex);
with_view_model(dialog_ex->view, DialogExModel * model, { model->center_text = text; }, true);
with_view_model(
dialog_ex->view,
DialogExModel * model,
{ furi_string_set(model->center_text, text); },
true);
}
void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text) {
furi_check(dialog_ex);
with_view_model(dialog_ex->view, DialogExModel * model, { model->right_text = text; }, true);
with_view_model(
dialog_ex->view,
DialogExModel * model,
{ furi_string_set(model->right_text, text); },
true);
}
void dialog_ex_reset(DialogEx* dialog_ex) {
furi_check(dialog_ex);
TextElement clean_text_el = {
.text = NULL, .x = 0, .y = 0, .horizontal = AlignLeft, .vertical = AlignLeft};
IconElement clean_icon_el = {.icon = NULL, .x = 0, .y = 0};
with_view_model(
dialog_ex->view,
DialogExModel * model,
{
model->header = clean_text_el;
model->text = clean_text_el;
model->icon = clean_icon_el;
model->left_text = NULL;
model->center_text = NULL;
model->right_text = NULL;
model->icon.icon = NULL;
furi_string_reset(model->header.text);
furi_string_reset(model->text.text);
furi_string_reset(model->left_text);
furi_string_reset(model->center_text);
furi_string_reset(model->right_text);
},
true);
dialog_ex->context = NULL;

View File

@@ -2,6 +2,7 @@
#include "rpc_i.h"
#include "gpio.pb.h"
#include <furi_hal_gpio.h>
#include <furi_hal_power.h>
#include <furi_hal_resources.h>
static const GpioPin* rpc_pin_to_hal_pin(PB_Gpio_GpioPin rpc_pin) {
@@ -188,6 +189,44 @@ void rpc_system_gpio_set_input_pull(const PB_Main* request, void* context) {
free(response);
}
void rpc_system_gpio_get_otg_mode(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_gpio_get_otg_mode_tag);
RpcSession* session = context;
const bool otg_enabled = furi_hal_power_is_otg_enabled();
PB_Main* response = malloc(sizeof(PB_Main));
response->command_id = request->command_id;
response->which_content = PB_Main_gpio_get_otg_mode_response_tag;
response->content.gpio_get_otg_mode_response.mode = otg_enabled ? PB_Gpio_GpioOtgMode_ON :
PB_Gpio_GpioOtgMode_OFF;
rpc_send_and_release(session, response);
free(response);
}
void rpc_system_gpio_set_otg_mode(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_gpio_set_otg_mode_tag);
RpcSession* session = context;
const PB_Gpio_GpioOtgMode mode = request->content.gpio_set_otg_mode.mode;
if(mode == PB_Gpio_GpioOtgMode_OFF) {
furi_hal_power_disable_otg();
} else {
furi_hal_power_enable_otg();
}
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
}
void* rpc_system_gpio_alloc(RpcSession* session) {
furi_assert(session);
@@ -212,5 +251,11 @@ void* rpc_system_gpio_alloc(RpcSession* session) {
rpc_handler.message_handler = rpc_system_gpio_set_input_pull;
rpc_add_handler(session, PB_Main_gpio_set_input_pull_tag, &rpc_handler);
rpc_handler.message_handler = rpc_system_gpio_get_otg_mode;
rpc_add_handler(session, PB_Main_gpio_get_otg_mode_tag, &rpc_handler);
rpc_handler.message_handler = rpc_system_gpio_set_otg_mode;
rpc_add_handler(session, PB_Main_gpio_set_otg_mode_tag, &rpc_handler);
return NULL;
}

View File

@@ -73,7 +73,7 @@ If the requested baud rate is supported by the host, it SHALL respond with a STA
### Control frame
CONTROL frames are used to control various aspects of the communication. As of now, the sole purpose of CONTROL frames is to start and stop the RPC session.
CONTROL frames are used to control various aspects of the communication and enable/disable various device features.
| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) |
|-----------------|-------------------|-------------------|
@@ -81,10 +81,18 @@ CONTROL frames are used to control various aspects of the communication. As of n
The `Command` field SHALL have one of the followind values:
| Command | Meaning |
|---------|-------------------|
| 0x00 | Start RPC session |
| 0x01 | Stop RPC session |
| Command | Meaning | Note |
|---------|--------------------------|:----:|
| 0x00 | Start RPC session | 1 |
| 0x01 | Stop RPC session | 2 |
| 0x02 | Enable OTG (5V) on GPIO | 3 |
| 0x03 | Disable OTG (5V) on GPIO | 3 |
Notes:
1. Must only be used while the RPC session NOT active.
2. Must only be used while the RPC session IS active.
3. See 1, otherwise OTG is to be controlled via RPC messages.
### Data frame

View File

@@ -47,6 +47,7 @@ def generate(env):
PVSOPTIONS=[
"@.pvsoptions",
"-j${PVSNCORES}",
"--disableLicenseExpirationCheck",
# "--incremental", # kinda broken on PVS side
],
PVSCONVOPTIONS=[

View File

@@ -13,13 +13,13 @@ Header,+,applications/services/gui/icon_i.h,,
Header,+,applications/services/gui/modules/button_menu.h,,
Header,+,applications/services/gui/modules/button_panel.h,,
Header,+,applications/services/gui/modules/byte_input.h,,
Header,+,applications/services/gui/modules/number_input.h,,
Header,+,applications/services/gui/modules/dialog_ex.h,,
Header,+,applications/services/gui/modules/empty_screen.h,,
Header,+,applications/services/gui/modules/file_browser.h,,
Header,+,applications/services/gui/modules/file_browser_worker.h,,
Header,+,applications/services/gui/modules/loading.h,,
Header,+,applications/services/gui/modules/menu.h,,
Header,+,applications/services/gui/modules/number_input.h,,
Header,+,applications/services/gui/modules/popup.h,,
Header,+,applications/services/gui/modules/submenu.h,,
Header,+,applications/services/gui/modules/text_box.h,,
@@ -723,11 +723,6 @@ Function,+,byte_input_free,void,ByteInput*
Function,+,byte_input_get_view,View*,ByteInput*
Function,+,byte_input_set_header_text,void,"ByteInput*, const char*"
Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t"
Function,+,number_input_alloc,NumberInput*,
Function,+,number_input_free,void,NumberInput*
Function,+,number_input_get_view,View*,NumberInput*
Function,+,number_input_set_header_text,void,"NumberInput*, const char*"
Function,+,number_input_set_result_callback,void,"NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t"
Function,-,bzero,void,"void*, size_t"
Function,+,calloc,void*,"size_t, size_t"
Function,+,canvas_clear,void,Canvas*
@@ -883,8 +878,10 @@ Function,+,elements_bold_rounded_frame,void,"Canvas*, int32_t, int32_t, size_t,
Function,+,elements_bubble,void,"Canvas*, int32_t, int32_t, size_t, size_t"
Function,+,elements_bubble_str,void,"Canvas*, int32_t, int32_t, const char*, Align, Align"
Function,+,elements_button_center,void,"Canvas*, const char*"
Function,+,elements_button_down,void,"Canvas*, const char*"
Function,+,elements_button_left,void,"Canvas*, const char*"
Function,+,elements_button_right,void,"Canvas*, const char*"
Function,+,elements_button_up,void,"Canvas*, const char*"
Function,+,elements_frame,void,"Canvas*, int32_t, int32_t, size_t, size_t"
Function,+,elements_multiline_text,void,"Canvas*, int32_t, int32_t, const char*"
Function,+,elements_multiline_text_aligned,void,"Canvas*, int32_t, int32_t, Align, Align, const char*"
@@ -2197,6 +2194,11 @@ Function,+,notification_internal_message_block,void,"NotificationApp*, const Not
Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*"
Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*"
Function,-,nrand48,long,unsigned short[3]
Function,+,number_input_alloc,NumberInput*,
Function,+,number_input_free,void,NumberInput*
Function,+,number_input_get_view,View*,NumberInput*
Function,+,number_input_set_header_text,void,"NumberInput*, const char*"
Function,+,number_input_set_result_callback,void,"NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t"
Function,-,on_exit,int,"void (*)(int, void*), void*"
Function,+,onewire_host_alloc,OneWireHost*,const GpioPin*
Function,+,onewire_host_free,void,OneWireHost*
1 entry status name type params
13 Header + applications/services/gui/modules/button_menu.h
14 Header + applications/services/gui/modules/button_panel.h
15 Header + applications/services/gui/modules/byte_input.h
Header + applications/services/gui/modules/number_input.h
16 Header + applications/services/gui/modules/dialog_ex.h
17 Header + applications/services/gui/modules/empty_screen.h
18 Header + applications/services/gui/modules/file_browser.h
19 Header + applications/services/gui/modules/file_browser_worker.h
20 Header + applications/services/gui/modules/loading.h
21 Header + applications/services/gui/modules/menu.h
22 Header + applications/services/gui/modules/number_input.h
23 Header + applications/services/gui/modules/popup.h
24 Header + applications/services/gui/modules/submenu.h
25 Header + applications/services/gui/modules/text_box.h
723 Function + byte_input_get_view View* ByteInput*
724 Function + byte_input_set_header_text void ByteInput*, const char*
725 Function + byte_input_set_result_callback void ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t
Function + number_input_alloc NumberInput*
Function + number_input_free void NumberInput*
Function + number_input_get_view View* NumberInput*
Function + number_input_set_header_text void NumberInput*, const char*
Function + number_input_set_result_callback void NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t
726 Function - bzero void void*, size_t
727 Function + calloc void* size_t, size_t
728 Function + canvas_clear void Canvas*
878 Function + elements_bubble void Canvas*, int32_t, int32_t, size_t, size_t
879 Function + elements_bubble_str void Canvas*, int32_t, int32_t, const char*, Align, Align
880 Function + elements_button_center void Canvas*, const char*
881 Function + elements_button_down void Canvas*, const char*
882 Function + elements_button_left void Canvas*, const char*
883 Function + elements_button_right void Canvas*, const char*
884 Function + elements_button_up void Canvas*, const char*
885 Function + elements_frame void Canvas*, int32_t, int32_t, size_t, size_t
886 Function + elements_multiline_text void Canvas*, int32_t, int32_t, const char*
887 Function + elements_multiline_text_aligned void Canvas*, int32_t, int32_t, Align, Align, const char*
2194 Function + notification_message void NotificationApp*, const NotificationSequence*
2195 Function + notification_message_block void NotificationApp*, const NotificationSequence*
2196 Function - nrand48 long unsigned short[3]
2197 Function + number_input_alloc NumberInput*
2198 Function + number_input_free void NumberInput*
2199 Function + number_input_get_view View* NumberInput*
2200 Function + number_input_set_header_text void NumberInput*, const char*
2201 Function + number_input_set_result_callback void NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t
2202 Function - on_exit int void (*)(int, void*), void*
2203 Function + onewire_host_alloc OneWireHost* const GpioPin*
2204 Function + onewire_host_free void OneWireHost*

View File

@@ -986,8 +986,10 @@ Function,+,elements_bold_rounded_frame,void,"Canvas*, int32_t, int32_t, size_t,
Function,+,elements_bubble,void,"Canvas*, int32_t, int32_t, size_t, size_t"
Function,+,elements_bubble_str,void,"Canvas*, int32_t, int32_t, const char*, Align, Align"
Function,+,elements_button_center,void,"Canvas*, const char*"
Function,+,elements_button_down,void,"Canvas*, const char*"
Function,+,elements_button_left,void,"Canvas*, const char*"
Function,+,elements_button_right,void,"Canvas*, const char*"
Function,+,elements_button_up,void,"Canvas*, const char*"
Function,+,elements_frame,void,"Canvas*, int32_t, int32_t, size_t, size_t"
Function,+,elements_multiline_text,void,"Canvas*, int32_t, int32_t, const char*"
Function,+,elements_multiline_text_aligned,void,"Canvas*, int32_t, int32_t, Align, Align, const char*"
1 entry status name type params
986 Function + elements_bubble void Canvas*, int32_t, int32_t, size_t, size_t
987 Function + elements_bubble_str void Canvas*, int32_t, int32_t, const char*, Align, Align
988 Function + elements_button_center void Canvas*, const char*
989 Function + elements_button_down void Canvas*, const char*
990 Function + elements_button_left void Canvas*, const char*
991 Function + elements_button_right void Canvas*, const char*
992 Function + elements_button_up void Canvas*, const char*
993 Function + elements_frame void Canvas*, int32_t, int32_t, size_t, size_t
994 Function + elements_multiline_text void Canvas*, int32_t, int32_t, const char*
995 Function + elements_multiline_text_aligned void Canvas*, int32_t, int32_t, Align, Align, const char*