mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-07 19:01:54 -07:00
Merge remote-tracking branch 'ofw/dev' into mntm-dev
This commit is contained in:
+19
-5
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
+1
-1
Submodule applications/external updated: c780ecbb00...dbfac79c88
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -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();
|
||||
}
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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); \
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
@@ -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*
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"); \
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user