mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 12:42:30 +04:00
Merge branch 'fz-dev' into dev
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -55,3 +55,5 @@ openocd.log
|
|||||||
# PVS Studio temporary files
|
# PVS Studio temporary files
|
||||||
.PVS-Studio/
|
.PVS-Studio/
|
||||||
PVS-Studio.log
|
PVS-Studio.log
|
||||||
|
|
||||||
|
.gdbinit
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
//-V:BPTREE_DEF2:779,1086,557,773,512
|
//-V:BPTREE_DEF2:779,1086,557,773,512
|
||||||
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685
|
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685
|
||||||
//-V:ALGO_DEF:1048,747,1044
|
//-V:ALGO_DEF:1048,747,1044
|
||||||
|
//-V:TUPLE_DEF2:524,590,1001,760
|
||||||
|
|
||||||
# Non-severe malloc/null pointer deref warnings
|
# Non-severe malloc/null pointer deref warnings
|
||||||
//-V::522:2,3
|
//-V::522:2,3
|
||||||
|
|||||||
4
.vscode/example/launch.json
vendored
4
.vscode/example/launch.json
vendored
@@ -38,6 +38,7 @@
|
|||||||
"postAttachCommands": [
|
"postAttachCommands": [
|
||||||
// "compare-sections",
|
// "compare-sections",
|
||||||
"source debug/flipperapps.py",
|
"source debug/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
// "source debug/FreeRTOS/FreeRTOS.py",
|
// "source debug/FreeRTOS/FreeRTOS.py",
|
||||||
// "svd_load debug/STM32WB55_CM4.svd"
|
// "svd_load debug/STM32WB55_CM4.svd"
|
||||||
]
|
]
|
||||||
@@ -59,6 +60,7 @@
|
|||||||
"set confirm off",
|
"set confirm off",
|
||||||
"set mem inaccessible-by-default off",
|
"set mem inaccessible-by-default off",
|
||||||
"source debug/flipperapps.py",
|
"source debug/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
// "compare-sections",
|
// "compare-sections",
|
||||||
]
|
]
|
||||||
// "showDevDebugOutput": "raw",
|
// "showDevDebugOutput": "raw",
|
||||||
@@ -76,6 +78,7 @@
|
|||||||
"rtos": "FreeRTOS",
|
"rtos": "FreeRTOS",
|
||||||
"postAttachCommands": [
|
"postAttachCommands": [
|
||||||
"source debug/flipperapps.py",
|
"source debug/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
]
|
]
|
||||||
// "showDevDebugOutput": "raw",
|
// "showDevDebugOutput": "raw",
|
||||||
},
|
},
|
||||||
@@ -95,6 +98,7 @@
|
|||||||
],
|
],
|
||||||
"postAttachCommands": [
|
"postAttachCommands": [
|
||||||
"source debug/flipperapps.py",
|
"source debug/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
],
|
],
|
||||||
// "showDevDebugOutput": "raw",
|
// "showDevDebugOutput": "raw",
|
||||||
},
|
},
|
||||||
|
|||||||
34
SConstruct
34
SConstruct
@@ -43,6 +43,7 @@ distenv = coreenv.Clone(
|
|||||||
"jflash",
|
"jflash",
|
||||||
],
|
],
|
||||||
ENV=os.environ,
|
ENV=os.environ,
|
||||||
|
UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}",
|
||||||
)
|
)
|
||||||
|
|
||||||
firmware_env = distenv.AddFwProject(
|
firmware_env = distenv.AddFwProject(
|
||||||
@@ -140,21 +141,28 @@ distenv.Default(basic_dist)
|
|||||||
dist_dir = distenv.GetProjetDirName()
|
dist_dir = distenv.GetProjetDirName()
|
||||||
fap_dist = [
|
fap_dist = [
|
||||||
distenv.Install(
|
distenv.Install(
|
||||||
f"#/dist/{dist_dir}/apps/debug_elf",
|
distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
|
||||||
firmware_env["FW_EXTAPPS"]["debug"].values(),
|
list(
|
||||||
|
app_artifact.debug
|
||||||
|
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
|
||||||
|
),
|
||||||
),
|
),
|
||||||
*(
|
distenv.Install(
|
||||||
distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1])
|
f"#/dist/{dist_dir}/apps",
|
||||||
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
|
"#/assets/resources/apps",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
|
Depends(
|
||||||
|
fap_dist,
|
||||||
|
list(
|
||||||
|
app_artifact.validator
|
||||||
|
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
|
||||||
|
),
|
||||||
|
)
|
||||||
Alias("fap_dist", fap_dist)
|
Alias("fap_dist", fap_dist)
|
||||||
# distenv.Default(fap_dist)
|
# distenv.Default(fap_dist)
|
||||||
|
|
||||||
distenv.Depends(
|
distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist)
|
||||||
firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"]["resources_dist"]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Target for bundling core2 package for qFlipper
|
# Target for bundling core2 package for qFlipper
|
||||||
@@ -192,6 +200,9 @@ firmware_debug = distenv.PhonyTarget(
|
|||||||
source=firmware_env["FW_ELF"],
|
source=firmware_env["FW_ELF"],
|
||||||
GDBOPTS="${GDBOPTS_BASE}",
|
GDBOPTS="${GDBOPTS_BASE}",
|
||||||
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||||
|
FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace(
|
||||||
|
"\\", "/"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
distenv.Depends(firmware_debug, firmware_flash)
|
distenv.Depends(firmware_debug, firmware_flash)
|
||||||
|
|
||||||
@@ -201,6 +212,9 @@ distenv.PhonyTarget(
|
|||||||
source=firmware_env["FW_ELF"],
|
source=firmware_env["FW_ELF"],
|
||||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||||
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||||
|
FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace(
|
||||||
|
"\\", "/"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Debug alien elf
|
# Debug alien elf
|
||||||
@@ -209,7 +223,7 @@ distenv.PhonyTarget(
|
|||||||
"${GDBPYCOM}",
|
"${GDBPYCOM}",
|
||||||
GDBOPTS="${GDBOPTS_BASE}",
|
GDBOPTS="${GDBOPTS_BASE}",
|
||||||
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||||
GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ',
|
GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ',
|
||||||
)
|
)
|
||||||
|
|
||||||
distenv.PhonyTarget(
|
distenv.PhonyTarget(
|
||||||
|
|||||||
@@ -424,6 +424,7 @@ MU_TEST(infrared_test_decoder_mixed) {
|
|||||||
infrared_test_run_decoder(InfraredProtocolRC5, 5);
|
infrared_test_run_decoder(InfraredProtocolRC5, 5);
|
||||||
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
|
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
|
||||||
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
|
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(infrared_test_decoder_nec) {
|
MU_TEST(infrared_test_decoder_nec) {
|
||||||
@@ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) {
|
|||||||
infrared_test_run_encoder(InfraredProtocolRC6, 1);
|
infrared_test_run_encoder(InfraredProtocolRC6, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(infrared_test_decoder_kaseikyo) {
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 2);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 3);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 4);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 5);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 6);
|
||||||
|
}
|
||||||
|
|
||||||
MU_TEST(infrared_test_encoder_decoder_all) {
|
MU_TEST(infrared_test_encoder_decoder_all) {
|
||||||
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
|
||||||
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
|
||||||
@@ -498,6 +508,7 @@ MU_TEST(infrared_test_encoder_decoder_all) {
|
|||||||
infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
|
||||||
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
|
||||||
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
|
||||||
|
infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST_SUITE(infrared_test) {
|
MU_TEST_SUITE(infrared_test) {
|
||||||
@@ -515,6 +526,7 @@ MU_TEST_SUITE(infrared_test) {
|
|||||||
MU_RUN_TEST(infrared_test_decoder_nec);
|
MU_RUN_TEST(infrared_test_decoder_nec);
|
||||||
MU_RUN_TEST(infrared_test_decoder_samsung32);
|
MU_RUN_TEST(infrared_test_decoder_samsung32);
|
||||||
MU_RUN_TEST(infrared_test_decoder_necext1);
|
MU_RUN_TEST(infrared_test_decoder_necext1);
|
||||||
|
MU_RUN_TEST(infrared_test_decoder_kaseikyo);
|
||||||
MU_RUN_TEST(infrared_test_decoder_mixed);
|
MU_RUN_TEST(infrared_test_decoder_mixed);
|
||||||
MU_RUN_TEST(infrared_test_encoder_decoder_all);
|
MU_RUN_TEST(infrared_test_encoder_decoder_all);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ App(
|
|||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
icon="A_BadUsb_14",
|
icon="A_BadUsb_14",
|
||||||
order=70,
|
order=70,
|
||||||
|
fap_libs=["assets"],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -347,10 +347,6 @@ static int32_t
|
|||||||
furi_hal_hid_kb_release(key);
|
furi_hal_hid_kb_release(key);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
if(error != NULL) {
|
|
||||||
strncpy(error, "Unknown error", error_len);
|
|
||||||
}
|
|
||||||
return SCRIPT_STATE_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
|
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
|
||||||
@@ -671,7 +667,7 @@ static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) {
|
|||||||
BadUsbScript* bad_usb_script_open(FuriString* file_path) {
|
BadUsbScript* bad_usb_script_open(FuriString* file_path) {
|
||||||
furi_assert(file_path);
|
furi_assert(file_path);
|
||||||
|
|
||||||
BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
|
BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); //-V773
|
||||||
bad_usb->file_path = furi_string_alloc();
|
bad_usb->file_path = furi_string_alloc();
|
||||||
furi_string_set(bad_usb->file_path, file_path);
|
furi_string_set(bad_usb->file_path, file_path);
|
||||||
bad_usb_script_set_default_keyboard_layout(bad_usb);
|
bad_usb_script_set_default_keyboard_layout(bad_usb);
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ static bool fap_loader_select_app(FapLoader* loader) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static FapLoader* fap_loader_alloc(const char* path) {
|
static FapLoader* fap_loader_alloc(const char* path) {
|
||||||
FapLoader* loader = malloc(sizeof(FapLoader));
|
FapLoader* loader = malloc(sizeof(FapLoader)); //-V773
|
||||||
loader->fap_path = furi_string_alloc_set(path);
|
loader->fap_path = furi_string_alloc_set(path);
|
||||||
loader->storage = furi_record_open(RECORD_STORAGE);
|
loader->storage = furi_record_open(RECORD_STORAGE);
|
||||||
loader->dialogs = furi_record_open(RECORD_DIALOGS);
|
loader->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ App(
|
|||||||
stack_size=1 * 1024,
|
stack_size=1 * 1024,
|
||||||
icon="A_GPIO_14",
|
icon="A_GPIO_14",
|
||||||
order=50,
|
order=50,
|
||||||
|
fap_libs=["assets"],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ static int32_t usb_uart_worker(void* context) {
|
|||||||
while(1) {
|
while(1) {
|
||||||
uint32_t events =
|
uint32_t events =
|
||||||
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
||||||
furi_check((events & FuriFlagError) == 0);
|
furi_check(!(events & FuriFlagError));
|
||||||
if(events & WorkerEvtStop) break;
|
if(events & WorkerEvtStop) break;
|
||||||
if(events & WorkerEvtRxDone) {
|
if(events & WorkerEvtRxDone) {
|
||||||
size_t len = furi_stream_buffer_receive(
|
size_t len = furi_stream_buffer_receive(
|
||||||
@@ -288,7 +288,7 @@ static int32_t usb_uart_tx_thread(void* context) {
|
|||||||
while(1) {
|
while(1) {
|
||||||
uint32_t events =
|
uint32_t events =
|
||||||
furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
||||||
furi_check((events & FuriFlagError) == 0);
|
furi_check(!(events & FuriFlagError));
|
||||||
if(events & WorkerEvtTxStop) break;
|
if(events & WorkerEvtTxStop) break;
|
||||||
if(events & WorkerEvtCdcRx) {
|
if(events & WorkerEvtCdcRx) {
|
||||||
furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk);
|
furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ App(
|
|||||||
icon="A_iButton_14",
|
icon="A_iButton_14",
|
||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
order=60,
|
order=60,
|
||||||
|
fap_libs=["assets"],
|
||||||
)
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ App(
|
|||||||
icon="A_Infrared_14",
|
icon="A_Infrared_14",
|
||||||
stack_size=3 * 1024,
|
stack_size=3 * 1024,
|
||||||
order=40,
|
order=40,
|
||||||
|
fap_libs=["assets"],
|
||||||
)
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ App(
|
|||||||
icon="A_125khz_14",
|
icon="A_125khz_14",
|
||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
order=20,
|
order=20,
|
||||||
|
fap_libs=["assets"],
|
||||||
)
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static LfRfid* lfrfid_alloc() {
|
static LfRfid* lfrfid_alloc() {
|
||||||
LfRfid* lfrfid = malloc(sizeof(LfRfid));
|
LfRfid* lfrfid = malloc(sizeof(LfRfid)); //-V773
|
||||||
|
|
||||||
lfrfid->storage = furi_record_open(RECORD_STORAGE);
|
lfrfid->storage = furi_record_open(RECORD_STORAGE);
|
||||||
lfrfid->dialogs = furi_record_open(RECORD_DIALOGS);
|
lfrfid->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) {
|
|||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc);
|
widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc);
|
||||||
|
|
||||||
FuriString* temp_str;
|
FuriString* temp_str = NULL;
|
||||||
if(furi_string_size(nfc->dev->dev_data.parsed_data)) {
|
if(furi_string_size(nfc->dev->dev_data.parsed_data)) {
|
||||||
temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data);
|
temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) {
|
|||||||
nfc_scene_mf_ultralight_read_success_widget_callback,
|
nfc_scene_mf_ultralight_read_success_widget_callback,
|
||||||
nfc);
|
nfc);
|
||||||
|
|
||||||
FuriString* temp_str;
|
FuriString* temp_str = NULL;
|
||||||
if(furi_string_size(nfc->dev->dev_data.parsed_data)) {
|
if(furi_string_size(nfc->dev->dev_data.parsed_data)) {
|
||||||
temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data);
|
temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -520,12 +520,6 @@ bool subghz_path_is_file(FuriString* path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t subghz_random_serial(void) {
|
uint32_t subghz_random_serial(void) {
|
||||||
static bool rand_generator_inited = false;
|
|
||||||
|
|
||||||
if(!rand_generator_inited) {
|
|
||||||
srand(DWT->CYCCNT);
|
|
||||||
rand_generator_inited = true;
|
|
||||||
}
|
|
||||||
return (uint32_t)rand();
|
return (uint32_t)rand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ App(
|
|||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
icon="A_U2F_14",
|
icon="A_U2F_14",
|
||||||
order=80,
|
order=80,
|
||||||
|
fap_libs=["assets"],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ static int32_t u2f_hid_worker(void* context) {
|
|||||||
WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest,
|
WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest,
|
||||||
FuriFlagWaitAny,
|
FuriFlagWaitAny,
|
||||||
FuriWaitForever);
|
FuriWaitForever);
|
||||||
furi_check((flags & FuriFlagError) == 0);
|
furi_check(!(flags & FuriFlagError));
|
||||||
if(flags & WorkerEvtStop) break;
|
if(flags & WorkerEvtStop) break;
|
||||||
if(flags & WorkerEvtConnect) {
|
if(flags & WorkerEvtConnect) {
|
||||||
u2f_set_state(u2f_hid->u2f_instance, 1);
|
u2f_set_state(u2f_hid->u2f_instance, 1);
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
#include "dap_config.h"
|
#include "dap_config.h"
|
||||||
#include "gui/dap_gui.h"
|
#include "gui/dap_gui.h"
|
||||||
#include "usb/dap_v2_usb.h"
|
#include "usb/dap_v2_usb.h"
|
||||||
|
#include <dialogs/dialogs.h>
|
||||||
|
#include "dap_link_icons.h"
|
||||||
|
|
||||||
/***************************************************************************/
|
/***************************************************************************/
|
||||||
/****************************** DAP COMMON *********************************/
|
/****************************** DAP COMMON *********************************/
|
||||||
@@ -495,6 +497,24 @@ DapConfig* dap_app_get_config(DapApp* app) {
|
|||||||
int32_t dap_link_app(void* p) {
|
int32_t dap_link_app(void* p) {
|
||||||
UNUSED(p);
|
UNUSED(p);
|
||||||
|
|
||||||
|
if(furi_hal_usb_is_locked()) {
|
||||||
|
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
|
DialogMessage* message = dialog_message_alloc();
|
||||||
|
dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop);
|
||||||
|
dialog_message_set_text(
|
||||||
|
message,
|
||||||
|
"Disconnect from\nPC or phone to\nuse this function.",
|
||||||
|
3,
|
||||||
|
30,
|
||||||
|
AlignLeft,
|
||||||
|
AlignTop);
|
||||||
|
dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0);
|
||||||
|
dialog_message_show(dialogs, message);
|
||||||
|
dialog_message_free(message);
|
||||||
|
furi_record_close(RECORD_DIALOGS);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// alloc app
|
// alloc app
|
||||||
DapApp* app = dap_app_alloc();
|
DapApp* app = dap_app_alloc();
|
||||||
app_handle = app;
|
app_handle = app;
|
||||||
|
|||||||
BIN
applications/plugins/dap_link/icons/ActiveConnection_50x64.png
Normal file
BIN
applications/plugins/dap_link/icons/ActiveConnection_50x64.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -315,7 +315,6 @@ static void
|
|||||||
|
|
||||||
int32_t snake_game_app(void* p) {
|
int32_t snake_game_app(void* p) {
|
||||||
UNUSED(p);
|
UNUSED(p);
|
||||||
srand(DWT->CYCCNT);
|
|
||||||
|
|
||||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent));
|
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent));
|
||||||
|
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* ou
|
|||||||
"%s %dbit\r\n"
|
"%s %dbit\r\n"
|
||||||
"Key:0x%lX%08lX\r\n"
|
"Key:0x%lX%08lX\r\n"
|
||||||
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
||||||
"Temp:%d.%d C Hum:%d%%",
|
"Temp:%3.1f C Hum:%d%%",
|
||||||
instance->generic.protocol_name,
|
instance->generic.protocol_name,
|
||||||
instance->generic.data_count_bit,
|
instance->generic.data_count_bit,
|
||||||
(uint32_t)(instance->generic.data >> 32),
|
(uint32_t)(instance->generic.data >> 32),
|
||||||
@@ -301,7 +301,6 @@ void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* ou
|
|||||||
instance->generic.id,
|
instance->generic.id,
|
||||||
instance->generic.channel,
|
instance->generic.channel,
|
||||||
instance->generic.battery_low,
|
instance->generic.battery_low,
|
||||||
(int16_t)instance->generic.temp,
|
(double)instance->generic.temp,
|
||||||
abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))),
|
|
||||||
instance->generic.humidity);
|
instance->generic.humidity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* out
|
|||||||
"%s %dbit\r\n"
|
"%s %dbit\r\n"
|
||||||
"Key:0x%lX%08lX\r\n"
|
"Key:0x%lX%08lX\r\n"
|
||||||
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
||||||
"Temp:%d.%d C Hum:%d%%",
|
"Temp:%3.1f C Hum:%d%%",
|
||||||
instance->generic.protocol_name,
|
instance->generic.protocol_name,
|
||||||
instance->generic.data_count_bit,
|
instance->generic.data_count_bit,
|
||||||
(uint32_t)(instance->generic.data >> 32),
|
(uint32_t)(instance->generic.data >> 32),
|
||||||
@@ -242,7 +242,6 @@ void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* out
|
|||||||
instance->generic.id,
|
instance->generic.id,
|
||||||
instance->generic.channel,
|
instance->generic.channel,
|
||||||
instance->generic.battery_low,
|
instance->generic.battery_low,
|
||||||
(int16_t)instance->generic.temp,
|
(double)instance->generic.temp,
|
||||||
abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))),
|
|
||||||
instance->generic.humidity);
|
instance->generic.humidity);
|
||||||
}
|
}
|
||||||
|
|||||||
247
applications/plugins/weather_station/protocols/acurite_609txc.c
Normal file
247
applications/plugins/weather_station/protocols/acurite_609txc.c
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
#include "acurite_609txc.h"
|
||||||
|
|
||||||
|
#define TAG "WSProtocolAcurite_609TXC"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Help
|
||||||
|
* https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L216
|
||||||
|
*
|
||||||
|
* 0000 1111 | 0011 0000 | 0101 1100 | 0000 0000 | 1110 0111
|
||||||
|
* iiii iiii | buuu tttt | tttt tttt | hhhh hhhh | cccc cccc
|
||||||
|
* - i: identification; changes on battery switch
|
||||||
|
* - c: checksum (sum of previous by bytes)
|
||||||
|
* - u: unknown
|
||||||
|
* - b: battery low; flag to indicate low battery voltage
|
||||||
|
* - t: temperature; in °C * 10, 12 bit with complement
|
||||||
|
* - h: humidity
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const SubGhzBlockConst ws_protocol_acurite_609txc_const = {
|
||||||
|
.te_short = 500,
|
||||||
|
.te_long = 1000,
|
||||||
|
.te_delta = 150,
|
||||||
|
.min_count_bit_for_found = 40,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolDecoderAcurite_609TXC {
|
||||||
|
SubGhzProtocolDecoderBase base;
|
||||||
|
|
||||||
|
SubGhzBlockDecoder decoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolEncoderAcurite_609TXC {
|
||||||
|
SubGhzProtocolEncoderBase base;
|
||||||
|
|
||||||
|
SubGhzProtocolBlockEncoder encoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
Acurite_609TXCDecoderStepReset = 0,
|
||||||
|
Acurite_609TXCDecoderStepSaveDuration,
|
||||||
|
Acurite_609TXCDecoderStepCheckDuration,
|
||||||
|
} Acurite_609TXCDecoderStep;
|
||||||
|
|
||||||
|
const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder = {
|
||||||
|
.alloc = ws_protocol_decoder_acurite_609txc_alloc,
|
||||||
|
.free = ws_protocol_decoder_acurite_609txc_free,
|
||||||
|
|
||||||
|
.feed = ws_protocol_decoder_acurite_609txc_feed,
|
||||||
|
.reset = ws_protocol_decoder_acurite_609txc_reset,
|
||||||
|
|
||||||
|
.get_hash_data = ws_protocol_decoder_acurite_609txc_get_hash_data,
|
||||||
|
.serialize = ws_protocol_decoder_acurite_609txc_serialize,
|
||||||
|
.deserialize = ws_protocol_decoder_acurite_609txc_deserialize,
|
||||||
|
.get_string = ws_protocol_decoder_acurite_609txc_get_string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder = {
|
||||||
|
.alloc = NULL,
|
||||||
|
.free = NULL,
|
||||||
|
|
||||||
|
.deserialize = NULL,
|
||||||
|
.stop = NULL,
|
||||||
|
.yield = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocol ws_protocol_acurite_609txc = {
|
||||||
|
.name = WS_PROTOCOL_ACURITE_609TXC_NAME,
|
||||||
|
.type = SubGhzProtocolWeatherStation,
|
||||||
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||||
|
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||||
|
|
||||||
|
.decoder = &ws_protocol_acurite_609txc_decoder,
|
||||||
|
.encoder = &ws_protocol_acurite_609txc_encoder,
|
||||||
|
};
|
||||||
|
|
||||||
|
void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment) {
|
||||||
|
UNUSED(environment);
|
||||||
|
WSProtocolDecoderAcurite_609TXC* instance = malloc(sizeof(WSProtocolDecoderAcurite_609TXC));
|
||||||
|
instance->base.protocol = &ws_protocol_acurite_609txc;
|
||||||
|
instance->generic.protocol_name = instance->base.protocol->name;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_acurite_609txc_free(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderAcurite_609TXC* instance = context;
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_acurite_609txc_reset(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderAcurite_609TXC* instance = context;
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ws_protocol_acurite_609txc_check(WSProtocolDecoderAcurite_609TXC* instance) {
|
||||||
|
if(!instance->decoder.decode_data) return false;
|
||||||
|
uint8_t crc = (uint8_t)(instance->decoder.decode_data >> 32) +
|
||||||
|
(uint8_t)(instance->decoder.decode_data >> 24) +
|
||||||
|
(uint8_t)(instance->decoder.decode_data >> 16) +
|
||||||
|
(uint8_t)(instance->decoder.decode_data >> 8);
|
||||||
|
return (crc == (instance->decoder.decode_data & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analysis of received data
|
||||||
|
* @param instance Pointer to a WSBlockGeneric* instance
|
||||||
|
*/
|
||||||
|
static void ws_protocol_acurite_609txc_remote_controller(WSBlockGeneric* instance) {
|
||||||
|
instance->id = (instance->data >> 32) & 0xFF;
|
||||||
|
instance->battery_low = (instance->data >> 31) & 1;
|
||||||
|
|
||||||
|
instance->channel = WS_NO_CHANNEL;
|
||||||
|
|
||||||
|
// Temperature in Celsius is encoded as a 12 bit integer value
|
||||||
|
// multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2)
|
||||||
|
// negative values are recovered by sign extend from int16_t.
|
||||||
|
int16_t temp_raw =
|
||||||
|
(int16_t)(((instance->data >> 12) & 0xf000) | ((instance->data >> 16) << 4));
|
||||||
|
instance->temp = (temp_raw >> 4) * 0.1f;
|
||||||
|
instance->humidity = (instance->data >> 8) & 0xff;
|
||||||
|
instance->btn = WS_NO_BTN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderAcurite_609TXC* instance = context;
|
||||||
|
|
||||||
|
switch(instance->decoder.parser_step) {
|
||||||
|
case Acurite_609TXCDecoderStepReset:
|
||||||
|
if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short * 17) <
|
||||||
|
ws_protocol_acurite_609txc_const.te_delta * 8)) {
|
||||||
|
//Found syncPrefix
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration;
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Acurite_609TXCDecoderStepSaveDuration:
|
||||||
|
if(level) {
|
||||||
|
instance->decoder.te_last = duration;
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepCheckDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Acurite_609TXCDecoderStepCheckDuration:
|
||||||
|
if(!level) {
|
||||||
|
if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_609txc_const.te_short) <
|
||||||
|
ws_protocol_acurite_609txc_const.te_delta) {
|
||||||
|
if((DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short) <
|
||||||
|
ws_protocol_acurite_609txc_const.te_delta) ||
|
||||||
|
(duration > ws_protocol_acurite_609txc_const.te_long * 3)) {
|
||||||
|
//Found syncPostfix
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepReset;
|
||||||
|
if((instance->decoder.decode_count_bit ==
|
||||||
|
ws_protocol_acurite_609txc_const.min_count_bit_for_found) &&
|
||||||
|
ws_protocol_acurite_609txc_check(instance)) {
|
||||||
|
instance->generic.data = instance->decoder.decode_data;
|
||||||
|
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||||
|
ws_protocol_acurite_609txc_remote_controller(&instance->generic);
|
||||||
|
if(instance->base.callback)
|
||||||
|
instance->base.callback(&instance->base, instance->base.context);
|
||||||
|
}
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
} else if(
|
||||||
|
DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long) <
|
||||||
|
ws_protocol_acurite_609txc_const.te_delta * 2) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration;
|
||||||
|
} else if(
|
||||||
|
DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long * 2) <
|
||||||
|
ws_protocol_acurite_609txc_const.te_delta * 4) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepReset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepReset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = Acurite_609TXCDecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderAcurite_609TXC* instance = context;
|
||||||
|
return subghz_protocol_blocks_get_hash_data(
|
||||||
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_acurite_609txc_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderAcurite_609TXC* instance = context;
|
||||||
|
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderAcurite_609TXC* instance = context;
|
||||||
|
bool ret = false;
|
||||||
|
do {
|
||||||
|
if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(instance->generic.data_count_bit !=
|
||||||
|
ws_protocol_acurite_609txc_const.min_count_bit_for_found) {
|
||||||
|
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret = true;
|
||||||
|
} while(false);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderAcurite_609TXC* instance = context;
|
||||||
|
furi_string_printf(
|
||||||
|
output,
|
||||||
|
"%s %dbit\r\n"
|
||||||
|
"Key:0x%lX%08lX\r\n"
|
||||||
|
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
||||||
|
"Temp:%3.1f C Hum:%d%%",
|
||||||
|
instance->generic.protocol_name,
|
||||||
|
instance->generic.data_count_bit,
|
||||||
|
(uint32_t)(instance->generic.data >> 40),
|
||||||
|
(uint32_t)(instance->generic.data),
|
||||||
|
instance->generic.id,
|
||||||
|
instance->generic.channel,
|
||||||
|
instance->generic.battery_low,
|
||||||
|
(double)instance->generic.temp,
|
||||||
|
instance->generic.humidity);
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lib/subghz/protocols/base.h>
|
||||||
|
|
||||||
|
#include <lib/subghz/blocks/const.h>
|
||||||
|
#include <lib/subghz/blocks/decoder.h>
|
||||||
|
#include <lib/subghz/blocks/encoder.h>
|
||||||
|
#include "ws_generic.h"
|
||||||
|
#include <lib/subghz/blocks/math.h>
|
||||||
|
|
||||||
|
#define WS_PROTOCOL_ACURITE_609TXC_NAME "Acurite-609TXC"
|
||||||
|
|
||||||
|
typedef struct WSProtocolDecoderAcurite_609TXC WSProtocolDecoderAcurite_609TXC;
|
||||||
|
typedef struct WSProtocolEncoderAcurite_609TXC WSProtocolEncoderAcurite_609TXC;
|
||||||
|
|
||||||
|
extern const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder;
|
||||||
|
extern const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder;
|
||||||
|
extern const SubGhzProtocol ws_protocol_acurite_609txc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate WSProtocolDecoderAcurite_609TXC.
|
||||||
|
* @param environment Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return WSProtocolDecoderAcurite_609TXC* pointer to a WSProtocolDecoderAcurite_609TXC instance
|
||||||
|
*/
|
||||||
|
void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free WSProtocolDecoderAcurite_609TXC.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_acurite_609txc_free(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset decoder WSProtocolDecoderAcurite_609TXC.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_acurite_609txc_reset(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a raw sequence of levels and durations received from the air.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance
|
||||||
|
* @param level Signal level true-high false-low
|
||||||
|
* @param duration Duration of this level in, us
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting the hash sum of the last randomly received parcel.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance
|
||||||
|
* @return hash Hash sum
|
||||||
|
*/
|
||||||
|
uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize data WSProtocolDecoderAcurite_609TXC.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
|
* @return true On success
|
||||||
|
*/
|
||||||
|
bool ws_protocol_decoder_acurite_609txc_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize data WSProtocolDecoderAcurite_609TXC.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return true On success
|
||||||
|
*/
|
||||||
|
bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting a textual representation of the received data.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance
|
||||||
|
* @param output Resulting text
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output);
|
||||||
@@ -200,7 +200,6 @@ void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_
|
|||||||
((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) ==
|
((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) ==
|
||||||
AMBIENT_WEATHER_PACKET_HEADER_2)) {
|
AMBIENT_WEATHER_PACKET_HEADER_2)) {
|
||||||
if(ws_protocol_ambient_weather_check_crc(instance)) {
|
if(ws_protocol_ambient_weather_check_crc(instance)) {
|
||||||
instance->decoder.decode_data = instance->decoder.decode_data;
|
|
||||||
instance->generic.data = instance->decoder.decode_data;
|
instance->generic.data = instance->decoder.decode_data;
|
||||||
instance->generic.data_count_bit =
|
instance->generic.data_count_bit =
|
||||||
ws_protocol_ambient_weather_const.min_count_bit_for_found;
|
ws_protocol_ambient_weather_const.min_count_bit_for_found;
|
||||||
@@ -264,7 +263,7 @@ void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* o
|
|||||||
"%s %dbit\r\n"
|
"%s %dbit\r\n"
|
||||||
"Key:0x%lX%08lX\r\n"
|
"Key:0x%lX%08lX\r\n"
|
||||||
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
||||||
"Temp:%d.%d C Hum:%d%%",
|
"Temp:%3.1f C Hum:%d%%",
|
||||||
instance->generic.protocol_name,
|
instance->generic.protocol_name,
|
||||||
instance->generic.data_count_bit,
|
instance->generic.data_count_bit,
|
||||||
(uint32_t)(instance->generic.data >> 32),
|
(uint32_t)(instance->generic.data >> 32),
|
||||||
@@ -272,7 +271,6 @@ void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* o
|
|||||||
instance->generic.id,
|
instance->generic.id,
|
||||||
instance->generic.channel,
|
instance->generic.channel,
|
||||||
instance->generic.battery_low,
|
instance->generic.battery_low,
|
||||||
(int16_t)instance->generic.temp,
|
(double)instance->generic.temp,
|
||||||
abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))),
|
|
||||||
instance->generic.humidity);
|
instance->generic.humidity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output)
|
|||||||
"%s %dbit\r\n"
|
"%s %dbit\r\n"
|
||||||
"Key:0x%lX%08lX\r\n"
|
"Key:0x%lX%08lX\r\n"
|
||||||
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
||||||
"Temp:%d.%d C Hum:%d%%",
|
"Temp:%3.1f C Hum:%d%%",
|
||||||
instance->generic.protocol_name,
|
instance->generic.protocol_name,
|
||||||
instance->generic.data_count_bit,
|
instance->generic.data_count_bit,
|
||||||
(uint32_t)(instance->generic.data >> 32),
|
(uint32_t)(instance->generic.data >> 32),
|
||||||
@@ -335,7 +335,6 @@ void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output)
|
|||||||
instance->generic.id,
|
instance->generic.id,
|
||||||
instance->generic.channel,
|
instance->generic.channel,
|
||||||
instance->generic.battery_low,
|
instance->generic.battery_low,
|
||||||
(int16_t)instance->generic.temp,
|
(double)instance->generic.temp,
|
||||||
abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))),
|
|
||||||
instance->generic.humidity);
|
instance->generic.humidity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output)
|
|||||||
"%s %dbit\r\n"
|
"%s %dbit\r\n"
|
||||||
"Key:0x%lX%08lX\r\n"
|
"Key:0x%lX%08lX\r\n"
|
||||||
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
||||||
"Temp:%d.%d C Hum:%d%%",
|
"Temp:%3.1f C Hum:%d%%",
|
||||||
instance->generic.protocol_name,
|
instance->generic.protocol_name,
|
||||||
instance->generic.data_count_bit,
|
instance->generic.data_count_bit,
|
||||||
(uint32_t)(instance->generic.data >> 32),
|
(uint32_t)(instance->generic.data >> 32),
|
||||||
@@ -291,7 +291,6 @@ void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output)
|
|||||||
instance->generic.id,
|
instance->generic.id,
|
||||||
instance->generic.channel,
|
instance->generic.channel,
|
||||||
instance->generic.battery_low,
|
instance->generic.battery_low,
|
||||||
(int16_t)instance->generic.temp,
|
(double)instance->generic.temp,
|
||||||
abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))),
|
|
||||||
instance->generic.humidity);
|
instance->generic.humidity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriStrin
|
|||||||
"%s %dbit\r\n"
|
"%s %dbit\r\n"
|
||||||
"Key:0x%lX%08lX\r\n"
|
"Key:0x%lX%08lX\r\n"
|
||||||
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
||||||
"Temp:%d.%d C Hum:%d%%",
|
"Temp:%3.1f C Hum:%d%%",
|
||||||
instance->generic.protocol_name,
|
instance->generic.protocol_name,
|
||||||
instance->generic.data_count_bit,
|
instance->generic.data_count_bit,
|
||||||
(uint32_t)(instance->generic.data >> 32),
|
(uint32_t)(instance->generic.data >> 32),
|
||||||
@@ -292,7 +292,6 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriStrin
|
|||||||
instance->generic.id,
|
instance->generic.id,
|
||||||
instance->generic.channel,
|
instance->generic.channel,
|
||||||
instance->generic.battery_low,
|
instance->generic.battery_low,
|
||||||
(int16_t)instance->generic.temp,
|
(double)instance->generic.temp,
|
||||||
abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))),
|
|
||||||
instance->generic.humidity);
|
instance->generic.humidity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output)
|
|||||||
"%s %dbit\r\n"
|
"%s %dbit\r\n"
|
||||||
"Key:0x%lX%08lX\r\n"
|
"Key:0x%lX%08lX\r\n"
|
||||||
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
||||||
"Temp:%d.%d C Hum:%d%%",
|
"Temp:%3.1f C Hum:%d%%",
|
||||||
instance->generic.protocol_name,
|
instance->generic.protocol_name,
|
||||||
instance->generic.data_count_bit,
|
instance->generic.data_count_bit,
|
||||||
(uint32_t)(instance->generic.data >> 32),
|
(uint32_t)(instance->generic.data >> 32),
|
||||||
@@ -255,7 +255,6 @@ void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output)
|
|||||||
instance->generic.id,
|
instance->generic.id,
|
||||||
instance->generic.channel,
|
instance->generic.channel,
|
||||||
instance->generic.battery_low,
|
instance->generic.battery_low,
|
||||||
(int16_t)instance->generic.temp,
|
(double)instance->generic.temp,
|
||||||
abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))),
|
|
||||||
instance->generic.humidity);
|
instance->generic.humidity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = {
|
|||||||
&ws_protocol_nexus_th,
|
&ws_protocol_nexus_th,
|
||||||
&ws_protocol_gt_wt_03,
|
&ws_protocol_gt_wt_03,
|
||||||
&ws_protocol_acurite_606tx,
|
&ws_protocol_acurite_606tx,
|
||||||
|
&ws_protocol_acurite_609txc,
|
||||||
&ws_protocol_lacrosse_tx141thbv2,
|
&ws_protocol_lacrosse_tx141thbv2,
|
||||||
&ws_protocol_oregon2,
|
&ws_protocol_oregon2,
|
||||||
&ws_protocol_acurite_592txr,
|
&ws_protocol_acurite_592txr,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "nexus_th.h"
|
#include "nexus_th.h"
|
||||||
#include "gt_wt_03.h"
|
#include "gt_wt_03.h"
|
||||||
#include "acurite_606tx.h"
|
#include "acurite_606tx.h"
|
||||||
|
#include "acurite_609txc.h"
|
||||||
#include "lacrosse_tx141thbv2.h"
|
#include "lacrosse_tx141thbv2.h"
|
||||||
#include "oregon2.h"
|
#include "oregon2.h"
|
||||||
#include "acurite_592txr.h"
|
#include "acurite_592txr.h"
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* out
|
|||||||
"%s %dbit\r\n"
|
"%s %dbit\r\n"
|
||||||
"Key:0x%lX%08lX\r\n"
|
"Key:0x%lX%08lX\r\n"
|
||||||
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
"Sn:0x%lX Ch:%d Bat:%d\r\n"
|
||||||
"Temp:%d.%d C Hum:%d%%",
|
"Temp:%3.1f C Hum:%d%%",
|
||||||
instance->generic.protocol_name,
|
instance->generic.protocol_name,
|
||||||
instance->generic.data_count_bit,
|
instance->generic.data_count_bit,
|
||||||
(uint32_t)(instance->generic.data >> 32),
|
(uint32_t)(instance->generic.data >> 32),
|
||||||
@@ -254,7 +254,6 @@ void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* out
|
|||||||
instance->generic.id,
|
instance->generic.id,
|
||||||
instance->generic.channel,
|
instance->generic.channel,
|
||||||
instance->generic.battery_low,
|
instance->generic.battery_low,
|
||||||
(int16_t)instance->generic.temp,
|
(double)instance->generic.temp,
|
||||||
abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))),
|
|
||||||
instance->generic.humidity);
|
instance->generic.humidity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,12 +75,7 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) {
|
|||||||
|
|
||||||
if(model->generic->temp != WS_NO_TEMPERATURE) {
|
if(model->generic->temp != WS_NO_TEMPERATURE) {
|
||||||
canvas_draw_icon(canvas, 18, 42, &I_Therm_7x16);
|
canvas_draw_icon(canvas, 18, 42, &I_Therm_7x16);
|
||||||
snprintf(
|
snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp);
|
||||||
buffer,
|
|
||||||
sizeof(buffer),
|
|
||||||
"%3.2d.%d C",
|
|
||||||
(int16_t)model->generic->temp,
|
|
||||||
abs(((int16_t)(model->generic->temp * 10) - (((int16_t)model->generic->temp) * 10))));
|
|
||||||
canvas_draw_str_aligned(canvas, 63, 46, AlignRight, AlignTop, buffer);
|
canvas_draw_str_aligned(canvas, 63, 46, AlignRight, AlignTop, buffer);
|
||||||
canvas_draw_circle(canvas, 55, 45, 1);
|
canvas_draw_circle(canvas, 55, 45, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,15 +61,20 @@ static void gpio_print_pins(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum { OK, ERR_CMD_SYNTAX, ERR_PIN, ERR_VALUE } GpioParseError;
|
typedef enum {
|
||||||
|
GpioParseReturnOk,
|
||||||
|
GpioParseReturnCmdSyntaxError,
|
||||||
|
GpioParseReturnPinError,
|
||||||
|
GpioParseReturnValueError
|
||||||
|
} GpioParseReturn;
|
||||||
|
|
||||||
static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) {
|
static GpioParseReturn gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) {
|
||||||
FuriString* pin_name;
|
FuriString* pin_name;
|
||||||
pin_name = furi_string_alloc();
|
pin_name = furi_string_alloc();
|
||||||
|
|
||||||
size_t ws = furi_string_search_char(args, ' ');
|
size_t ws = furi_string_search_char(args, ' ');
|
||||||
if(ws == FURI_STRING_FAILURE) {
|
if(ws == FURI_STRING_FAILURE) {
|
||||||
return ERR_CMD_SYNTAX;
|
return GpioParseReturnCmdSyntaxError;
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_string_set_n(pin_name, args, 0, ws);
|
furi_string_set_n(pin_name, args, 0, ws);
|
||||||
@@ -78,7 +83,7 @@ static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint
|
|||||||
|
|
||||||
if(!pin_name_to_int(pin_name, pin_num)) {
|
if(!pin_name_to_int(pin_name, pin_num)) {
|
||||||
furi_string_free(pin_name);
|
furi_string_free(pin_name);
|
||||||
return ERR_PIN;
|
return GpioParseReturnPinError;
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_string_free(pin_name);
|
furi_string_free(pin_name);
|
||||||
@@ -88,10 +93,10 @@ static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint
|
|||||||
} else if(!furi_string_cmp(args, "1")) {
|
} else if(!furi_string_cmp(args, "1")) {
|
||||||
*value = 1;
|
*value = 1;
|
||||||
} else {
|
} else {
|
||||||
return ERR_VALUE;
|
return GpioParseReturnValueError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return GpioParseReturnOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) {
|
void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) {
|
||||||
@@ -101,15 +106,15 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) {
|
|||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
uint8_t value = 255;
|
uint8_t value = 255;
|
||||||
|
|
||||||
GpioParseError err = gpio_command_parse(args, &num, &value);
|
GpioParseReturn err = gpio_command_parse(args, &num, &value);
|
||||||
|
|
||||||
if(ERR_CMD_SYNTAX == err) {
|
if(err == GpioParseReturnCmdSyntaxError) {
|
||||||
cli_print_usage("gpio mode", "<pin_name> <0|1>", furi_string_get_cstr(args));
|
cli_print_usage("gpio mode", "<pin_name> <0|1>", furi_string_get_cstr(args));
|
||||||
return;
|
return;
|
||||||
} else if(ERR_PIN == err) {
|
} else if(err == GpioParseReturnPinError) {
|
||||||
gpio_print_pins();
|
gpio_print_pins();
|
||||||
return;
|
return;
|
||||||
} else if(ERR_VALUE == err) {
|
} else if(err == GpioParseReturnValueError) {
|
||||||
printf("Value is invalid. Enter 1 for input or 0 for output");
|
printf("Value is invalid. Enter 1 for input or 0 for output");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -161,15 +166,15 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) {
|
|||||||
|
|
||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
uint8_t value = 0;
|
uint8_t value = 0;
|
||||||
GpioParseError err = gpio_command_parse(args, &num, &value);
|
GpioParseReturn err = gpio_command_parse(args, &num, &value);
|
||||||
|
|
||||||
if(ERR_CMD_SYNTAX == err) {
|
if(err == GpioParseReturnCmdSyntaxError) {
|
||||||
cli_print_usage("gpio set", "<pin_name> <0|1>", furi_string_get_cstr(args));
|
cli_print_usage("gpio set", "<pin_name> <0|1>", furi_string_get_cstr(args));
|
||||||
return;
|
return;
|
||||||
} else if(ERR_PIN == err) {
|
} else if(err == GpioParseReturnPinError) {
|
||||||
gpio_print_pins();
|
gpio_print_pins();
|
||||||
return;
|
return;
|
||||||
} else if(ERR_VALUE == err) {
|
} else if(err == GpioParseReturnValueError) {
|
||||||
printf("Value is invalid. Enter 1 for high or 0 for low");
|
printf("Value is invalid. Enter 1 for high or 0 for low");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ static int32_t vcp_worker(void* context) {
|
|||||||
while(1) {
|
while(1) {
|
||||||
uint32_t flags =
|
uint32_t flags =
|
||||||
furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever);
|
furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever);
|
||||||
furi_assert((flags & FuriFlagError) == 0);
|
furi_assert(!(flags & FuriFlagError));
|
||||||
|
|
||||||
// VCP session opened
|
// VCP session opened
|
||||||
if(flags & VcpEvtConnect) {
|
if(flags & VcpEvtConnect) {
|
||||||
@@ -303,7 +303,7 @@ static void vcp_on_cdc_control_line(void* context, uint8_t state) {
|
|||||||
static void vcp_on_cdc_rx(void* context) {
|
static void vcp_on_cdc_rx(void* context) {
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx);
|
uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx);
|
||||||
furi_check((ret & FuriFlagError) == 0);
|
furi_check(!(ret & FuriFlagError));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vcp_on_cdc_tx_complete(void* context) {
|
static void vcp_on_cdc_tx_complete(void* context) {
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) {
|
|||||||
void crypto_cli_has_key(Cli* cli, FuriString* args) {
|
void crypto_cli_has_key(Cli* cli, FuriString* args) {
|
||||||
UNUSED(cli);
|
UNUSED(cli);
|
||||||
int key_slot = 0;
|
int key_slot = 0;
|
||||||
uint8_t iv[16];
|
uint8_t iv[16] = {0};
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if(!args_read_int_and_trim(args, &key_slot) || !(key_slot > 0 && key_slot <= 100)) {
|
if(!args_read_int_and_trim(args, &key_slot) || !(key_slot > 0 && key_slot <= 100)) {
|
||||||
@@ -249,7 +249,7 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(key_slot > 0) {
|
if(key_slot > 0) {
|
||||||
uint8_t iv[16];
|
uint8_t iv[16] = {0};
|
||||||
if(key_slot > 1) {
|
if(key_slot > 1) {
|
||||||
if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) {
|
if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) {
|
||||||
printf(
|
printf(
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, vo
|
|||||||
const CanvasCallbackPair p = {callback, context};
|
const CanvasCallbackPair p = {callback, context};
|
||||||
|
|
||||||
gui_lock(gui);
|
gui_lock(gui);
|
||||||
furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 0);
|
furi_assert(!CanvasCallbackPairArray_count(gui->canvas_callback_pair, p));
|
||||||
CanvasCallbackPairArray_push_back(gui->canvas_callback_pair, p);
|
CanvasCallbackPairArray_push_back(gui->canvas_callback_pair, p);
|
||||||
gui_unlock(gui);
|
gui_unlock(gui);
|
||||||
|
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ static int32_t browser_worker(void* context) {
|
|||||||
|
|
||||||
BrowserWorker*
|
BrowserWorker*
|
||||||
file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets) {
|
file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets) {
|
||||||
BrowserWorker* browser = malloc(sizeof(BrowserWorker));
|
BrowserWorker* browser = malloc(sizeof(BrowserWorker)); //-V773
|
||||||
|
|
||||||
idx_last_array_init(browser->idx_last);
|
idx_last_array_init(browser->idx_last);
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ WidgetElement* widget_element_button_create(
|
|||||||
ButtonCallback callback,
|
ButtonCallback callback,
|
||||||
void* context) {
|
void* context) {
|
||||||
// Allocate and init model
|
// Allocate and init model
|
||||||
GuiButtonModel* model = malloc(sizeof(GuiButtonModel));
|
GuiButtonModel* model = malloc(sizeof(GuiButtonModel)); //-V773
|
||||||
model->button_type = button_type;
|
model->button_type = button_type;
|
||||||
model->callback = callback;
|
model->callback = callback;
|
||||||
model->context = context;
|
model->context = context;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) {
|
|||||||
gui_remove_view_port(view_dispatcher->gui, view_dispatcher->view_port);
|
gui_remove_view_port(view_dispatcher->gui, view_dispatcher->view_port);
|
||||||
}
|
}
|
||||||
// Crash if not all views were freed
|
// Crash if not all views were freed
|
||||||
furi_assert(ViewDict_size(view_dispatcher->views) == 0);
|
furi_assert(!ViewDict_size(view_dispatcher->views));
|
||||||
|
|
||||||
ViewDict_clear(view_dispatcher->views);
|
ViewDict_clear(view_dispatcher->views);
|
||||||
// Free ViewPort
|
// Free ViewPort
|
||||||
@@ -157,7 +157,7 @@ void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_
|
|||||||
view_dispatcher->ongoing_input_view = NULL;
|
view_dispatcher->ongoing_input_view = NULL;
|
||||||
}
|
}
|
||||||
// Remove view
|
// Remove view
|
||||||
ViewDict_erase(view_dispatcher->views, view_id);
|
furi_check(ViewDict_erase(view_dispatcher->views, view_id));
|
||||||
|
|
||||||
view_set_update_callback(view, NULL);
|
view_set_update_callback(view, NULL);
|
||||||
view_set_update_callback_context(view, NULL);
|
view_set_update_callback_context(view, NULL);
|
||||||
|
|||||||
@@ -269,22 +269,18 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
|
|||||||
event.type = LoaderEventTypeApplicationStarted;
|
event.type = LoaderEventTypeApplicationStarted;
|
||||||
furi_pubsub_publish(loader_instance->pubsub, &event);
|
furi_pubsub_publish(loader_instance->pubsub, &event);
|
||||||
|
|
||||||
if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) {
|
if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) {
|
||||||
furi_hal_power_insomnia_enter();
|
furi_hal_power_insomnia_enter();
|
||||||
}
|
}
|
||||||
} else if(thread_state == FuriThreadStateStopped) {
|
} else if(thread_state == FuriThreadStateStopped) {
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(TAG, "Application stopped. Free heap: %d", memmgr_get_free_heap());
|
||||||
TAG,
|
|
||||||
"Application thread stopped. Free heap: %d. Thread allocation balance: %d.",
|
|
||||||
memmgr_get_free_heap(),
|
|
||||||
furi_thread_get_heap_size(instance->application_thread));
|
|
||||||
|
|
||||||
if(loader_instance->application_arguments) {
|
if(loader_instance->application_arguments) {
|
||||||
free(loader_instance->application_arguments);
|
free(loader_instance->application_arguments);
|
||||||
loader_instance->application_arguments = NULL;
|
loader_instance->application_arguments = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) {
|
if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) {
|
||||||
furi_hal_power_insomnia_exit();
|
furi_hal_power_insomnia_exit();
|
||||||
}
|
}
|
||||||
loader_unlock(instance);
|
loader_unlock(instance);
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args
|
|||||||
uint32_t buffer_size;
|
uint32_t buffer_size;
|
||||||
int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size);
|
int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size);
|
||||||
|
|
||||||
if(parsed_count == EOF || parsed_count != 1) {
|
if(parsed_count != 1) {
|
||||||
storage_cli_print_usage();
|
storage_cli_print_usage();
|
||||||
} else if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
} else if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||||
uint64_t file_size = storage_file_size(file);
|
uint64_t file_size = storage_file_size(file);
|
||||||
@@ -315,7 +315,7 @@ static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args
|
|||||||
uint32_t buffer_size;
|
uint32_t buffer_size;
|
||||||
int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size);
|
int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size);
|
||||||
|
|
||||||
if(parsed_count == EOF || parsed_count != 1) {
|
if(parsed_count != 1) {
|
||||||
storage_cli_print_usage();
|
storage_cli_print_usage();
|
||||||
} else {
|
} else {
|
||||||
if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||||
|
|||||||
@@ -545,8 +545,8 @@ static FS_Error
|
|||||||
|
|
||||||
FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path) {
|
FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path) {
|
||||||
FS_Error error;
|
FS_Error error;
|
||||||
const char* new_path_tmp;
|
const char* new_path_tmp = NULL;
|
||||||
FuriString* new_path_next;
|
FuriString* new_path_next = NULL;
|
||||||
new_path_next = furi_string_alloc();
|
new_path_next = furi_string_alloc();
|
||||||
|
|
||||||
FileInfo fileinfo;
|
FileInfo fileinfo;
|
||||||
|
|||||||
105
assets/unit_tests/infrared/test_kaseikyo.irtest
Normal file
105
assets/unit_tests/infrared/test_kaseikyo.irtest
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
Filetype: IR tests file
|
||||||
|
Version: 1
|
||||||
|
#
|
||||||
|
name: decoder_input1
|
||||||
|
type: raw
|
||||||
|
data: 1000000 3363 1685 407 436 411 432 415 1240 434 410 437 1245 439 404 433 1249 435 408 439 431 406 1249 435 435 412 405 442 1241 433 1249 435 408 439 405 442 428 409 434 413 430 407 411 436 433 414 429 408 1248 436 407 440 1243 441 428 409 434 413 431 406 1249 435 1248 436 406 441 1242 442 1240 434 409 438 431 416 428 409 408 439 430 407 411 436 407 440 429 408 436 411 432 415 402 435 1247 437 1245 439 1243 441 1238 436
|
||||||
|
#
|
||||||
|
name: decoder_expected1
|
||||||
|
type: parsed_array
|
||||||
|
count: 1
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 41 54 32 00
|
||||||
|
command: 1B 00 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
|
name: decoder_input2
|
||||||
|
type: raw
|
||||||
|
data: 1000000 3365 1683 409 434 413 431 406 1276 408 435 412 1270 414 429 408 1248 436 434 413 430 407 1275 409 434 413 431 406 1276 408 1248 436 433 414 430 407 437 410 433 414 429 408 436 411 432 415 428 409 1246 438 432 415 1267 407 437 410 433 414 429 408 436 411 432 415 1266 408 1250 434 1248 436 432 415 429 408 435 412 432 415 428 409 434 413 430 407 437 410 433 414 429 408 436 411 432 415 428 409 435 412 1240 434
|
||||||
|
#
|
||||||
|
name: decoder_expected2
|
||||||
|
type: parsed_array
|
||||||
|
count: 1
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 41 54 32 00
|
||||||
|
command: 1C 00 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
|
name: decoder_input3
|
||||||
|
type: raw
|
||||||
|
data: 1000000 3361 1661 442 427 410 434 413 1243 441 428 409 1247 437 432 415 1241 433 410 437 407 440 1242 432 437 410 407 440 1242 442 1241 433 436 411 407 440 430 407 436 411 406 441 402 435 435 412 431 416 1240 434 410 437 1245 439 404 433 411 436 407 440 403 434 436 411 432 415 429 408 1249 435 1247 437 1245 439 430 407 1250 434 434 413 404 433 438 409 434 413 1243 441 1241 433 410 437 1245 439 430 407 1250 434 432 415
|
||||||
|
#
|
||||||
|
name: decoder_expected3
|
||||||
|
type: parsed_array
|
||||||
|
count: 1
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 41 54 32 00
|
||||||
|
command: 70 01 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
|
name: decoder_input4
|
||||||
|
type: raw
|
||||||
|
data: 1000000 3365 1656 436 406 441 402 435 1248 436 406 441 1242 432 410 437 1246 438 404 433 410 437 1246 438 404 433 437 410 1245 491 1190 442 401 436 435 412 431 416 427 410 433 414 429 408 435 412 431 416 1240 434 435 412 1244 440 1241 433 436 411 433 414 402 435 409 438 405 442 402 435 1247 437 1244 440 1241 433 437 410 1245 439 430 407 410 437 406 441 402 435 409 438 1243 441 402 435 1247 437 406 441 1240 434 433 414
|
||||||
|
#
|
||||||
|
name: decoder_expected4
|
||||||
|
type: parsed_array
|
||||||
|
count: 1
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 43 54 32 00
|
||||||
|
command: 70 01 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
|
name: decoder_input5
|
||||||
|
type: raw
|
||||||
|
data: 1000000 3357 1665 438 431 416 428 409 1247 437 432 415 1241 433 436 411 1245 439 430 407 436 411 1245 439 430 407 437 410 1246 438 1243 441 428 409 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 427 410 1247 437 1245 439 430 407 437 410 1246 438 1244 440 429 408 1250 434 1248 488 355 440 429 408 436 411 432 415 428 408 435 412 431 416 428 409 1247 437 432 415 428 409 1248 436 1246 490 1191 441 1240 434
|
||||||
|
#
|
||||||
|
name: decoder_expected5
|
||||||
|
type: parsed_array
|
||||||
|
count: 1
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 43 54 32 00
|
||||||
|
command: 1B 00 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
|
name: decoder_input6
|
||||||
|
type: raw
|
||||||
|
data: 1000000 3358 1664 439 430 407 437 410 1245 439 430 407 1250 434 434 413 1243 441 428 409 435 412 1244 440 428 409 435 412 1244 440 1242 432 437 410 434 413 430 407 436 411 432 415 428 409 435 412 431 416 1240 434 435 412 1244 440 1242 442 427 410 434 413 1243 441 427 409 1247 437 433 414 429 408 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 1240 486 357 438 432 415 1240 434 436 411 432 415 425 412
|
||||||
|
#
|
||||||
|
name: decoder_expected6
|
||||||
|
type: parsed_array
|
||||||
|
count: 1
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 43 54 32 00
|
||||||
|
command: 05 00 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
|
name: encoder_decoder_input1
|
||||||
|
type: parsed_array
|
||||||
|
count: 4
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 41 54 32 00
|
||||||
|
command: 1B 00 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 41 54 32 00
|
||||||
|
command: 70 01 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 43 54 32 00
|
||||||
|
command: 05 00 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
|
protocol: Kaseikyo
|
||||||
|
address: 43 54 32 00
|
||||||
|
command: 1B 00 00 00
|
||||||
|
repeat: false
|
||||||
|
#
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Tuple, Dict
|
from typing import Optional, Tuple, Dict, ClassVar
|
||||||
import struct
|
import struct
|
||||||
import posixpath
|
import posixpath
|
||||||
import os
|
import os
|
||||||
@@ -22,14 +22,18 @@ class AppState:
|
|||||||
debug_link_elf: str = ""
|
debug_link_elf: str = ""
|
||||||
debug_link_crc: int = 0
|
debug_link_crc: int = 0
|
||||||
|
|
||||||
|
DEBUG_ELF_ROOT: ClassVar[Optional[str]] = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.other_sections is None:
|
if self.other_sections is None:
|
||||||
self.other_sections = {}
|
self.other_sections = {}
|
||||||
|
|
||||||
def get_original_elf_path(self, elf_path="build/latest/.extapps") -> str:
|
def get_original_elf_path(self) -> str:
|
||||||
|
if self.DEBUG_ELF_ROOT is None:
|
||||||
|
raise ValueError("DEBUG_ELF_ROOT not set; call fap-set-debug-elf-root")
|
||||||
return (
|
return (
|
||||||
posixpath.join(elf_path, self.debug_link_elf)
|
posixpath.join(self.DEBUG_ELF_ROOT, self.debug_link_elf)
|
||||||
if elf_path
|
if self.DEBUG_ELF_ROOT
|
||||||
else self.debug_link_elf
|
else self.debug_link_elf
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -84,7 +88,9 @@ class AppState:
|
|||||||
if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]):
|
if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]):
|
||||||
debug_link_data = (
|
debug_link_data = (
|
||||||
gdb.selected_inferior()
|
gdb.selected_inferior()
|
||||||
.read_memory(int(app_state["debug_link_info"]["debug_link"]), debug_link_size)
|
.read_memory(
|
||||||
|
int(app_state["debug_link_info"]["debug_link"]), debug_link_size
|
||||||
|
)
|
||||||
.tobytes()
|
.tobytes()
|
||||||
)
|
)
|
||||||
state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data(
|
state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data(
|
||||||
@@ -103,6 +109,29 @@ class AppState:
|
|||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
class SetFapDebugElfRoot(gdb.Command):
|
||||||
|
"""Set path to original ELF files for debug info"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
"fap-set-debug-elf-root", gdb.COMMAND_FILES, gdb.COMPLETE_FILENAME
|
||||||
|
)
|
||||||
|
self.dont_repeat()
|
||||||
|
|
||||||
|
def invoke(self, arg, from_tty):
|
||||||
|
AppState.DEBUG_ELF_ROOT = arg
|
||||||
|
try:
|
||||||
|
global helper
|
||||||
|
print(f"Set '{arg}' as debug info lookup path for Flipper external apps")
|
||||||
|
helper.attach_fw()
|
||||||
|
gdb.events.stop.connect(helper.handle_stop)
|
||||||
|
except gdb.error as e:
|
||||||
|
print(f"Support for Flipper external apps debug is not available: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
SetFapDebugElfRoot()
|
||||||
|
|
||||||
|
|
||||||
class FlipperAppDebugHelper:
|
class FlipperAppDebugHelper:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app_ptr = None
|
self.app_ptr = None
|
||||||
@@ -149,9 +178,4 @@ class FlipperAppDebugHelper:
|
|||||||
|
|
||||||
|
|
||||||
helper = FlipperAppDebugHelper()
|
helper = FlipperAppDebugHelper()
|
||||||
try:
|
print("Support for Flipper external apps debug is loaded")
|
||||||
helper.attach_fw()
|
|
||||||
print("Support for Flipper external apps debug is enabled")
|
|
||||||
gdb.events.stop.connect(helper.handle_stop)
|
|
||||||
except gdb.error as e:
|
|
||||||
print(f"Support for Flipper external apps debug is not available: {e}")
|
|
||||||
|
|||||||
49
documentation/UnitTests.md
Normal file
49
documentation/UnitTests.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Unit tests
|
||||||
|
## Intro
|
||||||
|
Unit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they were correct.
|
||||||
|
They are crucial for writing robust, bug-free code.
|
||||||
|
|
||||||
|
Flipper Zero firmware includes a separate application called [unit_tests](/applications/debug/unit_tests).
|
||||||
|
It is run directly on the Flipper Zero in order to employ its hardware features and to rule out any platform-related differences.
|
||||||
|
|
||||||
|
When contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features.
|
||||||
|
Running existing unit tests is useful to ensure that the new code doesn't introduce any regressions.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
In order to run the unit tests, follow these steps:
|
||||||
|
1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests`.
|
||||||
|
2. Flash the firmware using your preferred method.
|
||||||
|
3. Copy the [assets/unit_tests](assets/unit_tests) folder to the root your Flipper Zero's SD card.
|
||||||
|
4. Launch the CLI session and run the `unit_tests` command.
|
||||||
|
|
||||||
|
**NOTE:** To run a particular test (and skip all others), specify its name as the command argument.
|
||||||
|
See [test_index.c](applications/debug/unit_tests/test_index.c) for the complete list of test names.
|
||||||
|
|
||||||
|
## Adding unit tests
|
||||||
|
### General
|
||||||
|
#### Entry point
|
||||||
|
The common entry point for all tests it the [unit_tests](applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](applications/debug/unit_tests/test_index.c) source file.
|
||||||
|
#### Test assets
|
||||||
|
Some unit tests require external data in order to function. These files (commonly called assets) reside in the [assets/unit_tests](/assets/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat(FFF), binary etc).
|
||||||
|
### Application-specific
|
||||||
|
#### Infrared
|
||||||
|
Each infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol.
|
||||||
|
In order to add unit tests for your protocol, follow these steps:
|
||||||
|
1. Create a file named `test_<your_protocol_name>.irtest` in the [assets](assets/unit_tests/infrared) directory.
|
||||||
|
2. Fill it with the test data (more on it below).
|
||||||
|
3. Add the test code to [infrared_test.c](applications/debug/unit_tests/infrared/infrared_test.c).
|
||||||
|
4. Update the [assets](assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass.
|
||||||
|
|
||||||
|
##### Test data format
|
||||||
|
Each unit test has 3 sections:
|
||||||
|
1. `decoder` - takes in raw signal and outputs decoded messages.
|
||||||
|
2. `encoder` - takes in decoded messages and outputs raw signal.
|
||||||
|
3. `encoder_decoder` - takes in decoded messages, turns them into raw signal and then decodes again.
|
||||||
|
|
||||||
|
Infrared test asset files have an `.irtest` extension and are regular `.ir` files with a few additions.
|
||||||
|
Decoder input data has signal names `decoder_input_N`, where N is a test sequence number. Expected data goes under the name `decoder_expected_N`. When testing the encoder these two are switched.
|
||||||
|
|
||||||
|
Decoded data is represented in arrays (since a single raw signal may decode to several messages). If there is only one signal, then it has to be an array of size 1. Use the existing files as syntax examples.
|
||||||
|
|
||||||
|
##### Getting raw signals
|
||||||
|
Recording raw IR signals is possible using Flipper Zero. Launch the CLI session, run `ir rx raw`, then point the remote towards Flipper's receiver and send the signals. The raw signal data will be printed to the console in a convenient format.
|
||||||
@@ -49,12 +49,12 @@ OPENOCD_OPTS = [
|
|||||||
"-c",
|
"-c",
|
||||||
"transport select hla_swd",
|
"transport select hla_swd",
|
||||||
"-f",
|
"-f",
|
||||||
"debug/stm32wbx.cfg",
|
"${FBT_DEBUG_DIR}/stm32wbx.cfg",
|
||||||
"-c",
|
"-c",
|
||||||
"stm32wbx.cpu configure -rtos auto",
|
"stm32wbx.cpu configure -rtos auto",
|
||||||
]
|
]
|
||||||
|
|
||||||
SVD_FILE = "debug/STM32WB55_CM4.svd"
|
SVD_FILE = "${FBT_DEBUG_DIR}/STM32WB55_CM4.svd"
|
||||||
|
|
||||||
# Look for blackmagic probe on serial ports and local network
|
# Look for blackmagic probe on serial ports and local network
|
||||||
BLACKMAGIC = "auto"
|
BLACKMAGIC = "auto"
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ env = ENV.Clone(
|
|||||||
BUILD_DIR=fw_build_meta["build_dir"],
|
BUILD_DIR=fw_build_meta["build_dir"],
|
||||||
IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware",
|
IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware",
|
||||||
FW_FLAVOR=fw_build_meta["flavor"],
|
FW_FLAVOR=fw_build_meta["flavor"],
|
||||||
PLUGIN_ELF_DIR="${BUILD_DIR}",
|
LIB_DIST_DIR=fw_build_meta["build_dir"].Dir("lib"),
|
||||||
LIB_DIST_DIR="${BUILD_DIR}/lib",
|
|
||||||
LINT_SOURCES=[
|
LINT_SOURCES=[
|
||||||
"applications",
|
"applications",
|
||||||
],
|
],
|
||||||
@@ -142,12 +141,14 @@ for app_dir, _ in env["APPDIRS"]:
|
|||||||
|
|
||||||
fwenv.PrepareApplicationsBuild()
|
fwenv.PrepareApplicationsBuild()
|
||||||
|
|
||||||
# Build external apps
|
# Build external apps + configure SDK
|
||||||
if env["IS_BASE_FIRMWARE"]:
|
if env["IS_BASE_FIRMWARE"]:
|
||||||
extapps = fwenv["FW_EXTAPPS"] = SConscript(
|
fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT="${BUILD_DIR}/.extapps")
|
||||||
"site_scons/extapps.scons", exports={"ENV": fwenv}
|
fwenv["FW_EXTAPPS"] = SConscript(
|
||||||
|
"site_scons/extapps.scons",
|
||||||
|
exports={"ENV": fwenv},
|
||||||
)
|
)
|
||||||
fw_artifacts.append(extapps["sdk_tree"])
|
fw_artifacts.append(fwenv["FW_EXTAPPS"].sdk_tree)
|
||||||
|
|
||||||
|
|
||||||
# Add preprocessor definitions for current set of apps
|
# Add preprocessor definitions for current set of apps
|
||||||
@@ -263,7 +264,10 @@ fw_artifacts.extend(
|
|||||||
|
|
||||||
fwcdb = fwenv.CompilationDatabase()
|
fwcdb = fwenv.CompilationDatabase()
|
||||||
# without filtering, both updater & firmware commands would be generated in same file
|
# without filtering, both updater & firmware commands would be generated in same file
|
||||||
fwenv.Replace(COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*"))
|
fwenv.Replace(
|
||||||
|
COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*"),
|
||||||
|
COMPILATIONDB_SRCPATH_FILTER="*.c*",
|
||||||
|
)
|
||||||
AlwaysBuild(fwcdb)
|
AlwaysBuild(fwcdb)
|
||||||
Precious(fwcdb)
|
Precious(fwcdb)
|
||||||
NoClean(fwcdb)
|
NoClean(fwcdb)
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
|
|||||||
|
|
||||||
case EVT_BLUE_GAP_PASS_KEY_REQUEST: {
|
case EVT_BLUE_GAP_PASS_KEY_REQUEST: {
|
||||||
// Generate random PIN code
|
// Generate random PIN code
|
||||||
uint32_t pin = rand() % 999999;
|
uint32_t pin = rand() % 999999; //-V1064
|
||||||
aci_gap_pass_key_resp(gap->service.connection_handle, pin);
|
aci_gap_pass_key_resp(gap->service.connection_handle, pin);
|
||||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
|
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
|
||||||
FURI_LOG_I(TAG, "Pass key request event. Pin: ******");
|
FURI_LOG_I(TAG, "Pass key request event. Pin: ******");
|
||||||
@@ -478,7 +478,6 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
|
|||||||
|
|
||||||
gap = malloc(sizeof(Gap));
|
gap = malloc(sizeof(Gap));
|
||||||
gap->config = config;
|
gap->config = config;
|
||||||
srand(DWT->CYCCNT);
|
|
||||||
// Create advertising timer
|
// Create advertising timer
|
||||||
gap->advertise_timer = furi_timer_alloc(gap_advetise_timer_callback, FuriTimerTypeOnce, NULL);
|
gap->advertise_timer = furi_timer_alloc(gap_advetise_timer_callback, FuriTimerTypeOnce, NULL);
|
||||||
// Initialization of GATT & GAP layer
|
// Initialization of GATT & GAP layer
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ bool furi_hal_crypto_verify_key(uint8_t key_slot) {
|
|||||||
uint8_t keys_nb = 0;
|
uint8_t keys_nb = 0;
|
||||||
uint8_t valid_keys_nb = 0;
|
uint8_t valid_keys_nb = 0;
|
||||||
uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS;
|
uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS;
|
||||||
uint8_t empty_iv[16];
|
uint8_t empty_iv[16] = {0};
|
||||||
furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb);
|
furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb);
|
||||||
if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key
|
if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key
|
||||||
if(key_slot > keys_nb) return false;
|
if(key_slot > keys_nb) return false;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ FURI_NORETURN void __furi_halt();
|
|||||||
/** Check condition and crash if check failed */
|
/** Check condition and crash if check failed */
|
||||||
#define furi_check(__e) \
|
#define furi_check(__e) \
|
||||||
do { \
|
do { \
|
||||||
if((__e) == 0) { \
|
if(!(__e)) { \
|
||||||
furi_crash("furi_check failed\r\n"); \
|
furi_crash("furi_check failed\r\n"); \
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
@@ -55,7 +55,7 @@ FURI_NORETURN void __furi_halt();
|
|||||||
#ifdef FURI_DEBUG
|
#ifdef FURI_DEBUG
|
||||||
#define furi_assert(__e) \
|
#define furi_assert(__e) \
|
||||||
do { \
|
do { \
|
||||||
if((__e) == 0) { \
|
if(!(__e)) { \
|
||||||
furi_crash("furi_assert failed\r\n"); \
|
furi_crash("furi_assert failed\r\n"); \
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {
|
|||||||
uint32_t rflags;
|
uint32_t rflags;
|
||||||
BaseType_t yield;
|
BaseType_t yield;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
yield = pdFALSE;
|
yield = pdFALSE;
|
||||||
if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {
|
if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {
|
||||||
rflags = (uint32_t)FuriStatusErrorResource;
|
rflags = (uint32_t)FuriStatusErrorResource;
|
||||||
@@ -48,7 +48,7 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) {
|
|||||||
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
|
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
|
||||||
uint32_t rflags;
|
uint32_t rflags;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
rflags = xEventGroupGetBitsFromISR(hEventGroup);
|
rflags = xEventGroupGetBitsFromISR(hEventGroup);
|
||||||
|
|
||||||
if(xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) {
|
if(xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) {
|
||||||
@@ -73,7 +73,7 @@ uint32_t furi_event_flag_get(FuriEventFlag* instance) {
|
|||||||
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
|
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
|
||||||
uint32_t rflags;
|
uint32_t rflags;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
rflags = xEventGroupGetBitsFromISR(hEventGroup);
|
rflags = xEventGroupGetBitsFromISR(hEventGroup);
|
||||||
} else {
|
} else {
|
||||||
rflags = xEventGroupGetBits(hEventGroup);
|
rflags = xEventGroupGetBits(hEventGroup);
|
||||||
|
|||||||
@@ -150,8 +150,7 @@ void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) {
|
|||||||
vTaskSuspendAll();
|
vTaskSuspendAll();
|
||||||
{
|
{
|
||||||
memmgr_heap_thread_trace_depth++;
|
memmgr_heap_thread_trace_depth++;
|
||||||
furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) != NULL);
|
furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id));
|
||||||
MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id);
|
|
||||||
memmgr_heap_thread_trace_depth--;
|
memmgr_heap_thread_trace_depth--;
|
||||||
}
|
}
|
||||||
(void)xTaskResumeAll();
|
(void)xTaskResumeAll();
|
||||||
@@ -212,7 +211,8 @@ static inline void traceFREE(void* pointer, size_t size) {
|
|||||||
MemmgrHeapAllocDict_t* alloc_dict =
|
MemmgrHeapAllocDict_t* alloc_dict =
|
||||||
MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id);
|
MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id);
|
||||||
if(alloc_dict) {
|
if(alloc_dict) {
|
||||||
MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer);
|
// In some cases thread may want to release memory that was not allocated by it
|
||||||
|
(void)MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer);
|
||||||
}
|
}
|
||||||
memmgr_heap_thread_trace_depth--;
|
memmgr_heap_thread_trace_depth--;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) {
|
|||||||
|
|
||||||
stat = FuriStatusOk;
|
stat = FuriStatusOk;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
stat = FuriStatusErrorISR;
|
stat = FuriStatusErrorISR;
|
||||||
} else if(hMutex == NULL) {
|
} else if(hMutex == NULL) {
|
||||||
stat = FuriStatusErrorParameter;
|
stat = FuriStatusErrorParameter;
|
||||||
@@ -85,7 +85,7 @@ FuriStatus furi_mutex_release(FuriMutex* instance) {
|
|||||||
|
|
||||||
stat = FuriStatusOk;
|
stat = FuriStatusOk;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
stat = FuriStatusErrorISR;
|
stat = FuriStatusErrorISR;
|
||||||
} else if(hMutex == NULL) {
|
} else if(hMutex == NULL) {
|
||||||
stat = FuriStatusErrorParameter;
|
stat = FuriStatusErrorParameter;
|
||||||
@@ -111,7 +111,7 @@ FuriThreadId furi_mutex_get_owner(FuriMutex* instance) {
|
|||||||
|
|
||||||
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
|
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
|
||||||
|
|
||||||
if((FURI_IS_IRQ_MODE() != 0U) || (hMutex == NULL)) {
|
if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) {
|
||||||
owner = 0;
|
owner = 0;
|
||||||
} else {
|
} else {
|
||||||
owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex);
|
owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {
|
|||||||
|
|
||||||
stat = FuriStatusOk;
|
stat = FuriStatusOk;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
if(timeout != 0U) {
|
if(timeout != 0U) {
|
||||||
stat = FuriStatusErrorParameter;
|
stat = FuriStatusErrorParameter;
|
||||||
} else {
|
} else {
|
||||||
@@ -80,7 +80,7 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
|
|||||||
|
|
||||||
stat = FuriStatusOk;
|
stat = FuriStatusOk;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
yield = pdFALSE;
|
yield = pdFALSE;
|
||||||
|
|
||||||
if(xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) {
|
if(xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) {
|
||||||
@@ -104,7 +104,7 @@ uint32_t furi_semaphore_get_count(FuriSemaphore* instance) {
|
|||||||
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
|
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore);
|
count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore);
|
||||||
} else {
|
} else {
|
||||||
count = (uint32_t)uxSemaphoreGetCount(hSemaphore);
|
count = (uint32_t)uxSemaphoreGetCount(hSemaphore);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ size_t furi_stream_buffer_send(
|
|||||||
uint32_t timeout) {
|
uint32_t timeout) {
|
||||||
size_t ret;
|
size_t ret;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
BaseType_t yield;
|
BaseType_t yield;
|
||||||
ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield);
|
ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield);
|
||||||
portYIELD_FROM_ISR(yield);
|
portYIELD_FROM_ISR(yield);
|
||||||
@@ -41,7 +41,7 @@ size_t furi_stream_buffer_receive(
|
|||||||
uint32_t timeout) {
|
uint32_t timeout) {
|
||||||
size_t ret;
|
size_t ret;
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE() != 0U) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
BaseType_t yield;
|
BaseType_t yield;
|
||||||
ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield);
|
ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield);
|
||||||
portYIELD_FROM_ISR(yield);
|
portYIELD_FROM_ISR(yield);
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ FuriString* furi_string_alloc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FuriString* furi_string_alloc_set(const FuriString* s) {
|
FuriString* furi_string_alloc_set(const FuriString* s) {
|
||||||
FuriString* string = malloc(sizeof(FuriString));
|
FuriString* string = malloc(sizeof(FuriString)); //-V773
|
||||||
string_init_set(string->string, s->string);
|
string_init_set(string->string, s->string);
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
FuriString* furi_string_alloc_set_str(const char cstr[]) {
|
FuriString* furi_string_alloc_set_str(const char cstr[]) {
|
||||||
FuriString* string = malloc(sizeof(FuriString));
|
FuriString* string = malloc(sizeof(FuriString)); //-V773
|
||||||
string_init_set(string->string, cstr);
|
string_init_set(string->string, cstr);
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include <furi_hal_rtc.h>
|
#include <furi_hal_rtc.h>
|
||||||
#include <furi_hal_console.h>
|
#include <furi_hal_console.h>
|
||||||
|
|
||||||
|
#define TAG "FuriThread"
|
||||||
|
|
||||||
#define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers
|
#define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers
|
||||||
|
|
||||||
typedef struct FuriThreadStdout FuriThreadStdout;
|
typedef struct FuriThreadStdout FuriThreadStdout;
|
||||||
@@ -50,6 +52,7 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread);
|
|||||||
__attribute__((__noreturn__)) void furi_thread_catch() {
|
__attribute__((__noreturn__)) void furi_thread_catch() {
|
||||||
asm volatile("nop"); // extra magic
|
asm volatile("nop"); // extra magic
|
||||||
furi_crash("You are doing it wrong");
|
furi_crash("You are doing it wrong");
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) {
|
static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) {
|
||||||
@@ -81,6 +84,12 @@ static void furi_thread_body(void* context) {
|
|||||||
if(thread->heap_trace_enabled == true) {
|
if(thread->heap_trace_enabled == true) {
|
||||||
furi_delay_ms(33);
|
furi_delay_ms(33);
|
||||||
thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle);
|
thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle);
|
||||||
|
furi_log_print_format(
|
||||||
|
thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo,
|
||||||
|
TAG,
|
||||||
|
"%s allocation balance: %d",
|
||||||
|
thread->name ? thread->name : "Thread",
|
||||||
|
thread->heap_size);
|
||||||
memmgr_heap_disable_thread_trace((FuriThreadId)task_handle);
|
memmgr_heap_disable_thread_trace((FuriThreadId)task_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +97,8 @@ static void furi_thread_body(void* context) {
|
|||||||
|
|
||||||
if(thread->is_service) {
|
if(thread->is_service) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
"Service",
|
TAG,
|
||||||
"%s thread exited. Thread memory cannot be reclaimed.",
|
"%s service thread exited. Thread memory cannot be reclaimed.",
|
||||||
thread->name ? thread->name : "<unknown service>");
|
thread->name ? thread->name : "<unknown service>");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +121,12 @@ FuriThread* furi_thread_alloc() {
|
|||||||
FuriThread* thread = malloc(sizeof(FuriThread));
|
FuriThread* thread = malloc(sizeof(FuriThread));
|
||||||
thread->output.buffer = furi_string_alloc();
|
thread->output.buffer = furi_string_alloc();
|
||||||
thread->is_service = false;
|
thread->is_service = false;
|
||||||
|
|
||||||
|
if(furi_thread_get_current_id()) {
|
||||||
|
FuriThread* parent = pvTaskGetThreadLocalStoragePointer(NULL, 0);
|
||||||
|
if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa
|
|||||||
furi_crash("Unknown FF type");
|
furi_crash("Unknown FF type");
|
||||||
}
|
}
|
||||||
|
|
||||||
if((size_t)(i + 1) < write_data->data_size) {
|
if(((size_t)i + 1) < write_data->data_size) {
|
||||||
furi_string_cat(value, " ");
|
furi_string_cat(value, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ static InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder
|
|||||||
if(timings->min_split_time && !level) {
|
if(timings->min_split_time && !level) {
|
||||||
if(timing > timings->min_split_time) {
|
if(timing > timings->min_split_time) {
|
||||||
/* long low timing - check if we're ready for any of protocol modification */
|
/* long low timing - check if we're ready for any of protocol modification */
|
||||||
for(size_t i = 0; decoder->protocol->databit_len[i] &&
|
for(size_t i = 0; i < COUNT_OF(decoder->protocol->databit_len) &&
|
||||||
(i < COUNT_OF(decoder->protocol->databit_len));
|
decoder->protocol->databit_len[i];
|
||||||
++i) {
|
++i) {
|
||||||
if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {
|
if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {
|
||||||
return InfraredStatusReady;
|
return InfraredStatusReady;
|
||||||
@@ -199,7 +199,7 @@ InfraredMessage* infrared_common_decoder_check_ready(InfraredCommonDecoder* deco
|
|||||||
bool found_length = false;
|
bool found_length = false;
|
||||||
|
|
||||||
for(size_t i = 0;
|
for(size_t i = 0;
|
||||||
decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len));
|
i < COUNT_OF(decoder->protocol->databit_len) && decoder->protocol->databit_len[i];
|
||||||
++i) {
|
++i) {
|
||||||
if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {
|
if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {
|
||||||
found_length = true;
|
found_length = true;
|
||||||
|
|||||||
@@ -115,3 +115,26 @@ const InfraredCommonProtocolSpec protocol_sirc = {
|
|||||||
.decode_repeat = NULL,
|
.decode_repeat = NULL,
|
||||||
.encode_repeat = infrared_encoder_sirc_encode_repeat,
|
.encode_repeat = infrared_encoder_sirc_encode_repeat,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const InfraredCommonProtocolSpec protocol_kaseikyo = {
|
||||||
|
.timings =
|
||||||
|
{
|
||||||
|
.preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK,
|
||||||
|
.preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE,
|
||||||
|
.bit1_mark = INFRARED_KASEIKYO_BIT1_MARK,
|
||||||
|
.bit1_space = INFRARED_KASEIKYO_BIT1_SPACE,
|
||||||
|
.bit0_mark = INFRARED_KASEIKYO_BIT0_MARK,
|
||||||
|
.bit0_space = INFRARED_KASEIKYO_BIT0_SPACE,
|
||||||
|
.preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE,
|
||||||
|
.bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE,
|
||||||
|
.silence_time = INFRARED_KASEIKYO_SILENCE,
|
||||||
|
.min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME,
|
||||||
|
},
|
||||||
|
.databit_len[0] = 48,
|
||||||
|
.no_stop_bit = false,
|
||||||
|
.decode = infrared_common_decode_pdwm,
|
||||||
|
.encode = infrared_common_encode_pdwm,
|
||||||
|
.interpret = infrared_decoder_kaseikyo_interpret,
|
||||||
|
.decode_repeat = NULL,
|
||||||
|
.encode_repeat = NULL,
|
||||||
|
};
|
||||||
|
|||||||
@@ -110,6 +110,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = {
|
|||||||
.free = infrared_encoder_sirc_free},
|
.free = infrared_encoder_sirc_free},
|
||||||
.get_protocol_spec = infrared_sirc_get_spec,
|
.get_protocol_spec = infrared_sirc_get_spec,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.decoder =
|
||||||
|
{.alloc = infrared_decoder_kaseikyo_alloc,
|
||||||
|
.decode = infrared_decoder_kaseikyo_decode,
|
||||||
|
.reset = infrared_decoder_kaseikyo_reset,
|
||||||
|
.check_ready = infrared_decoder_kaseikyo_check_ready,
|
||||||
|
.free = infrared_decoder_kaseikyo_free},
|
||||||
|
.encoder =
|
||||||
|
{.alloc = infrared_encoder_kaseikyo_alloc,
|
||||||
|
.encode = infrared_encoder_kaseikyo_encode,
|
||||||
|
.reset = infrared_encoder_kaseikyo_reset,
|
||||||
|
.free = infrared_encoder_kaseikyo_free},
|
||||||
|
.get_protocol_spec = infrared_kaseikyo_get_spec,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int infrared_find_index_by_protocol(InfraredProtocol protocol);
|
static int infrared_find_index_by_protocol(InfraredProtocol protocol);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ typedef enum {
|
|||||||
InfraredProtocolSIRC,
|
InfraredProtocolSIRC,
|
||||||
InfraredProtocolSIRC15,
|
InfraredProtocolSIRC15,
|
||||||
InfraredProtocolSIRC20,
|
InfraredProtocolSIRC20,
|
||||||
|
InfraredProtocolKaseikyo,
|
||||||
InfraredProtocolMAX,
|
InfraredProtocolMAX,
|
||||||
} InfraredProtocol;
|
} InfraredProtocol;
|
||||||
|
|
||||||
|
|||||||
@@ -267,3 +267,54 @@ InfraredStatus infrared_encoder_sirc_encode_repeat(
|
|||||||
bool* level);
|
bool* level);
|
||||||
|
|
||||||
extern const InfraredCommonProtocolSpec protocol_sirc;
|
extern const InfraredCommonProtocolSpec protocol_sirc;
|
||||||
|
|
||||||
|
/***************************************************************************************************
|
||||||
|
* Kaseikyo protocol description
|
||||||
|
* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp
|
||||||
|
****************************************************************************************************
|
||||||
|
* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble
|
||||||
|
* mark space Modulation up to period repeat repeat
|
||||||
|
* mark space
|
||||||
|
*
|
||||||
|
* 3360 1665 48 bit ...130000 3456 1728
|
||||||
|
* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________
|
||||||
|
* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________
|
||||||
|
*
|
||||||
|
***************************************************************************************************/
|
||||||
|
|
||||||
|
#define INFRARED_KASEIKYO_UNIT 432
|
||||||
|
#define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT)
|
||||||
|
#define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT)
|
||||||
|
#define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT
|
||||||
|
#define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT)
|
||||||
|
#define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT
|
||||||
|
#define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT
|
||||||
|
#define INFRARED_KASEIKYO_REPEAT_PERIOD 130000
|
||||||
|
#define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD
|
||||||
|
#define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN
|
||||||
|
#define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000
|
||||||
|
#define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000
|
||||||
|
#define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK
|
||||||
|
#define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000)
|
||||||
|
#define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us
|
||||||
|
#define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us
|
||||||
|
|
||||||
|
void* infrared_decoder_kaseikyo_alloc(void);
|
||||||
|
void infrared_decoder_kaseikyo_reset(void* decoder);
|
||||||
|
void infrared_decoder_kaseikyo_free(void* decoder);
|
||||||
|
InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder);
|
||||||
|
InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration);
|
||||||
|
void* infrared_encoder_kaseikyo_alloc(void);
|
||||||
|
InfraredStatus
|
||||||
|
infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level);
|
||||||
|
void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message);
|
||||||
|
void infrared_encoder_kaseikyo_free(void* encoder_ptr);
|
||||||
|
bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder);
|
||||||
|
InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder);
|
||||||
|
InfraredStatus infrared_encoder_kaseikyo_encode_repeat(
|
||||||
|
InfraredCommonEncoder* encoder,
|
||||||
|
uint32_t* duration,
|
||||||
|
bool* level);
|
||||||
|
const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol);
|
||||||
|
|
||||||
|
extern const InfraredCommonProtocolSpec protocol_kaseikyo;
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#include "infrared.h"
|
||||||
|
#include "infrared_protocol_defs_i.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include "../infrared_i.h"
|
||||||
|
|
||||||
|
InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) {
|
||||||
|
return infrared_common_decoder_check_ready(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) {
|
||||||
|
furi_assert(decoder);
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
uint16_t vendor_id = ((uint16_t)(decoder->data[1]) << 8) | (uint16_t)decoder->data[0];
|
||||||
|
uint8_t vendor_parity = decoder->data[2] & 0x0f;
|
||||||
|
uint8_t genre1 = decoder->data[2] >> 4;
|
||||||
|
uint8_t genre2 = decoder->data[3] & 0x0f;
|
||||||
|
uint16_t data = (uint16_t)(decoder->data[3] >> 4) | ((uint16_t)(decoder->data[4] & 0x3f) << 4);
|
||||||
|
uint8_t id = decoder->data[4] >> 6;
|
||||||
|
uint8_t parity = decoder->data[5];
|
||||||
|
|
||||||
|
uint8_t vendor_parity_check = decoder->data[0] ^ decoder->data[1];
|
||||||
|
vendor_parity_check = (vendor_parity_check & 0xf) ^ (vendor_parity_check >> 4);
|
||||||
|
uint8_t parity_check = decoder->data[2] ^ decoder->data[3] ^ decoder->data[4];
|
||||||
|
|
||||||
|
if(vendor_parity == vendor_parity_check && parity == parity_check) {
|
||||||
|
decoder->message.command = (uint32_t)data;
|
||||||
|
decoder->message.address = ((uint32_t)id << 24) | ((uint32_t)vendor_id << 8) |
|
||||||
|
((uint32_t)genre1 << 4) | (uint32_t)genre2;
|
||||||
|
decoder->message.protocol = InfraredProtocolKaseikyo;
|
||||||
|
decoder->message.repeat = false;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* infrared_decoder_kaseikyo_alloc(void) {
|
||||||
|
return infrared_common_decoder_alloc(&protocol_kaseikyo);
|
||||||
|
}
|
||||||
|
|
||||||
|
InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) {
|
||||||
|
return infrared_common_decode(decoder, level, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void infrared_decoder_kaseikyo_free(void* decoder) {
|
||||||
|
infrared_common_decoder_free(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void infrared_decoder_kaseikyo_reset(void* decoder) {
|
||||||
|
infrared_common_decoder_reset(decoder);
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#include <core/check.h>
|
||||||
|
#include "common/infrared_common_i.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../infrared_i.h"
|
||||||
|
#include "infrared_protocol_defs_i.h"
|
||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) {
|
||||||
|
furi_assert(encoder_ptr);
|
||||||
|
|
||||||
|
InfraredCommonEncoder* encoder = encoder_ptr;
|
||||||
|
infrared_common_encoder_reset(encoder);
|
||||||
|
|
||||||
|
uint32_t address = message->address;
|
||||||
|
uint16_t command = message->command;
|
||||||
|
|
||||||
|
uint8_t id = (address >> 24) & 3;
|
||||||
|
uint16_t vendor_id = (address >> 8) & 0xffff;
|
||||||
|
uint8_t genre1 = (address >> 4) & 0xf;
|
||||||
|
uint8_t genre2 = address & 0xf;
|
||||||
|
|
||||||
|
encoder->data[0] = (uint8_t)(vendor_id & 0xff);
|
||||||
|
encoder->data[1] = (uint8_t)(vendor_id >> 8);
|
||||||
|
uint8_t vendor_parity = encoder->data[0] ^ encoder->data[1];
|
||||||
|
vendor_parity = (vendor_parity & 0xf) ^ (vendor_parity >> 4);
|
||||||
|
encoder->data[2] = (vendor_parity & 0xf) | (genre1 << 4);
|
||||||
|
encoder->data[3] = (genre2 & 0xf) | ((uint8_t)(command & 0xf) << 4);
|
||||||
|
encoder->data[4] = (id << 6) | (uint8_t)(command >> 4);
|
||||||
|
encoder->data[5] = encoder->data[2] ^ encoder->data[3] ^ encoder->data[4];
|
||||||
|
|
||||||
|
encoder->bits_to_encode = encoder->protocol->databit_len[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void* infrared_encoder_kaseikyo_alloc(void) {
|
||||||
|
return infrared_common_encoder_alloc(&protocol_kaseikyo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void infrared_encoder_kaseikyo_free(void* encoder_ptr) {
|
||||||
|
infrared_common_encoder_free(encoder_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
InfraredStatus
|
||||||
|
infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level) {
|
||||||
|
return infrared_common_encode(encoder_ptr, duration, level);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#include "../infrared_i.h"
|
||||||
|
#include "infrared_protocol_defs_i.h"
|
||||||
|
|
||||||
|
static const InfraredProtocolSpecification infrared_kaseikyo_protocol_specification = {
|
||||||
|
.name = "Kaseikyo",
|
||||||
|
.address_length = 26,
|
||||||
|
.command_length = 10,
|
||||||
|
.frequency = INFRARED_COMMON_CARRIER_FREQUENCY,
|
||||||
|
.duty_cycle = INFRARED_COMMON_DUTY_CYCLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol) {
|
||||||
|
if(protocol == InfraredProtocolKaseikyo)
|
||||||
|
return &infrared_kaseikyo_protocol_specification;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
@@ -140,9 +140,8 @@ size_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol pro
|
|||||||
|
|
||||||
static int32_t lfrfid_worker_thread(void* thread_context) {
|
static int32_t lfrfid_worker_thread(void* thread_context) {
|
||||||
LFRFIDWorker* worker = thread_context;
|
LFRFIDWorker* worker = thread_context;
|
||||||
bool running = true;
|
|
||||||
|
|
||||||
while(running) {
|
while(true) {
|
||||||
uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever);
|
uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever);
|
||||||
if(flags != FuriFlagErrorTimeout) {
|
if(flags != FuriFlagErrorTimeout) {
|
||||||
// stop thread
|
// stop thread
|
||||||
|
|||||||
@@ -70,12 +70,12 @@ void nfc_worker_start(
|
|||||||
|
|
||||||
void nfc_worker_stop(NfcWorker* nfc_worker) {
|
void nfc_worker_stop(NfcWorker* nfc_worker) {
|
||||||
furi_assert(nfc_worker);
|
furi_assert(nfc_worker);
|
||||||
if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) {
|
furi_assert(nfc_worker->thread);
|
||||||
return;
|
if(furi_thread_get_state(nfc_worker->thread) != FuriThreadStateStopped) {
|
||||||
|
furi_hal_nfc_stop();
|
||||||
|
nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
|
||||||
|
furi_thread_join(nfc_worker->thread);
|
||||||
}
|
}
|
||||||
furi_hal_nfc_stop();
|
|
||||||
nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
|
|
||||||
furi_thread_join(nfc_worker->thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
|
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ typedef struct NfcWorker NfcWorker;
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
// Init states
|
// Init states
|
||||||
NfcWorkerStateNone,
|
NfcWorkerStateNone,
|
||||||
NfcWorkerStateBroken,
|
|
||||||
NfcWorkerStateReady,
|
NfcWorkerStateReady,
|
||||||
// Main worker states
|
// Main worker states
|
||||||
NfcWorkerStateRead,
|
NfcWorkerStateRead,
|
||||||
|
|||||||
@@ -541,7 +541,7 @@ static size_t _etoa(
|
|||||||
exp2 = (int)(expval * 3.321928094887362 + 0.5);
|
exp2 = (int)(expval * 3.321928094887362 + 0.5);
|
||||||
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
|
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
|
||||||
const double z2 = z * z;
|
const double z2 = z * z;
|
||||||
conv.U = (uint64_t)(exp2 + 1023) << 52U;
|
conv.U = ((uint64_t)exp2 + 1023) << 52U;
|
||||||
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
|
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
|
||||||
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
|
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
|
||||||
// correct for rounding errors
|
// correct for rounding errors
|
||||||
|
|||||||
@@ -312,7 +312,6 @@ void subghz_protocol_decoder_power_smart_feed(
|
|||||||
if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) ==
|
if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) ==
|
||||||
POWER_SMART_PACKET_HEADER) {
|
POWER_SMART_PACKET_HEADER) {
|
||||||
if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) {
|
if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) {
|
||||||
instance->decoder.decode_data = instance->decoder.decode_data;
|
|
||||||
instance->generic.data = instance->decoder.decode_data;
|
instance->generic.data = instance->decoder.decode_data;
|
||||||
instance->generic.data_count_bit =
|
instance->generic.data_count_bit =
|
||||||
subghz_protocol_power_smart_const.min_count_bit_for_found;
|
subghz_protocol_power_smart_const.min_count_bit_for_found;
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ bool subghz_keystore_raw_encrypted_save(
|
|||||||
}
|
}
|
||||||
stream_write_cstring(output_stream, encrypted_line);
|
stream_write_cstring(output_stream, encrypted_line);
|
||||||
|
|
||||||
} while(ret > 0 && result);
|
} while(result);
|
||||||
|
|
||||||
flipper_format_free(output_flipper_format);
|
flipper_format_free(output_flipper_format);
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,6 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
|
||||||
void set_random_name(char* name, uint8_t max_name_size) {
|
void set_random_name(char* name, uint8_t max_name_size) {
|
||||||
static bool rand_generator_inited = false;
|
|
||||||
|
|
||||||
if(!rand_generator_inited) {
|
|
||||||
srand(DWT->CYCCNT);
|
|
||||||
rand_generator_inited = true;
|
|
||||||
}
|
|
||||||
const char* prefix[] = {
|
const char* prefix[] = {
|
||||||
"super",
|
"super",
|
||||||
"big",
|
"big",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import struct
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from .appmanifest import FlipperApplication
|
from .appmanifest import FlipperApplication
|
||||||
|
from flipper.assets.icon import file2image
|
||||||
|
|
||||||
|
|
||||||
_MANIFEST_MAGIC = 0x52474448
|
_MANIFEST_MAGIC = 0x52474448
|
||||||
@@ -53,8 +54,6 @@ def assemble_manifest_data(
|
|||||||
):
|
):
|
||||||
image_data = b""
|
image_data = b""
|
||||||
if app_manifest.fap_icon:
|
if app_manifest.fap_icon:
|
||||||
from flipper.assets.icon import file2image
|
|
||||||
|
|
||||||
image = file2image(os.path.join(app_manifest._apppath, app_manifest.fap_icon))
|
image = file2image(os.path.join(app_manifest._apppath, app_manifest.fap_icon))
|
||||||
if (image.width, image.height) != (10, 10):
|
if (image.width, image.height) != (10, 10):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ class SdkCache:
|
|||||||
syms.update(map(lambda e: e.name, self.get_variables()))
|
syms.update(map(lambda e: e.name, self.get_variables()))
|
||||||
return syms
|
return syms
|
||||||
|
|
||||||
|
def get_disabled_names(self):
|
||||||
|
return set(map(lambda e: e.name, self.disabled_entries))
|
||||||
|
|
||||||
def get_functions(self):
|
def get_functions(self):
|
||||||
return self._filter_enabled(self.sdk.functions)
|
return self._filter_enabled(self.sdk.functions)
|
||||||
|
|
||||||
|
|||||||
@@ -43,12 +43,18 @@ def single_quote(arg_list):
|
|||||||
return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list)
|
return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list)
|
||||||
|
|
||||||
|
|
||||||
def extract_abs_dir_path(node):
|
def extract_abs_dir(node):
|
||||||
if isinstance(node, SCons.Node.FS.EntryProxy):
|
if isinstance(node, SCons.Node.FS.EntryProxy):
|
||||||
node = node.get()
|
node = node.get()
|
||||||
|
|
||||||
for repo_dir in node.get_all_rdirs():
|
for repo_dir in node.get_all_rdirs():
|
||||||
if os.path.exists(repo_dir.abspath):
|
if os.path.exists(repo_dir.abspath):
|
||||||
return repo_dir.abspath
|
return repo_dir
|
||||||
|
|
||||||
raise StopError(f"Can't find absolute path for {node.name}")
|
|
||||||
|
def extract_abs_dir_path(node):
|
||||||
|
abs_dir_node = extract_abs_dir(node)
|
||||||
|
if abs_dir_node is None:
|
||||||
|
raise StopError(f"Can't find absolute path for {node.name}")
|
||||||
|
|
||||||
|
return abs_dir_node.abspath
|
||||||
|
|||||||
278
scripts/fbt_tools/compilation_db.py
Normal file
278
scripts/fbt_tools/compilation_db.py
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
"""
|
||||||
|
Implements the ability for SCons to emit a compilation database for the MongoDB project. See
|
||||||
|
http://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation
|
||||||
|
database is, and why you might want one. The only user visible entry point here is
|
||||||
|
'env.CompilationDatabase'. This method takes an optional 'target' to name the file that
|
||||||
|
should hold the compilation database, otherwise, the file defaults to compile_commands.json,
|
||||||
|
which is the name that most clang tools search for by default.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Copyright 2020 MongoDB Inc.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included
|
||||||
|
# in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||||
|
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||||
|
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
import json
|
||||||
|
import itertools
|
||||||
|
import fnmatch
|
||||||
|
import SCons
|
||||||
|
|
||||||
|
from SCons.Tool.cxx import CXXSuffixes
|
||||||
|
from SCons.Tool.cc import CSuffixes
|
||||||
|
from SCons.Tool.asm import ASSuffixes, ASPPSuffixes
|
||||||
|
|
||||||
|
# TODO: Is there a better way to do this than this global? Right now this exists so that the
|
||||||
|
# emitter we add can record all of the things it emits, so that the scanner for the top level
|
||||||
|
# compilation database can access the complete list, and also so that the writer has easy
|
||||||
|
# access to write all of the files. But it seems clunky. How can the emitter and the scanner
|
||||||
|
# communicate more gracefully?
|
||||||
|
__COMPILATION_DB_ENTRIES = []
|
||||||
|
|
||||||
|
|
||||||
|
# We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even
|
||||||
|
# integrate with the cache, but there doesn't seem to be much call for it.
|
||||||
|
class __CompilationDbNode(SCons.Node.Python.Value):
|
||||||
|
def __init__(self, value):
|
||||||
|
SCons.Node.Python.Value.__init__(self, value)
|
||||||
|
self.Decider(changed_since_last_build_node)
|
||||||
|
|
||||||
|
|
||||||
|
def changed_since_last_build_node(child, target, prev_ni, node):
|
||||||
|
"""Dummy decider to force always building"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def make_emit_compilation_DB_entry(comstr):
|
||||||
|
"""
|
||||||
|
Effectively this creates a lambda function to capture:
|
||||||
|
* command line
|
||||||
|
* source
|
||||||
|
* target
|
||||||
|
:param comstr: unevaluated command line
|
||||||
|
:return: an emitter which has captured the above
|
||||||
|
"""
|
||||||
|
user_action = SCons.Action.Action(comstr)
|
||||||
|
|
||||||
|
def emit_compilation_db_entry(target, source, env):
|
||||||
|
"""
|
||||||
|
This emitter will be added to each c/c++ object build to capture the info needed
|
||||||
|
for clang tools
|
||||||
|
:param target: target node(s)
|
||||||
|
:param source: source node(s)
|
||||||
|
:param env: Environment for use building this node
|
||||||
|
:return: target(s), source(s)
|
||||||
|
"""
|
||||||
|
|
||||||
|
dbtarget = __CompilationDbNode(source)
|
||||||
|
|
||||||
|
entry = env.__COMPILATIONDB_Entry(
|
||||||
|
target=dbtarget,
|
||||||
|
source=[],
|
||||||
|
__COMPILATIONDB_UOUTPUT=target,
|
||||||
|
__COMPILATIONDB_USOURCE=source,
|
||||||
|
__COMPILATIONDB_UACTION=user_action,
|
||||||
|
__COMPILATIONDB_ENV=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: Technically, these next two lines should not be required: it should be fine to
|
||||||
|
# cache the entries. However, they don't seem to update properly. Since they are quick
|
||||||
|
# to re-generate disable caching and sidestep this problem.
|
||||||
|
env.AlwaysBuild(entry)
|
||||||
|
env.NoCache(entry)
|
||||||
|
|
||||||
|
__COMPILATION_DB_ENTRIES.append(dbtarget)
|
||||||
|
|
||||||
|
return target, source
|
||||||
|
|
||||||
|
return emit_compilation_db_entry
|
||||||
|
|
||||||
|
|
||||||
|
def compilation_db_entry_action(target, source, env, **kw):
|
||||||
|
"""
|
||||||
|
Create a dictionary with evaluated command line, target, source
|
||||||
|
and store that info as an attribute on the target
|
||||||
|
(Which has been stored in __COMPILATION_DB_ENTRIES array
|
||||||
|
:param target: target node(s)
|
||||||
|
:param source: source node(s)
|
||||||
|
:param env: Environment for use building this node
|
||||||
|
:param kw:
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
command = env["__COMPILATIONDB_UACTION"].strfunction(
|
||||||
|
target=env["__COMPILATIONDB_UOUTPUT"],
|
||||||
|
source=env["__COMPILATIONDB_USOURCE"],
|
||||||
|
env=env["__COMPILATIONDB_ENV"],
|
||||||
|
)
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
"directory": env.Dir("#").abspath,
|
||||||
|
"command": command,
|
||||||
|
"file": env["__COMPILATIONDB_USOURCE"][0],
|
||||||
|
"output": env["__COMPILATIONDB_UOUTPUT"][0],
|
||||||
|
}
|
||||||
|
|
||||||
|
target[0].write(entry)
|
||||||
|
|
||||||
|
|
||||||
|
def write_compilation_db(target, source, env):
|
||||||
|
entries = []
|
||||||
|
|
||||||
|
use_abspath = env["COMPILATIONDB_USE_ABSPATH"] in [True, 1, "True", "true"]
|
||||||
|
use_path_filter = env.subst("$COMPILATIONDB_PATH_FILTER")
|
||||||
|
use_srcpath_filter = env.subst("$COMPILATIONDB_SRCPATH_FILTER")
|
||||||
|
|
||||||
|
for s in __COMPILATION_DB_ENTRIES:
|
||||||
|
entry = s.read()
|
||||||
|
source_file = entry["file"]
|
||||||
|
output_file = entry["output"]
|
||||||
|
|
||||||
|
if source_file.rfile().srcnode().exists():
|
||||||
|
source_file = source_file.rfile().srcnode()
|
||||||
|
|
||||||
|
if use_abspath:
|
||||||
|
source_file = source_file.abspath
|
||||||
|
output_file = output_file.abspath
|
||||||
|
else:
|
||||||
|
source_file = source_file.path
|
||||||
|
output_file = output_file.path
|
||||||
|
|
||||||
|
# print("output_file, path_filter", output_file, use_path_filter)
|
||||||
|
if use_path_filter and not fnmatch.fnmatch(output_file, use_path_filter):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if use_srcpath_filter and not fnmatch.fnmatch(source_file, use_srcpath_filter):
|
||||||
|
continue
|
||||||
|
|
||||||
|
path_entry = {
|
||||||
|
"directory": entry["directory"],
|
||||||
|
"command": entry["command"],
|
||||||
|
"file": source_file,
|
||||||
|
"output": output_file,
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.append(path_entry)
|
||||||
|
|
||||||
|
with open(target[0].path, "w") as output_file:
|
||||||
|
json.dump(
|
||||||
|
entries, output_file, sort_keys=True, indent=4, separators=(",", ": ")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def scan_compilation_db(node, env, path):
|
||||||
|
return __COMPILATION_DB_ENTRIES
|
||||||
|
|
||||||
|
|
||||||
|
def compilation_db_emitter(target, source, env):
|
||||||
|
"""fix up the source/targets"""
|
||||||
|
|
||||||
|
# Someone called env.CompilationDatabase('my_targetname.json')
|
||||||
|
if not target and len(source) == 1:
|
||||||
|
target = source
|
||||||
|
|
||||||
|
# Default target name is compilation_db.json
|
||||||
|
if not target:
|
||||||
|
target = [
|
||||||
|
"compile_commands.json",
|
||||||
|
]
|
||||||
|
|
||||||
|
# No source should have been passed. Drop it.
|
||||||
|
if source:
|
||||||
|
source = []
|
||||||
|
|
||||||
|
return target, source
|
||||||
|
|
||||||
|
|
||||||
|
def generate(env, **kwargs):
|
||||||
|
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
|
||||||
|
|
||||||
|
env.SetDefault(
|
||||||
|
COMPILATIONDB_COMSTR=kwargs.get(
|
||||||
|
"COMPILATIONDB_COMSTR", "Building compilation database $TARGET"
|
||||||
|
),
|
||||||
|
COMPILATIONDB_USE_ABSPATH=False,
|
||||||
|
COMPILATIONDB_PATH_FILTER="",
|
||||||
|
COMPILATIONDB_SRCPATH_FILTER="",
|
||||||
|
)
|
||||||
|
|
||||||
|
components_by_suffix = itertools.chain(
|
||||||
|
itertools.product(
|
||||||
|
CSuffixes,
|
||||||
|
[
|
||||||
|
(static_obj, SCons.Defaults.StaticObjectEmitter, "$CCCOM"),
|
||||||
|
(shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCCCOM"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
itertools.product(
|
||||||
|
CXXSuffixes,
|
||||||
|
[
|
||||||
|
(static_obj, SCons.Defaults.StaticObjectEmitter, "$CXXCOM"),
|
||||||
|
(shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCXXCOM"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
itertools.product(
|
||||||
|
ASSuffixes,
|
||||||
|
[(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASCOM")],
|
||||||
|
[(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASCOM")],
|
||||||
|
),
|
||||||
|
itertools.product(
|
||||||
|
ASPPSuffixes,
|
||||||
|
[(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASPPCOM")],
|
||||||
|
[(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASPPCOM")],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
for entry in components_by_suffix:
|
||||||
|
suffix = entry[0]
|
||||||
|
builder, base_emitter, command = entry[1]
|
||||||
|
|
||||||
|
# Assumes a dictionary emitter
|
||||||
|
emitter = builder.emitter.get(suffix, False)
|
||||||
|
if emitter:
|
||||||
|
# We may not have tools installed which initialize all or any of
|
||||||
|
# cxx, cc, or assembly. If not skip resetting the respective emitter.
|
||||||
|
builder.emitter[suffix] = SCons.Builder.ListEmitter(
|
||||||
|
[
|
||||||
|
emitter,
|
||||||
|
make_emit_compilation_DB_entry(command),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
env.Append(
|
||||||
|
BUILDERS={
|
||||||
|
"__COMPILATIONDB_Entry": SCons.Builder.Builder(
|
||||||
|
action=SCons.Action.Action(compilation_db_entry_action, None),
|
||||||
|
),
|
||||||
|
"CompilationDatabase": SCons.Builder.Builder(
|
||||||
|
action=SCons.Action.Action(
|
||||||
|
write_compilation_db, "$COMPILATIONDB_COMSTR"
|
||||||
|
),
|
||||||
|
target_scanner=SCons.Scanner.Scanner(
|
||||||
|
function=scan_compilation_db, node_class=None
|
||||||
|
),
|
||||||
|
emitter=compilation_db_emitter,
|
||||||
|
suffix="json",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def exists(env):
|
||||||
|
return True
|
||||||
@@ -139,7 +139,7 @@ def generate(env):
|
|||||||
BUILDERS={
|
BUILDERS={
|
||||||
"IconBuilder": Builder(
|
"IconBuilder": Builder(
|
||||||
action=Action(
|
action=Action(
|
||||||
'${PYTHON3} "${ASSETS_COMPILER}" icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename ${ICON_FILE_NAME}',
|
'${PYTHON3} "${ASSETS_COMPILER}" icons "${ABSPATHGETTERFUNC(SOURCE)}" "${TARGET.dir}" --filename ${ICON_FILE_NAME}',
|
||||||
"${ICONSCOMSTR}",
|
"${ICONSCOMSTR}",
|
||||||
),
|
),
|
||||||
emitter=icons_emitter,
|
emitter=icons_emitter,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
from re import search
|
from re import search
|
||||||
|
|
||||||
from SCons.Errors import UserError
|
from SCons.Errors import UserError
|
||||||
from fbt_options import OPENOCD_OPTS
|
|
||||||
|
|
||||||
|
|
||||||
def _get_device_serials(search_str="STLink"):
|
def _get_device_serials(search_str="STLink"):
|
||||||
@@ -20,6 +19,9 @@ def GetDevices(env):
|
|||||||
|
|
||||||
def generate(env, **kw):
|
def generate(env, **kw):
|
||||||
env.AddMethod(GetDevices)
|
env.AddMethod(GetDevices)
|
||||||
|
env.SetDefault(
|
||||||
|
FBT_DEBUG_DIR="${ROOT_DIR}/debug",
|
||||||
|
)
|
||||||
|
|
||||||
if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto":
|
if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto":
|
||||||
env.Append(
|
env.Append(
|
||||||
@@ -36,7 +38,7 @@ def generate(env, **kw):
|
|||||||
|
|
||||||
env.SetDefault(
|
env.SetDefault(
|
||||||
OPENOCD_GDB_PIPE=[
|
OPENOCD_GDB_PIPE=[
|
||||||
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
"|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
||||||
],
|
],
|
||||||
GDBOPTS_BASE=[
|
GDBOPTS_BASE=[
|
||||||
"-ex",
|
"-ex",
|
||||||
@@ -58,17 +60,19 @@ def generate(env, **kw):
|
|||||||
],
|
],
|
||||||
GDBPYOPTS=[
|
GDBPYOPTS=[
|
||||||
"-ex",
|
"-ex",
|
||||||
"source debug/FreeRTOS/FreeRTOS.py",
|
"source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py",
|
||||||
"-ex",
|
"-ex",
|
||||||
"source debug/flipperapps.py",
|
"source ${FBT_DEBUG_DIR}/flipperapps.py",
|
||||||
"-ex",
|
"-ex",
|
||||||
"source debug/PyCortexMDebug/PyCortexMDebug.py",
|
"fap-set-debug-elf-root ${FBT_FAP_DEBUG_ELF_ROOT}",
|
||||||
|
"-ex",
|
||||||
|
"source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py",
|
||||||
"-ex",
|
"-ex",
|
||||||
"svd_load ${SVD_FILE}",
|
"svd_load ${SVD_FILE}",
|
||||||
"-ex",
|
"-ex",
|
||||||
"compare-sections",
|
"compare-sections",
|
||||||
],
|
],
|
||||||
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
|
JFLASHPROJECT="${FBT_DEBUG_DIR}/fw.jflash",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ def GetProjetDirName(env, project=None):
|
|||||||
|
|
||||||
def create_fw_build_targets(env, configuration_name):
|
def create_fw_build_targets(env, configuration_name):
|
||||||
flavor = GetProjetDirName(env, configuration_name)
|
flavor = GetProjetDirName(env, configuration_name)
|
||||||
build_dir = env.Dir("build").Dir(flavor).abspath
|
build_dir = env.Dir("build").Dir(flavor)
|
||||||
return env.SConscript(
|
return env.SConscript(
|
||||||
"firmware.scons",
|
"firmware.scons",
|
||||||
variant_dir=build_dir,
|
variant_dir=build_dir,
|
||||||
@@ -131,7 +131,7 @@ def generate(env):
|
|||||||
"UsbInstall": Builder(
|
"UsbInstall": Builder(
|
||||||
action=[
|
action=[
|
||||||
Action(
|
Action(
|
||||||
'${PYTHON3} "${SELFUPDATE_SCRIPT}" dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf'
|
'${PYTHON3} "${SELFUPDATE_SCRIPT}" ${UPDATE_BUNDLE_DIR}/update.fuf'
|
||||||
),
|
),
|
||||||
Touch("${TARGET}"),
|
Touch("${TARGET}"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Optional
|
||||||
from SCons.Builder import Builder
|
from SCons.Builder import Builder
|
||||||
from SCons.Action import Action
|
from SCons.Action import Action
|
||||||
from SCons.Errors import UserError
|
from SCons.Errors import UserError
|
||||||
|
from SCons.Node import NodeList
|
||||||
import SCons.Warnings
|
import SCons.Warnings
|
||||||
|
|
||||||
from fbt.elfmanifest import assemble_manifest_data
|
from fbt.elfmanifest import assemble_manifest_data
|
||||||
@@ -16,6 +19,15 @@ import shutil
|
|||||||
from ansi.color import fg
|
from ansi.color import fg
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FlipperExternalAppInfo:
|
||||||
|
app: FlipperApplication
|
||||||
|
compact: NodeList = field(default_factory=NodeList)
|
||||||
|
debug: NodeList = field(default_factory=NodeList)
|
||||||
|
validator: NodeList = field(default_factory=NodeList)
|
||||||
|
installer: NodeList = field(default_factory=NodeList)
|
||||||
|
|
||||||
|
|
||||||
def BuildAppElf(env, app):
|
def BuildAppElf(env, app):
|
||||||
ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR")
|
ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR")
|
||||||
app_work_dir = os.path.join(ext_apps_work_dir, app.appid)
|
app_work_dir = os.path.join(ext_apps_work_dir, app.appid)
|
||||||
@@ -26,15 +38,7 @@ def BuildAppElf(env, app):
|
|||||||
|
|
||||||
app_alias = f"fap_{app.appid}"
|
app_alias = f"fap_{app.appid}"
|
||||||
|
|
||||||
# Deprecation stub
|
app_artifacts = FlipperExternalAppInfo(app)
|
||||||
legacy_app_taget_name = f"{app_env['FIRMWARE_BUILD_CFG']}_{app.appid}"
|
|
||||||
|
|
||||||
def legacy_app_build_stub(**kw):
|
|
||||||
raise UserError(
|
|
||||||
f"Target name '{legacy_app_taget_name}' is deprecated, use '{app_alias}' instead"
|
|
||||||
)
|
|
||||||
|
|
||||||
app_env.PhonyTarget(legacy_app_taget_name, Action(legacy_app_build_stub, None))
|
|
||||||
|
|
||||||
externally_built_files = []
|
externally_built_files = []
|
||||||
if app.fap_extbuild:
|
if app.fap_extbuild:
|
||||||
@@ -53,11 +57,12 @@ def BuildAppElf(env, app):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if app.fap_icon_assets:
|
if app.fap_icon_assets:
|
||||||
app_env.CompileIcons(
|
fap_icons = app_env.CompileIcons(
|
||||||
app_env.Dir(app_work_dir),
|
app_env.Dir(app_work_dir),
|
||||||
app._appdir.Dir(app.fap_icon_assets),
|
app._appdir.Dir(app.fap_icon_assets),
|
||||||
icon_bundle_name=f"{app.appid}_icons",
|
icon_bundle_name=f"{app.appid}_icons",
|
||||||
)
|
)
|
||||||
|
app_env.Alias("_fap_icons", fap_icons)
|
||||||
|
|
||||||
private_libs = []
|
private_libs = []
|
||||||
|
|
||||||
@@ -115,20 +120,22 @@ def BuildAppElf(env, app):
|
|||||||
CPPPATH=env.Dir(app_work_dir),
|
CPPPATH=env.Dir(app_work_dir),
|
||||||
)
|
)
|
||||||
|
|
||||||
app_elf_raw = app_env.Program(
|
app_artifacts.debug = app_env.Program(
|
||||||
os.path.join(ext_apps_work_dir, f"{app.appid}_d"),
|
os.path.join(ext_apps_work_dir, f"{app.appid}_d"),
|
||||||
app_sources,
|
app_sources,
|
||||||
APP_ENTRY=app.entry_point,
|
APP_ENTRY=app.entry_point,
|
||||||
)
|
)
|
||||||
|
|
||||||
app_env.Clean(app_elf_raw, [*externally_built_files, app_env.Dir(app_work_dir)])
|
app_env.Clean(
|
||||||
|
app_artifacts.debug, [*externally_built_files, app_env.Dir(app_work_dir)]
|
||||||
|
)
|
||||||
|
|
||||||
app_elf_dump = app_env.ObjDump(app_elf_raw)
|
app_elf_dump = app_env.ObjDump(app_artifacts.debug)
|
||||||
app_env.Alias(f"{app_alias}_list", app_elf_dump)
|
app_env.Alias(f"{app_alias}_list", app_elf_dump)
|
||||||
|
|
||||||
app_elf_augmented = app_env.EmbedAppMetadata(
|
app_artifacts.compact = app_env.EmbedAppMetadata(
|
||||||
os.path.join(ext_apps_work_dir, app.appid),
|
os.path.join(ext_apps_work_dir, app.appid),
|
||||||
app_elf_raw,
|
app_artifacts.debug,
|
||||||
APP=app,
|
APP=app,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -139,19 +146,21 @@ def BuildAppElf(env, app):
|
|||||||
}
|
}
|
||||||
|
|
||||||
app_env.Depends(
|
app_env.Depends(
|
||||||
app_elf_augmented,
|
app_artifacts.compact,
|
||||||
[app_env["SDK_DEFINITION"], app_env.Value(manifest_vals)],
|
[app_env["SDK_DEFINITION"], app_env.Value(manifest_vals)],
|
||||||
)
|
)
|
||||||
if app.fap_icon:
|
if app.fap_icon:
|
||||||
app_env.Depends(
|
app_env.Depends(
|
||||||
app_elf_augmented,
|
app_artifacts.compact,
|
||||||
app_env.File(f"{app._apppath}/{app.fap_icon}"),
|
app_env.File(f"{app._apppath}/{app.fap_icon}"),
|
||||||
)
|
)
|
||||||
|
|
||||||
app_elf_import_validator = app_env.ValidateAppImports(app_elf_augmented)
|
app_artifacts.validator = app_env.ValidateAppImports(app_artifacts.compact)
|
||||||
app_env.AlwaysBuild(app_elf_import_validator)
|
app_env.AlwaysBuild(app_artifacts.validator)
|
||||||
app_env.Alias(app_alias, app_elf_import_validator)
|
app_env.Alias(app_alias, app_artifacts.validator)
|
||||||
return (app_elf_augmented, app_elf_raw, app_elf_import_validator)
|
|
||||||
|
env["EXT_APPS"][app.appid] = app_artifacts
|
||||||
|
return app_artifacts
|
||||||
|
|
||||||
|
|
||||||
def prepare_app_metadata(target, source, env):
|
def prepare_app_metadata(target, source, env):
|
||||||
@@ -182,11 +191,17 @@ def validate_app_imports(target, source, env):
|
|||||||
app_syms.add(line.split()[0])
|
app_syms.add(line.split()[0])
|
||||||
unresolved_syms = app_syms - sdk_cache.get_valid_names()
|
unresolved_syms = app_syms - sdk_cache.get_valid_names()
|
||||||
if unresolved_syms:
|
if unresolved_syms:
|
||||||
SCons.Warnings.warn(
|
warning_msg = fg.brightyellow(
|
||||||
SCons.Warnings.LinkWarning,
|
f"{source[0].path}: app won't run. Unresolved symbols: "
|
||||||
fg.brightyellow(f"{source[0].path}: app won't run. Unresolved symbols: ")
|
) + fg.brightmagenta(f"{unresolved_syms}")
|
||||||
+ fg.brightmagenta(f"{unresolved_syms}"),
|
disabled_api_syms = unresolved_syms.intersection(sdk_cache.get_disabled_names())
|
||||||
)
|
if disabled_api_syms:
|
||||||
|
warning_msg += (
|
||||||
|
fg.brightyellow(" (in API, but disabled: ")
|
||||||
|
+ fg.brightmagenta(f"{disabled_api_syms}")
|
||||||
|
+ fg.brightyellow(")")
|
||||||
|
)
|
||||||
|
SCons.Warnings.warn(SCons.Warnings.LinkWarning, warning_msg),
|
||||||
|
|
||||||
|
|
||||||
def GetExtAppFromPath(env, app_dir):
|
def GetExtAppFromPath(env, app_dir):
|
||||||
@@ -208,26 +223,26 @@ def GetExtAppFromPath(env, app_dir):
|
|||||||
if not app:
|
if not app:
|
||||||
raise UserError(f"Failed to resolve application for given APPSRC={app_dir}")
|
raise UserError(f"Failed to resolve application for given APPSRC={app_dir}")
|
||||||
|
|
||||||
app_elf = env["_extapps"]["compact"].get(app.appid, None)
|
app_artifacts = env["EXT_APPS"].get(app.appid, None)
|
||||||
if not app_elf:
|
if not app_artifacts:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
f"Application {app.appid} is not configured for building as external"
|
f"Application {app.appid} is not configured for building as external"
|
||||||
)
|
)
|
||||||
|
|
||||||
app_validator = env["_extapps"]["validators"].get(app.appid, None)
|
return app_artifacts
|
||||||
|
|
||||||
return (app, app_elf[0], app_validator[0])
|
|
||||||
|
|
||||||
|
|
||||||
def fap_dist_emitter(target, source, env):
|
def fap_dist_emitter(target, source, env):
|
||||||
target_dir = target[0]
|
target_dir = target[0]
|
||||||
|
|
||||||
target = []
|
target = []
|
||||||
for dist_entry in env["_extapps"]["dist"].values():
|
for _, app_artifacts in env["EXT_APPS"].items():
|
||||||
target.append(target_dir.Dir(dist_entry[0]).File(dist_entry[1][0].name))
|
source.extend(app_artifacts.compact)
|
||||||
|
target.append(
|
||||||
for compact_entry in env["_extapps"]["compact"].values():
|
target_dir.Dir(app_artifacts.app.fap_category).File(
|
||||||
source.extend(compact_entry)
|
app_artifacts.compact[0].name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return (target, source)
|
return (target, source)
|
||||||
|
|
||||||
@@ -244,10 +259,9 @@ def fap_dist_action(target, source, env):
|
|||||||
|
|
||||||
def generate(env, **kw):
|
def generate(env, **kw):
|
||||||
env.SetDefault(
|
env.SetDefault(
|
||||||
EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR"),
|
EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}",
|
||||||
APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py",
|
APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py",
|
||||||
)
|
)
|
||||||
|
|
||||||
if not env["VERBOSE"]:
|
if not env["VERBOSE"]:
|
||||||
env.SetDefault(
|
env.SetDefault(
|
||||||
FAPDISTCOMSTR="\tFAPDIST\t${TARGET}",
|
FAPDISTCOMSTR="\tFAPDIST\t${TARGET}",
|
||||||
@@ -256,6 +270,10 @@ def generate(env, **kw):
|
|||||||
APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}",
|
APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
env.SetDefault(
|
||||||
|
EXT_APPS={}, # appid -> FlipperExternalAppInfo
|
||||||
|
)
|
||||||
|
|
||||||
env.AddMethod(BuildAppElf)
|
env.AddMethod(BuildAppElf)
|
||||||
env.AddMethod(GetExtAppFromPath)
|
env.AddMethod(GetExtAppFromPath)
|
||||||
env.Append(
|
env.Append(
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
pyserial==3.5
|
ansi==0.3.6
|
||||||
|
black==22.6.0
|
||||||
|
colorlog==6.7.0
|
||||||
heatshrink2==0.11.0
|
heatshrink2==0.11.0
|
||||||
Pillow==9.1.1
|
Pillow==9.1.1
|
||||||
grpcio==1.47.0
|
protobuf==3.20.1
|
||||||
grpcio-tools==1.47.0
|
pyserial==3.5
|
||||||
protobuf==3.20.2
|
|
||||||
python3-protobuf==2.5.0
|
python3-protobuf==2.5.0
|
||||||
|
SCons==4.4.0
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ if not [%FBT_NOENV%] == [] (
|
|||||||
exit /b 0
|
exit /b 0
|
||||||
)
|
)
|
||||||
|
|
||||||
set "FLIPPER_TOOLCHAIN_VERSION=17"
|
set "FLIPPER_TOOLCHAIN_VERSION=19"
|
||||||
|
|
||||||
if [%FBT_TOOLCHAIN_ROOT%] == [] (
|
if [%FBT_TOOLCHAIN_ROOT%] == [] (
|
||||||
set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows"
|
set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# public variables
|
# public variables
|
||||||
DEFAULT_SCRIPT_PATH="$(pwd -P)";
|
DEFAULT_SCRIPT_PATH="$(pwd -P)";
|
||||||
SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}";
|
SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}";
|
||||||
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"17"}";
|
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"19"}";
|
||||||
FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}";
|
FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}";
|
||||||
|
|
||||||
fbtenv_show_usage()
|
fbtenv_show_usage()
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ vars.AddVariables(
|
|||||||
PathVariable(
|
PathVariable(
|
||||||
"SVD_FILE",
|
"SVD_FILE",
|
||||||
help="Path to SVD file",
|
help="Path to SVD file",
|
||||||
validator=PathVariable.PathIsFile,
|
validator=PathVariable.PathAccept,
|
||||||
default="",
|
default="",
|
||||||
),
|
),
|
||||||
PathVariable(
|
PathVariable(
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ coreenv = VAR_ENV.Clone(
|
|||||||
ABSPATHGETTERFUNC=extract_abs_dir_path,
|
ABSPATHGETTERFUNC=extract_abs_dir_path,
|
||||||
# Setting up temp file parameters - to overcome command line length limits
|
# Setting up temp file parameters - to overcome command line length limits
|
||||||
TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
|
TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
|
||||||
FBT_SCRIPT_DIR=Dir("#/scripts"),
|
|
||||||
ROOT_DIR=Dir("#"),
|
ROOT_DIR=Dir("#"),
|
||||||
|
FBT_SCRIPT_DIR="${ROOT_DIR}/scripts",
|
||||||
)
|
)
|
||||||
|
|
||||||
# If DIST_SUFFIX is set in environment, is has precedence (set by CI)
|
# If DIST_SUFFIX is set in environment, is has precedence (set by CI)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
from SCons.Errors import UserError
|
from SCons.Errors import UserError
|
||||||
|
from SCons.Node import NodeList
|
||||||
|
|
||||||
|
|
||||||
Import("ENV")
|
Import("ENV")
|
||||||
@@ -7,14 +9,7 @@ from fbt.appmanifest import FlipperAppType
|
|||||||
|
|
||||||
appenv = ENV["APPENV"] = ENV.Clone(
|
appenv = ENV["APPENV"] = ENV.Clone(
|
||||||
tools=[
|
tools=[
|
||||||
(
|
"fbt_extapps",
|
||||||
"fbt_extapps",
|
|
||||||
{
|
|
||||||
"EXT_APPS_WORK_DIR": ENV.subst(
|
|
||||||
"${BUILD_DIR}/.extapps",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"fbt_assets",
|
"fbt_assets",
|
||||||
"fbt_sdk",
|
"fbt_sdk",
|
||||||
]
|
]
|
||||||
@@ -60,22 +55,11 @@ appenv.AppendUnique(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
extapps = appenv["_extapps"] = {
|
@dataclass
|
||||||
"compact": {},
|
class FlipperExtAppBuildArtifacts:
|
||||||
"debug": {},
|
applications: dict = field(default_factory=dict)
|
||||||
"validators": {},
|
resources_dist: NodeList = field(default_factory=NodeList)
|
||||||
"dist": {},
|
sdk_tree: NodeList = field(default_factory=NodeList)
|
||||||
"resources_dist": None,
|
|
||||||
"sdk_tree": None,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def build_app_as_external(env, appdef):
|
|
||||||
compact_elf, debug_elf, validator = env.BuildAppElf(appdef)
|
|
||||||
extapps["compact"][appdef.appid] = compact_elf
|
|
||||||
extapps["debug"][appdef.appid] = debug_elf
|
|
||||||
extapps["validators"][appdef.appid] = validator
|
|
||||||
extapps["dist"][appdef.appid] = (appdef.fap_category, compact_elf)
|
|
||||||
|
|
||||||
|
|
||||||
apps_to_build_as_faps = [
|
apps_to_build_as_faps = [
|
||||||
@@ -85,42 +69,43 @@ apps_to_build_as_faps = [
|
|||||||
if appenv["DEBUG_TOOLS"]:
|
if appenv["DEBUG_TOOLS"]:
|
||||||
apps_to_build_as_faps.append(FlipperAppType.DEBUG)
|
apps_to_build_as_faps.append(FlipperAppType.DEBUG)
|
||||||
|
|
||||||
for apptype in apps_to_build_as_faps:
|
known_extapps = [
|
||||||
for app in appenv["APPBUILD"].get_apps_of_type(apptype, True):
|
app
|
||||||
build_app_as_external(appenv, app)
|
for apptype in apps_to_build_as_faps
|
||||||
|
for app in appenv["APPBUILD"].get_apps_of_type(apptype, True)
|
||||||
|
]
|
||||||
|
|
||||||
# Ugly access to global option
|
# Ugly access to global option
|
||||||
if extra_app_list := GetOption("extra_ext_apps"):
|
if extra_app_list := GetOption("extra_ext_apps"):
|
||||||
for extra_app in extra_app_list.split(","):
|
known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(",")))
|
||||||
build_app_as_external(appenv, appenv["APPMGR"].get(extra_app))
|
|
||||||
|
for app in known_extapps:
|
||||||
|
appenv.BuildAppElf(app)
|
||||||
|
|
||||||
|
|
||||||
if appenv["FORCE"]:
|
if appenv["FORCE"]:
|
||||||
appenv.AlwaysBuild(extapps["compact"].values())
|
appenv.AlwaysBuild(
|
||||||
|
list(app_artifact.compact for app_artifact in appenv["EXT_APPS"].values())
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Deprecation stub
|
Alias(
|
||||||
def legacy_app_build_stub(**kw):
|
"faps", list(app_artifact.validator for app_artifact in appenv["EXT_APPS"].values())
|
||||||
raise UserError(f"Target name 'firmware_extapps' is deprecated, use 'faps' instead")
|
)
|
||||||
|
|
||||||
|
extapps = FlipperExtAppBuildArtifacts()
|
||||||
appenv.PhonyTarget("firmware_extapps", appenv.Action(legacy_app_build_stub, None))
|
extapps.applications = appenv["EXT_APPS"]
|
||||||
|
extapps.resources_dist = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), [])
|
||||||
|
|
||||||
Alias("faps", extapps["compact"].values())
|
|
||||||
Alias("faps", extapps["validators"].values())
|
|
||||||
|
|
||||||
extapps["resources_dist"] = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), [])
|
|
||||||
|
|
||||||
if appsrc := appenv.subst("$APPSRC"):
|
if appsrc := appenv.subst("$APPSRC"):
|
||||||
app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc)
|
app_artifacts = appenv.GetExtAppFromPath(appsrc)
|
||||||
appenv.PhonyTarget(
|
appenv.PhonyTarget(
|
||||||
"launch_app",
|
"launch_app",
|
||||||
'${PYTHON3} "${APP_RUN_SCRIPT}" ${SOURCE} --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"',
|
'${PYTHON3} "${APP_RUN_SCRIPT}" "${SOURCE}" --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"',
|
||||||
source=fap_file,
|
source=app_artifacts.compact,
|
||||||
FAP_CATEGORY=app_manifest.fap_category,
|
FAP_CATEGORY=app_artifacts.app.fap_category,
|
||||||
)
|
)
|
||||||
appenv.Alias("launch_app", app_validator)
|
appenv.Alias("launch_app", app_artifacts.validator)
|
||||||
|
|
||||||
# SDK management
|
# SDK management
|
||||||
|
|
||||||
@@ -131,12 +116,14 @@ sdk_source = appenv.SDKPrebuilder(
|
|||||||
(appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]),
|
(appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]),
|
||||||
)
|
)
|
||||||
# Extra deps on headers included in deeper levels
|
# Extra deps on headers included in deeper levels
|
||||||
|
# Available on second and subsequent builds
|
||||||
Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d"))
|
Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d"))
|
||||||
|
|
||||||
appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk")
|
appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk")
|
||||||
sdk_tree = extapps["sdk_tree"] = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path)
|
sdk_tree = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path)
|
||||||
# AlwaysBuild(sdk_tree)
|
# AlwaysBuild(sdk_tree)
|
||||||
Alias("sdk_tree", sdk_tree)
|
Alias("sdk_tree", sdk_tree)
|
||||||
|
extapps.sdk_tree = sdk_tree
|
||||||
|
|
||||||
sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path)
|
sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path)
|
||||||
Precious(sdk_apicheck)
|
Precious(sdk_apicheck)
|
||||||
|
|||||||
Reference in New Issue
Block a user