1
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:
MX
2022-11-10 06:57:08 +03:00
90 changed files with 1317 additions and 265 deletions

2
.gitignore vendored
View File

@@ -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

View File

@@ -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

View File

@@ -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",
}, },

View File

@@ -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(

View File

@@ -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);
} }

View File

@@ -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"],
) )

View File

@@ -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);

View File

@@ -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);

View File

@@ -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"],
) )

View File

@@ -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);

View File

@@ -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(

View File

@@ -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(

View File

@@ -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(

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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();
} }

View File

@@ -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"],
) )

View File

@@ -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);

View File

@@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -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));

View File

@@ -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);
} }

View File

@@ -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);
} }

View 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);
}

View File

@@ -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);

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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,

View File

@@ -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"

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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;
} }

View File

@@ -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) {

View File

@@ -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(

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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)) {

View File

@@ -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;

View 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
#

View File

@@ -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}")

View 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.

View File

@@ -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"

View File

@@ -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)

View File

@@ -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

View File

@@ -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;

View File

@@ -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)

View File

@@ -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);

View File

@@ -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--;
} }

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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, " ");
} }

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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);

View File

@@ -31,6 +31,7 @@ typedef enum {
InfraredProtocolSIRC, InfraredProtocolSIRC,
InfraredProtocolSIRC15, InfraredProtocolSIRC15,
InfraredProtocolSIRC20, InfraredProtocolSIRC20,
InfraredProtocolKaseikyo,
InfraredProtocolMAX, InfraredProtocolMAX,
} InfraredProtocol; } InfraredProtocol;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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",

View File

@@ -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(

View File

@@ -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)

View File

@@ -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

View 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

View File

@@ -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,

View File

@@ -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",
) )

View File

@@ -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}"),
] ]

View File

@@ -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(

View File

@@ -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

View File

@@ -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"

View File

@@ -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()

View File

@@ -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(

View File

@@ -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)

View File

@@ -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)