mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge remote-tracking branch 'ofw/dev' into mntm-dev
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user