Merge branch 'dev' into zlo/tlsf-and-a-temple-of-memcorrupt

This commit is contained in:
SG
2024-05-31 13:41:44 +01:00
111 changed files with 2631 additions and 536 deletions
+1 -1
View File
@@ -79,7 +79,7 @@
"label": "[Debug:unit_tests] Flash (USB)",
"group": "build",
"type": "shell",
"command": "./fbt FIRMWARE_APP_SET=unit_tests FORCE=1 flash_usb"
"command": "./fbt FIRMWARE_APP_SET=unit_tests FORCE=1 flash_usb_full"
},
{
"label": "[Debug] Flash (USB, with resources)",
@@ -34,12 +34,16 @@ void AccessorApp::run(void) {
AccessorApp::AccessorApp()
: text_store{0} {
notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));
expansion = static_cast<Expansion*>(furi_record_open(RECORD_EXPANSION));
onewire_host = onewire_host_alloc(&gpio_ibutton);
expansion_disable(expansion);
furi_hal_power_enable_otg();
}
AccessorApp::~AccessorApp() {
furi_hal_power_disable_otg();
expansion_enable(expansion);
furi_record_close(RECORD_EXPANSION);
furi_record_close(RECORD_NOTIFICATION);
onewire_host_free(onewire_host);
}
@@ -6,6 +6,7 @@
#include "helpers/wiegand.h"
#include <one_wire/one_wire_host.h>
#include <notification/notification_messages.h>
#include <expansion/expansion.h>
class AccessorApp {
public:
@@ -51,4 +52,5 @@ private:
OneWireHost* onewire_host;
NotificationApp* notification;
Expansion* expansion;
};
@@ -0,0 +1,10 @@
App(
appid="text_box_element_test",
name="Text Box Element Test",
apptype=FlipperAppType.DEBUG,
entry_point="text_box_element_test_app",
requires=["gui"],
stack_size=1 * 1024,
order=140,
fap_category="Debug",
)
@@ -71,7 +71,7 @@ static void text_box_test_input_callback(InputEvent* input_event, void* ctx) {
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
}
int32_t text_box_test_app(void* p) {
int32_t text_box_element_test_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
furi_check(event_queue);
@@ -1,8 +1,8 @@
App(
appid="text_box_test",
name="Text Box Test",
appid="text_box_view_test",
name="Text Box View Test",
apptype=FlipperAppType.DEBUG,
entry_point="text_box_test_app",
entry_point="text_box_view_test_app",
requires=["gui"],
stack_size=1 * 1024,
order=140,
@@ -0,0 +1,161 @@
#include <furi.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/text_box.h>
#include <gui/view_stack.h>
#define TAG "TextBoxViewTest"
typedef struct {
TextBoxFont font;
TextBoxFocus focus;
const char* text;
} TextBoxViewTestContent;
static const TextBoxViewTestContent text_box_view_test_content_arr[] = {
{
.font = TextBoxFontText,
.focus = TextBoxFocusStart,
.text = "Hello, let's test text box. Press Right and Left to switch content",
},
{
.font = TextBoxFontText,
.focus = TextBoxFocusEnd,
.text = "First test to add dynamically lines with EndFocus set\nLine 0",
},
{
.font = TextBoxFontText,
.focus = TextBoxFocusEnd,
.text = "First test to add dynamically lines with EndFocus set\nLine 0\nLine 1",
},
{
.font = TextBoxFontText,
.focus = TextBoxFocusEnd,
.text = "First test to add dynamically lines with EndFocus set\nLine 0\nLine 1\nLine 2",
},
{
.font = TextBoxFontText,
.focus = TextBoxFocusEnd,
.text =
"First test to add dynamically lines with EndFocus set\nLine 0\nLine 1\nLine 2\nLine 3",
},
{
.font = TextBoxFontText,
.focus = TextBoxFocusEnd,
.text =
"First test to add dynamically lines with EndFocus set\nLine 0\nLine 1\nLine 2\nLine 3\nLine 4",
},
{
.font = TextBoxFontText,
.focus = TextBoxFocusStart,
.text =
"Verify that symbols don't overlap borders: llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllend",
},
{
.font = TextBoxFontText,
.focus = TextBoxFocusStart,
.text =
"\n\n\n Start from several newline chars. Verify that scrolling doesn't break.\n\n\n\n\nThe end",
},
{
.font = TextBoxFontText,
.focus = TextBoxFocusStart,
.text =
"Let's test big text.\n\n The ARM Cortex-M is a group of 32-bit RISC ARM processor cores licensed by ARM Limited. These cores are optimized for low-cost and energy-efficient integrated circuits, which have been embedded in tens of billions of consumer devices.[1] Though they are most often the main component of microcontroller chips, sometimes they are embedded inside other types of chips too. The Cortex-M family consists of Cortex-M0,[2] Cortex-M0+,[3] Cortex-M1,[4] Cortex-M3,[5] Cortex-M4,[6] Cortex-M7,[7] Cortex-M23,[8] Cortex-M33,[9] Cortex-M35P,[10] Cortex-M52,[11] Cortex-M55,[12] Cortex-M85.[13] A floating-point unit (FPU) option is available for Cortex-M4 / M7 / M33 / M35P / M52 / M55 / M85 cores, and when included in the silicon these cores are sometimes known as \"Cortex-MxF\", where 'x' is the core variant.\n\nThe ARM Cortex-M family are ARM microprocessor cores that are designed for use in microcontrollers, ASICs, ASSPs, FPGAs, and SoCs. Cortex-M cores are commonly used as dedicated microcontroller chips, but also are hidden inside of SoC chips as power management controllers, I/O controllers, system controllers, touch screen controllers, smart battery controllers, and sensor controllers. The main difference from Cortex-A cores is that Cortex-M cores have no memory management unit (MMU) for virtual memory, considered essential for full-fledged operating systems. Cortex-M programs instead run bare metal or on one of the many real-time operating systems which support a Cortex-M.Though 8-bit microcontrollers were very popular in the past, Cortex-M has slowly been chipping away at the 8-bit market as the prices of low-end Cortex-M chips have moved downward. Cortex-M have become a popular replacements for 8-bit chips in applications that benefit from 32-bit math operations, and replacing older legacy ARM cores such as ARM7 and ARM9.",
},
{
.font = TextBoxFontText,
.focus = TextBoxFocusEnd,
.text =
"The same but with EndFocus\n\n The ARM Cortex-M is a group of 32-bit RISC ARM processor cores licensed by ARM Limited. These cores are optimized for low-cost and energy-efficient integrated circuits, which have been embedded in tens of billions of consumer devices.[1] Though they are most often the main component of microcontroller chips, sometimes they are embedded inside other types of chips too. The Cortex-M family consists of Cortex-M0,[2] Cortex-M0+,[3] Cortex-M1,[4] Cortex-M3,[5] Cortex-M4,[6] Cortex-M7,[7] Cortex-M23,[8] Cortex-M33,[9] Cortex-M35P,[10] Cortex-M52,[11] Cortex-M55,[12] Cortex-M85.[13] A floating-point unit (FPU) option is available for Cortex-M4 / M7 / M33 / M35P / M52 / M55 / M85 cores, and when included in the silicon these cores are sometimes known as \"Cortex-MxF\", where 'x' is the core variant.\n\nThe ARM Cortex-M family are ARM microprocessor cores that are designed for use in microcontrollers, ASICs, ASSPs, FPGAs, and SoCs. Cortex-M cores are commonly used as dedicated microcontroller chips, but also are hidden inside of SoC chips as power management controllers, I/O controllers, system controllers, touch screen controllers, smart battery controllers, and sensor controllers. The main difference from Cortex-A cores is that Cortex-M cores have no memory management unit (MMU) for virtual memory, considered essential for full-fledged operating systems. Cortex-M programs instead run bare metal or on one of the many real-time operating systems which support a Cortex-M.Though 8-bit microcontrollers were very popular in the past, Cortex-M has slowly been chipping away at the 8-bit market as the prices of low-end Cortex-M chips have moved downward. Cortex-M have become a popular replacements for 8-bit chips in applications that benefit from 32-bit math operations, and replacing older legacy ARM cores such as ARM7 and ARM9.",
},
{
.font = TextBoxFontHex,
.focus = TextBoxFocusEnd,
.text =
"0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999",
},
};
typedef struct {
TextBox* text_box;
ViewDispatcher* view_dispatcher;
size_t current_content_i;
} TextBoxViewTest;
static void text_box_update_view(TextBoxViewTest* instance) {
// Intentional incorrect way to reset text box to verify that state resets if text changes
text_box_set_text(instance->text_box, "");
const TextBoxViewTestContent* content =
&text_box_view_test_content_arr[instance->current_content_i];
text_box_set_font(instance->text_box, content->font);
text_box_set_focus(instance->text_box, content->focus);
text_box_set_text(instance->text_box, content->text);
}
static bool text_box_switch_view_input_callback(InputEvent* event, void* context) {
bool consumed = false;
TextBoxViewTest* instance = context;
size_t contents_cnt = COUNT_OF(text_box_view_test_content_arr);
if(event->type == InputTypeShort) {
if(event->key == InputKeyRight) {
if(instance->current_content_i < contents_cnt - 1) {
instance->current_content_i++;
text_box_update_view(instance);
consumed = true;
}
} else if(event->key == InputKeyLeft) {
if(instance->current_content_i > 0) {
instance->current_content_i--;
text_box_update_view(instance);
consumed = true;
}
} else if(event->key == InputKeyBack) {
view_dispatcher_stop(instance->view_dispatcher);
}
}
return consumed;
}
int32_t text_box_view_test_app(void* p) {
UNUSED(p);
Gui* gui = furi_record_open(RECORD_GUI);
ViewDispatcher* view_dispatcher = view_dispatcher_alloc();
view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
view_dispatcher_enable_queue(view_dispatcher);
TextBoxViewTest instance = {
.text_box = text_box_alloc(),
.current_content_i = 0,
.view_dispatcher = view_dispatcher,
};
text_box_update_view(&instance);
View* text_box_switch_view = view_alloc();
view_set_input_callback(text_box_switch_view, text_box_switch_view_input_callback);
view_set_context(text_box_switch_view, &instance);
ViewStack* view_stack = view_stack_alloc();
view_stack_add_view(view_stack, text_box_switch_view);
view_stack_add_view(view_stack, text_box_get_view(instance.text_box));
view_dispatcher_add_view(view_dispatcher, 0, view_stack_get_view(view_stack));
view_dispatcher_switch_to_view(view_dispatcher, 0);
view_dispatcher_run(view_dispatcher);
view_dispatcher_remove_view(view_dispatcher, 0);
view_dispatcher_free(view_dispatcher);
view_stack_free(view_stack);
view_free(text_box_switch_view);
text_box_free(instance.text_box);
furi_record_close(RECORD_GUI);
return 0;
}
+203 -1
View File
@@ -2,8 +2,9 @@ App(
appid="unit_tests",
apptype=FlipperAppType.STARTUP,
entry_point="unit_tests_on_system_start",
sources=["unit_tests.c", "test_runner.c", "unit_test_api_table.cpp"],
cdefines=["APP_UNIT_TESTS"],
requires=["system_settings"],
requires=["system_settings", "subghz_start"],
provides=["delay_test"],
resources="resources",
order=100,
@@ -12,9 +13,210 @@ App(
App(
appid="delay_test",
name="Delay Test",
sources=["tests/common/*.c", "tests/rpc/*.c"],
apptype=FlipperAppType.SYSTEM,
entry_point="delay_test_app",
stack_size=1 * 1024,
requires=["unit_tests"],
order=110,
)
App(
appid="test_varint",
sources=["tests/common/*.c", "tests/varint/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_furi",
sources=["tests/common/*.c", "tests/furi/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_furi_hal",
sources=["tests/common/*.c", "tests/furi_hal/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_furi_hal_crypto",
sources=["tests/common/*.c", "tests/furi_hal_crypto/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_furi_string",
sources=["tests/common/*.c", "tests/furi_string/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_storage",
sources=["tests/common/*.c", "tests/storage/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_stream",
sources=["tests/common/*.c", "tests/stream/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_dirwalk",
sources=["tests/common/*.c", "tests/dirwalk/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_manifest",
sources=["tests/common/*.c", "tests/manifest/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_flipper_format",
sources=["tests/common/*.c", "tests/flipper_format/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_flipper_format_string",
sources=["tests/common/*.c", "tests/flipper_format_string/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_rpc",
sources=["tests/common/*.c", "tests/rpc/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_subghz",
sources=["tests/common/*.c", "tests/subghz/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_infrared",
sources=["tests/common/*.c", "tests/infrared/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_nfc",
sources=["tests/common/*.c", "tests/nfc/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_power",
sources=["tests/common/*.c", "tests/power/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_protocol_dict",
sources=["tests/common/*.c", "tests/protocol_dict/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_lfrfid",
sources=["tests/common/*.c", "tests/lfrfid/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_bit_lib",
sources=["tests/common/*.c", "tests/bit_lib/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_datetime",
sources=["tests/common/*.c", "tests/datetime/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_float_tools",
sources=["tests/common/*.c", "tests/float_tools/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_bt",
sources=["tests/common/*.c", "tests/bt/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_dialogs_file_browser_options",
sources=["tests/common/*.c", "tests/dialogs_file_browser_options/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_expansion",
sources=["tests/common/*.c", "tests/expansion/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
App(
appid="test_compress",
sources=["tests/common/*.c", "tests/compress/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)
@@ -0,0 +1,41 @@
Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB
Device type: SLIX
# UID is common for all formats
UID: E0 04 01 08 49 D0 DC 81
# ISO15693-3 specific data
# Data Storage Format Identifier
DSFID: 01
# Application Family Identifier
AFI: 3D
# IC Reference - Vendor specific meaning
IC Reference: 01
# Lock Bits
Lock DSFID: true
Lock AFI: true
# Number of memory blocks, valid range = 1..256
Block Count: 80
# Size of a single memory block, valid range = 01...20 (hex)
Block Size: 04
Data Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01
# Block Security Status: 01 = locked, 00 = not locked
Security Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# SLIX specific data
# SLIX capabilities field affects emulation modes. Possible options: Default, AcceptAllPasswords
Capabilities: AcceptAllPasswords
# Passwords are optional. If a password is omitted, a default value will be used
Password Read: 00 00 00 00
Password Write: 00 00 00 00
Password Privacy: 0F 0F 0F 0F
Password Destroy: 0F 0F 0F 0F
Password EAS: 00 00 00 00
# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.
Signature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D
Privacy Mode: false
# Protection pointer configuration
Protection Pointer: 32
Protection Condition: 02
# SLIX Lock Bits
Lock EAS: true
Lock PPL: true
@@ -0,0 +1,41 @@
Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB
Device type: SLIX
# UID is common for all formats
UID: E0 04 01 08 49 D0 DC 81
# ISO15693-3 specific data
# Data Storage Format Identifier
DSFID: 01
# Application Family Identifier
AFI: 3D
# IC Reference - Vendor specific meaning
IC Reference: 01
# Lock Bits
Lock DSFID: true
Lock AFI: true
# Number of memory blocks, valid range = 1..256
Block Count: 80
# Size of a single memory block, valid range = 01...20 (hex)
Block Size: 04
Data Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01
# Block Security Status: 01 = locked, 00 = not locked
Security Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# SLIX specific data
# SLIX capabilities field affects emulation modes. Possible options: Default, AcceptAllPasswords
Capabilities: Default
# Passwords are optional. If a password is omitted, a default value will be used
Password Read: 00 00 00 00
Password Write: 00 00 00 00
Password Privacy: 0F 0F 0F 0F
Password Destroy: 0F 0F 0F 0F
Password EAS: 00 00 00 00
# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.
Signature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D
Privacy Mode: false
# Protection pointer configuration
Protection Pointer: 32
Protection Condition: 02
# SLIX Lock Bits
Lock EAS: true
Lock PPL: true
@@ -0,0 +1,39 @@
Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB
Device type: SLIX
# UID is common for all formats
UID: E0 04 01 08 49 D0 DC 81
# ISO15693-3 specific data
# Data Storage Format Identifier
DSFID: 01
# Application Family Identifier
AFI: 3D
# IC Reference - Vendor specific meaning
IC Reference: 01
# Lock Bits
Lock DSFID: true
Lock AFI: true
# Number of memory blocks, valid range = 1..256
Block Count: 80
# Size of a single memory block, valid range = 01...20 (hex)
Block Size: 04
Data Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01
# Block Security Status: 01 = locked, 00 = not locked
Security Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# SLIX specific data
# Passwords are optional. If a password is omitted, a default value will be used
Password Read: 00 00 00 00
Password Write: 00 00 00 00
Password Privacy: 0F 0F 0F 0F
Password Destroy: 0F 0F 0F 0F
Password EAS: 00 00 00 00
# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.
Signature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D
Privacy Mode: false
# Protection pointer configuration
Protection Pointer: 32
Protection Condition: 02
# SLIX Lock Bits
Lock EAS: true
Lock PPL: true
-168
View File
@@ -1,168 +0,0 @@
#include <stdio.h>
#include <furi.h>
#include <furi_hal.h>
#include "minunit_vars.h"
#include <notification/notification_messages.h>
#include <cli/cli.h>
#include <loader/loader.h>
#define TAG "UnitTests"
int run_minunit_test_furi(void);
int run_minunit_test_furi_hal(void);
int run_minunit_test_furi_hal_crypto(void);
int run_minunit_test_furi_string(void);
int run_minunit_test_infrared(void);
int run_minunit_test_rpc(void);
int run_minunit_test_manifest(void);
int run_minunit_test_flipper_format(void);
int run_minunit_test_flipper_format_string(void);
int run_minunit_test_stream(void);
int run_minunit_test_storage(void);
int run_minunit_test_subghz(void);
int run_minunit_test_dirwalk(void);
int run_minunit_test_power(void);
int run_minunit_test_protocol_dict(void);
int run_minunit_test_lfrfid_protocols(void);
int run_minunit_test_nfc(void);
int run_minunit_test_bit_lib(void);
int run_minunit_test_datetime(void);
int run_minunit_test_float_tools(void);
int run_minunit_test_bt(void);
int run_minunit_test_dialogs_file_browser_options(void);
int run_minunit_test_expansion(void);
typedef int (*UnitTestEntry)(void);
typedef struct {
const char* name;
const UnitTestEntry entry;
} UnitTest;
const UnitTest unit_tests[] = {
{.name = "furi", .entry = run_minunit_test_furi},
{.name = "furi_hal", .entry = run_minunit_test_furi_hal},
{.name = "furi_hal_crypto", .entry = run_minunit_test_furi_hal_crypto},
{.name = "furi_string", .entry = run_minunit_test_furi_string},
{.name = "storage", .entry = run_minunit_test_storage},
{.name = "stream", .entry = run_minunit_test_stream},
{.name = "dirwalk", .entry = run_minunit_test_dirwalk},
{.name = "manifest", .entry = run_minunit_test_manifest},
{.name = "flipper_format", .entry = run_minunit_test_flipper_format},
{.name = "flipper_format_string", .entry = run_minunit_test_flipper_format_string},
{.name = "rpc", .entry = run_minunit_test_rpc},
{.name = "subghz", .entry = run_minunit_test_subghz},
{.name = "infrared", .entry = run_minunit_test_infrared},
{.name = "nfc", .entry = run_minunit_test_nfc},
{.name = "power", .entry = run_minunit_test_power},
{.name = "protocol_dict", .entry = run_minunit_test_protocol_dict},
{.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols},
{.name = "bit_lib", .entry = run_minunit_test_bit_lib},
{.name = "datetime", .entry = run_minunit_test_datetime},
{.name = "float_tools", .entry = run_minunit_test_float_tools},
{.name = "bt", .entry = run_minunit_test_bt},
{.name = "dialogs_file_browser_options",
.entry = run_minunit_test_dialogs_file_browser_options},
{.name = "expansion", .entry = run_minunit_test_expansion},
};
void minunit_print_progress(void) {
static const char progress[] = {'\\', '|', '/', '-'};
static uint8_t progress_counter = 0;
static uint32_t last_tick = 0;
uint32_t current_tick = furi_get_tick();
if(current_tick - last_tick > 20) {
last_tick = current_tick;
printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]);
fflush(stdout);
}
}
void minunit_print_fail(const char* str) {
printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
}
void minunit_printf_warning(const char* format, ...) {
FuriString* str = furi_string_alloc();
va_list args;
va_start(args, format);
furi_string_vprintf(str, format, args);
va_end(args);
printf(_FURI_LOG_CLR_W "%s\r\n" _FURI_LOG_CLR_RESET, furi_string_get_cstr(str));
furi_string_free(str);
}
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
UNUSED(cli);
UNUSED(args);
UNUSED(context);
minunit_run = 0;
minunit_assert = 0;
minunit_fail = 0;
minunit_status = 0;
Loader* loader = furi_record_open(RECORD_LOADER);
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
// TODO FL-3491: lock device while test running
if(loader_is_locked(loader)) {
printf("RPC: stop all applications to run tests\r\n");
notification_message(notification, &sequence_blink_magenta_100);
} else {
notification_message_block(notification, &sequence_set_only_blue_255);
uint32_t heap_before = memmgr_get_free_heap();
uint32_t cycle_counter = furi_get_tick();
for(size_t i = 0; i < COUNT_OF(unit_tests); i++) {
if(cli_cmd_interrupt_received(cli)) {
break;
}
if(furi_string_size(args)) {
if(furi_string_cmp_str(args, unit_tests[i].name) == 0) {
unit_tests[i].entry();
} else {
printf("Skipping %s\r\n", unit_tests[i].name);
}
} else {
unit_tests[i].entry();
}
}
if(minunit_run != 0) {
printf("\r\nFailed tests: %u\r\n", minunit_fail);
// Time report
cycle_counter = (furi_get_tick() - cycle_counter);
printf("Consumed: %lu ms\r\n", cycle_counter);
// Wait for tested services and apps to deallocate memory
furi_delay_ms(200);
uint32_t heap_after = memmgr_get_free_heap();
printf("Leaked: %ld\r\n", heap_before - heap_after);
// Final Report
if(minunit_fail == 0) {
notification_message(notification, &sequence_success);
printf("Status: PASSED\r\n");
} else {
notification_message(notification, &sequence_error);
printf("Status: FAILED\r\n");
}
}
}
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_LOADER);
}
void unit_tests_on_system_start(void) {
#ifdef SRV_CLI
Cli* cli = furi_record_open(RECORD_CLI);
// We need to launch apps from tests, so we cannot lock loader
cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL);
furi_record_close(RECORD_CLI);
#endif
}
+216
View File
@@ -0,0 +1,216 @@
#include "test_runner.h"
#include "tests/test_api.h"
#include <cli/cli.h>
#include <toolbox/path.h>
#include <loader/loader.h>
#include <storage/storage.h>
#include <notification/notification_messages.h>
#include <loader/firmware_api/firmware_api.h>
#include <flipper_application/flipper_application.h>
#include <flipper_application/api_hashtable/api_hashtable.h>
#include <flipper_application/plugins/composite_resolver.h>
extern const ElfApiInterface* const unit_tests_api_interface;
#define TAG "TestRunner"
#define PLUGINS_PATH "/ext/apps_data/unit_tests/plugins"
struct TestRunner {
Storage* storage;
Loader* loader;
NotificationApp* notification;
// Temporary used things
Cli* cli;
FuriString* args;
// ELF related stuff
CompositeApiResolver* composite_resolver;
// Report data
int minunit_run;
int minunit_assert;
int minunit_fail;
int minunit_status;
};
TestRunner* test_runner_alloc(Cli* cli, FuriString* args) {
TestRunner* instance = malloc(sizeof(TestRunner));
instance->storage = furi_record_open(RECORD_STORAGE);
instance->loader = furi_record_open(RECORD_LOADER);
instance->notification = furi_record_open(RECORD_NOTIFICATION);
instance->cli = cli;
instance->args = args;
instance->composite_resolver = composite_api_resolver_alloc();
composite_api_resolver_add(instance->composite_resolver, firmware_api_interface);
composite_api_resolver_add(instance->composite_resolver, unit_tests_api_interface);
return instance;
}
void test_runner_free(TestRunner* instance) {
furi_assert(instance);
composite_api_resolver_free(instance->composite_resolver);
furi_record_close(RECORD_NOTIFICATION);
instance->notification = NULL;
furi_record_close(RECORD_LOADER);
instance->loader = NULL;
furi_record_close(RECORD_STORAGE);
instance->storage = NULL;
free(instance);
}
static bool test_runner_run_plugin(TestRunner* instance, const char* path) {
furi_assert(instance);
FURI_LOG_D(TAG, "Loading %s", path);
FlipperApplication* lib = flipper_application_alloc(
instance->storage, composite_api_resolver_get(instance->composite_resolver));
bool result = false;
instance->minunit_fail = -1;
do {
FlipperApplicationPreloadStatus preload_res = flipper_application_preload(lib, path);
if(preload_res != FlipperApplicationPreloadStatusSuccess) {
FURI_LOG_E(TAG, "Failed to preload %s, %d", path, preload_res);
break;
}
if(!flipper_application_is_plugin(lib)) {
FURI_LOG_E(TAG, "Not a plugin %s", path);
break;
}
FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(lib);
if(load_status != FlipperApplicationLoadStatusSuccess) {
FURI_LOG_E(TAG, "Failed to load %s", path);
break;
}
const FlipperAppPluginDescriptor* app_descriptor =
flipper_application_plugin_get_descriptor(lib);
const TestApi* test = app_descriptor->entry_point;
instance->minunit_fail = test->run();
instance->minunit_run += test->get_minunit_run();
instance->minunit_assert += test->get_minunit_assert();
instance->minunit_status += test->get_minunit_status();
result = (instance->minunit_fail == 0);
} while(false);
flipper_application_free(lib);
return result;
}
static void test_runner_run_internal(TestRunner* instance) {
furi_assert(instance);
char file_name_buffer[256];
FuriString* file_name = furi_string_alloc();
FuriString* file_basename = furi_string_alloc();
File* directory = storage_file_alloc(instance->storage);
do {
if(!storage_dir_open(directory, PLUGINS_PATH)) {
FURI_LOG_E(TAG, "Failed to open directory %s", PLUGINS_PATH);
break;
}
while(true) {
if(cli_cmd_interrupt_received(instance->cli)) {
break;
}
if(!storage_dir_read(directory, NULL, file_name_buffer, sizeof(file_name_buffer))) {
break;
}
furi_string_set(file_name, file_name_buffer);
if(!furi_string_end_with_str(file_name, ".fal")) {
continue;
}
path_concat(PLUGINS_PATH, file_name_buffer, file_name);
path_extract_filename(file_name, file_basename, true);
const char* file_basename_cstr = furi_string_get_cstr(file_basename);
bool result = true;
if(furi_string_size(instance->args)) {
if(furi_string_cmp_str(instance->args, file_basename_cstr) == 0) {
result = test_runner_run_plugin(instance, furi_string_get_cstr(file_name));
} else {
printf("Skipping %s\r\n", file_basename_cstr);
}
} else {
result = test_runner_run_plugin(instance, furi_string_get_cstr(file_name));
}
if(!result) {
printf("Failed to execute test: %s\r\n", file_basename_cstr);
break;
}
}
} while(false);
storage_dir_close(directory);
storage_file_free(directory);
furi_string_free(file_name);
furi_string_free(file_basename);
}
void test_runner_run(TestRunner* instance) {
furi_assert(instance);
// TODO FL-3491: lock device while test running
if(loader_is_locked(instance->loader)) {
printf("RPC: stop all applications to run tests\r\n");
notification_message(instance->notification, &sequence_blink_magenta_100);
} else {
notification_message_block(instance->notification, &sequence_set_only_blue_255);
uint32_t heap_before = memmgr_get_free_heap();
uint32_t cycle_counter = furi_get_tick();
test_runner_run_internal(instance);
if(instance->minunit_run != 0) {
printf("\r\nFailed tests: %d\r\n", instance->minunit_fail);
// Time report
cycle_counter = (furi_get_tick() - cycle_counter);
printf("Consumed: %lu ms\r\n", cycle_counter);
// Wait for tested services and apps to deallocate memory
furi_delay_ms(200);
uint32_t heap_after = memmgr_get_free_heap();
printf("Leaked: %ld\r\n", heap_before - heap_after);
// Final Report
if(instance->minunit_fail == 0) {
notification_message(instance->notification, &sequence_success);
printf("Status: PASSED\r\n");
} else {
notification_message(instance->notification, &sequence_error);
printf("Status: FAILED\r\n");
}
}
}
}
@@ -0,0 +1,12 @@
#pragma once
#include <furi.h>
typedef struct TestRunner TestRunner;
typedef struct Cli Cli;
TestRunner* test_runner_alloc(Cli* cli, FuriString* args);
void test_runner_free(TestRunner* isntance);
void test_runner_run(TestRunner* isntance);
@@ -1,5 +1,5 @@
#include <furi.h>
#include "../minunit.h"
#include "../test.h"
#include <bit_lib/bit_lib.h>
MU_TEST(test_bit_lib_increment_index) {
@@ -737,4 +737,6 @@ MU_TEST_SUITE(test_bit_lib) {
int run_minunit_test_bit_lib(void) {
MU_RUN_SUITE(test_bit_lib);
return MU_EXIT_CODE;
}
}
TEST_API_DEFINE(run_minunit_test_bit_lib)
@@ -1,6 +1,6 @@
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
#include "../test.h"
#include <bt/bt_service/bt_keys_storage.h>
#include <storage/storage.h>
@@ -108,3 +108,5 @@ int run_minunit_test_bt(void) {
MU_RUN_SUITE(test_bt);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_bt)
@@ -0,0 +1,42 @@
#include "../test.h"
#include "../minunit_vars.h"
#include <furi.h>
void minunit_print_progress(void) {
static const char progress[] = {'\\', '|', '/', '-'};
static uint8_t progress_counter = 0;
static uint32_t last_tick = 0;
uint32_t current_tick = furi_get_tick();
if(current_tick - last_tick > 20) {
last_tick = current_tick;
printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]);
fflush(stdout);
}
}
void minunit_print_fail(const char* str) {
printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
}
void minunit_printf_warning(const char* format, ...) {
FuriString* str = furi_string_alloc();
va_list args;
va_start(args, format);
furi_string_vprintf(str, format, args);
va_end(args);
printf(_FURI_LOG_CLR_W "%s\r\n" _FURI_LOG_CLR_RESET, furi_string_get_cstr(str));
furi_string_free(str);
}
int get_minunit_run(void) {
return minunit_run;
}
int get_minunit_assert(void) {
return minunit_assert;
}
int get_minunit_status(void) {
return minunit_status;
}
@@ -0,0 +1,159 @@
#include "../test.h"
#include <toolbox/compress.h>
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_random.h>
#include <storage/storage.h>
#include <stdint.h>
#define COMPRESS_UNIT_TESTS_PATH(path) EXT_PATH("unit_tests/compress/" path)
static void compress_test_reference_comp_decomp() {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* compressed_file = storage_file_alloc(storage);
File* decompressed_file = storage_file_alloc(storage);
mu_assert(
storage_file_open(
compressed_file,
COMPRESS_UNIT_TESTS_PATH("compressed.bin"),
FSAM_READ,
FSOM_OPEN_EXISTING),
"Failed to open compressed file");
mu_assert(
storage_file_open(
decompressed_file,
COMPRESS_UNIT_TESTS_PATH("uncompressed.bin"),
FSAM_READ,
FSOM_OPEN_EXISTING),
"Failed to open decompressed file");
uint64_t compressed_ref_size = storage_file_size(compressed_file);
uint64_t decompressed_ref_size = storage_file_size(decompressed_file);
mu_assert(compressed_ref_size > 0 && decompressed_ref_size > 0, "Invalid file sizes");
uint8_t* compressed_ref_buff = malloc(compressed_ref_size);
uint8_t* decompressed_ref_buff = malloc(decompressed_ref_size);
mu_assert(
storage_file_read(compressed_file, compressed_ref_buff, compressed_ref_size) ==
compressed_ref_size,
"Failed to read compressed file");
mu_assert(
storage_file_read(decompressed_file, decompressed_ref_buff, decompressed_ref_size) ==
decompressed_ref_size,
"Failed to read decompressed file");
storage_file_free(compressed_file);
storage_file_free(decompressed_file);
furi_record_close(RECORD_STORAGE);
uint8_t* temp_buffer = malloc(1024);
Compress* comp = compress_alloc(1024);
size_t encoded_size = 0;
mu_assert(
compress_encode(
comp, decompressed_ref_buff, decompressed_ref_size, temp_buffer, 1024, &encoded_size),
"Compress failed");
mu_assert(encoded_size == compressed_ref_size, "Encoded size is not equal to reference size");
mu_assert(
memcmp(temp_buffer, compressed_ref_buff, compressed_ref_size) == 0,
"Encoded buffer is not equal to reference");
size_t decoded_size = 0;
mu_assert(
compress_decode(
comp, compressed_ref_buff, compressed_ref_size, temp_buffer, 1024, &decoded_size),
"Decompress failed");
mu_assert(
decoded_size == decompressed_ref_size, "Decoded size is not equal to reference size");
mu_assert(
memcmp(temp_buffer, decompressed_ref_buff, decompressed_ref_size) == 0,
"Decoded buffer is not equal to reference");
compress_free(comp);
free(temp_buffer);
free(compressed_ref_buff);
free(decompressed_ref_buff);
}
static void compress_test_random_comp_decomp() {
static const size_t src_buffer_size = 1024;
static const size_t encoded_buffer_size = 1024;
static const size_t small_buffer_size = src_buffer_size / 32;
// We only fill half of the buffer with random data, so if anything goes wrong, there's no overflow
static const size_t src_data_size = src_buffer_size / 2;
Compress* comp = compress_alloc(src_buffer_size);
uint8_t* src_buff = malloc(src_buffer_size);
uint8_t* encoded_buff = malloc(encoded_buffer_size);
uint8_t* decoded_buff = malloc(src_buffer_size);
uint8_t* small_buff = malloc(small_buffer_size);
furi_hal_random_fill_buf(src_buff, src_data_size);
size_t encoded_size = 0;
mu_assert(
compress_encode(
comp, src_buff, src_data_size, encoded_buff, encoded_buffer_size, &encoded_size),
"Compress failed");
mu_assert(encoded_size > 0, "Encoded size is zero");
size_t small_enc_dec_size = 0;
mu_assert(
compress_encode(
comp, src_buff, src_data_size, small_buff, small_buffer_size, &small_enc_dec_size) ==
false,
"Compress to small buffer failed");
size_t decoded_size = 0;
mu_assert(
compress_decode(
comp, encoded_buff, encoded_size, decoded_buff, src_buffer_size, &decoded_size),
"Decompress failed");
mu_assert(decoded_size == src_data_size, "Decoded size is not equal to source size");
mu_assert(
memcmp(src_buff, decoded_buff, src_data_size) == 0,
"Decoded buffer is not equal to source");
mu_assert(
compress_decode(
comp, encoded_buff, encoded_size, small_buff, small_buffer_size, &small_enc_dec_size) ==
false,
"Decompress to small buffer failed");
free(small_buff);
free(src_buff);
free(encoded_buff);
free(decoded_buff);
compress_free(comp);
}
MU_TEST_SUITE(test_compress) {
MU_RUN_TEST(compress_test_random_comp_decomp);
MU_RUN_TEST(compress_test_reference_comp_decomp);
}
int run_minunit_test_compress(void) {
MU_RUN_SUITE(test_compress);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_compress)
@@ -1,5 +1,5 @@
#include <furi.h>
#include "../minunit.h"
#include "../test.h"
#include <datetime/datetime.h>
@@ -188,4 +188,6 @@ int run_minunit_test_datetime(void) {
MU_RUN_SUITE(test_datetime_validate_datetime);
return MU_EXIT_CODE;
}
}
TEST_API_DEFINE(run_minunit_test_datetime)
@@ -1,6 +1,6 @@
#include <dialogs/dialogs.h>
#include "../minunit.h"
#include "../test.h"
MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) {
mu_assert(
@@ -30,3 +30,5 @@ int run_minunit_test_dialogs_file_browser_options(void) {
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_dialogs_file_browser_options)
@@ -1,4 +1,4 @@
#include "../minunit.h"
#include "../test.h"
#include <furi.h>
#include <m-dict.h>
#include <toolbox/dir_walk.h>
@@ -269,4 +269,6 @@ MU_TEST_SUITE(test_dirwalk_suite) {
int run_minunit_test_dirwalk(void) {
MU_RUN_SUITE(test_dirwalk_suite);
return MU_EXIT_CODE;
}
}
TEST_API_DEFINE(run_minunit_test_dirwalk)
@@ -1,4 +1,4 @@
#include "../minunit.h"
#include "../test.h"
#include <furi.h>
#include <furi_hal_random.h>
@@ -198,3 +198,5 @@ int run_minunit_test_expansion(void) {
MU_RUN_SUITE(test_expansion_suite);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_expansion)
@@ -2,7 +2,7 @@
#include <flipper_format/flipper_format.h>
#include <flipper_format/flipper_format_i.h>
#include <toolbox/stream/stream.h>
#include "../minunit.h"
#include "../test.h"
#define TEST_DIR TEST_DIR_NAME "/"
#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp")
@@ -549,3 +549,5 @@ int run_minunit_test_flipper_format(void) {
MU_RUN_SUITE(flipper_format);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_flipper_format)
@@ -3,7 +3,7 @@
#include <flipper_format/flipper_format_i.h>
#include <toolbox/stream/stream.h>
#include <storage/storage.h>
#include "../minunit.h"
#include "../test.h"
static const char* test_filetype = "Flipper Format test";
static const uint32_t test_version = 666;
@@ -335,3 +335,5 @@ int run_minunit_test_flipper_format_string(void) {
MU_RUN_SUITE(flipper_format_string_suite);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_flipper_format_string)
@@ -1,7 +1,7 @@
#include <float.h>
#include <float_tools.h>
#include "../minunit.h"
#include "../test.h"
MU_TEST(float_tools_equal_test) {
mu_check(float_is_equal(FLT_MAX, FLT_MAX));
@@ -58,3 +58,5 @@ int run_minunit_test_float_tools(void) {
MU_RUN_SUITE(float_tools_suite);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_float_tools)
@@ -1,5 +1,8 @@
#include "../minunit.h"
#include <furi.h>
#include "../test.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
void test_furi_memmgr(void) {
void* ptr;
@@ -1,7 +1,7 @@
#include <stdio.h>
#include <string.h>
#include <furi.h>
#include "../minunit.h"
#include "../test.h"
const uint32_t context_value = 0xdeadbeef;
const uint32_t notify_value_0 = 0x12345678;
@@ -1,7 +1,7 @@
#include <stdio.h>
#include <string.h>
#include <furi.h>
#include "../minunit.h"
#include "../test.h"
#define TEST_RECORD_NAME "test/holding"
@@ -1,7 +1,7 @@
#include <stdio.h>
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
#include "../test.h"
// v2 tests
void test_furi_create_open(void);
@@ -57,3 +57,5 @@ int run_minunit_test_furi(void) {
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_furi)
@@ -4,7 +4,7 @@
#include <furi.h>
#include <furi_hal.h>
#include <lp5562_reg.h>
#include "../minunit.h"
#include "../test.h"
#include <stdlib.h>
#define DATA_SIZE 4
@@ -232,3 +232,5 @@ int run_minunit_test_furi_hal(void) {
MU_RUN_SUITE(furi_hal_i2c_ext_suite);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_furi_hal)
@@ -1,7 +1,7 @@
#include <stdio.h>
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
#include "../test.h"
static const uint8_t key_ctr_1[32] = {
0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C,
@@ -600,3 +600,5 @@ int run_minunit_test_furi_hal_crypto(void) {
MU_RUN_SUITE(furi_hal_crypto_gcm_test);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_furi_hal_crypto)
@@ -1,5 +1,5 @@
#include <furi.h>
#include "../minunit.h"
#include "../test.h"
static void test_setup(void) {
}
@@ -466,4 +466,6 @@ int run_minunit_test_furi_string(void) {
MU_RUN_SUITE(test_suite);
return MU_EXIT_CODE;
}
}
TEST_API_DEFINE(run_minunit_test_furi_string)
@@ -2,7 +2,7 @@
#include <flipper_format.h>
#include <infrared.h>
#include <common/infrared_common_i.h>
#include "../minunit.h"
#include "../test.h"
#define IR_TEST_FILES_DIR EXT_PATH("unit_tests/infrared/")
#define IR_TEST_FILE_PREFIX "test_"
@@ -549,3 +549,5 @@ int run_minunit_test_infrared(void) {
MU_RUN_SUITE(infrared_test);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_infrared)
@@ -1,5 +1,5 @@
#include <furi.h>
#include "../minunit.h"
#include "../test.h"
#include <toolbox/protocols/protocol_dict.h>
#include <lfrfid/protocols/lfrfid_protocols.h>
#include <toolbox/pulse_protocols/pulse_glue.h>
@@ -550,4 +550,6 @@ MU_TEST_SUITE(test_lfrfid_protocols_suite) {
int run_minunit_test_lfrfid_protocols(void) {
MU_RUN_SUITE(test_lfrfid_protocols_suite);
return MU_EXIT_CODE;
}
}
TEST_API_DEFINE(run_minunit_test_lfrfid_protocols)
@@ -1,5 +1,5 @@
#include <furi.c>
#include "../minunit.h"
#include "../test.h"
#include <update_util/resources/manifest.h>
#define TAG "Manifest"
@@ -72,4 +72,6 @@ MU_TEST_SUITE(manifest_suite) {
int run_minunit_test_manifest(void) {
MU_RUN_SUITE(manifest_suite);
return MU_EXIT_CODE;
}
}
TEST_API_DEFINE(run_minunit_test_manifest)
@@ -13,12 +13,18 @@
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller.h>
#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>
#include <nfc/protocols/slix/slix.h>
#include <nfc/protocols/slix/slix_i.h>
#include <nfc/protocols/slix/slix_poller.h>
#include <nfc/protocols/slix/slix_poller_i.h>
#include <nfc/nfc_poller.h>
#include <toolbox/keys_dict.h>
#include <nfc/nfc.h>
#include "../minunit.h"
#include "../test.h"
#define TAG "NfcTest"
@@ -42,6 +48,19 @@ typedef struct {
FuriThreadId thread_id;
} NfcTestMfClassicSendFrameTest;
typedef enum {
NfcTestSlixPollerSetPasswordStateGetRandomNumber,
NfcTestSlixPollerSetPasswordStateSetPassword,
} NfcTestSlixPollerSetPasswordState;
typedef struct {
FuriThreadId thread_id;
NfcTestSlixPollerSetPasswordState state;
SlixRandomNumber random_number;
SlixPassword password;
SlixError error;
} NfcTestSlixPollerSetPasswordContext;
typedef struct {
Storage* storage;
} NfcTest;
@@ -627,6 +646,127 @@ MU_TEST(mf_classic_dict_test) {
"Remove test dict failed");
}
MU_TEST(slix_file_with_capabilities_test) {
NfcDevice* nfc_device_missed_cap = nfc_device_alloc();
mu_assert(
nfc_device_load(nfc_device_missed_cap, EXT_PATH("unit_tests/nfc/Slix_cap_missed.nfc")),
"nfc_device_load() failed\r\n");
NfcDevice* nfc_device_default_cap = nfc_device_alloc();
mu_assert(
nfc_device_load(nfc_device_default_cap, EXT_PATH("unit_tests/nfc/Slix_cap_default.nfc")),
"nfc_device_load() failed\r\n");
mu_assert(
nfc_device_is_equal(nfc_device_missed_cap, nfc_device_default_cap),
"nfc_device_is_equal() failed\r\n");
nfc_device_free(nfc_device_default_cap);
nfc_device_free(nfc_device_missed_cap);
}
NfcCommand slix_poller_set_password_callback(NfcGenericEventEx event, void* context) {
furi_check(event.poller);
furi_check(event.parent_event_data);
furi_check(context);
NfcCommand command = NfcCommandContinue;
Iso15693_3PollerEvent* iso15_event = event.parent_event_data;
SlixPoller* poller = event.poller;
NfcTestSlixPollerSetPasswordContext* slix_ctx = context;
if(iso15_event->type == Iso15693_3PollerEventTypeReady) {
iso15693_3_copy(
poller->data->iso15693_3_data, iso15693_3_poller_get_data(poller->iso15693_3_poller));
if(slix_ctx->state == NfcTestSlixPollerSetPasswordStateGetRandomNumber) {
slix_ctx->error = slix_poller_get_random_number(poller, &slix_ctx->random_number);
if(slix_ctx->error != SlixErrorNone) {
furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE);
command = NfcCommandStop;
} else {
slix_ctx->state = NfcTestSlixPollerSetPasswordStateSetPassword;
}
} else if(slix_ctx->state == NfcTestSlixPollerSetPasswordStateSetPassword) {
slix_ctx->error = slix_poller_set_password(
poller, SlixPasswordTypeRead, slix_ctx->password, slix_ctx->random_number);
furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE);
command = NfcCommandStop;
}
} else {
slix_ctx->error = slix_process_iso15693_3_error(iso15_event->data->error);
furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE);
command = NfcCommandStop;
}
return command;
}
static void slix_set_password_test(const char* file_path, SlixPassword pass, bool correct_pass) {
FURI_LOG_I(TAG, "Testing file: %s", file_path);
Nfc* poller = nfc_alloc();
Nfc* listener = nfc_alloc();
NfcDevice* nfc_device = nfc_device_alloc();
mu_assert(nfc_device_load(nfc_device, file_path), "nfc_device_load() failed\r\n");
const SlixData* slix_data = nfc_device_get_data(nfc_device, NfcProtocolSlix);
NfcListener* slix_listener = nfc_listener_alloc(listener, NfcProtocolSlix, slix_data);
nfc_listener_start(slix_listener, NULL, NULL);
SlixCapabilities slix_capabilities = slix_data->capabilities;
NfcPoller* slix_poller = nfc_poller_alloc(poller, NfcProtocolSlix);
NfcTestSlixPollerSetPasswordContext slix_poller_context = {
.thread_id = furi_thread_get_current_id(),
.state = NfcTestSlixPollerSetPasswordStateGetRandomNumber,
.password = pass,
.error = SlixErrorNone,
};
nfc_poller_start_ex(slix_poller, slix_poller_set_password_callback, &slix_poller_context);
uint32_t flag =
furi_thread_flags_wait(NFC_TEST_FLAG_WORKER_DONE, FuriFlagWaitAny, FuriWaitForever);
mu_assert(flag == NFC_TEST_FLAG_WORKER_DONE, "Wrong thread flag\r\n");
nfc_poller_stop(slix_poller);
nfc_poller_free(slix_poller);
nfc_listener_stop(slix_listener);
nfc_listener_free(slix_listener);
mu_assert(
slix_poller_context.state == NfcTestSlixPollerSetPasswordStateSetPassword,
"Poller failed before setting password\r\n");
if((slix_capabilities == SlixCapabilitiesAcceptAllPasswords) || (correct_pass)) {
mu_assert(slix_poller_context.error == SlixErrorNone, "Failed to set password\r\n");
} else {
mu_assert(
slix_poller_context.error == SlixErrorTimeout,
"Must have received SlixErrorTimeout\r\n");
}
nfc_device_free(nfc_device);
nfc_free(listener);
nfc_free(poller);
}
MU_TEST(slix_set_password_default_cap_correct_pass) {
slix_set_password_test(EXT_PATH("unit_tests/nfc/Slix_cap_default.nfc"), 0x00000000, true);
}
MU_TEST(slix_set_password_default_cap_incorrect_pass) {
slix_set_password_test(EXT_PATH("unit_tests/nfc/Slix_cap_default.nfc"), 0x12341234, false);
}
MU_TEST(slix_set_password_access_all_passwords_cap) {
slix_set_password_test(
EXT_PATH("unit_tests/nfc/Slix_cap_accept_all_pass.nfc"), 0x12341234, false);
}
MU_TEST_SUITE(nfc) {
nfc_test_alloc();
@@ -668,6 +808,11 @@ MU_TEST_SUITE(nfc) {
MU_RUN_TEST(mf_classic_send_frame_test);
MU_RUN_TEST(mf_classic_dict_test);
MU_RUN_TEST(slix_file_with_capabilities_test);
MU_RUN_TEST(slix_set_password_default_cap_correct_pass);
MU_RUN_TEST(slix_set_password_default_cap_incorrect_pass);
MU_RUN_TEST(slix_set_password_access_all_passwords_cap);
nfc_test_free();
}
@@ -675,3 +820,5 @@ int run_minunit_test_nfc(void) {
MU_RUN_SUITE(nfc);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_nfc)
@@ -1,6 +1,6 @@
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
#include "../test.h"
static void power_test_deinit(void) {
// Try to reset to default charge voltage limit
@@ -67,3 +67,5 @@ int run_minunit_test_power(void) {
MU_RUN_SUITE(test_power_suite);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_power)
@@ -1,5 +1,5 @@
#include <furi.h>
#include "../minunit.h"
#include "../test.h"
#include <toolbox/protocols/protocol_dict.h>
typedef enum {
@@ -219,4 +219,6 @@ MU_TEST_SUITE(test_protocol_dict_suite) {
int run_minunit_test_protocol_dict(void) {
MU_RUN_SUITE(test_protocol_dict_suite);
return MU_EXIT_CODE;
}
}
TEST_API_DEFINE(run_minunit_test_protocol_dict)
@@ -17,7 +17,7 @@
#include <lib/toolbox/path.h>
#include <m-list.h>
#include "../minunit.h"
#include "../test.h"
#include <protobuf_version.h>
#include <pb.h>
@@ -1864,3 +1864,5 @@ int32_t delay_test_app(void* p) {
return 0;
}
TEST_API_DEFINE(run_minunit_test_rpc)
@@ -1,4 +1,4 @@
#include "../minunit.h"
#include "../test.h"
#include <furi.h>
#include <storage/storage.h>
@@ -712,3 +712,5 @@ int run_minunit_test_storage(void) {
MU_RUN_SUITE(test_md5_calc_suite);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_storage)
@@ -4,7 +4,7 @@
#include <toolbox/stream/file_stream.h>
#include <toolbox/stream/buffered_file_stream.h>
#include <storage/storage.h>
#include "../minunit.h"
#include "../test.h"
static const char* stream_test_data = "I write differently from what I speak, "
"I speak differently from what I think, "
@@ -530,3 +530,5 @@ int run_minunit_test_stream(void) {
MU_RUN_SUITE(stream_suite);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_stream)
@@ -1,6 +1,6 @@
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
#include "../test.h"
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include <lib/subghz/subghz_keystore.h>
@@ -908,3 +908,5 @@ int run_minunit_test_subghz(void) {
MU_RUN_SUITE(subghz);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_subghz)
@@ -0,0 +1,12 @@
#pragma once
// Framework
#include "minunit.h"
#include "test_api.h"
int get_minunit_run(void);
int get_minunit_assert(void);
int get_minunit_status(void);
@@ -0,0 +1,29 @@
#pragma once
#include <flipper_application/flipper_application.h>
#define APPID "UnitTest"
#define API_VERSION (0u)
typedef struct {
int (*run)(void);
int (*get_minunit_run)(void);
int (*get_minunit_assert)(void);
int (*get_minunit_status)(void);
} TestApi;
#define TEST_API_DEFINE(entrypoint) \
const TestApi test_api = { \
.run = entrypoint, \
.get_minunit_run = get_minunit_run, \
.get_minunit_assert = get_minunit_assert, \
.get_minunit_status = get_minunit_status, \
}; \
const FlipperAppPluginDescriptor app_descriptor = { \
.appid = APPID, \
.ep_api_version = API_VERSION, \
.entry_point = &test_api, \
}; \
const FlipperAppPluginDescriptor* get_api(void) { \
return &app_descriptor; \
}
@@ -1,6 +1,8 @@
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
#include "../test.h"
#include <toolbox/varint.h>
#include <toolbox/profiler.h>
@@ -85,4 +87,6 @@ MU_TEST_SUITE(test_varint_suite) {
int run_minunit_test_varint(void) {
MU_RUN_SUITE(test_varint_suite);
return MU_EXIT_CODE;
}
}
TEST_API_DEFINE(run_minunit_test_varint)
@@ -0,0 +1,19 @@
#include <flipper_application/api_hashtable/api_hashtable.h>
#include <flipper_application/api_hashtable/compilesort.hpp>
#include "unit_test_api_table_i.h"
static_assert(!has_hash_collisions(unit_tests_api_table), "Detected API method hash collision!");
constexpr HashtableApiInterface unit_tests_hashtable_api_interface{
{
.api_version_major = 0,
.api_version_minor = 0,
.resolver_callback = &elf_resolve_from_hashtable,
},
unit_tests_api_table.cbegin(),
unit_tests_api_table.cend(),
};
extern "C" const ElfApiInterface* const unit_tests_api_interface =
&unit_tests_hashtable_api_interface;
@@ -0,0 +1,29 @@
#include <update_util/resources/manifest.h>
#include <nfc/protocols/slix/slix_i.h>
#include <nfc/protocols/iso15693_3/iso15693_3_poller_i.h>
#include <FreeRTOS.h>
#include <FreeRTOS-Kernel/include/queue.h>
#include <rpc/rpc_i.h>
#include <flipper.pb.h>
static constexpr auto unit_tests_api_table = sort(create_array_t<sym_entry>(
API_METHOD(resource_manifest_reader_alloc, ResourceManifestReader*, (Storage*)),
API_METHOD(resource_manifest_reader_free, void, (ResourceManifestReader*)),
API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char* filename)),
API_METHOD(resource_manifest_reader_next, ResourceManifestEntry*, (ResourceManifestReader*)),
API_METHOD(resource_manifest_reader_previous, ResourceManifestEntry*, (ResourceManifestReader*)),
API_METHOD(slix_process_iso15693_3_error, SlixError, (Iso15693_3Error)),
API_METHOD(iso15693_3_poller_get_data, const Iso15693_3Data*, (Iso15693_3Poller*)),
API_METHOD(rpc_system_storage_get_error, PB_CommandStatus, (FS_Error)),
API_METHOD(xQueueSemaphoreTake, BaseType_t, (QueueHandle_t, TickType_t)),
API_METHOD(vQueueDelete, void, (QueueHandle_t)),
API_METHOD(
xQueueGenericCreate,
QueueHandle_t,
(const UBaseType_t, const UBaseType_t, const uint8_t)),
API_METHOD(
xQueueGenericSend,
BaseType_t,
(QueueHandle_t, const void* const, TickType_t, const BaseType_t)),
API_VARIABLE(PB_Main_msg, PB_Main_msg_t)));
@@ -0,0 +1,21 @@
#include <furi.h>
#include <cli/cli.h>
#include "test_runner.h"
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
UNUSED(cli);
UNUSED(context);
TestRunner* test_runner = test_runner_alloc(cli, args);
test_runner_run(test_runner);
test_runner_free(test_runner);
}
void unit_tests_on_system_start(void) {
#ifdef SRV_CLI
Cli* cli = furi_record_open(RECORD_CLI);
cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL);
furi_record_close(RECORD_CLI);
#endif
}
+3
View File
@@ -16,6 +16,7 @@ ArchiveApp* archive_alloc(void) {
ArchiveApp* archive = malloc(sizeof(ArchiveApp));
archive->gui = furi_record_open(RECORD_GUI);
archive->loader = furi_record_open(RECORD_LOADER);
archive->text_input = text_input_alloc();
archive->fav_move_str = furi_string_alloc();
@@ -61,6 +62,8 @@ void archive_free(ArchiveApp* archive) {
text_input_free(archive->text_input);
furi_record_close(RECORD_LOADER);
archive->loader = NULL;
furi_record_close(RECORD_GUI);
archive->gui = NULL;
+1
View File
@@ -22,6 +22,7 @@ typedef enum {
struct ArchiveApp {
Gui* gui;
Loader* loader;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
ArchiveBrowserView* browser;
@@ -86,10 +86,8 @@ void archive_scene_browser_on_enter(void* context) {
archive_update_focus(browser, archive->text_store);
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser);
Loader* loader = furi_record_open(RECORD_LOADER);
archive->loader_stop_subscription =
furi_pubsub_subscribe(loader_get_pubsub(loader), archive_loader_callback, archive);
furi_record_close(RECORD_LOADER);
archive->loader_stop_subscription = furi_pubsub_subscribe(
loader_get_pubsub(archive->loader), archive_loader_callback, archive);
uint32_t state = scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneBrowser);
@@ -213,10 +211,11 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
if(!archive_is_home(browser)) {
archive_leave_dir(browser);
} else {
Loader* loader = furi_record_open(RECORD_LOADER);
furi_pubsub_unsubscribe(
loader_get_pubsub(loader), archive->loader_stop_subscription);
furi_record_close(RECORD_LOADER);
if(archive->loader_stop_subscription) {
furi_pubsub_unsubscribe(
loader_get_pubsub(archive->loader), archive->loader_stop_subscription);
archive->loader_stop_subscription = NULL;
}
view_dispatcher_stop(archive->view_dispatcher);
}
@@ -232,8 +231,9 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
void archive_scene_browser_on_exit(void* context) {
ArchiveApp* archive = (ArchiveApp*)context;
Loader* loader = furi_record_open(RECORD_LOADER);
furi_pubsub_unsubscribe(loader_get_pubsub(loader), archive->loader_stop_subscription);
furi_record_close(RECORD_LOADER);
if(archive->loader_stop_subscription) {
furi_pubsub_unsubscribe(
loader_get_pubsub(archive->loader), archive->loader_stop_subscription);
archive->loader_stop_subscription = NULL;
}
}
@@ -2,22 +2,12 @@
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
static void nfc_scene_set_uid_byte_input_changed_callback(void* context) {
NfcApp* instance = context;
// Retrieve previously saved UID length
const size_t uid_len = scene_manager_get_scene_state(instance->scene_manager, NfcSceneSetUid);
nfc_device_set_uid(instance->nfc_device, instance->byte_input_store, uid_len);
}
void nfc_scene_set_uid_on_enter(void* context) {
NfcApp* instance = context;
size_t uid_len;
const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len);
memcpy(instance->byte_input_store, uid, uid_len);
// Save UID length for use in callback
scene_manager_set_scene_state(instance->scene_manager, NfcSceneSetUid, uid_len);
// Setup view
ByteInput* byte_input = instance->byte_input;
@@ -25,7 +15,7 @@ void nfc_scene_set_uid_on_enter(void* context) {
byte_input_set_result_callback(
byte_input,
nfc_protocol_support_common_byte_input_done_callback,
nfc_scene_set_uid_byte_input_changed_callback,
NULL,
instance,
instance->byte_input_store,
uid_len);
@@ -39,6 +29,9 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) {
size_t uid_len = 0;
nfc_device_get_uid(instance->nfc_device, &uid_len);
nfc_device_set_uid(instance->nfc_device, instance->byte_input_store, uid_len);
if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu)) {
if(nfc_save(instance)) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);
+1 -1
View File
@@ -14,7 +14,7 @@ App(
],
stack_size=1 * 1024,
order=20,
sdk_headers=["bt_service/bt.h"],
sdk_headers=["bt_service/bt.h", "bt_service/bt_keys_storage.h"],
)
App(
@@ -3,6 +3,10 @@
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct BtKeysStorage BtKeysStorage;
BtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path);
@@ -18,3 +22,7 @@ bool bt_keys_storage_load(BtKeysStorage* instance);
bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size);
bool bt_keys_storage_delete(BtKeysStorage* instance);
#ifdef __cplusplus
}
#endif
@@ -1,6 +1,5 @@
#include "slideshow.h"
#include <stddef.h>
#include <storage/storage.h>
#include <gui/icon.h>
#include <gui/icon_i.h>
+3 -3
View File
@@ -15,7 +15,7 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = {
Canvas* canvas_init(void) {
Canvas* canvas = malloc(sizeof(Canvas));
canvas->compress_icon = compress_icon_alloc();
canvas->compress_icon = compress_icon_alloc(ICON_DECOMPRESSOR_BUFFER_SIZE);
// Initialize mutex
canvas->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
@@ -390,7 +390,7 @@ void canvas_draw_icon_ex(
x += canvas->offset_x;
y += canvas->offset_y;
uint8_t* icon_data = NULL;
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
compress_icon_decode(canvas->compress_icon, icon_get_frame_data(icon, 0), &icon_data);
canvas_draw_u8g2_bitmap(
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, rotation);
}
@@ -402,7 +402,7 @@ void canvas_draw_icon(Canvas* canvas, int32_t x, int32_t y, const Icon* icon) {
x += canvas->offset_x;
y += canvas->offset_y;
uint8_t* icon_data = NULL;
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
compress_icon_decode(canvas->compress_icon, icon_get_frame_data(icon, 0), &icon_data);
canvas_draw_u8g2_bitmap(
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, IconRotation0);
}
+2
View File
@@ -12,6 +12,8 @@
#include <m-algo.h>
#include <furi.h>
#define ICON_DECOMPRESSOR_BUFFER_SIZE (128u * 64 / 8)
#ifdef __cplusplus
extern "C" {
#endif
+15 -3
View File
@@ -1,13 +1,16 @@
#include "icon.h"
#include "icon_i.h"
#include <furi.h>
uint8_t icon_get_width(const Icon* instance) {
#include <furi.h>
uint16_t icon_get_width(const Icon* instance) {
furi_check(instance);
return instance->width;
}
uint8_t icon_get_height(const Icon* instance) {
uint16_t icon_get_height(const Icon* instance) {
furi_check(instance);
return instance->height;
@@ -16,5 +19,14 @@ uint8_t icon_get_height(const Icon* instance) {
const uint8_t* icon_get_data(const Icon* instance) {
furi_check(instance);
return instance->frames[0];
return icon_get_frame_data(instance, 0);
}
uint32_t icon_get_frame_count(const Icon* instance) {
return instance->frame_count;
}
const uint8_t* icon_get_frame_data(const Icon* instance, uint32_t frame) {
furi_check(frame < instance->frame_count);
return instance->frames[frame];
}
+23 -5
View File
@@ -6,6 +6,7 @@
#pragma once
#include <stdint.h>
#include <core/common_defines.h>
#ifdef __cplusplus
extern "C" {
@@ -19,7 +20,7 @@ typedef struct Icon Icon;
*
* @return width in pixels
*/
uint8_t icon_get_width(const Icon* instance);
uint16_t icon_get_width(const Icon* instance);
/** Get icon height
*
@@ -27,15 +28,32 @@ uint8_t icon_get_width(const Icon* instance);
*
* @return height in pixels
*/
uint8_t icon_get_height(const Icon* instance);
uint16_t icon_get_height(const Icon* instance);
/** Get Icon XBM bitmap data
/** Get Icon XBM bitmap data for the first frame
*
* @param[in] instance pointer to Icon data
*
* @return pointer to XBM bitmap data
* @return pointer to compressed XBM bitmap data
*/
const uint8_t* icon_get_data(const Icon* instance);
FURI_DEPRECATED const uint8_t* icon_get_data(const Icon* instance);
/** Get Icon frame count
*
* @param[in] instance pointer to Icon data
*
* @return frame count
*/
uint32_t icon_get_frame_count(const Icon* instance);
/** Get Icon XBM bitmap data for a particular frame
*
* @param[in] instance pointer to Icon data
* @param[in] frame frame index
*
* @return pointer to compressed XBM bitmap data
*/
const uint8_t* icon_get_frame_data(const Icon* instance, uint32_t frame);
#ifdef __cplusplus
}
+3 -3
View File
@@ -4,11 +4,11 @@
*/
#pragma once
#include "icon.h"
#include <stdint.h>
struct Icon {
const uint8_t width;
const uint8_t height;
const uint16_t width;
const uint16_t height;
const uint8_t frame_count;
const uint8_t frame_rate;
const uint8_t* const* frames;
+213 -103
View File
@@ -4,8 +4,13 @@
#include <furi.h>
#include <stdint.h>
#define TEXT_BOX_MAX_SYMBOL_WIDTH (10)
#define TEXT_BOX_LINE_WIDTH (120)
#define TEXT_BOX_TEXT_WIDTH (120)
#define TEXT_BOX_TEXT_HEIGHT (56)
#define TEXT_BOX_MAX_LINES_PER_SCREEN (10)
#define TEXT_BOX_LINES_SCROLL_SPEED_MEDIUM (3)
#define TEXT_BOX_LINES_SCROLL_SPEED_FAST (5)
#define TEXT_BOX_LINES_SCROLL_SPEED_SATURATION (9)
struct TextBox {
View* view;
@@ -14,13 +19,19 @@ struct TextBox {
};
typedef struct {
const char* text;
char* text_pos;
FuriString* text_formatted;
int32_t scroll_pos;
int32_t scroll_num;
TextBoxFont font;
TextBoxFocus focus;
const char* text;
int32_t scroll_pos;
int32_t scroll_num;
int32_t lines_on_screen;
int32_t line_offset;
int32_t text_offset;
FuriString* text_on_screen;
FuriString* text_line;
bool formatted;
} TextBoxModel;
@@ -29,20 +40,11 @@ static void text_box_process_down(TextBox* text_box, uint8_t lines) {
text_box->view,
TextBoxModel * model,
{
if(model->scroll_pos < model->scroll_num - lines) {
if(model->scroll_pos + lines < model->scroll_num) {
model->scroll_pos += lines;
for(uint8_t i = 0; i < lines; i++) {
// Search next line start
while(*model->text_pos++ != '\n')
;
}
} else if(lines > 1) {
lines = model->scroll_num - model->scroll_pos - 1;
model->scroll_pos = model->scroll_num - 1;
for(uint8_t i = 0; i < lines; i++) {
// Search next line start
while(*model->text_pos++ != '\n')
;
} else {
if(model->scroll_num > 0) {
model->scroll_pos = model->scroll_num - 1;
}
}
},
@@ -54,69 +56,195 @@ static void text_box_process_up(TextBox* text_box, uint8_t lines) {
text_box->view,
TextBoxModel * model,
{
if(model->scroll_pos > lines - 1) {
if(model->scroll_pos - lines > 0) {
model->scroll_pos -= lines;
for(uint8_t i = 0; i < lines; i++) {
// Reach last symbol of previous line
model->text_pos--;
// Search previous line start
while((model->text_pos != model->text) && (*(--model->text_pos) != '\n'))
;
if(*model->text_pos == '\n') {
model->text_pos++;
}
}
} else if(lines > 1) {
lines = model->scroll_pos;
} else {
model->scroll_pos = 0;
model->text_pos = (char*)model->text;
}
},
true);
}
static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) {
size_t i = 0;
size_t line_width = 0;
const char* str = model->text;
size_t line_num = 0;
static bool text_box_view_input_callback(InputEvent* event, void* context) {
furi_assert(context);
while(str[i] != '\0') {
char symb = str[i++];
if(symb != '\n') {
TextBox* text_box = context;
bool consumed = false;
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
int32_t scroll_speed = 1;
if(text_box->button_held_for_ticks > TEXT_BOX_LINES_SCROLL_SPEED_FAST) {
if(text_box->button_held_for_ticks % 2) {
scroll_speed = 0;
} else {
scroll_speed =
(text_box->button_held_for_ticks > TEXT_BOX_LINES_SCROLL_SPEED_SATURATION) ?
TEXT_BOX_LINES_SCROLL_SPEED_FAST :
TEXT_BOX_LINES_SCROLL_SPEED_MEDIUM;
}
}
if(event->key == InputKeyDown) {
text_box_process_down(text_box, scroll_speed);
consumed = true;
} else if(event->key == InputKeyUp) {
text_box_process_up(text_box, scroll_speed);
consumed = true;
}
text_box->button_held_for_ticks++;
} else if(event->type == InputTypeRelease) {
text_box->button_held_for_ticks = 0;
consumed = true;
}
return consumed;
}
static bool text_box_end_of_text_reached(TextBoxModel* model) {
return model->text[model->text_offset] == '\0';
}
static bool text_box_start_of_text_reached(TextBoxModel* model) {
return model->text_offset == 0;
}
static void text_box_seek_next_line(Canvas* canvas, TextBoxModel* model) {
size_t line_width = 0;
while(!text_box_end_of_text_reached(model)) {
char symb = model->text[model->text_offset];
if(symb == '\n') {
model->text_offset++;
break;
} else {
size_t glyph_width = canvas_glyph_width(canvas, symb);
if(line_width + glyph_width > TEXT_BOX_LINE_WIDTH) {
line_num++;
line_width = 0;
furi_string_push_back(model->text_formatted, '\n');
if(line_width + glyph_width > TEXT_BOX_TEXT_WIDTH) {
break;
}
line_width += glyph_width;
} else {
line_num++;
line_width = 0;
model->text_offset++;
}
furi_string_push_back(model->text_formatted, symb);
}
line_num++;
model->text = furi_string_get_cstr(model->text_formatted);
model->text_pos = (char*)model->text;
size_t lines_on_screen = 56 / canvas_current_font_height(canvas);
if(model->focus == TextBoxFocusEnd && line_num > lines_on_screen) {
// Set text position to 5th line from the end
const char* end = model->text + furi_string_size(model->text_formatted);
for(size_t i = 0; i < line_num - lines_on_screen; i++) {
while(model->text_pos < end) {
if(*model->text_pos++ == '\n') break;
}
}
static void text_box_seek_end_of_prev_line(TextBoxModel* model) {
do {
if(text_box_start_of_text_reached(model)) break;
model->text_offset--;
if(text_box_start_of_text_reached(model)) break;
if(model->text[model->text_offset] == '\n') {
model->text_offset--;
}
} while(false);
}
static void text_box_seek_prev_paragraph(TextBoxModel* model) {
while(!text_box_start_of_text_reached(model)) {
if(model->text[model->text_offset] == '\n') {
model->text_offset++;
break;
}
model->text_offset--;
}
}
static void text_box_seek_prev_line(Canvas* canvas, TextBoxModel* model) {
int32_t start_text_offset = model->text_offset;
text_box_seek_end_of_prev_line(model);
text_box_seek_prev_paragraph(model);
int32_t current_text_offset = model->text_offset;
while(true) {
text_box_seek_next_line(canvas, model);
if(model->text_offset == start_text_offset) {
break;
}
current_text_offset = model->text_offset;
}
model->text_offset = current_text_offset;
}
static void text_box_move_line_offset(Canvas* canvas, TextBoxModel* model, int32_t line_offset) {
if(line_offset >= 0) {
for(int32_t i = 0; i < line_offset; i++) {
text_box_seek_next_line(canvas, model);
}
model->scroll_num = line_num - (lines_on_screen - 1);
model->scroll_pos = line_num - lines_on_screen;
} else {
model->scroll_num = MAX(line_num - (lines_on_screen - 1), 0u);
model->scroll_pos = 0;
for(int32_t i = 0; i < (-line_offset); i++) {
text_box_seek_prev_line(canvas, model);
}
}
}
static void text_box_update_screen_text(Canvas* canvas, TextBoxModel* model) {
furi_string_reset(model->text_on_screen);
furi_string_reset(model->text_line);
int32_t start_text_offset = model->text_offset;
for(int32_t i = 0; i < model->lines_on_screen; i++) {
int32_t current_line_text_offset = model->text_offset;
text_box_seek_next_line(canvas, model);
int32_t next_line_text_offset = model->text_offset;
furi_string_set_strn(
model->text_line,
&model->text[current_line_text_offset],
next_line_text_offset - current_line_text_offset);
size_t str_len = furi_string_size(model->text_line);
if(furi_string_get_char(model->text_line, str_len - 1) != '\n') {
furi_string_push_back(model->text_line, '\n');
}
furi_string_cat(model->text_on_screen, model->text_line);
if(text_box_end_of_text_reached(model)) break;
current_line_text_offset = next_line_text_offset;
}
model->text_offset = start_text_offset;
}
static void text_box_update_text_on_screen(Canvas* canvas, TextBoxModel* model) {
int32_t line_offset = model->scroll_pos - model->line_offset;
text_box_move_line_offset(canvas, model, line_offset);
text_box_update_screen_text(canvas, model);
model->line_offset = model->scroll_pos;
}
static void text_box_prepare_model(Canvas* canvas, TextBoxModel* model) {
int32_t lines_num = 0;
model->text_offset = 0;
model->scroll_num = 0;
model->scroll_pos = 0;
model->line_offset = 0;
model->lines_on_screen = TEXT_BOX_TEXT_HEIGHT / canvas_current_font_height(canvas);
// Cache text offset to quick final text offset update if TextBoxFocusEnd is set
int32_t window_offset[TEXT_BOX_MAX_LINES_PER_SCREEN] = {};
do {
window_offset[lines_num % model->lines_on_screen] = model->text_offset;
text_box_seek_next_line(canvas, model);
lines_num++;
} while(!text_box_end_of_text_reached(model));
model->text_offset = 0;
lines_num++;
if(model->focus == TextBoxFocusEnd) {
if(lines_num > model->lines_on_screen) {
model->text_offset = window_offset[(lines_num - 1) % model->lines_on_screen];
}
}
if(lines_num > model->lines_on_screen) {
model->scroll_num = lines_num - model->lines_on_screen;
model->scroll_pos = (model->focus == TextBoxFocusEnd) ? model->scroll_num - 1 : 0;
}
text_box_update_screen_text(canvas, model);
model->line_offset = model->scroll_pos;
}
static void text_box_view_draw_callback(Canvas* canvas, void* _model) {
TextBoxModel* model = _model;
@@ -132,44 +260,17 @@ static void text_box_view_draw_callback(Canvas* canvas, void* _model) {
}
if(!model->formatted) {
text_box_insert_endline(canvas, model);
text_box_prepare_model(canvas, model);
model->formatted = true;
}
elements_slightly_rounded_frame(canvas, 0, 0, 124, 64);
elements_multiline_text(canvas, 3, 11, model->text_pos);
elements_scrollbar(canvas, model->scroll_pos, model->scroll_num);
}
static bool text_box_view_input_callback(InputEvent* event, void* context) {
furi_assert(context);
TextBox* text_box = context;
bool consumed = false;
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
int32_t scroll_speed = 1;
if(text_box->button_held_for_ticks > 5) {
if(text_box->button_held_for_ticks % 2) {
scroll_speed = 0;
} else {
scroll_speed = text_box->button_held_for_ticks > 9 ? 5 : 3;
}
}
if(event->key == InputKeyDown) {
text_box_process_down(text_box, scroll_speed);
consumed = true;
} else if(event->key == InputKeyUp) {
text_box_process_up(text_box, scroll_speed);
consumed = true;
}
text_box->button_held_for_ticks++;
} else if(event->type == InputTypeRelease) {
text_box->button_held_for_ticks = 0;
consumed = true;
if(model->line_offset != model->scroll_pos) {
text_box_update_text_on_screen(canvas, model);
}
return consumed;
elements_multiline_text(canvas, 3, 11, furi_string_get_cstr(model->text_on_screen));
}
TextBox* text_box_alloc(void) {
@@ -185,7 +286,8 @@ TextBox* text_box_alloc(void) {
TextBoxModel * model,
{
model->text = NULL;
model->text_formatted = furi_string_alloc_set("");
model->text_on_screen = furi_string_alloc();
model->text_line = furi_string_alloc();
model->formatted = false;
model->font = TextBoxFontText;
},
@@ -198,7 +300,13 @@ void text_box_free(TextBox* text_box) {
furi_check(text_box);
with_view_model(
text_box->view, TextBoxModel * model, { furi_string_free(model->text_formatted); }, true);
text_box->view,
TextBoxModel * model,
{
furi_string_free(model->text_on_screen);
furi_string_free(model->text_line);
},
true);
view_free(text_box->view);
free(text_box);
}
@@ -216,9 +324,15 @@ void text_box_reset(TextBox* text_box) {
TextBoxModel * model,
{
model->text = NULL;
furi_string_set(model->text_formatted, "");
model->font = TextBoxFontText;
model->focus = TextBoxFocusStart;
furi_string_reset(model->text_line);
furi_string_reset(model->text_on_screen);
model->line_offset = 0;
model->text_offset = 0;
model->lines_on_screen = 0;
model->scroll_num = 0;
model->scroll_pos = 0;
model->formatted = false;
},
true);
@@ -227,16 +341,12 @@ void text_box_reset(TextBox* text_box) {
void text_box_set_text(TextBox* text_box, const char* text) {
furi_check(text_box);
furi_check(text);
size_t str_length = strlen(text);
size_t formating_margin = str_length * TEXT_BOX_MAX_SYMBOL_WIDTH / TEXT_BOX_LINE_WIDTH;
with_view_model(
text_box->view,
TextBoxModel * model,
{
model->text = text;
furi_string_reset(model->text_formatted);
furi_string_reserve(model->text_formatted, str_length + formating_margin);
model->formatted = false;
},
true);
+2 -1
View File
@@ -97,10 +97,11 @@ void view_free_model(View* view) {
furi_mutex_free(model->mutex);
free(model->data);
free(model);
view->model = NULL;
} else {
furi_crash();
}
view->model = NULL;
view->model_type = ViewModelTypeNone;
}
void* view_get_model(View* view) {
@@ -10,19 +10,6 @@
static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!");
#ifdef APP_UNIT_TESTS
constexpr HashtableApiInterface mock_elf_api_interface{
{
.api_version_major = 0,
.api_version_minor = 0,
.resolver_callback = &elf_resolve_from_hashtable,
},
nullptr,
nullptr,
};
const ElfApiInterface* const firmware_api_interface = &mock_elf_api_interface;
#else
constexpr HashtableApiInterface elf_api_interface{
{
.api_version_major = (elf_api_version >> 16),
@@ -33,7 +20,6 @@ constexpr HashtableApiInterface elf_api_interface{
elf_api_table.cend(),
};
const ElfApiInterface* const firmware_api_interface = &elf_api_interface;
#endif
extern "C" void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) {
*major = firmware_api_interface->api_version_major;
+6
View File
@@ -353,6 +353,12 @@ static LoaderStatus loader_start_external_app(
FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start));
if(flipper_application_is_plugin(loader->app.fap)) {
status = loader_make_status_error(
LoaderStatusErrorInternal, error_message, "Plugin %s is not runnable", path);
break;
}
loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args);
FuriString* app_name = furi_string_alloc();
path_extract_filename_no_ext(path, app_name);
@@ -92,7 +92,7 @@ static bool loader_applications_item_callback(
path, loader_applications_app->storage, icon_ptr, item_name);
} else {
path_extract_filename(path, item_name, false);
memcpy(*icon_ptr, icon_get_data(&I_js_script_10px), FAP_MANIFEST_MAX_ICON_SIZE);
memcpy(*icon_ptr, icon_get_frame_data(&I_js_script_10px, 0), FAP_MANIFEST_MAX_ICON_SIZE);
return true;
}
}
+8
View File
@@ -7,6 +7,10 @@
#include <flipper.pb.h>
#include <cli/cli.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void* (*RpcSystemAlloc)(RpcSession* session);
typedef void (*RpcSystemFree)(void* context);
typedef void (*PBMessageHandler)(const PB_Main* msg_request, void* context);
@@ -45,3 +49,7 @@ void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size);
void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context);
PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error);
#ifdef __cplusplus
}
#endif
@@ -80,6 +80,7 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
furi_assert(favorite_id < DummyAppNumber);
curr_favorite_app = &app->settings.dummy_apps[favorite_id];
default_passport = true;
favorite_id |= SCENE_STATE_SET_DUMMY_APP;
}
// Special case: Application browser
@@ -141,28 +142,24 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
switch(favorite_id) {
case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort:
submenu_set_header(submenu, "Left - Short");
case SCENE_STATE_SET_DUMMY_APP | DummyAppLeft:
submenu_set_header(submenu, "Left - Press");
break;
case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong:
submenu_set_header(submenu, "Left - Long");
submenu_set_header(submenu, "Left - Hold");
break;
case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort:
submenu_set_header(submenu, "Right - Short");
case SCENE_STATE_SET_DUMMY_APP | DummyAppRight:
submenu_set_header(submenu, "Right - Press");
break;
case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong:
submenu_set_header(submenu, "Right - Long");
break;
case SCENE_STATE_SET_DUMMY_APP | DummyAppLeft:
submenu_set_header(submenu, "Left");
break;
case SCENE_STATE_SET_DUMMY_APP | DummyAppRight:
submenu_set_header(submenu, "Right");
submenu_set_header(submenu, "Right - Hold");
break;
case SCENE_STATE_SET_DUMMY_APP | DummyAppDown:
submenu_set_header(submenu, "Down");
submenu_set_header(submenu, "Down - Press");
break;
case SCENE_STATE_SET_DUMMY_APP | DummyAppOk:
submenu_set_header(submenu, "Middle");
submenu_set_header(submenu, "Middle - Press");
break;
default:
break;
@@ -25,7 +25,7 @@ void desktop_settings_scene_pin_disable_on_enter(void* context) {
popup_set_context(app->popup, app);
popup_set_callback(app->popup, pin_disable_back_callback);
popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(app->popup, "PIN\nDeleted!", 100, 0, AlignCenter, AlignTop);
popup_set_header(app->popup, "Removed", 100, 10, AlignCenter, AlignTop);
popup_set_timeout(app->popup, 1500);
popup_enable_timeout(app->popup);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);
@@ -37,7 +37,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) {
submenu_add_item(
submenu,
"Disable",
"Remove PIN",
SCENE_EVENT_DISABLE_PIN,
desktop_settings_scene_pin_menu_submenu_callback,
app);
@@ -34,14 +34,14 @@ void desktop_settings_scene_quick_apps_direction_menu_on_enter(void* context) {
if(favorite_id == SCENE_STATE_SET_FAVORITE_APP) {
submenu_add_item(
submenu,
"Left - Click",
"Left - Press",
QuickAppsSubmenuIndexFavoriteLeftClick,
desktop_settings_scene_quick_apps_direction_menu_submenu_callback,
app);
submenu_add_item(
submenu,
"Right - Click",
"Right - Press",
QuickAppsSubmenuIndexFavoriteRightClick,
desktop_settings_scene_quick_apps_direction_menu_submenu_callback,
app);
@@ -64,28 +64,28 @@ void desktop_settings_scene_quick_apps_direction_menu_on_enter(void* context) {
} else {
submenu_add_item(
submenu,
"Left - Click",
"Left - Press",
QuickAppsSubmenuIndexDummyLeftClick,
desktop_settings_scene_quick_apps_direction_menu_submenu_callback,
app);
submenu_add_item(
submenu,
"Right - Click",
"Right - Press",
QuickAppsSubmenuIndexDummyRightClick,
desktop_settings_scene_quick_apps_direction_menu_submenu_callback,
app);
submenu_add_item(
submenu,
"Down - Click",
"Down - Press",
QuickAppsSubmenuIndexDummyDownClick,
desktop_settings_scene_quick_apps_direction_menu_submenu_callback,
app);
submenu_add_item(
submenu,
"Middle - Click",
"Middle - Press",
QuickAppsSubmenuIndexDummyMiddleClick,
desktop_settings_scene_quick_apps_direction_menu_submenu_callback,
app);
@@ -51,15 +51,15 @@ void storage_settings_scene_formatting_on_enter(void* context) {
dialog_ex_set_text(
dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter);
} else {
dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);
dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop);
dialog_ex_set_icon(dialog_ex, 48, 6, &I_DolphinDone_80x58);
dialog_ex_set_header(dialog_ex, "Formatted", 5, 10, AlignLeft, AlignTop);
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message(notification, &sequence_single_vibro);
notification_message(notification, &sequence_set_green_255);
notification_message(notification, &sequence_success);
furi_record_close(RECORD_NOTIFICATION);
}
dialog_ex_set_center_button_text(dialog_ex, "OK");
dialog_ex_set_left_button_text(dialog_ex, "Finish");
}
bool storage_settings_scene_formatting_on_event(void* context, SceneManagerEvent event) {
@@ -68,7 +68,7 @@ bool storage_settings_scene_formatting_on_event(void* context, SceneManagerEvent
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultCenter:
case DialogExResultLeft:
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, StorageSettingsStart);
break;
@@ -54,3 +54,19 @@ App(
requires=["js_app"],
sources=["modules/js_submenu.c"],
)
App(
appid="js_math",
apptype=FlipperAppType.PLUGIN,
entry_point="js_math_ep",
requires=["js_app"],
sources=["modules/js_math.c"],
)
App(
appid="js_textbox",
apptype=FlipperAppType.PLUGIN,
entry_point="js_textbox_ep",
requires=["js_app"],
sources=["modules/js_textbox.c"],
)
@@ -0,0 +1,69 @@
let math = require("math");
print("math.abs(-5):", math.abs(-5));
print("math.acos(0.5):", math.acos(0.5));
print("math.acosh(2):", math.acosh(2));
print("math.asin(0.5):", math.asin(0.5));
print("math.asinh(2):", math.asinh(2));
print("math.atan(1):", math.atan(1));
print("math.atan2(1, 1):", math.atan2(1, 1));
print("math.atanh(0.5):", math.atanh(0.5));
print("math.cbrt(27):", math.cbrt(27));
print("math.ceil(5.3):", math.ceil(5.3));
print("math.clz32(1):", math.clz32(1));
print("math.cos(math.PI):", math.cos(math.PI));
print("math.exp(1):", math.exp(1));
print("math.floor(5.7):", math.floor(5.7));
print("math.max(3, 5):", math.max(3, 5));
print("math.min(3, 5):", math.min(3, 5));
print("math.pow(2, 3):", math.pow(2, 3));
print("math.random():", math.random());
print("math.sign(-5):", math.sign(-5));
print("math.sin(math.PI/2):", math.sin(math.PI / 2));
print("math.sqrt(25):", math.sqrt(25));
print("math.trunc(5.7):", math.trunc(5.7));
// Unit tests. Please add more if you have time and knowledge.
// math.EPSILON on Flipper Zero is 2.22044604925031308085e-16
let succeeded = 0;
let failed = 0;
function test(text, result, expected, epsilon) {
let is_equal = math.is_equal(result, expected, epsilon);
if (is_equal) {
succeeded += 1;
} else {
failed += 1;
print(text, "expected", expected, "got", result);
}
}
test("math.abs(5)", math.abs(-5), 5, math.EPSILON);
test("math.abs(0.5)", math.abs(-0.5), 0.5, math.EPSILON);
test("math.abs(5)", math.abs(5), 5, math.EPSILON);
test("math.abs(-0.5)", math.abs(0.5), 0.5, math.EPSILON);
test("math.acos(0.5)", math.acos(0.5), 1.0471975511965976, math.EPSILON);
test("math.acosh(2)", math.acosh(2), 1.3169578969248166, math.EPSILON);
test("math.asin(0.5)", math.asin(0.5), 0.5235987755982988, math.EPSILON);
test("math.asinh(2)", math.asinh(2), 1.4436354751788103, math.EPSILON);
test("math.atan(1)", math.atan(1), 0.7853981633974483, math.EPSILON);
test("math.atan2(1, 1)", math.atan2(1, 1), 0.7853981633974483, math.EPSILON);
test("math.atanh(0.5)", math.atanh(0.5), 0.5493061443340549, math.EPSILON);
test("math.cbrt(27)", math.cbrt(27), 3, math.EPSILON);
test("math.ceil(5.3)", math.ceil(5.3), 6, math.EPSILON);
test("math.clz32(1)", math.clz32(1), 31, math.EPSILON);
test("math.floor(5.7)", math.floor(5.7), 5, math.EPSILON);
test("math.max(3, 5)", math.max(3, 5), 5, math.EPSILON);
test("math.min(3, 5)", math.min(3, 5), 3, math.EPSILON);
test("math.pow(2, 3)", math.pow(2, 3), 8, math.EPSILON);
test("math.sign(-5)", math.sign(-5), -1, math.EPSILON);
test("math.sqrt(25)", math.sqrt(25), 5, math.EPSILON);
test("math.trunc(5.7)", math.trunc(5.7), 5, math.EPSILON);
test("math.cos(math.PI)", math.cos(math.PI), -1, math.EPSILON * 18); // Error 3.77475828372553223744e-15
test("math.exp(1)", math.exp(1), 2.718281828459045, math.EPSILON * 2); // Error 4.44089209850062616169e-16
test("math.sin(math.PI / 2)", math.sin(math.PI / 2), 1, math.EPSILON * 4.5); // Error 9.99200722162640886381e-16
if (failed > 0) {
print("!!!", failed, "Unit tests failed !!!");
}
@@ -0,0 +1,30 @@
let textbox = require("textbox");
// You should set config before adding text
// Focus (start / end), Font (text / hex)
textbox.setConfig("end", "text");
// Can make sure it's cleared before showing, in case of reusing in same script
// (Closing textbox already clears the text, but maybe you added more in a loop for example)
textbox.clearText();
// Add default text
textbox.addText("Example dynamic updating textbox\n");
// Non-blocking, can keep updating text after, can close in JS or in GUI
textbox.show();
let i = 0;
while (textbox.isOpen() && i < 20) {
print("console", i++);
// Add text to textbox buffer
textbox.addText("textbox " + to_string(i) + "\n");
delay(500);
}
// If not closed by user (instead i < 20 is false above), close forcefully
if (textbox.isOpen()) {
textbox.close();
}
+1 -1
View File
@@ -285,7 +285,7 @@ static int32_t js_thread(void* arg) {
}
const char* stack_trace = mjs_get_stack_trace(mjs);
if(stack_trace != NULL) {
FURI_LOG_E(TAG, "Stack trace:\n%s", stack_trace);
FURI_LOG_E(TAG, "Stack trace:\r\n%s", stack_trace);
if(worker->app_callback) {
worker->app_callback(JsThreadEventErrorTrace, stack_trace, worker->context);
}
@@ -0,0 +1,355 @@
#include "../js_modules.h"
#include "furi_hal_random.h"
#include <float.h>
#define JS_MATH_PI ((double)M_PI)
#define JS_MATH_E ((double)M_E)
#define JS_MATH_EPSILON ((double)DBL_EPSILON)
#define TAG "JsMath"
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_args(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;
}
for(size_t i = 0; i < count; i++) {
if(!mjs_is_number(mjs_arg(mjs, i))) {
ret_bad_args(mjs, "Wrong argument type");
return false;
}
}
return true;
}
void js_math_is_equal(struct mjs* mjs) {
if(!check_args(mjs, 3)) {
return;
}
double a = mjs_get_double(mjs, mjs_arg(mjs, 0));
double b = mjs_get_double(mjs, mjs_arg(mjs, 1));
double e = mjs_get_double(mjs, mjs_arg(mjs, 2));
double f = fabs(a - b);
mjs_return(mjs, mjs_mk_boolean(mjs, (f <= e)));
}
void js_math_abs(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, fabs(x)));
}
void js_math_acos(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x < (double)-1. || x > (double)1.) {
ret_bad_args(mjs, "Invalid input value for math.acos");
return;
}
mjs_return(mjs, mjs_mk_number(mjs, acos(x)));
}
void js_math_acosh(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x < (double)1.) {
ret_bad_args(mjs, "Invalid input value for math.acosh");
return;
}
mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x - (double)1.))));
}
void js_math_asin(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, asin(x)));
}
void js_math_asinh(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x + (double)1.))));
}
void js_math_atan(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, atan(x)));
}
void js_math_atan2(struct mjs* mjs) {
if(!check_args(mjs, 2)) {
return;
}
double y = mjs_get_double(mjs, mjs_arg(mjs, 0));
double x = mjs_get_double(mjs, mjs_arg(mjs, 1));
mjs_return(mjs, mjs_mk_number(mjs, atan2(y, x)));
}
void js_math_atanh(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x < (double)-1. || x > (double)1.) {
ret_bad_args(mjs, "Invalid input value for math.atanh");
return;
}
mjs_return(mjs, mjs_mk_number(mjs, (double)0.5 * log(((double)1. + x) / ((double)1. - x))));
}
void js_math_cbrt(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, cbrt(x)));
}
void js_math_ceil(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, ceil(x)));
}
void js_math_clz32(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
unsigned int x = (unsigned int)mjs_get_int(mjs, mjs_arg(mjs, 0));
int count = 0;
while(x) {
x >>= 1;
count++;
}
mjs_return(mjs, mjs_mk_number(mjs, 32 - count));
}
void js_math_cos(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, cos(x)));
}
void js_math_exp(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, exp(x)));
}
void js_math_floor(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, floor(x)));
}
void js_math_log(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x <= 0) {
ret_bad_args(mjs, "Invalid input value for math.log");
return;
}
mjs_return(mjs, mjs_mk_number(mjs, log(x)));
}
void js_math_max(struct mjs* mjs) {
if(!check_args(mjs, 2)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
double y = mjs_get_double(mjs, mjs_arg(mjs, 1));
mjs_return(mjs, mjs_mk_number(mjs, x > y ? x : y));
}
void js_math_min(struct mjs* mjs) {
if(!check_args(mjs, 2)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
double y = mjs_get_double(mjs, mjs_arg(mjs, 1));
mjs_return(mjs, mjs_mk_number(mjs, x < y ? x : y));
}
void js_math_pow(struct mjs* mjs) {
if(!check_args(mjs, 2)) {
return;
}
double base = mjs_get_double(mjs, mjs_arg(mjs, 0));
double exponent = mjs_get_double(mjs, mjs_arg(mjs, 1));
mjs_return(mjs, mjs_mk_number(mjs, pow(base, exponent)));
}
void js_math_random(struct mjs* mjs) {
if(!check_args(mjs, 0)) {
return;
}
// double clearly provides more bits for entropy then we pack
// 32bit should be enough for now, but fix it maybe
const uint32_t random_val = furi_hal_random_get();
double rnd = (double)random_val / (double)FURI_HAL_RANDOM_MAX;
mjs_return(mjs, mjs_mk_number(mjs, rnd));
}
void js_math_sign(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(
mjs,
mjs_mk_number(
mjs, fabs(x) <= JS_MATH_EPSILON ? 0 : (x < (double)0. ? (double)-1.0 : (double)1.0)));
}
void js_math_sin(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, sin(x)));
}
void js_math_sqrt(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
if(x < (double)0.) {
ret_bad_args(mjs, "Invalid input value for math.sqrt");
return;
}
mjs_return(mjs, mjs_mk_number(mjs, sqrt(x)));
}
void js_math_trunc(struct mjs* mjs) {
if(!check_args(mjs, 1)) {
return;
}
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
mjs_return(mjs, mjs_mk_number(mjs, x < (double)0. ? ceil(x) : floor(x)));
}
static void* js_math_create(struct mjs* mjs, mjs_val_t* object) {
mjs_val_t math_obj = mjs_mk_object(mjs);
mjs_set(mjs, math_obj, "is_equal", ~0, MJS_MK_FN(js_math_is_equal));
mjs_set(mjs, math_obj, "abs", ~0, MJS_MK_FN(js_math_abs));
mjs_set(mjs, math_obj, "acos", ~0, MJS_MK_FN(js_math_acos));
mjs_set(mjs, math_obj, "acosh", ~0, MJS_MK_FN(js_math_acosh));
mjs_set(mjs, math_obj, "asin", ~0, MJS_MK_FN(js_math_asin));
mjs_set(mjs, math_obj, "asinh", ~0, MJS_MK_FN(js_math_asinh));
mjs_set(mjs, math_obj, "atan", ~0, MJS_MK_FN(js_math_atan));
mjs_set(mjs, math_obj, "atan2", ~0, MJS_MK_FN(js_math_atan2));
mjs_set(mjs, math_obj, "atanh", ~0, MJS_MK_FN(js_math_atanh));
mjs_set(mjs, math_obj, "cbrt", ~0, MJS_MK_FN(js_math_cbrt));
mjs_set(mjs, math_obj, "ceil", ~0, MJS_MK_FN(js_math_ceil));
mjs_set(mjs, math_obj, "clz32", ~0, MJS_MK_FN(js_math_clz32));
mjs_set(mjs, math_obj, "cos", ~0, MJS_MK_FN(js_math_cos));
mjs_set(mjs, math_obj, "exp", ~0, MJS_MK_FN(js_math_exp));
mjs_set(mjs, math_obj, "floor", ~0, MJS_MK_FN(js_math_floor));
mjs_set(mjs, math_obj, "log", ~0, MJS_MK_FN(js_math_log));
mjs_set(mjs, math_obj, "max", ~0, MJS_MK_FN(js_math_max));
mjs_set(mjs, math_obj, "min", ~0, MJS_MK_FN(js_math_min));
mjs_set(mjs, math_obj, "pow", ~0, MJS_MK_FN(js_math_pow));
mjs_set(mjs, math_obj, "random", ~0, MJS_MK_FN(js_math_random));
mjs_set(mjs, math_obj, "sign", ~0, MJS_MK_FN(js_math_sign));
mjs_set(mjs, math_obj, "sin", ~0, MJS_MK_FN(js_math_sin));
mjs_set(mjs, math_obj, "sqrt", ~0, MJS_MK_FN(js_math_sqrt));
mjs_set(mjs, math_obj, "trunc", ~0, MJS_MK_FN(js_math_trunc));
mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, JS_MATH_PI));
mjs_set(mjs, math_obj, "E", ~0, mjs_mk_number(mjs, JS_MATH_E));
mjs_set(mjs, math_obj, "EPSILON", ~0, mjs_mk_number(mjs, JS_MATH_EPSILON));
*object = math_obj;
return (void*)1;
}
static const JsModuleDescriptor js_math_desc = {
"math",
js_math_create,
NULL,
};
static const FlipperAppPluginDescriptor plugin_descriptor = {
.appid = PLUGIN_APP_ID,
.ep_api_version = PLUGIN_API_VERSION,
.entry_point = &js_math_desc,
};
const FlipperAppPluginDescriptor* js_math_ep(void) {
return &plugin_descriptor;
}
@@ -0,0 +1,220 @@
#include <gui/modules/text_box.h>
#include <gui/view_holder.h>
#include "../js_modules.h"
typedef struct {
TextBox* text_box;
ViewHolder* view_holder;
FuriString* text;
bool is_shown;
} JsTextboxInst;
static JsTextboxInst* get_this_ctx(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsTextboxInst* textbox = mjs_get_ptr(mjs, obj_inst);
furi_assert(textbox);
return textbox;
}
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_textbox_set_config(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 2)) return;
TextBoxFocus set_focus = TextBoxFocusStart;
mjs_val_t focus_arg = mjs_arg(mjs, 0);
const char* focus = mjs_get_string(mjs, &focus_arg, NULL);
if(!focus) {
ret_bad_args(mjs, "Focus must be a string");
return;
} else {
if(!strncmp(focus, "start", strlen("start"))) {
set_focus = TextBoxFocusStart;
} else if(!strncmp(focus, "end", strlen("end"))) {
set_focus = TextBoxFocusEnd;
} else {
ret_bad_args(mjs, "Bad focus value");
return;
}
}
TextBoxFont set_font = TextBoxFontText;
mjs_val_t font_arg = mjs_arg(mjs, 1);
const char* font = mjs_get_string(mjs, &font_arg, NULL);
if(!font) {
ret_bad_args(mjs, "Font must be a string");
return;
} else {
if(!strncmp(font, "text", strlen("text"))) {
set_font = TextBoxFontText;
} else if(!strncmp(font, "hex", strlen("hex"))) {
set_font = TextBoxFontHex;
} else {
ret_bad_args(mjs, "Bad font value");
return;
}
}
text_box_set_focus(textbox->text_box, set_focus);
text_box_set_font(textbox->text_box, set_font);
mjs_return(mjs, MJS_UNDEFINED);
}
static void js_textbox_add_text(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 1)) return;
mjs_val_t text_arg = mjs_arg(mjs, 0);
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;
}
// Avoid condition race between GUI and JS thread
text_box_set_text(textbox->text_box, "");
size_t new_len = furi_string_size(textbox->text) + text_len;
if(new_len >= 4096) {
furi_string_right(textbox->text, new_len / 2);
}
furi_string_cat(textbox->text, text);
text_box_set_text(textbox->text_box, furi_string_get_cstr(textbox->text));
mjs_return(mjs, MJS_UNDEFINED);
}
static void js_textbox_clear_text(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
// Avoid condition race between GUI and JS thread
text_box_set_text(textbox->text_box, "");
furi_string_reset(textbox->text);
text_box_set_text(textbox->text_box, furi_string_get_cstr(textbox->text));
mjs_return(mjs, MJS_UNDEFINED);
}
static void js_textbox_is_open(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
mjs_return(mjs, mjs_mk_boolean(mjs, textbox->is_shown));
}
static void textbox_callback(void* context, uint32_t arg) {
UNUSED(arg);
JsTextboxInst* textbox = context;
view_holder_stop(textbox->view_holder);
textbox->is_shown = false;
}
static void textbox_exit(void* context) {
JsTextboxInst* textbox = context;
// Using timer to schedule view_holder stop, will not work under high CPU load
furi_timer_pending_callback(textbox_callback, textbox, 0);
}
static void js_textbox_show(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
if(textbox->is_shown) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Textbox is already shown");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
view_holder_start(textbox->view_holder);
textbox->is_shown = true;
mjs_return(mjs, MJS_UNDEFINED);
}
static void js_textbox_close(struct mjs* mjs) {
JsTextboxInst* textbox = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
view_holder_stop(textbox->view_holder);
textbox->is_shown = false;
mjs_return(mjs, MJS_UNDEFINED);
}
static void* js_textbox_create(struct mjs* mjs, mjs_val_t* object) {
JsTextboxInst* textbox = malloc(sizeof(JsTextboxInst));
mjs_val_t textbox_obj = mjs_mk_object(mjs);
mjs_set(mjs, textbox_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, textbox));
mjs_set(mjs, textbox_obj, "setConfig", ~0, MJS_MK_FN(js_textbox_set_config));
mjs_set(mjs, textbox_obj, "addText", ~0, MJS_MK_FN(js_textbox_add_text));
mjs_set(mjs, textbox_obj, "clearText", ~0, MJS_MK_FN(js_textbox_clear_text));
mjs_set(mjs, textbox_obj, "isOpen", ~0, MJS_MK_FN(js_textbox_is_open));
mjs_set(mjs, textbox_obj, "show", ~0, MJS_MK_FN(js_textbox_show));
mjs_set(mjs, textbox_obj, "close", ~0, MJS_MK_FN(js_textbox_close));
textbox->text = furi_string_alloc();
textbox->text_box = text_box_alloc();
Gui* gui = furi_record_open(RECORD_GUI);
textbox->view_holder = view_holder_alloc();
view_holder_attach_to_gui(textbox->view_holder, gui);
view_holder_set_back_callback(textbox->view_holder, textbox_exit, textbox);
view_holder_set_view(textbox->view_holder, text_box_get_view(textbox->text_box));
*object = textbox_obj;
return textbox;
}
static void js_textbox_destroy(void* inst) {
JsTextboxInst* textbox = inst;
view_holder_stop(textbox->view_holder);
view_holder_free(textbox->view_holder);
textbox->view_holder = NULL;
furi_record_close(RECORD_GUI);
text_box_reset(textbox->text_box);
furi_string_reset(textbox->text);
text_box_free(textbox->text_box);
furi_string_free(textbox->text);
free(textbox);
}
static const JsModuleDescriptor js_textbox_desc = {
"textbox",
js_textbox_create,
js_textbox_destroy,
};
static const FlipperAppPluginDescriptor textbox_plugin_descriptor = {
.appid = PLUGIN_APP_ID,
.ep_api_version = PLUGIN_API_VERSION,
.entry_point = &js_textbox_desc,
};
const FlipperAppPluginDescriptor* js_textbox_ep(void) {
return &textbox_plugin_descriptor;
}
+6 -4
View File
@@ -136,15 +136,17 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) {
furi_mutex_release(snake_state->mutex);
}
static void snake_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
static void snake_game_input_callback(InputEvent* input_event, void* context) {
furi_assert(context);
FuriMessageQueue* event_queue = context;
SnakeEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void snake_game_update_timer_callback(FuriMessageQueue* event_queue) {
furi_assert(event_queue);
static void snake_game_update_timer_callback(void* context) {
furi_assert(context);
FuriMessageQueue* event_queue = context;
SnakeEvent event = {.type = EventTypeTick};
furi_message_queue_put(event_queue, &event, 0);
+25 -10
View File
@@ -5,13 +5,30 @@
#include <FreeRTOS.h>
#include <queue.h>
struct FuriMessageQueue {
// !!! Semi-Opaque type inheritance, Very Fragile, DO NOT MOVE !!!
StaticQueue_t container;
// !!! Data buffer, must be last in the structure, DO NOT MOVE !!!
uint8_t buffer[];
};
FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) {
furi_check((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U));
QueueHandle_t handle = xQueueCreate(msg_count, msg_size);
furi_check(handle);
FuriMessageQueue* instance = malloc(sizeof(FuriMessageQueue) + msg_count * msg_size);
return ((FuriMessageQueue*)handle);
// 3 things happens here:
// - create queue
// - check results
// - ensure that queue container is first in the FuriMessageQueue structure
//
// As a bonus it guarantees that FuriMessageQueue* can be casted into StaticQueue_t* or QueueHandle_t.
furi_check(
xQueueCreateStatic(msg_count, msg_size, instance->buffer, &instance->container) ==
(void*)instance);
return instance;
}
void furi_message_queue_free(FuriMessageQueue* instance) {
@@ -19,6 +36,7 @@ void furi_message_queue_free(FuriMessageQueue* instance) {
furi_check(instance);
vQueueDelete((QueueHandle_t)instance);
free(instance);
}
FuriStatus
@@ -102,11 +120,10 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin
uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) {
furi_check(instance);
StaticQueue_t* mq = (StaticQueue_t*)instance;
uint32_t capacity;
/* capacity = pxQueue->uxLength */
capacity = mq->uxDummy4[1];
capacity = instance->container.uxDummy4[1];
/* Return maximum number of messages */
return (capacity);
@@ -115,11 +132,10 @@ uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) {
uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) {
furi_check(instance);
StaticQueue_t* mq = (StaticQueue_t*)instance;
uint32_t size;
/* size = pxQueue->uxItemSize */
size = mq->uxDummy4[2];
size = instance->container.uxDummy4[2];
/* Return maximum message size */
return (size);
@@ -144,7 +160,6 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {
uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
furi_check(instance);
StaticQueue_t* mq = (StaticQueue_t*)instance;
uint32_t space;
uint32_t isrm;
@@ -152,11 +167,11 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
isrm = taskENTER_CRITICAL_FROM_ISR();
/* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */
space = mq->uxDummy4[1] - mq->uxDummy4[0];
space = instance->container.uxDummy4[1] - instance->container.uxDummy4[0];
taskEXIT_CRITICAL_FROM_ISR(isrm);
} else {
space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)mq);
space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)instance);
}
/* Return number of available slots */
+1 -1
View File
@@ -10,7 +10,7 @@
extern "C" {
#endif
typedef void FuriMessageQueue;
typedef struct FuriMessageQueue FuriMessageQueue;
/** Allocate furi message queue
*
+1
View File
@@ -411,6 +411,7 @@ bool protocol_electra_write_data(ProtocolElectra* protocol, void* data) {
};
void protocol_electra_render_data(ProtocolElectra* protocol, FuriString* result) {
protocol_electra_encoder_start(protocol);
furi_string_printf(result, "Epilogue: %016llX", protocol->encoded_epilogue);
};
+1 -1
View File
@@ -280,7 +280,7 @@ static void mjs_append_stack_trace_line(struct mjs* mjs, size_t offset) {
const char* filename = mjs_get_bcode_filename_by_offset(mjs, offset);
int line_no = mjs_get_lineno_by_offset(mjs, offset);
char* new_line = NULL;
const char* fmt = "at %s:%d\n";
const char* fmt = "\tat %s:%d\r\n";
if(filename == NULL) {
// fprintf(
// stderr,
+1
View File
@@ -33,6 +33,7 @@ env.Append(
File("protocols/mf_ultralight/mf_ultralight_poller.h"),
File("protocols/mf_classic/mf_classic_poller.h"),
File("protocols/mf_desfire/mf_desfire_poller.h"),
File("protocols/slix/slix_poller.h"),
File("protocols/st25tb/st25tb_poller.h"),
File("protocols/felica/felica_poller.h"),
# Listeners
+1 -1
View File
@@ -660,4 +660,4 @@ NfcError nfc_felica_listener_set_sensf_res_data(
return nfc_process_hal_error(error);
}
#endif // APP_UNIT_TESTS
#endif // FW_CFG_unit_tests
@@ -55,6 +55,7 @@ struct Nfc {
Iso14443_3aColResStatus col_res_status;
Iso14443_3aColResData col_res_data;
bool software_col_res_required;
NfcEventCallback callback;
void* context;
@@ -170,6 +171,7 @@ NfcError nfc_iso14443a_listener_set_col_res_data(
furi_check(atqa);
nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak);
instance->software_col_res_required = true;
return NfcErrorNone;
}
@@ -275,7 +277,8 @@ static int32_t nfc_worker_listener(void* context) {
} else if(message.type == NfcMessageTypeTx) {
nfc_test_print(
NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits);
if(instance->col_res_status != Iso14443_3aColResStatusDone) {
if(instance->software_col_res_required &&
(instance->col_res_status != Iso14443_3aColResStatusDone)) {
nfc_worker_listener_pass_col_res(
instance, message.data.data, message.data.data_bits);
} else {
@@ -8,7 +8,7 @@
#define TAG "Iso15693_3Listener"
#define ISO15693_3_LISTENER_BUFFER_SIZE (64U)
#define ISO15693_3_LISTENER_BUFFER_SIZE (256U)
Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) {
furi_assert(nfc);
@@ -67,6 +67,7 @@ NfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) {
if(nfc_event->type == NfcEventTypeRxEnd) {
BitBuffer* rx_buffer = nfc_event->data.buffer;
bit_buffer_reset(instance->tx_buffer);
if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {
iso13239_crc_trim(rx_buffer);
@@ -64,7 +64,9 @@ static Iso15693_3Error iso15693_3_listener_inventory_handler(
if(afi_flag) {
const uint8_t afi = *data++;
// When AFI flag is set, ignore non-matching requests
if(afi != instance->data->system_info.afi) break;
if(afi != 0) {
if(afi != instance->data->system_info.afi) break;
}
}
const uint8_t mask_len = *data++;
@@ -260,16 +262,9 @@ static Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler(
}
const uint32_t block_index_start = request->first_block_num;
const uint32_t block_index_end = block_index_start + request->block_count;
const uint32_t block_count = request->block_count + 1;
const uint32_t block_count_max = instance->data->system_info.block_count;
const uint32_t block_count_available = block_count_max - block_index_start;
if(block_count > block_count_available) {
error = Iso15693_3ErrorInternal;
break;
}
const uint32_t block_index_end =
MIN((block_index_start + request->block_count + 1),
((uint32_t)instance->data->system_info.block_count - 1));
error = iso15693_3_listener_extension_handler(
instance,
+63 -1
View File
@@ -14,6 +14,7 @@
#define SLIX_TYPE_INDICATOR_SLIX (0x02U)
#define SLIX_TYPE_INDICATOR_SLIX2 (0x01U)
#define SLIX_CAPABILITIES_KEY "Capabilities"
#define SLIX_PASSWORD_READ_KEY "Password Read"
#define SLIX_PASSWORD_WRITE_KEY "Password Write"
#define SLIX_PASSWORD_PRIVACY_KEY "Password Privacy"
@@ -69,6 +70,11 @@ static const SlixTypeFeatures slix_type_features[] = {
[SlixTypeSlix2] = SLIX_TYPE_FEATURES_SLIX2,
};
static const char* slix_capabilities_names[SlixCapabilitiesCount] = {
[SlixCapabilitiesDefault] = "Default",
[SlixCapabilitiesAcceptAllPasswords] = "AcceptAllPasswords",
};
typedef struct {
const char* key;
SlixTypeFeatures feature_flag;
@@ -110,6 +116,7 @@ void slix_reset(SlixData* data) {
furi_check(data);
iso15693_3_reset(data->iso15693_3_data);
data->capabilities = SlixCapabilitiesDefault;
slix_password_set_defaults(data->passwords);
memset(&data->system_info, 0, sizeof(SlixSystemInfo));
@@ -123,6 +130,7 @@ void slix_copy(SlixData* data, const SlixData* other) {
furi_check(other);
iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data);
data->capabilities = other->capabilities;
memcpy(data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount);
memcpy(data->signature, other->signature, sizeof(SlixSignature));
@@ -138,6 +146,30 @@ bool slix_verify(SlixData* data, const FuriString* device_type) {
return false;
}
static bool slix_load_capabilities(SlixData* data, FlipperFormat* ff) {
bool capabilities_loaded = false;
FuriString* capabilities_str = furi_string_alloc();
if(!flipper_format_read_string(ff, SLIX_CAPABILITIES_KEY, capabilities_str)) {
if(flipper_format_rewind(ff)) {
data->capabilities = SlixCapabilitiesDefault;
capabilities_loaded = true;
}
} else {
for(size_t i = 0; i < COUNT_OF(slix_capabilities_names); i++) {
if(furi_string_cmp_str(capabilities_str, slix_capabilities_names[i]) == 0) {
data->capabilities = i;
capabilities_loaded = true;
break;
}
}
}
furi_string_free(capabilities_str);
return capabilities_loaded;
}
static bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {
bool ret = true;
@@ -164,13 +196,14 @@ bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) {
furi_check(ff);
bool loaded = false;
do {
if(!iso15693_3_load(data->iso15693_3_data, ff, version)) break;
const SlixType slix_type = slix_get_type(data);
if(slix_type >= SlixTypeCount) break;
if(!slix_load_capabilities(data, ff)) break;
if(!slix_load_passwords(data->passwords, slix_type, ff)) break;
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) {
@@ -220,6 +253,33 @@ bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) {
return loaded;
}
static bool slix_save_capabilities(const SlixData* data, FlipperFormat* ff) {
bool save_success = false;
FuriString* tmp_str = furi_string_alloc();
do {
furi_string_set_str(
tmp_str, "SLIX capabilities field affects emulation modes. Possible options: ");
for(size_t i = 0; i < SlixCapabilitiesCount; i++) {
furi_string_cat_str(tmp_str, slix_capabilities_names[i]);
if(i < SlixCapabilitiesCount - 1) {
furi_string_cat(tmp_str, ", ");
}
}
if(!flipper_format_write_comment_cstr(ff, furi_string_get_cstr(tmp_str))) break;
if(!flipper_format_write_string_cstr(
ff, SLIX_CAPABILITIES_KEY, slix_capabilities_names[data->capabilities]))
break;
save_success = true;
} while(false);
furi_string_free(tmp_str);
return save_success;
}
static bool
slix_save_passwords(const SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {
bool ret = true;
@@ -251,6 +311,8 @@ bool slix_save(const SlixData* data, FlipperFormat* ff) {
if(!iso15693_3_save(data->iso15693_3_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, SLIX_PROTOCOL_NAME " specific data")) break;
if(!slix_save_capabilities(data, ff)) break;
if(!flipper_format_write_comment_cstr(
ff,
"Passwords are optional. If a password is omitted, a default value will be used"))
+8
View File
@@ -91,12 +91,20 @@ typedef struct {
SlixLockBits lock_bits;
} SlixSystemInfo;
typedef enum {
SlixCapabilitiesDefault,
SlixCapabilitiesAcceptAllPasswords,
SlixCapabilitiesCount,
} SlixCapabilities;
typedef struct {
Iso15693_3Data* iso15693_3_data;
SlixSystemInfo system_info;
SlixSignature signature;
SlixPassword passwords[SlixPasswordTypeCount];
SlixPrivacy privacy;
SlixCapabilities capabilities;
} SlixData;
SlixData* slix_alloc(void);
+7
View File
@@ -54,6 +54,13 @@ static SlixError slix_listener_set_password(
}
SlixListenerSessionState* session_state = &instance->session_state;
// With AcceptAllPassword capability set skip password validation
if(instance->data->capabilities == SlixCapabilitiesAcceptAllPasswords) {
session_state->password_match[password_type] = true;
break;
}
session_state->password_match[password_type] =
(password == slix_get_password(slix_data, password_type));
+4 -2
View File
@@ -114,7 +114,8 @@ static NfcCommand slix_poller_handler_check_privacy_password(SlixPoller* instanc
break;
}
instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd);
instance->error = slix_poller_set_password(
instance, SlixPasswordTypePrivacy, pwd, instance->random_number);
if(instance->error != SlixErrorNone) {
command = NfcCommandReset;
break;
@@ -145,7 +146,8 @@ static NfcCommand slix_poller_handler_privacy_unlock(SlixPoller* instance) {
instance->error = slix_poller_get_random_number(instance, &instance->random_number);
if(instance->error != SlixErrorNone) break;
instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd);
instance->error = slix_poller_set_password(
instance, SlixPasswordTypePrivacy, pwd, instance->random_number);
if(instance->error != SlixErrorNone) {
command = NfcCommandReset;
break;
+8 -4
View File
@@ -107,12 +107,16 @@ SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber*
* Must ONLY be used inside the callback function.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[out] type SlixPasswordType instance.
* @param[out] password SlixPassword instance.
* @param[in] type SlixPasswordType instance.
* @param[in] password SlixPassword instance.
* @param[in] random_number SlixRandomNumber instance.
* @return SlixErrorNone on success, an error code on failure.
*/
SlixError
slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password);
SlixError slix_poller_set_password(
SlixPoller* instance,
SlixPasswordType type,
SlixPassword password,
SlixRandomNumber random_number);
#ifdef __cplusplus
}
+7 -4
View File
@@ -92,8 +92,11 @@ SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber*
return error;
}
SlixError
slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password) {
SlixError slix_poller_set_password(
SlixPoller* instance,
SlixPasswordType type,
SlixPassword password,
SlixRandomNumber random_number) {
furi_assert(instance);
bool skip_uid = (type == SlixPasswordTypePrivacy);
@@ -102,8 +105,8 @@ SlixError
uint8_t password_type = (0x01 << type);
bit_buffer_append_byte(instance->tx_buffer, password_type);
uint8_t rn_l = instance->random_number >> 8;
uint8_t rn_h = instance->random_number;
uint8_t rn_l = random_number >> 8;
uint8_t rn_h = random_number;
uint32_t double_rand_num = (rn_h << 24) | (rn_l << 16) | (rn_h << 8) | rn_l;
uint32_t xored_password = double_rand_num ^ password;
uint8_t xored_password_arr[4] = {};
+1
View File
@@ -25,6 +25,7 @@ env.Append(
File("subghz_protocol_registry.h"),
File("devices/cc1101_configs.h"),
File("devices/cc1101_int/cc1101_int_interconnect.h"),
File("subghz_file_encoder_worker.h"),
],
)
+8
View File
@@ -2,6 +2,10 @@
#include <furi_hal.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*SubGhzFileEncoderWorkerCallbackEnd)(void* context);
typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker;
@@ -59,3 +63,7 @@ void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance);
* @return bool - true if running
*/
bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance);
#ifdef __cplusplus
}
#endif

Some files were not shown because too many files have changed in this diff Show More