Merge branch 'fz-dev' into dev
107
SConstruct
|
@ -7,6 +7,7 @@
|
|||
# construction of certain targets behind command-line options.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
EnsurePythonVersion(3, 8)
|
||||
|
||||
|
@ -33,8 +34,10 @@ coreenv["ROOT_DIR"] = Dir(".")
|
|||
|
||||
# Create a separate "dist" environment and add construction envs to it
|
||||
distenv = coreenv.Clone(
|
||||
tools=["fbt_dist", "openocd", "blackmagic"],
|
||||
OPENOCD_GDB_PIPE=["|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"],
|
||||
tools=["fbt_dist", "openocd", "blackmagic", "jflash"],
|
||||
OPENOCD_GDB_PIPE=[
|
||||
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
||||
],
|
||||
GDBOPTS_BASE=[
|
||||
"-ex",
|
||||
"target extended-remote ${GDBREMOTE}",
|
||||
|
@ -61,6 +64,7 @@ distenv = coreenv.Clone(
|
|||
"-ex",
|
||||
"compare-sections",
|
||||
],
|
||||
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
|
||||
ENV=os.environ,
|
||||
)
|
||||
|
||||
|
@ -71,7 +75,9 @@ firmware_env = distenv.AddFwProject(
|
|||
)
|
||||
|
||||
# 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(
|
||||
base_env=coreenv,
|
||||
fw_type="updater",
|
||||
|
@ -79,11 +85,11 @@ if GetOption("fullenv"):
|
|||
)
|
||||
|
||||
# Target for self-update package
|
||||
dist_arguments = [
|
||||
"-r",
|
||||
'"${ROOT_DIR.abspath}/assets/resources"',
|
||||
dist_basic_arguments = [
|
||||
"--bundlever",
|
||||
'"${UPDATE_VERSION_STRING}"',
|
||||
]
|
||||
dist_radio_arguments = [
|
||||
"--radio",
|
||||
'"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"',
|
||||
"--radiotype",
|
||||
|
@ -92,16 +98,34 @@ if GetOption("fullenv"):
|
|||
"--obdata",
|
||||
'"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"',
|
||||
]
|
||||
if distenv["UPDATE_SPLASH"]:
|
||||
dist_arguments += [
|
||||
dist_resource_arguments = [
|
||||
"-r",
|
||||
'"${ROOT_DIR.abspath}/assets/resources"',
|
||||
]
|
||||
dist_splash_arguments = (
|
||||
[
|
||||
"--splash",
|
||||
distenv.subst("assets/slideshow/$UPDATE_SPLASH"),
|
||||
]
|
||||
if distenv["UPDATE_SPLASH"]
|
||||
else []
|
||||
)
|
||||
|
||||
selfupdate_dist = distenv.DistCommand(
|
||||
"updater_package",
|
||||
(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
|
||||
|
@ -121,18 +145,16 @@ if GetOption("fullenv"):
|
|||
)
|
||||
|
||||
# Installation over USB & CLI
|
||||
usb_update_package = distenv.UsbInstall(
|
||||
"#build/usbinstall.flag",
|
||||
(
|
||||
distenv["DIST_DEPENDS"],
|
||||
firmware_env["FW_RESOURCES"],
|
||||
selfupdate_dist,
|
||||
),
|
||||
usb_update_package = distenv.AddUsbFlashTarget(
|
||||
"#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist)
|
||||
)
|
||||
if distenv["FORCE"]:
|
||||
distenv.AlwaysBuild(usb_update_package)
|
||||
distenv.Depends(usb_update_package, selfupdate_dist)
|
||||
distenv.Alias("flash_usb", usb_update_package)
|
||||
distenv.Alias("flash_usb_full", usb_update_package)
|
||||
|
||||
usb_minupdate_package = distenv.AddUsbFlashTarget(
|
||||
"#build/minusbinstall.flag", (selfupdate_min_dist,)
|
||||
)
|
||||
distenv.Alias("flash_usb", usb_minupdate_package)
|
||||
|
||||
|
||||
# Target for copying & renaming binaries to dist folder
|
||||
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)
|
||||
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(
|
||||
"flash_blackmagic",
|
||||
|
@ -209,6 +232,46 @@ distenv.PhonyTarget(
|
|||
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
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ appenv.AppendUnique(
|
|||
"-Wl,--no-export-dynamic",
|
||||
"-fvisibility=hidden",
|
||||
"-Wl,-e${APP_ENTRY}",
|
||||
"-Xlinker",
|
||||
"-Map=${TARGET}.map",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "m-string.h"
|
||||
#include <toolbox/path.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include "rpc/rpc_app.h"
|
||||
#include <rpc/rpc_app.h>
|
||||
|
||||
#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);
|
||||
bool result = false;
|
||||
string_t data;
|
||||
|
@ -99,33 +99,20 @@ static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show
|
|||
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);
|
||||
iButton* ibutton = context;
|
||||
|
||||
bool result = false;
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL);
|
||||
ibutton->rpc_ctx = NULL;
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
|
||||
result = true;
|
||||
view_dispatcher_send_custom_event(
|
||||
ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose);
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
|
||||
result = true;
|
||||
} else if(event == RpcAppEventLoadFile) {
|
||||
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);
|
||||
view_dispatcher_send_custom_event(
|
||||
ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
|
||||
} else {
|
||||
rpc_system_app_confirm(ibutton->rpc_ctx, event, false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ibutton_custom_event_callback(void* context, uint32_t event) {
|
||||
|
|
|
@ -12,4 +12,5 @@ enum iButtonCustomEvent {
|
|||
|
||||
iButtonCustomEventRpcLoad,
|
||||
iButtonCustomEventRpcExit,
|
||||
iButtonCustomEventRpcSessionClose,
|
||||
};
|
||||
|
|
|
@ -78,6 +78,7 @@ typedef enum {
|
|||
} iButtonNotificationMessage;
|
||||
|
||||
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_delete_key(iButton* ibutton);
|
||||
void ibutton_text_store_set(iButton* ibutton, const char* text, ...);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "../ibutton_i.h"
|
||||
#include <toolbox/path.h>
|
||||
#include <rpc/rpc_app.h>
|
||||
|
||||
void ibutton_scene_rpc_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
|
@ -26,23 +27,40 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == iButtonCustomEventRpcLoad) {
|
||||
string_t key_name;
|
||||
string_init(key_name);
|
||||
if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
|
||||
path_extract_filename(ibutton->file_path, key_name, true);
|
||||
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_init(key_name);
|
||||
if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
|
||||
path_extract_filename(ibutton->file_path, key_name, true);
|
||||
}
|
||||
|
||||
if(!string_empty_p(key_name)) {
|
||||
ibutton_text_store_set(
|
||||
ibutton, "emulating\n%s", string_get_cstr(key_name));
|
||||
} else {
|
||||
ibutton_text_store_set(ibutton, "emulating");
|
||||
}
|
||||
popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
|
||||
|
||||
string_clear(key_name);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!string_empty_p(key_name)) {
|
||||
ibutton_text_store_set(ibutton, "emulating\n%s", string_get_cstr(key_name));
|
||||
} else {
|
||||
ibutton_text_store_set(ibutton, "emulating");
|
||||
}
|
||||
popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
|
||||
|
||||
string_clear(key_name);
|
||||
rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
} 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);
|
||||
view_dispatcher_stop(ibutton->view_dispatcher);
|
||||
}
|
||||
|
|
|
@ -36,52 +36,29 @@ static void infrared_tick_event_callback(void* context) {
|
|||
scene_manager_handle_tick_event(infrared->scene_manager);
|
||||
}
|
||||
|
||||
static bool
|
||||
infrared_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) {
|
||||
static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
|
||||
if(!infrared->rpc_ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
furi_assert(infrared->rpc_ctx);
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);
|
||||
infrared->rpc_ctx = NULL;
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeBackPressed);
|
||||
result = true;
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose);
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeBackPressed);
|
||||
result = true;
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcExit);
|
||||
} 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(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoaded);
|
||||
}
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad);
|
||||
} else if(event == RpcAppEventButtonPress) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress);
|
||||
} else if(event == RpcAppEventButtonRelease) {
|
||||
infrared_tx_stop(infrared);
|
||||
result = true;
|
||||
view_dispatcher_send_custom_event(
|
||||
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) {
|
||||
|
|
|
@ -14,7 +14,12 @@ enum InfraredCustomEventType {
|
|||
InfraredCustomEventTypePopupClosed,
|
||||
InfraredCustomEventTypeButtonSelected,
|
||||
InfraredCustomEventTypeBackPressed,
|
||||
InfraredCustomEventTypeRpcLoaded,
|
||||
|
||||
InfraredCustomEventTypeRpcLoad,
|
||||
InfraredCustomEventTypeRpcExit,
|
||||
InfraredCustomEventTypeRpcButtonPress,
|
||||
InfraredCustomEventTypeRpcButtonRelease,
|
||||
InfraredCustomEventTypeRpcSessionClose,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
|
|
@ -28,12 +28,45 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
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);
|
||||
|
||||
infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name);
|
||||
popup_set_text(
|
||||
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;
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <toolbox/path.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_extension = ".rfid";
|
||||
|
@ -48,38 +48,25 @@ 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);
|
||||
LfRfidApp* app = static_cast<LfRfidApp*>(context);
|
||||
|
||||
bool result = false;
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL);
|
||||
app->rpc_ctx = NULL;
|
||||
if(rpc_event == RpcAppEventSessionClose) {
|
||||
LfRfidApp::Event event;
|
||||
event.type = LfRfidApp::EventType::RpcSessionClose;
|
||||
app->view_controller.send_event(&event);
|
||||
} else if(rpc_event == RpcAppEventAppExit) {
|
||||
LfRfidApp::Event event;
|
||||
event.type = LfRfidApp::EventType::Exit;
|
||||
app->view_controller.send_event(&event);
|
||||
result = true;
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
} else if(rpc_event == RpcAppEventLoadFile) {
|
||||
LfRfidApp::Event event;
|
||||
event.type = LfRfidApp::EventType::Exit;
|
||||
event.type = LfRfidApp::EventType::RpcLoadFile;
|
||||
app->view_controller.send_event(&event);
|
||||
result = true;
|
||||
} else if(event == RpcAppEventLoadFile) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rpc_system_app_confirm(app->rpc_ctx, rpc_event, false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void LfRfidApp::run(void* _args) {
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
Retry,
|
||||
Exit,
|
||||
EmulateStart,
|
||||
RpcLoadFile,
|
||||
RpcSessionClose,
|
||||
};
|
||||
|
||||
enum class SceneType : uint8_t {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "lfrfid_app_scene_rpc.h"
|
||||
#include <core/common_defines.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <rpc/rpc_app.h>
|
||||
|
||||
static const NotificationSequence sequence_blink_start_magenta = {
|
||||
&message_blink_start_10,
|
||||
|
@ -36,6 +37,16 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
|||
LfRfidApp::Event view_event;
|
||||
view_event.type = LfRfidApp::EventType::Back;
|
||||
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) {
|
||||
auto popup = app->view_controller.get<PopupVM>();
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,4 +11,5 @@ enum NfcCustomEvent {
|
|||
NfcCustomEventDictAttackDone,
|
||||
NfcCustomEventDictAttackSkip,
|
||||
NfcCustomEventRpcLoad,
|
||||
NfcCustomEventRpcSessionClose,
|
||||
};
|
||||
|
|
|
@ -13,78 +13,21 @@ bool nfc_back_event_callback(void* context) {
|
|||
return scene_manager_handle_back_event(nfc->scene_manager);
|
||||
}
|
||||
|
||||
void nfc_rpc_exit_callback(Nfc* 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_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) {
|
||||
static void nfc_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
|
||||
if(!nfc->rpc_ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
furi_assert(nfc->rpc_ctx);
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL);
|
||||
nfc->rpc_ctx = NULL;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
result = true;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose);
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
result = true;
|
||||
} 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);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad);
|
||||
} else {
|
||||
rpc_system_app_confirm(nfc->rpc_ctx, event, false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Nfc* nfc_alloc() {
|
||||
|
@ -163,6 +106,21 @@ Nfc* nfc_alloc() {
|
|||
void nfc_free(Nfc* 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_free(nfc->dev);
|
||||
|
||||
|
|
|
@ -102,5 +102,3 @@ void nfc_blink_start(Nfc* nfc);
|
|||
void nfc_blink_stop(Nfc* nfc);
|
||||
|
||||
void nfc_show_loading_popup(void* context, bool show);
|
||||
|
||||
void nfc_rpc_exit_callback(Nfc* nfc);
|
||||
|
|
|
@ -14,6 +14,14 @@ void nfc_scene_rpc_on_enter(void* context) {
|
|||
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) {
|
||||
Nfc* nfc = context;
|
||||
Popup* popup = nfc->popup;
|
||||
|
@ -22,13 +30,47 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
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);
|
||||
nfc_blink_stop(nfc);
|
||||
} 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_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name);
|
||||
popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop);
|
||||
nfc_blink_start(nfc);
|
||||
nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name);
|
||||
popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop);
|
||||
}
|
||||
}
|
||||
|
||||
rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
@ -38,7 +80,6 @@ void nfc_scene_rpc_on_exit(void* context) {
|
|||
Nfc* nfc = context;
|
||||
Popup* popup = nfc->popup;
|
||||
|
||||
nfc_rpc_exit_callback(nfc);
|
||||
nfc_blink_stop(nfc);
|
||||
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
|
||||
|
|
|
@ -6,24 +6,18 @@
|
|||
#include "rpc_app.h"
|
||||
|
||||
#define TAG "RpcSystemApp"
|
||||
#define APP_BUTTON_TIMEOUT 1000
|
||||
|
||||
struct RpcAppSystem {
|
||||
RpcSession* session;
|
||||
RpcAppSystemCallback app_callback;
|
||||
void* app_context;
|
||||
PB_Main* state_msg;
|
||||
FuriTimer* timer;
|
||||
|
||||
uint32_t last_id;
|
||||
char* last_data;
|
||||
};
|
||||
|
||||
static void rpc_system_app_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
|
||||
if(rpc_app->app_callback) {
|
||||
rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context);
|
||||
}
|
||||
}
|
||||
#define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16
|
||||
|
||||
static void rpc_system_app_start_process(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
|
@ -33,9 +27,12 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context)
|
|||
RpcAppSystem* rpc_app = context;
|
||||
RpcSession* session = rpc_app->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;
|
||||
|
||||
|
@ -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;
|
||||
if(app_name) {
|
||||
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
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
result = PB_CommandStatus_OK;
|
||||
} else {
|
||||
furi_assert(0);
|
||||
furi_crash("Programming Error");
|
||||
}
|
||||
} else {
|
||||
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_LOG_D(TAG, "StartProcess: response id %d, result %d", 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_LOG_D(TAG, "LockStatus: response");
|
||||
rpc_send_and_release(session, &response);
|
||||
pb_release(&PB_Main_msg, &response);
|
||||
}
|
||||
|
@ -109,17 +108,17 @@ static void rpc_system_app_exit_request(const PB_Main* request, void* context) {
|
|||
PB_CommandStatus status;
|
||||
|
||||
if(rpc_app->app_callback) {
|
||||
if(rpc_app->app_callback(RpcAppEventAppExit, NULL, rpc_app->app_context)) {
|
||||
status = PB_CommandStatus_OK;
|
||||
furi_timer_stop(rpc_app->timer);
|
||||
} else {
|
||||
status = PB_CommandStatus_ERROR_APP_CMD_ERROR;
|
||||
}
|
||||
FURI_LOG_D(TAG, "ExitRequest: id %d", request->command_id);
|
||||
furi_assert(!rpc_app->last_id);
|
||||
furi_assert(!rpc_app->last_data);
|
||||
rpc_app->last_id = request->command_id;
|
||||
rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context);
|
||||
} else {
|
||||
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) {
|
||||
|
@ -133,17 +132,18 @@ static void rpc_system_app_load_file(const PB_Main* request, void* context) {
|
|||
|
||||
PB_CommandStatus status;
|
||||
if(rpc_app->app_callback) {
|
||||
const char* file_path = request->content.app_load_file_request.path;
|
||||
if(rpc_app->app_callback(RpcAppEventLoadFile, file_path, rpc_app->app_context)) {
|
||||
status = PB_CommandStatus_OK;
|
||||
} else {
|
||||
status = PB_CommandStatus_ERROR_APP_CMD_ERROR;
|
||||
}
|
||||
FURI_LOG_D(TAG, "LoadFile: id %d", request->command_id);
|
||||
furi_assert(!rpc_app->last_id);
|
||||
furi_assert(!rpc_app->last_data);
|
||||
rpc_app->last_id = request->command_id;
|
||||
rpc_app->last_data = strdup(request->content.app_load_file_request.path);
|
||||
rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context);
|
||||
} else {
|
||||
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) {
|
||||
|
@ -157,18 +157,18 @@ static void rpc_system_app_button_press(const PB_Main* request, void* context) {
|
|||
|
||||
PB_CommandStatus status;
|
||||
if(rpc_app->app_callback) {
|
||||
const char* args = request->content.app_button_press_request.args;
|
||||
if(rpc_app->app_callback(RpcAppEventButtonPress, args, rpc_app->app_context)) {
|
||||
status = PB_CommandStatus_OK;
|
||||
furi_timer_start(rpc_app->timer, APP_BUTTON_TIMEOUT);
|
||||
} else {
|
||||
status = PB_CommandStatus_ERROR_APP_CMD_ERROR;
|
||||
}
|
||||
FURI_LOG_D(TAG, "ButtonPress");
|
||||
furi_assert(!rpc_app->last_id);
|
||||
furi_assert(!rpc_app->last_data);
|
||||
rpc_app->last_id = request->command_id;
|
||||
rpc_app->last_data = strdup(request->content.app_button_press_request.args);
|
||||
rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context);
|
||||
} else {
|
||||
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) {
|
||||
|
@ -182,17 +182,17 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context)
|
|||
|
||||
PB_CommandStatus status;
|
||||
if(rpc_app->app_callback) {
|
||||
if(rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context)) {
|
||||
status = PB_CommandStatus_OK;
|
||||
furi_timer_stop(rpc_app->timer);
|
||||
} else {
|
||||
status = PB_CommandStatus_ERROR_APP_CMD_ERROR;
|
||||
}
|
||||
FURI_LOG_D(TAG, "ButtonRelease");
|
||||
furi_assert(!rpc_app->last_id);
|
||||
furi_assert(!rpc_app->last_data);
|
||||
rpc_app->last_id = request->command_id;
|
||||
rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context);
|
||||
} else {
|
||||
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) {
|
||||
|
@ -201,6 +201,8 @@ void rpc_system_app_send_started(RpcAppSystem* rpc_app) {
|
|||
furi_assert(session);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -210,9 +212,46 @@ void rpc_system_app_send_exited(RpcAppSystem* rpc_app) {
|
|||
furi_assert(session);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
furi_assert(rpc_app);
|
||||
|
||||
|
@ -226,8 +265,6 @@ void* rpc_system_app_alloc(RpcSession* session) {
|
|||
RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem));
|
||||
rpc_app->session = session;
|
||||
|
||||
rpc_app->timer = furi_timer_alloc(rpc_system_app_timer_callback, FuriTimerTypeOnce, rpc_app);
|
||||
|
||||
// App exit message
|
||||
rpc_app->state_msg = malloc(sizeof(PB_Main));
|
||||
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;
|
||||
furi_assert(session);
|
||||
|
||||
furi_timer_free(rpc_app->timer);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ typedef enum {
|
|||
RpcAppEventButtonRelease,
|
||||
} RpcAppSystemEvent;
|
||||
|
||||
typedef bool (*RpcAppSystemCallback)(RpcAppSystemEvent event, const char* arg, void* context);
|
||||
typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context);
|
||||
|
||||
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);
|
||||
|
||||
const char* rpc_system_app_get_data(RpcAppSystem* rpc_app);
|
||||
|
||||
void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -50,6 +50,9 @@ typedef enum {
|
|||
SubGhzCustomEventSceneStay,
|
||||
|
||||
SubGhzCustomEventSceneRpcLoad,
|
||||
SubGhzCustomEventSceneRpcButtonPress,
|
||||
SubGhzCustomEventSceneRpcButtonRelease,
|
||||
SubGhzCustomEventSceneRpcSessionClose,
|
||||
|
||||
SubGhzCustomEventViewReceiverOK,
|
||||
SubGhzCustomEventViewReceiverConfig,
|
||||
|
|
|
@ -22,20 +22,60 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == SubGhzCustomEventSceneExit) {
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||
subghz_tx_stop(subghz);
|
||||
subghz_sleep(subghz);
|
||||
}
|
||||
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) {
|
||||
string_t file_name;
|
||||
string_init(file_name);
|
||||
path_extract_filename(subghz->file_path, file_name, true);
|
||||
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_init(file_name);
|
||||
path_extract_filename(subghz->file_path, file_name, true);
|
||||
|
||||
snprintf(
|
||||
subghz->file_name_tmp,
|
||||
SUBGHZ_MAX_LEN_NAME,
|
||||
"loaded\n%s",
|
||||
string_get_cstr(file_name));
|
||||
popup_set_text(popup, subghz->file_name_tmp, 82, 32, AlignCenter, AlignTop);
|
||||
snprintf(
|
||||
subghz->file_name_tmp,
|
||||
SUBGHZ_MAX_LEN_NAME,
|
||||
"loaded\n%s",
|
||||
string_get_cstr(file_name));
|
||||
popup_set_text(popup, subghz->file_name_tmp, 82, 32, AlignCenter, AlignTop);
|
||||
|
||||
string_clear(file_name);
|
||||
string_clear(file_name);
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
|
|
@ -35,57 +35,38 @@ void subghz_tick_event_callback(void* context) {
|
|||
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);
|
||||
SubGhz* subghz = context;
|
||||
|
||||
if(!subghz->rpc_ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
furi_assert(subghz->rpc_ctx);
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL);
|
||||
subghz->rpc_ctx = NULL;
|
||||
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;
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose);
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
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) {
|
||||
if(arg) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad);
|
||||
} else if(event == RpcAppEventButtonPress) {
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) {
|
||||
notification_message(subghz->notifications, &sequence_blink_start_magenta);
|
||||
result = subghz_tx_start(subghz, subghz->txrx->fff_data);
|
||||
}
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress);
|
||||
} else if(event == RpcAppEventButtonRelease) {
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||
notification_message(subghz->notifications, &sequence_blink_stop);
|
||||
subghz_tx_stop(subghz);
|
||||
subghz_sleep(subghz);
|
||||
result = true;
|
||||
}
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease);
|
||||
} else {
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, event, false);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -243,7 +224,7 @@ void subghz_free(SubGhz* subghz) {
|
|||
if(subghz->rpc_ctx) {
|
||||
rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL);
|
||||
rpc_system_app_send_exited(subghz->rpc_ctx);
|
||||
notification_message(subghz->notifications, &sequence_blink_stop);
|
||||
subghz_blink_stop(subghz);
|
||||
subghz->rpc_ctx = NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -117,6 +117,10 @@ void subghz_begin(SubGhz* subghz, uint8_t* preset_data);
|
|||
uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency);
|
||||
void subghz_rx_end(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);
|
||||
void subghz_tx_stop(SubGhz* subghz);
|
||||
void subghz_dialog_message_show_only_rx(SubGhz* subghz);
|
||||
|
|
|
@ -387,6 +387,34 @@ MU_TEST(stream_split_test) {
|
|||
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) {
|
||||
string_t input_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_composite_test);
|
||||
MU_RUN_TEST(stream_split_test);
|
||||
MU_RUN_TEST(stream_buffered_write_after_read_test);
|
||||
MU_RUN_TEST(stream_buffered_large_file_test);
|
||||
}
|
||||
|
||||
|
|
|
@ -103,8 +103,7 @@ if assetsenv["IS_BASE_FIRMWARE"]:
|
|||
)
|
||||
assetsenv.Precious(resources)
|
||||
assetsenv.NoClean(resources)
|
||||
if assetsenv["FORCE"]:
|
||||
assetsenv.AlwaysBuild(resources)
|
||||
assetsenv.AlwaysBuild(resources)
|
||||
|
||||
# Exporting resources node to external environment
|
||||
env["FW_RESOURCES"] = resources
|
||||
|
|
Before Width: | Height: | Size: 301 B |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 624 B |
Before Width: | Height: | Size: 558 B |
Before Width: | Height: | Size: 574 B |
Before Width: | Height: | Size: 527 B |
Before Width: | Height: | Size: 556 B |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 553 B |
Before Width: | Height: | Size: 423 B |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 364 B |
Before Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 99 B |
Before Width: | Height: | Size: 99 B |
Before Width: | Height: | Size: 96 B |
Before Width: | Height: | Size: 97 B |
Before Width: | Height: | Size: 99 B |
|
@ -1 +0,0 @@
|
|||
3
|
Before Width: | Height: | Size: 98 B |
Before Width: | Height: | Size: 81 B |
Before Width: | Height: | Size: 86 B |
Before Width: | Height: | Size: 89 B |
Before Width: | Height: | Size: 89 B |
Before Width: | Height: | Size: 94 B |
Before Width: | Height: | Size: 97 B |
Before Width: | Height: | Size: 97 B |
Before Width: | Height: | Size: 99 B |
Before Width: | Height: | Size: 98 B |
|
@ -1 +0,0 @@
|
|||
3
|
Before Width: | Height: | Size: 326 B |
|
@ -1 +0,0 @@
|
|||
3
|
Before Width: | Height: | Size: 106 B |
Before Width: | Height: | Size: 106 B |
Before Width: | Height: | Size: 106 B |
Before Width: | Height: | Size: 106 B |
Before Width: | Height: | Size: 106 B |
Before Width: | Height: | Size: 106 B |
|
@ -1 +0,0 @@
|
|||
3
|
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 83 B |
Before Width: | Height: | Size: 81 B |
Before Width: | Height: | Size: 312 B |
Before Width: | Height: | Size: 318 B |
90
debug/fw.jflash
Normal 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
|
|
@ -110,7 +110,12 @@ Even if something goes wrong, Updater gives you an option to retry failed operat
|
|||
|
||||
## 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
|
||||
|
@ -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.
|
||||
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.
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
||||
`./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.
|
||||
|
||||
|
@ -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)
|
||||
|
||||
- `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
|
||||
- `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_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)
|
||||
- `openocd` - just start OpenOCD
|
||||
- `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
|
||||
|
||||
|
@ -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
|
||||
- `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
|
||||
- `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
|
||||
- `firmware_all`, `updater_all` - build basic set of binaries
|
||||
- `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
|
||||
|
||||
|
@ -66,7 +70,7 @@ FBT keeps track of internal dependencies, so you only need to build the highest-
|
|||
## Command-line parameters
|
||||
|
||||
- `--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-ext-apps=app1,app2,appN` - forces listed apps to be built as external with `firmware_extapps` target
|
||||
|
||||
|
|
16
fbt
|
@ -4,16 +4,20 @@
|
|||
# unofficial strict mode
|
||||
set -eu;
|
||||
|
||||
SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built";
|
||||
# private variables
|
||||
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)";
|
||||
SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built";
|
||||
|
||||
if [ -z "${FBT_NOENV:-}" ]; then
|
||||
echo "info: FBT_NOENV is not set, using included toolchain env";
|
||||
# public variables
|
||||
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";
|
||||
fi
|
||||
|
||||
if [ -z "${FBT_NO_SYNC:-}" ]; then
|
||||
echo "info: FBT_NO_SYNC is not set, running git submodule update";
|
||||
if [ -z "$FBT_NO_SYNC" ]; then
|
||||
if [ ! -d "$SCRIPT_PATH/.git" ]; then
|
||||
echo "\".git\" directory not found, please clone repo via \"git clone --recursive\"";
|
||||
exit 1;
|
||||
|
@ -21,4 +25,4 @@ if [ -z "${FBT_NO_SYNC:-}" ]; then
|
|||
git submodule update --init;
|
||||
fi
|
||||
|
||||
python3 "$SCRIPT_PATH/lib/scons/scripts/scons.py" $SCONS_DEFAULT_FLAGS "$@"
|
||||
python3 "$SCRIPT_PATH/lib/scons/scripts/scons.py" $SCONS_DEFAULT_FLAGS "$@"
|
||||
|
|
|
@ -85,6 +85,7 @@ FIRMWARE_APPS = {
|
|||
],
|
||||
"unit_tests": [
|
||||
"basic_services",
|
||||
"updater_app",
|
||||
"unit_tests",
|
||||
],
|
||||
"no_custom_apps": [
|
||||
|
|
|
@ -9,7 +9,11 @@ from fbt.util import (
|
|||
|
||||
# Building initial C environment for libs
|
||||
env = ENV.Clone(
|
||||
tools=["compilation_db", "fwbin", "fbt_apps"],
|
||||
tools=[
|
||||
("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}),
|
||||
"fwbin",
|
||||
"fbt_apps",
|
||||
],
|
||||
COMPILATIONDB_USE_ABSPATH=False,
|
||||
BUILD_DIR=fw_build_meta["build_dir"],
|
||||
IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware",
|
||||
|
@ -76,7 +80,6 @@ if not env["VERBOSE"]:
|
|||
HEXCOMSTR="\tHEX\t${TARGET}",
|
||||
BINCOMSTR="\tBIN\t${TARGET}",
|
||||
DFUCOMSTR="\tDFU\t${TARGET}",
|
||||
OPENOCDCOMSTR="\tFLASH\t${SOURCE}",
|
||||
)
|
||||
|
||||
|
||||
|
@ -139,7 +142,7 @@ apps_c = fwenv.ApplicationsC(
|
|||
Value(fwenv["APPS"]),
|
||||
)
|
||||
# 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]
|
||||
# Gather sources only from app folders in current configuration
|
||||
|
@ -164,6 +167,8 @@ fwenv.AppendUnique(
|
|||
"-u",
|
||||
"_printf_float",
|
||||
"-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
|
||||
Depends(fwelf, lib_targets)
|
||||
# Output extra details after building firmware
|
||||
|
@ -232,7 +236,8 @@ if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS):
|
|||
fwcdb = fwenv.CompilationDatabase()
|
||||
# without filtering, both updater & firmware commands would be generated
|
||||
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)
|
||||
|
||||
# Adding as a phony target, so folder link is updated even if elf didn't change
|
||||
|
|
|
@ -75,6 +75,7 @@ SECTIONS
|
|||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*lib*.a:*(.text .text.*) /* code from libraries before apps */
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
|
|
|
@ -4,8 +4,6 @@ env.Append(
|
|||
CPPPATH=[
|
||||
"#/lib/loclass",
|
||||
],
|
||||
CPPDEFINES=[
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ env.Append(
|
|||
"#/lib/mbedtls",
|
||||
"#/lib/mbedtls/include",
|
||||
],
|
||||
CPPDEFINES=[
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "buffered_file_stream.h"
|
||||
|
||||
#include "core/check.h"
|
||||
#include "stream_i.h"
|
||||
#include "file_stream.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,
|
||||
};
|
||||
|
||||
static bool buffered_file_stream_unread(BufferedFileStream* stream);
|
||||
|
||||
Stream* buffered_file_stream_alloc(Storage* storage) {
|
||||
BufferedFileStream* stream = malloc(sizeof(BufferedFileStream));
|
||||
|
||||
|
@ -125,8 +128,12 @@ static size_t buffered_file_stream_size(BufferedFileStream* stream) {
|
|||
|
||||
static size_t
|
||||
buffered_file_stream_write(BufferedFileStream* stream, const uint8_t* data, size_t size) {
|
||||
stream_cache_drop(stream->cache);
|
||||
return stream_write(stream->file_stream, data, size);
|
||||
size_t need_to_write = 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) {
|
||||
|
@ -150,6 +157,19 @@ static bool buffered_file_stream_delete_and_insert(
|
|||
size_t delete_size,
|
||||
StreamWriteCB write_callback,
|
||||
const void* ctx) {
|
||||
stream_cache_drop(stream->cache);
|
||||
return stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx);
|
||||
return buffered_file_stream_unread(stream) &&
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -53,13 +53,14 @@ class FlipperStorage:
|
|||
CLI_PROMPT = ">: "
|
||||
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.port = portname
|
||||
self.port.timeout = 2
|
||||
self.port.baudrate = portbaud
|
||||
self.port.baudrate = 115200 # Doesn't matter for VCP
|
||||
self.read = BufferedRead(self.port)
|
||||
self.last_error = ""
|
||||
self.chunk_size = chunk_size
|
||||
|
||||
def start(self):
|
||||
self.port.open()
|
||||
|
@ -192,7 +193,7 @@ class FlipperStorage:
|
|||
with open(filename_from, "rb") as file:
|
||||
filesize = os.fstat(file.fileno()).st_size
|
||||
|
||||
buffer_size = 512
|
||||
buffer_size = self.chunk_size
|
||||
while True:
|
||||
filedata = file.read(buffer_size)
|
||||
size = len(filedata)
|
||||
|
@ -221,7 +222,7 @@ class FlipperStorage:
|
|||
|
||||
def read_file(self, filename):
|
||||
"""Receive file from Flipper, and get filedata (bytes)"""
|
||||
buffer_size = 512
|
||||
buffer_size = self.chunk_size
|
||||
self.send_and_wait_eol(
|
||||
'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r"
|
||||
)
|
||||
|
@ -355,7 +356,7 @@ class FlipperStorage:
|
|||
"""Hash of local file"""
|
||||
hash_md5 = hashlib.md5()
|
||||
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)
|
||||
return hash_md5.hexdigest()
|
||||
|
||||
|
|
|
@ -14,14 +14,6 @@ import serial.tools.list_ports as list_ports
|
|||
class Main(App):
|
||||
def init(self):
|
||||
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(
|
||||
|
@ -64,7 +56,7 @@ class Main(App):
|
|||
if not (port := resolve_port(self.logger, self.args.port)):
|
||||
return 1
|
||||
|
||||
storage = FlipperStorage(port, self.args.baud)
|
||||
storage = FlipperStorage(port)
|
||||
storage.start()
|
||||
|
||||
try:
|
||||
|
@ -99,6 +91,7 @@ class Main(App):
|
|||
self.logger.error(f"Error: {storage.last_error}")
|
||||
return -3
|
||||
|
||||
# return -11
|
||||
storage.send_and_wait_eol(
|
||||
f"update install {flipper_update_path}/{manifest_name}\r"
|
||||
)
|
||||
|
|
14
scripts/serial_cli.py
Normal 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()
|
|
@ -14,14 +14,7 @@ import tempfile
|
|||
class Main(App):
|
||||
def init(self):
|
||||
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.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)):
|
||||
return None
|
||||
|
||||
storage = FlipperStorage(port, self.args.baud)
|
||||
storage = FlipperStorage(port)
|
||||
storage.start()
|
||||
return storage
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ if not [%FBT_NOENV%] == [] (
|
|||
exit /b 0
|
||||
)
|
||||
|
||||
set "FLIPPER_TOOLCHAIN_VERSION=3"
|
||||
set "FLIPPER_TOOLCHAIN_VERSION=8"
|
||||
set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows"
|
||||
|
||||
|
||||
|
|
|
@ -1,54 +1,211 @@
|
|||
#!/bin/sh
|
||||
|
||||
# unofficial strict mode
|
||||
set -eu;
|
||||
# shellcheck disable=SC2034,SC2016,SC2086
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
echo "In MinGW shell use \"fbt.cmd\" instead of \"fbt\"";
|
||||
exit 1;
|
||||
return 1;
|
||||
else
|
||||
echo "Sorry, your system is not supported. Please report your configuration to us.";
|
||||
exit 1;
|
||||
echo "Your system is not recognized. Sorry.. Please report us your configuration.";
|
||||
return 1;
|
||||
fi
|
||||
return 0;
|
||||
}
|
||||
|
||||
check_download_toolchain()
|
||||
fbtenv_check_rosetta()
|
||||
{
|
||||
if [ ! -d "$SCRIPT_PATH/$TOOLCHAIN_PATH" ]; then
|
||||
download_toolchain;
|
||||
elif [ ! -f "$SCRIPT_PATH/$TOOLCHAIN_PATH/VERSION" ]; then
|
||||
download_toolchain;
|
||||
elif [ "$(cat "$SCRIPT_PATH/$TOOLCHAIN_PATH/VERSION")" -ne "$FLIPPER_TOOLCHAIN_VERSION" ]; then
|
||||
download_toolchain;
|
||||
if [ "$ARCH_TYPE" = "arm64" ]; then
|
||||
if ! /usr/bin/pgrep -q oahd; then
|
||||
echo "Flipper Zero Toolchain needs Rosetta2 to run under Apple Silicon";
|
||||
echo "Please instal it by typing 'softwareupdate --install-rosetta --agree-to-license'";
|
||||
return 1;
|
||||
fi
|
||||
fi
|
||||
return 0;
|
||||
}
|
||||
|
||||
download_toolchain()
|
||||
fbtenv_check_tar()
|
||||
{
|
||||
chmod 755 "$SCRIPT_PATH/scripts/toolchain/unix-toolchain-download.sh";
|
||||
"$SCRIPT_PATH/scripts/toolchain/unix-toolchain-download.sh" "$FLIPPER_TOOLCHAIN_VERSION" || exit 1;
|
||||
printf "Checking tar..";
|
||||
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
|
||||
echo "Manual running of this script is not allowed.";
|
||||
exit 1;
|
||||
printf "Checking downloaded toolchain tgz..";
|
||||
if [ ! -f "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" ]; then
|
||||
echo "no";
|
||||
return 1;
|
||||
fi
|
||||
get_kernel_type; # sets TOOLCHAIN_PATH
|
||||
check_download_toolchain;
|
||||
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";
|
||||
echo "yes";
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -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
|
|
@ -2,6 +2,7 @@ from SCons.Builder import Builder
|
|||
from SCons.Action import Action
|
||||
from SCons.Warnings import warn, WarningOnByDefault
|
||||
import SCons
|
||||
import os.path
|
||||
|
||||
from fbt.appmanifest import (
|
||||
FlipperAppType,
|
||||
|
@ -17,10 +18,12 @@ from fbt.appmanifest import (
|
|||
|
||||
def LoadApplicationManifests(env):
|
||||
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("."):
|
||||
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:
|
||||
warn(WarningOnByDefault, str(e))
|
||||
|
||||
|
@ -64,6 +67,7 @@ def generate(env):
|
|||
build_apps_c,
|
||||
"${APPSCOMSTR}",
|
||||
),
|
||||
suffix=".c",
|
||||
),
|
||||
}
|
||||
)
|
||||
|
|
|
@ -66,9 +66,38 @@ def AddOpenOCDFlashTarget(env, targetenv, **kw):
|
|||
**kw,
|
||||
)
|
||||
env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target)
|
||||
if env["FORCE"]:
|
||||
env.AlwaysBuild(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):
|
||||
target = f"dist_{name}"
|
||||
command = env.Command(
|
||||
|
@ -86,6 +115,8 @@ def generate(env):
|
|||
env.AddMethod(AddFwProject)
|
||||
env.AddMethod(DistCommand)
|
||||
env.AddMethod(AddOpenOCDFlashTarget)
|
||||
env.AddMethod(AddJFlashTarget)
|
||||
env.AddMethod(AddUsbFlashTarget)
|
||||
|
||||
env.SetDefault(
|
||||
COPRO_MCU_FAMILY="STM32WB5x",
|
||||
|
|
27
site_scons/site_tools/jflash.py
Normal 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
|
|
@ -1,5 +1,6 @@
|
|||
import posixpath
|
||||
import os
|
||||
from SCons.Errors import UserError
|
||||
|
||||
|
||||
def BuildModule(env, module):
|
||||
|
@ -8,9 +9,9 @@ def BuildModule(env, module):
|
|||
if not os.path.exists(module_sconscript):
|
||||
module_sconscript = posixpath.join(src_dir, f"{module}.scons")
|
||||
if not os.path.exists(module_sconscript):
|
||||
print(f"Cannot build module {module}: scons file not found")
|
||||
Exit(2)
|
||||
raise UserError(f"Cannot build module {module}: scons file not found")
|
||||
|
||||
env.Append(PY_LINT_SOURCES=[module_sconscript])
|
||||
return env.SConscript(
|
||||
module_sconscript,
|
||||
variant_dir=posixpath.join(env.subst("$BUILD_DIR"), module),
|
||||
|
|