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-08-02 19:27:01 +03:00
94 changed files with 928 additions and 529 deletions

View File

@@ -7,6 +7,7 @@
# construction of certain targets behind command-line options. # construction of certain targets behind command-line options.
import os import os
import subprocess
EnsurePythonVersion(3, 8) EnsurePythonVersion(3, 8)
@@ -33,8 +34,10 @@ coreenv["ROOT_DIR"] = Dir(".")
# Create a separate "dist" environment and add construction envs to it # Create a separate "dist" environment and add construction envs to it
distenv = coreenv.Clone( distenv = coreenv.Clone(
tools=["fbt_dist", "openocd", "blackmagic"], tools=["fbt_dist", "openocd", "blackmagic", "jflash"],
OPENOCD_GDB_PIPE=["|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"], OPENOCD_GDB_PIPE=[
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
],
GDBOPTS_BASE=[ GDBOPTS_BASE=[
"-ex", "-ex",
"target extended-remote ${GDBREMOTE}", "target extended-remote ${GDBREMOTE}",
@@ -61,6 +64,7 @@ distenv = coreenv.Clone(
"-ex", "-ex",
"compare-sections", "compare-sections",
], ],
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
ENV=os.environ, ENV=os.environ,
) )
@@ -71,7 +75,9 @@ firmware_env = distenv.AddFwProject(
) )
# If enabled, initialize updater-related targets # If enabled, initialize updater-related targets
if GetOption("fullenv"): if GetOption("fullenv") or any(
filter(lambda target: "updater" in target or "flash_usb" in target, BUILD_TARGETS)
):
updater_env = distenv.AddFwProject( updater_env = distenv.AddFwProject(
base_env=coreenv, base_env=coreenv,
fw_type="updater", fw_type="updater",
@@ -79,11 +85,11 @@ if GetOption("fullenv"):
) )
# Target for self-update package # Target for self-update package
dist_arguments = [ dist_basic_arguments = [
"-r",
'"${ROOT_DIR.abspath}/assets/resources"',
"--bundlever", "--bundlever",
'"${UPDATE_VERSION_STRING}"', '"${UPDATE_VERSION_STRING}"',
]
dist_radio_arguments = [
"--radio", "--radio",
'"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"', '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"',
"--radiotype", "--radiotype",
@@ -92,16 +98,34 @@ if GetOption("fullenv"):
"--obdata", "--obdata",
'"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"', '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"',
] ]
if distenv["UPDATE_SPLASH"]: dist_resource_arguments = [
dist_arguments += [ "-r",
'"${ROOT_DIR.abspath}/assets/resources"',
]
dist_splash_arguments = (
[
"--splash", "--splash",
distenv.subst("assets/slideshow/$UPDATE_SPLASH"), distenv.subst("assets/slideshow/$UPDATE_SPLASH"),
] ]
if distenv["UPDATE_SPLASH"]
else []
)
selfupdate_dist = distenv.DistCommand( selfupdate_dist = distenv.DistCommand(
"updater_package", "updater_package",
(distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]), (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]),
DIST_EXTRA=dist_arguments, DIST_EXTRA=[
*dist_basic_arguments,
*dist_radio_arguments,
*dist_resource_arguments,
*dist_splash_arguments,
],
)
selfupdate_min_dist = distenv.DistCommand(
"updater_minpackage",
distenv["DIST_DEPENDS"],
DIST_EXTRA=dist_basic_arguments,
) )
# Updater debug # Updater debug
@@ -121,18 +145,16 @@ if GetOption("fullenv"):
) )
# Installation over USB & CLI # Installation over USB & CLI
usb_update_package = distenv.UsbInstall( usb_update_package = distenv.AddUsbFlashTarget(
"#build/usbinstall.flag", "#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist)
(
distenv["DIST_DEPENDS"],
firmware_env["FW_RESOURCES"],
selfupdate_dist,
),
) )
if distenv["FORCE"]: distenv.Alias("flash_usb_full", usb_update_package)
distenv.AlwaysBuild(usb_update_package)
distenv.Depends(usb_update_package, selfupdate_dist) usb_minupdate_package = distenv.AddUsbFlashTarget(
distenv.Alias("flash_usb", usb_update_package) "#build/minusbinstall.flag", (selfupdate_min_dist,)
)
distenv.Alias("flash_usb", usb_minupdate_package)
# Target for copying & renaming binaries to dist folder # Target for copying & renaming binaries to dist folder
basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"]) basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
@@ -147,8 +169,9 @@ distenv.Alias("copro_dist", copro_dist)
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env) firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
distenv.Alias("flash", firmware_flash) distenv.Alias("flash", firmware_flash)
if distenv["FORCE"]:
distenv.AlwaysBuild(firmware_flash) firmware_jflash = distenv.AddJFlashTarget(firmware_env)
distenv.Alias("jflash", firmware_jflash)
firmware_bm_flash = distenv.PhonyTarget( firmware_bm_flash = distenv.PhonyTarget(
"flash_blackmagic", "flash_blackmagic",
@@ -209,6 +232,46 @@ distenv.PhonyTarget(
LINT_SOURCES=firmware_env["LINT_SOURCES"], LINT_SOURCES=firmware_env["LINT_SOURCES"],
) )
# PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests
# Here we add additional Python files residing in repo root
firmware_env.Append(
PY_LINT_SOURCES=[
# Py code folders
"site_scons",
"scripts",
# Extra files
"applications/extapps.scons",
"SConstruct",
"firmware.scons",
"fbt_options.py",
]
)
black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}"
black_base_args = ["--include", '"\\.scons|\\.py|SConscript|SConstruct"']
distenv.PhonyTarget(
"lint_py",
black_commandline,
PY_BLACK_ARGS=[
"--check",
"--diff",
*black_base_args,
],
PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"],
)
distenv.PhonyTarget(
"format_py",
black_commandline,
PY_BLACK_ARGS=black_base_args,
PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"],
)
# Start Flipper CLI via PySerial's miniterm
distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py")
# Find blackmagic probe # Find blackmagic probe

View File

@@ -38,6 +38,8 @@ appenv.AppendUnique(
"-Wl,--no-export-dynamic", "-Wl,--no-export-dynamic",
"-fvisibility=hidden", "-fvisibility=hidden",
"-Wl,-e${APP_ENTRY}", "-Wl,-e${APP_ENTRY}",
"-Xlinker",
"-Map=${TARGET}.map",
], ],
) )

View File

@@ -5,7 +5,7 @@
#include "m-string.h" #include "m-string.h"
#include <toolbox/path.h> #include <toolbox/path.h>
#include <flipper_format/flipper_format.h> #include <flipper_format/flipper_format.h>
#include "rpc/rpc_app.h" #include <rpc/rpc_app.h>
#define TAG "iButtonApp" #define TAG "iButtonApp"
@@ -58,7 +58,7 @@ static void ibutton_make_app_folder(iButton* ibutton) {
} }
} }
static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) {
FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); FlipperFormat* file = flipper_format_file_alloc(ibutton->storage);
bool result = false; bool result = false;
string_t data; string_t data;
@@ -99,34 +99,21 @@ static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show
return result; return result;
} }
static bool ibutton_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) {
furi_assert(context); furi_assert(context);
iButton* ibutton = context; iButton* ibutton = context;
bool result = false;
if(event == RpcAppEventSessionClose) { if(event == RpcAppEventSessionClose) {
rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); view_dispatcher_send_custom_event(
ibutton->rpc_ctx = NULL; ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose);
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
result = true;
} else if(event == RpcAppEventAppExit) { } else if(event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
result = true;
} else if(event == RpcAppEventLoadFile) { } else if(event == RpcAppEventLoadFile) {
if(arg) { view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
string_set_str(ibutton->file_path, arg); } else {
if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { rpc_system_app_confirm(ibutton->rpc_ctx, event, false);
ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key);
view_dispatcher_send_custom_event(
ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
result = true;
} }
} }
}
return result;
}
bool ibutton_custom_event_callback(void* context, uint32_t event) { bool ibutton_custom_event_callback(void* context, uint32_t event) {
furi_assert(context); furi_assert(context);

View File

@@ -12,4 +12,5 @@ enum iButtonCustomEvent {
iButtonCustomEventRpcLoad, iButtonCustomEventRpcLoad,
iButtonCustomEventRpcExit, iButtonCustomEventRpcExit,
iButtonCustomEventRpcSessionClose,
}; };

View File

@@ -78,6 +78,7 @@ typedef enum {
} iButtonNotificationMessage; } iButtonNotificationMessage;
bool ibutton_file_select(iButton* ibutton); bool ibutton_file_select(iButton* ibutton);
bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog);
bool ibutton_save_key(iButton* ibutton, const char* key_name); bool ibutton_save_key(iButton* ibutton, const char* key_name);
bool ibutton_delete_key(iButton* ibutton); bool ibutton_delete_key(iButton* ibutton);
void ibutton_text_store_set(iButton* ibutton, const char* text, ...); void ibutton_text_store_set(iButton* ibutton, const char* text, ...);

View File

@@ -1,5 +1,6 @@
#include "../ibutton_i.h" #include "../ibutton_i.h"
#include <toolbox/path.h> #include <toolbox/path.h>
#include <rpc/rpc_app.h>
void ibutton_scene_rpc_on_enter(void* context) { void ibutton_scene_rpc_on_enter(void* context) {
iButton* ibutton = context; iButton* ibutton = context;
@@ -26,6 +27,12 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
consumed = true; consumed = true;
if(event.event == iButtonCustomEventRpcLoad) { if(event.event == iButtonCustomEventRpcLoad) {
const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx);
bool result = false;
if(arg) {
string_set_str(ibutton->file_path, arg);
if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) {
ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key);
string_t key_name; string_t key_name;
string_init(key_name); string_init(key_name);
if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
@@ -33,7 +40,8 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
} }
if(!string_empty_p(key_name)) { if(!string_empty_p(key_name)) {
ibutton_text_store_set(ibutton, "emulating\n%s", string_get_cstr(key_name)); ibutton_text_store_set(
ibutton, "emulating\n%s", string_get_cstr(key_name));
} else { } else {
ibutton_text_store_set(ibutton, "emulating"); ibutton_text_store_set(ibutton, "emulating");
} }
@@ -42,7 +50,17 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
string_clear(key_name); string_clear(key_name);
result = true;
}
}
rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result);
} else if(event.event == iButtonCustomEventRpcExit) { } else if(event.event == iButtonCustomEventRpcExit) {
rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true);
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
view_dispatcher_stop(ibutton->view_dispatcher);
} else if(event.event == iButtonCustomEventRpcSessionClose) {
rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL);
ibutton->rpc_ctx = NULL;
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
view_dispatcher_stop(ibutton->view_dispatcher); view_dispatcher_stop(ibutton->view_dispatcher);
} }

View File

@@ -36,52 +36,29 @@ static void infrared_tick_event_callback(void* context) {
scene_manager_handle_tick_event(infrared->scene_manager); scene_manager_handle_tick_event(infrared->scene_manager);
} }
static bool static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) {
infrared_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) {
furi_assert(context); furi_assert(context);
Infrared* infrared = context; Infrared* infrared = context;
furi_assert(infrared->rpc_ctx);
if(!infrared->rpc_ctx) {
return false;
}
bool result = false;
if(event == RpcAppEventSessionClose) { if(event == RpcAppEventSessionClose) {
rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);
infrared->rpc_ctx = NULL;
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose);
result = true;
} else if(event == RpcAppEventAppExit) { } else if(event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); infrared->view_dispatcher, InfraredCustomEventTypeRpcExit);
result = true;
} else if(event == RpcAppEventLoadFile) { } else if(event == RpcAppEventLoadFile) {
if(arg) {
string_set_str(infrared->file_path, arg);
result = infrared_remote_load(infrared->remote, infrared->file_path);
infrared_worker_tx_set_get_signal_callback(
infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared);
infrared_worker_tx_set_signal_sent_callback(
infrared->worker, infrared_signal_sent_callback, infrared);
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoaded); infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad);
}
} else if(event == RpcAppEventButtonPress) { } else if(event == RpcAppEventButtonPress) {
if(arg) { view_dispatcher_send_custom_event(
size_t button_index = 0; infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress);
if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) {
infrared_tx_start_button_index(infrared, button_index);
result = true;
}
}
} else if(event == RpcAppEventButtonRelease) { } else if(event == RpcAppEventButtonRelease) {
infrared_tx_stop(infrared); view_dispatcher_send_custom_event(
result = true; infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease);
} else {
rpc_system_app_confirm(infrared->rpc_ctx, event, false);
} }
return result;
} }
static void infrared_find_vacant_remote_name(string_t name, const char* path) { static void infrared_find_vacant_remote_name(string_t name, const char* path) {

View File

@@ -14,7 +14,12 @@ enum InfraredCustomEventType {
InfraredCustomEventTypePopupClosed, InfraredCustomEventTypePopupClosed,
InfraredCustomEventTypeButtonSelected, InfraredCustomEventTypeButtonSelected,
InfraredCustomEventTypeBackPressed, InfraredCustomEventTypeBackPressed,
InfraredCustomEventTypeRpcLoaded,
InfraredCustomEventTypeRpcLoad,
InfraredCustomEventTypeRpcExit,
InfraredCustomEventTypeRpcButtonPress,
InfraredCustomEventTypeRpcButtonRelease,
InfraredCustomEventTypeRpcSessionClose,
}; };
#pragma pack(push, 1) #pragma pack(push, 1)

View File

@@ -28,12 +28,45 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
view_dispatcher_stop(infrared->view_dispatcher); view_dispatcher_stop(infrared->view_dispatcher);
} else if(event.event == InfraredCustomEventTypePopupClosed) { } else if(event.event == InfraredCustomEventTypePopupClosed) {
view_dispatcher_stop(infrared->view_dispatcher); view_dispatcher_stop(infrared->view_dispatcher);
} else if(event.event == InfraredCustomEventTypeRpcLoaded) { } else if(event.event == InfraredCustomEventTypeRpcLoad) {
bool result = false;
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
if(arg) {
string_set_str(infrared->file_path, arg);
result = infrared_remote_load(infrared->remote, infrared->file_path);
infrared_worker_tx_set_get_signal_callback(
infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared);
infrared_worker_tx_set_signal_sent_callback(
infrared->worker, infrared_signal_sent_callback, infrared);
}
const char* remote_name = infrared_remote_get_name(infrared->remote); const char* remote_name = infrared_remote_get_name(infrared->remote);
infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name);
popup_set_text( popup_set_text(
infrared->popup, infrared->text_store[0], 82, 32, AlignCenter, AlignTop); infrared->popup, infrared->text_store[0], 82, 32, AlignCenter, AlignTop);
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result);
} else if(event.event == InfraredCustomEventTypeRpcButtonPress) {
bool result = false;
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
if(arg) {
size_t button_index = 0;
if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) {
infrared_tx_start_button_index(infrared, button_index);
result = true;
}
}
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result);
} else if(event.event == InfraredCustomEventTypeRpcButtonRelease) {
infrared_tx_stop(infrared);
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, true);
} else if(event.event == InfraredCustomEventTypeRpcExit) {
view_dispatcher_stop(infrared->view_dispatcher);
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true);
} else if(event.event == InfraredCustomEventTypeRpcSessionClose) {
rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);
infrared->rpc_ctx = NULL;
view_dispatcher_stop(infrared->view_dispatcher);
} }
} }
return consumed; return consumed;

View File

@@ -25,7 +25,7 @@
#include <toolbox/path.h> #include <toolbox/path.h>
#include <flipper_format/flipper_format.h> #include <flipper_format/flipper_format.h>
#include "rpc/rpc_app.h" #include <rpc/rpc_app.h>
const char* LfRfidApp::app_folder = ANY_PATH("lfrfid"); const char* LfRfidApp::app_folder = ANY_PATH("lfrfid");
const char* LfRfidApp::app_extension = ".rfid"; const char* LfRfidApp::app_extension = ".rfid";
@@ -48,39 +48,26 @@ LfRfidApp::~LfRfidApp() {
} }
} }
static bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
furi_assert(context); furi_assert(context);
LfRfidApp* app = static_cast<LfRfidApp*>(context); LfRfidApp* app = static_cast<LfRfidApp*>(context);
bool result = false; if(rpc_event == RpcAppEventSessionClose) {
LfRfidApp::Event event;
if(event == RpcAppEventSessionClose) { event.type = LfRfidApp::EventType::RpcSessionClose;
rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); app->view_controller.send_event(&event);
app->rpc_ctx = NULL; } else if(rpc_event == RpcAppEventAppExit) {
LfRfidApp::Event event; LfRfidApp::Event event;
event.type = LfRfidApp::EventType::Exit; event.type = LfRfidApp::EventType::Exit;
app->view_controller.send_event(&event); app->view_controller.send_event(&event);
result = true; } else if(rpc_event == RpcAppEventLoadFile) {
} else if(event == RpcAppEventAppExit) {
LfRfidApp::Event event; LfRfidApp::Event event;
event.type = LfRfidApp::EventType::Exit; event.type = LfRfidApp::EventType::RpcLoadFile;
app->view_controller.send_event(&event); app->view_controller.send_event(&event);
result = true; } else {
} else if(event == RpcAppEventLoadFile) { rpc_system_app_confirm(app->rpc_ctx, rpc_event, false);
if(arg) {
string_set_str(app->file_path, arg);
if(app->load_key_data(app->file_path, &(app->worker.key), false)) {
LfRfidApp::Event event;
event.type = LfRfidApp::EventType::EmulateStart;
app->view_controller.send_event(&event);
app->worker.start_emulate();
result = true;
} }
} }
}
return result;
}
void LfRfidApp::run(void* _args) { void LfRfidApp::run(void* _args) {
const char* args = reinterpret_cast<const char*>(_args); const char* args = reinterpret_cast<const char*>(_args);

View File

@@ -33,6 +33,8 @@ public:
Retry, Retry,
Exit, Exit,
EmulateStart, EmulateStart,
RpcLoadFile,
RpcSessionClose,
}; };
enum class SceneType : uint8_t { enum class SceneType : uint8_t {

View File

@@ -1,6 +1,7 @@
#include "lfrfid_app_scene_rpc.h" #include "lfrfid_app_scene_rpc.h"
#include <core/common_defines.h> #include <core/common_defines.h>
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <rpc/rpc_app.h>
static const NotificationSequence sequence_blink_start_magenta = { static const NotificationSequence sequence_blink_start_magenta = {
&message_blink_start_10, &message_blink_start_10,
@@ -36,6 +37,16 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
LfRfidApp::Event view_event; LfRfidApp::Event view_event;
view_event.type = LfRfidApp::EventType::Back; view_event.type = LfRfidApp::EventType::Back;
app->view_controller.send_event(&view_event); app->view_controller.send_event(&view_event);
rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true);
} else if(event->type == LfRfidApp::EventType::RpcSessionClose) {
// Detach RPC
rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL);
app->rpc_ctx = NULL;
consumed = true;
LfRfidApp::Event view_event;
view_event.type = LfRfidApp::EventType::Back;
app->view_controller.send_event(&view_event);
} else if(event->type == LfRfidApp::EventType::EmulateStart) { } else if(event->type == LfRfidApp::EventType::EmulateStart) {
auto popup = app->view_controller.get<PopupVM>(); auto popup = app->view_controller.get<PopupVM>();
consumed = true; consumed = true;
@@ -45,7 +56,22 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
popup->set_text(app->text_store.text, 89, 43, AlignCenter, AlignTop); popup->set_text(app->text_store.text, 89, 43, AlignCenter, AlignTop);
notification_message(app->notification, &sequence_blink_start_magenta); notification_message(app->notification, &sequence_blink_start_magenta);
} else if(event->type == LfRfidApp::EventType::RpcLoadFile) {
const char* arg = rpc_system_app_get_data(app->rpc_ctx);
bool result = false;
if(arg) {
string_set_str(app->file_path, arg);
if(app->load_key_data(app->file_path, &(app->worker.key), false)) {
LfRfidApp::Event event;
event.type = LfRfidApp::EventType::EmulateStart;
app->view_controller.send_event(&event);
app->worker.start_emulate();
result = true;
} }
}
rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result);
}
return consumed; return consumed;
} }

View File

@@ -11,4 +11,5 @@ enum NfcCustomEvent {
NfcCustomEventDictAttackDone, NfcCustomEventDictAttackDone,
NfcCustomEventDictAttackSkip, NfcCustomEventDictAttackSkip,
NfcCustomEventRpcLoad, NfcCustomEventRpcLoad,
NfcCustomEventRpcSessionClose,
}; };

View File

@@ -13,79 +13,22 @@ bool nfc_back_event_callback(void* context) {
return scene_manager_handle_back_event(nfc->scene_manager); return scene_manager_handle_back_event(nfc->scene_manager);
} }
void nfc_rpc_exit_callback(Nfc* nfc) { static void nfc_rpc_command_callback(RpcAppSystemEvent event, void* context) {
if(nfc->rpc_state == NfcRpcStateEmulating) {
// Stop worker
nfc_worker_stop(nfc->worker);
} else if(nfc->rpc_state == NfcRpcStateEmulated) {
// Stop worker
nfc_worker_stop(nfc->worker);
// Save data in shadow file
nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
}
if(nfc->rpc_ctx) {
rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL);
rpc_system_app_send_exited(nfc->rpc_ctx);
nfc->rpc_ctx = NULL;
}
}
static bool nfc_rpc_emulate_callback(NfcWorkerEvent event, void* context) {
UNUSED(event);
Nfc* nfc = context;
nfc->rpc_state = NfcRpcStateEmulated;
return true;
}
static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) {
furi_assert(context); furi_assert(context);
Nfc* nfc = context; Nfc* nfc = context;
if(!nfc->rpc_ctx) { furi_assert(nfc->rpc_ctx);
return false;
}
bool result = false;
if(event == RpcAppEventSessionClose) { if(event == RpcAppEventSessionClose) {
rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose);
nfc->rpc_ctx = NULL;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
result = true;
} else if(event == RpcAppEventAppExit) { } else if(event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
result = true;
} else if(event == RpcAppEventLoadFile) { } else if(event == RpcAppEventLoadFile) {
if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) {
if(nfc_device_load(nfc->dev, arg, false)) {
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
nfc_worker_start(
nfc->worker,
NfcWorkerStateMfUltralightEmulate,
&nfc->dev->dev_data,
nfc_rpc_emulate_callback,
nfc);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
nfc_worker_start(
nfc->worker,
NfcWorkerStateMfClassicEmulate,
&nfc->dev->dev_data,
nfc_rpc_emulate_callback,
nfc);
} else {
nfc_worker_start(
nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc);
}
nfc->rpc_state = NfcRpcStateEmulating;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad);
result = true; } else {
rpc_system_app_confirm(nfc->rpc_ctx, event, false);
} }
} }
}
return result;
}
Nfc* nfc_alloc() { Nfc* nfc_alloc() {
Nfc* nfc = malloc(sizeof(Nfc)); Nfc* nfc = malloc(sizeof(Nfc));
@@ -163,6 +106,21 @@ Nfc* nfc_alloc() {
void nfc_free(Nfc* nfc) { void nfc_free(Nfc* nfc) {
furi_assert(nfc); furi_assert(nfc);
if(nfc->rpc_state == NfcRpcStateEmulating) {
// Stop worker
nfc_worker_stop(nfc->worker);
} else if(nfc->rpc_state == NfcRpcStateEmulated) {
// Stop worker
nfc_worker_stop(nfc->worker);
// Save data in shadow file
nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
}
if(nfc->rpc_ctx) {
rpc_system_app_send_exited(nfc->rpc_ctx);
rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL);
nfc->rpc_ctx = NULL;
}
// Nfc device // Nfc device
nfc_device_free(nfc->dev); nfc_device_free(nfc->dev);

View File

@@ -102,5 +102,3 @@ void nfc_blink_start(Nfc* nfc);
void nfc_blink_stop(Nfc* nfc); void nfc_blink_stop(Nfc* nfc);
void nfc_show_loading_popup(void* context, bool show); void nfc_show_loading_popup(void* context, bool show);
void nfc_rpc_exit_callback(Nfc* nfc);

View File

@@ -14,6 +14,14 @@ void nfc_scene_rpc_on_enter(void* context) {
notification_message(nfc->notifications, &sequence_display_backlight_on); notification_message(nfc->notifications, &sequence_display_backlight_on);
} }
static bool nfc_scene_rpc_emulate_callback(NfcWorkerEvent event, void* context) {
UNUSED(event);
Nfc* nfc = context;
nfc->rpc_state = NfcRpcStateEmulated;
return true;
}
bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context; Nfc* nfc = context;
Popup* popup = nfc->popup; Popup* popup = nfc->popup;
@@ -22,15 +30,49 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
consumed = true; consumed = true;
if(event.event == NfcCustomEventViewExit) { if(event.event == NfcCustomEventViewExit) {
rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventAppExit, true);
view_dispatcher_stop(nfc->view_dispatcher);
nfc_blink_stop(nfc);
} else if(event.event == NfcCustomEventRpcSessionClose) {
rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL);
nfc->rpc_ctx = NULL;
view_dispatcher_stop(nfc->view_dispatcher); view_dispatcher_stop(nfc->view_dispatcher);
nfc_blink_stop(nfc); nfc_blink_stop(nfc);
} else if(event.event == NfcCustomEventRpcLoad) { } else if(event.event == NfcCustomEventRpcLoad) {
nfc_blink_start(nfc); bool result = false;
const char* arg = rpc_system_app_get_data(nfc->rpc_ctx);
if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) {
if(nfc_device_load(nfc->dev, arg, false)) {
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
nfc_worker_start(
nfc->worker,
NfcWorkerStateMfUltralightEmulate,
&nfc->dev->dev_data,
nfc_scene_rpc_emulate_callback,
nfc);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
nfc_worker_start(
nfc->worker,
NfcWorkerStateMfClassicEmulate,
&nfc->dev->dev_data,
nfc_scene_rpc_emulate_callback,
nfc);
} else {
nfc_worker_start(
nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc);
}
nfc->rpc_state = NfcRpcStateEmulating;
result = true;
nfc_blink_start(nfc);
nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name); nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name);
popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop); popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop);
} }
} }
rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventLoadFile, result);
}
}
return consumed; return consumed;
} }
@@ -38,7 +80,6 @@ void nfc_scene_rpc_on_exit(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
Popup* popup = nfc->popup; Popup* popup = nfc->popup;
nfc_rpc_exit_callback(nfc);
nfc_blink_stop(nfc); nfc_blink_stop(nfc);
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);

View File

@@ -6,24 +6,18 @@
#include "rpc_app.h" #include "rpc_app.h"
#define TAG "RpcSystemApp" #define TAG "RpcSystemApp"
#define APP_BUTTON_TIMEOUT 1000
struct RpcAppSystem { struct RpcAppSystem {
RpcSession* session; RpcSession* session;
RpcAppSystemCallback app_callback; RpcAppSystemCallback app_callback;
void* app_context; void* app_context;
PB_Main* state_msg; PB_Main* state_msg;
FuriTimer* timer;
uint32_t last_id;
char* last_data;
}; };
static void rpc_system_app_timer_callback(void* context) { #define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16
furi_assert(context);
RpcAppSystem* rpc_app = context;
if(rpc_app->app_callback) {
rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context);
}
}
static void rpc_system_app_start_process(const PB_Main* request, void* context) { static void rpc_system_app_start_process(const PB_Main* request, void* context) {
furi_assert(request); furi_assert(request);
@@ -33,9 +27,12 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context)
RpcAppSystem* rpc_app = context; RpcAppSystem* rpc_app = context;
RpcSession* session = rpc_app->session; RpcSession* session = rpc_app->session;
furi_assert(session); furi_assert(session);
char args_temp[16]; char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE];
FURI_LOG_D(TAG, "Start"); furi_assert(!rpc_app->last_id);
furi_assert(!rpc_app->last_data);
FURI_LOG_D(TAG, "StartProcess: id %d", request->command_id);
PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START;
@@ -43,9 +40,9 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context)
const char* app_name = request->content.app_start_request.name; const char* app_name = request->content.app_start_request.name;
if(app_name) { if(app_name) {
const char* app_args = request->content.app_start_request.args; const char* app_args = request->content.app_start_request.args;
if(strcmp(app_args, "RPC") == 0) { if(app_args && strcmp(app_args, "RPC") == 0) {
// If app is being started in RPC mode - pass RPC context via args string // If app is being started in RPC mode - pass RPC context via args string
snprintf(args_temp, 16, "RPC %08lX", (uint32_t)rpc_app); snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app);
app_args = args_temp; app_args = args_temp;
} }
LoaderStatus status = loader_start(loader, app_name, app_args); LoaderStatus status = loader_start(loader, app_name, app_args);
@@ -58,7 +55,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context)
} else if(status == LoaderStatusOk) { } else if(status == LoaderStatusOk) {
result = PB_CommandStatus_OK; result = PB_CommandStatus_OK;
} else { } else {
furi_assert(0); furi_crash("Programming Error");
} }
} else { } else {
result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; result = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
@@ -66,6 +63,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context)
furi_record_close(RECORD_LOADER); furi_record_close(RECORD_LOADER);
FURI_LOG_D(TAG, "StartProcess: response id %d, result %d", request->command_id, result);
rpc_send_and_release_empty(session, request->command_id, result); rpc_send_and_release_empty(session, request->command_id, result);
} }
@@ -93,6 +91,7 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con
furi_record_close(RECORD_LOADER); furi_record_close(RECORD_LOADER);
FURI_LOG_D(TAG, "LockStatus: response");
rpc_send_and_release(session, &response); rpc_send_and_release(session, &response);
pb_release(&PB_Main_msg, &response); pb_release(&PB_Main_msg, &response);
} }
@@ -109,18 +108,18 @@ static void rpc_system_app_exit_request(const PB_Main* request, void* context) {
PB_CommandStatus status; PB_CommandStatus status;
if(rpc_app->app_callback) { if(rpc_app->app_callback) {
if(rpc_app->app_callback(RpcAppEventAppExit, NULL, rpc_app->app_context)) { FURI_LOG_D(TAG, "ExitRequest: id %d", request->command_id);
status = PB_CommandStatus_OK; furi_assert(!rpc_app->last_id);
furi_timer_stop(rpc_app->timer); furi_assert(!rpc_app->last_data);
} else { rpc_app->last_id = request->command_id;
status = PB_CommandStatus_ERROR_APP_CMD_ERROR; rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context);
}
} else { } else {
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
} FURI_LOG_E(
TAG, "ExitRequest: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status);
rpc_send_and_release_empty(session, request->command_id, status); rpc_send_and_release_empty(session, request->command_id, status);
} }
}
static void rpc_system_app_load_file(const PB_Main* request, void* context) { static void rpc_system_app_load_file(const PB_Main* request, void* context) {
furi_assert(request); furi_assert(request);
@@ -133,18 +132,19 @@ static void rpc_system_app_load_file(const PB_Main* request, void* context) {
PB_CommandStatus status; PB_CommandStatus status;
if(rpc_app->app_callback) { if(rpc_app->app_callback) {
const char* file_path = request->content.app_load_file_request.path; FURI_LOG_D(TAG, "LoadFile: id %d", request->command_id);
if(rpc_app->app_callback(RpcAppEventLoadFile, file_path, rpc_app->app_context)) { furi_assert(!rpc_app->last_id);
status = PB_CommandStatus_OK; furi_assert(!rpc_app->last_data);
} else { rpc_app->last_id = request->command_id;
status = PB_CommandStatus_ERROR_APP_CMD_ERROR; rpc_app->last_data = strdup(request->content.app_load_file_request.path);
} rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context);
} else { } else {
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
} FURI_LOG_E(
TAG, "LoadFile: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status);
rpc_send_and_release_empty(session, request->command_id, status); rpc_send_and_release_empty(session, request->command_id, status);
} }
}
static void rpc_system_app_button_press(const PB_Main* request, void* context) { static void rpc_system_app_button_press(const PB_Main* request, void* context) {
furi_assert(request); furi_assert(request);
@@ -157,19 +157,19 @@ static void rpc_system_app_button_press(const PB_Main* request, void* context) {
PB_CommandStatus status; PB_CommandStatus status;
if(rpc_app->app_callback) { if(rpc_app->app_callback) {
const char* args = request->content.app_button_press_request.args; FURI_LOG_D(TAG, "ButtonPress");
if(rpc_app->app_callback(RpcAppEventButtonPress, args, rpc_app->app_context)) { furi_assert(!rpc_app->last_id);
status = PB_CommandStatus_OK; furi_assert(!rpc_app->last_data);
furi_timer_start(rpc_app->timer, APP_BUTTON_TIMEOUT); rpc_app->last_id = request->command_id;
} else { rpc_app->last_data = strdup(request->content.app_button_press_request.args);
status = PB_CommandStatus_ERROR_APP_CMD_ERROR; rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context);
}
} else { } else {
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
} FURI_LOG_E(
TAG, "ButtonPress: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status);
rpc_send_and_release_empty(session, request->command_id, status); rpc_send_and_release_empty(session, request->command_id, status);
} }
}
static void rpc_system_app_button_release(const PB_Main* request, void* context) { static void rpc_system_app_button_release(const PB_Main* request, void* context) {
furi_assert(request); furi_assert(request);
@@ -182,18 +182,18 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context)
PB_CommandStatus status; PB_CommandStatus status;
if(rpc_app->app_callback) { if(rpc_app->app_callback) {
if(rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context)) { FURI_LOG_D(TAG, "ButtonRelease");
status = PB_CommandStatus_OK; furi_assert(!rpc_app->last_id);
furi_timer_stop(rpc_app->timer); furi_assert(!rpc_app->last_data);
} else { rpc_app->last_id = request->command_id;
status = PB_CommandStatus_ERROR_APP_CMD_ERROR; rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context);
}
} else { } else {
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
} FURI_LOG_E(
TAG, "ButtonRelease: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status);
rpc_send_and_release_empty(session, request->command_id, status); rpc_send_and_release_empty(session, request->command_id, status);
} }
}
void rpc_system_app_send_started(RpcAppSystem* rpc_app) { void rpc_system_app_send_started(RpcAppSystem* rpc_app) {
furi_assert(rpc_app); furi_assert(rpc_app);
@@ -201,6 +201,8 @@ void rpc_system_app_send_started(RpcAppSystem* rpc_app) {
furi_assert(session); furi_assert(session);
rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED; rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED;
FURI_LOG_D(TAG, "SendStarted");
rpc_send(session, rpc_app->state_msg); rpc_send(session, rpc_app->state_msg);
} }
@@ -210,9 +212,46 @@ void rpc_system_app_send_exited(RpcAppSystem* rpc_app) {
furi_assert(session); furi_assert(session);
rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED; rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED;
FURI_LOG_D(TAG, "SendExit");
rpc_send(session, rpc_app->state_msg); rpc_send(session, rpc_app->state_msg);
} }
const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) {
furi_assert(rpc_app);
furi_assert(rpc_app->last_data);
return rpc_app->last_data;
}
void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) {
furi_assert(rpc_app);
RpcSession* session = rpc_app->session;
furi_assert(session);
furi_assert(rpc_app->last_id);
PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR;
uint32_t last_id = 0;
switch(event) {
case RpcAppEventAppExit:
case RpcAppEventLoadFile:
case RpcAppEventButtonPress:
case RpcAppEventButtonRelease:
last_id = rpc_app->last_id;
rpc_app->last_id = 0;
if(rpc_app->last_data) {
free(rpc_app->last_data);
rpc_app->last_data = NULL;
}
FURI_LOG_D(TAG, "AppConfirm: event %d last_id %d status %d", event, last_id, status);
rpc_send_and_release_empty(session, last_id, status);
break;
default:
furi_crash("RPC App state programming Error");
break;
}
}
void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) {
furi_assert(rpc_app); furi_assert(rpc_app);
@@ -226,8 +265,6 @@ void* rpc_system_app_alloc(RpcSession* session) {
RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem));
rpc_app->session = session; rpc_app->session = session;
rpc_app->timer = furi_timer_alloc(rpc_system_app_timer_callback, FuriTimerTypeOnce, rpc_app);
// App exit message // App exit message
rpc_app->state_msg = malloc(sizeof(PB_Main)); rpc_app->state_msg = malloc(sizeof(PB_Main));
rpc_app->state_msg->which_content = PB_Main_app_state_response_tag; rpc_app->state_msg->which_content = PB_Main_app_state_response_tag;
@@ -265,12 +302,16 @@ void rpc_system_app_free(void* context) {
RpcSession* session = rpc_app->session; RpcSession* session = rpc_app->session;
furi_assert(session); furi_assert(session);
furi_timer_free(rpc_app->timer);
if(rpc_app->app_callback) { if(rpc_app->app_callback) {
rpc_app->app_callback(RpcAppEventSessionClose, NULL, rpc_app->app_context); rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context);
} }
while(rpc_app->app_callback) {
furi_delay_tick(1);
}
if(rpc_app->last_data) free(rpc_app->last_data);
free(rpc_app->state_msg); free(rpc_app->state_msg);
free(rpc_app); free(rpc_app);
} }

View File

@@ -13,7 +13,7 @@ typedef enum {
RpcAppEventButtonRelease, RpcAppEventButtonRelease,
} RpcAppSystemEvent; } RpcAppSystemEvent;
typedef bool (*RpcAppSystemCallback)(RpcAppSystemEvent event, const char* arg, void* context); typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context);
typedef struct RpcAppSystem RpcAppSystem; typedef struct RpcAppSystem RpcAppSystem;
@@ -23,6 +23,10 @@ void rpc_system_app_send_started(RpcAppSystem* rpc_app);
void rpc_system_app_send_exited(RpcAppSystem* rpc_app); void rpc_system_app_send_exited(RpcAppSystem* rpc_app);
const char* rpc_system_app_get_data(RpcAppSystem* rpc_app);
void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -50,6 +50,9 @@ typedef enum {
SubGhzCustomEventSceneStay, SubGhzCustomEventSceneStay,
SubGhzCustomEventSceneRpcLoad, SubGhzCustomEventSceneRpcLoad,
SubGhzCustomEventSceneRpcButtonPress,
SubGhzCustomEventSceneRpcButtonRelease,
SubGhzCustomEventSceneRpcSessionClose,
SubGhzCustomEventViewReceiverOK, SubGhzCustomEventViewReceiverOK,
SubGhzCustomEventViewReceiverConfig, SubGhzCustomEventViewReceiverConfig,

View File

@@ -22,8 +22,45 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
consumed = true; consumed = true;
if(event.event == SubGhzCustomEventSceneExit) { if(event.event == SubGhzCustomEventSceneExit) {
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
view_dispatcher_stop(subghz->view_dispatcher); view_dispatcher_stop(subghz->view_dispatcher);
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventAppExit, true);
} else if(event.event == SubGhzCustomEventSceneRpcSessionClose) {
rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL);
subghz->rpc_ctx = NULL;
subghz_blink_stop(subghz);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit);
} else if(event.event == SubGhzCustomEventSceneRpcButtonPress) {
bool result = false;
if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) {
subghz_blink_start(subghz);
result = subghz_tx_start(subghz, subghz->txrx->fff_data);
result = true;
}
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result);
} else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) {
bool result = false;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_blink_stop(subghz);
subghz_tx_stop(subghz);
subghz_sleep(subghz);
result = true;
}
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result);
} else if(event.event == SubGhzCustomEventSceneRpcLoad) { } else if(event.event == SubGhzCustomEventSceneRpcLoad) {
bool result = false;
const char* arg = rpc_system_app_get_data(subghz->rpc_ctx);
if(arg) {
if(subghz_key_load(subghz, arg, false)) {
string_set_str(subghz->file_path, arg);
result = true;
string_t file_name; string_t file_name;
string_init(file_name); string_init(file_name);
path_extract_filename(subghz->file_path, file_name, true); path_extract_filename(subghz->file_path, file_name, true);
@@ -38,6 +75,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
string_clear(file_name); string_clear(file_name);
} }
} }
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result);
}
}
return consumed; return consumed;
} }

View File

@@ -35,57 +35,38 @@ void subghz_tick_event_callback(void* context) {
scene_manager_handle_tick_event(subghz->scene_manager); scene_manager_handle_tick_event(subghz->scene_manager);
} }
static bool subghz_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
if(!subghz->rpc_ctx) { furi_assert(subghz->rpc_ctx);
return false;
}
bool result = false;
if(event == RpcAppEventSessionClose) { if(event == RpcAppEventSessionClose) {
rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); view_dispatcher_send_custom_event(
subghz->rpc_ctx = NULL; subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose);
notification_message(subghz->notifications, &sequence_blink_stop);
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
result = true;
} else if(event == RpcAppEventAppExit) { } else if(event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
result = true;
} else if(event == RpcAppEventLoadFile) { } else if(event == RpcAppEventLoadFile) {
if(arg) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad);
if(subghz_key_load(subghz, arg, false)) {
string_set_str(subghz->file_path, arg);
view_dispatcher_send_custom_event(
subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad);
result = true;
}
}
} else if(event == RpcAppEventButtonPress) { } else if(event == RpcAppEventButtonPress) {
if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { view_dispatcher_send_custom_event(
notification_message(subghz->notifications, &sequence_blink_start_magenta); subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress);
result = subghz_tx_start(subghz, subghz->txrx->fff_data);
}
} else if(event == RpcAppEventButtonRelease) { } else if(event == RpcAppEventButtonRelease) {
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { view_dispatcher_send_custom_event(
notification_message(subghz->notifications, &sequence_blink_stop); subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease);
subghz_tx_stop(subghz); } else {
subghz_sleep(subghz); rpc_system_app_confirm(subghz->rpc_ctx, event, false);
result = true;
} }
} }
return result; void subghz_blink_start(SubGhz* instance) {
furi_assert(instance);
notification_message(instance->notifications, &sequence_blink_start_magenta);
}
void subghz_blink_stop(SubGhz* instance) {
furi_assert(instance);
notification_message(instance->notifications, &sequence_blink_stop);
} }
SubGhz* subghz_alloc() { SubGhz* subghz_alloc() {
@@ -243,7 +224,7 @@ void subghz_free(SubGhz* subghz) {
if(subghz->rpc_ctx) { if(subghz->rpc_ctx) {
rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL);
rpc_system_app_send_exited(subghz->rpc_ctx); rpc_system_app_send_exited(subghz->rpc_ctx);
notification_message(subghz->notifications, &sequence_blink_stop); subghz_blink_stop(subghz);
subghz->rpc_ctx = NULL; subghz->rpc_ctx = NULL;
} }

View File

@@ -117,6 +117,10 @@ void subghz_begin(SubGhz* subghz, uint8_t* preset_data);
uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency); uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency);
void subghz_rx_end(SubGhz* subghz); void subghz_rx_end(SubGhz* subghz);
void subghz_sleep(SubGhz* subghz); void subghz_sleep(SubGhz* subghz);
void subghz_blink_start(SubGhz* instance);
void subghz_blink_stop(SubGhz* instance);
bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format);
void subghz_tx_stop(SubGhz* subghz); void subghz_tx_stop(SubGhz* subghz);
void subghz_dialog_message_show_only_rx(SubGhz* subghz); void subghz_dialog_message_show_only_rx(SubGhz* subghz);

View File

@@ -387,6 +387,34 @@ MU_TEST(stream_split_test) {
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
} }
MU_TEST(stream_buffered_write_after_read_test) {
const char* prefix = "I write ";
const char* substr = "Hello there";
const size_t substr_len = strlen(substr);
const size_t prefix_len = strlen(prefix);
const size_t buf_size = substr_len + 1;
char buf[buf_size];
memset(buf, 0, buf_size);
Storage* storage = furi_record_open(RECORD_STORAGE);
Stream* stream = buffered_file_stream_alloc(storage);
mu_check(buffered_file_stream_open(
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
mu_check(stream_rewind(stream));
mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len));
mu_assert_string_eq(prefix, buf);
mu_assert_int_eq(substr_len, stream_write(stream, (uint8_t*)substr, substr_len));
mu_check(stream_seek(stream, prefix_len, StreamOffsetFromStart));
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
mu_assert_string_eq(substr, buf);
stream_free(stream);
furi_record_close(RECORD_STORAGE);
}
MU_TEST(stream_buffered_large_file_test) { MU_TEST(stream_buffered_large_file_test) {
string_t input_data; string_t input_data;
string_t output_data; string_t output_data;
@@ -470,6 +498,7 @@ MU_TEST_SUITE(stream_suite) {
MU_RUN_TEST(stream_write_read_save_load_test); MU_RUN_TEST(stream_write_read_save_load_test);
MU_RUN_TEST(stream_composite_test); MU_RUN_TEST(stream_composite_test);
MU_RUN_TEST(stream_split_test); MU_RUN_TEST(stream_split_test);
MU_RUN_TEST(stream_buffered_write_after_read_test);
MU_RUN_TEST(stream_buffered_large_file_test); MU_RUN_TEST(stream_buffered_large_file_test);
} }

View File

@@ -103,7 +103,6 @@ if assetsenv["IS_BASE_FIRMWARE"]:
) )
assetsenv.Precious(resources) assetsenv.Precious(resources)
assetsenv.NoClean(resources) assetsenv.NoClean(resources)
if assetsenv["FORCE"]:
assetsenv.AlwaysBuild(resources) assetsenv.AlwaysBuild(resources)
# Exporting resources node to external environment # Exporting resources node to external environment

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

View File

@@ -1 +0,0 @@
3

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 B

View File

@@ -1 +0,0 @@
3

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

View File

@@ -1 +0,0 @@
3

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 B

View File

@@ -1 +0,0 @@
3

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

90
debug/fw.jflash Normal file
View File

@@ -0,0 +1,90 @@
AppVersion = 76803
FileVersion = 2
[GENERAL]
aATEModuleSel[24] = 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
ConnectMode = 0
CurrentFile = "..\build\latest\firmware.bin"
DataFileSAddr = 0x08000000
GUIMode = 0
HostName = ""
TargetIF = 1
USBPort = 0
USBSerialNo = 0x00000000
UseATEModuleSelection = 0
[JTAG]
IRLen = 0
MultipleTargets = 0
NumDevices = 0
Speed0 = 8000
Speed1 = 8000
TAP_Number = 0
UseAdaptive0 = 0
UseAdaptive1 = 0
UseMaxSpeed0 = 0
UseMaxSpeed1 = 0
[CPU]
NumInitSteps = 2
InitStep0_Action = "Reset"
InitStep0_Value0 = 0x00000000
InitStep0_Value1 = 0x00000000
InitStep0_Comment = ""
InitStep1_Action = "Halt"
InitStep1_Value0 = 0xFFFFFFFF
InitStep1_Value1 = 0xFFFFFFFF
InitStep1_Comment = ""
NumExitSteps = 1
ExitStep0_Action = "Reset"
ExitStep0_Value0 = 0x00000005
ExitStep0_Value1 = 0x00000032
ExitStep0_Comment = ""
UseScriptFile = 0
ScriptFile = ""
UseRAM = 1
RAMAddr = 0x20000000
RAMSize = 0x00030000
CheckCoreID = 1
CoreID = 0x6BA02477
CoreIDMask = 0x0F000FFF
UseAutoSpeed = 0x00000001
ClockSpeed = 0x00000000
EndianMode = 0
ChipName = "ST STM32WB55RG"
[FLASH]
aRangeSel[1] = 0-255
BankName = "Internal flash"
BankSelMode = 1
BaseAddr = 0x08000000
NumBanks = 1
[PRODUCTION]
AutoPerformsDisconnect = 0
AutoPerformsErase = 1
AutoPerformsProgram = 1
AutoPerformsSecure = 0
AutoPerformsStartApp = 1
AutoPerformsUnsecure = 0
AutoPerformsVerify = 0
EnableFixedVTref = 0
EnableTargetPower = 0
EraseType = 1
FixedVTref = 0x00000CE4
MonitorVTref = 0
MonitorVTrefMax = 0x0000157C
MonitorVTrefMin = 0x000003E8
OverrideTimeouts = 0
ProgramSN = 0
SerialFile = ""
SNAddr = 0x00000000
SNInc = 0x00000001
SNLen = 0x00000004
SNListFile = ""
SNValue = 0x00000001
StartAppType = 1
TargetPowerDelay = 0x00000014
TimeoutErase = 0x00003A98
TimeoutProgram = 0x00002710
TimeoutVerify = 0x00002710
VerifyType = 1
[PERFORMANCE]
DisableSkipBlankDataOnProgram = 0x00000000
PerfromBlankCheckPriorEraseChip = 0x00000001
PerfromBlankCheckPriorEraseSelectedSectors = 0x00000001

View File

@@ -110,7 +110,12 @@ Even if something goes wrong, Updater gives you an option to retry failed operat
## Full package ## Full package
To build a basic update package, run `./fbt --with-updater COMPACT=1 DEBUG=0 updater_package` To build full update package, including firmware, radio stack and resources for SD card, run `./fbt COMPACT=1 DEBUG=0 updater_package`
## Minimal package
To build minimal update package, including only firmware, run `./fbt COMPACT=1 DEBUG=0 updater_minpackage`
## Customizing update bundles ## Customizing update bundles
@@ -118,7 +123,7 @@ To build a basic update package, run `./fbt --with-updater COMPACT=1 DEBUG=0 upd
Default update packages are built with Bluetooth Light stack. Default update packages are built with Bluetooth Light stack.
You can pick a different stack, if your firmware version supports it, and build a bundle with it passing stack type and binary name to `fbt`: You can pick a different stack, if your firmware version supports it, and build a bundle with it passing stack type and binary name to `fbt`:
`./fbt --with-updater updater_package COMPACT=1 DEBUG=0 COPRO_OB_DATA=scripts/ob_custradio.data COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin COPRO_STACK_TYPE=ble_full` `./fbt updater_package COMPACT=1 DEBUG=0 COPRO_OB_DATA=scripts/ob_custradio.data COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin COPRO_STACK_TYPE=ble_full`
Note that `COPRO_OB_DATA` must point to a valid file in `scripts` folder containing reference Option Byte data matching to your radio stack type. Note that `COPRO_OB_DATA` must point to a valid file in `scripts` folder containing reference Option Byte data matching to your radio stack type.

View File

@@ -20,7 +20,7 @@ Make sure that `gcc-arm-none-eabi` toolchain & OpenOCD executables are in system
To build with FBT, call it specifying configuration options & targets to build. For example, To build with FBT, call it specifying configuration options & targets to build. For example,
`./fbt --with-updater COMPACT=1 DEBUG=0 VERBOSE=1 updater_package copro_dist` `./fbt COMPACT=1 DEBUG=0 VERBOSE=1 updater_package copro_dist`
To run cleanup (think of `make clean`) for specified targets, add `-c` option. To run cleanup (think of `make clean`) for specified targets, add `-c` option.
@@ -31,16 +31,18 @@ FBT keeps track of internal dependencies, so you only need to build the highest-
### High-level (what you most likely need) ### High-level (what you most likely need)
- `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified - `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified
- `updater_package` - build self-update package. _Requires `--with-updater` option_ - `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card
- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper - `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper
- `flash` - flash attached device with OpenOCD over ST-Link - `flash` - flash attached device with OpenOCD over ST-Link
- `flash_usb` - build, upload and install update package to device over USB. _Requires `--with-updater` option_ - `flash_usb`, `flash_usb_full` - build, upload and install update package to device over USB. See details on `updater_package`, `updater_minpackage`
- `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded - `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded
- `debug_updater` - attach gdb with updater's .elf loaded. _Requires `--with-updater` option_ - `debug_other` - attach gdb without loading any .elf. Allows to manually add external elf files with `add-symbol-file` in gdb
- `debug_other` - attach gdb without loading any .elf. Allows to manually add external elf files with `add-symbol-file` in gdb. - `updater_debug` - attach gdb with updater's .elf loaded
- `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board) - `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board)
- `openocd` - just start OpenOCD - `openocd` - just start OpenOCD
- `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration - `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration
- `lint`, `format` - run clang-tidy on C source code to check and reformat it according to `.clang-format` specs
- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests
### Firmware targets ### Firmware targets
@@ -49,9 +51,11 @@ FBT keeps track of internal dependencies, so you only need to build the highest-
- Check out `--extra-ext-apps` for force adding extra apps to external build - Check out `--extra-ext-apps` for force adding extra apps to external build
- `firmware_snake_game_list`, etc - generate source + assembler listing for app's .elf - `firmware_snake_game_list`, etc - generate source + assembler listing for app's .elf
- `flash`, `firmware_flash` - flash current version to attached device with OpenOCD over ST-Link - `flash`, `firmware_flash` - flash current version to attached device with OpenOCD over ST-Link
- `jflash` - flash current version to attached device with JFlash using J-Link probe. JFlash executable must be on your $PATH
- `flash_blackmagic` - flash current version to attached device with Blackmagic probe - `flash_blackmagic` - flash current version to attached device with Blackmagic probe
- `firmware_all`, `updater_all` - build basic set of binaries - `firmware_all`, `updater_all` - build basic set of binaries
- `firmware_list`, `updater_list` - generate source + assembler listing - `firmware_list`, `updater_list` - generate source + assembler listing
- `firmware_cdb`, `updater_cdb` - generate `compilation_database.json` file for external tools and IDEs. It can be created without actually building the firmware.
### Assets ### Assets
@@ -66,7 +70,7 @@ FBT keeps track of internal dependencies, so you only need to build the highest-
## Command-line parameters ## Command-line parameters
- `--options optionfile.py` (default value `fbt_options.py`) - load file with multiple configuration values - `--options optionfile.py` (default value `fbt_options.py`) - load file with multiple configuration values
- `--with-updater` - enables updater-related targets and dependency tracking. Enabling this option introduces extra startup time costs, so use it when bundling update packages. Or if you have a fast computer and don't care about a few extra seconds of startup time - `--with-updater` - enables updater-related targets and dependency tracking. Enabling this option introduces extra startup time costs, so use it when bundling update packages. _Explicily enabling this should no longer be required, fbt now has specific handling for updater-related targets_
- `--extra-int-apps=app1,app2,appN` - forces listed apps to be built as internal with `firmware` target - `--extra-int-apps=app1,app2,appN` - forces listed apps to be built as internal with `firmware` target
- `--extra-ext-apps=app1,app2,appN` - forces listed apps to be built as external with `firmware_extapps` target - `--extra-ext-apps=app1,app2,appN` - forces listed apps to be built as external with `firmware_extapps` target

14
fbt
View File

@@ -4,16 +4,20 @@
# unofficial strict mode # unofficial strict mode
set -eu; set -eu;
SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built"; # private variables
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)"; SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)";
SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built";
if [ -z "${FBT_NOENV:-}" ]; then # public variables
echo "info: FBT_NOENV is not set, using included toolchain env"; FBT_NOENV="${FBT_NOENV:-""}";
FBT_NO_SYNC="${FBT_NO_SYNC:-""}";
FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}";
if [ -z "$FBT_NOENV" ]; then
. "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh"; . "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh";
fi fi
if [ -z "${FBT_NO_SYNC:-}" ]; then if [ -z "$FBT_NO_SYNC" ]; then
echo "info: FBT_NO_SYNC is not set, running git submodule update";
if [ ! -d "$SCRIPT_PATH/.git" ]; then if [ ! -d "$SCRIPT_PATH/.git" ]; then
echo "\".git\" directory not found, please clone repo via \"git clone --recursive\""; echo "\".git\" directory not found, please clone repo via \"git clone --recursive\"";
exit 1; exit 1;

View File

@@ -85,6 +85,7 @@ FIRMWARE_APPS = {
], ],
"unit_tests": [ "unit_tests": [
"basic_services", "basic_services",
"updater_app",
"unit_tests", "unit_tests",
], ],
"no_custom_apps": [ "no_custom_apps": [

View File

@@ -9,7 +9,11 @@ from fbt.util import (
# Building initial C environment for libs # Building initial C environment for libs
env = ENV.Clone( env = ENV.Clone(
tools=["compilation_db", "fwbin", "fbt_apps"], tools=[
("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}),
"fwbin",
"fbt_apps",
],
COMPILATIONDB_USE_ABSPATH=False, COMPILATIONDB_USE_ABSPATH=False,
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",
@@ -76,7 +80,6 @@ if not env["VERBOSE"]:
HEXCOMSTR="\tHEX\t${TARGET}", HEXCOMSTR="\tHEX\t${TARGET}",
BINCOMSTR="\tBIN\t${TARGET}", BINCOMSTR="\tBIN\t${TARGET}",
DFUCOMSTR="\tDFU\t${TARGET}", DFUCOMSTR="\tDFU\t${TARGET}",
OPENOCDCOMSTR="\tFLASH\t${SOURCE}",
) )
@@ -139,7 +142,7 @@ apps_c = fwenv.ApplicationsC(
Value(fwenv["APPS"]), Value(fwenv["APPS"]),
) )
# Adding dependency on manifest files so apps.c is rebuilt when any manifest is changed # Adding dependency on manifest files so apps.c is rebuilt when any manifest is changed
fwenv.Depends(apps_c, fwenv.GlobRecursive("*.fam", "applications")) fwenv.Depends(apps_c, fwenv.GlobRecursive("*.fam", "#/applications"))
sources = [apps_c] sources = [apps_c]
# Gather sources only from app folders in current configuration # Gather sources only from app folders in current configuration
@@ -164,6 +167,8 @@ fwenv.AppendUnique(
"-u", "-u",
"_printf_float", "_printf_float",
"-n", "-n",
"-Xlinker",
"-Map=${TARGET}.map",
], ],
) )
@@ -202,7 +207,6 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program(
], ],
) )
# Make it depend on everything child builders returned
# Firmware depends on everything child builders returned # Firmware depends on everything child builders returned
Depends(fwelf, lib_targets) Depends(fwelf, lib_targets)
# Output extra details after building firmware # Output extra details after building firmware
@@ -232,7 +236,8 @@ if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS):
fwcdb = fwenv.CompilationDatabase() fwcdb = fwenv.CompilationDatabase()
# without filtering, both updater & firmware commands would be generated # without filtering, both updater & firmware commands would be generated
fwenv.Replace(COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*")) fwenv.Replace(COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*"))
Depends(fwcdb, fwelf) AlwaysBuild(fwcdb)
Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_cdb", fwcdb)
fw_artifacts.append(fwcdb) fw_artifacts.append(fwcdb)
# Adding as a phony target, so folder link is updated even if elf didn't change # Adding as a phony target, so folder link is updated even if elf didn't change

View File

@@ -75,6 +75,7 @@ SECTIONS
.text : .text :
{ {
. = ALIGN(4); . = ALIGN(4);
*lib*.a:*(.text .text.*) /* code from libraries before apps */
*(.text) /* .text sections (code) */ *(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */ *(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */ *(.glue_7) /* glue arm to thumb code */

View File

@@ -4,8 +4,6 @@ env.Append(
CPPPATH=[ CPPPATH=[
"#/lib/loclass", "#/lib/loclass",
], ],
CPPDEFINES=[
],
) )

View File

@@ -5,8 +5,6 @@ env.Append(
"#/lib/mbedtls", "#/lib/mbedtls",
"#/lib/mbedtls/include", "#/lib/mbedtls/include",
], ],
CPPDEFINES=[
],
) )

View File

@@ -1,5 +1,6 @@
#include "buffered_file_stream.h" #include "buffered_file_stream.h"
#include "core/check.h"
#include "stream_i.h" #include "stream_i.h"
#include "file_stream.h" #include "file_stream.h"
#include "stream_cache.h" #include "stream_cache.h"
@@ -38,6 +39,8 @@ const StreamVTable buffered_file_stream_vtable = {
.delete_and_insert = (StreamDeleteAndInsertFn)buffered_file_stream_delete_and_insert, .delete_and_insert = (StreamDeleteAndInsertFn)buffered_file_stream_delete_and_insert,
}; };
static bool buffered_file_stream_unread(BufferedFileStream* stream);
Stream* buffered_file_stream_alloc(Storage* storage) { Stream* buffered_file_stream_alloc(Storage* storage) {
BufferedFileStream* stream = malloc(sizeof(BufferedFileStream)); BufferedFileStream* stream = malloc(sizeof(BufferedFileStream));
@@ -125,8 +128,12 @@ static size_t buffered_file_stream_size(BufferedFileStream* stream) {
static size_t static size_t
buffered_file_stream_write(BufferedFileStream* stream, const uint8_t* data, size_t size) { buffered_file_stream_write(BufferedFileStream* stream, const uint8_t* data, size_t size) {
stream_cache_drop(stream->cache); size_t need_to_write = size;
return stream_write(stream->file_stream, data, size); do {
if(!buffered_file_stream_unread(stream)) break;
need_to_write -= stream_write(stream->file_stream, data, size);
} while(false);
return size - need_to_write;
} }
static size_t buffered_file_stream_read(BufferedFileStream* stream, uint8_t* data, size_t size) { static size_t buffered_file_stream_read(BufferedFileStream* stream, uint8_t* data, size_t size) {
@@ -150,6 +157,19 @@ static bool buffered_file_stream_delete_and_insert(
size_t delete_size, size_t delete_size,
StreamWriteCB write_callback, StreamWriteCB write_callback,
const void* ctx) { const void* ctx) {
stream_cache_drop(stream->cache); return buffered_file_stream_unread(stream) &&
return stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx); stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx);
}
// Drop read cache and adjust the underlying stream seek position
static bool buffered_file_stream_unread(BufferedFileStream* stream) {
bool success = true;
const size_t cache_size = stream_cache_size(stream->cache);
const size_t cache_pos = stream_cache_pos(stream->cache);
if(cache_pos < cache_size) {
const int32_t offset = cache_size - cache_pos;
success = stream_seek(stream->file_stream, -offset, StreamOffsetFromCurrent);
}
stream_cache_drop(stream->cache);
return success;
} }

View File

@@ -53,13 +53,14 @@ class FlipperStorage:
CLI_PROMPT = ">: " CLI_PROMPT = ">: "
CLI_EOL = "\r\n" CLI_EOL = "\r\n"
def __init__(self, portname: str, portbaud: int = 115200): def __init__(self, portname: str, chunk_size: int = 8192):
self.port = serial.Serial() self.port = serial.Serial()
self.port.port = portname self.port.port = portname
self.port.timeout = 2 self.port.timeout = 2
self.port.baudrate = portbaud self.port.baudrate = 115200 # Doesn't matter for VCP
self.read = BufferedRead(self.port) self.read = BufferedRead(self.port)
self.last_error = "" self.last_error = ""
self.chunk_size = chunk_size
def start(self): def start(self):
self.port.open() self.port.open()
@@ -192,7 +193,7 @@ class FlipperStorage:
with open(filename_from, "rb") as file: with open(filename_from, "rb") as file:
filesize = os.fstat(file.fileno()).st_size filesize = os.fstat(file.fileno()).st_size
buffer_size = 512 buffer_size = self.chunk_size
while True: while True:
filedata = file.read(buffer_size) filedata = file.read(buffer_size)
size = len(filedata) size = len(filedata)
@@ -221,7 +222,7 @@ class FlipperStorage:
def read_file(self, filename): def read_file(self, filename):
"""Receive file from Flipper, and get filedata (bytes)""" """Receive file from Flipper, and get filedata (bytes)"""
buffer_size = 512 buffer_size = self.chunk_size
self.send_and_wait_eol( self.send_and_wait_eol(
'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r" 'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r"
) )
@@ -355,7 +356,7 @@ class FlipperStorage:
"""Hash of local file""" """Hash of local file"""
hash_md5 = hashlib.md5() hash_md5 = hashlib.md5()
with open(filename, "rb") as f: with open(filename, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""): for chunk in iter(lambda: f.read(self.chunk_size), b""):
hash_md5.update(chunk) hash_md5.update(chunk)
return hash_md5.hexdigest() return hash_md5.hexdigest()

View File

@@ -14,14 +14,6 @@ import serial.tools.list_ports as list_ports
class Main(App): class Main(App):
def init(self): def init(self):
self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") self.parser.add_argument("-p", "--port", help="CDC Port", default="auto")
self.parser.add_argument(
"-b",
"--baud",
help="Port Baud rate",
required=False,
default=115200 * 4,
type=int,
)
self.parser.add_argument("manifest_path", help="Manifest path") self.parser.add_argument("manifest_path", help="Manifest path")
self.parser.add_argument( self.parser.add_argument(
@@ -64,7 +56,7 @@ class Main(App):
if not (port := resolve_port(self.logger, self.args.port)): if not (port := resolve_port(self.logger, self.args.port)):
return 1 return 1
storage = FlipperStorage(port, self.args.baud) storage = FlipperStorage(port)
storage.start() storage.start()
try: try:
@@ -99,6 +91,7 @@ class Main(App):
self.logger.error(f"Error: {storage.last_error}") self.logger.error(f"Error: {storage.last_error}")
return -3 return -3
# return -11
storage.send_and_wait_eol( storage.send_and_wait_eol(
f"update install {flipper_update_path}/{manifest_name}\r" f"update install {flipper_update_path}/{manifest_name}\r"
) )

14
scripts/serial_cli.py Normal file
View File

@@ -0,0 +1,14 @@
import logging
import subprocess
from flipper.utils.cdc import resolve_port
def main():
logger = logging.getLogger()
if not (port := resolve_port(logger, "auto")):
return 1
subprocess.call(["python3", "-m", "serial.tools.miniterm", "--raw", port, "230400"])
if __name__ == "__main__":
main()

View File

@@ -14,14 +14,7 @@ import tempfile
class Main(App): class Main(App):
def init(self): def init(self):
self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") self.parser.add_argument("-p", "--port", help="CDC Port", default="auto")
self.parser.add_argument(
"-b",
"--baud",
help="Port Baud rate",
required=False,
default=115200 * 4,
type=int,
)
self.subparsers = self.parser.add_subparsers(help="sub-command help") self.subparsers = self.parser.add_subparsers(help="sub-command help")
self.parser_mkdir = self.subparsers.add_parser("mkdir", help="Create directory") self.parser_mkdir = self.subparsers.add_parser("mkdir", help="Create directory")
@@ -77,7 +70,7 @@ class Main(App):
if not (port := resolve_port(self.logger, self.args.port)): if not (port := resolve_port(self.logger, self.args.port)):
return None return None
storage = FlipperStorage(port, self.args.baud) storage = FlipperStorage(port)
storage.start() storage.start()
return storage return storage

View File

@@ -13,7 +13,7 @@ if not [%FBT_NOENV%] == [] (
exit /b 0 exit /b 0
) )
set "FLIPPER_TOOLCHAIN_VERSION=3" set "FLIPPER_TOOLCHAIN_VERSION=8"
set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows" set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows"

View File

@@ -1,54 +1,211 @@
#!/bin/sh #!/bin/sh
# unofficial strict mode # shellcheck disable=SC2034,SC2016,SC2086
set -eu;
FLIPPER_TOOLCHAIN_VERSION="3"; # public variables
DEFAULT_SCRIPT_PATH="$(pwd -P)";
SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}";
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"8"}";
FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}";
get_kernel_type() fbtenv_check_sourced()
{ {
SYS_TYPE="$(uname -s)" case "${ZSH_EVAL_CONTEXT:-""}" in *:file:*)
return 0;;
esac
case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh)
return 0;;
esac
if [ "$(basename $0)" = "fbt" ]; then
return 0;
fi
echo "Running this script manually is wrong, please source it";
echo "Example:";
printf "\tsource scripts/toolchain/fbtenv.sh\n";
return 1;
}
fbtenv_check_script_path()
{
if [ ! -x "$SCRIPT_PATH/fbt" ]; then
echo "Please source this script being into flipperzero-firmware root directory, or specify 'SCRIPT_PATH' manually";
echo "Example:";
printf "\tSCRIPT_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n";
echo "If current directory is right, type 'unset SCRIPT_PATH' and try again"
return 1;
fi
return 0;
}
fbtenv_get_kernel_type()
{
SYS_TYPE="$(uname -s)";
ARCH_TYPE="$(uname -m)";
if [ "$ARCH_TYPE" != "x86_64" ] && [ "$SYS_TYPE" != "Darwin" ]; then
echo "Now we provide toolchain only for x86_64 arhitecture, sorry..";
return 1;
fi
if [ "$SYS_TYPE" = "Darwin" ]; then if [ "$SYS_TYPE" = "Darwin" ]; then
TOOLCHAIN_PATH="toolchain/x86_64-darwin"; fbtenv_check_rosetta || return 1;
TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-darwin";
TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-darwin-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz";
elif [ "$SYS_TYPE" = "Linux" ]; then elif [ "$SYS_TYPE" = "Linux" ]; then
TOOLCHAIN_PATH="toolchain/x86_64-linux"; TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-linux";
TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz";
elif echo "$SYS_TYPE" | grep -q "MINGW"; then elif echo "$SYS_TYPE" | grep -q "MINGW"; then
echo "In MinGW shell use \"fbt.cmd\" instead of \"fbt\""; echo "In MinGW shell use \"fbt.cmd\" instead of \"fbt\"";
exit 1; return 1;
else else
echo "Sorry, your system is not supported. Please report your configuration to us."; echo "Your system is not recognized. Sorry.. Please report us your configuration.";
exit 1; return 1;
fi fi
return 0;
} }
check_download_toolchain() fbtenv_check_rosetta()
{ {
if [ ! -d "$SCRIPT_PATH/$TOOLCHAIN_PATH" ]; then if [ "$ARCH_TYPE" = "arm64" ]; then
download_toolchain; if ! /usr/bin/pgrep -q oahd; then
elif [ ! -f "$SCRIPT_PATH/$TOOLCHAIN_PATH/VERSION" ]; then echo "Flipper Zero Toolchain needs Rosetta2 to run under Apple Silicon";
download_toolchain; echo "Please instal it by typing 'softwareupdate --install-rosetta --agree-to-license'";
elif [ "$(cat "$SCRIPT_PATH/$TOOLCHAIN_PATH/VERSION")" -ne "$FLIPPER_TOOLCHAIN_VERSION" ]; then return 1;
download_toolchain;
fi fi
fi
return 0;
} }
download_toolchain() fbtenv_check_tar()
{ {
chmod 755 "$SCRIPT_PATH/scripts/toolchain/unix-toolchain-download.sh"; printf "Checking tar..";
"$SCRIPT_PATH/scripts/toolchain/unix-toolchain-download.sh" "$FLIPPER_TOOLCHAIN_VERSION" || exit 1; if ! tar --version > /dev/null 2>&1; then
echo "no";
return 1;
fi
echo "yes";
return 0;
} }
main() fbtenv_check_downloaded_toolchain()
{ {
if [ -z "${SCRIPT_PATH:-}" ]; then printf "Checking downloaded toolchain tgz..";
echo "Manual running of this script is not allowed."; if [ ! -f "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" ]; then
exit 1; echo "no";
return 1;
fi fi
get_kernel_type; # sets TOOLCHAIN_PATH echo "yes";
check_download_toolchain; return 0;
PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/python/bin:$PATH";
PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/bin:$PATH";
PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/protobuf/bin:$PATH";
PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/openocd/bin:$PATH";
} }
main;
fbtenv_download_toolchain_tar()
{
echo "Downloading toolchain:";
mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1;
"$DOWNLOADER" $DOWNLOADER_ARGS "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" "$TOOLCHAIN_URL" || return 1;
echo "done";
return 0;
}
fbtenv_remove_old_tooclhain()
{
printf "Removing old toolchain (if exist)..";
rm -rf "${TOOLCHAIN_ARCH_DIR}";
echo "done";
}
fbtenv_show_unpack_percentage()
{
LINE=0;
while read -r line; do
LINE=$(( LINE + 1 ));
if [ $(( LINE % 300 )) -eq 0 ]; then
printf "#";
fi
done
echo " 100.0%";
}
fbtenv_unpack_toolchain()
{
echo "Unpacking toolchain:";
tar -xvf "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" -C "$FBT_TOOLCHAIN_PATH/toolchain" 2>&1 | fbtenv_show_unpack_percentage;
mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1;
mv "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_DIR" "$TOOLCHAIN_ARCH_DIR" || return 1;
echo "done";
return 0;
}
fbtenv_clearing()
{
printf "Clearing..";
rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/$TOOLCHAIN_TAR";
echo "done";
return 0;
}
fbtenv_curl_wget_check()
{
printf "Checking curl..";
if ! curl --version > /dev/null 2>&1; then
echo "no";
printf "Checking wget..";
if ! wget --version > /dev/null 2>&1; then
echo "no";
echo "No curl or wget found in your PATH";
echo "Please provide it or download this file:";
echo;
echo "$TOOLCHAIN_URL";
echo;
echo "And place in $FBT_TOOLCHAIN_PATH/toolchain/ dir mannualy";
return 1;
fi
echo "yes"
DOWNLOADER="wget";
DOWNLOADER_ARGS="--show-progress --progress=bar:force -qO";
return 0;
fi
echo "yes"
DOWNLOADER="curl";
DOWNLOADER_ARGS="--progress-bar -SLo";
return 0;
}
fbtenv_check_download_toolchain()
{
if [ ! -d "$TOOLCHAIN_ARCH_DIR" ]; then
fbtenv_download_toolchain || return 1;
elif [ ! -f "$TOOLCHAIN_ARCH_DIR/VERSION" ]; then
fbtenv_download_toolchain || return 1;
elif [ "$(cat "$TOOLCHAIN_ARCH_DIR/VERSION")" -ne "$FBT_TOOLCHAIN_VERSION" ]; then
fbtenv_download_toolchain || return 1;
fi
return 0;
}
fbtenv_download_toolchain()
{
fbtenv_check_tar || return 1;
TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")";
TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$FBT_TOOLCHAIN_VERSION.tar.gz//g")";
if ! fbtenv_check_downloaded_toolchain; then
fbtenv_curl_wget_check || return 1;
fbtenv_download_toolchain_tar;
fi
fbtenv_remove_old_tooclhain;
fbtenv_unpack_toolchain || { fbtenv_clearing && return 1; };
fbtenv_clearing;
return 0;
}
fbtenv_main()
{
fbtenv_check_sourced || return 1;
fbtenv_check_script_path || return 1;
fbtenv_get_kernel_type || return 1;
fbtenv_check_download_toolchain || return 1;
PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH";
PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH";
PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH";
PATH="$TOOLCHAIN_ARCH_DIR/openocd/bin:$PATH";
}
fbtenv_main;

View File

@@ -1,145 +0,0 @@
#!/bin/sh
# shellcheck disable=SC2086,SC2034
# unofficial strict mode
set -eu;
check_system()
{
VER="$1"; # toolchain version
printf "Checking kernel type..";
SYS_TYPE="$(uname -s)"
if [ "$SYS_TYPE" = "Darwin" ]; then
echo "darwin";
if [ -z "${FBT_TOOLS_CUSTOM_LINK:-}" ]; then
TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-darwin-flipper-$VER.tar.gz";
else
echo "info: custom toolchain link is used";
TOOLCHAIN_URL=$FBT_TOOLS_CUSTOM_LINK;
fi
TOOLCHAIN_PATH="toolchain/x86_64-darwin";
elif [ "$SYS_TYPE" = "Linux" ]; then
echo "linux";
if [ -z "${FBT_TOOLS_CUSTOM_LINK:-}" ]; then
TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$VER.tar.gz";
else
echo "info: custom toolchain link is used";
TOOLCHAIN_URL=$FBT_TOOLS_CUSTOM_LINK;
fi
TOOLCHAIN_PATH="toolchain/x86_64-linux";
else
echo "unsupported.";
echo "Your system is unsupported.. sorry..";
exit 1;
fi
}
check_tar()
{
printf "Checking tar..";
if ! tar --version > /dev/null 2>&1; then
echo "no";
exit 1;
fi
echo "yes";
}
curl_wget_check()
{
printf "Checking curl..";
if ! curl --version > /dev/null 2>&1; then
echo "no";
printf "Checking wget..";
if ! wget --version > /dev/null 2>&1; then
echo "no";
echo "No curl or wget found in your PATH.";
echo "Please provide it or download this file:";
echo;
echo "$TOOLCHAIN_URL";
echo;
echo "And place in repo root dir mannualy.";
exit 1;
fi
echo "yes"
DOWNLOADER="wget";
DOWNLOADER_ARGS="--show-progress --progress=bar:force -qO";
return;
fi
echo "yes"
DOWNLOADER="curl";
DOWNLOADER_ARGS="--progress-bar -SLo";
}
check_downloaded_toolchain()
{
printf "Checking downloaded toolchain tgz..";
if [ -f "$REPO_ROOT/$TOOLCHAIN_TAR" ]; then
echo "yes";
return 0;
fi
echo "no";
return 1;
}
download_toolchain()
{
echo "Downloading toolchain:";
"$DOWNLOADER" $DOWNLOADER_ARGS "$REPO_ROOT/$TOOLCHAIN_TAR" "$TOOLCHAIN_URL";
echo "done";
}
remove_old_tooclhain()
{
printf "Removing old toolchain (if exist)..";
rm -rf "${REPO_ROOT:?}/$TOOLCHAIN_PATH";
echo "done";
}
show_unpack_percentage()
{
LINE=0;
while read -r line; do
LINE=$(( LINE + 1 ));
if [ $(( LINE % 300 )) -eq 0 ]; then
printf "#";
fi
done
echo " 100.0%";
}
unpack_toolchain()
{
echo "Unpacking toolchain:";
tar -xvf "$REPO_ROOT/$TOOLCHAIN_TAR" -C "$REPO_ROOT/" 2>&1 | show_unpack_percentage;
mkdir -p "$REPO_ROOT/toolchain";
mv "$REPO_ROOT/$TOOLCHAIN_DIR" "$REPO_ROOT/$TOOLCHAIN_PATH/";
echo "done";
}
clearing()
{
printf "Clearing..";
rm -rf "${REPO_ROOT:?}/$TOOLCHAIN_TAR";
echo "done";
}
main()
{
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)"
REPO_ROOT="$(cd "$SCRIPT_PATH/../../" && pwd)";
check_system "$1"; # recives TOOLCHAIN_VERSION, defines TOOLCHAIN_URL and TOOLCHAIN_PATH
check_tar;
TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")";
TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$VER.tar.gz//g")";
if ! check_downloaded_toolchain; then
curl_wget_check;
download_toolchain;
fi
remove_old_tooclhain;
unpack_toolchain;
}
trap clearing EXIT;
trap clearing 2; # SIGINT not coverable by EXIT
main "$1"; # toochain version

View File

@@ -2,6 +2,7 @@ from SCons.Builder import Builder
from SCons.Action import Action from SCons.Action import Action
from SCons.Warnings import warn, WarningOnByDefault from SCons.Warnings import warn, WarningOnByDefault
import SCons import SCons
import os.path
from fbt.appmanifest import ( from fbt.appmanifest import (
FlipperAppType, FlipperAppType,
@@ -17,10 +18,12 @@ from fbt.appmanifest import (
def LoadApplicationManifests(env): def LoadApplicationManifests(env):
appmgr = env["APPMGR"] = AppManager() appmgr = env["APPMGR"] = AppManager()
for entry in env.Glob("#/applications/*", source=True): for entry in env.Glob("#/applications/*", ondisk=True, source=True):
if isinstance(entry, SCons.Node.FS.Dir) and not str(entry).startswith("."): if isinstance(entry, SCons.Node.FS.Dir) and not str(entry).startswith("."):
try: try:
appmgr.load_manifest(entry.File("application.fam").abspath, entry.name) app_manifest_file_path = os.path.join(entry.abspath, "application.fam")
appmgr.load_manifest(app_manifest_file_path, entry.name)
env.Append(PY_LINT_SOURCES=[app_manifest_file_path])
except FlipperManifestException as e: except FlipperManifestException as e:
warn(WarningOnByDefault, str(e)) warn(WarningOnByDefault, str(e))
@@ -64,6 +67,7 @@ def generate(env):
build_apps_c, build_apps_c,
"${APPSCOMSTR}", "${APPSCOMSTR}",
), ),
suffix=".c",
), ),
} }
) )

View File

@@ -66,9 +66,38 @@ def AddOpenOCDFlashTarget(env, targetenv, **kw):
**kw, **kw,
) )
env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target) env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target)
if env["FORCE"]:
env.AlwaysBuild(openocd_target)
return openocd_target return openocd_target
def AddJFlashTarget(env, targetenv, **kw):
jflash_target = env.JFlash(
"#build/jflash-${BUILD_CFG}-flash.flag",
targetenv["FW_BIN"],
JFLASHADDR=targetenv.subst("$IMAGE_BASE_ADDRESS"),
BUILD_CFG=targetenv.subst("${FIRMWARE_BUILD_CFG}"),
**kw,
)
env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_jflash"), jflash_target)
if env["FORCE"]:
env.AlwaysBuild(jflash_target)
return jflash_target
def AddUsbFlashTarget(env, file_flag, extra_deps, **kw):
usb_update = env.UsbInstall(
file_flag,
(
env["DIST_DEPENDS"],
*extra_deps,
),
)
if env["FORCE"]:
env.AlwaysBuild(usb_update)
return usb_update
def DistCommand(env, name, source, **kw): def DistCommand(env, name, source, **kw):
target = f"dist_{name}" target = f"dist_{name}"
command = env.Command( command = env.Command(
@@ -86,6 +115,8 @@ def generate(env):
env.AddMethod(AddFwProject) env.AddMethod(AddFwProject)
env.AddMethod(DistCommand) env.AddMethod(DistCommand)
env.AddMethod(AddOpenOCDFlashTarget) env.AddMethod(AddOpenOCDFlashTarget)
env.AddMethod(AddJFlashTarget)
env.AddMethod(AddUsbFlashTarget)
env.SetDefault( env.SetDefault(
COPRO_MCU_FAMILY="STM32WB5x", COPRO_MCU_FAMILY="STM32WB5x",

View File

@@ -0,0 +1,27 @@
from SCons.Builder import Builder
from SCons.Defaults import Touch
def generate(env):
env.SetDefault(
JFLASH="JFlash" if env.subst("$PLATFORM") == "win32" else "JFlashExe",
JFLASHFLAGS=[
"-auto",
"-exit",
],
JFLASHCOM="${JFLASH} -openprj${JFLASHPROJECT} -open${SOURCE},${JFLASHADDR} ${JFLASHFLAGS}",
)
env.Append(
BUILDERS={
"JFlash": Builder(
action=[
"${JFLASHCOM}",
Touch("${TARGET}"),
],
),
}
)
def exists(env):
return True

View File

@@ -1,5 +1,6 @@
import posixpath import posixpath
import os import os
from SCons.Errors import UserError
def BuildModule(env, module): def BuildModule(env, module):
@@ -8,9 +9,9 @@ def BuildModule(env, module):
if not os.path.exists(module_sconscript): if not os.path.exists(module_sconscript):
module_sconscript = posixpath.join(src_dir, f"{module}.scons") module_sconscript = posixpath.join(src_dir, f"{module}.scons")
if not os.path.exists(module_sconscript): if not os.path.exists(module_sconscript):
print(f"Cannot build module {module}: scons file not found") raise UserError(f"Cannot build module {module}: scons file not found")
Exit(2)
env.Append(PY_LINT_SOURCES=[module_sconscript])
return env.SConscript( return env.SConscript(
module_sconscript, module_sconscript,
variant_dir=posixpath.join(env.subst("$BUILD_DIR"), module), variant_dir=posixpath.join(env.subst("$BUILD_DIR"), module),