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

This commit is contained in:
Willy-JL
2025-02-28 00:56:55 +00:00
81 changed files with 1521 additions and 1815 deletions
+19 -5
View File
@@ -21,10 +21,16 @@
- Desktop:
- UL: Option to prevent Auto Lock when connected to USB/RPC (by @Dmitry422)
- OFW: Add the Showtime animation (by @Astrrra)
- OFW: JS: Features & bugfixes, SDK 0.2 (by @portasynthinca3)
- New `gui/widget` view, replaces old `widget` module
- Support for PWM in `gpio` module
- Stop `eventloop` on request and error
- JS:
- OFW: Features & bugfixes, SDK 0.2 (by @portasynthinca3)
- New `gui/widget` view, replaces old `widget` module
- Support for PWM in `gpio` module
- Stop `eventloop` on request and error
- OFW: SDK 0.3
- Backport of missing features to new `gui/widget` (by @Willy-JL)
- UART framing data/stop/parity bits options in `serial` module (by @portasynthinca3)
- OFW: Furi: UART framing mode selection, support for different data/stop/parity bits (by @portasynthinca3)
- OFW: GUI: Widget elements for line, rect and circle with fill options (by @Willy-JL)
### Updated:
- Apps:
@@ -40,19 +46,27 @@
- Additionally, can now customize MAC address when BLE Remember is enabled
- NFC:
- OFW: Added naming for DESFire cards + fix MF3ICD40 cards unable to be read (by @Demae)
- OFW: FeliCa Protocol Expose Read Block API and Allow Specifying Service (by @zinongli)
- OFW: Enable MFUL sync poller to be provided with passwords (by @GMMan)
- Infrared:
- OFW: Add Fujitsu ASTG12LVCC to AC Universal Remote (by @KereruA0i)
- OFW: Increase max carrier limit to 1000000 (by @skotopes)
- OFW: API: Update mbedtls & expose AES (by @portasynthinca3)
- OFW: Power: Added OTG controls to Power service, remembers OTG when unplugging USB (by @Astrrra & @skotopes)
- OFW: GUI: Updated Button Panel with more options for button handling (by @Akiva-Cohen)
- Furi:
- OFW: Update heap4 implementation, enabled heap corruption detection (by @portasynthinca3)
- OFW: Update mbedtls & expose AES to API (by @portasynthinca3)
- OFW: Stdio API improvements, pipe stdout timeout (by @portasynthinca3)
### Fixed:
- Asset Packs: Fix level-up animations not being themed (by @Willy-JL)
- About: Fix missing Prev. button when invoked from Device Info keybind (by @Willy-JL)
- OFW: NFC: ST25TB poller mode check (by @RebornedBrain)
- RFID: Fix Detection Conflict Between Securakey and Noralsy Format (by @zinongli)
- Furi:
- OFW: EventLoop unsubscribe fix (by @gsurkov & @portasynthinca3)
- OFW: Various bug fixes and improvements (by @skotopes)
- OFW: Clear IRQ status before calling user handler, fixes some interrupt edge cases / weirdness (by @mammothbane)
- OFW: Ensure that `furi_record_create()` is passed a non-NULL data pointer (by @dcoles)
- OFW: CLI: Fixed repeat in subghz tx_from_file command (by @Jnesselr)
- OFW: VSCode: Disabled auto-update for clangd since correct version is in the toolchain (by @hedger)
+5 -2
View File
@@ -2,6 +2,7 @@
#include <furi.h>
#include <furi_hal.h>
#include <stdarg.h>
#include <power/power_service/power.h>
void AccessorApp::run(void) {
AccessorEvent event;
@@ -35,16 +36,18 @@ AccessorApp::AccessorApp()
: text_store{0} {
notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));
expansion = static_cast<Expansion*>(furi_record_open(RECORD_EXPANSION));
power = static_cast<Power*>(furi_record_open(RECORD_POWER));
onewire_host = onewire_host_alloc(&gpio_ibutton);
expansion_disable(expansion);
furi_hal_power_enable_otg();
power_enable_otg(power, true);
}
AccessorApp::~AccessorApp() {
furi_hal_power_disable_otg();
power_enable_otg(power, false);
expansion_enable(expansion);
furi_record_close(RECORD_EXPANSION);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_POWER);
onewire_host_free(onewire_host);
}
@@ -7,6 +7,7 @@
#include <one_wire/one_wire_host.h>
#include <notification/notification_messages.h>
#include <expansion/expansion.h>
#include <power/power_service/power.h>
class AccessorApp {
public:
@@ -53,4 +54,5 @@ private:
NotificationApp* notification;
Expansion* expansion;
Power* power;
};
@@ -24,8 +24,49 @@ typedef enum {
CrashTestSubmenuAssertMessage,
CrashTestSubmenuCrash,
CrashTestSubmenuHalt,
CrashTestSubmenuHeapUnderflow,
CrashTestSubmenuHeapOverflow,
} CrashTestSubmenu;
static void crash_test_corrupt_heap_underflow(void) {
const size_t block_size = 1000;
const size_t underflow_size = 123;
uint8_t* block = malloc(block_size);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow" // that's what we want!
memset(block - underflow_size, 0xDD, underflow_size); // -V769
#pragma GCC diagnostic pop
free(block); // should crash here (if compiled with DEBUG=1)
// If we got here, the heap wasn't able to detect our corruption and crash
furi_crash("Test failed, should've crashed with \"FreeRTOS Assert\" error");
}
static void crash_test_corrupt_heap_overflow(void) {
const size_t block_size = 1000;
const size_t overflow_size = 123;
uint8_t* block1 = malloc(block_size);
uint8_t* block2 = malloc(block_size);
memset(block2, 12, 34); // simulate use to avoid optimization // -V597 // -V1086
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow" // that's what we want!
memset(block1 + block_size, 0xDD, overflow_size); // -V769 // -V512
#pragma GCC diagnostic pop
uint8_t* block3 = malloc(block_size);
memset(block3, 12, 34); // simulate use to avoid optimization // -V597 // -V1086
free(block3); // should crash here (if compiled with DEBUG=1)
free(block2);
free(block1);
// If we got here, the heap wasn't able to detect our corruption and crash
furi_crash("Test failed, should've crashed with \"FreeRTOS Assert\" error");
}
static void crash_test_submenu_callback(void* context, uint32_t index) {
CrashTest* instance = (CrashTest*)context;
UNUSED(instance);
@@ -49,6 +90,12 @@ static void crash_test_submenu_callback(void* context, uint32_t index) {
case CrashTestSubmenuHalt:
furi_halt("Crash test: furi_halt");
break;
case CrashTestSubmenuHeapUnderflow:
crash_test_corrupt_heap_underflow();
break;
case CrashTestSubmenuHeapOverflow:
crash_test_corrupt_heap_overflow();
break;
default:
furi_crash();
}
@@ -94,6 +141,18 @@ CrashTest* crash_test_alloc(void) {
instance->submenu, "Crash", CrashTestSubmenuCrash, crash_test_submenu_callback, instance);
submenu_add_item(
instance->submenu, "Halt", CrashTestSubmenuHalt, crash_test_submenu_callback, instance);
submenu_add_item(
instance->submenu,
"Heap underflow",
CrashTestSubmenuHeapUnderflow,
crash_test_submenu_callback,
instance);
submenu_add_item(
instance->submenu,
"Heap overflow",
CrashTestSubmenuHeapOverflow,
crash_test_submenu_callback,
instance);
return instance;
}
+89 -9
View File
@@ -16,6 +16,9 @@
#define LINES_ON_SCREEN 6
#define COLUMNS_ON_SCREEN 21
#define DEFAULT_BAUD_RATE 230400
#define DEFAULT_DATA_BITS FuriHalSerialDataBits8
#define DEFAULT_PARITY FuriHalSerialParityNone
#define DEFAULT_STOP_BITS FuriHalSerialStopBits1
typedef struct UartDumpModel UartDumpModel;
@@ -49,11 +52,12 @@ typedef enum {
WorkerEventRxOverrunError = (1 << 4),
WorkerEventRxFramingError = (1 << 5),
WorkerEventRxNoiseError = (1 << 6),
WorkerEventRxParityError = (1 << 7),
} WorkerEventFlags;
#define WORKER_EVENTS_MASK \
(WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \
WorkerEventRxFramingError | WorkerEventRxNoiseError)
WorkerEventRxFramingError | WorkerEventRxNoiseError | WorkerEventRxParityError)
const NotificationSequence sequence_notification = {
&message_display_backlight_on,
@@ -62,6 +66,13 @@ const NotificationSequence sequence_notification = {
NULL,
};
const NotificationSequence sequence_error = {
&message_display_backlight_on,
&message_red_255,
&message_delay_10,
NULL,
};
static void uart_echo_view_draw_callback(Canvas* canvas, void* _model) {
UartDumpModel* model = _model;
@@ -133,6 +144,9 @@ static void
if(event & FuriHalSerialRxEventOverrunError) {
flag |= WorkerEventRxOverrunError;
}
if(event & FuriHalSerialRxEventParityError) {
flag |= WorkerEventRxParityError;
}
furi_thread_flags_set(furi_thread_get_id(app->worker_thread), flag);
}
@@ -227,13 +241,21 @@ static int32_t uart_echo_worker(void* context) {
if(events & WorkerEventRxNoiseError) {
furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect NE\r\n", 13);
}
if(events & WorkerEventRxParityError) {
furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect PE\r\n", 13);
}
notification_message(app->notification, &sequence_error);
}
}
return 0;
}
static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) {
static UartEchoApp* uart_echo_app_alloc(
uint32_t baudrate,
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits) {
UartEchoApp* app = malloc(sizeof(UartEchoApp));
app->rx_stream = furi_stream_buffer_alloc(2048, 1);
@@ -275,6 +297,7 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) {
app->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart);
furi_check(app->serial_handle);
furi_hal_serial_init(app->serial_handle, baudrate);
furi_hal_serial_configure_framing(app->serial_handle, data_bits, parity, stop_bits);
furi_hal_serial_async_rx_start(app->serial_handle, uart_echo_on_irq_cb, app, true);
@@ -319,19 +342,76 @@ static void uart_echo_app_free(UartEchoApp* app) {
free(app);
}
// silences "same-assignment" false positives in the arg parser below
// -V::1048
int32_t uart_echo_app(void* p) {
uint32_t baudrate = DEFAULT_BAUD_RATE;
FuriHalSerialDataBits data_bits = DEFAULT_DATA_BITS;
FuriHalSerialParity parity = DEFAULT_PARITY;
FuriHalSerialStopBits stop_bits = DEFAULT_STOP_BITS;
if(p) {
const char* baudrate_str = p;
if(strint_to_uint32(baudrate_str, NULL, &baudrate, 10) != StrintParseNoError) {
FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str);
baudrate = DEFAULT_BAUD_RATE;
// parse argument
char* parse_ptr = p;
bool parse_success = false;
do {
if(strint_to_uint32(parse_ptr, &parse_ptr, &baudrate, 10) != StrintParseNoError) break;
if(*(parse_ptr++) != '_') break;
uint16_t data_bits_int;
if(strint_to_uint16(parse_ptr, &parse_ptr, &data_bits_int, 10) != StrintParseNoError)
break;
if(data_bits_int == 6)
data_bits = FuriHalSerialDataBits6;
else if(data_bits_int == 7)
data_bits = FuriHalSerialDataBits7;
else if(data_bits_int == 8)
data_bits = FuriHalSerialDataBits8;
else if(data_bits_int == 9)
data_bits = FuriHalSerialDataBits9;
else
break;
char parity_char = *(parse_ptr++);
if(parity_char == 'N')
parity = FuriHalSerialParityNone;
else if(parity_char == 'E')
parity = FuriHalSerialParityEven;
else if(parity_char == 'O')
parity = FuriHalSerialParityOdd;
else
break;
uint16_t stop_bits_int;
if(strint_to_uint16(parse_ptr, &parse_ptr, &stop_bits_int, 10) != StrintParseNoError)
break;
if(stop_bits_int == 5)
stop_bits = FuriHalSerialStopBits0_5;
else if(stop_bits_int == 1)
stop_bits = FuriHalSerialStopBits1;
else if(stop_bits_int == 15)
stop_bits = FuriHalSerialStopBits1_5;
else if(stop_bits_int == 2)
stop_bits = FuriHalSerialStopBits2;
else
break;
parse_success = true;
} while(0);
if(!parse_success) {
FURI_LOG_I(
TAG,
"Couldn't parse baud rate and framing (%s). Applying defaults (%d_8N1)",
(const char*)p,
DEFAULT_BAUD_RATE);
}
}
FURI_LOG_I(TAG, "Using baudrate: %lu", baudrate);
UartEchoApp* app = uart_echo_app_alloc(baudrate);
UartEchoApp* app = uart_echo_app_alloc(baudrate, data_bits, parity, stop_bits);
view_dispatcher_run(app->view_dispatcher);
uart_echo_app_free(app);
return 0;
@@ -12,4 +12,4 @@ tests.assert_eq(false, doesSdkSupport(["abobus", "other-nonexistent-feature"]));
tests.assert_eq("momentum", flipper.firmwareVendor);
tests.assert_eq(0, flipper.jsSdkVersion[0]);
tests.assert_eq(2, flipper.jsSdkVersion[1]);
tests.assert_eq(3, flipper.jsSdkVersion[1]);
@@ -30,7 +30,9 @@ static size_t mock_in_cb(char* buffer, size_t size, FuriWait wait, void* context
}
void test_stdin(void) {
FuriThreadStdinReadCallback in_cb = furi_thread_get_stdin_callback();
FuriThreadStdinReadCallback in_cb;
void* in_ctx;
furi_thread_get_stdin_callback(&in_cb, &in_ctx);
furi_thread_set_stdin_callback(mock_in_cb, CONTEXT_MAGIC);
char buf[256];
@@ -63,13 +65,14 @@ void test_stdin(void) {
fgets(buf, sizeof(buf), stdin);
mu_assert_string_eq(" World!\n", buf);
furi_thread_set_stdin_callback(in_cb, CONTEXT_MAGIC);
furi_thread_set_stdin_callback(in_cb, in_ctx);
}
// stdout
static FuriString* mock_out;
FuriThreadStdoutWriteCallback original_out_cb;
static FuriThreadStdoutWriteCallback original_out_cb;
static void* original_out_ctx;
static void mock_out_cb(const char* data, size_t size, void* context) {
furi_check(context == CONTEXT_MAGIC);
@@ -83,7 +86,7 @@ static void assert_and_clear_mock_out(const char* expected) {
// return the original stdout callback for the duration of the check
// if the check fails, we don't want the error to end up in our buffer,
// we want to be able to see it!
furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC);
furi_thread_set_stdout_callback(original_out_cb, original_out_ctx);
mu_assert_string_eq(expected, furi_string_get_cstr(mock_out));
furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC);
@@ -91,7 +94,7 @@ static void assert_and_clear_mock_out(const char* expected) {
}
void test_stdout(void) {
original_out_cb = furi_thread_get_stdout_callback();
furi_thread_get_stdout_callback(&original_out_cb, &original_out_ctx);
furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC);
mock_out = furi_string_alloc();
@@ -104,5 +107,5 @@ void test_stdout(void) {
assert_and_clear_mock_out("Hello!");
furi_string_free(mock_out);
furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC);
furi_thread_set_stdout_callback(original_out_cb, original_out_ctx);
}
@@ -22,7 +22,7 @@
#include <one_wire/maxim_crc.h>
#include <one_wire/one_wire_host.h>
#include <furi_hal_power.h>
#include <power/power_service/power.h>
#define UPDATE_PERIOD_MS 1000UL
#define TEXT_STORE_SIZE 64U
@@ -76,6 +76,7 @@ typedef struct {
FuriThread* reader_thread;
FuriMessageQueue* event_queue;
OneWireHost* onewire;
Power* power;
float temp_celsius;
bool has_device;
} ExampleThermoContext;
@@ -273,7 +274,7 @@ static void example_thermo_input_callback(InputEvent* event, void* ctx) {
/* Starts the reader thread and handles the input */
static void example_thermo_run(ExampleThermoContext* context) {
/* Enable power on external pins */
furi_hal_power_enable_otg();
power_enable_otg(context->power, true);
/* Configure the hardware in host mode */
onewire_host_start(context->onewire);
@@ -309,7 +310,7 @@ static void example_thermo_run(ExampleThermoContext* context) {
onewire_host_stop(context->onewire);
/* Disable power on external pins */
furi_hal_power_disable_otg();
power_enable_otg(context->power, false);
}
/******************** Initialisation & startup *****************************/
@@ -334,6 +335,8 @@ static ExampleThermoContext* example_thermo_context_alloc(void) {
context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN);
context->power = furi_record_open(RECORD_POWER);
return context;
}
@@ -348,6 +351,7 @@ static void example_thermo_context_free(ExampleThermoContext* context) {
view_port_free(context->view_port);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_POWER);
}
/* The application's entry point. Execution starts from here. */
+3
View File
@@ -30,6 +30,8 @@ GpioApp* gpio_app_alloc(void) {
app->gui = furi_record_open(RECORD_GUI);
app->gpio_items = gpio_items_alloc();
app->power = furi_record_open(RECORD_POWER);
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
@@ -114,6 +116,7 @@ void gpio_app_free(GpioApp* app) {
// Close records
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_POWER);
expansion_enable(app->expansion);
furi_record_close(RECORD_EXPANSION);
+2
View File
@@ -5,6 +5,7 @@
#include "scenes/gpio_scene.h"
#include "gpio_custom_event.h"
#include "usb_uart_bridge.h"
#include <power/power_service/power.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
@@ -29,6 +30,7 @@ struct GpioApp {
SceneManager* scene_manager;
Widget* widget;
DialogEx* dialog;
Power* power;
VariableItemList* var_item_list;
VariableItem* var_item_flow;
@@ -69,9 +69,7 @@ void gpio_scene_start_on_enter(void* context) {
GpioOtgSettingsNum,
gpio_scene_start_var_list_change_callback,
app);
if(furi_hal_power_is_charging()) {
variable_item_set_locked(item, true, "Unplug USB!");
} else if(furi_hal_power_is_otg_enabled()) {
if(power_is_otg_enabled(app->power)) {
variable_item_set_current_value_index(item, GpioOtgOn);
variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOn]);
} else {
@@ -94,9 +92,9 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GpioStartEventOtgOn) {
if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg();
power_enable_otg(app->power, true);
} else if(event.event == GpioStartEventOtgOff) {
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
power_enable_otg(app->power, false);
} else if(event.event == GpioStartEventManualControl) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest);
scene_manager_next_scene(app->scene_manager, GpioSceneTest);
+6 -6
View File
@@ -2,7 +2,7 @@
#include "infrared_settings.h"
#include <furi_hal_power.h>
#include <power/power_service/power.h>
#include <string.h>
#include <toolbox/path.h>
@@ -498,12 +498,12 @@ void infrared_set_tx_pin(InfraredApp* infrared, FuriHalInfraredTxPin tx_pin) {
}
void infrared_enable_otg(InfraredApp* infrared, bool enable) {
if(enable) {
if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg();
} else {
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
}
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, enable);
infrared->app_state.is_otg_enabled = enable;
furi_record_close(RECORD_POWER);
}
static void infrared_load_settings(InfraredApp* infrared) {
@@ -13,10 +13,12 @@ typedef union {
} InfraredSceneState;
#pragma pack(pop)
void infrared_scene_universal_common_item_callback(void* context, uint32_t index) {
void infrared_scene_universal_common_item_callback(void* context, uint32_t index, InputType type) {
InfraredApp* infrared = context;
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index);
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
if(type == InputTypeShort) {
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index);
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
}
}
static void infrared_scene_universal_common_progress_input_callback(
@@ -5,4 +5,4 @@
void infrared_scene_universal_common_on_enter(void* context);
bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event);
void infrared_scene_universal_common_on_exit(void* context);
void infrared_scene_universal_common_item_callback(void* context, uint32_t index);
void infrared_scene_universal_common_item_callback(void* context, uint32_t index, InputType type);
@@ -107,7 +107,11 @@ void infrared_progress_view_set_paused(InfraredProgressView* instance, bool is_p
bool infrared_progress_view_input_callback(InputEvent* event, void* context) {
InfraredProgressView* instance = context;
if(event->type != InputTypeShort && event->type != InputTypeRepeat) return false;
if(event->type == InputTypePress || event->type == InputTypeRelease) {
return false;
}
if(!instance->input_callback) return false;
with_view_model(
+7 -2
View File
@@ -1,6 +1,8 @@
#include <furi.h>
#include <furi_hal.h>
#include <power/power_service/power.h>
#include <cli/cli.h>
#include <toolbox/args.h>
@@ -29,13 +31,14 @@ static void onewire_cli_print_usage(void) {
static void onewire_cli_search(Cli* cli) {
UNUSED(cli);
OneWireHost* onewire = onewire_host_alloc(&gpio_ibutton);
Power* power = furi_record_open(RECORD_POWER);
uint8_t address[8];
bool done = false;
printf("Search started\r\n");
onewire_host_start(onewire);
if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg();
power_enable_otg(power, true);
while(!done) {
if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) {
@@ -52,8 +55,10 @@ static void onewire_cli_search(Cli* cli) {
furi_delay_ms(100);
}
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
power_enable_otg(power, false);
onewire_host_free(onewire);
furi_record_close(RECORD_POWER);
}
void onewire_cli(Cli* cli, FuriString* args, void* context) {
@@ -5,21 +5,22 @@
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
#include <lib/subghz/blocks/custom_btn.h>
#include <power/power_service/power.h>
#define TAG "SubGhzTxRx"
static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) {
UNUSED(instance);
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
//CC1101 power-up time
furi_delay_ms(10);
}
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, true);
furi_record_close(RECORD_POWER);
}
static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) {
UNUSED(instance);
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, false);
furi_record_close(RECORD_POWER);
}
SubGhzTxRx* subghz_txrx_alloc(void) {
+6 -13
View File
@@ -35,22 +35,15 @@
#define TAG "SubGhzCli"
static void subghz_cli_radio_device_power_on(void) {
uint8_t attempts = 5;
while(--attempts > 0) {
if(furi_hal_power_enable_otg()) break;
}
if(attempts == 0) {
if(furi_hal_power_get_usb_voltage() < 4.5f) {
FURI_LOG_E(
"TAG",
"Error power otg enable. BQ2589 check otg fault = %d",
furi_hal_power_check_otg_fault() ? 1 : 0);
}
}
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, true);
furi_record_close(RECORD_POWER);
}
static void subghz_cli_radio_device_power_off(void) {
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, false);
furi_record_close(RECORD_POWER);
}
static SubGhzEnvironment* subghz_cli_environment_init(void) {
+2
View File
@@ -35,6 +35,8 @@
#include "rpc/rpc_app.h"
#include <power/power_service/power.h>
#include "helpers/subghz_threshold_rssi.h"
#include "helpers/subghz_txrx.h"
@@ -1,6 +1,8 @@
#include "expansion_worker.h"
#include <power/power_service/power.h>
#include <furi_hal_power.h>
#include <furi_hal_serial.h>
#include <furi_hal_serial_control.h>
@@ -251,9 +253,13 @@ static bool expansion_worker_handle_state_connected(
if(!expansion_worker_rpc_session_open(instance)) break;
instance->state = ExpansionWorkerStateRpcActive;
} else if(command == ExpansionFrameControlCommandEnableOtg) {
if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, true);
furi_record_close(RECORD_POWER);
} else if(command == ExpansionFrameControlCommandDisableOtg) {
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, false);
furi_record_close(RECORD_POWER);
} else {
break;
}
@@ -2,6 +2,7 @@
#include <gui/canvas.h>
#include <gui/elements.h>
#include <input/input.h>
#include <furi.h>
#include <furi_hal_resources.h>
@@ -46,6 +47,7 @@ ARRAY_DEF(ButtonMatrix, ButtonArray_t);
struct ButtonPanel {
View* view;
bool freeze_input;
};
typedef struct {
@@ -63,7 +65,7 @@ static void button_panel_process_up(ButtonPanel* button_panel);
static void button_panel_process_down(ButtonPanel* button_panel);
static void button_panel_process_left(ButtonPanel* button_panel);
static void button_panel_process_right(ButtonPanel* button_panel);
static void button_panel_process_ok(ButtonPanel* button_panel);
static void button_panel_process_ok(ButtonPanel* button_panel, InputType input);
static void button_panel_view_draw_callback(Canvas* canvas, void* _model);
static bool button_panel_view_input_callback(InputEvent* event, void* context);
@@ -347,7 +349,7 @@ static void button_panel_process_right(ButtonPanel* button_panel) {
true);
}
void button_panel_process_ok(ButtonPanel* button_panel) {
void button_panel_process_ok(ButtonPanel* button_panel, InputType type) {
ButtonItem* button_item = NULL;
with_view_model(
@@ -360,7 +362,7 @@ void button_panel_process_ok(ButtonPanel* button_panel) {
true);
if(button_item && button_item->callback) {
button_item->callback(button_item->callback_context, button_item->index);
button_item->callback(button_item->callback_context, button_item->index, type);
}
}
@@ -368,8 +370,15 @@ static bool button_panel_view_input_callback(InputEvent* event, void* context) {
ButtonPanel* button_panel = context;
furi_assert(button_panel);
bool consumed = false;
if(event->type == InputTypeShort) {
if(event->key == InputKeyOk) {
if((event->type == InputTypePress) || (event->type == InputTypeRelease)) {
button_panel->freeze_input = (event->type == InputTypePress);
}
consumed = true;
button_panel_process_ok(button_panel, event->type);
}
if(!button_panel->freeze_input &&
(!(event->type == InputTypePress) && !(event->type == InputTypeRelease))) {
switch(event->key) {
case InputKeyUp:
consumed = true;
@@ -387,10 +396,6 @@ static bool button_panel_view_input_callback(InputEvent* event, void* context) {
consumed = true;
button_panel_process_right(button_panel);
break;
case InputKeyOk:
consumed = true;
button_panel_process_ok(button_panel);
break;
default:
break;
}
@@ -15,7 +15,7 @@ extern "C" {
typedef struct ButtonPanel ButtonPanel;
/** Callback type to call for handling selecting button_panel items */
typedef void (*ButtonItemCallback)(void* context, uint32_t index);
typedef void (*ButtonItemCallback)(void* context, uint32_t index, InputType type);
/** Allocate new button_panel module.
*
+22 -15
View File
@@ -119,7 +119,7 @@ static void widget_add_element(Widget* widget, WidgetElement* element) {
true);
}
WidgetElement* widget_add_string_multiline_element(
void widget_add_string_multiline_element(
Widget* widget,
uint8_t x,
uint8_t y,
@@ -131,10 +131,9 @@ WidgetElement* widget_add_string_multiline_element(
WidgetElement* string_multiline_element =
widget_element_string_multiline_create(x, y, horizontal, vertical, font, text);
widget_add_element(widget, string_multiline_element);
return string_multiline_element;
}
WidgetElement* widget_add_string_element(
void widget_add_string_element(
Widget* widget,
uint8_t x,
uint8_t y,
@@ -146,7 +145,6 @@ WidgetElement* widget_add_string_element(
WidgetElement* string_element =
widget_element_string_create(x, y, horizontal, vertical, font, text);
widget_add_element(widget, string_element);
return string_element;
}
WidgetElement* widget_add_text_box_element(
@@ -166,7 +164,7 @@ WidgetElement* widget_add_text_box_element(
return text_box_element;
}
WidgetElement* widget_add_text_scroll_element(
void widget_add_text_scroll_element(
Widget* widget,
uint8_t x,
uint8_t y,
@@ -177,10 +175,9 @@ WidgetElement* widget_add_text_scroll_element(
WidgetElement* text_scroll_element =
widget_element_text_scroll_create(x, y, width, height, text);
widget_add_element(widget, text_scroll_element);
return text_scroll_element;
}
WidgetElement* widget_add_button_element(
void widget_add_button_element(
Widget* widget,
GuiButtonType button_type,
const char* text,
@@ -190,26 +187,36 @@ WidgetElement* widget_add_button_element(
WidgetElement* button_element =
widget_element_button_create(button_type, text, callback, context);
widget_add_element(widget, button_element);
return button_element;
}
WidgetElement* widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon) {
void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon) {
furi_check(widget);
furi_check(icon);
WidgetElement* icon_element = widget_element_icon_create(x, y, icon);
widget_add_element(widget, icon_element);
return icon_element;
}
WidgetElement* widget_add_frame_element(
void widget_add_rect_element(
Widget* widget,
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t height,
uint8_t radius) {
uint8_t radius,
bool fill) {
furi_check(widget);
WidgetElement* frame_element = widget_element_frame_create(x, y, width, height, radius);
widget_add_element(widget, frame_element);
return frame_element;
WidgetElement* rect_element = widget_element_rect_create(x, y, width, height, radius, fill);
widget_add_element(widget, rect_element);
}
void widget_add_circle_element(Widget* widget, uint8_t x, uint8_t y, uint8_t radius, bool fill) {
furi_check(widget);
WidgetElement* circle_element = widget_element_circle_create(x, y, radius, fill);
widget_add_element(widget, circle_element);
}
void widget_add_line_element(Widget* widget, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
furi_check(widget);
WidgetElement* line_element = widget_element_line_create(x1, y1, x2, y2);
widget_add_element(widget, line_element);
}
+46 -10
View File
@@ -51,7 +51,7 @@ View* widget_get_view(Widget* widget);
* @param font Font instance
* @param[in] text The text
*/
WidgetElement* widget_add_string_multiline_element(
void widget_add_string_multiline_element(
Widget* widget,
uint8_t x,
uint8_t y,
@@ -70,7 +70,7 @@ WidgetElement* widget_add_string_multiline_element(
* @param font Font instance
* @param[in] text The text
*/
WidgetElement* widget_add_string_element(
void widget_add_string_element(
Widget* widget,
uint8_t x,
uint8_t y,
@@ -119,7 +119,7 @@ WidgetElement* widget_add_text_box_element(
* "\ecCenter-aligned text" - sets center horizontal align until the next '\n' symbol
* "\erRight-aligned text" - sets right horizontal align until the next '\n' symbol
*/
WidgetElement* widget_add_text_scroll_element(
void widget_add_text_scroll_element(
Widget* widget,
uint8_t x,
uint8_t y,
@@ -135,7 +135,7 @@ WidgetElement* widget_add_text_scroll_element(
* @param callback ButtonCallback instance
* @param context pointer to context
*/
WidgetElement* widget_add_button_element(
void widget_add_button_element(
Widget* widget,
GuiButtonType button_type,
const char* text,
@@ -149,24 +149,60 @@ WidgetElement* widget_add_button_element(
* @param y top left y coordinate
* @param icon Icon instance
*/
WidgetElement* widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon);
void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon);
/** Add Frame Element
*
* @param widget Widget instance
* @param x top left x coordinate
* @param y top left y coordinate
* @param width frame width
* @param height frame height
* @param radius frame radius
*
* @warning deprecated, use widget_add_rect_element instead
*/
#define widget_add_frame_element(widget, x, y, width, height, radius) \
widget_add_rect_element((widget), (x), (y), (width), (height), (radius), false)
/** Add Rect Element
*
* @param widget Widget instance
* @param x top left x coordinate
* @param y top left y coordinate
* @param width frame width
* @param height frame height
* @param radius frame radius
* @param width rect width
* @param height rect height
* @param radius corner radius
* @param fill whether to fill the box or not
*/
WidgetElement* widget_add_frame_element(
void widget_add_rect_element(
Widget* widget,
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t height,
uint8_t radius);
uint8_t radius,
bool fill);
/** Add Circle Element
*
* @param widget Widget instance
* @param x center x coordinate
* @param y center y coordinate
* @param radius circle radius
* @param fill whether to fill the circle or not
*/
void widget_add_circle_element(Widget* widget, uint8_t x, uint8_t y, uint8_t radius, bool fill);
/** Add Line Element
*
* @param widget Widget instance
* @param x1 first x coordinate
* @param y1 first y coordinate
* @param x2 second x coordinate
* @param y2 second y coordinate
*/
void widget_add_line_element(Widget* widget, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
#ifdef __cplusplus
}
@@ -5,6 +5,8 @@
#pragma once
#include <input/input.h>
#ifdef __cplusplus
extern "C" {
#endif
@@ -0,0 +1,45 @@
#include "widget_element_i.h"
typedef struct {
uint8_t x;
uint8_t y;
uint8_t radius;
bool fill;
} GuiCircleModel;
static void gui_circle_draw(Canvas* canvas, WidgetElement* element) {
furi_assert(canvas);
furi_assert(element);
GuiCircleModel* model = element->model;
if(model->fill) {
canvas_draw_disc(canvas, model->x, model->y, model->radius);
} else {
canvas_draw_circle(canvas, model->x, model->y, model->radius);
}
}
static void gui_circle_free(WidgetElement* gui_circle) {
furi_assert(gui_circle);
free(gui_circle->model);
free(gui_circle);
}
WidgetElement* widget_element_circle_create(uint8_t x, uint8_t y, uint8_t radius, bool fill) {
// Allocate and init model
GuiCircleModel* model = malloc(sizeof(GuiCircleModel));
model->x = x;
model->y = y;
model->radius = radius;
model->fill = fill;
// Allocate and init Element
WidgetElement* gui_circle = malloc(sizeof(WidgetElement));
gui_circle->parent = NULL;
gui_circle->input = NULL;
gui_circle->draw = gui_circle_draw;
gui_circle->free = gui_circle_free;
gui_circle->model = model;
return gui_circle;
}
@@ -1,48 +0,0 @@
#include "widget_element_i.h"
typedef struct {
uint8_t x;
uint8_t y;
uint8_t width;
uint8_t height;
uint8_t radius;
} GuiFrameModel;
static void gui_frame_draw(Canvas* canvas, WidgetElement* element) {
furi_assert(canvas);
furi_assert(element);
GuiFrameModel* model = element->model;
canvas_draw_rframe(canvas, model->x, model->y, model->width, model->height, model->radius);
}
static void gui_frame_free(WidgetElement* gui_frame) {
furi_assert(gui_frame);
free(gui_frame->model);
free(gui_frame);
}
WidgetElement* widget_element_frame_create(
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t height,
uint8_t radius) {
// Allocate and init model
GuiFrameModel* model = malloc(sizeof(GuiFrameModel));
model->x = x;
model->y = y;
model->width = width;
model->height = height;
model->radius = radius;
// Allocate and init Element
WidgetElement* gui_frame = malloc(sizeof(WidgetElement));
gui_frame->parent = NULL;
gui_frame->input = NULL;
gui_frame->draw = gui_frame_draw;
gui_frame->free = gui_frame_free;
gui_frame->model = model;
return gui_frame;
}
@@ -76,14 +76,16 @@ WidgetElement* widget_element_button_create(
/** Create icon element */
WidgetElement* widget_element_icon_create(uint8_t x, uint8_t y, const Icon* icon);
/** Create frame element */
WidgetElement* widget_element_frame_create(
/** Create rect element */
WidgetElement* widget_element_rect_create(
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t height,
uint8_t radius);
uint8_t radius,
bool fill);
/** Create text scroll element */
WidgetElement* widget_element_text_scroll_create(
uint8_t x,
uint8_t y,
@@ -91,6 +93,12 @@ WidgetElement* widget_element_text_scroll_create(
uint8_t height,
const char* text);
/** Create circle element */
WidgetElement* widget_element_circle_create(uint8_t x, uint8_t y, uint8_t radius, bool fill);
/** Create line element */
WidgetElement* widget_element_line_create(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,41 @@
#include "widget_element_i.h"
typedef struct {
uint8_t x1;
uint8_t y1;
uint8_t x2;
uint8_t y2;
} GuiLineModel;
static void gui_line_draw(Canvas* canvas, WidgetElement* element) {
furi_assert(canvas);
furi_assert(element);
GuiLineModel* model = element->model;
canvas_draw_line(canvas, model->x1, model->y1, model->x2, model->y2);
}
static void gui_line_free(WidgetElement* gui_line) {
furi_assert(gui_line);
free(gui_line->model);
free(gui_line);
}
WidgetElement* widget_element_line_create(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
// Allocate and init model
GuiLineModel* model = malloc(sizeof(GuiLineModel));
model->x1 = x1;
model->y1 = y1;
model->x2 = x2;
model->y2 = y2;
// Allocate and init Element
WidgetElement* gui_line = malloc(sizeof(WidgetElement));
gui_line->parent = NULL;
gui_line->input = NULL;
gui_line->draw = gui_line_draw;
gui_line->free = gui_line_free;
gui_line->model = model;
return gui_line;
}
@@ -0,0 +1,55 @@
#include "widget_element_i.h"
typedef struct {
uint8_t x;
uint8_t y;
uint8_t width;
uint8_t height;
uint8_t radius;
bool fill;
} GuiRectModel;
static void gui_rect_draw(Canvas* canvas, WidgetElement* element) {
furi_assert(canvas);
furi_assert(element);
GuiRectModel* model = element->model;
if(model->fill) {
canvas_draw_rbox(canvas, model->x, model->y, model->width, model->height, model->radius);
} else {
canvas_draw_rframe(canvas, model->x, model->y, model->width, model->height, model->radius);
}
}
static void gui_rect_free(WidgetElement* gui_rect) {
furi_assert(gui_rect);
free(gui_rect->model);
free(gui_rect);
}
WidgetElement* widget_element_rect_create(
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t height,
uint8_t radius,
bool fill) {
// Allocate and init model
GuiRectModel* model = malloc(sizeof(GuiRectModel));
model->x = x;
model->y = y;
model->width = width;
model->height = height;
model->radius = radius;
model->fill = fill;
// Allocate and init Element
WidgetElement* gui_rect = malloc(sizeof(WidgetElement));
gui_rect->parent = NULL;
gui_rect->input = NULL;
gui_rect->draw = gui_rect_draw;
gui_rect->free = gui_rect_free;
gui_rect->model = model;
return gui_rect;
}
+5 -2
View File
@@ -30,13 +30,16 @@ void power_cli_reboot2dfu(Cli* cli, FuriString* args) {
void power_cli_5v(Cli* cli, FuriString* args) {
UNUSED(cli);
Power* power = furi_record_open(RECORD_POWER);
if(!furi_string_cmp(args, "0")) {
furi_hal_power_disable_otg();
power_enable_otg(power, false);
} else if(!furi_string_cmp(args, "1")) {
furi_hal_power_enable_otg();
power_enable_otg(power, true);
} else {
cli_print_usage("power_otg", "<1|0>", furi_string_get_cstr(args));
}
furi_record_close(RECORD_POWER);
}
void power_cli_3v3(Cli* cli, FuriString* args) {
@@ -258,6 +258,7 @@ static bool power_update_info(Power* power) {
.is_charging = furi_hal_power_is_charging(),
.gauge_is_ok = furi_hal_power_gauge_is_ok(),
.is_shutdown_requested = furi_hal_power_is_shutdown_requested(),
.is_otg_enabled = furi_hal_power_is_otg_enabled(),
.charge = furi_hal_power_get_pct(),
.health = furi_hal_power_get_bat_health_pct(),
.capacity_remaining = furi_hal_power_get_battery_remaining_capacity(),
@@ -520,6 +521,30 @@ static void power_message_callback(FuriEventLoopObject* object, void* context) {
case PowerMessageTypeShowBatteryLowWarning:
power->show_battery_low_warning = *msg.bool_param;
break;
case PowerMessageTypeSwitchOTG:
power->is_otg_requested = *msg.bool_param;
if(power->is_otg_requested) {
// Only try to enable if VBUS voltage is low, otherwise charger will refuse
if(power->info.voltage_vbus < 4.5f) {
size_t retries = 5;
while(retries-- > 0) {
if(furi_hal_power_enable_otg()) {
break;
}
}
if(!retries) {
FURI_LOG_W(TAG, "Failed to enable OTG, will try later");
}
} else {
FURI_LOG_W(
TAG,
"Postponing OTG enable: VBUS(%0.1f) >= 4.5v",
(double)power->info.voltage_vbus);
}
} else {
furi_hal_power_disable_otg();
}
break;
case PowerMessageTypeGetSettings:
furi_assert(msg.lock);
*msg.settings = power->settings;
@@ -563,9 +588,18 @@ static void power_tick_callback(void* context) {
if(need_refresh) {
view_port_update(power->battery_view_port);
}
// Check OTG status and disable it in case of fault
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_check_otg_status();
// Check OTG status, disable in case of a fault
if(furi_hal_power_check_otg_fault()) {
FURI_LOG_E(TAG, "OTG fault detected, disabling OTG");
furi_hal_power_disable_otg();
power->is_otg_requested = false;
}
// Change OTG state if needed (i.e. after disconnecting USB power)
if(power->is_otg_requested &&
(!power->info.is_otg_enabled && power->info.voltage_vbus < 4.5f)) {
FURI_LOG_D(TAG, "OTG requested but not enabled, enabling OTG");
furi_hal_power_enable_otg();
}
}
@@ -39,6 +39,7 @@ typedef struct {
bool gauge_is_ok;
bool is_charging;
bool is_shutdown_requested;
bool is_otg_enabled;
float current_charger;
float current_gauge;
@@ -96,6 +97,19 @@ bool power_is_battery_healthy(Power* power);
*/
void power_enable_low_battery_level_notification(Power* power, bool enable);
/** Enable or disable OTG
*
* @param power Power instance
* @param enable true - enable, false - disable
*/
void power_enable_otg(Power* power, bool enable);
/** Check OTG status
*
* @return true if OTG is requested
*/
bool power_is_otg_enabled(Power* power);
#ifdef __cplusplus
}
#endif
@@ -71,6 +71,25 @@ void power_enable_low_battery_level_notification(Power* power, bool enable) {
furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);
}
void power_enable_otg(Power* power, bool enable) {
furi_check(power);
PowerMessage msg = {
.type = PowerMessageTypeSwitchOTG,
.bool_param = &enable,
.lock = api_lock_alloc_locked(),
};
furi_check(
furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);
api_lock_wait_unlock_and_free(msg.lock);
}
bool power_is_otg_enabled(Power* power) {
furi_check(power);
return power->is_otg_requested;
}
/*
* Private API for the Settings app
*/
@@ -35,6 +35,7 @@ struct Power {
bool battery_low;
bool show_battery_low_warning;
bool is_otg_requested;
uint8_t battery_level;
uint8_t power_off_timeout;
@@ -59,6 +60,7 @@ typedef enum {
PowerMessageTypeGetInfo,
PowerMessageTypeIsBatteryHealthy,
PowerMessageTypeShowBatteryLowWarning,
PowerMessageTypeSwitchOTG,
PowerMessageTypeGetSettings,
PowerMessageTypeSetSettings,
+7 -2
View File
@@ -4,6 +4,7 @@
#include <furi_hal_gpio.h>
#include <furi_hal_power.h>
#include <furi_hal_resources.h>
#include <power/power_service/power.h>
static const GpioPin* rpc_pin_to_hal_pin(PB_Gpio_GpioPin rpc_pin) {
switch(rpc_pin) {
@@ -218,12 +219,16 @@ void rpc_system_gpio_set_otg_mode(const PB_Main* request, void* context) {
const PB_Gpio_GpioOtgMode mode = request->content.gpio_set_otg_mode.mode;
Power* power = furi_record_open(RECORD_POWER);
if(mode == PB_Gpio_GpioOtgMode_OFF) {
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
power_enable_otg(power, false);
} else {
if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg();
power_enable_otg(power, true);
}
furi_record_close(RECORD_POWER);
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
}
@@ -124,7 +124,7 @@ static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) {
bool consumed = false;
bool rate_changed = false;
if(event->type != InputTypeShort && event->type != InputTypeRepeat) {
if(event->type == InputTypePress || event->type == InputTypeRelease) {
return false;
}
@@ -19,8 +19,8 @@ let jsLogo = icon.getBuiltin("js_script_10px");
let stopwatchWidgetElements = [
{ element: "string", x: 67, y: 44, align: "bl", font: "big_numbers", text: "00 00" },
{ element: "string", x: 77, y: 22, align: "bl", font: "primary", text: "Stopwatch" },
{ element: "frame", x: 64, y: 27, w: 28, h: 20, radius: 3 },
{ element: "frame", x: 100, y: 27, w: 28, h: 20, radius: 3 },
{ element: "rect", x: 64, y: 27, w: 28, h: 20, radius: 3, fill: false },
{ element: "rect", x: 100, y: 27, w: 28, h: 20, radius: 3, fill: false },
{ element: "icon", x: 0, y: 5, iconData: cuteDolphinWithWatch },
{ element: "icon", x: 64, y: 13, iconData: jsLogo },
{ element: "button", button: "right", text: "Back" },
@@ -0,0 +1,15 @@
// This script is like uart_echo, except it uses 8E1 framing (8 data bits, even
// parity, 1 stop bit) as opposed to the default 8N1 (8 data bits, no parity,
// 1 stop bit)
let serial = require("serial");
serial.setup("usart", 230400, { dataBits: "8", parity: "even", stopBits: "1" });
while (1) {
let rx_data = serial.readBytes(1, 1000);
if (rx_data !== undefined) {
serial.write(rx_data);
let data_view = Uint8Array(rx_data);
print("0x" + data_view[0].toString(16));
}
}
@@ -1,70 +0,0 @@
// Script cannot work without widget module so check before
checkSdkFeatures(["widget"]);
let widget = require("widget");
let demo_seconds = 30;
print("Loading file", __filename);
print("From directory", __dirname);
// addText supports "Primary" and "Secondary" font sizes.
widget.addText(10, 10, "Primary", "Example JS widget");
widget.addText(10, 20, "Secondary", "Example widget from JS!");
// load a Xbm file from the same directory as this script.
widget.addText(0, 30, "Secondary", __filename);
let logo = widget.loadImageXbm(__dirname + "/widget-js.fxbm");
// add a line (x1, y1, x2, y2)
widget.addLine(10, 35, 120, 35);
// add a circle/disc (x, y, radius)
widget.addCircle(12, 52, 10);
widget.addDisc(12, 52, 5);
// add a frame/box (x, y, width, height)
widget.addFrame(30, 45, 10, 10);
widget.addBox(32, 47, 6, 6);
// add a rounded frame/box (x, y, width, height, radius)
widget.addRframe(50, 45, 15, 15, 3);
widget.addRbox(53, 48, 6, 6, 2);
// add a dot (x, y)
widget.addDot(100, 45);
widget.addDot(102, 44);
widget.addDot(104, 43);
// add an icon (x, y, icon)
// not available in all firmwares, but not essential for this script's
// functionality, so we just check at runtime and use it if it is available
if (doesSdkSupport(["widget-addicon"])) {
widget.addIcon(100, 50, "ButtonUp_7x4");
widget.addIcon(100, 55, "ButtonDown_7x4");
}
// add a glyph (x, y, glyph)
widget.addGlyph(115, 50, "#".charCodeAt(0));
// Show the widget (drawing the layers in the orderer they were added)
widget.show();
let i = 1;
let bitmap = undefined;
while (widget.isOpen() && i <= demo_seconds) {
// Print statements will only show up once the widget is closed.
print("count is at", i++);
// You can call remove on any added item, it does not impact the other ids.
if (bitmap) { widget.remove(bitmap); bitmap = undefined; }
// All of the addXXX functions return an id that can be used to remove the item.
else { bitmap = widget.addXbm(77, 45, logo); }
delay(1000);
}
// If user did not press the back button, close the widget.
if (widget.isOpen()) {
widget.close();
}
+2 -2
View File
@@ -269,6 +269,8 @@ static const char* extra_features[] = {
"baseline", // dummy "feature"
"gpio-pwm",
"gui-widget",
"serial-framing",
"gui-widget-extras",
// extra modules
"blebeacon",
@@ -277,13 +279,11 @@ static const char* extra_features[] = {
"subghz",
"usbdisk",
"vgm",
"widget",
// extra features
"gui-textinput-illegalsymbols",
"storage-virtual",
"usbdisk-createimage",
"widget-addicon",
};
/**
+77 -35
View File
@@ -12,7 +12,7 @@
#define JS_SDK_VENDOR_FIRMWARE "momentum"
#define JS_SDK_VENDOR "flipperdevices"
#define JS_SDK_MAJOR 0
#define JS_SDK_MINOR 2
#define JS_SDK_MINOR 3
/**
* @brief Returns the foreign pointer in `obj["_"]`
@@ -82,6 +82,11 @@ typedef enum {
*/
#define JS_AT_LEAST >=
typedef struct {
const char* name;
size_t value;
} JsEnumMapping;
#define JS_ENUM_MAP(var_name, ...) \
static const JsEnumMapping var_name##_mapping[] = { \
{NULL, sizeof(var_name)}, \
@@ -91,8 +96,14 @@ typedef enum {
typedef struct {
const char* name;
size_t value;
} JsEnumMapping;
size_t offset;
} JsObjectMapping;
#define JS_OBJ_MAP(var_name, ...) \
static const JsObjectMapping var_name##_mapping[] = { \
__VA_ARGS__, \
{NULL, 0}, \
};
typedef struct {
void* out;
@@ -200,6 +211,54 @@ static inline void
_js_validate_enum, \
var_name##_mapping})
static inline bool _js_validate_object(struct mjs* mjs, mjs_val_t val, const void* extra) {
for(const JsObjectMapping* mapping = (JsObjectMapping*)extra; mapping->name; mapping++)
if(mjs_get(mjs, val, mapping->name, ~0) == MJS_UNDEFINED) return false;
return true;
}
static inline void
_js_convert_object(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) {
const JsObjectMapping* mapping = (JsObjectMapping*)extra;
for(; mapping->name; mapping++) {
mjs_val_t field_val = mjs_get(mjs, *val, mapping->name, ~0);
*(mjs_val_t*)((uint8_t*)out + mapping->offset) = field_val;
}
}
#define JS_ARG_OBJECT(var_name, name) \
((_js_arg_decl){ \
&var_name, \
mjs_is_object, \
_js_convert_object, \
name " object", \
_js_validate_object, \
var_name##_mapping})
/**
* @brief Validates and converts a JS value with a declarative interface
*
* Example: `int32_t my_value; JS_CONVERT_OR_RETURN(mjs, &mjs_val, JS_ARG_INT32(&my_value), "value source");`
*
* @warning This macro executes `return;` by design in case of a validation failure
*/
#define JS_CONVERT_OR_RETURN(mjs, value, decl, source, ...) \
if(decl.validator) \
if(!decl.validator(*value)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
source ": expected %s", \
##__VA_ARGS__, \
decl.expected_type); \
if(decl.extended_validator) \
if(!decl.extended_validator(mjs, *value, decl.extra_data)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
source ": expected %s", \
##__VA_ARGS__, \
decl.expected_type); \
decl.converter(mjs, value, decl.out, decl.extra_data);
//-V:JS_FETCH_ARGS_OR_RETURN:1008
/**
* @brief Fetches and validates the arguments passed to a JS function
@@ -209,38 +268,21 @@ static inline void
* @warning This macro executes `return;` by design in case of an argument count
* mismatch or a validation failure
*/
#define JS_FETCH_ARGS_OR_RETURN(mjs, arg_operator, ...) \
_js_arg_decl _js_args[] = {__VA_ARGS__}; \
int _js_arg_cnt = COUNT_OF(_js_args); \
mjs_val_t _js_arg_vals[_js_arg_cnt]; \
if(!(mjs_nargs(mjs) arg_operator _js_arg_cnt)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
"expected %s%d arguments, got %d", \
#arg_operator, \
_js_arg_cnt, \
mjs_nargs(mjs)); \
for(int _i = 0; _i < _js_arg_cnt; _i++) { \
_js_arg_vals[_i] = mjs_arg(mjs, _i); \
if(_js_args[_i].validator) \
if(!_js_args[_i].validator(_js_arg_vals[_i])) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
"argument %d: expected %s", \
_i, \
_js_args[_i].expected_type); \
if(_js_args[_i].extended_validator) \
if(!_js_args[_i].extended_validator(mjs, _js_arg_vals[_i], _js_args[_i].extra_data)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
"argument %d: expected %s", \
_i, \
_js_args[_i].expected_type); \
_js_args[_i].converter( \
mjs, &_js_arg_vals[_i], _js_args[_i].out, _js_args[_i].extra_data); \
#define JS_FETCH_ARGS_OR_RETURN(mjs, arg_operator, ...) \
_js_arg_decl _js_args[] = {__VA_ARGS__}; \
int _js_arg_cnt = COUNT_OF(_js_args); \
mjs_val_t _js_arg_vals[_js_arg_cnt]; \
if(!(mjs_nargs(mjs) arg_operator _js_arg_cnt)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
"expected %s%d arguments, got %d", \
#arg_operator, \
_js_arg_cnt, \
mjs_nargs(mjs)); \
for(int _i = 0; _i < _js_arg_cnt; _i++) { \
_js_arg_vals[_i] = mjs_arg(mjs, _i); \
JS_CONVERT_OR_RETURN(mjs, &_js_arg_vals[_i], _js_args[_i], "argument %d", _i); \
}
/**
+1 -1
View File
@@ -308,7 +308,7 @@ static void js_gpio_is_pwm_running(struct mjs* mjs) {
*/
static void js_gpio_pwm_stop(struct mjs* mjs) {
JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs);
if(manager_data->pwm_output != FuriHalPwmOutputIdNone) {
if(manager_data->pwm_output == FuriHalPwmOutputIdNone) {
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "PWM is not supported on this pin");
}
@@ -1,5 +1,28 @@
#include "../../js_modules.h"
#include <assets_icons.h>
#include <core/dangerous_defines.h>
#include <gui/icon_i.h>
#include <m-list.h>
// Firmware's Icon struct needs a frames array, and uses a small CompressHeader
// Here we use a variable size allocation to add the uncompressed data in same allocation
// Also use a one-long array pointing to later in the same struct as the frames array
// CompressHeader includes a first is_compressed byte so we don't need to compress (.fxbm is uncompressed)
typedef struct FURI_PACKED {
Icon icon;
uint8_t* frames[1];
struct {
uint8_t is_compressed;
uint8_t uncompressed_data[];
} frame;
} FxbmIconWrapper;
LIST_DEF(FxbmIconWrapperList, FxbmIconWrapper*, M_PTR_OPLIST); // NOLINT
#define M_OPL_FxbmIconWrapperList_t() LIST_OPLIST(FxbmIconWrapperList)
typedef struct {
FxbmIconWrapperList_t fxbm_list;
} JsGuiIconInst;
static void js_gui_icon_get_builtin(struct mjs* mjs) {
const char* icon_name;
@@ -18,17 +41,78 @@ static void js_gui_icon_get_builtin(struct mjs* mjs) {
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "no such built-in icon");
}
static void js_gui_icon_load_fxbm(struct mjs* mjs) {
const char* fxbm_path;
JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&fxbm_path));
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
FxbmIconWrapper* fxbm = NULL;
do {
if(!storage_file_open(file, fxbm_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
break;
}
struct {
uint32_t size; // Total following size including width and height values
uint32_t width;
uint32_t height;
} fxbm_header;
if(storage_file_read(file, &fxbm_header, sizeof(fxbm_header)) != sizeof(fxbm_header)) {
break;
}
size_t frame_size = fxbm_header.size - sizeof(uint32_t) * 2;
fxbm = malloc(sizeof(FxbmIconWrapper) + frame_size);
if(storage_file_read(file, fxbm->frame.uncompressed_data, frame_size) != frame_size) {
free(fxbm);
fxbm = NULL;
break;
}
FURI_CONST_ASSIGN(fxbm->icon.width, fxbm_header.width);
FURI_CONST_ASSIGN(fxbm->icon.height, fxbm_header.height);
FURI_CONST_ASSIGN(fxbm->icon.frame_count, 1);
FURI_CONST_ASSIGN(fxbm->icon.frame_rate, 1);
FURI_CONST_ASSIGN_PTR(fxbm->icon.frames, fxbm->frames);
fxbm->frames[0] = (void*)&fxbm->frame;
fxbm->frame.is_compressed = false;
} while(false);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
if(!fxbm) {
JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "could not load .fxbm icon");
}
JsGuiIconInst* js_icon = JS_GET_CONTEXT(mjs);
FxbmIconWrapperList_push_back(js_icon->fxbm_list, fxbm);
mjs_return(mjs, mjs_mk_foreign(mjs, (void*)&fxbm->icon));
}
static void* js_gui_icon_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {
UNUSED(modules);
JsGuiIconInst* js_icon = malloc(sizeof(JsGuiIconInst));
FxbmIconWrapperList_init(js_icon->fxbm_list);
*object = mjs_mk_object(mjs);
JS_ASSIGN_MULTI(mjs, *object) {
JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, js_icon));
JS_FIELD("getBuiltin", MJS_MK_FN(js_gui_icon_get_builtin));
JS_FIELD("loadFxbm", MJS_MK_FN(js_gui_icon_load_fxbm));
}
return NULL;
return js_icon;
}
static void js_gui_icon_destroy(void* inst) {
UNUSED(inst);
JsGuiIconInst* js_icon = inst;
for
M_EACH(fxbm, js_icon->fxbm_list, FxbmIconWrapperList_t) {
free(*fxbm);
}
FxbmIconWrapperList_clear(js_icon->fxbm_list);
free(js_icon);
}
static const JsModuleDescriptor js_gui_icon_desc = {
@@ -202,7 +202,7 @@ static bool js_widget_add_child(
const Icon* icon = mjs_get_ptr(mjs, icon_data_in);
widget_add_icon_element(widget, x, y, icon);
} else if(strcmp(element_type, "frame") == 0) {
} else if(strcmp(element_type, "rect") == 0) {
int32_t x, y, w, h;
DESTRUCTURE_OR_RETURN(mjs, child_obj, position, &x, &y);
DESTRUCTURE_OR_RETURN(mjs, child_obj, size, &w, &h);
@@ -211,7 +211,43 @@ static bool js_widget_add_child(
JS_ERROR_AND_RETURN_VAL(
mjs, MJS_BAD_ARGS_ERROR, false, "failed to fetch element radius");
int32_t radius = mjs_get_int32(mjs, radius_in);
widget_add_frame_element(widget, x, y, w, h, radius);
mjs_val_t fill_in = mjs_get(mjs, child_obj, "fill", ~0);
if(!mjs_is_boolean(fill_in))
JS_ERROR_AND_RETURN_VAL(
mjs, MJS_BAD_ARGS_ERROR, false, "failed to fetch element fill");
int32_t fill = mjs_get_bool(mjs, fill_in);
widget_add_rect_element(widget, x, y, w, h, radius, fill);
} else if(strcmp(element_type, "circle") == 0) {
int32_t x, y;
DESTRUCTURE_OR_RETURN(mjs, child_obj, position, &x, &y);
mjs_val_t radius_in = mjs_get(mjs, child_obj, "radius", ~0);
if(!mjs_is_number(radius_in))
JS_ERROR_AND_RETURN_VAL(
mjs, MJS_BAD_ARGS_ERROR, false, "failed to fetch element radius");
int32_t radius = mjs_get_int32(mjs, radius_in);
mjs_val_t fill_in = mjs_get(mjs, child_obj, "fill", ~0);
if(!mjs_is_boolean(fill_in))
JS_ERROR_AND_RETURN_VAL(
mjs, MJS_BAD_ARGS_ERROR, false, "failed to fetch element fill");
int32_t fill = mjs_get_bool(mjs, fill_in);
widget_add_circle_element(widget, x, y, radius, fill);
} else if(strcmp(element_type, "line") == 0) {
int32_t x1, y1, x2, y2;
mjs_val_t x1_in = mjs_get(mjs, child_obj, "x1", ~0);
mjs_val_t y1_in = mjs_get(mjs, child_obj, "y1", ~0);
mjs_val_t x2_in = mjs_get(mjs, child_obj, "x2", ~0);
mjs_val_t y2_in = mjs_get(mjs, child_obj, "y2", ~0);
if(!mjs_is_number(x1_in) || !mjs_is_number(y1_in) || !mjs_is_number(x2_in) ||
!mjs_is_number(y2_in))
JS_ERROR_AND_RETURN_VAL(
mjs, MJS_BAD_ARGS_ERROR, false, "failed to fetch element positions");
x1 = mjs_get_int32(mjs, x1_in);
y1 = mjs_get_int32(mjs, y1_in);
x2 = mjs_get_int32(mjs, x2_in);
y2 = mjs_get_int32(mjs, y2_in);
widget_add_line_element(widget, x1, y1, x2, y2);
}
return true;
+49 -48
View File
@@ -20,14 +20,6 @@ typedef struct {
char* data;
} PatternArrayItem;
static const struct {
const char* name;
const FuriHalSerialId value;
} serial_channels[] = {
{"usart", FuriHalSerialIdUsart},
{"lpuart", FuriHalSerialIdLpuart},
};
ARRAY_DEF(PatternArray, PatternArrayItem, M_POD_OPLIST);
static void
@@ -43,9 +35,54 @@ static void
}
static void js_serial_setup(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
furi_assert(serial);
FuriHalSerialId serial_id;
int32_t baudrate;
JS_ENUM_MAP(serial_id, {"lpuart", FuriHalSerialIdLpuart}, {"usart", FuriHalSerialIdUsart});
JS_FETCH_ARGS_OR_RETURN(
mjs, JS_AT_LEAST, JS_ARG_ENUM(serial_id, "SerialId"), JS_ARG_INT32(&baudrate));
FuriHalSerialDataBits data_bits = FuriHalSerialDataBits8;
FuriHalSerialParity parity = FuriHalSerialParityNone;
FuriHalSerialStopBits stop_bits = FuriHalSerialStopBits1;
if(mjs_nargs(mjs) > 2) {
struct framing {
mjs_val_t data_bits;
mjs_val_t parity;
mjs_val_t stop_bits;
} framing;
JS_OBJ_MAP(
framing,
{"dataBits", offsetof(struct framing, data_bits)},
{"parity", offsetof(struct framing, parity)},
{"stopBits", offsetof(struct framing, stop_bits)});
JS_ENUM_MAP(
data_bits,
{"6", FuriHalSerialDataBits6},
{"7", FuriHalSerialDataBits7},
{"8", FuriHalSerialDataBits8},
{"9", FuriHalSerialDataBits9});
JS_ENUM_MAP(
parity,
{"none", FuriHalSerialParityNone},
{"even", FuriHalSerialParityEven},
{"odd", FuriHalSerialParityOdd});
JS_ENUM_MAP(
stop_bits,
{"0.5", FuriHalSerialStopBits0_5},
{"1", FuriHalSerialStopBits1},
{"1.5", FuriHalSerialStopBits1_5},
{"2", FuriHalSerialStopBits2});
mjs_val_t framing_obj = mjs_arg(mjs, 2);
JS_CONVERT_OR_RETURN(mjs, &framing_obj, JS_ARG_OBJECT(framing, "Framing"), "argument 2");
JS_CONVERT_OR_RETURN(
mjs, &framing.data_bits, JS_ARG_ENUM(data_bits, "DataBits"), "argument 2: dataBits");
JS_CONVERT_OR_RETURN(
mjs, &framing.parity, JS_ARG_ENUM(parity, "Parity"), "argument 2: parity");
JS_CONVERT_OR_RETURN(
mjs, &framing.stop_bits, JS_ARG_ENUM(stop_bits, "StopBits"), "argument 2: stopBits");
}
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
if(serial->setup_done) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is already configured");
@@ -53,43 +90,6 @@ static void js_serial_setup(struct mjs* mjs) {
return;
}
bool args_correct = false;
FuriHalSerialId serial_id = FuriHalSerialIdMax;
uint32_t baudrate = 0;
do {
if(mjs_nargs(mjs) != 2) break;
mjs_val_t arg = mjs_arg(mjs, 0);
if(!mjs_is_string(arg)) break;
size_t str_len = 0;
const char* arg_str = mjs_get_string(mjs, &arg, &str_len);
for(size_t i = 0; i < COUNT_OF(serial_channels); i++) {
size_t name_len = strlen(serial_channels[i].name);
if(str_len != name_len) continue;
if(strncmp(arg_str, serial_channels[i].name, str_len) == 0) {
serial_id = serial_channels[i].value;
break;
}
}
if(serial_id == FuriHalSerialIdMax) {
break;
}
arg = mjs_arg(mjs, 1);
if(!mjs_is_number(arg)) break;
baudrate = mjs_get_int32(mjs, arg);
args_correct = true;
} while(0);
if(!args_correct) {
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
expansion_disable(furi_record_open(RECORD_EXPANSION));
furi_record_close(RECORD_EXPANSION);
@@ -97,6 +97,7 @@ static void js_serial_setup(struct mjs* mjs) {
if(serial->serial_handle) {
serial->rx_stream = furi_stream_buffer_alloc(RX_BUF_LEN, 1);
furi_hal_serial_init(serial->serial_handle, baudrate);
furi_hal_serial_configure_framing(serial->serial_handle, data_bits, parity, stop_bits);
furi_hal_serial_async_rx_start(
serial->serial_handle, js_serial_on_async_rx, serial, false);
serial->setup_done = true;
@@ -1,990 +0,0 @@
#include <assets_icons.h>
#include <gui/view_holder.h>
#include <m-array.h>
#include <m-list.h>
#include <string.h>
#include "../js_modules.h"
typedef struct WidgetComponent WidgetComponent;
ARRAY_DEF(ComponentArray, WidgetComponent*, M_PTR_OPLIST);
typedef struct XbmImage XbmImage;
LIST_DEF(XbmImageList, XbmImage*, M_POD_OPLIST);
struct WidgetComponent {
void (*draw)(Canvas* canvas, void* model);
void (*free)(WidgetComponent* component);
void* model;
uint32_t id;
};
struct XbmImage {
uint32_t width;
uint32_t height;
uint8_t data[];
};
typedef struct {
uint8_t x;
uint8_t y;
uint8_t w;
uint8_t h;
} BoxElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t r;
} CircleElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t r;
} DiscElement;
typedef struct {
uint8_t x;
uint8_t y;
} DotElement;
typedef struct {
uint8_t x;
uint8_t y;
const Icon* icon;
} IconElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t w;
uint8_t h;
} FrameElement;
typedef struct {
uint8_t x;
uint8_t y;
uint16_t ch;
} GlyphElement;
typedef struct {
uint8_t x1;
uint8_t y1;
uint8_t x2;
uint8_t y2;
} LineElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t w;
uint8_t h;
uint8_t r;
} RboxElement;
typedef struct {
uint8_t x;
uint8_t y;
uint8_t w;
uint8_t h;
uint8_t r;
} RframeElement;
typedef struct {
uint8_t x;
uint8_t y;
Font font;
FuriString* text;
} TextElement;
typedef struct {
uint8_t x;
uint8_t y;
uint32_t index;
View* view;
} XbmElement;
typedef struct {
ComponentArray_t component;
XbmImageList_t image;
uint32_t max_assigned_id;
} WidgetModel;
typedef struct {
View* view;
ViewHolder* view_holder;
bool is_shown;
} JsWidgetInst;
static JsWidgetInst* get_this_ctx(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsWidgetInst* widget = mjs_get_ptr(mjs, obj_inst);
furi_assert(widget);
return widget;
}
static void ret_bad_args(struct mjs* mjs, const char* error) {
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error);
mjs_return(mjs, MJS_UNDEFINED);
}
static bool check_arg_count(struct mjs* mjs, size_t count) {
size_t num_args = mjs_nargs(mjs);
if(num_args != count) {
ret_bad_args(mjs, "Wrong argument count");
return false;
}
return true;
}
static void js_widget_load_image_xbm(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 1)) {
return;
}
mjs_val_t path_arg = mjs_arg(mjs, 0);
size_t path_len = 0;
const char* path = mjs_get_string(mjs, &path_arg, &path_len);
if(!path) {
ret_bad_args(mjs, "Path must be a string");
return;
}
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
XbmImage* xbm = NULL;
do {
if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
ret_bad_args(mjs, "Failed to open file");
break;
}
uint32_t size = 0;
if(storage_file_read(file, &size, sizeof(size)) != sizeof(size)) {
ret_bad_args(mjs, "Failed to get file size");
break;
}
xbm = malloc(size);
if(storage_file_read(file, xbm, size) != size) {
ret_bad_args(mjs, "Failed to load entire file");
free(xbm);
xbm = NULL;
break;
}
} while(false);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
if(xbm == NULL) {
mjs_return(mjs, MJS_UNDEFINED);
return;
}
uint32_t count = 0;
with_view_model(
widget->view,
WidgetModel * model,
{
count = XbmImageList_size(model->image);
XbmImageList_push_back(model->image, xbm);
},
false);
mjs_return(mjs, mjs_mk_number(mjs, count));
}
static void js_widget_remove(struct mjs* mjs) {
bool removed = false;
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 1)) {
return;
}
with_view_model(
widget->view,
WidgetModel * model,
{
uint32_t id = mjs_get_int32(mjs, mjs_arg(mjs, 0));
ComponentArray_it_t it;
ComponentArray_it(it, model->component);
while(!ComponentArray_end_p(it)) {
WidgetComponent* component = *ComponentArray_ref(it);
if(component->id == id) {
if(component->free) {
component->free(component);
}
ComponentArray_remove(model->component, it);
removed = true;
break;
}
ComponentArray_next(it);
}
},
true);
mjs_return(mjs, mjs_mk_boolean(mjs, removed));
}
static void widget_box_draw(Canvas* canvas, void* model) {
BoxElement* element = model;
canvas_draw_box(canvas, element->x, element->y, element->w, element->h);
}
static void widget_box_free(WidgetComponent* component) {
BoxElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_box(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 4)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t w = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t h = mjs_get_int32(mjs, mjs_arg(mjs, 3));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_box_draw;
component->free = widget_box_free;
component->model = malloc(sizeof(BoxElement));
BoxElement* element = component->model;
element->x = x;
element->y = y;
element->w = w;
element->h = h;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_circle_draw(Canvas* canvas, void* model) {
CircleElement* element = model;
canvas_draw_circle(canvas, element->x, element->y, element->r);
}
static void widget_circle_free(WidgetComponent* component) {
CircleElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_circle(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 3)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t r = mjs_get_int32(mjs, mjs_arg(mjs, 2));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_circle_draw;
component->free = widget_circle_free;
component->model = malloc(sizeof(CircleElement));
CircleElement* element = component->model;
element->x = x;
element->y = y;
element->r = r;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_disc_draw(Canvas* canvas, void* model) {
DiscElement* element = model;
canvas_draw_disc(canvas, element->x, element->y, element->r);
}
static void widget_disc_free(WidgetComponent* component) {
DiscElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_disc(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 3)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t r = mjs_get_int32(mjs, mjs_arg(mjs, 2));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_disc_draw;
component->free = widget_disc_free;
component->model = malloc(sizeof(DiscElement));
DiscElement* element = component->model;
element->x = x;
element->y = y;
element->r = r;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_dot_draw(Canvas* canvas, void* model) {
DotElement* element = model;
canvas_draw_dot(canvas, element->x, element->y);
}
static void widget_dot_free(WidgetComponent* component) {
DotElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_dot(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 2)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_dot_draw;
component->free = widget_dot_free;
component->model = malloc(sizeof(DotElement));
DotElement* element = component->model;
element->x = x;
element->y = y;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_frame_draw(Canvas* canvas, void* model) {
FrameElement* element = model;
canvas_draw_frame(canvas, element->x, element->y, element->w, element->h);
}
static void widget_frame_free(WidgetComponent* component) {
FrameElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_frame(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 4)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t w = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t h = mjs_get_int32(mjs, mjs_arg(mjs, 3));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_frame_draw;
component->free = widget_frame_free;
component->model = malloc(sizeof(FrameElement));
FrameElement* element = component->model;
element->x = x;
element->y = y;
element->w = w;
element->h = h;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_glyph_draw(Canvas* canvas, void* model) {
GlyphElement* element = model;
canvas_draw_glyph(canvas, element->x, element->y, element->ch);
}
static void widget_glyph_free(WidgetComponent* component) {
GlyphElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_glyph(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 3)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t ch = mjs_get_int32(mjs, mjs_arg(mjs, 2));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_glyph_draw;
component->free = widget_glyph_free;
component->model = malloc(sizeof(GlyphElement));
GlyphElement* element = component->model;
element->x = x;
element->y = y;
element->ch = ch;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_icon_draw(Canvas* canvas, void* model) {
IconElement* element = model;
canvas_draw_icon(canvas, element->x, element->y, element->icon);
}
static void widget_icon_free(WidgetComponent* component) {
IconElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_icon(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 3)) {
return;
}
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
mjs_val_t icon_arg = mjs_arg(mjs, 2);
size_t icon_len = 0;
const char* icon_name = mjs_get_string(mjs, &icon_arg, &icon_len);
if(!icon_name) {
ret_bad_args(mjs, "Icon name must be a string");
return;
}
const Icon* icon = NULL;
for(size_t i = 0; i < ICON_PATHS_COUNT; i++) {
if(ICON_PATHS[i].path == NULL) continue;
const char* iter_name = strrchr(ICON_PATHS[i].path, '/');
if(iter_name++ == NULL) continue;
if(strnlen(iter_name, icon_len + 1) == icon_len &&
strncmp(iter_name, icon_name, icon_len) == 0) {
icon = ICON_PATHS[i].icon;
break;
}
}
if(icon == NULL) {
ret_bad_args(mjs, "Unknown icon name");
return;
}
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_icon_draw;
component->free = widget_icon_free;
component->model = malloc(sizeof(IconElement));
IconElement* element = component->model;
element->x = x;
element->y = y;
element->icon = icon;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_line_draw(Canvas* canvas, void* model) {
LineElement* element = model;
canvas_draw_line(canvas, element->x1, element->y1, element->x2, element->y2);
}
static void widget_line_free(WidgetComponent* component) {
LineElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_line(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 4)) return;
int32_t x1 = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y1 = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t x2 = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t y2 = mjs_get_int32(mjs, mjs_arg(mjs, 3));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_line_draw;
component->free = widget_line_free;
component->model = malloc(sizeof(LineElement));
LineElement* element = component->model;
element->x1 = x1;
element->y1 = y1;
element->x2 = x2;
element->y2 = y2;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_rbox_draw(Canvas* canvas, void* model) {
RboxElement* element = model;
canvas_draw_rbox(canvas, element->x, element->y, element->w, element->h, element->r);
}
static void widget_rbox_free(WidgetComponent* component) {
BoxElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_rbox(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 5)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t w = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t h = mjs_get_int32(mjs, mjs_arg(mjs, 3));
int32_t r = mjs_get_int32(mjs, mjs_arg(mjs, 4));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_rbox_draw;
component->free = widget_rbox_free;
component->model = malloc(sizeof(RboxElement));
RboxElement* element = component->model;
element->x = x;
element->y = y;
element->w = w;
element->h = h;
element->r = r;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_rframe_draw(Canvas* canvas, void* model) {
RframeElement* element = model;
canvas_draw_rframe(canvas, element->x, element->y, element->w, element->h, element->r);
}
static void widget_rframe_free(WidgetComponent* component) {
RframeElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_rframe(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 5)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t w = mjs_get_int32(mjs, mjs_arg(mjs, 2));
int32_t h = mjs_get_int32(mjs, mjs_arg(mjs, 3));
int32_t r = mjs_get_int32(mjs, mjs_arg(mjs, 4));
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_rframe_draw;
component->free = widget_rframe_free;
component->model = malloc(sizeof(RframeElement));
RframeElement* element = component->model;
element->x = x;
element->y = y;
element->w = w;
element->h = h;
element->r = r;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_text_draw(Canvas* canvas, void* model) {
TextElement* element = model;
canvas_set_font(canvas, element->font);
canvas_draw_str(canvas, element->x, element->y, furi_string_get_cstr(element->text));
}
static void widget_text_free(WidgetComponent* component) {
TextElement* element = component->model;
furi_string_free(element->text);
free(element);
free(component);
}
static void js_widget_add_text(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 4)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
mjs_val_t font_arg = mjs_arg(mjs, 2);
size_t font_name_len = 0;
const char* font_name_text = mjs_get_string(mjs, &font_arg, &font_name_len);
if(!font_name_text) {
ret_bad_args(mjs, "Font name must be a string");
return;
}
Font font = FontTotalNumber;
size_t cmp_str_len = strlen("Primary");
if(font_name_len == cmp_str_len && strncmp(font_name_text, "Primary", cmp_str_len) == 0) {
font = FontPrimary;
} else {
cmp_str_len = strlen("Secondary");
if(font_name_len == cmp_str_len &&
strncmp(font_name_text, "Secondary", cmp_str_len) == 0) {
font = FontSecondary;
}
}
if(font == FontTotalNumber) {
ret_bad_args(mjs, "Unknown font name");
return;
}
mjs_val_t text_arg = mjs_arg(mjs, 3);
size_t text_len = 0;
const char* text = mjs_get_string(mjs, &text_arg, &text_len);
if(!text) {
ret_bad_args(mjs, "Text must be a string");
return;
}
FuriString* text_str = furi_string_alloc_set(text);
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_text_draw;
component->free = widget_text_free;
component->model = malloc(sizeof(TextElement));
TextElement* element = component->model;
element->x = x;
element->y = y;
element->font = font;
element->text = text_str;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void widget_xbm_draw(Canvas* canvas, void* model) {
XbmElement* element = model;
XbmImage* image = NULL;
with_view_model(
element->view,
WidgetModel * widget_model,
{ image = *XbmImageList_get(widget_model->image, element->index); },
false);
if(image) {
canvas_draw_xbm(canvas, element->x, element->y, image->width, image->height, image->data);
}
}
static void widget_xbm_free(WidgetComponent* component) {
XbmElement* element = component->model;
free(element);
free(component);
}
static void js_widget_add_xbm(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 3)) return;
int32_t x = mjs_get_int32(mjs, mjs_arg(mjs, 0));
int32_t y = mjs_get_int32(mjs, mjs_arg(mjs, 1));
int32_t index = mjs_get_int32(mjs, mjs_arg(mjs, 2));
with_view_model(
widget->view,
WidgetModel * widget_model,
{
size_t count = XbmImageList_size(widget_model->image);
if(index < 0 || index >= (int32_t)count) {
ret_bad_args(mjs, "Invalid image index");
return;
}
},
false);
WidgetComponent* component = malloc(sizeof(WidgetComponent));
component->draw = widget_xbm_draw;
component->free = widget_xbm_free;
component->model = malloc(sizeof(XbmElement));
XbmElement* element = component->model;
element->x = x;
element->y = y;
element->index = index;
with_view_model(
widget->view,
WidgetModel * model,
{
++model->max_assigned_id;
component->id = model->max_assigned_id;
element->view = widget->view;
ComponentArray_push_back(model->component, component);
},
true);
mjs_return(mjs, mjs_mk_number(mjs, component->id));
}
static void js_widget_is_open(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
mjs_return(mjs, mjs_mk_boolean(mjs, widget->is_shown));
}
static void widget_callback(void* context, uint32_t arg) {
UNUSED(arg);
JsWidgetInst* widget = context;
view_holder_set_view(widget->view_holder, NULL);
widget->is_shown = false;
}
static void widget_exit(void* context) {
JsWidgetInst* widget = context;
// Using timer to schedule view_holder stop, will not work under high CPU load
furi_timer_pending_callback(widget_callback, widget, 0);
}
static void js_widget_show(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
if(widget->is_shown) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Widget is already shown");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
view_holder_set_view(widget->view_holder, widget->view);
widget->is_shown = true;
mjs_return(mjs, MJS_UNDEFINED);
}
static void js_widget_close(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
view_holder_set_view(widget->view_holder, NULL);
widget->is_shown = false;
mjs_return(mjs, MJS_UNDEFINED);
}
static void widget_draw_callback(Canvas* canvas, void* model) {
WidgetModel* widget_model = model;
canvas_clear(canvas);
ComponentArray_it_t it;
ComponentArray_it(it, widget_model->component);
while(!ComponentArray_end_p(it)) {
WidgetComponent* component = *ComponentArray_ref(it);
if(component->draw != NULL) {
component->draw(canvas, component->model);
}
ComponentArray_next(it);
}
}
static void* js_widget_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {
UNUSED(modules);
JsWidgetInst* widget = malloc(sizeof(JsWidgetInst));
mjs_val_t widget_obj = mjs_mk_object(mjs);
mjs_set(mjs, widget_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, widget));
// addBox(x: number, y: number, w: number, h: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addBox", ~0, MJS_MK_FN(js_widget_add_box));
// addCircle(x: number, y: number, r: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addCircle", ~0, MJS_MK_FN(js_widget_add_circle));
// addDisc(x: number, y: number, r: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addDisc", ~0, MJS_MK_FN(js_widget_add_disc));
// addDot(x: number, y: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addDot", ~0, MJS_MK_FN(js_widget_add_dot));
// addFrame(x: number, y: number, w: number, h: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addFrame", ~0, MJS_MK_FN(js_widget_add_frame));
// addGlyph(x: number, y: number, ch: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addGlyph", ~0, MJS_MK_FN(js_widget_add_glyph));
// addIcon(x: number, y: number, icon: string): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addIcon", ~0, MJS_MK_FN(js_widget_add_icon));
// addLine(x1: number, y1: number, x2: number, y2: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addLine", ~0, MJS_MK_FN(js_widget_add_line));
// addRbox(x: number, y: number, w: number, h: number, r: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addRbox", ~0, MJS_MK_FN(js_widget_add_rbox));
// addRframe(x: number, y: number, w: number, h: number, r: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addRframe", ~0, MJS_MK_FN(js_widget_add_rframe));
// addText(x: number, y: number, font: string, text: string): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addText", ~0, MJS_MK_FN(js_widget_add_text));
// addXbm(x: number, y: number, index: number): number (returns id of the added component)
mjs_set(mjs, widget_obj, "addXbm", ~0, MJS_MK_FN(js_widget_add_xbm));
// loadImageXbm(path: string): number (returns index of the loaded image)
mjs_set(mjs, widget_obj, "loadImageXbm", ~0, MJS_MK_FN(js_widget_load_image_xbm));
// remove(id: number): boolean (returns true if the component was removed)
mjs_set(mjs, widget_obj, "remove", ~0, MJS_MK_FN(js_widget_remove));
// isOpen(): boolean (returns true if the widget is open)
mjs_set(mjs, widget_obj, "isOpen", ~0, MJS_MK_FN(js_widget_is_open));
// show(): void (shows the widget)
mjs_set(mjs, widget_obj, "show", ~0, MJS_MK_FN(js_widget_show));
// close(): void (closes the widget)
mjs_set(mjs, widget_obj, "close", ~0, MJS_MK_FN(js_widget_close));
widget->view = view_alloc();
view_allocate_model(widget->view, ViewModelTypeLockFree, sizeof(WidgetModel));
view_set_draw_callback(widget->view, widget_draw_callback);
with_view_model(
widget->view,
WidgetModel * model,
{
ComponentArray_init(model->component);
XbmImageList_init(model->image);
model->max_assigned_id = 0;
},
true);
Gui* gui = furi_record_open(RECORD_GUI);
widget->view_holder = view_holder_alloc();
view_holder_attach_to_gui(widget->view_holder, gui);
view_holder_set_back_callback(widget->view_holder, widget_exit, widget);
*object = widget_obj;
return widget;
}
static void js_widget_destroy(void* inst) {
JsWidgetInst* widget = inst;
view_holder_set_view(widget->view_holder, NULL);
view_holder_free(widget->view_holder);
widget->view_holder = NULL;
furi_record_close(RECORD_GUI);
with_view_model(
widget->view,
WidgetModel * model,
{
ComponentArray_it_t it;
ComponentArray_it(it, model->component);
while(!ComponentArray_end_p(it)) {
WidgetComponent* component = *ComponentArray_ref(it);
if(component && component->free) {
component->free(component);
}
ComponentArray_next(it);
}
ComponentArray_reset(model->component);
ComponentArray_clear(model->component);
XbmImageList_clear(model->image);
},
false);
view_free(widget->view);
widget->view = NULL;
free(widget);
}
static const JsModuleDescriptor js_widget_desc = {
"widget",
js_widget_create,
js_widget_destroy,
NULL,
};
static const FlipperAppPluginDescriptor widget_plugin_descriptor = {
.appid = PLUGIN_APP_ID,
.ep_api_version = PLUGIN_API_VERSION,
.entry_point = &js_widget_desc,
};
const FlipperAppPluginDescriptor* js_widget_ep(void) {
return &widget_plugin_descriptor;
}
@@ -6,7 +6,7 @@
"start": "npm run build && node node_modules/@next-flip/fz-sdk-mntm/sdk.js upload"
},
"devDependencies": {
"@next-flip/fz-sdk-mntm": "^0.1",
"@next-flip/fz-sdk-mntm": "^0.3",
"typescript": "^5.6.3"
}
}
+3 -3
View File
@@ -72,7 +72,7 @@
* @brief Checks compatibility between the script and the JS SDK that the
* firmware provides
*
* @note You're looking at JS SDK v0.1
* @note You're looking at JS SDK v0.3
*
* @param expectedMajor JS SDK major version expected by the script
* @param expectedMinor JS SDK minor version expected by the script
@@ -92,7 +92,7 @@ declare function sdkCompatibilityStatus(expectedMajor: number, expectedMinor: nu
* @brief Checks compatibility between the script and the JS SDK that the
* firmware provides in a boolean fashion
*
* @note You're looking at JS SDK v0.1
* @note You're looking at JS SDK v0.3
*
* @param expectedMajor JS SDK major version expected by the script
* @param expectedMinor JS SDK minor version expected by the script
@@ -105,7 +105,7 @@ declare function isSdkCompatible(expectedMajor: number, expectedMinor: number):
* @brief Asks the user whether to continue executing the script if the versions
* are not compatible. Does nothing if they are.
*
* @note You're looking at JS SDK v0.1
* @note You're looking at JS SDK v0.3
*
* @param expectedMajor JS SDK major version expected by the script
* @param expectedMinor JS SDK minor version expected by the script
@@ -9,3 +9,10 @@ export type IconData = symbol & { "__tag__": "icon" };
* @version Added in JS SDK 0.2, extra feature `"gui-widget"`
*/
export declare function getBuiltin(icon: BuiltinIcon): IconData;
/**
* Loads a .fxbm icon (XBM Flipper sprite, from flipperzero-game-engine) for use in GUI
* @param path Path to the .fxbm file
* @version Added in JS SDK 0.3, extra feature `"gui-widget-extras"`
*/
export declare function loadFxbm(path: string): IconData;
+6 -2
View File
@@ -42,7 +42,9 @@ type TextBoxElement = { element: "text_box", stripToDots: boolean } & Position &
type TextScrollElement = { element: "text_scroll" } & Position & Size & Text;
type ButtonElement = { element: "button", button: "left" | "center" | "right" } & Text;
type IconElement = { element: "icon", iconData: IconData } & Position;
type FrameElement = { element: "frame", radius: number } & Position & Size;
type RectElement = { element: "rect", radius: number, fill: boolean } & Position & Size; /** @version Amended in JS SDK 0.3, extra feature `"gui-widget-extras"` */
type CircleElement = { element: "circle", radius: number, fill: boolean } & Position; /** @version Added in JS SDK 0.3, extra feature `"gui-widget-extras"` */
type LineElement = { element: "line", x1: number, y1: number, x2: number, y2: number }; /** @version Added in JS SDK 0.3, extra feature `"gui-widget-extras"` */
type Element = StringMultilineElement
| StringElement
@@ -50,7 +52,9 @@ type Element = StringMultilineElement
| TextScrollElement
| ButtonElement
| IconElement
| FrameElement;
| RectElement
| CircleElement
| LineElement;
type Props = {};
type Child = Element;
@@ -1,6 +1,6 @@
{
"name": "@next-flip/fz-sdk-mntm",
"version": "0.2.0",
"version": "0.3.0",
"description": "Type declarations and documentation for native JS modules available on Momentum Custom Firmware for Flipper Zero",
"keywords": [
"momentum",
@@ -4,16 +4,33 @@
* @module
*/
export interface Framing {
/**
* @note 6 data bits can only be selected when parity is enabled (even or
* odd)
* @note 9 data bits can only be selected when parity is disabled (none)
*/
dataBits: "6" | "7" | "8" | "9";
parity: "none" | "even" | "odd";
/**
* @note LPUART only supports whole stop bit lengths (i.e. 1 and 2 but not
* 0.5 and 1.5)
*/
stopBits: "0.5" | "1" | "1.5" | "2";
}
/**
* @brief Initializes the serial port
*
* Automatically disables Expansion module service to prevent interference.
*
* @param port The port to initialize (`"lpuart"` or `"start"`)
* @param baudRate
* @param port The port to initialize (`"lpuart"` or `"usart"`)
* @param baudRate Baud rate
* @param framing See `Framing` type
* @version Added in JS SDK 0.1
* @version Added `framing` parameter in JS SDK 0.3, extra feature `"serial-framing"`
*/
export declare function setup(port: "lpuart" | "usart", baudRate: number): void;
export declare function setup(port: "lpuart" | "usart", baudRate: number, framing?: Framing): void;
/**
* @brief Writes data to the serial port
@@ -1,140 +0,0 @@
/**
* Displays a customizable Widget on screen
* @version Available with JS feature `widget`
* @module
*/
type ComponentId = number;
/**
* @brief Add a box component
* @param x Horizontal position
* @param y Vertical position
* @param w Width
* @param h Height
*/
export declare function addBox(x: number, y: number, w: number, h: number): ComponentId;
/**
* @brief Add a circle component
* @param x Horizontal position
* @param y Vertical position
* @param r Radius
*/
export declare function addCircle(x: number, y: number, r: number): ComponentId;
/**
* @brief Add a disc component
* @param x Horizontal position
* @param y Vertical position
* @param r Radius
*/
export declare function addDisc(x: number, y: number, r: number): ComponentId;
/**
* @brief Add a dot component
* @param x Horizontal position
* @param y Vertical position
*/
export declare function addDot(x: number, y: number): ComponentId;
/**
* @brief Add a frame component
* @param x Horizontal position
* @param y Vertical position
* @param w Width
* @param h Height
*/
export declare function addFrame(x: number, y: number, w: number, h: number): ComponentId;
/**
* @brief Add a glyph component
* @param x Horizontal position
* @param y Vertical position
* @param ch ASCII character code (eg. `"C".charCodeAt(0)`)
*/
export declare function addGlyph(x: number, y: number, ch: number): ComponentId;
/**
* @brief Add an icon component
* @param x Horizontal position
* @param y Vertical position
* @param icon Name of the icon (eg. `"ButtonUp_7x4"`)
* @version Available with JS feature `widget-addicon`
*/
export declare function addIcon(x: number, y: number, icon: string): ComponentId;
/**
* @brief Add a line component
* @param x1 Horizontal position 1
* @param y1 Vertical position 1
* @param x2 Horizontal position 2
* @param y2 Vertical position 2
*/
export declare function addLine(x1: number, y1: number, x2: number, y2: number): ComponentId;
/**
* @brief Add a rounded box component
* @param x Horizontal position
* @param y Vertical position
* @param w Width
* @param h Height
* @param r Radius
*/
export declare function addRbox(x: number, y: number, w: number, h: number, r: number): ComponentId;
/**
* @brief Add a rounded frame component
* @param x Horizontal position
* @param y Vertical position
* @param w Width
* @param h Height
* @param r Radius
*/
export declare function addRframe(x: number, y: number, w: number, h: number, r: number): ComponentId;
/**
* @brief Add a text component
* @param x Horizontal position
* @param y Vertical position
* @param font What font to use, Primary or Secondary
* @param text Text to display
*/
export declare function addText(x: number, y: number, font: "Primary" | "Secondary", text: string): ComponentId;
type XbmId = number;
/**
* @brief Add an xbm image component
* @param x Horizontal position
* @param y Vertical position
* @param index Loaded xbm id to use
*/
export declare function addXbm(x: number, y: number, index: XbmId): ComponentId;
/**
* @brief Load an xbm image sprite
* @param path Xbm file to load
*/
export declare function loadImageXbm(path: string): XbmId;
/**
* @brief Remove a component
* @param id Component id to remove
*/
export declare function remove(id: ComponentId): boolean;
/**
* @brief Check if the widget view is shown
*/
export declare function isOpen(): boolean;
/**
* @brief Show the widget view
*/
export declare function show(): void;
/**
* @brief Close the widget view
*/
export declare function close(): void;
+4 -2
View File
@@ -102,11 +102,13 @@ static void __furi_print_bt_stack_info(void) {
static void __furi_print_heap_info(void) {
furi_log_puts("\r\n\t heap total: ");
__furi_put_uint32_as_text(xPortGetTotalHeapSize());
__furi_put_uint32_as_text(configTOTAL_HEAP_SIZE);
furi_log_puts("\r\n\t heap free: ");
__furi_put_uint32_as_text(xPortGetFreeHeapSize());
HeapStats_t heap_stats;
vPortGetHeapStats(&heap_stats);
furi_log_puts("\r\n\t heap watermark: ");
__furi_put_uint32_as_text(xPortGetMinimumEverFreeHeapSize());
__furi_put_uint32_as_text(heap_stats.xMinimumEverFreeBytesRemaining);
}
static void __furi_print_name(bool isr) {
+2 -1
View File
@@ -1,6 +1,7 @@
#include "memmgr.h"
#include <string.h>
#include <furi_hal_memory.h>
#include <FreeRTOS.h>
extern void* pvPortMalloc(size_t xSize);
extern void vPortFree(void* pv);
@@ -51,7 +52,7 @@ size_t memmgr_get_free_heap(void) {
}
size_t memmgr_get_total_heap(void) {
return xPortGetTotalHeapSize();
return configTOTAL_HEAP_SIZE;
}
size_t memmgr_get_minimum_free_heap(void) {
+334 -259
View File
@@ -1,6 +1,8 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* FreeRTOS Kernel V11.1.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
@@ -19,10 +21,9 @@
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
* 1 tab == 4 spaces!
*/
/*
@@ -31,21 +32,25 @@
* limits memory fragmentation.
*
* See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the
* memory management pages of http://www.FreeRTOS.org for more information.
* memory management pages of https://www.FreeRTOS.org for more information.
*/
#include "memmgr_heap.h"
#include "check.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stm32wbxx.h>
#include <stm32wb55_linker.h>
#include <core/log.h>
#include <core/common_defines.h>
// -V::562
// -V::650
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
all the API functions to use the MPU wrappers. That should only be done when
task.h is included from an application file. */
* all the API functions to use the MPU wrappers. That should only be done when
* task.h is included from an application file. */
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#include <FreeRTOS.h>
@@ -53,8 +58,12 @@ task.h is included from an application file. */
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#ifdef HEAP_PRINT_DEBUG
#error This feature is broken, logging transport must be replaced with RTT
#if(configSUPPORT_DYNAMIC_ALLOCATION == 0)
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif
#ifndef configHEAP_CLEAR_MEMORY_ON_FREE
#define configHEAP_CLEAR_MEMORY_ON_FREE 0
#endif
/* Block sizes must not get too small. */
@@ -63,16 +72,75 @@ task.h is included from an application file. */
/* Assumes 8bit bytes! */
#define heapBITS_PER_BYTE ((size_t)8)
/* Max value that fits in a size_t type. */
#define heapSIZE_MAX (~((size_t)0))
/* Check if multiplying a and b will result in overflow. */
#define heapMULTIPLY_WILL_OVERFLOW(a, b) (((a) > 0) && ((b) > (heapSIZE_MAX / (a))))
/* Check if adding a and b will result in overflow. */
#define heapADD_WILL_OVERFLOW(a, b) ((a) > (heapSIZE_MAX - (b)))
/* Check if the subtraction operation ( a - b ) will result in underflow. */
#define heapSUBTRACT_WILL_UNDERFLOW(a, b) ((a) < (b))
/* MSB of the xBlockSize member of an BlockLink_t structure is used to track
* the allocation status of a block. When MSB of the xBlockSize member of
* an BlockLink_t structure is set then the block belongs to the application.
* When the bit is free the block is still part of the free heap space. */
#define heapBLOCK_ALLOCATED_BITMASK (((size_t)1) << ((sizeof(size_t) * heapBITS_PER_BYTE) - 1))
#define heapBLOCK_SIZE_IS_VALID(xBlockSize) (((xBlockSize) & heapBLOCK_ALLOCATED_BITMASK) == 0)
#define heapBLOCK_IS_ALLOCATED(pxBlock) \
(((pxBlock->xBlockSize) & heapBLOCK_ALLOCATED_BITMASK) != 0)
#define heapALLOCATE_BLOCK(pxBlock) ((pxBlock->xBlockSize) |= heapBLOCK_ALLOCATED_BITMASK)
#define heapFREE_BLOCK(pxBlock) ((pxBlock->xBlockSize) &= ~heapBLOCK_ALLOCATED_BITMASK)
/*-----------------------------------------------------------*/
/* Heap start end symbols provided by linker */
uint8_t* ucHeap = (uint8_t*)&__heap_start__;
/* Define the linked list structure. This is used to link free blocks in order
of their memory address. */
* of their memory address. */
typedef struct A_BLOCK_LINK {
struct A_BLOCK_LINK* pxNextFreeBlock; /*<< The next free block in the list. */
size_t xBlockSize; /*<< The size of the free block. */
struct A_BLOCK_LINK* pxNextFreeBlock; /**< The next free block in the list. */
size_t xBlockSize; /**< The size of the free block. */
} BlockLink_t;
/* Setting configENABLE_HEAP_PROTECTOR to 1 enables heap block pointers
* protection using an application supplied canary value to catch heap
* corruption should a heap buffer overflow occur.
*/
#if(configENABLE_HEAP_PROTECTOR == 1)
/**
* @brief Application provided function to get a random value to be used as canary.
*
* @param pxHeapCanary [out] Output parameter to return the canary value.
*/
extern void vApplicationGetRandomHeapCanary(portPOINTER_SIZE_TYPE* pxHeapCanary);
/* Canary value for protecting internal heap pointers. */
PRIVILEGED_DATA static portPOINTER_SIZE_TYPE xHeapCanary;
/* Macro to load/store BlockLink_t pointers to memory. By XORing the
* pointers with a random canary value, heap overflows will result
* in randomly unpredictable pointer values which will be caught by
* heapVALIDATE_BLOCK_POINTER assert. */
#define heapPROTECT_BLOCK_POINTER(pxBlock) \
((BlockLink_t*)(((portPOINTER_SIZE_TYPE)(pxBlock)) ^ xHeapCanary))
#else
#define heapPROTECT_BLOCK_POINTER(pxBlock) (pxBlock)
#endif /* configENABLE_HEAP_PROTECTOR */
/* Assert that a heap block pointer is within the heap bounds. */
#define heapVALIDATE_BLOCK_POINTER(pxBlock) \
configASSERT( \
((uint8_t*)(pxBlock) >= &(ucHeap[0])) && \
((uint8_t*)(pxBlock) <= &(ucHeap[configTOTAL_HEAP_SIZE - 1])))
/*-----------------------------------------------------------*/
/*
@@ -81,34 +149,31 @@ typedef struct A_BLOCK_LINK {
* the block in front it and/or the block behind it if the memory blocks are
* adjacent to each other.
*/
static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert);
static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert) PRIVILEGED_FUNCTION;
/*
* Called automatically to setup the required heap structures the first time
* pvPortMalloc() is called.
*/
static void prvHeapInit(void);
static void prvHeapInit(void) PRIVILEGED_FUNCTION;
/*-----------------------------------------------------------*/
/* The size of the structure placed at the beginning of each allocated memory
block must by correctly byte aligned. */
* block must by correctly byte aligned. */
static const size_t xHeapStructSize = (sizeof(BlockLink_t) + ((size_t)(portBYTE_ALIGNMENT - 1))) &
~((size_t)portBYTE_ALIGNMENT_MASK);
/* Create a couple of list links to mark the start and end of the list. */
static BlockLink_t xStart, *pxEnd = NULL;
PRIVILEGED_DATA static BlockLink_t xStart;
PRIVILEGED_DATA static BlockLink_t* pxEnd = NULL;
/* Keeps track of the number of free bytes remaining, but says nothing about
fragmentation. */
static size_t xFreeBytesRemaining = 0U;
static size_t xMinimumEverFreeBytesRemaining = 0U;
/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize
member of an BlockLink_t structure is set then the block belongs to the
application. When the bit is free the block is still part of the free heap
space. */
static size_t xBlockAllocatedBit = 0;
/* Keeps track of the number of calls to allocate and free memory as well as the
* number of free bytes remaining, but says nothing about fragmentation. */
PRIVILEGED_DATA static size_t xFreeBytesRemaining = (size_t)0U;
PRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = (size_t)0U;
PRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = (size_t)0U;
PRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = (size_t)0U;
/* Furi heap extension */
#include <m-dict.h>
@@ -175,7 +240,7 @@ size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) {
puc -= xHeapStructSize;
BlockLink_t* pxLink = (void*)puc;
if((pxLink->xBlockSize & xBlockAllocatedBit) != 0 &&
if((pxLink->xBlockSize & heapBLOCK_ALLOCATED_BITMASK) &&
pxLink->pxNextFreeBlock == NULL) {
leftovers += data->value;
}
@@ -220,20 +285,9 @@ static inline void traceFREE(void* pointer, size_t size) {
}
size_t memmgr_heap_get_max_free_block(void) {
size_t max_free_size = 0;
BlockLink_t* pxBlock;
vTaskSuspendAll();
pxBlock = xStart.pxNextFreeBlock;
while(pxBlock->pxNextFreeBlock != NULL) {
if(pxBlock->xBlockSize > max_free_size) {
max_free_size = pxBlock->xBlockSize;
}
pxBlock = pxBlock->pxNextFreeBlock;
}
xTaskResumeAll();
return max_free_size;
HeapStats_t heap_stats;
vPortGetHeapStats(&heap_stats);
return heap_stats.xSizeOfLargestFreeBlockInBytes;
}
void memmgr_heap_printf_free_blocks(void) {
@@ -250,186 +304,115 @@ void memmgr_heap_printf_free_blocks(void) {
//xTaskResumeAll();
}
#ifdef HEAP_PRINT_DEBUG
char* ultoa(unsigned long num, char* str, int radix) {
char temp[33]; // at radix 2 the string is at most 32 + 1 null long.
int temp_loc = 0;
int digit;
int str_loc = 0;
//construct a backward string of the number.
do {
digit = (unsigned long)num % ((unsigned long)radix);
if(digit < 10)
temp[temp_loc++] = digit + '0';
else
temp[temp_loc++] = digit - 10 + 'A';
num = ((unsigned long)num) / ((unsigned long)radix);
} while((unsigned long)num > 0);
temp_loc--;
//now reverse the string.
while(temp_loc >= 0) { // while there are still chars
str[str_loc++] = temp[temp_loc--];
}
str[str_loc] = 0; // add null termination.
return str;
}
static void print_heap_init(void) {
char tmp_str[33];
size_t heap_start = (size_t)&__heap_start__;
size_t heap_end = (size_t)&__heap_end__;
// {PHStart|heap_start|heap_end}
FURI_CRITICAL_ENTER();
furi_log_puts("{PHStart|");
ultoa(heap_start, tmp_str, 16);
furi_log_puts(tmp_str);
furi_log_puts("|");
ultoa(heap_end, tmp_str, 16);
furi_log_puts(tmp_str);
furi_log_puts("}\r\n");
FURI_CRITICAL_EXIT();
}
static void print_heap_malloc(void* ptr, size_t size) {
char tmp_str[33];
const char* name = furi_thread_get_name(furi_thread_get_current_id());
if(!name) {
name = "";
}
// {thread name|m|address|size}
FURI_CRITICAL_ENTER();
furi_log_puts("{");
furi_log_puts(name);
furi_log_puts("|m|0x");
ultoa((unsigned long)ptr, tmp_str, 16);
furi_log_puts(tmp_str);
furi_log_puts("|");
utoa(size, tmp_str, 10);
furi_log_puts(tmp_str);
furi_log_puts("}\r\n");
FURI_CRITICAL_EXIT();
}
static void print_heap_free(void* ptr) {
char tmp_str[33];
const char* name = furi_thread_get_name(furi_thread_get_current_id());
if(!name) {
name = "";
}
// {thread name|f|address}
FURI_CRITICAL_ENTER();
furi_log_puts("{");
furi_log_puts(name);
furi_log_puts("|f|0x");
ultoa((unsigned long)ptr, tmp_str, 16);
furi_log_puts(tmp_str);
furi_log_puts("}\r\n");
FURI_CRITICAL_EXIT();
}
#endif
/*-----------------------------------------------------------*/
void* pvPortMalloc(size_t xWantedSize) {
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
BlockLink_t* pxBlock;
BlockLink_t* pxPreviousBlock;
BlockLink_t* pxNewBlockLink;
void* pvReturn = NULL;
size_t to_wipe = xWantedSize;
size_t xToWipe = xWantedSize;
size_t xAdditionalRequiredSize;
size_t xAllocatedBlockSize = 0;
if(FURI_IS_IRQ_MODE()) {
furi_crash("memmgt in ISR");
}
#ifdef HEAP_PRINT_DEBUG
BlockLink_t* print_heap_block = NULL;
#endif
if(xWantedSize > 0) {
/* The wanted size must be increased so it can contain a BlockLink_t
* structure in addition to the requested amount of bytes. */
if(heapADD_WILL_OVERFLOW(xWantedSize, xHeapStructSize) == 0) {
xWantedSize += xHeapStructSize;
/* If this is the first call to malloc then the heap will require
initialisation to setup the list of free blocks. */
if(pxEnd == NULL) {
#ifdef HEAP_PRINT_DEBUG
print_heap_init();
#endif
/* Ensure that blocks are always aligned to the required number
* of bytes. */
if((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0x00) {
/* Byte alignment required. */
xAdditionalRequiredSize =
portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK);
vTaskSuspendAll();
{
prvHeapInit();
memmgr_heap_init();
if(heapADD_WILL_OVERFLOW(xWantedSize, xAdditionalRequiredSize) == 0) {
xWantedSize += xAdditionalRequiredSize;
} else {
xWantedSize = 0;
}
} else {
mtCOVERAGE_TEST_MARKER();
}
} else {
xWantedSize = 0;
}
(void)xTaskResumeAll();
} else {
mtCOVERAGE_TEST_MARKER();
}
vTaskSuspendAll();
{
/* Check the requested block size is not so large that the top bit is
set. The top bit of the block size member of the BlockLink_t structure
is used to determine who owns the block - the application or the
kernel, so it must be free. */
if((xWantedSize & xBlockAllocatedBit) == 0) {
/* The wanted size is increased so it can contain a BlockLink_t
structure in addition to the requested amount of bytes. */
if(xWantedSize > 0) {
xWantedSize += xHeapStructSize;
/* Ensure that blocks are always aligned to the required number
of bytes. */
if((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0x00) {
/* Byte alignment required. */
xWantedSize += (portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK));
configASSERT((xWantedSize & portBYTE_ALIGNMENT_MASK) == 0);
} else {
mtCOVERAGE_TEST_MARKER();
}
} else {
mtCOVERAGE_TEST_MARKER();
}
/* If this is the first call to malloc then the heap will require
* initialisation to setup the list of free blocks. */
if(pxEnd == NULL) {
prvHeapInit();
memmgr_heap_init();
} else {
mtCOVERAGE_TEST_MARKER();
}
/* Check the block size we are trying to allocate is not so large that the
* top bit is set. The top bit of the block size member of the BlockLink_t
* structure is used to determine who owns the block - the application or
* the kernel, so it must be free. */
if(heapBLOCK_SIZE_IS_VALID(xWantedSize) != 0) {
if((xWantedSize > 0) && (xWantedSize <= xFreeBytesRemaining)) {
/* Traverse the list from the start (lowest address) block until
one of adequate size is found. */
* one of adequate size is found. */
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while((pxBlock->xBlockSize < xWantedSize) && (pxBlock->pxNextFreeBlock != NULL)) {
pxBlock = heapPROTECT_BLOCK_POINTER(xStart.pxNextFreeBlock);
heapVALIDATE_BLOCK_POINTER(pxBlock);
while((pxBlock->xBlockSize < xWantedSize) &&
(pxBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER(NULL))) {
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
pxBlock = heapPROTECT_BLOCK_POINTER(pxBlock->pxNextFreeBlock);
heapVALIDATE_BLOCK_POINTER(pxBlock);
}
/* If the end marker was reached then a block of adequate size
was not found. */
* was not found. */
if(pxBlock != pxEnd) {
/* Return the memory space pointed to - jumping over the
BlockLink_t structure at its start. */
pvReturn =
(void*)(((uint8_t*)pxPreviousBlock->pxNextFreeBlock) + xHeapStructSize);
* BlockLink_t structure at its start. */
pvReturn = (void*)(((uint8_t*)heapPROTECT_BLOCK_POINTER(
pxPreviousBlock->pxNextFreeBlock)) +
xHeapStructSize);
heapVALIDATE_BLOCK_POINTER(pvReturn);
/* This block is being returned for use so must be taken out
of the list of free blocks. */
* of the list of free blocks. */
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
/* If the block is larger than required it can be split into
two. */
* two. */
configASSERT(
heapSUBTRACT_WILL_UNDERFLOW(pxBlock->xBlockSize, xWantedSize) == 0);
if((pxBlock->xBlockSize - xWantedSize) > heapMINIMUM_BLOCK_SIZE) {
/* This block is to be split into two. Create a new
block following the number of bytes requested. The void
cast is used to prevent byte alignment warnings from the
compiler. */
* block following the number of bytes requested. The void
* cast is used to prevent byte alignment warnings from the
* compiler. */
pxNewBlockLink = (void*)(((uint8_t*)pxBlock) + xWantedSize);
configASSERT((((size_t)pxNewBlockLink) & portBYTE_ALIGNMENT_MASK) == 0);
/* Calculate the sizes of two blocks split from the
single block. */
* single block. */
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
pxBlock->xBlockSize = xWantedSize;
/* Insert the new block into the list of free blocks. */
prvInsertBlockIntoFreeList(pxNewBlockLink);
pxNewBlockLink->pxNextFreeBlock = pxPreviousBlock->pxNextFreeBlock;
pxPreviousBlock->pxNextFreeBlock =
heapPROTECT_BLOCK_POINTER(pxNewBlockLink);
} else {
mtCOVERAGE_TEST_MARKER();
}
@@ -442,14 +425,13 @@ void* pvPortMalloc(size_t xWantedSize) {
mtCOVERAGE_TEST_MARKER();
}
/* The block is being returned - it is allocated and owned
by the application and has no "next" block. */
pxBlock->xBlockSize |= xBlockAllocatedBit;
pxBlock->pxNextFreeBlock = NULL;
xAllocatedBlockSize = pxBlock->xBlockSize;
#ifdef HEAP_PRINT_DEBUG
print_heap_block = pxBlock;
#endif
/* The block is being returned - it is allocated and owned
* by the application and has no "next" block. */
heapALLOCATE_BLOCK(pxBlock);
pxBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(NULL);
xNumberOfSuccessfulAllocations++;
} else {
mtCOVERAGE_TEST_MARKER();
}
@@ -460,29 +442,27 @@ void* pvPortMalloc(size_t xWantedSize) {
mtCOVERAGE_TEST_MARKER();
}
traceMALLOC(pvReturn, xWantedSize);
traceMALLOC(pvReturn, xAllocatedBlockSize);
/* Prevent compiler warnings when trace macros are not used. */
(void)xAllocatedBlockSize;
}
(void)xTaskResumeAll();
#ifdef HEAP_PRINT_DEBUG
print_heap_malloc(print_heap_block, print_heap_block->xBlockSize & ~xBlockAllocatedBit);
#endif
#if(configUSE_MALLOC_FAILED_HOOK == 1)
{
if(pvReturn == NULL) {
extern void vApplicationMallocFailedHook(void);
vApplicationMallocFailedHook();
} else {
mtCOVERAGE_TEST_MARKER();
}
}
#endif
#endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */
configASSERT((((size_t)pvReturn) & (size_t)portBYTE_ALIGNMENT_MASK) == 0);
furi_check(pvReturn, xWantedSize ? "out of memory" : "malloc(0)");
pvReturn = memset(pvReturn, 0, to_wipe);
pvReturn = memset(pvReturn, 0, xToWipe);
return pvReturn;
}
/*-----------------------------------------------------------*/
@@ -497,24 +477,30 @@ void vPortFree(void* pv) {
if(pv != NULL) {
/* The memory being freed will have an BlockLink_t structure immediately
before it. */
* before it. */
puc -= xHeapStructSize;
/* This casting is to keep the compiler from issuing warnings. */
pxLink = (void*)puc;
/* Check the block is actually allocated. */
configASSERT((pxLink->xBlockSize & xBlockAllocatedBit) != 0);
configASSERT(pxLink->pxNextFreeBlock == NULL);
heapVALIDATE_BLOCK_POINTER(pxLink);
configASSERT(heapBLOCK_IS_ALLOCATED(pxLink) != 0);
configASSERT(pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER(NULL));
if((pxLink->xBlockSize & xBlockAllocatedBit) != 0) {
if(pxLink->pxNextFreeBlock == NULL) {
if(heapBLOCK_IS_ALLOCATED(pxLink) != 0) {
if(pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER(NULL)) {
/* The block is being returned to the heap - it is no longer
allocated. */
pxLink->xBlockSize &= ~xBlockAllocatedBit;
#ifdef HEAP_PRINT_DEBUG
print_heap_free(pxLink);
* allocated. */
heapFREE_BLOCK(pxLink);
#if(configHEAP_CLEAR_MEMORY_ON_FREE == 1)
{
/* Check for underflow as this can occur if xBlockSize is
* overwritten in a heap block. */
if(heapSUBTRACT_WILL_UNDERFLOW(pxLink->xBlockSize, xHeapStructSize) == 0) {
(void)memset(
puc + xHeapStructSize, 0, pxLink->xBlockSize - xHeapStructSize);
}
}
#endif
vTaskSuspendAll();
@@ -527,8 +513,8 @@ void vPortFree(void* pv) {
/* Add this block to the list of free blocks. */
xFreeBytesRemaining += pxLink->xBlockSize;
traceFREE(pv, pxLink->xBlockSize);
memset(pv, 0, pxLink->xBlockSize - xHeapStructSize);
prvInsertBlockIntoFreeList((BlockLink_t*)pxLink);
prvInsertBlockIntoFreeList(((BlockLink_t*)pxLink));
xNumberOfSuccessfulFrees++;
}
(void)xTaskResumeAll();
} else {
@@ -537,19 +523,10 @@ void vPortFree(void* pv) {
} else {
mtCOVERAGE_TEST_MARKER();
}
} else {
#ifdef HEAP_PRINT_DEBUG
print_heap_free(pv);
#endif
}
}
/*-----------------------------------------------------------*/
size_t xPortGetTotalHeapSize(void) {
return (size_t)&__heap_end__ - (size_t)&__heap_start__;
}
/*-----------------------------------------------------------*/
size_t xPortGetFreeHeapSize(void) {
return xFreeBytesRemaining;
}
@@ -560,71 +537,98 @@ size_t xPortGetMinimumEverFreeHeapSize(void) {
}
/*-----------------------------------------------------------*/
void xPortResetHeapMinimumEverFreeHeapSize(void) {
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
void vPortInitialiseBlocks(void) {
/* This just exists to keep the linker quiet. */
}
/*-----------------------------------------------------------*/
static void prvHeapInit(void) {
BlockLink_t* pxFirstFreeBlock;
uint8_t* pucAlignedHeap;
size_t uxAddress;
size_t xTotalHeapSize = (size_t)&__heap_end__ - (size_t)&__heap_start__;
void* pvPortCalloc(size_t xNum, size_t xSize) {
void* pv = NULL;
/* Ensure the heap starts on a correctly aligned boundary. */
uxAddress = (size_t)ucHeap;
if(heapMULTIPLY_WILL_OVERFLOW(xNum, xSize) == 0) {
pv = pvPortMalloc(xNum * xSize);
if((uxAddress & portBYTE_ALIGNMENT_MASK) != 0) {
uxAddress += (portBYTE_ALIGNMENT - 1);
uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK);
xTotalHeapSize -= uxAddress - (size_t)ucHeap;
if(pv != NULL) {
(void)memset(pv, 0, xNum * xSize);
}
}
pucAlignedHeap = (uint8_t*)uxAddress;
return pv;
}
/*-----------------------------------------------------------*/
static void prvHeapInit(void) /* PRIVILEGED_FUNCTION */
{
BlockLink_t* pxFirstFreeBlock;
portPOINTER_SIZE_TYPE uxStartAddress, uxEndAddress;
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;
/* Ensure the heap starts on a correctly aligned boundary. */
uxStartAddress = (portPOINTER_SIZE_TYPE)ucHeap;
if((uxStartAddress & portBYTE_ALIGNMENT_MASK) != 0) {
uxStartAddress += (portBYTE_ALIGNMENT - 1);
uxStartAddress &= ~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK);
xTotalHeapSize -= (size_t)(uxStartAddress - (portPOINTER_SIZE_TYPE)ucHeap);
}
#if(configENABLE_HEAP_PROTECTOR == 1)
{ vApplicationGetRandomHeapCanary(&(xHeapCanary)); }
#endif
/* xStart is used to hold a pointer to the first item in the list of free
blocks. The void cast is used to prevent compiler warnings. */
xStart.pxNextFreeBlock = (void*)pucAlignedHeap;
* blocks. The void cast is used to prevent compiler warnings. */
xStart.pxNextFreeBlock = (void*)heapPROTECT_BLOCK_POINTER(uxStartAddress);
xStart.xBlockSize = (size_t)0;
/* pxEnd is used to mark the end of the list of free blocks and is inserted
at the end of the heap space. */
uxAddress = ((size_t)pucAlignedHeap) + xTotalHeapSize;
uxAddress -= xHeapStructSize;
uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK);
pxEnd = (void*)uxAddress;
* at the end of the heap space. */
uxEndAddress = uxStartAddress + (portPOINTER_SIZE_TYPE)xTotalHeapSize;
uxEndAddress -= (portPOINTER_SIZE_TYPE)xHeapStructSize;
uxEndAddress &= ~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK);
pxEnd = (BlockLink_t*)uxEndAddress;
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;
pxEnd->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(NULL);
/* To start with there is a single free block that is sized to take up the
entire heap space, minus the space taken by pxEnd. */
pxFirstFreeBlock = (void*)pucAlignedHeap;
pxFirstFreeBlock->xBlockSize = uxAddress - (size_t)pxFirstFreeBlock;
pxFirstFreeBlock->pxNextFreeBlock = pxEnd;
* entire heap space, minus the space taken by pxEnd. */
pxFirstFreeBlock = (BlockLink_t*)uxStartAddress;
pxFirstFreeBlock->xBlockSize =
(size_t)(uxEndAddress - (portPOINTER_SIZE_TYPE)pxFirstFreeBlock);
pxFirstFreeBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(pxEnd);
/* Only one block exists - and it covers the entire usable heap space. */
xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
/* Work out the position of the top bit in a size_t variable. */
xBlockAllocatedBit = ((size_t)1) << ((sizeof(size_t) * heapBITS_PER_BYTE) - 1);
}
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert) {
static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert) /* PRIVILEGED_FUNCTION */
{
BlockLink_t* pxIterator;
uint8_t* puc;
/* Iterate through the list until a block is found that has a higher address
than the block being inserted. */
for(pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert;
pxIterator = pxIterator->pxNextFreeBlock) {
* than the block being inserted. */
for(pxIterator = &xStart;
heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock) < pxBlockToInsert;
pxIterator = heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock)) {
/* Nothing to do here, just iterate to the right position. */
}
if(pxIterator != &xStart) {
heapVALIDATE_BLOCK_POINTER(pxIterator);
}
/* Do the block being inserted, and the block it is being inserted after
make a contiguous block of memory? */
* make a contiguous block of memory? */
puc = (uint8_t*)pxIterator;
if((puc + pxIterator->xBlockSize) == (uint8_t*)pxBlockToInsert) {
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
pxBlockToInsert = pxIterator;
@@ -633,27 +637,98 @@ static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert) {
}
/* Do the block being inserted, and the block it is being inserted before
make a contiguous block of memory? */
* make a contiguous block of memory? */
puc = (uint8_t*)pxBlockToInsert;
if((puc + pxBlockToInsert->xBlockSize) == (uint8_t*)pxIterator->pxNextFreeBlock) {
if(pxIterator->pxNextFreeBlock != pxEnd) {
if((puc + pxBlockToInsert->xBlockSize) ==
(uint8_t*)heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock)) {
if(heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock) != pxEnd) {
/* Form one big block from the two blocks. */
pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
pxBlockToInsert->xBlockSize +=
heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock)->xBlockSize;
pxBlockToInsert->pxNextFreeBlock =
heapPROTECT_BLOCK_POINTER(pxIterator->pxNextFreeBlock)->pxNextFreeBlock;
} else {
pxBlockToInsert->pxNextFreeBlock = pxEnd;
pxBlockToInsert->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(pxEnd);
}
} else {
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
}
/* If the block being inserted plugged a gab, so was merged with the block
before and the block after, then it's pxNextFreeBlock pointer will have
already been set, and should not be set here as that would make it point
to itself. */
/* If the block being inserted plugged a gap, so was merged with the block
* before and the block after, then it's pxNextFreeBlock pointer will have
* already been set, and should not be set here as that would make it point
* to itself. */
if(pxIterator != pxBlockToInsert) {
pxIterator->pxNextFreeBlock = pxBlockToInsert;
pxIterator->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER(pxBlockToInsert);
} else {
mtCOVERAGE_TEST_MARKER();
}
}
/*-----------------------------------------------------------*/
void vPortGetHeapStats(HeapStats_t* pxHeapStats) {
BlockLink_t* pxBlock;
size_t
xBlocks = 0,
xMaxSize = 0,
xMinSize =
portMAX_DELAY; /* portMAX_DELAY used as a portable way of getting the maximum value. */
vTaskSuspendAll();
{
pxBlock = heapPROTECT_BLOCK_POINTER(xStart.pxNextFreeBlock);
/* pxBlock will be NULL if the heap has not been initialised. The heap
* is initialised automatically when the first allocation is made. */
if(pxBlock != NULL) {
while(pxBlock != pxEnd) {
/* Increment the number of blocks and record the largest block seen
* so far. */
xBlocks++;
if(pxBlock->xBlockSize > xMaxSize) {
xMaxSize = pxBlock->xBlockSize;
}
if(pxBlock->xBlockSize < xMinSize) {
xMinSize = pxBlock->xBlockSize;
}
/* Move to the next block in the chain until the last block is
* reached. */
pxBlock = heapPROTECT_BLOCK_POINTER(pxBlock->pxNextFreeBlock);
}
}
}
(void)xTaskResumeAll();
pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize;
pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize;
pxHeapStats->xNumberOfFreeBlocks = xBlocks;
taskENTER_CRITICAL();
{
pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining;
pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations;
pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees;
pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining;
}
taskEXIT_CRITICAL();
}
/*-----------------------------------------------------------*/
/*
* Reset the state in this file. This state is normally initialized at start up.
* This function must be called by the application before restarting the
* scheduler.
*/
void vPortHeapResetState(void) {
pxEnd = NULL;
xFreeBytesRemaining = (size_t)0U;
xMinimumEverFreeBytesRemaining = (size_t)0U;
xNumberOfSuccessfulAllocations = (size_t)0U;
xNumberOfSuccessfulFrees = (size_t)0U;
}
/*-----------------------------------------------------------*/
+10 -4
View File
@@ -765,16 +765,22 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
return 0;
}
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void) {
void furi_thread_get_stdout_callback(FuriThreadStdoutWriteCallback* callback, void** context) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
return thread->output.write_callback;
furi_check(callback);
furi_check(context);
*callback = thread->output.write_callback;
*context = thread->output.context;
}
FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void) {
void furi_thread_get_stdin_callback(FuriThreadStdinReadCallback* callback, void** context) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
return thread->input.read_callback;
furi_check(callback);
furi_check(context);
*callback = thread->input.read_callback;
*context = thread->input.context;
}
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context) {
+6 -4
View File
@@ -479,16 +479,18 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id);
/**
* @brief Get the standard output callback for the current thead.
*
* @return pointer to the standard out callback function
* @param[out] callback where to store the stdout callback
* @param[out] context where to store the context
*/
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void);
void furi_thread_get_stdout_callback(FuriThreadStdoutWriteCallback* callback, void** context);
/**
* @brief Get the standard input callback for the current thead.
*
* @return pointer to the standard in callback function
* @param[out] callback where to store the stdin callback
* @param[out] context where to store the context
*/
FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void);
void furi_thread_get_stdin_callback(FuriThreadStdinReadCallback* callback, void** context);
/** Set standard output callback for the current thread.
*
+6
View File
@@ -12,6 +12,8 @@
#define TAG "Flipper"
#define HEAP_CANARY_VALUE 0x8BADF00D
static void flipper_print_version(const char* target, const Version* version) {
if(version) {
FURI_LOG_I(
@@ -244,3 +246,7 @@ void vApplicationGetTimerTaskMemory(
*stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH);
*stack_size = configTIMER_TASK_STACK_DEPTH;
}
void vApplicationGetRandomHeapCanary(portPOINTER_SIZE_TYPE* pxHeapCanary) {
*pxHeapCanary = HEAP_CANARY_VALUE;
}
+14 -5
View File
@@ -1,9 +1,10 @@
#include "ibutton_worker_i.h"
#include <core/check.h>
#include <core/record.h>
#include <furi_hal_rfid.h>
#include <furi_hal_power.h>
#include <power/power_service/power.h>
#include "ibutton_protocols.h"
@@ -75,7 +76,9 @@ void ibutton_worker_mode_idle_stop(iButtonWorker* worker) {
void ibutton_worker_mode_read_start(iButtonWorker* worker) {
UNUSED(worker);
if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, true);
furi_record_close(RECORD_POWER);
}
void ibutton_worker_mode_read_tick(iButtonWorker* worker) {
@@ -90,7 +93,9 @@ void ibutton_worker_mode_read_tick(iButtonWorker* worker) {
void ibutton_worker_mode_read_stop(iButtonWorker* worker) {
UNUSED(worker);
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, false);
furi_record_close(RECORD_POWER);
}
/*********************** EMULATE ***********************/
@@ -120,7 +125,9 @@ void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) {
void ibutton_worker_mode_write_common_start(iButtonWorker* worker) { //-V524
UNUSED(worker);
if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, true);
furi_record_close(RECORD_POWER);
}
void ibutton_worker_mode_write_id_tick(iButtonWorker* worker) {
@@ -149,5 +156,7 @@ void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker) {
void ibutton_worker_mode_write_common_stop(iButtonWorker* worker) { //-V524
UNUSED(worker);
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, false);
furi_record_close(RECORD_POWER);
}
+13 -8
View File
@@ -61,17 +61,22 @@ uint8_t* protocol_securakey_get_data(ProtocolSecurakey* protocol) {
static bool protocol_securakey_can_be_decoded(ProtocolSecurakey* protocol) {
// check 19 bits preamble + format flag
if(bit_lib_get_bits_32(protocol->RKKT_encoded_data, 0, 19) == 0b0111111111000000000) {
protocol->bit_format = 0;
return true;
if(bit_lib_test_parity(protocol->RKKT_encoded_data, 2, 54, BitLibParityAlways0, 9)) {
protocol->bit_format = 0;
return true;
}
} else if(bit_lib_get_bits_32(protocol->RKKT_encoded_data, 0, 19) == 0b0111111111001011010) {
protocol->bit_format = 26;
return true;
if(bit_lib_test_parity(protocol->RKKT_encoded_data, 2, 90, BitLibParityAlways0, 9)) {
protocol->bit_format = 26;
return true;
}
} else if(bit_lib_get_bits_32(protocol->RKKT_encoded_data, 0, 19) == 0b0111111111001100000) {
protocol->bit_format = 32;
return true;
} else {
return false;
if(bit_lib_test_parity(protocol->RKKT_encoded_data, 2, 90, BitLibParityAlways0, 9)) {
protocol->bit_format = 32;
return true;
}
}
return false;
}
static void protocol_securakey_decode(ProtocolSecurakey* protocol) {
+5 -3
View File
@@ -118,7 +118,8 @@ NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) {
blocks[1] = FELICA_BLOCK_INDEX_WCNT;
blocks[2] = FELICA_BLOCK_INDEX_MAC_A;
FelicaPollerReadCommandResponse* rx_resp;
error = felica_poller_read_blocks(instance, sizeof(blocks), blocks, &rx_resp);
error = felica_poller_read_blocks(
instance, sizeof(blocks), blocks, FELICA_SERVICE_RO_ACCESS, &rx_resp);
if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;
if(felica_check_mac(
@@ -174,7 +175,7 @@ NfcCommand felica_poller_state_handler_auth_external(FelicaPoller* instance) {
if(error != FelicaErrorNone || tx_resp->SF1 != 0 || tx_resp->SF2 != 0) break;
FelicaPollerReadCommandResponse* rx_resp;
error = felica_poller_read_blocks(instance, 1, blocks, &rx_resp);
error = felica_poller_read_blocks(instance, 1, blocks, FELICA_SERVICE_RO_ACCESS, &rx_resp);
if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;
instance->data->data.fs.state.SF1 = 0;
@@ -203,7 +204,8 @@ NfcCommand felica_poller_state_handler_read_blocks(FelicaPoller* instance) {
}
FelicaPollerReadCommandResponse* response;
FelicaError error = felica_poller_read_blocks(instance, block_count, block_list, &response);
FelicaError error = felica_poller_read_blocks(
instance, block_count, block_list, FELICA_SERVICE_RO_ACCESS, &response);
if(error == FelicaErrorNone) {
block_count = (response->SF1 == 0) ? response->block_count : block_count;
uint8_t* data_ptr =
+17
View File
@@ -56,6 +56,23 @@ typedef struct {
*/
FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data);
/**
* @brief Performs felica read operation for blocks provided as parameters
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] block_count Amount of blocks involved in reading procedure
* @param[in] block_numbers Array with block indexes according to felica docs
* @param[in] service_code Service code for the read operation
* @param[out] response_ptr Pointer to the response structure
* @return FelicaErrorNone on success, an error code on failure.
*/
FelicaError felica_poller_read_blocks(
FelicaPoller* instance,
const uint8_t block_count,
const uint8_t* const block_numbers,
uint16_t service_code,
FelicaPollerReadCommandResponse** const response_ptr);
#ifdef __cplusplus
}
#endif
+2 -1
View File
@@ -134,6 +134,7 @@ FelicaError felica_poller_read_blocks(
FelicaPoller* instance,
const uint8_t block_count,
const uint8_t* const block_numbers,
uint16_t service_code,
FelicaPollerReadCommandResponse** const response_ptr) {
furi_assert(instance);
furi_assert(block_count <= 4);
@@ -143,7 +144,7 @@ FelicaError felica_poller_read_blocks(
felica_poller_prepare_tx_buffer(
instance,
FELICA_CMD_READ_WITHOUT_ENCRYPTION,
FELICA_SERVICE_RO_ACCESS,
service_code,
block_count,
block_numbers,
0,
@@ -70,21 +70,6 @@ FelicaError felica_poller_polling(
const FelicaPollerPollingCommand* cmd,
FelicaPollerPollingResponse* resp);
/**
* @brief Performs felica read operation for blocks provided as parameters
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] block_count Amount of blocks involved in reading procedure
* @param[in] block_numbers Array with block indexes according to felica docs
* @param[out] response_ptr Pointer to the response structure
* @return FelicaErrorNone on success, an error code on failure.
*/
FelicaError felica_poller_read_blocks(
FelicaPoller* instance,
const uint8_t block_count,
const uint8_t* const block_numbers,
FelicaPollerReadCommandResponse** const response_ptr);
/**
* @brief Performs felica write operation with data provided as parameters
*
+1 -1
View File
@@ -230,7 +230,7 @@ static bool subghz_protocol_secplus_v1_encode(SubGhzProtocolEncoderSecPlus_v1* i
rolling = 0xE6000000;
}
if(fixed > 0xCFD41B90) {
FURI_LOG_E("TAG", "Encode wrong fixed data");
FURI_LOG_E(TAG, "Encode wrong fixed data");
return false;
}
+10 -1
View File
@@ -23,6 +23,7 @@ struct PipeSide {
PipeSideDataArrivedCallback on_data_arrived;
PipeSideSpaceFreedCallback on_space_freed;
PipeSideBrokenCallback on_pipe_broken;
FuriWait stdout_timeout;
};
PipeSideBundle pipe_alloc(size_t capacity, size_t trigger_level) {
@@ -52,12 +53,14 @@ PipeSideBundle pipe_alloc_ex(PipeSideReceiveSettings alice, PipeSideReceiveSetti
.shared = shared,
.sending = alice_to_bob,
.receiving = bob_to_alice,
.stdout_timeout = FuriWaitForever,
};
*bobs_side = (PipeSide){
.role = PipeRoleBob,
.shared = shared,
.sending = bob_to_alice,
.receiving = alice_to_bob,
.stdout_timeout = FuriWaitForever,
};
return (PipeSideBundle){.alices_side = alices_side, .bobs_side = bobs_side};
@@ -100,7 +103,8 @@ static void _pipe_stdout_cb(const char* data, size_t size, void* context) {
furi_assert(context);
PipeSide* pipe = context;
while(size) {
size_t sent = pipe_send(pipe, data, size, FuriWaitForever);
size_t sent = pipe_send(pipe, data, size, pipe->stdout_timeout);
if(!sent) break;
data += sent;
size -= sent;
}
@@ -118,6 +122,11 @@ void pipe_install_as_stdio(PipeSide* pipe) {
furi_thread_set_stdin_callback(_pipe_stdin_cb, pipe);
}
void pipe_set_stdout_timeout(PipeSide* pipe, FuriWait timeout) {
furi_check(pipe);
pipe->stdout_timeout = timeout;
}
size_t pipe_receive(PipeSide* pipe, void* data, size_t length, FuriWait timeout) {
furi_check(pipe);
return furi_stream_buffer_receive(pipe->receiving, data, length, timeout);
+10
View File
@@ -147,6 +147,16 @@ void pipe_free(PipeSide* pipe);
*/
void pipe_install_as_stdio(PipeSide* pipe);
/**
* @brief Sets the timeout for `stdout` write operations
*
* @note This value is set to `FuriWaitForever` when the pipe is created
*
* @param [in] pipe Pipe side to set the timeout of
* @param [in] timeout Timeout value in ticks
*/
void pipe_set_stdout_timeout(PipeSide* pipe, FuriWait timeout);
/**
* @brief Receives data from the pipe.
*
+1 -1
View File
@@ -35,7 +35,7 @@ class Main(App):
FLASH_BASE = 0x8000000
FLASH_PAGE_SIZE = 4 * 1024
MIN_GAP_PAGES = 2
MIN_GAP_PAGES = 1
# Update stage file larger than that is not loadable without fix
# https://github.com/flipperdevices/flipperzero-firmware/pull/3676
+9 -3
View File
@@ -1437,6 +1437,7 @@ Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle*
Function,+,furi_hal_serial_async_rx_available,_Bool,FuriHalSerialHandle*
Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool"
Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle*
Function,+,furi_hal_serial_configure_framing,void,"FuriHalSerialHandle*, FuriHalSerialDataBits, FuriHalSerialParity, FuriHalSerialStopBits"
Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId
Function,+,furi_hal_serial_control_deinit,void,
Function,+,furi_hal_serial_control_init,void,
@@ -1666,8 +1667,8 @@ Function,+,furi_thread_get_return_code,int32_t,FuriThread*
Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread*
Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId
Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
Function,+,furi_thread_get_stdin_callback,FuriThreadStdinReadCallback,
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
Function,+,furi_thread_get_stdin_callback,void,"FuriThreadStdinReadCallback*, void**"
Function,+,furi_thread_get_stdout_callback,void,"FuriThreadStdoutWriteCallback*, void**"
Function,+,furi_thread_is_suspended,_Bool,FuriThreadId
Function,+,furi_thread_join,_Bool,FuriThread*
Function,+,furi_thread_list_alloc,FuriThreadList*,
@@ -2333,6 +2334,7 @@ Function,+,pipe_set_broken_callback,void,"PipeSide*, PipeSideBrokenCallback, Fur
Function,+,pipe_set_callback_context,void,"PipeSide*, void*"
Function,+,pipe_set_data_arrived_callback,void,"PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent"
Function,+,pipe_set_space_freed_callback,void,"PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent"
Function,+,pipe_set_stdout_timeout,void,"PipeSide*, FuriWait"
Function,+,pipe_spaces_available,size_t,PipeSide*
Function,+,pipe_state,PipeState,PipeSide*
Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*"
@@ -2363,8 +2365,10 @@ Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool"
Function,+,power_get_info,void,"Power*, PowerInfo*"
Function,+,power_get_pubsub,FuriPubSub*,Power*
Function,+,power_is_battery_healthy,_Bool,Power*
Function,+,power_is_otg_enabled,_Bool,Power*
Function,+,power_off,void,Power*
Function,+,power_reboot,void,"Power*, PowerBootMode"
Function,+,power_enable_otg,void,"Power*, _Bool"
Function,+,powf,float,"float, float"
Function,-,powl,long double,"long double, long double"
Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t"
@@ -2897,8 +2901,10 @@ Function,-,vsscanf,int,"const char*, const char*, __gnuc_va_list"
Function,-,wcstombs,size_t,"char*, const wchar_t*, size_t"
Function,-,wctomb,int,"char*, wchar_t"
Function,+,widget_add_button_element,void,"Widget*, GuiButtonType, const char*, ButtonCallback, void*"
Function,+,widget_add_frame_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
Function,+,widget_add_circle_element,void,"Widget*, uint8_t, uint8_t, uint8_t, _Bool"
Function,+,widget_add_icon_element,void,"Widget*, uint8_t, uint8_t, const Icon*"
Function,+,widget_add_line_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t"
Function,+,widget_add_rect_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, _Bool"
Function,+,widget_add_string_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*"
Function,+,widget_add_string_multiline_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*"
Function,+,widget_add_text_box_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool"
1 entry status name type params
1437 Function + furi_hal_serial_async_rx_available _Bool FuriHalSerialHandle*
1438 Function + furi_hal_serial_async_rx_start void FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool
1439 Function + furi_hal_serial_async_rx_stop void FuriHalSerialHandle*
1440 Function + furi_hal_serial_configure_framing void FuriHalSerialHandle*, FuriHalSerialDataBits, FuriHalSerialParity, FuriHalSerialStopBits
1441 Function + furi_hal_serial_control_acquire FuriHalSerialHandle* FuriHalSerialId
1442 Function + furi_hal_serial_control_deinit void
1443 Function + furi_hal_serial_control_init void
1667 Function + furi_thread_get_signal_callback FuriThreadSignalCallback const FuriThread*
1668 Function + furi_thread_get_stack_space uint32_t FuriThreadId
1669 Function + furi_thread_get_state FuriThreadState FuriThread*
1670 Function + furi_thread_get_stdin_callback FuriThreadStdinReadCallback void FuriThreadStdinReadCallback*, void**
1671 Function + furi_thread_get_stdout_callback FuriThreadStdoutWriteCallback void FuriThreadStdoutWriteCallback*, void**
1672 Function + furi_thread_is_suspended _Bool FuriThreadId
1673 Function + furi_thread_join _Bool FuriThread*
1674 Function + furi_thread_list_alloc FuriThreadList*
2334 Function + pipe_set_callback_context void PipeSide*, void*
2335 Function + pipe_set_data_arrived_callback void PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent
2336 Function + pipe_set_space_freed_callback void PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent
2337 Function + pipe_set_stdout_timeout void PipeSide*, FuriWait
2338 Function + pipe_spaces_available size_t PipeSide*
2339 Function + pipe_state PipeState PipeSide*
2340 Function + plugin_manager_alloc PluginManager* const char*, uint32_t, const ElfApiInterface*
2365 Function + power_get_info void Power*, PowerInfo*
2366 Function + power_get_pubsub FuriPubSub* Power*
2367 Function + power_is_battery_healthy _Bool Power*
2368 Function + power_is_otg_enabled _Bool Power*
2369 Function + power_off void Power*
2370 Function + power_reboot void Power*, PowerBootMode
2371 Function + power_enable_otg void Power*, _Bool
2372 Function + powf float float, float
2373 Function - powl long double long double, long double
2374 Function + pretty_format_bytes_hex_canonical void FuriString*, size_t, const char*, const uint8_t*, size_t
2901 Function - wcstombs size_t char*, const wchar_t*, size_t
2902 Function - wctomb int char*, wchar_t
2903 Function + widget_add_button_element void Widget*, GuiButtonType, const char*, ButtonCallback, void*
2904 Function + widget_add_frame_element widget_add_circle_element void Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t Widget*, uint8_t, uint8_t, uint8_t, _Bool
2905 Function + widget_add_icon_element void Widget*, uint8_t, uint8_t, const Icon*
2906 Function + widget_add_line_element void Widget*, uint8_t, uint8_t, uint8_t, uint8_t
2907 Function + widget_add_rect_element void Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, _Bool
2908 Function + widget_add_string_element void Widget*, uint8_t, uint8_t, Align, Align, Font, const char*
2909 Function + widget_add_string_multiline_element void Widget*, uint8_t, uint8_t, Align, Align, Font, const char*
2910 Function + widget_add_text_box_element void Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool
+15 -8
View File
@@ -1097,6 +1097,7 @@ Function,+,felica_get_uid,const uint8_t*,"const FelicaData*, size_t*"
Function,+,felica_is_equal,_Bool,"const FelicaData*, const FelicaData*"
Function,+,felica_load,_Bool,"FelicaData*, FlipperFormat*, uint32_t"
Function,+,felica_poller_activate,FelicaError,"FelicaPoller*, FelicaData*"
Function,+,felica_poller_read_blocks,FelicaError,"FelicaPoller*, const uint8_t, const uint8_t*, uint16_t, FelicaPollerReadCommandResponse**"
Function,+,felica_poller_sync_read,FelicaError,"Nfc*, FelicaData*, const FelicaCardKey*"
Function,+,felica_reset,void,FelicaData*
Function,+,felica_save,_Bool,"const FelicaData*, FlipperFormat*"
@@ -1681,6 +1682,7 @@ Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle*
Function,+,furi_hal_serial_async_rx_available,_Bool,FuriHalSerialHandle*
Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool"
Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle*
Function,+,furi_hal_serial_configure_framing,void,"FuriHalSerialHandle*, FuriHalSerialDataBits, FuriHalSerialParity, FuriHalSerialStopBits"
Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId
Function,+,furi_hal_serial_control_deinit,void,
Function,+,furi_hal_serial_control_init,void,
@@ -1949,8 +1951,8 @@ Function,+,furi_thread_get_return_code,int32_t,FuriThread*
Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread*
Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId
Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
Function,+,furi_thread_get_stdin_callback,FuriThreadStdinReadCallback,
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
Function,+,furi_thread_get_stdin_callback,void,"FuriThreadStdinReadCallback*, void**"
Function,+,furi_thread_get_stdout_callback,void,"FuriThreadStdoutWriteCallback*, void**"
Function,+,furi_thread_is_suspended,_Bool,FuriThreadId
Function,+,furi_thread_join,_Bool,FuriThread*
Function,+,furi_thread_list_alloc,FuriThreadList*,
@@ -3048,6 +3050,7 @@ Function,+,pipe_set_broken_callback,void,"PipeSide*, PipeSideBrokenCallback, Fur
Function,+,pipe_set_callback_context,void,"PipeSide*, void*"
Function,+,pipe_set_data_arrived_callback,void,"PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent"
Function,+,pipe_set_space_freed_callback,void,"PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent"
Function,+,pipe_set_stdout_timeout,void,"PipeSide*, FuriWait"
Function,+,pipe_spaces_available,size_t,PipeSide*
Function,+,pipe_state,PipeState,PipeSide*
Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*"
@@ -3075,9 +3078,11 @@ Function,-,pow,double,"double, double"
Function,-,pow10,double,double
Function,-,pow10f,float,float
Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool"
Function,+,power_enable_otg,void,"Power*, _Bool"
Function,+,power_get_info,void,"Power*, PowerInfo*"
Function,+,power_get_pubsub,FuriPubSub*,Power*
Function,+,power_is_battery_healthy,_Bool,Power*
Function,+,power_is_otg_enabled,_Bool,Power*
Function,+,power_off,void,Power*
Function,+,power_reboot,void,"Power*, PowerBootMode"
Function,+,powf,float,"float, float"
@@ -3886,13 +3891,15 @@ Function,-,vsprintf,int,"char*, const char*, __gnuc_va_list"
Function,-,vsscanf,int,"const char*, const char*, __gnuc_va_list"
Function,-,wcstombs,size_t,"char*, const wchar_t*, size_t"
Function,-,wctomb,int,"char*, wchar_t"
Function,+,widget_add_button_element,WidgetElement*,"Widget*, GuiButtonType, const char*, ButtonCallback, void*"
Function,+,widget_add_frame_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
Function,+,widget_add_icon_element,WidgetElement*,"Widget*, uint8_t, uint8_t, const Icon*"
Function,+,widget_add_string_element,WidgetElement*,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*"
Function,+,widget_add_string_multiline_element,WidgetElement*,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*"
Function,+,widget_add_button_element,void,"Widget*, GuiButtonType, const char*, ButtonCallback, void*"
Function,+,widget_add_circle_element,void,"Widget*, uint8_t, uint8_t, uint8_t, _Bool"
Function,+,widget_add_icon_element,void,"Widget*, uint8_t, uint8_t, const Icon*"
Function,+,widget_add_line_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t"
Function,+,widget_add_rect_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, _Bool"
Function,+,widget_add_string_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*"
Function,+,widget_add_string_multiline_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*"
Function,+,widget_add_text_box_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool"
Function,+,widget_add_text_scroll_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*"
Function,+,widget_add_text_scroll_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*"
Function,+,widget_alloc,Widget*,
Function,+,widget_free,void,Widget*
Function,+,widget_get_view,View*,Widget*
1 entry status name type params
1097 Function + felica_is_equal _Bool const FelicaData*, const FelicaData*
1098 Function + felica_load _Bool FelicaData*, FlipperFormat*, uint32_t
1099 Function + felica_poller_activate FelicaError FelicaPoller*, FelicaData*
1100 Function + felica_poller_read_blocks FelicaError FelicaPoller*, const uint8_t, const uint8_t*, uint16_t, FelicaPollerReadCommandResponse**
1101 Function + felica_poller_sync_read FelicaError Nfc*, FelicaData*, const FelicaCardKey*
1102 Function + felica_reset void FelicaData*
1103 Function + felica_save _Bool const FelicaData*, FlipperFormat*
1682 Function + furi_hal_serial_async_rx_available _Bool FuriHalSerialHandle*
1683 Function + furi_hal_serial_async_rx_start void FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool
1684 Function + furi_hal_serial_async_rx_stop void FuriHalSerialHandle*
1685 Function + furi_hal_serial_configure_framing void FuriHalSerialHandle*, FuriHalSerialDataBits, FuriHalSerialParity, FuriHalSerialStopBits
1686 Function + furi_hal_serial_control_acquire FuriHalSerialHandle* FuriHalSerialId
1687 Function + furi_hal_serial_control_deinit void
1688 Function + furi_hal_serial_control_init void
1951 Function + furi_thread_get_signal_callback FuriThreadSignalCallback const FuriThread*
1952 Function + furi_thread_get_stack_space uint32_t FuriThreadId
1953 Function + furi_thread_get_state FuriThreadState FuriThread*
1954 Function + furi_thread_get_stdin_callback FuriThreadStdinReadCallback void FuriThreadStdinReadCallback*, void**
1955 Function + furi_thread_get_stdout_callback FuriThreadStdoutWriteCallback void FuriThreadStdoutWriteCallback*, void**
1956 Function + furi_thread_is_suspended _Bool FuriThreadId
1957 Function + furi_thread_join _Bool FuriThread*
1958 Function + furi_thread_list_alloc FuriThreadList*
3050 Function + pipe_set_callback_context void PipeSide*, void*
3051 Function + pipe_set_data_arrived_callback void PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent
3052 Function + pipe_set_space_freed_callback void PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent
3053 Function + pipe_set_stdout_timeout void PipeSide*, FuriWait
3054 Function + pipe_spaces_available size_t PipeSide*
3055 Function + pipe_state PipeState PipeSide*
3056 Function + plugin_manager_alloc PluginManager* const char*, uint32_t, const ElfApiInterface*
3078 Function - pow10 double double
3079 Function - pow10f float float
3080 Function + power_enable_low_battery_level_notification void Power*, _Bool
3081 Function + power_enable_otg void Power*, _Bool
3082 Function + power_get_info void Power*, PowerInfo*
3083 Function + power_get_pubsub FuriPubSub* Power*
3084 Function + power_is_battery_healthy _Bool Power*
3085 Function + power_is_otg_enabled _Bool Power*
3086 Function + power_off void Power*
3087 Function + power_reboot void Power*, PowerBootMode
3088 Function + powf float float, float
3891 Function - vsscanf int const char*, const char*, __gnuc_va_list
3892 Function - wcstombs size_t char*, const wchar_t*, size_t
3893 Function - wctomb int char*, wchar_t
3894 Function + widget_add_button_element WidgetElement* void Widget*, GuiButtonType, const char*, ButtonCallback, void*
3895 Function + widget_add_frame_element widget_add_circle_element WidgetElement* void Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t Widget*, uint8_t, uint8_t, uint8_t, _Bool
3896 Function + widget_add_icon_element WidgetElement* void Widget*, uint8_t, uint8_t, const Icon*
3897 Function + widget_add_string_element widget_add_line_element WidgetElement* void Widget*, uint8_t, uint8_t, Align, Align, Font, const char* Widget*, uint8_t, uint8_t, uint8_t, uint8_t
3898 Function + widget_add_string_multiline_element widget_add_rect_element WidgetElement* void Widget*, uint8_t, uint8_t, Align, Align, Font, const char* Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, _Bool
3899 Function + widget_add_string_element void Widget*, uint8_t, uint8_t, Align, Align, Font, const char*
3900 Function + widget_add_string_multiline_element void Widget*, uint8_t, uint8_t, Align, Align, Font, const char*
3901 Function + widget_add_text_box_element WidgetElement* Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool
3902 Function + widget_add_text_scroll_element WidgetElement* void Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*
3903 Function + widget_alloc Widget*
3904 Function + widget_free void Widget*
3905 Function + widget_get_view View* Widget*
+16 -16
View File
@@ -258,85 +258,85 @@ FURI_ALWAYS_INLINE static void furi_hal_gpio_int_call(uint16_t pin_num) {
/* Interrupt handlers */
void EXTI0_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)) {
furi_hal_gpio_int_call(0);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0);
furi_hal_gpio_int_call(0);
}
}
void EXTI1_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_1)) {
furi_hal_gpio_int_call(1);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1);
furi_hal_gpio_int_call(1);
}
}
void EXTI2_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_2)) {
furi_hal_gpio_int_call(2);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2);
furi_hal_gpio_int_call(2);
}
}
void EXTI3_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) {
furi_hal_gpio_int_call(3);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3);
furi_hal_gpio_int_call(3);
}
}
void EXTI4_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_4)) {
furi_hal_gpio_int_call(4);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4);
furi_hal_gpio_int_call(4);
}
}
void EXTI9_5_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_5)) {
furi_hal_gpio_int_call(5);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5);
furi_hal_gpio_int_call(5);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_6)) {
furi_hal_gpio_int_call(6);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6);
furi_hal_gpio_int_call(6);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_7)) {
furi_hal_gpio_int_call(7);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7);
furi_hal_gpio_int_call(7);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_8)) {
furi_hal_gpio_int_call(8);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8);
furi_hal_gpio_int_call(8);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_9)) {
furi_hal_gpio_int_call(9);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9);
furi_hal_gpio_int_call(9);
}
}
void EXTI15_10_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_10)) {
furi_hal_gpio_int_call(10);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10);
furi_hal_gpio_int_call(10);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_11)) {
furi_hal_gpio_int_call(11);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11);
furi_hal_gpio_int_call(11);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_12)) {
furi_hal_gpio_int_call(12);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12);
furi_hal_gpio_int_call(12);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13)) {
furi_hal_gpio_int_call(13);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13);
furi_hal_gpio_int_call(13);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14)) {
furi_hal_gpio_int_call(14);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14);
furi_hal_gpio_int_call(14);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_15)) {
furi_hal_gpio_int_call(15);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15);
furi_hal_gpio_int_call(15);
}
}
+88 -2
View File
@@ -120,7 +120,7 @@ static void furi_hal_serial_usart_irq_callback(void* context) {
}
if(USART1->ISR & USART_ISR_PE) {
USART1->ICR = USART_ICR_PECF;
event |= FuriHalSerialRxEventFrameError;
event |= FuriHalSerialRxEventParityError;
}
if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL) {
@@ -321,7 +321,7 @@ static void furi_hal_serial_lpuart_irq_callback(void* context) {
}
if(LPUART1->ISR & USART_ISR_PE) {
LPUART1->ICR = USART_ICR_PECF;
event |= FuriHalSerialRxEventFrameError;
event |= FuriHalSerialRxEventParityError;
}
if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL) {
@@ -605,6 +605,92 @@ void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud) {
}
}
// Avoid duplicating look-up tables between USART and LPUART
static_assert(LL_LPUART_DATAWIDTH_7B == LL_USART_DATAWIDTH_7B);
static_assert(LL_LPUART_DATAWIDTH_8B == LL_USART_DATAWIDTH_8B);
static_assert(LL_LPUART_DATAWIDTH_9B == LL_USART_DATAWIDTH_9B);
static_assert(LL_LPUART_PARITY_NONE == LL_USART_PARITY_NONE);
static_assert(LL_LPUART_PARITY_EVEN == LL_USART_PARITY_EVEN);
static_assert(LL_LPUART_PARITY_ODD == LL_USART_PARITY_ODD);
static_assert(LL_LPUART_STOPBITS_1 == LL_USART_STOPBITS_1);
static_assert(LL_LPUART_STOPBITS_2 == LL_USART_STOPBITS_2);
static const uint32_t serial_data_bits_lut[] = {
[FuriHalSerialDataBits7] = LL_USART_DATAWIDTH_7B,
[FuriHalSerialDataBits8] = LL_USART_DATAWIDTH_8B,
[FuriHalSerialDataBits9] = LL_USART_DATAWIDTH_9B,
};
static const uint32_t serial_parity_lut[] = {
[FuriHalSerialParityNone] = LL_USART_PARITY_NONE,
[FuriHalSerialParityEven] = LL_USART_PARITY_EVEN,
[FuriHalSerialParityOdd] = LL_USART_PARITY_ODD,
};
static const uint32_t serial_stop_bits_lut[] = {
[FuriHalSerialStopBits0_5] = LL_USART_STOPBITS_0_5,
[FuriHalSerialStopBits1] = LL_USART_STOPBITS_1,
[FuriHalSerialStopBits1_5] = LL_USART_STOPBITS_1_5,
[FuriHalSerialStopBits2] = LL_USART_STOPBITS_2,
};
static void furi_hal_serial_usart_configure_framing(
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits) {
LL_USART_SetDataWidth(USART1, serial_data_bits_lut[data_bits]);
LL_USART_SetParity(USART1, serial_parity_lut[parity]);
LL_USART_SetStopBitsLength(USART1, serial_stop_bits_lut[stop_bits]);
}
static void furi_hal_serial_lpuart_configure_framing(
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits) {
LL_LPUART_SetDataWidth(LPUART1, serial_data_bits_lut[data_bits]);
LL_LPUART_SetParity(LPUART1, serial_parity_lut[parity]);
// Unsupported non-whole stop bit numbers have been furi_check'ed away
LL_LPUART_SetStopBitsLength(LPUART1, serial_stop_bits_lut[stop_bits]);
}
void furi_hal_serial_configure_framing(
FuriHalSerialHandle* handle,
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits) {
furi_check(handle);
// Unsupported combinations
if(data_bits == FuriHalSerialDataBits9) furi_check(parity == FuriHalSerialParityNone);
if(data_bits == FuriHalSerialDataBits6) furi_check(parity != FuriHalSerialParityNone);
// Extend data word to account for parity bit
if(parity != FuriHalSerialParityNone) data_bits++;
if(handle->id == FuriHalSerialIdUsart) {
if(LL_USART_IsEnabled(USART1)) {
// Wait for transfer complete flag
while(!LL_USART_IsActiveFlag_TC(USART1))
;
LL_USART_Disable(USART1);
furi_hal_serial_usart_configure_framing(data_bits, parity, stop_bits);
LL_USART_Enable(USART1);
}
} else if(handle->id == FuriHalSerialIdLpuart) {
// Unsupported configurations
furi_check(stop_bits == FuriHalSerialStopBits1 || stop_bits == FuriHalSerialStopBits2);
if(LL_LPUART_IsEnabled(LPUART1)) {
// Wait for transfer complete flag
while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
;
LL_LPUART_Disable(LPUART1);
furi_hal_serial_lpuart_configure_framing(data_bits, parity, stop_bits);
LL_LPUART_Enable(LPUART1);
}
}
}
void furi_hal_serial_deinit(FuriHalSerialHandle* handle) {
furi_check(handle);
furi_hal_serial_async_rx_configure(handle, NULL, NULL);
+20 -3
View File
@@ -16,7 +16,9 @@ extern "C" {
/** Initialize Serial
*
* Configures GPIO, configures and enables transceiver.
* Configures GPIO, configures and enables transceiver. Default framing settings
* are used: 8 data bits, no parity, 1 stop bit. Override them with
* `furi_hal_serial_configure_framing`.
*
* @param handle Serial handle
* @param baud baud rate
@@ -64,6 +66,20 @@ bool furi_hal_serial_is_baud_rate_supported(FuriHalSerialHandle* handle, uint32_
*/
void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud);
/**
* @brief Configures framing of a serial interface
*
* @param handle Serial handle
* @param data_bits Data bits
* @param parity Parity
* @param stop_bits Stop bits
*/
void furi_hal_serial_configure_framing(
FuriHalSerialHandle* handle,
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits);
/** Transmits data in semi-blocking mode
*
* Fills transmission pipe with data, returns as soon as all bytes from buffer
@@ -93,6 +109,7 @@ typedef enum {
FuriHalSerialRxEventFrameError = (1 << 2), /**< Framing Error: incorrect frame detected */
FuriHalSerialRxEventNoiseError = (1 << 3), /**< Noise Error: noise on the line detected */
FuriHalSerialRxEventOverrunError = (1 << 4), /**< Overrun Error: no space for received data */
FuriHalSerialRxEventParityError = (1 << 5), /**< Parity Error: incorrect parity bit received */
} FuriHalSerialRxEvent;
/** Receive callback
@@ -172,7 +189,7 @@ typedef void (*FuriHalSerialDmaRxCallback)(
void* context);
/**
* @brief Enable an input/output directon
* @brief Enable an input/output direction
*
* Takes over the respective pin by reconfiguring it to
* the appropriate alternative function.
@@ -185,7 +202,7 @@ void furi_hal_serial_enable_direction(
FuriHalSerialDirection direction);
/**
* @brief Disable an input/output directon
* @brief Disable an input/output direction
*
* Releases the respective pin by reconfiguring it to
* initial state, making possible its use for other purposes.
@@ -19,4 +19,39 @@ typedef enum {
FuriHalSerialDirectionMax,
} FuriHalSerialDirection;
/**
* @brief Actual data bits, i.e. not including start/stop and parity bits
* @note 6 data bits are only permitted when parity is enabled
* @note 9 data bits are only permitted when parity is disabled
*/
typedef enum {
FuriHalSerialDataBits6,
FuriHalSerialDataBits7,
FuriHalSerialDataBits8,
FuriHalSerialDataBits9,
FuriHalSerialDataBitsMax,
} FuriHalSerialDataBits;
typedef enum {
FuriHalSerialParityNone,
FuriHalSerialParityEven,
FuriHalSerialParityOdd,
FuriHalSerialParityMax,
} FuriHalSerialParity;
/**
* @brief Stop bit length
* @note LPUART only supports whole stop bit lengths (i.e. 1 and 2, but not 0.5 and 1.5)
*/
typedef enum {
FuriHalSerialStopBits0_5,
FuriHalSerialStopBits1,
FuriHalSerialStopBits1_5,
FuriHalSerialStopBits2,
FuriHalSerialStopBits2Max,
} FuriHalSerialStopBits;
typedef struct FuriHalSerialHandle FuriHalSerialHandle;
+6 -3
View File
@@ -11,13 +11,16 @@
#endif /* CMSIS_device_header */
#include CMSIS_device_header
#include <stm32wb55_linker.h>
#define configENABLE_FPU 1
#define configENABLE_MPU 0
#define configUSE_PREEMPTION 1
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configENABLE_HEAP_PROTECTOR 1
#define configHEAP_CLEAR_MEMORY_ON_FREE 1
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
@@ -30,7 +33,7 @@
#define configUSE_POSIX_ERRNO 1
/* Heap size determined automatically by linker */
// #define configTOTAL_HEAP_SIZE ((size_t)0)
#define configTOTAL_HEAP_SIZE ((uint32_t) & __heap_end__ - (uint32_t) & __heap_start__)
#define configMAX_TASK_NAME_LEN (32)
#define configGENERATE_RUN_TIME_STATS 1
@@ -146,7 +149,7 @@ standard names. */
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#ifdef DEBUG
#ifdef FURI_DEBUG
#define configASSERT(x) \
if((x) == 0) { \
furi_crash("FreeRTOS Assert"); \
+15 -12
View File
@@ -9,25 +9,28 @@
#ifdef __cplusplus
extern "C" {
typedef const char linker_symbol_t;
#else
typedef const void linker_symbol_t;
#endif
extern const void _stack_end; /**< end of stack */
extern const void _stack_size; /**< stack size */
extern linker_symbol_t _stack_end; /**< end of stack */
extern linker_symbol_t _stack_size; /**< stack size */
extern const void _sidata; /**< data initial value start */
extern const void _sdata; /**< data start */
extern const void _edata; /**< data end */
extern linker_symbol_t _sidata; /**< data initial value start */
extern linker_symbol_t _sdata; /**< data start */
extern linker_symbol_t _edata; /**< data end */
extern const void _sbss; /**< bss start */
extern const void _ebss; /**< bss end */
extern linker_symbol_t _sbss; /**< bss start */
extern linker_symbol_t _ebss; /**< bss end */
extern const void _sMB_MEM2; /**< RAM2a start */
extern const void _eMB_MEM2; /**< RAM2a end */
extern linker_symbol_t _sMB_MEM2; /**< RAM2a start */
extern linker_symbol_t _eMB_MEM2; /**< RAM2a end */
extern const void __heap_start__; /**< RAM1 Heap start */
extern const void __heap_end__; /**< RAM1 Heap end */
extern linker_symbol_t __heap_start__; /**< RAM1 Heap start */
extern linker_symbol_t __heap_end__; /**< RAM1 Heap end */
extern const void __free_flash_start__; /**< Free Flash space start */
extern linker_symbol_t __free_flash_start__; /**< Free Flash space start */
#ifdef __cplusplus
}
@@ -105,10 +105,14 @@ void furi_hal_power_off(void);
FURI_NORETURN void furi_hal_power_reset(void);
/** OTG enable
*
* @warning this is low level control, use power service instead
*/
bool furi_hal_power_enable_otg(void);
/** OTG disable
*
* @warning this is low level control, use power service instead
*/
void furi_hal_power_disable_otg(void);