mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-15 00:58:36 -07:00
Merge branch 'dev' into ntag-auto-pwd-capture
This commit is contained in:
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -127,7 +127,7 @@ jobs:
|
|||||||
**Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:**
|
**Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:**
|
||||||
- [📦 Update package](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz)
|
- [📦 Update package](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz)
|
||||||
- [📥 DFU file](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-full-${{steps.names.outputs.suffix}}.dfu)
|
- [📥 DFU file](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-full-${{steps.names.outputs.suffix}}.dfu)
|
||||||
- [☁️ Web updater](https://my.flipp.dev/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}})
|
- [☁️ Web/App updater](https://my.flipp.dev/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}})
|
||||||
edit-mode: replace
|
edit-mode: replace
|
||||||
|
|
||||||
compact:
|
compact:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ App(
|
|||||||
name="Applications",
|
name="Applications",
|
||||||
apptype=FlipperAppType.APP,
|
apptype=FlipperAppType.APP,
|
||||||
entry_point="fap_loader_app",
|
entry_point="fap_loader_app",
|
||||||
|
cdefines=["APP_FAP_LOADER"],
|
||||||
requires=[
|
requires=[
|
||||||
"gui",
|
"gui",
|
||||||
"storage",
|
"storage",
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
|
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
|
||||||
FURI_LOG_I(TAG, "FAP Loader is staring app");
|
FURI_LOG_I(TAG, "FAP Loader is starting app");
|
||||||
|
|
||||||
FuriThread* thread = flipper_application_spawn(loader->app, NULL);
|
FuriThread* thread = flipper_application_spawn(loader->app, NULL);
|
||||||
furi_thread_start(thread);
|
furi_thread_start(thread);
|
||||||
@@ -182,6 +182,7 @@ int32_t fap_loader_app(void* p) {
|
|||||||
FapLoader* loader;
|
FapLoader* loader;
|
||||||
if(p) {
|
if(p) {
|
||||||
loader = fap_loader_alloc((const char*)p);
|
loader = fap_loader_alloc((const char*)p);
|
||||||
|
view_dispatcher_switch_to_view(loader->view_dispatcher, 0);
|
||||||
fap_loader_run_selected_app(loader);
|
fap_loader_run_selected_app(loader);
|
||||||
} else {
|
} else {
|
||||||
loader = fap_loader_alloc(EXT_PATH("apps"));
|
loader = fap_loader_alloc(EXT_PATH("apps"));
|
||||||
|
|||||||
@@ -165,6 +165,11 @@ static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context
|
|||||||
ret = rpc_session_get_available_size(bt->rpc_session);
|
ret = rpc_session_get_available_size(bt->rpc_session);
|
||||||
} else if(event.event == SerialServiceEventTypeDataSent) {
|
} else if(event.event == SerialServiceEventTypeDataSent) {
|
||||||
furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_BUFF_SENT);
|
furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_BUFF_SENT);
|
||||||
|
} else if(event.event == SerialServiceEventTypesBleResetRequest) {
|
||||||
|
FURI_LOG_I(TAG, "BLE restart request received");
|
||||||
|
BtMessage message = {.type = BtMessageTypeSetProfile, .data.profile = BtProfileSerial};
|
||||||
|
furi_check(
|
||||||
|
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -226,6 +231,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
|
|||||||
rpc_session_set_context(bt->rpc_session, bt);
|
rpc_session_set_context(bt->rpc_session, bt);
|
||||||
furi_hal_bt_serial_set_event_callback(
|
furi_hal_bt_serial_set_event_callback(
|
||||||
RPC_BUFFER_SIZE, bt_serial_event_callback, bt);
|
RPC_BUFFER_SIZE, bt_serial_event_callback, bt);
|
||||||
|
furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatusActive);
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_W(TAG, "RPC is busy, failed to open new session");
|
FURI_LOG_W(TAG, "RPC is busy, failed to open new session");
|
||||||
}
|
}
|
||||||
@@ -241,6 +247,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
|
|||||||
} else if(event.type == GapEventTypeDisconnected) {
|
} else if(event.type == GapEventTypeDisconnected) {
|
||||||
if(bt->profile == BtProfileSerial && bt->rpc_session) {
|
if(bt->profile == BtProfileSerial && bt->rpc_session) {
|
||||||
FURI_LOG_I(TAG, "Close RPC connection");
|
FURI_LOG_I(TAG, "Close RPC connection");
|
||||||
|
furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatusNotActive);
|
||||||
furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED);
|
furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED);
|
||||||
rpc_session_close(bt->rpc_session);
|
rpc_session_close(bt->rpc_session);
|
||||||
furi_hal_bt_serial_set_event_callback(0, NULL, NULL);
|
furi_hal_bt_serial_set_event_callback(0, NULL, NULL);
|
||||||
@@ -330,15 +337,21 @@ static void bt_change_profile(Bt* bt, BtMessage* message) {
|
|||||||
}
|
}
|
||||||
furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt);
|
furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt);
|
||||||
bt->profile = message->data.profile;
|
bt->profile = message->data.profile;
|
||||||
|
if(message->result) {
|
||||||
*message->result = true;
|
*message->result = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(TAG, "Failed to start Bt App");
|
FURI_LOG_E(TAG, "Failed to start Bt App");
|
||||||
|
if(message->result) {
|
||||||
*message->result = false;
|
*message->result = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
bt_show_warning(bt, "Radio stack doesn't support this app");
|
bt_show_warning(bt, "Radio stack doesn't support this app");
|
||||||
|
if(message->result) {
|
||||||
*message->result = false;
|
*message->result = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
|
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include <toolbox/saved_struct.h>
|
#include <toolbox/saved_struct.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
|
|
||||||
#define DESKTOP_SETTINGS_VER (5)
|
#define DESKTOP_SETTINGS_VER (6)
|
||||||
|
|
||||||
#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME)
|
#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME)
|
||||||
#define DESKTOP_SETTINGS_MAGIC (0x17)
|
#define DESKTOP_SETTINGS_MAGIC (0x17)
|
||||||
@@ -34,6 +34,9 @@
|
|||||||
|
|
||||||
#define MAX_PIN_SIZE 10
|
#define MAX_PIN_SIZE 10
|
||||||
#define MIN_PIN_SIZE 4
|
#define MIN_PIN_SIZE 4
|
||||||
|
#define MAX_APP_LENGTH 128
|
||||||
|
|
||||||
|
#define FAP_LOADER_APP_NAME "Applications"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
InputKey data[MAX_PIN_SIZE];
|
InputKey data[MAX_PIN_SIZE];
|
||||||
@@ -41,8 +44,13 @@ typedef struct {
|
|||||||
} PinCode;
|
} PinCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t favorite_primary;
|
bool is_external;
|
||||||
uint16_t favorite_secondary;
|
char name_or_path[MAX_APP_LENGTH];
|
||||||
|
} FavoriteApp;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FavoriteApp favorite_primary;
|
||||||
|
FavoriteApp favorite_secondary;
|
||||||
PinCode pin_code;
|
PinCode pin_code;
|
||||||
uint8_t is_locked;
|
uint8_t is_locked;
|
||||||
uint32_t auto_lock_delay_ms;
|
uint32_t auto_lock_delay_ms;
|
||||||
|
|||||||
@@ -114,29 +114,39 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||||||
|
|
||||||
case DesktopMainEventOpenFavoritePrimary:
|
case DesktopMainEventOpenFavoritePrimary:
|
||||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||||
if(desktop->settings.favorite_primary < FLIPPER_APPS_COUNT) {
|
if(desktop->settings.favorite_primary.is_external) {
|
||||||
LoaderStatus status = loader_start(
|
LoaderStatus status = loader_start(
|
||||||
desktop->loader, FLIPPER_APPS[desktop->settings.favorite_primary].name, NULL);
|
desktop->loader,
|
||||||
|
FAP_LOADER_APP_NAME,
|
||||||
|
desktop->settings.favorite_primary.name_or_path);
|
||||||
if(status != LoaderStatusOk) {
|
if(status != LoaderStatusOk) {
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(TAG, "Can't find primary favorite application");
|
LoaderStatus status = loader_start(
|
||||||
|
desktop->loader, desktop->settings.favorite_primary.name_or_path, NULL);
|
||||||
|
if(status != LoaderStatusOk) {
|
||||||
|
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
case DesktopMainEventOpenFavoriteSecondary:
|
case DesktopMainEventOpenFavoriteSecondary:
|
||||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||||
if(desktop->settings.favorite_secondary < FLIPPER_APPS_COUNT) {
|
if(desktop->settings.favorite_secondary.is_external) {
|
||||||
LoaderStatus status = loader_start(
|
LoaderStatus status = loader_start(
|
||||||
desktop->loader,
|
desktop->loader,
|
||||||
FLIPPER_APPS[desktop->settings.favorite_secondary].name,
|
FAP_LOADER_APP_NAME,
|
||||||
NULL);
|
desktop->settings.favorite_secondary.name_or_path);
|
||||||
if(status != LoaderStatusOk) {
|
if(status != LoaderStatusOk) {
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(TAG, "Can't find secondary favorite application");
|
LoaderStatus status = loader_start(
|
||||||
|
desktop->loader, desktop->settings.favorite_secondary.name_or_path, NULL);
|
||||||
|
if(status != LoaderStatusOk) {
|
||||||
|
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
@@ -166,7 +176,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||||||
}
|
}
|
||||||
case DesktopMainEventOpenGameMenu: {
|
case DesktopMainEventOpenGameMenu: {
|
||||||
LoaderStatus status = loader_start(
|
LoaderStatus status = loader_start(
|
||||||
desktop->loader, "Applications", EXT_PATH("/apps/Games/snake_game.fap"));
|
desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("/apps/Games/snake_game.fap"));
|
||||||
if(status != LoaderStatusOk) {
|
if(status != LoaderStatusOk) {
|
||||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ static const DolphinDeedWeight dolphin_deed_weights[] = {
|
|||||||
{2, DolphinAppIbutton}, // DolphinDeedIbuttonAdd
|
{2, DolphinAppIbutton}, // DolphinDeedIbuttonAdd
|
||||||
|
|
||||||
{3, DolphinAppBadusb}, // DolphinDeedBadUsbPlayScript
|
{3, DolphinAppBadusb}, // DolphinDeedBadUsbPlayScript
|
||||||
{3, DolphinAppU2f}, // DolphinDeedU2fAuthorized
|
{3, DolphinAppPlugin}, // DolphinDeedU2fAuthorized
|
||||||
|
|
||||||
{1, DolphinAppGpio}, // DolphinDeedGpioUartBridge
|
{1, DolphinAppPlugin}, // DolphinDeedGpioUartBridge
|
||||||
|
|
||||||
{1, DolphinAppPlugin}, // DolphinDeedPluginStart
|
{1, DolphinAppPlugin}, // DolphinDeedPluginStart
|
||||||
{1, DolphinAppPlugin}, // DolphinDeedPluginGameStart
|
{1, DolphinAppPlugin}, // DolphinDeedPluginGameStart
|
||||||
@@ -51,8 +51,6 @@ static uint8_t dolphin_deed_limits[] = {
|
|||||||
20, // DolphinAppIr
|
20, // DolphinAppIr
|
||||||
20, // DolphinAppIbutton
|
20, // DolphinAppIbutton
|
||||||
20, // DolphinAppBadusb
|
20, // DolphinAppBadusb
|
||||||
20, // DolphinAppU2f
|
|
||||||
20, // DolphinAppGpio
|
|
||||||
20, // DolphinAppPlugin
|
20, // DolphinAppPlugin
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ typedef enum {
|
|||||||
DolphinAppIr,
|
DolphinAppIr,
|
||||||
DolphinAppIbutton,
|
DolphinAppIbutton,
|
||||||
DolphinAppBadusb,
|
DolphinAppBadusb,
|
||||||
DolphinAppU2f,
|
|
||||||
DolphinAppGpio,
|
|
||||||
DolphinAppPlugin,
|
DolphinAppPlugin,
|
||||||
DolphinAppMAX,
|
DolphinAppMAX,
|
||||||
} DolphinApp;
|
} DolphinApp;
|
||||||
@@ -55,7 +53,6 @@ typedef enum {
|
|||||||
DolphinDeedBadUsbPlayScript,
|
DolphinDeedBadUsbPlayScript,
|
||||||
|
|
||||||
DolphinDeedU2fAuthorized,
|
DolphinDeedU2fAuthorized,
|
||||||
|
|
||||||
DolphinDeedGpioUartBridge,
|
DolphinDeedGpioUartBridge,
|
||||||
|
|
||||||
DolphinDeedPluginStart,
|
DolphinDeedPluginStart,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ DesktopSettingsApp* desktop_settings_app_alloc() {
|
|||||||
DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp));
|
DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp));
|
||||||
|
|
||||||
app->gui = furi_record_open(RECORD_GUI);
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
|
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
app->view_dispatcher = view_dispatcher_alloc();
|
app->view_dispatcher = view_dispatcher_alloc();
|
||||||
app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app);
|
app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app);
|
||||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||||
@@ -83,6 +84,7 @@ void desktop_settings_app_free(DesktopSettingsApp* app) {
|
|||||||
view_dispatcher_free(app->view_dispatcher);
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
scene_manager_free(app->scene_manager);
|
scene_manager_free(app->scene_manager);
|
||||||
// Records
|
// Records
|
||||||
|
furi_record_close(RECORD_DIALOGS);
|
||||||
furi_record_close(RECORD_GUI);
|
furi_record_close(RECORD_GUI);
|
||||||
free(app);
|
free(app);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <gui/modules/submenu.h>
|
#include <gui/modules/submenu.h>
|
||||||
#include <gui/modules/variable_item_list.h>
|
#include <gui/modules/variable_item_list.h>
|
||||||
|
#include <dialogs/dialogs.h>
|
||||||
|
|
||||||
#include <desktop/desktop_settings.h>
|
#include <desktop/desktop_settings.h>
|
||||||
#include <desktop/views/desktop_view_pin_input.h>
|
#include <desktop/views/desktop_view_pin_input.h>
|
||||||
@@ -25,6 +26,7 @@ typedef struct {
|
|||||||
DesktopSettings settings;
|
DesktopSettings settings;
|
||||||
|
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
|
DialogsApp* dialogs;
|
||||||
SceneManager* scene_manager;
|
SceneManager* scene_manager;
|
||||||
ViewDispatcher* view_dispatcher;
|
ViewDispatcher* view_dispatcher;
|
||||||
VariableItemList* variable_item_list;
|
VariableItemList* variable_item_list;
|
||||||
|
|||||||
@@ -1,6 +1,35 @@
|
|||||||
#include "../desktop_settings_app.h"
|
#include "../desktop_settings_app.h"
|
||||||
#include "applications.h"
|
#include "applications.h"
|
||||||
#include "desktop_settings_scene.h"
|
#include "desktop_settings_scene.h"
|
||||||
|
#include <storage/storage.h>
|
||||||
|
#include <dialogs/dialogs.h>
|
||||||
|
#include <fap_loader/fap_loader_app.h>
|
||||||
|
|
||||||
|
static bool favorite_fap_selector_item_callback(
|
||||||
|
FuriString* file_path,
|
||||||
|
void* context,
|
||||||
|
uint8_t** icon_ptr,
|
||||||
|
FuriString* item_name) {
|
||||||
|
UNUSED(context);
|
||||||
|
#ifdef APP_FAP_LOADER
|
||||||
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
|
bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
#else
|
||||||
|
UNUSED(file_path);
|
||||||
|
UNUSED(icon_ptr);
|
||||||
|
UNUSED(item_name);
|
||||||
|
bool success = false;
|
||||||
|
#endif
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool favorite_fap_selector_file_exists(char* file_path) {
|
||||||
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
|
bool exists = storage_file_exists(storage, file_path);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) {
|
static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) {
|
||||||
DesktopSettingsApp* app = context;
|
DesktopSettingsApp* app = context;
|
||||||
@@ -12,6 +41,10 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
|||||||
Submenu* submenu = app->submenu;
|
Submenu* submenu = app->submenu;
|
||||||
submenu_reset(submenu);
|
submenu_reset(submenu);
|
||||||
|
|
||||||
|
uint32_t primary_favorite =
|
||||||
|
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||||
|
uint32_t pre_select_item = 0;
|
||||||
|
|
||||||
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
@@ -19,38 +52,96 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
|||||||
i,
|
i,
|
||||||
desktop_settings_scene_favorite_submenu_callback,
|
desktop_settings_scene_favorite_submenu_callback,
|
||||||
app);
|
app);
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t primary_favorite =
|
if(primary_favorite) { // Select favorite item in submenu
|
||||||
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
if((app->settings.favorite_primary.is_external &&
|
||||||
|
!strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) ||
|
||||||
|
(!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_primary.name_or_path))) {
|
||||||
|
pre_select_item = i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if((app->settings.favorite_secondary.is_external &&
|
||||||
|
!strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) ||
|
||||||
|
(!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_secondary.name_or_path))) {
|
||||||
|
pre_select_item = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
submenu_set_header(
|
submenu_set_header(
|
||||||
app->submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:");
|
submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:");
|
||||||
|
submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch.
|
||||||
|
|
||||||
if(primary_favorite) {
|
|
||||||
submenu_set_selected_item(app->submenu, app->settings.favorite_primary);
|
|
||||||
} else {
|
|
||||||
submenu_set_selected_item(app->submenu, app->settings.favorite_secondary);
|
|
||||||
}
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) {
|
bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) {
|
||||||
DesktopSettingsApp* app = context;
|
DesktopSettingsApp* app = context;
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps"));
|
||||||
|
|
||||||
uint32_t primary_favorite =
|
uint32_t primary_favorite =
|
||||||
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME)) {
|
||||||
if(primary_favorite) {
|
if(primary_favorite) {
|
||||||
app->settings.favorite_primary = event.event;
|
app->settings.favorite_primary.is_external = false;
|
||||||
|
strncpy(
|
||||||
|
app->settings.favorite_primary.name_or_path,
|
||||||
|
FLIPPER_APPS[event.event].name,
|
||||||
|
MAX_APP_LENGTH);
|
||||||
} else {
|
} else {
|
||||||
app->settings.favorite_secondary = event.event;
|
app->settings.favorite_secondary.is_external = false;
|
||||||
|
strncpy(
|
||||||
|
app->settings.favorite_secondary.name_or_path,
|
||||||
|
FLIPPER_APPS[event.event].name,
|
||||||
|
MAX_APP_LENGTH);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const DialogsFileBrowserOptions browser_options = {
|
||||||
|
.extension = ".fap",
|
||||||
|
.icon = &I_unknown_10px,
|
||||||
|
.skip_assets = true,
|
||||||
|
.hide_ext = true,
|
||||||
|
.item_loader_callback = favorite_fap_selector_item_callback,
|
||||||
|
.item_loader_context = app,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(primary_favorite) { // Select favorite fap in file browser
|
||||||
|
if(favorite_fap_selector_file_exists(
|
||||||
|
app->settings.favorite_primary.name_or_path)) {
|
||||||
|
furi_string_set_str(temp_path, app->settings.favorite_primary.name_or_path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(favorite_fap_selector_file_exists(
|
||||||
|
app->settings.favorite_secondary.name_or_path)) {
|
||||||
|
furi_string_set_str(temp_path, app->settings.favorite_secondary.name_or_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) {
|
||||||
|
if(primary_favorite) {
|
||||||
|
app->settings.favorite_primary.is_external = true;
|
||||||
|
strncpy(
|
||||||
|
app->settings.favorite_primary.name_or_path,
|
||||||
|
furi_string_get_cstr(temp_path),
|
||||||
|
MAX_APP_LENGTH);
|
||||||
|
} else {
|
||||||
|
app->settings.favorite_secondary.is_external = true;
|
||||||
|
strncpy(
|
||||||
|
app->settings.favorite_secondary.name_or_path,
|
||||||
|
furi_string_get_cstr(temp_path),
|
||||||
|
MAX_APP_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
scene_manager_previous_scene(app->scene_manager);
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
furi_string_free(temp_path);
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ typedef struct {
|
|||||||
uint16_t rx_char_handle;
|
uint16_t rx_char_handle;
|
||||||
uint16_t tx_char_handle;
|
uint16_t tx_char_handle;
|
||||||
uint16_t flow_ctrl_char_handle;
|
uint16_t flow_ctrl_char_handle;
|
||||||
|
uint16_t rpc_status_char_handle;
|
||||||
FuriMutex* buff_size_mtx;
|
FuriMutex* buff_size_mtx;
|
||||||
uint32_t buff_size;
|
uint32_t buff_size;
|
||||||
uint16_t bytes_ready_to_receive;
|
uint16_t bytes_ready_to_receive;
|
||||||
@@ -28,6 +29,8 @@ static const uint8_t char_rx_uuid[] =
|
|||||||
{0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
|
{0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
|
||||||
static const uint8_t flow_ctrl_uuid[] =
|
static const uint8_t flow_ctrl_uuid[] =
|
||||||
{0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
|
{0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
|
||||||
|
static const uint8_t rpc_status_uuid[] =
|
||||||
|
{0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
|
||||||
|
|
||||||
static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
||||||
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
|
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
|
||||||
@@ -67,6 +70,17 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
|||||||
furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk);
|
furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk);
|
||||||
}
|
}
|
||||||
ret = SVCCTL_EvtAckFlowEnable;
|
ret = SVCCTL_EvtAckFlowEnable;
|
||||||
|
} else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) {
|
||||||
|
SerialServiceRpcStatus* rpc_status =
|
||||||
|
(SerialServiceRpcStatus*)attribute_modified->Attr_Data;
|
||||||
|
if(*rpc_status == SerialServiceRpcStatusNotActive) {
|
||||||
|
if(serial_svc->callback) {
|
||||||
|
SerialServiceEvent event = {
|
||||||
|
.event = SerialServiceEventTypesBleResetRequest,
|
||||||
|
};
|
||||||
|
serial_svc->callback(event, serial_svc->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
|
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
|
||||||
FURI_LOG_T(TAG, "Ack received");
|
FURI_LOG_T(TAG, "Ack received");
|
||||||
@@ -82,6 +96,18 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) {
|
||||||
|
tBleStatus ble_status = aci_gatt_update_char_value(
|
||||||
|
serial_svc->svc_handle,
|
||||||
|
serial_svc->rpc_status_char_handle,
|
||||||
|
0,
|
||||||
|
sizeof(SerialServiceRpcStatus),
|
||||||
|
(uint8_t*)&status);
|
||||||
|
if(ble_status) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void serial_svc_start() {
|
void serial_svc_start() {
|
||||||
tBleStatus status;
|
tBleStatus status;
|
||||||
serial_svc = malloc(sizeof(SerialSvc));
|
serial_svc = malloc(sizeof(SerialSvc));
|
||||||
@@ -90,7 +116,7 @@ void serial_svc_start() {
|
|||||||
|
|
||||||
// Add service
|
// Add service
|
||||||
status = aci_gatt_add_service(
|
status = aci_gatt_add_service(
|
||||||
UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 10, &serial_svc->svc_handle);
|
UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle);
|
||||||
if(status) {
|
if(status) {
|
||||||
FURI_LOG_E(TAG, "Failed to add Serial service: %d", status);
|
FURI_LOG_E(TAG, "Failed to add Serial service: %d", status);
|
||||||
}
|
}
|
||||||
@@ -141,6 +167,22 @@ void serial_svc_start() {
|
|||||||
if(status) {
|
if(status) {
|
||||||
FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status);
|
FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status);
|
||||||
}
|
}
|
||||||
|
// Add RPC status characteristic
|
||||||
|
status = aci_gatt_add_char(
|
||||||
|
serial_svc->svc_handle,
|
||||||
|
UUID_TYPE_128,
|
||||||
|
(const Char_UUID_t*)rpc_status_uuid,
|
||||||
|
sizeof(SerialServiceRpcStatus),
|
||||||
|
CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY,
|
||||||
|
ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE,
|
||||||
|
GATT_NOTIFY_ATTRIBUTE_WRITE,
|
||||||
|
10,
|
||||||
|
CHAR_VALUE_LEN_CONSTANT,
|
||||||
|
&serial_svc->rpc_status_char_handle);
|
||||||
|
if(status) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status);
|
||||||
|
}
|
||||||
|
serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive);
|
||||||
// Allocate buffer size mutex
|
// Allocate buffer size mutex
|
||||||
serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal);
|
serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
}
|
}
|
||||||
@@ -198,6 +240,10 @@ void serial_svc_stop() {
|
|||||||
if(status) {
|
if(status) {
|
||||||
FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status);
|
FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status);
|
||||||
}
|
}
|
||||||
|
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle);
|
||||||
|
if(status) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status);
|
||||||
|
}
|
||||||
// Delete service
|
// Delete service
|
||||||
status = aci_gatt_del_service(serial_svc->svc_handle);
|
status = aci_gatt_del_service(serial_svc->svc_handle);
|
||||||
if(status) {
|
if(status) {
|
||||||
@@ -242,3 +288,8 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serial_svc_set_rpc_status(SerialServiceRpcStatus status) {
|
||||||
|
furi_assert(serial_svc);
|
||||||
|
serial_svc_update_rpc_char(status);
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,9 +10,15 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SerialServiceRpcStatusNotActive = 0UL,
|
||||||
|
SerialServiceRpcStatusActive = 1UL,
|
||||||
|
} SerialServiceRpcStatus;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SerialServiceEventTypeDataReceived,
|
SerialServiceEventTypeDataReceived,
|
||||||
SerialServiceEventTypeDataSent,
|
SerialServiceEventTypeDataSent,
|
||||||
|
SerialServiceEventTypesBleResetRequest,
|
||||||
} SerialServiceEventType;
|
} SerialServiceEventType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -34,6 +40,8 @@ void serial_svc_set_callbacks(
|
|||||||
SerialServiceEventCallback callback,
|
SerialServiceEventCallback callback,
|
||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
|
void serial_svc_set_rpc_status(SerialServiceRpcStatus status);
|
||||||
|
|
||||||
void serial_svc_notify_buffer_is_empty();
|
void serial_svc_notify_buffer_is_empty();
|
||||||
|
|
||||||
void serial_svc_stop();
|
void serial_svc_stop();
|
||||||
|
|||||||
@@ -31,6 +31,16 @@ void furi_hal_bt_serial_notify_buffer_is_empty() {
|
|||||||
serial_svc_notify_buffer_is_empty();
|
serial_svc_notify_buffer_is_empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatus status) {
|
||||||
|
SerialServiceRpcStatus st;
|
||||||
|
if(status == FuriHalBtSerialRpcStatusActive) {
|
||||||
|
st = SerialServiceRpcStatusActive;
|
||||||
|
} else {
|
||||||
|
st = SerialServiceRpcStatusNotActive;
|
||||||
|
}
|
||||||
|
serial_svc_set_rpc_status(st);
|
||||||
|
}
|
||||||
|
|
||||||
bool furi_hal_bt_serial_tx(uint8_t* data, uint16_t size) {
|
bool furi_hal_bt_serial_tx(uint8_t* data, uint16_t size) {
|
||||||
if(size > FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX) {
|
if(size > FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ extern "C" {
|
|||||||
|
|
||||||
#define FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX SERIAL_SVC_DATA_LEN_MAX
|
#define FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX SERIAL_SVC_DATA_LEN_MAX
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FuriHalBtSerialRpcStatusNotActive,
|
||||||
|
FuriHalBtSerialRpcStatusActive,
|
||||||
|
} FuriHalBtSerialRpcStatus;
|
||||||
|
|
||||||
/** Serial service callback type */
|
/** Serial service callback type */
|
||||||
typedef SerialServiceEventCallback FuriHalBtSerialCallback;
|
typedef SerialServiceEventCallback FuriHalBtSerialCallback;
|
||||||
|
|
||||||
@@ -30,6 +35,12 @@ void furi_hal_bt_serial_set_event_callback(
|
|||||||
FuriHalBtSerialCallback callback,
|
FuriHalBtSerialCallback callback,
|
||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
|
/** Set BLE RPC status
|
||||||
|
*
|
||||||
|
* @param status FuriHalBtSerialRpcStatus instance
|
||||||
|
*/
|
||||||
|
void furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatus status);
|
||||||
|
|
||||||
/** Notify that application buffer is empty
|
/** Notify that application buffer is empty
|
||||||
*/
|
*/
|
||||||
void furi_hal_bt_serial_notify_buffer_is_empty();
|
void furi_hal_bt_serial_notify_buffer_is_empty();
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
|
|||||||
uint8_t sectors_total =
|
uint8_t sectors_total =
|
||||||
mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type);
|
mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type);
|
||||||
FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total);
|
FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total);
|
||||||
read_success = (sectors_read == sectors_total);
|
read_success = mf_classic_is_card_read(&nfc_worker->dev_data->mf_classic_data);
|
||||||
}
|
}
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
@@ -488,6 +488,9 @@ static void nfc_worker_mf_classic_key_attack(
|
|||||||
uint16_t start_sector) {
|
uint16_t start_sector) {
|
||||||
furi_assert(nfc_worker);
|
furi_assert(nfc_worker);
|
||||||
|
|
||||||
|
bool card_found_notified = true;
|
||||||
|
bool card_removed_notified = false;
|
||||||
|
|
||||||
MfClassicData* data = &nfc_worker->dev_data->mf_classic_data;
|
MfClassicData* data = &nfc_worker->dev_data->mf_classic_data;
|
||||||
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
||||||
|
|
||||||
@@ -495,6 +498,14 @@ static void nfc_worker_mf_classic_key_attack(
|
|||||||
|
|
||||||
// Check every sector's A and B keys with the given key
|
// Check every sector's A and B keys with the given key
|
||||||
for(size_t i = start_sector; i < total_sectors; i++) {
|
for(size_t i = start_sector; i < total_sectors; i++) {
|
||||||
|
furi_hal_nfc_sleep();
|
||||||
|
if(furi_hal_nfc_activate_nfca(200, NULL)) {
|
||||||
|
furi_hal_nfc_sleep();
|
||||||
|
if(!card_found_notified) {
|
||||||
|
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||||
|
card_found_notified = true;
|
||||||
|
card_removed_notified = false;
|
||||||
|
}
|
||||||
uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i);
|
uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i);
|
||||||
if(mf_classic_is_sector_read(data, i)) continue;
|
if(mf_classic_is_sector_read(data, i)) continue;
|
||||||
if(!mf_classic_is_key_found(data, i, MfClassicKeyA)) {
|
if(!mf_classic_is_key_found(data, i, MfClassicKeyA)) {
|
||||||
@@ -523,8 +534,16 @@ static void nfc_worker_mf_classic_key_attack(
|
|||||||
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
|
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mf_classic_is_sector_read(data, i)) continue;
|
if(mf_classic_is_sector_read(data, i)) continue;
|
||||||
mf_classic_read_sector(tx_rx, data, i);
|
mf_classic_read_sector(tx_rx, data, i);
|
||||||
|
} else {
|
||||||
|
if(!card_removed_notified) {
|
||||||
|
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
|
||||||
|
card_removed_notified = true;
|
||||||
|
card_found_notified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -538,6 +557,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
|
|||||||
&nfc_worker->dev_data->mf_classic_dict_attack_data;
|
&nfc_worker->dev_data->mf_classic_dict_attack_data;
|
||||||
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
||||||
uint64_t key = 0;
|
uint64_t key = 0;
|
||||||
|
uint64_t prev_key = 0;
|
||||||
FuriHalNfcTxRxContext tx_rx = {};
|
FuriHalNfcTxRxContext tx_rx = {};
|
||||||
bool card_found_notified = true;
|
bool card_found_notified = true;
|
||||||
bool card_removed_notified = false;
|
bool card_removed_notified = false;
|
||||||
@@ -572,6 +592,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
|
|||||||
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||||
card_found_notified = true;
|
card_found_notified = true;
|
||||||
card_removed_notified = false;
|
card_removed_notified = false;
|
||||||
|
nfc_worker_mf_classic_key_attack(nfc_worker, prev_key, &tx_rx, i);
|
||||||
}
|
}
|
||||||
FURI_LOG_D(
|
FURI_LOG_D(
|
||||||
TAG,
|
TAG,
|
||||||
@@ -608,6 +629,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
|
|||||||
}
|
}
|
||||||
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
||||||
}
|
}
|
||||||
|
memcpy(&prev_key, &key, sizeof(key));
|
||||||
}
|
}
|
||||||
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
||||||
mf_classic_read_sector(&tx_rx, data, i);
|
mf_classic_read_sector(&tx_rx, data, i);
|
||||||
|
|||||||
@@ -155,6 +155,16 @@ void mf_classic_set_key_found(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mf_classic_set_key_not_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) {
|
||||||
|
furi_assert(data);
|
||||||
|
|
||||||
|
if(key_type == MfClassicKeyA) {
|
||||||
|
FURI_BIT_CLEAR(data->key_a_mask, sector_num);
|
||||||
|
} else if(key_type == MfClassicKeyB) {
|
||||||
|
FURI_BIT_CLEAR(data->key_b_mask, sector_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) {
|
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) {
|
||||||
furi_assert(data);
|
furi_assert(data);
|
||||||
|
|
||||||
@@ -203,6 +213,18 @@ void mf_classic_get_read_sectors_and_keys(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mf_classic_is_card_read(MfClassicData* data) {
|
||||||
|
furi_assert(data);
|
||||||
|
|
||||||
|
uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
|
||||||
|
uint8_t sectors_read = 0;
|
||||||
|
uint8_t keys_found = 0;
|
||||||
|
mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found);
|
||||||
|
bool card_read = (sectors_read == sectors_total) && (keys_found == sectors_total * 2);
|
||||||
|
|
||||||
|
return card_read;
|
||||||
|
}
|
||||||
|
|
||||||
static bool mf_classic_is_allowed_access_sector_trailer(
|
static bool mf_classic_is_allowed_access_sector_trailer(
|
||||||
MfClassicEmulator* emulator,
|
MfClassicEmulator* emulator,
|
||||||
uint8_t block_num,
|
uint8_t block_num,
|
||||||
@@ -612,7 +634,15 @@ static bool mf_classic_read_sector_with_reader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auth to first block in sector
|
// Auth to first block in sector
|
||||||
if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) break;
|
if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) {
|
||||||
|
// Set key to MF_CLASSIC_NO_KEY to prevent further attempts
|
||||||
|
if(key_type == MfClassicKeyA) {
|
||||||
|
sector_reader->key_a = MF_CLASSIC_NO_KEY;
|
||||||
|
} else {
|
||||||
|
sector_reader->key_b = MF_CLASSIC_NO_KEY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num);
|
sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num);
|
||||||
|
|
||||||
// Read blocks
|
// Read blocks
|
||||||
@@ -711,6 +741,13 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data
|
|||||||
mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]);
|
mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]);
|
||||||
}
|
}
|
||||||
sectors_read++;
|
sectors_read++;
|
||||||
|
} else {
|
||||||
|
// Invalid key, set it to not found
|
||||||
|
if(key_a != MF_CLASSIC_NO_KEY) {
|
||||||
|
mf_classic_set_key_not_found(data, i, MfClassicKeyA);
|
||||||
|
} else {
|
||||||
|
mf_classic_set_key_not_found(data, i, MfClassicKeyB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,12 +98,16 @@ void mf_classic_set_key_found(
|
|||||||
MfClassicKey key_type,
|
MfClassicKey key_type,
|
||||||
uint64_t key);
|
uint64_t key);
|
||||||
|
|
||||||
|
void mf_classic_set_key_not_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type);
|
||||||
|
|
||||||
bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num);
|
bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num);
|
||||||
|
|
||||||
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
|
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
|
||||||
|
|
||||||
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
|
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
|
||||||
|
|
||||||
|
bool mf_classic_is_card_read(MfClassicData* data);
|
||||||
|
|
||||||
void mf_classic_get_read_sectors_and_keys(
|
void mf_classic_get_read_sectors_and_keys(
|
||||||
MfClassicData* data,
|
MfClassicData* data,
|
||||||
uint8_t* sectors_read,
|
uint8_t* sectors_read,
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class Copro:
|
|||||||
array.append({"name": filename, "sha256": file_sha256(source_file), **kwargs})
|
array.append({"name": filename, "sha256": file_sha256(source_file), **kwargs})
|
||||||
|
|
||||||
def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None):
|
def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None):
|
||||||
self.output_tar = tarfile.open(output_file, "w:gz")
|
self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT)
|
||||||
|
|
||||||
stack_file = os.path.join(self.mcu_copro, stack_file_name)
|
stack_file = os.path.join(self.mcu_copro, stack_file_name)
|
||||||
# Form Manifest
|
# Form Manifest
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class ProjectDir:
|
|||||||
|
|
||||||
class Main(App):
|
class Main(App):
|
||||||
DIST_FILE_PREFIX = "flipper-z-"
|
DIST_FILE_PREFIX = "flipper-z-"
|
||||||
|
DIST_FOLDER_MAX_NAME_LENGTH = 80
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
||||||
@@ -129,7 +130,9 @@ class Main(App):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.args.version:
|
if self.args.version:
|
||||||
bundle_dir_name = f"{self.target}-update-{self.args.suffix}"
|
bundle_dir_name = f"{self.target}-update-{self.args.suffix}"[
|
||||||
|
: self.DIST_FOLDER_MAX_NAME_LENGTH
|
||||||
|
]
|
||||||
bundle_dir = join(self.output_dir_path, bundle_dir_name)
|
bundle_dir = join(self.output_dir_path, bundle_dir_name)
|
||||||
bundle_args = [
|
bundle_args = [
|
||||||
"generate",
|
"generate",
|
||||||
@@ -170,6 +173,7 @@ class Main(App):
|
|||||||
),
|
),
|
||||||
"w:gz",
|
"w:gz",
|
||||||
compresslevel=9,
|
compresslevel=9,
|
||||||
|
format=tarfile.USTAR_FORMAT,
|
||||||
) as tar:
|
) as tar:
|
||||||
tar.add(bundle_dir, arcname=bundle_dir_name)
|
tar.add(bundle_dir, arcname=bundle_dir_name)
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class Main(App):
|
|||||||
RESOURCE_TAR_MODE = "w:"
|
RESOURCE_TAR_MODE = "w:"
|
||||||
RESOURCE_TAR_FORMAT = tarfile.USTAR_FORMAT
|
RESOURCE_TAR_FORMAT = tarfile.USTAR_FORMAT
|
||||||
RESOURCE_FILE_NAME = "resources.tar"
|
RESOURCE_FILE_NAME = "resources.tar"
|
||||||
|
RESOURCE_ENTRY_NAME_MAX_LENGTH = 100
|
||||||
|
|
||||||
WHITELISTED_STACK_TYPES = set(
|
WHITELISTED_STACK_TYPES = set(
|
||||||
map(
|
map(
|
||||||
@@ -76,9 +77,13 @@ class Main(App):
|
|||||||
self.parser_generate.set_defaults(func=self.generate)
|
self.parser_generate.set_defaults(func=self.generate)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
stage_basename = basename(self.args.stage)
|
stage_basename = "updater.bin" # used to be basename(self.args.stage)
|
||||||
dfu_basename = basename(self.args.dfu)
|
dfu_basename = (
|
||||||
radiobin_basename = basename(self.args.radiobin)
|
"firmware.dfu" if self.args.dfu else ""
|
||||||
|
) # used to be basename(self.args.dfu)
|
||||||
|
radiobin_basename = (
|
||||||
|
"radio.bin" if self.args.radiobin else ""
|
||||||
|
) # used to be basename(self.args.radiobin)
|
||||||
resources_basename = ""
|
resources_basename = ""
|
||||||
|
|
||||||
radio_version = 0
|
radio_version = 0
|
||||||
@@ -120,9 +125,10 @@ class Main(App):
|
|||||||
)
|
)
|
||||||
if self.args.resources:
|
if self.args.resources:
|
||||||
resources_basename = self.RESOURCE_FILE_NAME
|
resources_basename = self.RESOURCE_FILE_NAME
|
||||||
self.package_resources(
|
if not self.package_resources(
|
||||||
self.args.resources, join(self.args.directory, resources_basename)
|
self.args.resources, join(self.args.directory, resources_basename)
|
||||||
)
|
):
|
||||||
|
return 3
|
||||||
|
|
||||||
if not self.layout_check(dfu_size, radio_addr):
|
if not self.layout_check(dfu_size, radio_addr):
|
||||||
self.logger.warn("Memory layout looks suspicious")
|
self.logger.warn("Memory layout looks suspicious")
|
||||||
@@ -199,11 +205,28 @@ class Main(App):
|
|||||||
"Please confirm that you REALLY want to do that with --I-understand-what-I-am-doing=yes"
|
"Please confirm that you REALLY want to do that with --I-understand-what-I-am-doing=yes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _tar_filter(self, tarinfo: tarfile.TarInfo):
|
||||||
|
if len(tarinfo.name) > self.RESOURCE_ENTRY_NAME_MAX_LENGTH:
|
||||||
|
self.logger.error(
|
||||||
|
f"Cannot package resource: name '{tarinfo.name}' too long"
|
||||||
|
)
|
||||||
|
raise ValueError("Resource name too long")
|
||||||
|
return tarinfo
|
||||||
|
|
||||||
def package_resources(self, srcdir: str, dst_name: str):
|
def package_resources(self, srcdir: str, dst_name: str):
|
||||||
|
try:
|
||||||
with tarfile.open(
|
with tarfile.open(
|
||||||
dst_name, self.RESOURCE_TAR_MODE, format=self.RESOURCE_TAR_FORMAT
|
dst_name, self.RESOURCE_TAR_MODE, format=self.RESOURCE_TAR_FORMAT
|
||||||
) as tarball:
|
) as tarball:
|
||||||
tarball.add(srcdir, arcname="")
|
tarball.add(
|
||||||
|
srcdir,
|
||||||
|
arcname="",
|
||||||
|
filter=self._tar_filter,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except ValueError as e:
|
||||||
|
self.logger.error(f"Cannot package resources: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def copro_version_as_int(coprometa, stacktype):
|
def copro_version_as_int(coprometa, stacktype):
|
||||||
|
|||||||
Reference in New Issue
Block a user