Merge branch 'release-candidate' into release
This commit is contained in:
commit
90eb608f08
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,2 +1 @@
|
|||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
|
|||||||
43
.github/workflows/build.yml
vendored
43
.github/workflows/build.yml
vendored
@ -103,6 +103,32 @@ jobs:
|
|||||||
-o firmware/.obj/${TARGET}/full.hex -Intel
|
-o firmware/.obj/${TARGET}/full.hex -Intel
|
||||||
done
|
done
|
||||||
|
|
||||||
|
- name: 'Generate full dfu file'
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
uses: ./.github/actions/docker
|
||||||
|
with:
|
||||||
|
run: |
|
||||||
|
for TARGET in ${TARGETS}
|
||||||
|
do
|
||||||
|
hex2dfu \
|
||||||
|
-i firmware/.obj/${TARGET}/full.hex \
|
||||||
|
-o artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.dfu \
|
||||||
|
-l "Flipper Zero $(echo $TARGET | tr a-z A-Z)"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: 'Generate full json file'
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
uses: ./.github/actions/docker
|
||||||
|
with:
|
||||||
|
run: |
|
||||||
|
for TARGET in ${TARGETS}
|
||||||
|
do
|
||||||
|
jq -s '.[0] * .[1]' \
|
||||||
|
bootloader/.obj/${TARGET}/bootloader.json \
|
||||||
|
firmware/.obj/${TARGET}/firmware.json \
|
||||||
|
> artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.json
|
||||||
|
done
|
||||||
|
|
||||||
- name: 'Move upload files'
|
- name: 'Move upload files'
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
uses: ./.github/actions/docker
|
uses: ./.github/actions/docker
|
||||||
@ -116,25 +142,16 @@ jobs:
|
|||||||
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.bin
|
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.bin
|
||||||
mv bootloader/.obj/${TARGET}/bootloader.elf \
|
mv bootloader/.obj/${TARGET}/bootloader.elf \
|
||||||
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.elf
|
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.elf
|
||||||
|
mv bootloader/.obj/${TARGET}/bootloader.json \
|
||||||
|
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.json
|
||||||
mv firmware/.obj/${TARGET}/firmware.dfu \
|
mv firmware/.obj/${TARGET}/firmware.dfu \
|
||||||
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.dfu
|
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.dfu
|
||||||
mv firmware/.obj/${TARGET}/firmware.bin \
|
mv firmware/.obj/${TARGET}/firmware.bin \
|
||||||
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.bin
|
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.bin
|
||||||
mv firmware/.obj/${TARGET}/firmware.elf \
|
mv firmware/.obj/${TARGET}/firmware.elf \
|
||||||
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.elf
|
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.elf
|
||||||
done
|
mv firmware/.obj/${TARGET}/firmware.json \
|
||||||
|
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.json
|
||||||
- name: 'Generate full dfu file'
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
uses: ./.github/actions/docker
|
|
||||||
with:
|
|
||||||
run: |
|
|
||||||
for TARGET in ${TARGETS}
|
|
||||||
do
|
|
||||||
hex2dfu \
|
|
||||||
-i firmware/.obj/${TARGET}/full.hex \
|
|
||||||
-o artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.dfu \
|
|
||||||
-l "Flipper Zero $(echo $TARGET | tr a-z A-Z)"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: 'Full flash asssembly: bootloader as base'
|
- name: 'Full flash asssembly: bootloader as base'
|
||||||
|
|||||||
18
Makefile
18
Makefile
@ -26,6 +26,10 @@ flash: bootloader_flash firmware_flash
|
|||||||
debug:
|
debug:
|
||||||
$(MAKE) -C firmware -j$(NPROCS) debug
|
$(MAKE) -C firmware -j$(NPROCS) debug
|
||||||
|
|
||||||
|
.PHONY: blackmagic
|
||||||
|
blackmagic:
|
||||||
|
$(MAKE) -C firmware -j$(NPROCS) blackmagic
|
||||||
|
|
||||||
.PHONY: wipe
|
.PHONY: wipe
|
||||||
wipe:
|
wipe:
|
||||||
$(PROJECT_ROOT)/scripts/flash.py wipe
|
$(PROJECT_ROOT)/scripts/flash.py wipe
|
||||||
@ -49,12 +53,16 @@ firmware_clean:
|
|||||||
|
|
||||||
.PHONY: bootloader_flash
|
.PHONY: bootloader_flash
|
||||||
bootloader_flash:
|
bootloader_flash:
|
||||||
|
ifeq ($(FORCE), 1)
|
||||||
rm $(PROJECT_ROOT)/bootloader/.obj/f*/flash || true
|
rm $(PROJECT_ROOT)/bootloader/.obj/f*/flash || true
|
||||||
|
endif
|
||||||
$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) flash
|
$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) flash
|
||||||
|
|
||||||
.PHONY: firmware_flash
|
.PHONY: firmware_flash
|
||||||
firmware_flash:
|
firmware_flash:
|
||||||
|
ifeq ($(FORCE), 1)
|
||||||
rm $(PROJECT_ROOT)/firmware/.obj/f*/flash || true
|
rm $(PROJECT_ROOT)/firmware/.obj/f*/flash || true
|
||||||
|
endif
|
||||||
$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash
|
$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash
|
||||||
|
|
||||||
.PHONY: flash_radio
|
.PHONY: flash_radio
|
||||||
@ -73,8 +81,16 @@ flash_radio_fus:
|
|||||||
@echo "================ JUST DON'T ================"
|
@echo "================ JUST DON'T ================"
|
||||||
@echo
|
@echo
|
||||||
|
|
||||||
.PHONY:
|
.PHONY: flash_radio_fus_please_i_m_not_going_to_complain
|
||||||
flash_radio_fus_please_i_m_not_going_to_complain:
|
flash_radio_fus_please_i_m_not_going_to_complain:
|
||||||
$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin
|
$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin
|
||||||
$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin
|
$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin
|
||||||
$(PROJECT_ROOT)/scripts/ob.py set
|
$(PROJECT_ROOT)/scripts/ob.py set
|
||||||
|
|
||||||
|
FORMAT_SOURCES = $(shell find applications bootloader core -iname "*.h" -o -iname "*.c" -o -iname "*.cpp")
|
||||||
|
|
||||||
|
.PHONY: format
|
||||||
|
format:
|
||||||
|
@echo "Formatting sources with clang-format"
|
||||||
|
@clang-format -style=file -i $(FORMAT_SOURCES)
|
||||||
|
|
||||||
|
|||||||
@ -129,11 +129,11 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage*
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DialogMessageButton boot_version_screen(DialogsApp* dialogs, DialogMessage* message) {
|
static DialogMessageButton bootloader_version_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||||
DialogMessageButton result;
|
DialogMessageButton result;
|
||||||
string_t buffer;
|
string_t buffer;
|
||||||
string_init(buffer);
|
string_init(buffer);
|
||||||
const Version* ver = furi_hal_version_get_boot_version();
|
const Version* ver = furi_hal_version_get_bootloader_version();
|
||||||
|
|
||||||
if(!ver) {
|
if(!ver) {
|
||||||
string_cat_printf(buffer, "No info\n");
|
string_cat_printf(buffer, "No info\n");
|
||||||
@ -167,7 +167,7 @@ const AboutDialogScreen about_screens[] = {
|
|||||||
icon2_screen,
|
icon2_screen,
|
||||||
hw_version_screen,
|
hw_version_screen,
|
||||||
fw_version_screen,
|
fw_version_screen,
|
||||||
boot_version_screen};
|
bootloader_version_screen};
|
||||||
|
|
||||||
const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
|
const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
|
||||||
|
|
||||||
|
|||||||
@ -65,55 +65,55 @@ extern int32_t power_settings_app(void* p);
|
|||||||
const FlipperApplication FLIPPER_SERVICES[] = {
|
const FlipperApplication FLIPPER_SERVICES[] = {
|
||||||
/* Services */
|
/* Services */
|
||||||
#ifdef SRV_RPC
|
#ifdef SRV_RPC
|
||||||
{.app = rpc_srv, .name = "RPC", .stack_size = 1024 * 4, .icon = NULL},
|
{.app = rpc_srv, .name = "RpcSrv", .stack_size = 1024 * 4, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_BT
|
#ifdef SRV_BT
|
||||||
{.app = bt_srv, .name = "BT", .stack_size = 1024, .icon = NULL},
|
{.app = bt_srv, .name = "BtSrv", .stack_size = 1024, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
{.app = cli_srv, .name = "Cli", .stack_size = 4096, .icon = NULL},
|
{.app = cli_srv, .name = "CliSrv", .stack_size = 4096, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_DIALOGS
|
#ifdef SRV_DIALOGS
|
||||||
{.app = dialogs_srv, .name = "Dialogs", .stack_size = 1024, .icon = NULL},
|
{.app = dialogs_srv, .name = "DialogsSrv", .stack_size = 1024, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_DOLPHIN
|
#ifdef SRV_DOLPHIN
|
||||||
{.app = dolphin_srv, .name = "Dolphin", .stack_size = 1024, .icon = NULL},
|
{.app = dolphin_srv, .name = "DolphinSrv", .stack_size = 1024, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_DESKTOP
|
#ifdef SRV_DESKTOP
|
||||||
{.app = desktop_srv, .name = "Desktop", .stack_size = 1024, .icon = NULL},
|
{.app = desktop_srv, .name = "DesktopSrv", .stack_size = 2048, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_GUI
|
#ifdef SRV_GUI
|
||||||
{.app = gui_srv, .name = "Gui", .stack_size = 8192, .icon = NULL},
|
{.app = gui_srv, .name = "GuiSrv", .stack_size = 2048, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_INPUT
|
#ifdef SRV_INPUT
|
||||||
{.app = input_srv, .name = "Input", .stack_size = 1024, .icon = NULL},
|
{.app = input_srv, .name = "InputSrv", .stack_size = 1024, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_LOADER
|
#ifdef SRV_LOADER
|
||||||
{.app = loader_srv, .name = "Loader", .stack_size = 1024, .icon = NULL},
|
{.app = loader_srv, .name = "LoaderSrv", .stack_size = 1024, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_NOTIFICATION
|
#ifdef SRV_NOTIFICATION
|
||||||
{.app = notification_srv, .name = "Notification", .stack_size = 1024, .icon = NULL},
|
{.app = notification_srv, .name = "NotificationSrv", .stack_size = 1536, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_POWER
|
#ifdef SRV_POWER
|
||||||
{.app = power_srv, .name = "Power", .stack_size = 1024, .icon = NULL},
|
{.app = power_srv, .name = "PowerSrv", .stack_size = 1024, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_POWER_OBSERVER
|
#ifdef SRV_POWER_OBSERVER
|
||||||
{.app = power_observer_srv, .name = "PowerObserver", .stack_size = 1024, .icon = NULL},
|
{.app = power_observer_srv, .name = "PowerAuditSrv", .stack_size = 1024, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_STORAGE
|
#ifdef SRV_STORAGE
|
||||||
{.app = storage_srv, .name = "Storage", .stack_size = 4096, .icon = NULL},
|
{.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -231,6 +231,10 @@ endif
|
|||||||
SRV_RPC ?= 0
|
SRV_RPC ?= 0
|
||||||
ifeq ($(SRV_RPC), 1)
|
ifeq ($(SRV_RPC), 1)
|
||||||
CFLAGS += -DSRV_RPC
|
CFLAGS += -DSRV_RPC
|
||||||
|
ifeq ($(SRV_RPC_DEBUG), 1)
|
||||||
|
CFLAGS += -DSRV_RPC_DEBUG
|
||||||
|
endif
|
||||||
|
SRV_CLI = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SRV_LOADER ?= 0
|
SRV_LOADER ?= 0
|
||||||
|
|||||||
@ -104,7 +104,7 @@ void archive_file_array_swap(ArchiveBrowserView* browser, int8_t d) {
|
|||||||
void archive_file_array_rm_all(ArchiveBrowserView* browser) {
|
void archive_file_array_rm_all(ArchiveBrowserView* browser) {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
browser->view, (ArchiveBrowserViewModel * model) {
|
browser->view, (ArchiveBrowserViewModel * model) {
|
||||||
files_array_clean(model->files);
|
files_array_reset(model->files);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ bool archive_favorites_read(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
archive_add_item(browser, &file_info, string_get_cstr(buffer));
|
archive_add_item(browser, &file_info, string_get_cstr(buffer));
|
||||||
string_clean(buffer);
|
string_reset(buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
string_clear(buffer);
|
string_clear(buffer);
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
#include "archive_files.h"
|
#include "archive_files.h"
|
||||||
#include "archive_browser.h"
|
#include "archive_browser.h"
|
||||||
|
|
||||||
|
#define TAG "Archive"
|
||||||
|
|
||||||
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) {
|
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) {
|
||||||
furi_assert(file_info);
|
furi_assert(file_info);
|
||||||
furi_assert(tab_ext);
|
furi_assert(tab_ext);
|
||||||
@ -147,11 +149,11 @@ void archive_file_append(const char* path, const char* format, ...) {
|
|||||||
FileWorker* file_worker = file_worker_alloc(false);
|
FileWorker* file_worker = file_worker_alloc(false);
|
||||||
|
|
||||||
if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||||
FURI_LOG_E("Archive", "Append open error");
|
FURI_LOG_E(TAG, "Append open error");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) {
|
if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) {
|
||||||
FURI_LOG_E("Archive", "Append write error");
|
FURI_LOG_E(TAG, "Append write error");
|
||||||
}
|
}
|
||||||
|
|
||||||
file_worker_close(file_worker);
|
file_worker_close(file_worker);
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
#include "bt_i.h"
|
#include "bt_i.h"
|
||||||
#include "battery_service.h"
|
#include "battery_service.h"
|
||||||
|
#include "bt_keys_storage.h"
|
||||||
|
|
||||||
#define BT_SERVICE_TAG "BT"
|
#define TAG "BtSrv"
|
||||||
|
|
||||||
static void bt_draw_statusbar_callback(Canvas* canvas, void* context) {
|
static void bt_draw_statusbar_callback(Canvas* canvas, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
@ -69,8 +70,8 @@ Bt* bt_alloc() {
|
|||||||
|
|
||||||
// Power
|
// Power
|
||||||
bt->power = furi_record_open("power");
|
bt->power = furi_record_open("power");
|
||||||
PubSub* power_pubsub = power_get_pubsub(bt->power);
|
FuriPubSub* power_pubsub = power_get_pubsub(bt->power);
|
||||||
subscribe_pubsub(power_pubsub, bt_battery_level_changed_callback, bt);
|
furi_pubsub_subscribe(power_pubsub, bt_battery_level_changed_callback, bt);
|
||||||
|
|
||||||
// RPC
|
// RPC
|
||||||
bt->rpc = furi_record_open("rpc");
|
bt->rpc = furi_record_open("rpc");
|
||||||
@ -80,14 +81,15 @@ Bt* bt_alloc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called from GAP thread from Serial service
|
// Called from GAP thread from Serial service
|
||||||
static void bt_on_data_received_callback(uint8_t* data, uint16_t size, void* context) {
|
static uint16_t bt_on_data_received_callback(uint8_t* data, uint16_t size, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
Bt* bt = context;
|
Bt* bt = context;
|
||||||
|
|
||||||
size_t bytes_processed = rpc_feed_bytes(bt->rpc_session, data, size, 1000);
|
size_t bytes_processed = rpc_session_feed(bt->rpc_session, data, size, 1000);
|
||||||
if(bytes_processed != size) {
|
if(bytes_processed != size) {
|
||||||
FURI_LOG_E(BT_SERVICE_TAG, "Only %d of %d bytes processed by RPC", bytes_processed, size);
|
FURI_LOG_E(TAG, "Only %d of %d bytes processed by RPC", bytes_processed, size);
|
||||||
}
|
}
|
||||||
|
return rpc_session_get_available_size(bt->rpc_session);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called from GAP thread from Serial service
|
// Called from GAP thread from Serial service
|
||||||
@ -117,6 +119,11 @@ static void bt_rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t byt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bt_rpc_buffer_is_empty_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
furi_hal_bt_notify_buffer_is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
// Called from GAP thread
|
// Called from GAP thread
|
||||||
static void bt_on_gap_event_callback(BleEvent event, void* context) {
|
static void bt_on_gap_event_callback(BleEvent event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
@ -128,11 +135,13 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
|
|||||||
BtMessage message = {.type = BtMessageTypeUpdateStatusbar};
|
BtMessage message = {.type = BtMessageTypeUpdateStatusbar};
|
||||||
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
|
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
|
||||||
// Open RPC session
|
// Open RPC session
|
||||||
FURI_LOG_I(BT_SERVICE_TAG, "Open RPC connection");
|
FURI_LOG_I(TAG, "Open RPC connection");
|
||||||
bt->rpc_session = rpc_open_session(bt->rpc);
|
bt->rpc_session = rpc_session_open(bt->rpc);
|
||||||
rpc_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback, bt);
|
rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback);
|
||||||
|
rpc_session_set_buffer_is_empty_callback(bt->rpc_session, bt_rpc_buffer_is_empty_callback);
|
||||||
|
rpc_session_set_context(bt->rpc_session, bt);
|
||||||
furi_hal_bt_set_data_event_callbacks(
|
furi_hal_bt_set_data_event_callbacks(
|
||||||
bt_on_data_received_callback, bt_on_data_sent_callback, bt);
|
RPC_BUFFER_SIZE, bt_on_data_received_callback, bt_on_data_sent_callback, bt);
|
||||||
// Update battery level
|
// Update battery level
|
||||||
PowerInfo info;
|
PowerInfo info;
|
||||||
power_get_info(bt->power, &info);
|
power_get_info(bt->power, &info);
|
||||||
@ -140,9 +149,9 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
|
|||||||
message.data.battery_level = info.charge;
|
message.data.battery_level = info.charge;
|
||||||
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
|
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
|
||||||
} else if(event.type == BleEventTypeDisconnected) {
|
} else if(event.type == BleEventTypeDisconnected) {
|
||||||
FURI_LOG_I(BT_SERVICE_TAG, "Close RPC connection");
|
FURI_LOG_I(TAG, "Close RPC connection");
|
||||||
if(bt->rpc_session) {
|
if(bt->rpc_session) {
|
||||||
rpc_close_session(bt->rpc_session);
|
rpc_session_close(bt->rpc_session);
|
||||||
bt->rpc_session = NULL;
|
bt->rpc_session = NULL;
|
||||||
}
|
}
|
||||||
} else if(event.type == BleEventTypeStartAdvertising) {
|
} else if(event.type == BleEventTypeStartAdvertising) {
|
||||||
@ -160,6 +169,14 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
Bt* bt = context;
|
||||||
|
FURI_LOG_I(TAG, "Changed addr start: %08lX, size changed: %d", addr, size);
|
||||||
|
BtMessage message = {.type = BtMessageTypeKeysStorageUpdated};
|
||||||
|
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
|
||||||
|
}
|
||||||
|
|
||||||
static void bt_statusbar_update(Bt* bt) {
|
static void bt_statusbar_update(Bt* bt) {
|
||||||
if(bt->status == BtStatusAdvertising) {
|
if(bt->status == BtStatusAdvertising) {
|
||||||
view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_5x8));
|
view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_5x8));
|
||||||
@ -176,19 +193,26 @@ int32_t bt_srv() {
|
|||||||
Bt* bt = bt_alloc();
|
Bt* bt = bt_alloc();
|
||||||
furi_record_create("bt", bt);
|
furi_record_create("bt", bt);
|
||||||
|
|
||||||
if(!furi_hal_bt_wait_startup()) {
|
// Read keys
|
||||||
FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed");
|
if(!bt_load_key_storage(bt)) {
|
||||||
|
FURI_LOG_W(TAG, "Failed to load saved bonding keys");
|
||||||
|
}
|
||||||
|
// Start 2nd core
|
||||||
|
if(!furi_hal_bt_start_core2()) {
|
||||||
|
FURI_LOG_E(TAG, "Core2 startup failed");
|
||||||
} else {
|
} else {
|
||||||
view_port_enabled_set(bt->statusbar_view_port, true);
|
view_port_enabled_set(bt->statusbar_view_port, true);
|
||||||
if(furi_hal_bt_init_app(bt_on_gap_event_callback, bt)) {
|
if(furi_hal_bt_init_app(bt_on_gap_event_callback, bt)) {
|
||||||
FURI_LOG_I(BT_SERVICE_TAG, "BLE stack started");
|
FURI_LOG_I(TAG, "BLE stack started");
|
||||||
if(bt->bt_settings.enabled) {
|
if(bt->bt_settings.enabled) {
|
||||||
furi_hal_bt_start_advertising();
|
furi_hal_bt_start_advertising();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(BT_SERVICE_TAG, "BT App start failed");
|
FURI_LOG_E(TAG, "BT App start failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt);
|
||||||
|
|
||||||
// Update statusbar
|
// Update statusbar
|
||||||
bt_statusbar_update(bt);
|
bt_statusbar_update(bt);
|
||||||
|
|
||||||
@ -206,6 +230,8 @@ int32_t bt_srv() {
|
|||||||
} else if(message.type == BtMessageTypePinCodeShow) {
|
} else if(message.type == BtMessageTypePinCodeShow) {
|
||||||
// Display PIN code
|
// Display PIN code
|
||||||
bt_pin_code_show_event_handler(bt, message.data.pin_code);
|
bt_pin_code_show_event_handler(bt, message.data.pin_code);
|
||||||
|
} else if(message.type == BtMessageTypeKeysStorageUpdated) {
|
||||||
|
bt_save_key_storage(bt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -25,6 +25,7 @@ typedef enum {
|
|||||||
BtMessageTypeUpdateStatusbar,
|
BtMessageTypeUpdateStatusbar,
|
||||||
BtMessageTypeUpdateBatteryLevel,
|
BtMessageTypeUpdateBatteryLevel,
|
||||||
BtMessageTypePinCodeShow,
|
BtMessageTypePinCodeShow,
|
||||||
|
BtMessageTypeKeysStorageUpdated,
|
||||||
} BtMessageType;
|
} BtMessageType;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
@ -38,6 +39,8 @@ typedef struct {
|
|||||||
} BtMessage;
|
} BtMessage;
|
||||||
|
|
||||||
struct Bt {
|
struct Bt {
|
||||||
|
uint8_t* bt_keys_addr_start;
|
||||||
|
uint16_t bt_keys_size;
|
||||||
BtSettings bt_settings;
|
BtSettings bt_settings;
|
||||||
BtStatus status;
|
BtStatus status;
|
||||||
osMessageQueueId_t message_queue;
|
osMessageQueueId_t message_queue;
|
||||||
|
|||||||
41
applications/bt/bt_service/bt_keys_storage.c
Normal file
41
applications/bt/bt_service/bt_keys_storage.c
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include "bt_keys_storage.h"
|
||||||
|
#include <furi.h>
|
||||||
|
#include <file-worker.h>
|
||||||
|
|
||||||
|
#define BT_KEYS_STORAGE_TAG "bt keys storage"
|
||||||
|
#define BT_KEYS_STORAGE_PATH "/int/bt.keys"
|
||||||
|
|
||||||
|
bool bt_load_key_storage(Bt* bt) {
|
||||||
|
furi_assert(bt);
|
||||||
|
|
||||||
|
bool file_loaded = false;
|
||||||
|
furi_hal_bt_get_key_storage_buff(&bt->bt_keys_addr_start, &bt->bt_keys_size);
|
||||||
|
|
||||||
|
FileWorker* file_worker = file_worker_alloc(true);
|
||||||
|
if(file_worker_open(file_worker, BT_KEYS_STORAGE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||||
|
furi_hal_bt_nvm_sram_sem_acquire();
|
||||||
|
if(file_worker_read(file_worker, bt->bt_keys_addr_start, bt->bt_keys_size)) {
|
||||||
|
file_loaded = true;
|
||||||
|
}
|
||||||
|
furi_hal_bt_nvm_sram_sem_release();
|
||||||
|
}
|
||||||
|
file_worker_free(file_worker);
|
||||||
|
return file_loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bt_save_key_storage(Bt* bt) {
|
||||||
|
furi_assert(bt);
|
||||||
|
furi_assert(bt->bt_keys_addr_start);
|
||||||
|
|
||||||
|
bool file_saved = false;
|
||||||
|
FileWorker* file_worker = file_worker_alloc(true);
|
||||||
|
if(file_worker_open(file_worker, BT_KEYS_STORAGE_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
|
||||||
|
furi_hal_bt_nvm_sram_sem_acquire();
|
||||||
|
if(file_worker_write(file_worker, bt->bt_keys_addr_start, bt->bt_keys_size)) {
|
||||||
|
file_saved = true;
|
||||||
|
}
|
||||||
|
furi_hal_bt_nvm_sram_sem_release();
|
||||||
|
}
|
||||||
|
file_worker_free(file_worker);
|
||||||
|
return file_saved;
|
||||||
|
}
|
||||||
7
applications/bt/bt_service/bt_keys_storage.h
Normal file
7
applications/bt/bt_service/bt_keys_storage.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "bt_i.h"
|
||||||
|
|
||||||
|
bool bt_load_key_storage(Bt* bt);
|
||||||
|
|
||||||
|
bool bt_save_key_storage(Bt* bt);
|
||||||
@ -2,7 +2,7 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <file-worker.h>
|
#include <file-worker.h>
|
||||||
|
|
||||||
#define BT_SETTINGS_TAG "bt settings"
|
#define TAG "BtSettings"
|
||||||
#define BT_SETTINGS_PATH "/int/bt.settings"
|
#define BT_SETTINGS_PATH "/int/bt.settings"
|
||||||
|
|
||||||
bool bt_settings_load(BtSettings* bt_settings) {
|
bool bt_settings_load(BtSettings* bt_settings) {
|
||||||
@ -10,7 +10,7 @@ bool bt_settings_load(BtSettings* bt_settings) {
|
|||||||
bool file_loaded = false;
|
bool file_loaded = false;
|
||||||
BtSettings settings = {};
|
BtSettings settings = {};
|
||||||
|
|
||||||
FURI_LOG_I(BT_SETTINGS_TAG, "Loading settings from \"%s\"", BT_SETTINGS_PATH);
|
FURI_LOG_I(TAG, "Loading settings from \"%s\"", BT_SETTINGS_PATH);
|
||||||
FileWorker* file_worker = file_worker_alloc(true);
|
FileWorker* file_worker = file_worker_alloc(true);
|
||||||
if(file_worker_open(file_worker, BT_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
if(file_worker_open(file_worker, BT_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||||
if(file_worker_read(file_worker, &settings, sizeof(settings))) {
|
if(file_worker_read(file_worker, &settings, sizeof(settings))) {
|
||||||
@ -20,16 +20,16 @@ bool bt_settings_load(BtSettings* bt_settings) {
|
|||||||
file_worker_free(file_worker);
|
file_worker_free(file_worker);
|
||||||
|
|
||||||
if(file_loaded) {
|
if(file_loaded) {
|
||||||
FURI_LOG_I(BT_SETTINGS_TAG, "Settings load success");
|
FURI_LOG_I(TAG, "Settings load success");
|
||||||
if(settings.version != BT_SETTINGS_VERSION) {
|
if(settings.version != BT_SETTINGS_VERSION) {
|
||||||
FURI_LOG_E(BT_SETTINGS_TAG, "Settings version mismatch");
|
FURI_LOG_E(TAG, "Settings version mismatch");
|
||||||
} else {
|
} else {
|
||||||
osKernelLock();
|
osKernelLock();
|
||||||
*bt_settings = settings;
|
*bt_settings = settings;
|
||||||
osKernelUnlock();
|
osKernelUnlock();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(BT_SETTINGS_TAG, "Settings load failed");
|
FURI_LOG_E(TAG, "Settings load failed");
|
||||||
}
|
}
|
||||||
return file_loaded;
|
return file_loaded;
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ bool bt_settings_save(BtSettings* bt_settings) {
|
|||||||
FileWorker* file_worker = file_worker_alloc(true);
|
FileWorker* file_worker = file_worker_alloc(true);
|
||||||
if(file_worker_open(file_worker, BT_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
|
if(file_worker_open(file_worker, BT_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
|
||||||
if(file_worker_write(file_worker, bt_settings, sizeof(BtSettings))) {
|
if(file_worker_write(file_worker, bt_settings, sizeof(BtSettings))) {
|
||||||
FURI_LOG_I(BT_SETTINGS_TAG, "Settings saved to \"%s\"", BT_SETTINGS_PATH);
|
FURI_LOG_I(TAG, "Settings saved to \"%s\"", BT_SETTINGS_PATH);
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -263,7 +263,7 @@ static void cli_handle_autocomplete(Cli* cli) {
|
|||||||
cli->cursor_position = string_size(cli->line);
|
cli->cursor_position = string_size(cli->line);
|
||||||
}
|
}
|
||||||
// Cleanup
|
// Cleanup
|
||||||
string_clean(common);
|
string_clear(common);
|
||||||
// Show prompt
|
// Show prompt
|
||||||
cli_prompt(cli);
|
cli_prompt(cli);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,7 @@ static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_KEY_SLOTS][ENC
|
|||||||
*/
|
*/
|
||||||
void cli_command_device_info(Cli* cli, string_t args, void* context) {
|
void cli_command_device_info(Cli* cli, string_t args, void* context) {
|
||||||
// Device Info version
|
// Device Info version
|
||||||
printf("device_info_major : %d\r\n", 1);
|
printf("device_info_major : %d\r\n", 2);
|
||||||
printf("device_info_minor : %d\r\n", 0);
|
printf("device_info_minor : %d\r\n", 0);
|
||||||
// Model name
|
// Model name
|
||||||
printf("hardware_model : %s\r\n", furi_hal_version_get_model_name());
|
printf("hardware_model : %s\r\n", furi_hal_version_get_model_name());
|
||||||
@ -89,14 +89,14 @@ void cli_command_device_info(Cli* cli, string_t args, void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bootloader Version
|
// Bootloader Version
|
||||||
const Version* boot_version = furi_hal_version_get_boot_version();
|
const Version* bootloader_version = furi_hal_version_get_bootloader_version();
|
||||||
if(boot_version) {
|
if(bootloader_version) {
|
||||||
printf("boot_commit : %s\r\n", version_get_githash(boot_version));
|
printf("bootloader_commit : %s\r\n", version_get_githash(bootloader_version));
|
||||||
printf("boot_branch : %s\r\n", version_get_gitbranch(boot_version));
|
printf("bootloader_branch : %s\r\n", version_get_gitbranch(bootloader_version));
|
||||||
printf("boot_branch_num : %s\r\n", version_get_gitbranchnum(boot_version));
|
printf("bootloader_branch_num : %s\r\n", version_get_gitbranchnum(bootloader_version));
|
||||||
printf("boot_version : %s\r\n", version_get_version(boot_version));
|
printf("bootloader_version : %s\r\n", version_get_version(bootloader_version));
|
||||||
printf("boot_build_date : %s\r\n", version_get_builddate(boot_version));
|
printf("bootloader_build_date : %s\r\n", version_get_builddate(bootloader_version));
|
||||||
printf("boot_target : %d\r\n", version_get_target(boot_version));
|
printf("bootloader_target : %d\r\n", version_get_target(bootloader_version));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firmware version
|
// Firmware version
|
||||||
@ -350,7 +350,7 @@ void cli_command_gpio_set(Cli* cli, string_t args, void* context) {
|
|||||||
"PA4",
|
"PA4",
|
||||||
"PA6",
|
"PA6",
|
||||||
"PA7",
|
"PA7",
|
||||||
#ifdef DEBUG
|
#ifdef FURI_DEBUG
|
||||||
"PA0",
|
"PA0",
|
||||||
"PB7",
|
"PB7",
|
||||||
"PB8",
|
"PB8",
|
||||||
@ -366,7 +366,7 @@ void cli_command_gpio_set(Cli* cli, string_t args, void* context) {
|
|||||||
{.port = GPIOA, .pin = LL_GPIO_PIN_4},
|
{.port = GPIOA, .pin = LL_GPIO_PIN_4},
|
||||||
{.port = GPIOA, .pin = LL_GPIO_PIN_6},
|
{.port = GPIOA, .pin = LL_GPIO_PIN_6},
|
||||||
{.port = GPIOA, .pin = LL_GPIO_PIN_7},
|
{.port = GPIOA, .pin = LL_GPIO_PIN_7},
|
||||||
#ifdef DEBUG
|
#ifdef FURI_DEBUG
|
||||||
{.port = GPIOA, .pin = LL_GPIO_PIN_0}, // IR_RX (PA0)
|
{.port = GPIOA, .pin = LL_GPIO_PIN_0}, // IR_RX (PA0)
|
||||||
{.port = GPIOB, .pin = LL_GPIO_PIN_7}, // UART RX (PB7)
|
{.port = GPIOB, .pin = LL_GPIO_PIN_7}, // UART RX (PB7)
|
||||||
{.port = GPIOB, .pin = LL_GPIO_PIN_8}, // SPEAKER (PB8)
|
{.port = GPIOB, .pin = LL_GPIO_PIN_8}, // SPEAKER (PB8)
|
||||||
@ -411,7 +411,7 @@ void cli_command_gpio_set(Cli* cli, string_t args, void* context) {
|
|||||||
LL_GPIO_SetPinOutputType(gpio[num].port, gpio[num].pin, LL_GPIO_OUTPUT_PUSHPULL);
|
LL_GPIO_SetPinOutputType(gpio[num].port, gpio[num].pin, LL_GPIO_OUTPUT_PUSHPULL);
|
||||||
LL_GPIO_ResetOutputPin(gpio[num].port, gpio[num].pin);
|
LL_GPIO_ResetOutputPin(gpio[num].port, gpio[num].pin);
|
||||||
} else if(!string_cmp(args, "1")) {
|
} else if(!string_cmp(args, "1")) {
|
||||||
#ifdef DEBUG
|
#ifdef FURI_DEBUG
|
||||||
if(num == 8) { // PA0
|
if(num == 8) { // PA0
|
||||||
printf(
|
printf(
|
||||||
"Setting PA0 pin HIGH with TSOP connected can damage IR receiver. Are you sure you want to continue? (y/n)?\r\n");
|
"Setting PA0 pin HIGH with TSOP connected can damage IR receiver. Are you sure you want to continue? (y/n)?\r\n");
|
||||||
|
|||||||
@ -6,6 +6,9 @@
|
|||||||
#include <furi-hal-usb-hid.h>
|
#include <furi-hal-usb-hid.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
|
|
||||||
|
#define TAG "BadUsb"
|
||||||
|
#define WORKER_TAG TAG "Worker"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EventTypeInput,
|
EventTypeInput,
|
||||||
EventTypeWorkerState,
|
EventTypeWorkerState,
|
||||||
@ -191,7 +194,7 @@ static bool ducky_parse_line(string_t line, BadUsbParams* app) {
|
|||||||
|
|
||||||
static void badusb_worker(void* context) {
|
static void badusb_worker(void* context) {
|
||||||
BadUsbParams* app = context;
|
BadUsbParams* app = context;
|
||||||
FURI_LOG_I("BadUSB worker", "Init");
|
FURI_LOG_I(WORKER_TAG, "Init");
|
||||||
File* script_file = storage_file_alloc(furi_record_open("storage"));
|
File* script_file = storage_file_alloc(furi_record_open("storage"));
|
||||||
BadUsbEvent evt;
|
BadUsbEvent evt;
|
||||||
string_t line;
|
string_t line;
|
||||||
@ -203,7 +206,7 @@ static void badusb_worker(void* context) {
|
|||||||
uint32_t flags =
|
uint32_t flags =
|
||||||
osThreadFlagsWait(WorkerCmdStart | WorkerCmdStop, osFlagsWaitAny, osWaitForever);
|
osThreadFlagsWait(WorkerCmdStart | WorkerCmdStop, osFlagsWaitAny, osWaitForever);
|
||||||
if(flags & WorkerCmdStart) {
|
if(flags & WorkerCmdStart) {
|
||||||
FURI_LOG_I("BadUSB worker", "Start");
|
FURI_LOG_I(WORKER_TAG, "Start");
|
||||||
do {
|
do {
|
||||||
ret = storage_file_read(script_file, buffer, 16);
|
ret = storage_file_read(script_file, buffer, 16);
|
||||||
for(uint16_t i = 0; i < ret; i++) {
|
for(uint16_t i = 0; i < ret; i++) {
|
||||||
@ -211,7 +214,7 @@ static void badusb_worker(void* context) {
|
|||||||
line_cnt++;
|
line_cnt++;
|
||||||
if(ducky_parse_line(line, app) == false) {
|
if(ducky_parse_line(line, app) == false) {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
FURI_LOG_E("BadUSB worker", "Unknown command at line %lu", line_cnt);
|
FURI_LOG_E(WORKER_TAG, "Unknown command at line %lu", line_cnt);
|
||||||
evt.type = EventTypeWorkerState;
|
evt.type = EventTypeWorkerState;
|
||||||
evt.worker.state = WorkerStateScriptError;
|
evt.worker.state = WorkerStateScriptError;
|
||||||
evt.worker.line = line_cnt;
|
evt.worker.line = line_cnt;
|
||||||
@ -223,7 +226,7 @@ static void badusb_worker(void* context) {
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
string_clean(line);
|
string_reset(line);
|
||||||
} else {
|
} else {
|
||||||
string_push_back(line, buffer[i]);
|
string_push_back(line, buffer[i]);
|
||||||
}
|
}
|
||||||
@ -231,19 +234,19 @@ static void badusb_worker(void* context) {
|
|||||||
} while(ret > 0);
|
} while(ret > 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E("BadUSB worker", "Script file open error");
|
FURI_LOG_E(WORKER_TAG, "Script file open error");
|
||||||
evt.type = EventTypeWorkerState;
|
evt.type = EventTypeWorkerState;
|
||||||
evt.worker.state = WorkerStateNoFile;
|
evt.worker.state = WorkerStateNoFile;
|
||||||
osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
|
osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
|
||||||
}
|
}
|
||||||
string_clean(line);
|
string_reset(line);
|
||||||
string_clear(line);
|
string_clear(line);
|
||||||
|
|
||||||
furi_hal_hid_kb_release_all();
|
furi_hal_hid_kb_release_all();
|
||||||
storage_file_close(script_file);
|
storage_file_close(script_file);
|
||||||
storage_file_free(script_file);
|
storage_file_free(script_file);
|
||||||
|
|
||||||
FURI_LOG_I("BadUSB worker", "End");
|
FURI_LOG_I(WORKER_TAG, "End");
|
||||||
evt.type = EventTypeWorkerState;
|
evt.type = EventTypeWorkerState;
|
||||||
evt.worker.state = WorkerStateDone;
|
evt.worker.state = WorkerStateDone;
|
||||||
osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
|
osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
|
||||||
@ -324,7 +327,7 @@ int32_t bad_usb_app(void* p) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(event.type == EventTypeWorkerState) {
|
} else if(event.type == EventTypeWorkerState) {
|
||||||
FURI_LOG_I("BadUSB app", "ev: %d", event.worker.state);
|
FURI_LOG_I(TAG, "ev: %d", event.worker.state);
|
||||||
if(event.worker.state == WorkerStateDone) {
|
if(event.worker.state == WorkerStateDone) {
|
||||||
worker_running = false;
|
worker_running = false;
|
||||||
if(app_state == AppStateExit)
|
if(app_state == AppStateExit)
|
||||||
|
|||||||
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "view_display_test.h"
|
#include "view_display_test.h"
|
||||||
|
|
||||||
|
#define TAG "DisplayTest"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
ViewDispatcher* view_dispatcher;
|
ViewDispatcher* view_dispatcher;
|
||||||
@ -77,7 +79,7 @@ static uint32_t display_test_exit_callback(void* context) {
|
|||||||
|
|
||||||
static void display_test_reload_config(DisplayTest* instance) {
|
static void display_test_reload_config(DisplayTest* instance) {
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(
|
||||||
"DisplayTest",
|
TAG,
|
||||||
"contrast: %d, regulation_ratio: %d, bias: %d",
|
"contrast: %d, regulation_ratio: %d, bias: %d",
|
||||||
instance->config_contrast,
|
instance->config_contrast,
|
||||||
instance->config_regulation_ratio,
|
instance->config_regulation_ratio,
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <input/input.h>
|
#include <input/input.h>
|
||||||
|
|
||||||
|
#define TAG "KeypadTest"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool press[5];
|
bool press[5];
|
||||||
uint16_t up;
|
uint16_t up;
|
||||||
@ -80,7 +82,7 @@ int32_t keypad_test_app(void* p) {
|
|||||||
|
|
||||||
ValueMutex state_mutex;
|
ValueMutex state_mutex;
|
||||||
if(!init_mutex(&state_mutex, &_state, sizeof(KeypadTestState))) {
|
if(!init_mutex(&state_mutex, &_state, sizeof(KeypadTestState))) {
|
||||||
FURI_LOG_E("KeypadTest", "cannot create mutex");
|
FURI_LOG_E(TAG, "cannot create mutex");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +103,7 @@ int32_t keypad_test_app(void* p) {
|
|||||||
if(event_status == osOK) {
|
if(event_status == osOK) {
|
||||||
if(event.type == EventTypeInput) {
|
if(event.type == EventTypeInput) {
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(
|
||||||
"KeypadTest",
|
TAG,
|
||||||
"key: %s type: %s",
|
"key: %s type: %s",
|
||||||
input_get_key_name(event.input.key),
|
input_get_key_name(event.input.key),
|
||||||
input_get_type_name(event.input.type));
|
input_get_type_name(event.input.type));
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#include "desktop_i.h"
|
#include "desktop_i.h"
|
||||||
|
#include <furi-hal-lock.h>
|
||||||
|
|
||||||
static void desktop_lock_icon_callback(Canvas* canvas, void* context) {
|
static void desktop_lock_icon_callback(Canvas* canvas, void* context) {
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
@ -41,6 +42,7 @@ Desktop* desktop_alloc() {
|
|||||||
desktop->debug_view = desktop_debug_alloc();
|
desktop->debug_view = desktop_debug_alloc();
|
||||||
desktop->first_start_view = desktop_first_start_alloc();
|
desktop->first_start_view = desktop_first_start_alloc();
|
||||||
desktop->hw_mismatch_popup = popup_alloc();
|
desktop->hw_mismatch_popup = popup_alloc();
|
||||||
|
desktop->code_input = code_input_alloc();
|
||||||
|
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view));
|
desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view));
|
||||||
@ -62,7 +64,8 @@ Desktop* desktop_alloc() {
|
|||||||
desktop->view_dispatcher,
|
desktop->view_dispatcher,
|
||||||
DesktopViewHwMismatch,
|
DesktopViewHwMismatch,
|
||||||
popup_get_view(desktop->hw_mismatch_popup));
|
popup_get_view(desktop->hw_mismatch_popup));
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
desktop->view_dispatcher, DesktopViewPinSetup, code_input_get_view(desktop->code_input));
|
||||||
// Lock icon
|
// Lock icon
|
||||||
desktop->lock_viewport = view_port_alloc();
|
desktop->lock_viewport = view_port_alloc();
|
||||||
view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8));
|
view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8));
|
||||||
@ -82,6 +85,7 @@ void desktop_free(Desktop* desktop) {
|
|||||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug);
|
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug);
|
||||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart);
|
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart);
|
||||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch);
|
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch);
|
||||||
|
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewPinSetup);
|
||||||
|
|
||||||
view_dispatcher_free(desktop->view_dispatcher);
|
view_dispatcher_free(desktop->view_dispatcher);
|
||||||
scene_manager_free(desktop->scene_manager);
|
scene_manager_free(desktop->scene_manager);
|
||||||
@ -92,6 +96,7 @@ void desktop_free(Desktop* desktop) {
|
|||||||
desktop_debug_free(desktop->debug_view);
|
desktop_debug_free(desktop->debug_view);
|
||||||
desktop_first_start_free(desktop->first_start_view);
|
desktop_first_start_free(desktop->first_start_view);
|
||||||
popup_free(desktop->hw_mismatch_popup);
|
popup_free(desktop->hw_mismatch_popup);
|
||||||
|
code_input_free(desktop->code_input);
|
||||||
|
|
||||||
furi_record_close("gui");
|
furi_record_close("gui");
|
||||||
desktop->gui = NULL;
|
desktop->gui = NULL;
|
||||||
@ -113,9 +118,22 @@ static bool desktop_is_first_start() {
|
|||||||
|
|
||||||
int32_t desktop_srv(void* p) {
|
int32_t desktop_srv(void* p) {
|
||||||
Desktop* desktop = desktop_alloc();
|
Desktop* desktop = desktop_alloc();
|
||||||
|
bool loaded = LOAD_DESKTOP_SETTINGS(&desktop->settings);
|
||||||
|
if(!loaded) {
|
||||||
|
furi_hal_lock_set(false);
|
||||||
|
memset(&desktop->settings, 0, sizeof(desktop->settings));
|
||||||
|
SAVE_DESKTOP_SETTINGS(&desktop->settings);
|
||||||
|
}
|
||||||
|
|
||||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
||||||
|
|
||||||
|
if(furi_hal_lock_get()) {
|
||||||
|
furi_hal_usb_disable();
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
desktop->scene_manager, DesktopSceneLocked, DesktopLockedWithPin);
|
||||||
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
||||||
|
}
|
||||||
|
|
||||||
if(desktop_is_first_start()) {
|
if(desktop_is_first_start()) {
|
||||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart);
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
#include <gui/modules/popup.h>
|
#include <gui/modules/popup.h>
|
||||||
|
#include <gui/modules/code_input.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
@ -29,6 +30,7 @@ typedef enum {
|
|||||||
DesktopViewDebug,
|
DesktopViewDebug,
|
||||||
DesktopViewFirstStart,
|
DesktopViewFirstStart,
|
||||||
DesktopViewHwMismatch,
|
DesktopViewHwMismatch,
|
||||||
|
DesktopViewPinSetup,
|
||||||
DesktopViewTotal,
|
DesktopViewTotal,
|
||||||
} DesktopViewEnum;
|
} DesktopViewEnum;
|
||||||
|
|
||||||
@ -46,7 +48,10 @@ struct Desktop {
|
|||||||
DesktopLockMenuView* lock_menu;
|
DesktopLockMenuView* lock_menu;
|
||||||
DesktopLockedView* locked_view;
|
DesktopLockedView* locked_view;
|
||||||
DesktopDebugView* debug_view;
|
DesktopDebugView* debug_view;
|
||||||
|
CodeInput* code_input;
|
||||||
|
|
||||||
DesktopSettings settings;
|
DesktopSettings settings;
|
||||||
|
PinCode pincode_buffer;
|
||||||
|
|
||||||
ViewPort* lock_viewport;
|
ViewPort* lock_viewport;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
#include <furi.h>
|
|
||||||
#include <file-worker.h>
|
|
||||||
#include "desktop_settings.h"
|
|
||||||
|
|
||||||
#define DESKTOP_SETTINGS_TAG "Desktop settings"
|
|
||||||
#define DESKTOP_SETTINGS_PATH "/int/desktop.settings"
|
|
||||||
|
|
||||||
bool desktop_settings_load(DesktopSettings* desktop_settings) {
|
|
||||||
furi_assert(desktop_settings);
|
|
||||||
bool file_loaded = false;
|
|
||||||
DesktopSettings settings = {};
|
|
||||||
|
|
||||||
FURI_LOG_I(DESKTOP_SETTINGS_TAG, "Loading settings from \"%s\"", DESKTOP_SETTINGS_PATH);
|
|
||||||
FileWorker* file_worker = file_worker_alloc(true);
|
|
||||||
if(file_worker_open(file_worker, DESKTOP_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
|
||||||
if(file_worker_read(file_worker, &settings, sizeof(settings))) {
|
|
||||||
file_loaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_worker_free(file_worker);
|
|
||||||
|
|
||||||
if(file_loaded) {
|
|
||||||
if(settings.version != DESKTOP_SETTINGS_VER) {
|
|
||||||
FURI_LOG_E(DESKTOP_SETTINGS_TAG, "Settings version mismatch");
|
|
||||||
} else {
|
|
||||||
osKernelLock();
|
|
||||||
*desktop_settings = settings;
|
|
||||||
osKernelUnlock();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
FURI_LOG_E(DESKTOP_SETTINGS_TAG, "Settings load failed");
|
|
||||||
}
|
|
||||||
return file_loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool desktop_settings_save(DesktopSettings* desktop_settings) {
|
|
||||||
furi_assert(desktop_settings);
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
FileWorker* file_worker = file_worker_alloc(true);
|
|
||||||
if(file_worker_open(file_worker, DESKTOP_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
|
|
||||||
if(file_worker_write(file_worker, desktop_settings, sizeof(DesktopSettings))) {
|
|
||||||
FURI_LOG_I(DESKTOP_SETTINGS_TAG, "Settings saved to \"%s\"", DESKTOP_SETTINGS_PATH);
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_worker_free(file_worker);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
@ -2,14 +2,35 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <toolbox/saved_struct.h>
|
||||||
|
|
||||||
#define DESKTOP_SETTINGS_VER (0)
|
#define DESKTOP_SETTINGS_VER (1)
|
||||||
|
#define DESKTOP_SETTINGS_PATH "/int/desktop.settings"
|
||||||
|
#define DESKTOP_SETTINGS_MAGIC (0x17)
|
||||||
|
#define PIN_MAX_LENGTH 12
|
||||||
|
|
||||||
|
#define SAVE_DESKTOP_SETTINGS(x) \
|
||||||
|
saved_struct_save( \
|
||||||
|
DESKTOP_SETTINGS_PATH, \
|
||||||
|
(x), \
|
||||||
|
sizeof(DesktopSettings), \
|
||||||
|
DESKTOP_SETTINGS_MAGIC, \
|
||||||
|
DESKTOP_SETTINGS_VER)
|
||||||
|
|
||||||
|
#define LOAD_DESKTOP_SETTINGS(x) \
|
||||||
|
saved_struct_load( \
|
||||||
|
DESKTOP_SETTINGS_PATH, \
|
||||||
|
(x), \
|
||||||
|
sizeof(DesktopSettings), \
|
||||||
|
DESKTOP_SETTINGS_MAGIC, \
|
||||||
|
DESKTOP_SETTINGS_VER)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t data[PIN_MAX_LENGTH];
|
||||||
|
} PinCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t version;
|
|
||||||
uint16_t favorite;
|
uint16_t favorite;
|
||||||
|
PinCode pincode;
|
||||||
} DesktopSettings;
|
} DesktopSettings;
|
||||||
|
|
||||||
bool desktop_settings_load(DesktopSettings* desktop_settings);
|
|
||||||
|
|
||||||
bool desktop_settings_save(DesktopSettings* desktop_settings);
|
|
||||||
|
|||||||
@ -15,9 +15,6 @@ static bool desktop_settings_back_event_callback(void* context) {
|
|||||||
DesktopSettingsApp* desktop_settings_app_alloc() {
|
DesktopSettingsApp* desktop_settings_app_alloc() {
|
||||||
DesktopSettingsApp* app = furi_alloc(sizeof(DesktopSettingsApp));
|
DesktopSettingsApp* app = furi_alloc(sizeof(DesktopSettingsApp));
|
||||||
|
|
||||||
app->settings.version = DESKTOP_SETTINGS_VER;
|
|
||||||
desktop_settings_load(&app->settings);
|
|
||||||
|
|
||||||
app->gui = furi_record_open("gui");
|
app->gui = furi_record_open("gui");
|
||||||
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);
|
||||||
@ -33,10 +30,13 @@ DesktopSettingsApp* desktop_settings_app_alloc() {
|
|||||||
|
|
||||||
app->submenu = submenu_alloc();
|
app->submenu = submenu_alloc();
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
app->view_dispatcher, DesktopSettingsAppViewMain, submenu_get_view(app->submenu));
|
app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu));
|
||||||
|
|
||||||
|
app->code_input = code_input_alloc();
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
app->view_dispatcher, DesktopSettingsAppViewFavorite, submenu_get_view(app->submenu));
|
app->view_dispatcher,
|
||||||
|
DesktopSettingsAppViewPincodeInput,
|
||||||
|
code_input_get_view(app->code_input));
|
||||||
|
|
||||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);
|
||||||
return app;
|
return app;
|
||||||
@ -45,9 +45,10 @@ DesktopSettingsApp* desktop_settings_app_alloc() {
|
|||||||
void desktop_settings_app_free(DesktopSettingsApp* app) {
|
void desktop_settings_app_free(DesktopSettingsApp* app) {
|
||||||
furi_assert(app);
|
furi_assert(app);
|
||||||
// Variable item list
|
// Variable item list
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMain);
|
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewFavorite);
|
|
||||||
submenu_free(app->submenu);
|
submenu_free(app->submenu);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput);
|
||||||
|
code_input_free(app->code_input);
|
||||||
// View dispatcher
|
// View dispatcher
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
scene_manager_free(app->scene_manager);
|
scene_manager_free(app->scene_manager);
|
||||||
@ -58,8 +59,8 @@ void desktop_settings_app_free(DesktopSettingsApp* app) {
|
|||||||
|
|
||||||
extern int32_t desktop_settings_app(void* p) {
|
extern int32_t desktop_settings_app(void* p) {
|
||||||
DesktopSettingsApp* app = desktop_settings_app_alloc();
|
DesktopSettingsApp* app = desktop_settings_app_alloc();
|
||||||
|
LOAD_DESKTOP_SETTINGS(&app->settings);
|
||||||
view_dispatcher_run(app->view_dispatcher);
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
desktop_settings_save(&app->settings);
|
|
||||||
desktop_settings_app_free(app);
|
desktop_settings_app_free(app);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,20 +6,32 @@
|
|||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <gui/modules/submenu.h>
|
#include <gui/modules/submenu.h>
|
||||||
|
#include <gui/modules/code_input.h>
|
||||||
|
|
||||||
#include "desktop_settings.h"
|
#include "desktop_settings.h"
|
||||||
|
|
||||||
#include "scenes/desktop_settings_scene.h"
|
#include "scenes/desktop_settings_scene.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DesktopSettingsAppViewMain,
|
CodeEventsSetPin,
|
||||||
DesktopSettingsAppViewFavorite,
|
CodeEventsChangePin,
|
||||||
|
CodeEventsDisablePin,
|
||||||
|
} CodeEventsEnum;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DesktopSettingsAppViewMenu,
|
||||||
|
DesktopSettingsAppViewPincodeInput,
|
||||||
} DesktopSettingsAppView;
|
} DesktopSettingsAppView;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
DesktopSettings settings;
|
DesktopSettings settings;
|
||||||
|
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
SceneManager* scene_manager;
|
SceneManager* scene_manager;
|
||||||
ViewDispatcher* view_dispatcher;
|
ViewDispatcher* view_dispatcher;
|
||||||
Submenu* submenu;
|
Submenu* submenu;
|
||||||
|
CodeInput* code_input;
|
||||||
|
|
||||||
|
uint8_t menu_idx;
|
||||||
|
|
||||||
} DesktopSettingsApp;
|
} DesktopSettingsApp;
|
||||||
|
|||||||
@ -1,2 +1,4 @@
|
|||||||
ADD_SCENE(desktop_settings, start, Start)
|
ADD_SCENE(desktop_settings, start, Start)
|
||||||
ADD_SCENE(desktop_settings, favorite, Favorite)
|
ADD_SCENE(desktop_settings, favorite, Favorite)
|
||||||
|
ADD_SCENE(desktop_settings, pincode_menu, PinCodeMenu)
|
||||||
|
ADD_SCENE(desktop_settings, pincode_input, PinCodeInput)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "../desktop_settings_app.h"
|
#include "../desktop_settings_app.h"
|
||||||
#include "applications.h"
|
#include "applications.h"
|
||||||
|
#include "desktop/desktop_settings/desktop_settings.h"
|
||||||
|
|
||||||
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;
|
||||||
@ -22,7 +23,7 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
|||||||
|
|
||||||
submenu_set_header(app->submenu, "Quick access app:");
|
submenu_set_header(app->submenu, "Quick access app:");
|
||||||
submenu_set_selected_item(app->submenu, app->settings.favorite);
|
submenu_set_selected_item(app->submenu, app->settings.favorite);
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewFavorite);
|
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) {
|
||||||
@ -43,5 +44,6 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
|
|||||||
|
|
||||||
void desktop_settings_scene_favorite_on_exit(void* context) {
|
void desktop_settings_scene_favorite_on_exit(void* context) {
|
||||||
DesktopSettingsApp* app = context;
|
DesktopSettingsApp* app = context;
|
||||||
|
SAVE_DESKTOP_SETTINGS(&app->settings);
|
||||||
submenu_clean(app->submenu);
|
submenu_clean(app->submenu);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,64 @@
|
|||||||
|
#include "../desktop_settings_app.h"
|
||||||
|
#include "desktop/desktop_settings/desktop_settings.h"
|
||||||
|
|
||||||
|
#define SCENE_EXIT_EVENT (0U)
|
||||||
|
|
||||||
|
void desktop_settings_scene_ok_callback(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
uint32_t state =
|
||||||
|
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
|
||||||
|
if(state == CodeEventsDisablePin) {
|
||||||
|
memset(app->settings.pincode.data, 0, app->settings.pincode.length * sizeof(uint8_t));
|
||||||
|
app->settings.pincode.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_settings_scene_pincode_input_on_enter(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
CodeInput* code_input = app->code_input;
|
||||||
|
|
||||||
|
uint32_t state =
|
||||||
|
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
bool update = state != CodeEventsDisablePin;
|
||||||
|
|
||||||
|
code_input_set_header_text(code_input, "PIN Code Setup");
|
||||||
|
code_input_set_result_callback(
|
||||||
|
code_input,
|
||||||
|
desktop_settings_scene_ok_callback,
|
||||||
|
NULL,
|
||||||
|
app,
|
||||||
|
app->settings.pincode.data,
|
||||||
|
&app->settings.pincode.length,
|
||||||
|
update);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool desktop_settings_scene_pincode_input_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
switch(event.event) {
|
||||||
|
case SCENE_EXIT_EVENT:
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_settings_scene_pincode_input_on_exit(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
SAVE_DESKTOP_SETTINGS(&app->settings);
|
||||||
|
code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0);
|
||||||
|
code_input_set_header_text(app->code_input, "");
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
#include "../desktop_settings_app.h"
|
||||||
|
#include "applications.h"
|
||||||
|
|
||||||
|
static void desktop_settings_scene_pincode_menu_submenu_callback(void* context, uint32_t index) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_settings_scene_pincode_menu_on_enter(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
submenu_clean(submenu);
|
||||||
|
|
||||||
|
if(!app->settings.pincode.length) {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Set Pin",
|
||||||
|
CodeEventsSetPin,
|
||||||
|
desktop_settings_scene_pincode_menu_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Change Pin",
|
||||||
|
CodeEventsChangePin,
|
||||||
|
desktop_settings_scene_pincode_menu_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Disable",
|
||||||
|
CodeEventsDisablePin,
|
||||||
|
desktop_settings_scene_pincode_menu_submenu_callback,
|
||||||
|
app);
|
||||||
|
}
|
||||||
|
|
||||||
|
submenu_set_header(app->submenu, "Pin code settings:");
|
||||||
|
submenu_set_selected_item(app->submenu, app->menu_idx);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
switch(event.event) {
|
||||||
|
case CodeEventsSetPin:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event);
|
||||||
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case CodeEventsChangePin:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event);
|
||||||
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case CodeEventsDisablePin:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event);
|
||||||
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_settings_scene_pincode_menu_on_exit(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
submenu_clean(app->submenu);
|
||||||
|
}
|
||||||
@ -29,7 +29,7 @@ void desktop_settings_scene_start_on_enter(void* context) {
|
|||||||
desktop_settings_scene_start_submenu_callback,
|
desktop_settings_scene_start_submenu_callback,
|
||||||
app);
|
app);
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMain);
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
|
bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||||
@ -39,7 +39,11 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even
|
|||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
switch(event.event) {
|
switch(event.event) {
|
||||||
case DesktopSettingsStartSubmenuIndexFavorite:
|
case DesktopSettingsStartSubmenuIndexFavorite:
|
||||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppViewFavorite);
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case DesktopSettingsStartSubmenuIndexPinSetup:
|
||||||
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeMenu);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#include "desktop_animation.h"
|
#include "desktop_animation.h"
|
||||||
|
|
||||||
|
#define TAG "DesktopAnimation"
|
||||||
|
|
||||||
static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64};
|
static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64};
|
||||||
|
|
||||||
const Icon* desktop_get_icon() {
|
const Icon* desktop_get_icon() {
|
||||||
@ -12,10 +14,10 @@ const Icon* desktop_get_icon() {
|
|||||||
DolphinStats stats = dolphin_stats(dolphin);
|
DolphinStats stats = dolphin_stats(dolphin);
|
||||||
float timediff = fabs(difftime(stats.timestamp, dolphin_state_timestamp()));
|
float timediff = fabs(difftime(stats.timestamp, dolphin_state_timestamp()));
|
||||||
|
|
||||||
FURI_LOG_I("desktop-animation", "background change");
|
FURI_LOG_I(TAG, "background change");
|
||||||
FURI_LOG_I("desktop-animation", "icounter: %d", stats.icounter);
|
FURI_LOG_I(TAG, "icounter: %d", stats.icounter);
|
||||||
FURI_LOG_I("desktop-animation", "butthurt: %d", stats.butthurt);
|
FURI_LOG_I(TAG, "butthurt: %d", stats.butthurt);
|
||||||
FURI_LOG_I("desktop-animation", "time since deeed: %.0f", timediff);
|
FURI_LOG_I(TAG, "time since deeed: %.0f", timediff);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if((random() % 100) > 50) { // temp rnd selection
|
if((random() % 100) > 50) { // temp rnd selection
|
||||||
|
|||||||
@ -4,3 +4,4 @@ ADD_SCENE(desktop, locked, Locked)
|
|||||||
ADD_SCENE(desktop, debug, Debug)
|
ADD_SCENE(desktop, debug, Debug)
|
||||||
ADD_SCENE(desktop, first_start, FirstStart)
|
ADD_SCENE(desktop, first_start, FirstStart)
|
||||||
ADD_SCENE(desktop, hw_mismatch, HwMismatch)
|
ADD_SCENE(desktop, hw_mismatch, HwMismatch)
|
||||||
|
ADD_SCENE(desktop, pinsetup, PinSetup)
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
#include "../desktop_i.h"
|
#include "../desktop_i.h"
|
||||||
#include "../views/desktop_lock_menu.h"
|
#include "../views/desktop_lock_menu.h"
|
||||||
|
#include <toolbox/saved_struct.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <furi-hal-lock.h>
|
||||||
|
|
||||||
void desktop_scene_lock_menu_callback(DesktopLockMenuEvent event, void* context) {
|
void desktop_scene_lock_menu_callback(DesktopLockMenuEvent event, void* context) {
|
||||||
Desktop* desktop = (Desktop*)context;
|
Desktop* desktop = (Desktop*)context;
|
||||||
@ -9,7 +12,10 @@ void desktop_scene_lock_menu_callback(DesktopLockMenuEvent event, void* context)
|
|||||||
void desktop_scene_lock_menu_on_enter(void* context) {
|
void desktop_scene_lock_menu_on_enter(void* context) {
|
||||||
Desktop* desktop = (Desktop*)context;
|
Desktop* desktop = (Desktop*)context;
|
||||||
|
|
||||||
|
LOAD_DESKTOP_SETTINGS(&desktop->settings);
|
||||||
|
|
||||||
desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
|
desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
|
||||||
|
desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pincode.length > 0);
|
||||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu);
|
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,15 +26,29 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
switch(event.event) {
|
switch(event.event) {
|
||||||
case DesktopLockMenuEventLock:
|
case DesktopLockMenuEventLock:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
desktop->scene_manager, DesktopSceneLocked, DesktopLockedNoPin);
|
||||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
|
case DesktopLockMenuEventPinLock:
|
||||||
|
if(desktop->settings.pincode.length > 0) {
|
||||||
|
furi_hal_lock_set(true);
|
||||||
|
furi_hal_usb_disable();
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
desktop->scene_manager, DesktopSceneLocked, DesktopLockedWithPin);
|
||||||
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
||||||
|
} else {
|
||||||
|
scene_manager_next_scene(desktop->scene_manager, DesktopScenePinSetup);
|
||||||
|
}
|
||||||
|
|
||||||
case DesktopLockMenuEventExit:
|
|
||||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
|
case DesktopLockMenuEventExit:
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
desktop->scene_manager, DesktopSceneMain);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "../desktop_i.h"
|
#include "../desktop_i.h"
|
||||||
#include "../views/desktop_locked.h"
|
#include "../views/desktop_locked.h"
|
||||||
|
#include <furi-hal-lock.h>
|
||||||
|
|
||||||
void desktop_scene_locked_callback(DesktopLockedEvent event, void* context) {
|
void desktop_scene_locked_callback(DesktopLockedEvent event, void* context) {
|
||||||
Desktop* desktop = (Desktop*)context;
|
Desktop* desktop = (Desktop*)context;
|
||||||
@ -15,12 +16,39 @@ void desktop_scene_locked_on_enter(void* context) {
|
|||||||
desktop_locked_update_hint_timeout(locked_view);
|
desktop_locked_update_hint_timeout(locked_view);
|
||||||
desktop_locked_set_dolphin_animation(locked_view);
|
desktop_locked_set_dolphin_animation(locked_view);
|
||||||
|
|
||||||
|
uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopViewLocked);
|
||||||
|
|
||||||
|
desktop_locked_with_pin(desktop->locked_view, state == DesktopLockedWithPin);
|
||||||
|
|
||||||
view_port_enabled_set(desktop->lock_viewport, true);
|
view_port_enabled_set(desktop->lock_viewport, true);
|
||||||
osTimerStart(locked_view->timer, 63);
|
osTimerStart(locked_view->timer, 63);
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLocked);
|
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLocked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool desktop_scene_locked_check_pin(Desktop* desktop, DesktopMainEvent event) {
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
size_t length = desktop->pincode_buffer.length;
|
||||||
|
length = code_input_push(desktop->pincode_buffer.data, length, event);
|
||||||
|
desktop->pincode_buffer.length = length;
|
||||||
|
|
||||||
|
match = code_input_compare(
|
||||||
|
desktop->pincode_buffer.data,
|
||||||
|
length,
|
||||||
|
desktop->settings.pincode.data,
|
||||||
|
desktop->settings.pincode.length);
|
||||||
|
|
||||||
|
if(match) {
|
||||||
|
desktop->pincode_buffer.length = 0;
|
||||||
|
furi_hal_usb_enable();
|
||||||
|
furi_hal_lock_set(false);
|
||||||
|
desktop_main_unlocked(desktop->main_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
||||||
Desktop* desktop = (Desktop*)context;
|
Desktop* desktop = (Desktop*)context;
|
||||||
|
|
||||||
@ -36,7 +64,17 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
|||||||
case DesktopLockedEventUpdate:
|
case DesktopLockedEventUpdate:
|
||||||
desktop_locked_manage_redraw(desktop->locked_view);
|
desktop_locked_manage_redraw(desktop->locked_view);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case DesktopLockedEventInputReset:
|
||||||
|
desktop->pincode_buffer.length = 0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
|
if(desktop_scene_locked_check_pin(desktop, event.event)) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
desktop->scene_manager, DesktopSceneMain, DesktopMainEventUnlocked);
|
||||||
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case DesktopMainEventOpenFavorite:
|
case DesktopMainEventOpenFavorite:
|
||||||
desktop_settings_load(&desktop->settings);
|
LOAD_DESKTOP_SETTINGS(&desktop->settings);
|
||||||
desktop_switch_to_app(desktop, &FLIPPER_APPS[desktop->settings.favorite]);
|
desktop_switch_to_app(desktop, &FLIPPER_APPS[desktop->settings.favorite]);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
50
applications/desktop/scenes/desktop_scene_pinsetup.c
Normal file
50
applications/desktop/scenes/desktop_scene_pinsetup.c
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include "../desktop_i.h"
|
||||||
|
|
||||||
|
#define SCENE_EXIT_EVENT (0U)
|
||||||
|
|
||||||
|
void desktop_scene_ok_callback(void* context) {
|
||||||
|
Desktop* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_scene_pinsetup_on_enter(void* context) {
|
||||||
|
Desktop* app = context;
|
||||||
|
CodeInput* code_input = app->code_input;
|
||||||
|
|
||||||
|
code_input_set_result_callback(
|
||||||
|
code_input,
|
||||||
|
desktop_scene_ok_callback,
|
||||||
|
NULL,
|
||||||
|
app,
|
||||||
|
app->settings.pincode.data,
|
||||||
|
&app->settings.pincode.length,
|
||||||
|
true);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopViewPinSetup);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool desktop_scene_pinsetup_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
Desktop* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
switch(event.event) {
|
||||||
|
case SCENE_EXIT_EVENT:
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_scene_pinsetup_on_exit(void* context) {
|
||||||
|
Desktop* app = context;
|
||||||
|
SAVE_DESKTOP_SETTINGS(&app->settings);
|
||||||
|
code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0);
|
||||||
|
code_input_set_header_text(app->code_input, "");
|
||||||
|
}
|
||||||
@ -42,7 +42,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
|||||||
my_name ? my_name : "Unknown");
|
my_name ? my_name : "Unknown");
|
||||||
canvas_draw_str(canvas, 5, 23, buffer);
|
canvas_draw_str(canvas, 5, 23, buffer);
|
||||||
|
|
||||||
ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_boot_version() :
|
ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_bootloader_version() :
|
||||||
furi_hal_version_get_firmware_version();
|
furi_hal_version_get_firmware_version();
|
||||||
|
|
||||||
if(!ver) {
|
if(!ver) {
|
||||||
|
|||||||
@ -12,6 +12,14 @@ void desktop_lock_menu_set_callback(
|
|||||||
lock_menu->context = context;
|
lock_menu->context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set) {
|
||||||
|
with_view_model(
|
||||||
|
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
||||||
|
model->pin_set = pin_is_set;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) {
|
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
||||||
@ -26,6 +34,10 @@ static void lock_menu_callback(void* context, uint8_t index) {
|
|||||||
switch(index) {
|
switch(index) {
|
||||||
case 0: // lock
|
case 0: // lock
|
||||||
lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context);
|
lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context);
|
||||||
|
break;
|
||||||
|
case 1: // lock
|
||||||
|
lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context);
|
||||||
|
break;
|
||||||
default: // wip message
|
default: // wip message
|
||||||
with_view_model(
|
with_view_model(
|
||||||
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
||||||
@ -37,7 +49,7 @@ static void lock_menu_callback(void* context, uint8_t index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void desktop_lock_menu_render(Canvas* canvas, void* model) {
|
void desktop_lock_menu_render(Canvas* canvas, void* model) {
|
||||||
const char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"};
|
const char* Lockmenu_Items[3] = {"Lock", "Lock with PIN", "DUMB mode"};
|
||||||
|
|
||||||
DesktopLockMenuViewModel* m = model;
|
DesktopLockMenuViewModel* m = model;
|
||||||
canvas_clear(canvas);
|
canvas_clear(canvas);
|
||||||
@ -47,13 +59,13 @@ void desktop_lock_menu_render(Canvas* canvas, void* model) {
|
|||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
for(uint8_t i = 0; i < 3; ++i) {
|
for(uint8_t i = 0; i < 3; ++i) {
|
||||||
canvas_draw_str_aligned(
|
const char* str = Lockmenu_Items[i];
|
||||||
canvas,
|
|
||||||
64,
|
if(i == 1 && !m->pin_set) str = "Set PIN";
|
||||||
13 + (i * 17),
|
if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not implemented";
|
||||||
AlignCenter,
|
|
||||||
AlignCenter,
|
canvas_draw_str_aligned(canvas, 64, 13 + (i * 17), AlignCenter, AlignCenter, str);
|
||||||
(m->hint_timeout && m->idx == i && m->idx) ? "Not implemented" : Lockmenu_Items[i]);
|
|
||||||
if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15);
|
if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DesktopLockMenuEventLock,
|
DesktopLockMenuEventLock,
|
||||||
DesktopLockMenuEventUnlock,
|
DesktopLockMenuEventPinLock,
|
||||||
DesktopLockMenuEventExit,
|
DesktopLockMenuEventExit,
|
||||||
} DesktopLockMenuEvent;
|
} DesktopLockMenuEvent;
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ struct DesktopLockMenuView {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t idx;
|
uint8_t idx;
|
||||||
uint8_t hint_timeout;
|
uint8_t hint_timeout;
|
||||||
|
bool pin_set;
|
||||||
} DesktopLockMenuViewModel;
|
} DesktopLockMenuViewModel;
|
||||||
|
|
||||||
void desktop_lock_menu_set_callback(
|
void desktop_lock_menu_set_callback(
|
||||||
@ -35,6 +36,7 @@ void desktop_lock_menu_set_callback(
|
|||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu);
|
View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu);
|
||||||
|
void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set);
|
||||||
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu);
|
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu);
|
||||||
DesktopLockMenuView* desktop_lock_menu_alloc();
|
DesktopLockMenuView* desktop_lock_menu_alloc();
|
||||||
void desktop_lock_menu_free(DesktopLockMenuView* lock_menu);
|
void desktop_lock_menu_free(DesktopLockMenuView* lock_menu);
|
||||||
|
|||||||
@ -80,6 +80,14 @@ void desktop_locked_reset_counter(DesktopLockedView* locked_view) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void desktop_locked_with_pin(DesktopLockedView* locked_view, bool locked) {
|
||||||
|
with_view_model(
|
||||||
|
locked_view->view, (DesktopLockedViewModel * model) {
|
||||||
|
model->pin_lock = locked;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void desktop_locked_render(Canvas* canvas, void* model) {
|
void desktop_locked_render(Canvas* canvas, void* model) {
|
||||||
DesktopLockedViewModel* m = model;
|
DesktopLockedViewModel* m = model;
|
||||||
uint32_t now = osKernelGetTickCount();
|
uint32_t now = osKernelGetTickCount();
|
||||||
@ -100,7 +108,7 @@ void desktop_locked_render(Canvas* canvas, void* model) {
|
|||||||
canvas_set_font(canvas, FontPrimary);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
elements_multiline_text_framed(canvas, 42, 30, "Locked");
|
elements_multiline_text_framed(canvas, 42, 30, "Locked");
|
||||||
|
|
||||||
} else {
|
} else if(!m->pin_lock) {
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49);
|
canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49);
|
||||||
elements_multiline_text(canvas, 65, 20, "To unlock\npress:");
|
elements_multiline_text(canvas, 65, 20, "To unlock\npress:");
|
||||||
@ -116,18 +124,34 @@ View* desktop_locked_get_view(DesktopLockedView* locked_view) {
|
|||||||
bool desktop_locked_input(InputEvent* event, void* context) {
|
bool desktop_locked_input(InputEvent* event, void* context) {
|
||||||
furi_assert(event);
|
furi_assert(event);
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
|
|
||||||
DesktopLockedView* locked_view = context;
|
DesktopLockedView* locked_view = context;
|
||||||
|
|
||||||
|
uint32_t press_time = 0;
|
||||||
|
bool locked_with_pin = false;
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
locked_view->view, (DesktopLockedViewModel * model) {
|
||||||
|
locked_with_pin = model->pin_lock;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
if(event->type == InputTypeShort) {
|
if(event->type == InputTypeShort) {
|
||||||
|
if(locked_with_pin) {
|
||||||
|
press_time = osKernelGetTickCount();
|
||||||
|
|
||||||
|
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT * 3) {
|
||||||
|
locked_view->lock_lastpress = press_time;
|
||||||
|
locked_view->callback(DesktopLockedEventInputReset, locked_view->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
locked_view->callback(event->key, locked_view->context);
|
||||||
|
} else {
|
||||||
desktop_locked_update_hint_timeout(locked_view);
|
desktop_locked_update_hint_timeout(locked_view);
|
||||||
|
|
||||||
if(event->key == InputKeyBack) {
|
if(event->key == InputKeyBack) {
|
||||||
uint32_t press_time = osKernelGetTickCount();
|
press_time = osKernelGetTickCount();
|
||||||
// check if pressed sequentially
|
// check if pressed sequentially
|
||||||
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {
|
if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) {
|
||||||
locked_view->lock_lastpress = press_time;
|
|
||||||
locked_view->lock_count = 0;
|
|
||||||
} else if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) {
|
|
||||||
locked_view->lock_lastpress = press_time;
|
locked_view->lock_lastpress = press_time;
|
||||||
locked_view->lock_count++;
|
locked_view->lock_count++;
|
||||||
}
|
}
|
||||||
@ -138,6 +162,12 @@ bool desktop_locked_input(InputEvent* event, void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {
|
||||||
|
locked_view->lock_lastpress = press_time;
|
||||||
|
locked_view->lock_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
// All events consumed
|
// All events consumed
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,10 +15,16 @@
|
|||||||
#define DOOR_R_POS_MIN 60
|
#define DOOR_R_POS_MIN 60
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DesktopLockedEventUnlock,
|
DesktopLockedEventUnlock = 10U,
|
||||||
DesktopLockedEventUpdate,
|
DesktopLockedEventUpdate = 11U,
|
||||||
|
DesktopLockedEventInputReset = 12U,
|
||||||
} DesktopLockedEvent;
|
} DesktopLockedEvent;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DesktopLockedWithPin,
|
||||||
|
DesktopLockedNoPin,
|
||||||
|
} DesktopLockedSceneState;
|
||||||
|
|
||||||
typedef struct DesktopLockedView DesktopLockedView;
|
typedef struct DesktopLockedView DesktopLockedView;
|
||||||
|
|
||||||
typedef void (*DesktopLockedViewCallback)(DesktopLockedEvent event, void* context);
|
typedef void (*DesktopLockedViewCallback)(DesktopLockedEvent event, void* context);
|
||||||
@ -42,6 +48,7 @@ typedef struct {
|
|||||||
int8_t door_right_x;
|
int8_t door_right_x;
|
||||||
bool animation_seq_end;
|
bool animation_seq_end;
|
||||||
|
|
||||||
|
bool pin_lock;
|
||||||
} DesktopLockedViewModel;
|
} DesktopLockedViewModel;
|
||||||
|
|
||||||
void desktop_locked_set_callback(
|
void desktop_locked_set_callback(
|
||||||
@ -58,5 +65,4 @@ void desktop_locked_manage_redraw(DesktopLockedView* locked_view);
|
|||||||
View* desktop_locked_get_view(DesktopLockedView* locked_view);
|
View* desktop_locked_get_view(DesktopLockedView* locked_view);
|
||||||
DesktopLockedView* desktop_locked_alloc();
|
DesktopLockedView* desktop_locked_alloc();
|
||||||
void desktop_locked_free(DesktopLockedView* locked_view);
|
void desktop_locked_free(DesktopLockedView* locked_view);
|
||||||
void desktop_main_unlocked(DesktopMainView* main_view);
|
void desktop_locked_with_pin(DesktopLockedView* lock_menu, bool locked);
|
||||||
void desktop_main_reset_hint(DesktopMainView* main_view);
|
|
||||||
@ -67,6 +67,7 @@ bool desktop_main_input(InputEvent* event, void* context) {
|
|||||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||||
main_view->callback(DesktopMainEventOpenFavorite, main_view->context);
|
main_view->callback(DesktopMainEventOpenFavorite, main_view->context);
|
||||||
}
|
}
|
||||||
|
|
||||||
desktop_main_reset_hint(main_view);
|
desktop_main_reset_hint(main_view);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -7,12 +7,12 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DesktopMainEventOpenMenu,
|
|
||||||
DesktopMainEventOpenLockMenu,
|
DesktopMainEventOpenLockMenu,
|
||||||
DesktopMainEventOpenDebug,
|
|
||||||
DesktopMainEventUnlocked,
|
|
||||||
DesktopMainEventOpenArchive,
|
DesktopMainEventOpenArchive,
|
||||||
DesktopMainEventOpenFavorite,
|
DesktopMainEventOpenFavorite,
|
||||||
|
DesktopMainEventOpenMenu,
|
||||||
|
DesktopMainEventOpenDebug,
|
||||||
|
DesktopMainEventUnlocked,
|
||||||
} DesktopMainEvent;
|
} DesktopMainEvent;
|
||||||
|
|
||||||
typedef struct DesktopMainView DesktopMainView;
|
typedef struct DesktopMainView DesktopMainView;
|
||||||
@ -37,9 +37,8 @@ void desktop_main_set_callback(
|
|||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
View* desktop_main_get_view(DesktopMainView* main_view);
|
View* desktop_main_get_view(DesktopMainView* main_view);
|
||||||
|
|
||||||
DesktopMainView* desktop_main_alloc();
|
DesktopMainView* desktop_main_alloc();
|
||||||
|
|
||||||
void desktop_main_free(DesktopMainView* main_view);
|
void desktop_main_free(DesktopMainView* main_view);
|
||||||
|
|
||||||
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view);
|
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view);
|
||||||
|
void desktop_main_unlocked(DesktopMainView* main_view);
|
||||||
|
void desktop_main_reset_hint(DesktopMainView* main_view);
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
#include "view-holder.h"
|
#include "view-holder.h"
|
||||||
#include <gui/view_i.h>
|
#include <gui/view_i.h>
|
||||||
|
|
||||||
|
#define TAG "ViewHolder"
|
||||||
|
|
||||||
struct ViewHolder {
|
struct ViewHolder {
|
||||||
View* view;
|
View* view;
|
||||||
ViewPort* view_port;
|
ViewPort* view_port;
|
||||||
@ -125,7 +127,7 @@ static void view_holder_input_callback(InputEvent* event, void* context) {
|
|||||||
view_holder->ongoing_input &= ~key_bit;
|
view_holder->ongoing_input &= ~key_bit;
|
||||||
} else if(!(view_holder->ongoing_input & key_bit)) {
|
} else if(!(view_holder->ongoing_input & key_bit)) {
|
||||||
FURI_LOG_W(
|
FURI_LOG_W(
|
||||||
"ViewHolder",
|
TAG,
|
||||||
"non-complementary input, discarding key: %s, type: %s",
|
"non-complementary input, discarding key: %s, type: %s",
|
||||||
input_get_key_name(event->key),
|
input_get_key_name(event->key),
|
||||||
input_get_type_name(event->type));
|
input_get_type_name(event->type));
|
||||||
|
|||||||
@ -81,13 +81,8 @@ static void dolphin_check_butthurt(DolphinState* state) {
|
|||||||
furi_assert(state);
|
furi_assert(state);
|
||||||
float diff_time = difftime(dolphin_state_get_timestamp(state), dolphin_state_timestamp());
|
float diff_time = difftime(dolphin_state_get_timestamp(state), dolphin_state_timestamp());
|
||||||
|
|
||||||
#if 0
|
|
||||||
FURI_LOG_I("dolphin-state", "Butthurt check, time since deed %.0f", fabs(diff_time));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if((fabs(diff_time)) > DOLPHIN_TIMEGATE) {
|
if((fabs(diff_time)) > DOLPHIN_TIMEGATE) {
|
||||||
// increase butthurt
|
FURI_LOG_I("DolphinState", "Increasing butthurt");
|
||||||
FURI_LOG_I("dolphin-state", "Increasing butthurt");
|
|
||||||
dolphin_state_butthurted(state);
|
dolphin_state_butthurted(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,20 +2,14 @@
|
|||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <toolbox/saved_struct.h>
|
||||||
|
|
||||||
#define DOLPHIN_STORE_KEY "/int/dolphin.state"
|
#define TAG "DolphinState"
|
||||||
#define DOLPHIN_STORE_HEADER_MAGIC 0xD0
|
#define DOLPHIN_STATE_PATH "/int/dolphin.state"
|
||||||
#define DOLPHIN_STORE_HEADER_VERSION 0x01
|
#define DOLPHIN_STATE_HEADER_MAGIC 0xD0
|
||||||
|
#define DOLPHIN_STATE_HEADER_VERSION 0x01
|
||||||
#define DOLPHIN_LVL_THRESHOLD 20.0f
|
#define DOLPHIN_LVL_THRESHOLD 20.0f
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t magic;
|
|
||||||
uint8_t version;
|
|
||||||
uint8_t checksum;
|
|
||||||
uint8_t flags;
|
|
||||||
uint32_t timestamp;
|
|
||||||
} DolphinStoreHeader;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t limit_ibutton;
|
uint32_t limit_ibutton;
|
||||||
uint32_t limit_nfc;
|
uint32_t limit_nfc;
|
||||||
@ -28,25 +22,16 @@ typedef struct {
|
|||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
} DolphinStoreData;
|
} DolphinStoreData;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
DolphinStoreHeader header;
|
|
||||||
DolphinStoreData data;
|
|
||||||
} DolphinStore;
|
|
||||||
|
|
||||||
struct DolphinState {
|
struct DolphinState {
|
||||||
Storage* fs_api;
|
|
||||||
DolphinStoreData data;
|
DolphinStoreData data;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
};
|
};
|
||||||
|
|
||||||
DolphinState* dolphin_state_alloc() {
|
DolphinState* dolphin_state_alloc() {
|
||||||
DolphinState* dolphin_state = furi_alloc(sizeof(DolphinState));
|
return furi_alloc(sizeof(DolphinState));
|
||||||
dolphin_state->fs_api = furi_record_open("storage");
|
|
||||||
return dolphin_state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dolphin_state_free(DolphinState* dolphin_state) {
|
void dolphin_state_free(DolphinState* dolphin_state) {
|
||||||
furi_record_close("storage");
|
|
||||||
free(dolphin_state);
|
free(dolphin_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,121 +40,38 @@ bool dolphin_state_save(DolphinState* dolphin_state) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I("dolphin-state", "State is dirty, saving to \"%s\"", DOLPHIN_STORE_KEY);
|
bool result = saved_struct_save(
|
||||||
DolphinStore store;
|
DOLPHIN_STATE_PATH,
|
||||||
// Calculate checksum
|
&dolphin_state->data,
|
||||||
uint8_t* source = (uint8_t*)&dolphin_state->data;
|
sizeof(DolphinStoreData),
|
||||||
uint8_t checksum = 0;
|
DOLPHIN_STATE_HEADER_MAGIC,
|
||||||
for(size_t i = 0; i < sizeof(DolphinStoreData); i++) {
|
DOLPHIN_STATE_HEADER_VERSION);
|
||||||
checksum += source[i];
|
|
||||||
}
|
|
||||||
// Set header
|
|
||||||
store.header.magic = DOLPHIN_STORE_HEADER_MAGIC;
|
|
||||||
store.header.version = DOLPHIN_STORE_HEADER_VERSION;
|
|
||||||
store.header.checksum = checksum;
|
|
||||||
store.header.flags = 0;
|
|
||||||
store.header.timestamp = 0;
|
|
||||||
// Set data
|
|
||||||
store.data = dolphin_state->data;
|
|
||||||
|
|
||||||
// Store
|
if(result) {
|
||||||
File* file = storage_file_alloc(dolphin_state->fs_api);
|
FURI_LOG_I(TAG, "State saved");
|
||||||
bool save_result = storage_file_open(file, DOLPHIN_STORE_KEY, FSAM_WRITE, FSOM_CREATE_ALWAYS);
|
dolphin_state->dirty = false;
|
||||||
|
} else {
|
||||||
if(save_result) {
|
FURI_LOG_E(TAG, "Failed to save state");
|
||||||
uint16_t bytes_count = storage_file_write(file, &store, sizeof(DolphinStore));
|
|
||||||
|
|
||||||
if(bytes_count != sizeof(DolphinStore)) {
|
|
||||||
save_result = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!save_result) {
|
return result;
|
||||||
FURI_LOG_E(
|
|
||||||
"dolphin-state",
|
|
||||||
"Save failed. Storage returned: %s",
|
|
||||||
storage_file_get_error_desc(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
storage_file_close(file);
|
|
||||||
storage_file_free(file);
|
|
||||||
|
|
||||||
dolphin_state->dirty = !save_result;
|
|
||||||
|
|
||||||
FURI_LOG_I("dolphin-state", "Saved");
|
|
||||||
|
|
||||||
return save_result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dolphin_state_load(DolphinState* dolphin_state) {
|
bool dolphin_state_load(DolphinState* dolphin_state) {
|
||||||
DolphinStore store;
|
bool loaded = saved_struct_load(
|
||||||
// Read Dolphin State Store
|
DOLPHIN_STATE_PATH,
|
||||||
FURI_LOG_I("dolphin-state", "Loading state from \"%s\"", DOLPHIN_STORE_KEY);
|
&dolphin_state->data,
|
||||||
|
sizeof(DolphinStoreData),
|
||||||
|
DOLPHIN_STATE_HEADER_MAGIC,
|
||||||
|
DOLPHIN_STATE_HEADER_VERSION);
|
||||||
|
|
||||||
File* file = storage_file_alloc(dolphin_state->fs_api);
|
if(!loaded) {
|
||||||
bool load_result = storage_file_open(file, DOLPHIN_STORE_KEY, FSAM_READ, FSOM_OPEN_EXISTING);
|
FURI_LOG_W(TAG, "Reset dolphin-state");
|
||||||
if(!load_result) {
|
memset(dolphin_state, 0, sizeof(*dolphin_state));
|
||||||
FURI_LOG_E(
|
dolphin_state->dirty = true;
|
||||||
"dolphin-state",
|
|
||||||
"Load failed. Storage returned: %s",
|
|
||||||
storage_file_get_error_desc(file));
|
|
||||||
} else {
|
|
||||||
uint16_t bytes_count = storage_file_read(file, &store, sizeof(DolphinStore));
|
|
||||||
|
|
||||||
if(bytes_count != sizeof(DolphinStore)) {
|
|
||||||
load_result = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!load_result) {
|
return loaded;
|
||||||
FURI_LOG_E("dolphin-state", "DolphinStore size mismatch");
|
|
||||||
} else {
|
|
||||||
if(store.header.magic == DOLPHIN_STORE_HEADER_MAGIC &&
|
|
||||||
store.header.version == DOLPHIN_STORE_HEADER_VERSION) {
|
|
||||||
FURI_LOG_I(
|
|
||||||
"dolphin-state",
|
|
||||||
"Magic(%d) and Version(%d) match",
|
|
||||||
store.header.magic,
|
|
||||||
store.header.version);
|
|
||||||
uint8_t checksum = 0;
|
|
||||||
const uint8_t* source = (const uint8_t*)&store.data;
|
|
||||||
for(size_t i = 0; i < sizeof(DolphinStoreData); i++) {
|
|
||||||
checksum += source[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(store.header.checksum == checksum) {
|
|
||||||
FURI_LOG_I("dolphin-state", "Checksum(%d) match", store.header.checksum);
|
|
||||||
dolphin_state->data = store.data;
|
|
||||||
} else {
|
|
||||||
FURI_LOG_E(
|
|
||||||
"dolphin-state",
|
|
||||||
"Checksum(%d != %d) mismatch",
|
|
||||||
store.header.checksum,
|
|
||||||
checksum);
|
|
||||||
load_result = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
FURI_LOG_E(
|
|
||||||
"dolphin-state",
|
|
||||||
"Magic(%d != %d) or Version(%d != %d) mismatch",
|
|
||||||
store.header.magic,
|
|
||||||
DOLPHIN_STORE_HEADER_MAGIC,
|
|
||||||
store.header.version,
|
|
||||||
DOLPHIN_STORE_HEADER_VERSION);
|
|
||||||
load_result = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
storage_file_close(file);
|
|
||||||
storage_file_free(file);
|
|
||||||
|
|
||||||
dolphin_state->dirty = !load_result;
|
|
||||||
|
|
||||||
return load_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dolphin_state_clear(DolphinState* dolphin_state) {
|
|
||||||
memset(&dolphin_state->data, 0, sizeof(DolphinStoreData));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t dolphin_state_timestamp() {
|
uint64_t dolphin_state_timestamp() {
|
||||||
|
|||||||
@ -12,6 +12,14 @@
|
|||||||
#include <gui/modules/variable-item-list.h>
|
#include <gui/modules/variable-item-list.h>
|
||||||
#include "views/gpio_test.h"
|
#include "views/gpio_test.h"
|
||||||
|
|
||||||
|
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL)
|
||||||
|
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL)
|
||||||
|
#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL)
|
||||||
|
#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL)
|
||||||
|
|
||||||
|
#define GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE (4UL)
|
||||||
|
#define GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE (5UL)
|
||||||
|
|
||||||
struct GpioApp {
|
struct GpioApp {
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
ViewDispatcher* view_dispatcher;
|
ViewDispatcher* view_dispatcher;
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
#include "../gpio_app_i.h"
|
#include "../gpio_app_i.h"
|
||||||
#include "furi-hal-power.h"
|
#include "furi-hal-power.h"
|
||||||
|
|
||||||
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL)
|
|
||||||
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL)
|
|
||||||
#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL)
|
|
||||||
#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL)
|
|
||||||
|
|
||||||
enum GpioItem {
|
enum GpioItem {
|
||||||
GpioItemOtg,
|
GpioItemOtg,
|
||||||
GpioItemTest,
|
GpioItemTest,
|
||||||
@ -72,6 +67,9 @@ void gpio_scene_start_on_enter(void* context) {
|
|||||||
variable_item_list_add(var_item_list, "GPIO tester", 0, NULL, NULL);
|
variable_item_list_add(var_item_list, "GPIO tester", 0, NULL, NULL);
|
||||||
variable_item_list_add(var_item_list, "USB-UART bridge", 0, NULL, NULL);
|
variable_item_list_add(var_item_list, "USB-UART bridge", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_set_selected_item(
|
||||||
|
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneStart));
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList);
|
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,8 +83,10 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
|
|||||||
} else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF) {
|
} else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF) {
|
||||||
furi_hal_power_disable_otg();
|
furi_hal_power_disable_otg();
|
||||||
} else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_TEST) {
|
} else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_TEST) {
|
||||||
|
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, 1);
|
||||||
scene_manager_next_scene(app->scene_manager, GpioSceneTest);
|
scene_manager_next_scene(app->scene_manager, GpioSceneTest);
|
||||||
} else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_USB_UART) {
|
} else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_USB_UART) {
|
||||||
|
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, 2);
|
||||||
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
|
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
|||||||
@ -1,17 +1,6 @@
|
|||||||
|
#include "../usb_uart_bridge.h"
|
||||||
#include "../gpio_app_i.h"
|
#include "../gpio_app_i.h"
|
||||||
#include "furi-hal.h"
|
#include "furi-hal.h"
|
||||||
#include <stream_buffer.h>
|
|
||||||
#include <furi-hal-usb-cdc_i.h>
|
|
||||||
#include "usb_cdc.h"
|
|
||||||
|
|
||||||
#define USB_PKT_LEN CDC_DATA_SZ
|
|
||||||
#define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3)
|
|
||||||
#define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3)
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
WorkerCmdStop = (1 << 0),
|
|
||||||
|
|
||||||
} WorkerCommandFlags;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UsbUartLineIndexVcp,
|
UsbUartLineIndexVcp,
|
||||||
@ -21,42 +10,7 @@ typedef enum {
|
|||||||
UsbUartLineIndexDisable,
|
UsbUartLineIndexDisable,
|
||||||
} LineIndex;
|
} LineIndex;
|
||||||
|
|
||||||
typedef enum {
|
static UsbUartConfig* cfg_set;
|
||||||
UsbUartPortUSART1 = 0,
|
|
||||||
UsbUartPortLPUART1 = 1,
|
|
||||||
} PortIdx;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t vcp_ch;
|
|
||||||
PortIdx uart_ch;
|
|
||||||
uint32_t baudrate;
|
|
||||||
} UsbUartConfig;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
UsbUartConfig cfg_cur;
|
|
||||||
UsbUartConfig cfg_set;
|
|
||||||
char br_text[8];
|
|
||||||
|
|
||||||
bool running;
|
|
||||||
osThreadId_t parent_thread;
|
|
||||||
|
|
||||||
osThreadAttr_t thread_attr;
|
|
||||||
osThreadId_t thread;
|
|
||||||
|
|
||||||
osThreadAttr_t tx_thread_attr;
|
|
||||||
osThreadId_t tx_thread;
|
|
||||||
|
|
||||||
StreamBufferHandle_t rx_stream;
|
|
||||||
osSemaphoreId_t rx_done_sem;
|
|
||||||
osSemaphoreId_t usb_sof_sem;
|
|
||||||
|
|
||||||
StreamBufferHandle_t tx_stream;
|
|
||||||
|
|
||||||
uint8_t rx_buf[USB_PKT_LEN];
|
|
||||||
uint8_t tx_buf[USB_PKT_LEN];
|
|
||||||
} UsbUartParams;
|
|
||||||
|
|
||||||
static UsbUartParams* usb_uart;
|
|
||||||
|
|
||||||
static const char* vcp_ch[] = {"0 (CLI)", "1"};
|
static const char* vcp_ch[] = {"0 (CLI)", "1"};
|
||||||
static const char* uart_ch[] = {"USART1", "LPUART1"};
|
static const char* uart_ch[] = {"USART1", "LPUART1"};
|
||||||
@ -73,197 +27,14 @@ static const uint32_t baudrate_list[] = {
|
|||||||
921600,
|
921600,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void vcp_on_cdc_tx_complete();
|
|
||||||
static void vcp_on_cdc_rx();
|
|
||||||
static void vcp_state_callback(uint8_t state);
|
|
||||||
static void vcp_on_cdc_control_line(uint8_t state);
|
|
||||||
static void vcp_on_line_config(struct usb_cdc_line_coding* config);
|
|
||||||
|
|
||||||
static CdcCallbacks cdc_cb = {
|
|
||||||
vcp_on_cdc_tx_complete,
|
|
||||||
vcp_on_cdc_rx,
|
|
||||||
vcp_state_callback,
|
|
||||||
vcp_on_cdc_control_line,
|
|
||||||
vcp_on_line_config,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* USB UART worker */
|
|
||||||
|
|
||||||
static void usb_uart_tx_thread(void* context);
|
|
||||||
|
|
||||||
static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) {
|
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
|
|
||||||
if(ev == UartIrqEventRXNE) {
|
|
||||||
size_t ret =
|
|
||||||
xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
|
|
||||||
furi_check(ret == 1);
|
|
||||||
ret = xStreamBufferBytesAvailable(usb_uart->rx_stream);
|
|
||||||
if(ret > USB_PKT_LEN) osSemaphoreRelease(usb_uart->rx_done_sem);
|
|
||||||
} else if(ev == UartIrqEventIDLE) {
|
|
||||||
osSemaphoreRelease(usb_uart->rx_done_sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_uart_worker(void* context) {
|
|
||||||
memcpy(&usb_uart->cfg_cur, &usb_uart->cfg_set, sizeof(UsbUartConfig));
|
|
||||||
|
|
||||||
usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1);
|
|
||||||
usb_uart->rx_done_sem = osSemaphoreNew(1, 1, NULL);
|
|
||||||
usb_uart->usb_sof_sem = osSemaphoreNew(1, 1, NULL);
|
|
||||||
|
|
||||||
usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1);
|
|
||||||
|
|
||||||
usb_uart->tx_thread = NULL;
|
|
||||||
usb_uart->tx_thread_attr.name = "usb_uart_tx";
|
|
||||||
usb_uart->tx_thread_attr.stack_size = 512;
|
|
||||||
|
|
||||||
UsbMode usb_mode_prev = furi_hal_usb_get_config();
|
|
||||||
if(usb_uart->cfg_cur.vcp_ch == 0) {
|
|
||||||
furi_hal_usb_set_config(UsbModeVcpSingle);
|
|
||||||
furi_hal_vcp_disable();
|
|
||||||
} else {
|
|
||||||
furi_hal_usb_set_config(UsbModeVcpDual);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) {
|
|
||||||
furi_hal_usart_init();
|
|
||||||
furi_hal_usart_set_irq_cb(usb_uart_on_irq_cb);
|
|
||||||
if(usb_uart->cfg_cur.baudrate != 0)
|
|
||||||
furi_hal_usart_set_br(usb_uart->cfg_cur.baudrate);
|
|
||||||
else
|
|
||||||
vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
|
|
||||||
} else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) {
|
|
||||||
furi_hal_lpuart_init();
|
|
||||||
furi_hal_lpuart_set_irq_cb(usb_uart_on_irq_cb);
|
|
||||||
if(usb_uart->cfg_cur.baudrate != 0)
|
|
||||||
furi_hal_lpuart_set_br(usb_uart->cfg_cur.baudrate);
|
|
||||||
else
|
|
||||||
vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
|
|
||||||
}
|
|
||||||
|
|
||||||
furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, &cdc_cb);
|
|
||||||
usb_uart->tx_thread = osThreadNew(usb_uart_tx_thread, NULL, &usb_uart->tx_thread_attr);
|
|
||||||
|
|
||||||
while(1) {
|
|
||||||
furi_check(osSemaphoreAcquire(usb_uart->rx_done_sem, osWaitForever) == osOK);
|
|
||||||
if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, 0) == WorkerCmdStop) break;
|
|
||||||
size_t len = 0;
|
|
||||||
do {
|
|
||||||
len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0);
|
|
||||||
if(len > 0) {
|
|
||||||
if(osSemaphoreAcquire(usb_uart->usb_sof_sem, 100) == osOK)
|
|
||||||
furi_hal_cdc_send(usb_uart->cfg_cur.vcp_ch, usb_uart->rx_buf, len);
|
|
||||||
else
|
|
||||||
xStreamBufferReset(usb_uart->rx_stream);
|
|
||||||
}
|
|
||||||
} while(len > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
osThreadTerminate(usb_uart->tx_thread);
|
|
||||||
|
|
||||||
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
|
|
||||||
furi_hal_usart_deinit();
|
|
||||||
else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
|
|
||||||
furi_hal_lpuart_deinit();
|
|
||||||
|
|
||||||
furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, NULL);
|
|
||||||
furi_hal_usb_set_config(usb_mode_prev);
|
|
||||||
if(usb_uart->cfg_cur.vcp_ch == 0) furi_hal_vcp_enable();
|
|
||||||
|
|
||||||
vStreamBufferDelete(usb_uart->rx_stream);
|
|
||||||
osSemaphoreDelete(usb_uart->rx_done_sem);
|
|
||||||
osSemaphoreDelete(usb_uart->usb_sof_sem);
|
|
||||||
|
|
||||||
vStreamBufferDelete(usb_uart->tx_stream);
|
|
||||||
osThreadFlagsSet(usb_uart->parent_thread, WorkerCmdStop);
|
|
||||||
osThreadExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_uart_tx_thread(void* context) {
|
|
||||||
uint8_t data = 0;
|
|
||||||
while(1) {
|
|
||||||
size_t len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, osWaitForever);
|
|
||||||
if(len > 0) {
|
|
||||||
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
|
|
||||||
furi_hal_usart_tx(&data, len);
|
|
||||||
else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
|
|
||||||
furi_hal_lpuart_tx(&data, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
osThreadExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VCP callbacks */
|
|
||||||
|
|
||||||
static void vcp_on_cdc_tx_complete() {
|
|
||||||
osSemaphoreRelease(usb_uart->usb_sof_sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vcp_on_cdc_rx() {
|
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
|
|
||||||
uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream);
|
|
||||||
if(max_len > 0) {
|
|
||||||
if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN;
|
|
||||||
int32_t size = furi_hal_cdc_receive(usb_uart->cfg_cur.vcp_ch, usb_uart->tx_buf, max_len);
|
|
||||||
|
|
||||||
if(size > 0) {
|
|
||||||
size_t ret = xStreamBufferSendFromISR(
|
|
||||||
usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken);
|
|
||||||
furi_check(ret == size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vcp_state_callback(uint8_t state) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vcp_on_cdc_control_line(uint8_t state) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vcp_on_line_config(struct usb_cdc_line_coding* config) {
|
|
||||||
if((usb_uart->cfg_cur.baudrate == 0) && (config->dwDTERate != 0)) {
|
|
||||||
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
|
|
||||||
furi_hal_usart_set_br(config->dwDTERate);
|
|
||||||
else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
|
|
||||||
furi_hal_lpuart_set_br(config->dwDTERate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* USB UART app */
|
|
||||||
|
|
||||||
static void usb_uart_enable() {
|
|
||||||
if(usb_uart->running == false) {
|
|
||||||
usb_uart->thread = NULL;
|
|
||||||
usb_uart->thread_attr.name = "usb_uart";
|
|
||||||
usb_uart->thread_attr.stack_size = 1024;
|
|
||||||
usb_uart->parent_thread = osThreadGetId();
|
|
||||||
usb_uart->running = true;
|
|
||||||
usb_uart->thread = osThreadNew(usb_uart_worker, NULL, &usb_uart->thread_attr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_uart_disable() {
|
|
||||||
if(usb_uart->running == true) {
|
|
||||||
osThreadFlagsSet(usb_uart->thread, WorkerCmdStop);
|
|
||||||
osSemaphoreRelease(usb_uart->rx_done_sem);
|
|
||||||
osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, osWaitForever);
|
|
||||||
usb_uart->running = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
|
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
|
||||||
//GpioApp* app = context;
|
//GpioApp* app = context;
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == UsbUartLineIndexEnable) {
|
if(event.event == GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE) {
|
||||||
usb_uart_enable();
|
usb_uart_enable(cfg_set);
|
||||||
} else if(event.event == UsbUartLineIndexDisable) {
|
} else if(event.event == GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE) {
|
||||||
usb_uart_disable();
|
usb_uart_disable();
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
@ -271,15 +42,13 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
|
|||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scene callbacks */
|
|
||||||
|
|
||||||
static void line_vcp_cb(VariableItem* item) {
|
static void line_vcp_cb(VariableItem* item) {
|
||||||
//GpioApp* app = variable_item_get_context(item);
|
//GpioApp* app = variable_item_get_context(item);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
variable_item_set_current_value_text(item, vcp_ch[index]);
|
variable_item_set_current_value_text(item, vcp_ch[index]);
|
||||||
|
|
||||||
usb_uart->cfg_set.vcp_ch = index;
|
cfg_set->vcp_ch = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_port_cb(VariableItem* item) {
|
static void line_port_cb(VariableItem* item) {
|
||||||
@ -288,34 +57,44 @@ static void line_port_cb(VariableItem* item) {
|
|||||||
|
|
||||||
variable_item_set_current_value_text(item, uart_ch[index]);
|
variable_item_set_current_value_text(item, uart_ch[index]);
|
||||||
|
|
||||||
usb_uart->cfg_set.uart_ch = index;
|
if(index == 0)
|
||||||
|
cfg_set->uart_ch = FuriHalUartIdUSART1;
|
||||||
|
else if(index == 1)
|
||||||
|
cfg_set->uart_ch = FuriHalUartIdLPUART1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_baudrate_cb(VariableItem* item) {
|
static void line_baudrate_cb(VariableItem* item) {
|
||||||
//GpioApp* app = variable_item_get_context(item);
|
//GpioApp* app = variable_item_get_context(item);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
|
char br_text[8];
|
||||||
|
|
||||||
if(index > 0) {
|
if(index > 0) {
|
||||||
snprintf(usb_uart->br_text, 7, "%lu", baudrate_list[index - 1]);
|
snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
|
||||||
variable_item_set_current_value_text(item, usb_uart->br_text);
|
variable_item_set_current_value_text(item, br_text);
|
||||||
usb_uart->cfg_set.baudrate = baudrate_list[index - 1];
|
cfg_set->baudrate = baudrate_list[index - 1];
|
||||||
} else {
|
} else {
|
||||||
variable_item_set_current_value_text(item, baudrate_mode[index]);
|
variable_item_set_current_value_text(item, baudrate_mode[index]);
|
||||||
usb_uart->cfg_set.baudrate = 0;
|
cfg_set->baudrate = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) {
|
static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
GpioApp* app = context;
|
GpioApp* app = context;
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
if(index == UsbUartLineIndexEnable)
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE);
|
||||||
|
else if(index == UsbUartLineIndexDisable)
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpio_scene_usb_uart_on_enter(void* context) {
|
void gpio_scene_usb_uart_on_enter(void* context) {
|
||||||
GpioApp* app = context;
|
GpioApp* app = context;
|
||||||
VariableItemList* var_item_list = app->var_item_list;
|
VariableItemList* var_item_list = app->var_item_list;
|
||||||
|
|
||||||
usb_uart = furi_alloc(sizeof(UsbUartParams));
|
cfg_set = furi_alloc(sizeof(UsbUartConfig));
|
||||||
|
|
||||||
VariableItem* item;
|
VariableItem* item;
|
||||||
|
|
||||||
@ -341,12 +120,19 @@ void gpio_scene_usb_uart_on_enter(void* context) {
|
|||||||
item = variable_item_list_add(var_item_list, "Enable", 0, NULL, NULL);
|
item = variable_item_list_add(var_item_list, "Enable", 0, NULL, NULL);
|
||||||
item = variable_item_list_add(var_item_list, "Disable", 0, NULL, NULL);
|
item = variable_item_list_add(var_item_list, "Disable", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_set_selected_item(
|
||||||
|
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart));
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
|
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpio_scene_usb_uart_on_exit(void* context) {
|
void gpio_scene_usb_uart_on_exit(void* context) {
|
||||||
GpioApp* app = context;
|
GpioApp* app = context;
|
||||||
usb_uart_disable();
|
usb_uart_disable();
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager,
|
||||||
|
GpioSceneUsbUart,
|
||||||
|
variable_item_list_get_selected_item_index(app->var_item_list));
|
||||||
variable_item_list_clean(app->var_item_list);
|
variable_item_list_clean(app->var_item_list);
|
||||||
free(usb_uart);
|
free(cfg_set);
|
||||||
}
|
}
|
||||||
212
applications/gpio/usb_uart_bridge.c
Normal file
212
applications/gpio/usb_uart_bridge.c
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
#include "usb_uart_bridge.h"
|
||||||
|
#include "furi-hal.h"
|
||||||
|
#include <stream_buffer.h>
|
||||||
|
#include <furi-hal-usb-cdc_i.h>
|
||||||
|
#include "usb_cdc.h"
|
||||||
|
|
||||||
|
#define USB_CDC_PKT_LEN CDC_DATA_SZ
|
||||||
|
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WorkerEvtReserved = (1 << 0), // Reserved for StreamBuffer internal event
|
||||||
|
WorkerEvtStop = (1 << 1),
|
||||||
|
WorkerEvtRxDone = (1 << 2),
|
||||||
|
|
||||||
|
WorkerEvtTxStop = (1 << 3),
|
||||||
|
WorkerEvtCdcRx = (1 << 4),
|
||||||
|
} WorkerEvtFlags;
|
||||||
|
|
||||||
|
#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone)
|
||||||
|
#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
UsbUartConfig cfg;
|
||||||
|
|
||||||
|
FuriThread* thread;
|
||||||
|
FuriThread* tx_thread;
|
||||||
|
|
||||||
|
StreamBufferHandle_t rx_stream;
|
||||||
|
|
||||||
|
osMutexId_t usb_mutex;
|
||||||
|
|
||||||
|
osSemaphoreId_t tx_sem;
|
||||||
|
|
||||||
|
uint8_t rx_buf[USB_CDC_PKT_LEN];
|
||||||
|
|
||||||
|
bool buf_full;
|
||||||
|
} UsbUartParams;
|
||||||
|
|
||||||
|
static UsbUartParams* usb_uart;
|
||||||
|
static bool running = false;
|
||||||
|
|
||||||
|
static void vcp_on_cdc_tx_complete();
|
||||||
|
static void vcp_on_cdc_rx();
|
||||||
|
static void vcp_state_callback(uint8_t state);
|
||||||
|
static void vcp_on_cdc_control_line(uint8_t state);
|
||||||
|
static void vcp_on_line_config(struct usb_cdc_line_coding* config);
|
||||||
|
|
||||||
|
static CdcCallbacks cdc_cb = {
|
||||||
|
vcp_on_cdc_tx_complete,
|
||||||
|
vcp_on_cdc_rx,
|
||||||
|
vcp_state_callback,
|
||||||
|
vcp_on_cdc_control_line,
|
||||||
|
vcp_on_line_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* USB UART worker */
|
||||||
|
|
||||||
|
static int32_t usb_uart_tx_thread(void* context);
|
||||||
|
|
||||||
|
static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) {
|
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
|
||||||
|
if(ev == UartIrqEventRXNE) {
|
||||||
|
xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
|
||||||
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||||
|
osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtRxDone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t usb_uart_worker(void* context) {
|
||||||
|
memcpy(&usb_uart->cfg, context, sizeof(UsbUartConfig));
|
||||||
|
|
||||||
|
usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1);
|
||||||
|
|
||||||
|
usb_uart->tx_sem = osSemaphoreNew(1, 1, NULL);
|
||||||
|
usb_uart->usb_mutex = osMutexNew(NULL);
|
||||||
|
|
||||||
|
usb_uart->tx_thread = furi_thread_alloc();
|
||||||
|
furi_thread_set_name(usb_uart->tx_thread, "UsbUartTxWorker");
|
||||||
|
furi_thread_set_stack_size(usb_uart->tx_thread, 512);
|
||||||
|
furi_thread_set_context(usb_uart->tx_thread, NULL);
|
||||||
|
furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread);
|
||||||
|
|
||||||
|
UsbMode usb_mode_prev = furi_hal_usb_get_config();
|
||||||
|
if(usb_uart->cfg.vcp_ch == 0) {
|
||||||
|
furi_hal_usb_set_config(UsbModeVcpSingle);
|
||||||
|
furi_hal_vcp_disable();
|
||||||
|
} else {
|
||||||
|
furi_hal_usb_set_config(UsbModeVcpDual);
|
||||||
|
}
|
||||||
|
osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtCdcRx);
|
||||||
|
|
||||||
|
if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) {
|
||||||
|
furi_hal_console_disable();
|
||||||
|
} else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) {
|
||||||
|
furi_hal_uart_init(usb_uart->cfg.uart_ch, 115200);
|
||||||
|
furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb);
|
||||||
|
if(usb_uart->cfg.baudrate != 0)
|
||||||
|
furi_hal_uart_set_br(usb_uart->cfg.uart_ch, usb_uart->cfg.baudrate);
|
||||||
|
else
|
||||||
|
vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch));
|
||||||
|
|
||||||
|
furi_hal_cdc_set_callbacks(usb_uart->cfg.vcp_ch, &cdc_cb);
|
||||||
|
|
||||||
|
furi_thread_start(usb_uart->tx_thread);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
uint32_t events = osThreadFlagsWait(WORKER_ALL_RX_EVENTS, osFlagsWaitAny, osWaitForever);
|
||||||
|
furi_check((events & osFlagsError) == 0);
|
||||||
|
if(events & WorkerEvtStop) break;
|
||||||
|
if(events & WorkerEvtRxDone) {
|
||||||
|
size_t len =
|
||||||
|
xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0);
|
||||||
|
if(len > 0) {
|
||||||
|
if(osSemaphoreAcquire(usb_uart->tx_sem, 100) == osOK) {
|
||||||
|
furi_check(osMutexAcquire(usb_uart->usb_mutex, osWaitForever) == osOK);
|
||||||
|
furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len);
|
||||||
|
furi_check(osMutexRelease(usb_uart->usb_mutex) == osOK);
|
||||||
|
} else {
|
||||||
|
xStreamBufferReset(usb_uart->rx_stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtTxStop);
|
||||||
|
furi_thread_join(usb_uart->tx_thread);
|
||||||
|
furi_thread_free(usb_uart->tx_thread);
|
||||||
|
|
||||||
|
if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1)
|
||||||
|
furi_hal_console_enable();
|
||||||
|
else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1)
|
||||||
|
furi_hal_uart_deinit(usb_uart->cfg.uart_ch);
|
||||||
|
|
||||||
|
furi_hal_cdc_set_callbacks(usb_uart->cfg.vcp_ch, NULL);
|
||||||
|
furi_hal_usb_set_config(usb_mode_prev);
|
||||||
|
if(usb_uart->cfg.vcp_ch == 0) furi_hal_vcp_enable();
|
||||||
|
|
||||||
|
vStreamBufferDelete(usb_uart->rx_stream);
|
||||||
|
osMutexDelete(usb_uart->usb_mutex);
|
||||||
|
osSemaphoreDelete(usb_uart->tx_sem);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t usb_uart_tx_thread(void* context) {
|
||||||
|
uint8_t data[USB_CDC_PKT_LEN];
|
||||||
|
while(1) {
|
||||||
|
uint32_t events = osThreadFlagsWait(WORKER_ALL_TX_EVENTS, osFlagsWaitAny, osWaitForever);
|
||||||
|
furi_check((events & osFlagsError) == 0);
|
||||||
|
if(events & WorkerEvtTxStop) break;
|
||||||
|
if(events & WorkerEvtCdcRx) {
|
||||||
|
furi_check(osMutexAcquire(usb_uart->usb_mutex, osWaitForever) == osOK);
|
||||||
|
int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_CDC_PKT_LEN);
|
||||||
|
furi_check(osMutexRelease(usb_uart->usb_mutex) == osOK);
|
||||||
|
|
||||||
|
if(size > 0) {
|
||||||
|
furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VCP callbacks */
|
||||||
|
|
||||||
|
static void vcp_on_cdc_tx_complete() {
|
||||||
|
osSemaphoreRelease(usb_uart->tx_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcp_on_cdc_rx() {
|
||||||
|
osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtCdcRx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcp_state_callback(uint8_t state) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcp_on_cdc_control_line(uint8_t state) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcp_on_line_config(struct usb_cdc_line_coding* config) {
|
||||||
|
if((usb_uart->cfg.baudrate == 0) && (config->dwDTERate != 0))
|
||||||
|
furi_hal_uart_set_br(usb_uart->cfg.uart_ch, config->dwDTERate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_uart_enable(UsbUartConfig* cfg) {
|
||||||
|
if(running == false) {
|
||||||
|
running = true;
|
||||||
|
usb_uart = furi_alloc(sizeof(UsbUartParams));
|
||||||
|
|
||||||
|
usb_uart->thread = furi_thread_alloc();
|
||||||
|
furi_thread_set_name(usb_uart->thread, "UsbUartWorker");
|
||||||
|
furi_thread_set_stack_size(usb_uart->thread, 1024);
|
||||||
|
furi_thread_set_context(usb_uart->thread, cfg);
|
||||||
|
furi_thread_set_callback(usb_uart->thread, usb_uart_worker);
|
||||||
|
|
||||||
|
furi_thread_start(usb_uart->thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_uart_disable() {
|
||||||
|
if(running == true) {
|
||||||
|
osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtStop);
|
||||||
|
furi_thread_join(usb_uart->thread);
|
||||||
|
furi_thread_free(usb_uart->thread);
|
||||||
|
free(usb_uart);
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
applications/gpio/usb_uart_bridge.h
Normal file
13
applications/gpio/usb_uart_bridge.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t vcp_ch;
|
||||||
|
uint8_t uart_ch;
|
||||||
|
uint32_t baudrate;
|
||||||
|
} UsbUartConfig;
|
||||||
|
|
||||||
|
void usb_uart_enable(UsbUartConfig* cfg);
|
||||||
|
|
||||||
|
void usb_uart_disable();
|
||||||
36
applications/gui/canvas.c
Executable file → Normal file
36
applications/gui/canvas.c
Executable file → Normal file
@ -6,22 +6,32 @@
|
|||||||
#include <furi-hal.h>
|
#include <furi-hal.h>
|
||||||
#include <u8g2_glue.h>
|
#include <u8g2_glue.h>
|
||||||
|
|
||||||
|
const CanvasFontParameters canvas_font_params[FontTotalNumber] = {
|
||||||
|
[FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2},
|
||||||
|
[FontSecondary] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2},
|
||||||
|
[FontKeyboard] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2},
|
||||||
|
[FontBigNumbers] = {.leading_default = 18, .leading_min = 16, .height = 15, .descender = 0},
|
||||||
|
};
|
||||||
|
|
||||||
Canvas* canvas_init() {
|
Canvas* canvas_init() {
|
||||||
Canvas* canvas = furi_alloc(sizeof(Canvas));
|
Canvas* canvas = furi_alloc(sizeof(Canvas));
|
||||||
|
|
||||||
furi_hal_power_insomnia_enter();
|
furi_hal_power_insomnia_enter();
|
||||||
|
|
||||||
canvas->orientation = CanvasOrientationHorizontal;
|
// Setup u8g2
|
||||||
u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
||||||
|
canvas->orientation = CanvasOrientationHorizontal;
|
||||||
// send init sequence to the display, display is in sleep mode after this
|
// Initialize display
|
||||||
u8g2_InitDisplay(&canvas->fb);
|
u8g2_InitDisplay(&canvas->fb);
|
||||||
// wake up display
|
// Wake up display
|
||||||
u8g2_ClearBuffer(&canvas->fb);
|
|
||||||
u8g2_SetPowerSave(&canvas->fb, 0);
|
u8g2_SetPowerSave(&canvas->fb, 0);
|
||||||
u8g2_SendBuffer(&canvas->fb);
|
|
||||||
|
// Clear buffer and send to device
|
||||||
|
canvas_clear(canvas);
|
||||||
|
canvas_commit(canvas);
|
||||||
|
|
||||||
furi_hal_power_insomnia_exit();
|
furi_hal_power_insomnia_exit();
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,9 +42,12 @@ void canvas_free(Canvas* canvas) {
|
|||||||
|
|
||||||
void canvas_reset(Canvas* canvas) {
|
void canvas_reset(Canvas* canvas) {
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
|
|
||||||
canvas_clear(canvas);
|
canvas_clear(canvas);
|
||||||
|
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_set_font_direction(canvas, CanvasFontDirectionLeftToRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void canvas_commit(Canvas* canvas) {
|
void canvas_commit(Canvas* canvas) {
|
||||||
@ -86,6 +99,12 @@ uint8_t canvas_current_font_height(Canvas* canvas) {
|
|||||||
return font_height;
|
return font_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font) {
|
||||||
|
furi_assert(canvas);
|
||||||
|
furi_assert(font < FontTotalNumber);
|
||||||
|
return (CanvasFontParameters*)&canvas_font_params[font];
|
||||||
|
}
|
||||||
|
|
||||||
void canvas_clear(Canvas* canvas) {
|
void canvas_clear(Canvas* canvas) {
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
u8g2_ClearBuffer(&canvas->fb);
|
u8g2_ClearBuffer(&canvas->fb);
|
||||||
@ -96,6 +115,11 @@ void canvas_set_color(Canvas* canvas, Color color) {
|
|||||||
u8g2_SetDrawColor(&canvas->fb, color);
|
u8g2_SetDrawColor(&canvas->fb, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void canvas_set_font_direction(Canvas* canvas, CanvasFontDirection dir) {
|
||||||
|
furi_assert(canvas);
|
||||||
|
u8g2_SetFontDirection(&canvas->fb, dir);
|
||||||
|
}
|
||||||
|
|
||||||
void canvas_invert_color(Canvas* canvas) {
|
void canvas_invert_color(Canvas* canvas) {
|
||||||
canvas->fb.draw_color = !canvas->fb.draw_color;
|
canvas->fb.draw_color = !canvas->fb.draw_color;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,15 @@ typedef enum {
|
|||||||
} Color;
|
} Color;
|
||||||
|
|
||||||
/** Fonts enumeration */
|
/** Fonts enumeration */
|
||||||
typedef enum { FontPrimary, FontSecondary, FontKeyboard, FontBigNumbers } Font;
|
typedef enum {
|
||||||
|
FontPrimary,
|
||||||
|
FontSecondary,
|
||||||
|
FontKeyboard,
|
||||||
|
FontBigNumbers,
|
||||||
|
|
||||||
|
// Keep last for fonts number calculation
|
||||||
|
FontTotalNumber,
|
||||||
|
} Font;
|
||||||
|
|
||||||
/** Alignment enumeration */
|
/** Alignment enumeration */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -37,6 +45,22 @@ typedef enum {
|
|||||||
CanvasOrientationVertical,
|
CanvasOrientationVertical,
|
||||||
} CanvasOrientation;
|
} CanvasOrientation;
|
||||||
|
|
||||||
|
/** Font Direction */
|
||||||
|
typedef enum {
|
||||||
|
CanvasFontDirectionLeftToRight,
|
||||||
|
CanvasFontDirectionTopToDown,
|
||||||
|
CanvasFontDirectionRightToLeft,
|
||||||
|
CanvasFontDirectionDownToTop,
|
||||||
|
} CanvasFontDirection;
|
||||||
|
|
||||||
|
/** Font parameters */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t leading_default;
|
||||||
|
uint8_t leading_min;
|
||||||
|
uint8_t height;
|
||||||
|
uint8_t descender;
|
||||||
|
} CanvasFontParameters;
|
||||||
|
|
||||||
/** Canvas anonymouse structure */
|
/** Canvas anonymouse structure */
|
||||||
typedef struct Canvas Canvas;
|
typedef struct Canvas Canvas;
|
||||||
|
|
||||||
@ -64,6 +88,15 @@ uint8_t canvas_height(Canvas* canvas);
|
|||||||
*/
|
*/
|
||||||
uint8_t canvas_current_font_height(Canvas* canvas);
|
uint8_t canvas_current_font_height(Canvas* canvas);
|
||||||
|
|
||||||
|
/** Get font parameters
|
||||||
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param font Font
|
||||||
|
*
|
||||||
|
* @return pointer to CanvasFontParameters structure
|
||||||
|
*/
|
||||||
|
CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font);
|
||||||
|
|
||||||
/** Clear canvas
|
/** Clear canvas
|
||||||
*
|
*
|
||||||
* @param canvas Canvas instance
|
* @param canvas Canvas instance
|
||||||
@ -77,6 +110,14 @@ void canvas_clear(Canvas* canvas);
|
|||||||
*/
|
*/
|
||||||
void canvas_set_color(Canvas* canvas, Color color);
|
void canvas_set_color(Canvas* canvas, Color color);
|
||||||
|
|
||||||
|
/** Set font swap
|
||||||
|
* Argument String Rotation Description
|
||||||
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param dir Direction font
|
||||||
|
*/
|
||||||
|
void canvas_set_font_direction(Canvas* canvas, CanvasFontDirection dir);
|
||||||
|
|
||||||
/** Invert drawing color
|
/** Invert drawing color
|
||||||
*
|
*
|
||||||
* @param canvas Canvas instance
|
* @param canvas Canvas instance
|
||||||
|
|||||||
215
applications/gui/elements.c
Normal file → Executable file
215
applications/gui/elements.c
Normal file → Executable file
@ -10,6 +10,18 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t x;
|
||||||
|
uint8_t y;
|
||||||
|
uint8_t leading_min;
|
||||||
|
uint8_t leading_default;
|
||||||
|
uint8_t height;
|
||||||
|
uint8_t descender;
|
||||||
|
uint8_t len;
|
||||||
|
const char* text;
|
||||||
|
} ElementTextBoxLine;
|
||||||
|
|
||||||
void elements_progress_bar(
|
void elements_progress_bar(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -352,3 +364,206 @@ void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) {
|
|||||||
string_cat(string, "...");
|
string_cat(string, "...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void elements_text_box(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text) {
|
||||||
|
furi_assert(canvas);
|
||||||
|
|
||||||
|
ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM];
|
||||||
|
bool bold = false;
|
||||||
|
bool mono = false;
|
||||||
|
bool inversed = false;
|
||||||
|
bool inversed_present = false;
|
||||||
|
Font current_font = FontSecondary;
|
||||||
|
Font prev_font = FontSecondary;
|
||||||
|
CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font);
|
||||||
|
|
||||||
|
// Fill line parameters
|
||||||
|
uint8_t line_leading_min = font_params->leading_min;
|
||||||
|
uint8_t line_leading_default = font_params->leading_default;
|
||||||
|
uint8_t line_height = font_params->height;
|
||||||
|
uint8_t line_descender = font_params->descender;
|
||||||
|
uint8_t line_num = 0;
|
||||||
|
uint8_t line_width = 0;
|
||||||
|
uint8_t line_len = 0;
|
||||||
|
uint8_t total_height_min = 0;
|
||||||
|
uint8_t total_height_default = 0;
|
||||||
|
uint16_t i = 0;
|
||||||
|
bool full_text_processed = false;
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
// Fill all lines
|
||||||
|
line[0].text = text;
|
||||||
|
for(i = 0; !full_text_processed; i++) {
|
||||||
|
line_len++;
|
||||||
|
// Identify line height
|
||||||
|
if(prev_font != current_font) {
|
||||||
|
font_params = canvas_get_font_params(canvas, current_font);
|
||||||
|
line_leading_min = MAX(line_leading_min, font_params->leading_min);
|
||||||
|
line_leading_default = MAX(line_leading_default, font_params->leading_default);
|
||||||
|
line_height = MAX(line_height, font_params->height);
|
||||||
|
line_descender = MAX(line_descender, font_params->descender);
|
||||||
|
prev_font = current_font;
|
||||||
|
}
|
||||||
|
// Set the font
|
||||||
|
if(text[i] == '\e' && text[i + 1]) {
|
||||||
|
i++;
|
||||||
|
line_len++;
|
||||||
|
if(text[i] == ELEMENTS_BOLD_MARKER) {
|
||||||
|
if(bold) {
|
||||||
|
current_font = FontSecondary;
|
||||||
|
} else {
|
||||||
|
current_font = FontPrimary;
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, current_font);
|
||||||
|
bold = !bold;
|
||||||
|
}
|
||||||
|
if(text[i] == ELEMENTS_MONO_MARKER) {
|
||||||
|
if(mono) {
|
||||||
|
current_font = FontSecondary;
|
||||||
|
} else {
|
||||||
|
current_font = FontKeyboard;
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, FontKeyboard);
|
||||||
|
mono = !mono;
|
||||||
|
}
|
||||||
|
if(text[i] == ELEMENTS_INVERSED_MARKER) {
|
||||||
|
inversed_present = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(text[i] != '\n') {
|
||||||
|
line_width += canvas_glyph_width(canvas, text[i]);
|
||||||
|
}
|
||||||
|
// Process new line
|
||||||
|
if(text[i] == '\n' || text[i] == '\0' || line_width > width) {
|
||||||
|
if(line_width > width) {
|
||||||
|
line_width -= canvas_glyph_width(canvas, text[i--]);
|
||||||
|
line_len--;
|
||||||
|
}
|
||||||
|
if(text[i] == '\0') {
|
||||||
|
full_text_processed = true;
|
||||||
|
}
|
||||||
|
if(inversed_present) {
|
||||||
|
line_leading_min += 1;
|
||||||
|
line_leading_default += 1;
|
||||||
|
inversed_present = false;
|
||||||
|
}
|
||||||
|
line[line_num].leading_min = line_leading_min;
|
||||||
|
line[line_num].leading_default = line_leading_default;
|
||||||
|
line[line_num].height = line_height;
|
||||||
|
line[line_num].descender = line_descender;
|
||||||
|
if(total_height_min + line_leading_min > height) {
|
||||||
|
line_num--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
total_height_min += line_leading_min;
|
||||||
|
total_height_default += line_leading_default;
|
||||||
|
line[line_num].len = line_len;
|
||||||
|
if(horizontal == AlignCenter) {
|
||||||
|
line[line_num].x = x + (width - line_width) / 2;
|
||||||
|
} else if(horizontal == AlignRight) {
|
||||||
|
line[line_num].x = x + (width - line_width);
|
||||||
|
} else {
|
||||||
|
line[line_num].x = x;
|
||||||
|
}
|
||||||
|
line[line_num].y = total_height_min;
|
||||||
|
line_num++;
|
||||||
|
if(text[i + 1]) {
|
||||||
|
line[line_num].text = &text[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
line_leading_min = font_params->leading_min;
|
||||||
|
line_height = font_params->height;
|
||||||
|
line_descender = font_params->descender;
|
||||||
|
line_width = 0;
|
||||||
|
line_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set vertical alignment for all lines
|
||||||
|
if(full_text_processed) {
|
||||||
|
if(total_height_default < height) {
|
||||||
|
if(vertical == AlignTop) {
|
||||||
|
line[0].y = y + line[0].height;
|
||||||
|
} else if(vertical == AlignCenter) {
|
||||||
|
line[0].y = y + line[0].height + (height - total_height_default) / 2;
|
||||||
|
} else if(vertical == AlignBottom) {
|
||||||
|
line[0].y = y + line[0].height + (height - total_height_default);
|
||||||
|
}
|
||||||
|
if(line_num > 1) {
|
||||||
|
for(uint8_t i = 1; i < line_num; i++) {
|
||||||
|
line[i].y = line[i - 1].y + line[i - 1].leading_default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(line_num > 1) {
|
||||||
|
uint8_t free_pixel_num = height - total_height_min;
|
||||||
|
uint8_t fill_pixel = 0;
|
||||||
|
uint8_t j = 1;
|
||||||
|
line[0].y = line[0].height;
|
||||||
|
while(fill_pixel < free_pixel_num) {
|
||||||
|
line[j].y = line[j - 1].y + line[j - 1].leading_min + 1;
|
||||||
|
fill_pixel++;
|
||||||
|
j = j % (line_num - 1) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw line by line
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
bold = false;
|
||||||
|
mono = false;
|
||||||
|
inversed = false;
|
||||||
|
for(uint8_t i = 0; i < line_num; i++) {
|
||||||
|
for(uint8_t j = 0; j < line[i].len; j++) {
|
||||||
|
// Process format symbols
|
||||||
|
if(line[i].text[j] == ELEMENTS_BOLD_MARKER) {
|
||||||
|
if(bold) {
|
||||||
|
current_font = FontSecondary;
|
||||||
|
} else {
|
||||||
|
current_font = FontPrimary;
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, current_font);
|
||||||
|
bold = !bold;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(line[i].text[j] == ELEMENTS_MONO_MARKER) {
|
||||||
|
if(mono) {
|
||||||
|
current_font = FontSecondary;
|
||||||
|
} else {
|
||||||
|
current_font = FontKeyboard;
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, current_font);
|
||||||
|
mono = !mono;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(line[i].text[j] == ELEMENTS_INVERSED_MARKER) {
|
||||||
|
inversed = !inversed;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(inversed) {
|
||||||
|
canvas_draw_box(
|
||||||
|
canvas,
|
||||||
|
line[i].x - 1,
|
||||||
|
line[i].y - line[i].height - 1,
|
||||||
|
canvas_glyph_width(canvas, line[i].text[j]) + 1,
|
||||||
|
line[i].height + line[i].descender + 2);
|
||||||
|
canvas_invert_color(canvas);
|
||||||
|
canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);
|
||||||
|
canvas_invert_color(canvas);
|
||||||
|
} else {
|
||||||
|
canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);
|
||||||
|
}
|
||||||
|
line[i].x += canvas_glyph_width(canvas, line[i].text[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
}
|
||||||
|
|||||||
127
applications/gui/elements.h
Normal file → Executable file
127
applications/gui/elements.h
Normal file → Executable file
@ -16,12 +16,19 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ELEMENTS_MAX_LINES_NUM (7)
|
||||||
|
#define ELEMENTS_BOLD_MARKER '#'
|
||||||
|
#define ELEMENTS_MONO_MARKER '*'
|
||||||
|
#define ELEMENTS_INVERSED_MARKER '!'
|
||||||
|
|
||||||
/** Draw progress bar.
|
/** Draw progress bar.
|
||||||
* @param x - progress bar position on X axis
|
*
|
||||||
* @param y - progress bar position on Y axis
|
* @param canvas Canvas instance
|
||||||
* @param width - progress bar width
|
* @param x progress bar position on X axis
|
||||||
* @param progress - progress in unnamed metric
|
* @param y progress bar position on Y axis
|
||||||
* @param total - total amount in unnamed metric
|
* @param width progress bar width
|
||||||
|
* @param progress progress in unnamed metric
|
||||||
|
* @param total total amount in unnamed metric
|
||||||
*/
|
*/
|
||||||
void elements_progress_bar(
|
void elements_progress_bar(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -32,11 +39,13 @@ void elements_progress_bar(
|
|||||||
uint8_t total);
|
uint8_t total);
|
||||||
|
|
||||||
/** Draw scrollbar on canvas at specific position.
|
/** Draw scrollbar on canvas at specific position.
|
||||||
* @param x - scrollbar position on X axis
|
*
|
||||||
* @param y - scrollbar position on Y axis
|
* @param canvas Canvas instance
|
||||||
* @param height - scrollbar height
|
* @param x scrollbar position on X axis
|
||||||
* @param pos - current element
|
* @param y scrollbar position on Y axis
|
||||||
* @param total - total elements
|
* @param height scrollbar height
|
||||||
|
* @param pos current element
|
||||||
|
* @param total total elements
|
||||||
*/
|
*/
|
||||||
void elements_scrollbar_pos(
|
void elements_scrollbar_pos(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -47,37 +56,49 @@ void elements_scrollbar_pos(
|
|||||||
uint16_t total);
|
uint16_t total);
|
||||||
|
|
||||||
/** Draw scrollbar on canvas.
|
/** Draw scrollbar on canvas.
|
||||||
* width 3px, height equal to canvas height
|
* @note width 3px, height equal to canvas height
|
||||||
* @param pos - current element of total elements
|
*
|
||||||
* @param total - total elements
|
* @param canvas Canvas instance
|
||||||
|
* @param pos current element of total elements
|
||||||
|
* @param total total elements
|
||||||
*/
|
*/
|
||||||
void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total);
|
void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total);
|
||||||
|
|
||||||
/** Draw rounded frame
|
/** Draw rounded frame
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param width, height - frame width and height
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param width, height frame width and height
|
||||||
*/
|
*/
|
||||||
void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
||||||
|
|
||||||
/** Draw button in left corner
|
/** Draw button in left corner
|
||||||
* @param str - button text
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param str button text
|
||||||
*/
|
*/
|
||||||
void elements_button_left(Canvas* canvas, const char* str);
|
void elements_button_left(Canvas* canvas, const char* str);
|
||||||
|
|
||||||
/** Draw button in right corner
|
/** Draw button in right corner
|
||||||
* @param str - button text
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param str button text
|
||||||
*/
|
*/
|
||||||
void elements_button_right(Canvas* canvas, const char* str);
|
void elements_button_right(Canvas* canvas, const char* str);
|
||||||
|
|
||||||
/** Draw button in center
|
/** Draw button in center
|
||||||
* @param str - button text
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param str button text
|
||||||
*/
|
*/
|
||||||
void elements_button_center(Canvas* canvas, const char* str);
|
void elements_button_center(Canvas* canvas, const char* str);
|
||||||
|
|
||||||
/** Draw aligned multiline text
|
/** Draw aligned multiline text
|
||||||
* @param x, y - coordinates based on align param
|
*
|
||||||
* @param horizontal, vertical - aligment of multiline text
|
* @param canvas Canvas instance
|
||||||
* @param text - string (possible multiline)
|
* @param x, y coordinates based on align param
|
||||||
|
* @param horizontal, vertical aligment of multiline text
|
||||||
|
* @param text string (possible multiline)
|
||||||
*/
|
*/
|
||||||
void elements_multiline_text_aligned(
|
void elements_multiline_text_aligned(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -88,20 +109,26 @@ void elements_multiline_text_aligned(
|
|||||||
const char* text);
|
const char* text);
|
||||||
|
|
||||||
/** Draw multiline text
|
/** Draw multiline text
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param text - string (possible multiline)
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param text string (possible multiline)
|
||||||
*/
|
*/
|
||||||
void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
||||||
|
|
||||||
/** Draw framed multiline text
|
/** Draw framed multiline text
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param text - string (possible multiline)
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param text string (possible multiline)
|
||||||
*/
|
*/
|
||||||
void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
||||||
|
|
||||||
/** Draw slightly rounded frame
|
/** Draw slightly rounded frame
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param width, height - size of frame
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param width, height size of frame
|
||||||
*/
|
*/
|
||||||
void elements_slightly_rounded_frame(
|
void elements_slightly_rounded_frame(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -111,8 +138,10 @@ void elements_slightly_rounded_frame(
|
|||||||
uint8_t height);
|
uint8_t height);
|
||||||
|
|
||||||
/** Draw slightly rounded box
|
/** Draw slightly rounded box
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param width, height - size of box
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param width, height size of box
|
||||||
*/
|
*/
|
||||||
void elements_slightly_rounded_box(
|
void elements_slightly_rounded_box(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -122,19 +151,47 @@ void elements_slightly_rounded_box(
|
|||||||
uint8_t height);
|
uint8_t height);
|
||||||
|
|
||||||
/** Draw bubble frame for text
|
/** Draw bubble frame for text
|
||||||
* @param x - left x coordinates
|
*
|
||||||
* @param y - top y coordinate
|
* @param canvas Canvas instance
|
||||||
* @param width - bubble width
|
* @param x left x coordinates
|
||||||
* @param height - bubble height
|
* @param y top y coordinate
|
||||||
|
* @param width bubble width
|
||||||
|
* @param height bubble height
|
||||||
*/
|
*/
|
||||||
void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
||||||
|
|
||||||
/** Trim string buffer to fit width in pixels
|
/** Trim string buffer to fit width in pixels
|
||||||
* @param string - string to trim
|
*
|
||||||
* @param width - max width
|
* @param canvas Canvas instance
|
||||||
|
* @param string string to trim
|
||||||
|
* @param width max width
|
||||||
*/
|
*/
|
||||||
void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width);
|
void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width);
|
||||||
|
|
||||||
|
/** Draw text box element
|
||||||
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param x x coordinate
|
||||||
|
* @param y y coordinate
|
||||||
|
* @param width width to fit text
|
||||||
|
* @param height height to fit text
|
||||||
|
* @param horizontal Align instance
|
||||||
|
* @param vertical Align instance
|
||||||
|
* @param[in] text Formatted text. The following formats are available:
|
||||||
|
* "\e#Bold text\e#" - bold font is used
|
||||||
|
* "\e*Monospaced text\e*" - monospaced font is used
|
||||||
|
* "\e#Inversed text\e#" - white text on black background
|
||||||
|
*/
|
||||||
|
void elements_text_box(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#include "gui_i.h"
|
#include "gui_i.h"
|
||||||
|
|
||||||
|
#define TAG "GuiSrv"
|
||||||
|
|
||||||
ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {
|
ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {
|
||||||
// Iterating backward
|
// Iterating backward
|
||||||
ViewPortArray_it_t it;
|
ViewPortArray_it_t it;
|
||||||
@ -189,8 +191,8 @@ void gui_input(Gui* gui, InputEvent* input_event) {
|
|||||||
} else if(input_event->type == InputTypePress) {
|
} else if(input_event->type == InputTypePress) {
|
||||||
gui->ongoing_input |= key_bit;
|
gui->ongoing_input |= key_bit;
|
||||||
} else if(!(gui->ongoing_input & key_bit)) {
|
} else if(!(gui->ongoing_input & key_bit)) {
|
||||||
FURI_LOG_W(
|
FURI_LOG_D(
|
||||||
"Gui",
|
TAG,
|
||||||
"non-complementary input, discarding key: %s type: %s, sequence: %p",
|
"non-complementary input, discarding key: %s type: %s, sequence: %p",
|
||||||
input_get_key_name(input_event->key),
|
input_get_key_name(input_event->key),
|
||||||
input_get_type_name(input_event->type),
|
input_get_type_name(input_event->type),
|
||||||
@ -211,8 +213,8 @@ void gui_input(Gui* gui, InputEvent* input_event) {
|
|||||||
if(view_port && view_port == gui->ongoing_input_view_port) {
|
if(view_port && view_port == gui->ongoing_input_view_port) {
|
||||||
view_port_input(view_port, input_event);
|
view_port_input(view_port, input_event);
|
||||||
} else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) {
|
} else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) {
|
||||||
FURI_LOG_W(
|
FURI_LOG_D(
|
||||||
"Gui",
|
TAG,
|
||||||
"ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
|
"ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
|
||||||
gui->ongoing_input_view_port,
|
gui->ongoing_input_view_port,
|
||||||
view_port,
|
view_port,
|
||||||
@ -221,8 +223,8 @@ void gui_input(Gui* gui, InputEvent* input_event) {
|
|||||||
input_event->sequence);
|
input_event->sequence);
|
||||||
view_port_input(gui->ongoing_input_view_port, input_event);
|
view_port_input(gui->ongoing_input_view_port, input_event);
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_W(
|
FURI_LOG_D(
|
||||||
"Gui",
|
TAG,
|
||||||
"ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p",
|
"ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p",
|
||||||
gui->ongoing_input_view_port,
|
gui->ongoing_input_view_port,
|
||||||
view_port,
|
view_port,
|
||||||
@ -258,8 +260,7 @@ void gui_cli_screen_stream_callback(uint8_t* data, size_t size, void* context) {
|
|||||||
void gui_cli_screen_stream(Cli* cli, string_t args, void* context) {
|
void gui_cli_screen_stream(Cli* cli, string_t args, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
Gui* gui = context;
|
Gui* gui = context;
|
||||||
gui_set_framebuffer_callback_context(gui, gui);
|
gui_set_framebuffer_callback(gui, gui_cli_screen_stream_callback, gui);
|
||||||
gui_set_framebuffer_callback(gui, gui_cli_screen_stream_callback);
|
|
||||||
gui_redraw(gui);
|
gui_redraw(gui);
|
||||||
|
|
||||||
// Wait for control events
|
// Wait for control events
|
||||||
@ -279,8 +280,7 @@ void gui_cli_screen_stream(Cli* cli, string_t args, void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gui_set_framebuffer_callback(gui, NULL);
|
gui_set_framebuffer_callback(gui, NULL, NULL);
|
||||||
gui_set_framebuffer_callback_context(gui, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) {
|
void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) {
|
||||||
@ -387,14 +387,12 @@ void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) {
|
|||||||
gui_unlock(gui);
|
gui_unlock(gui);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_set_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback) {
|
void gui_set_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) {
|
||||||
furi_assert(gui);
|
furi_assert(gui);
|
||||||
|
gui_lock(gui);
|
||||||
gui->canvas_callback = callback;
|
gui->canvas_callback = callback;
|
||||||
}
|
|
||||||
|
|
||||||
void gui_set_framebuffer_callback_context(Gui* gui, void* context) {
|
|
||||||
furi_assert(gui);
|
|
||||||
gui->canvas_callback_context = context;
|
gui->canvas_callback_context = context;
|
||||||
|
gui_unlock(gui);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gui* gui_alloc() {
|
Gui* gui_alloc() {
|
||||||
@ -414,7 +412,7 @@ Gui* gui_alloc() {
|
|||||||
gui->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
|
gui->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
|
||||||
gui->input_events = furi_record_open("input_events");
|
gui->input_events = furi_record_open("input_events");
|
||||||
furi_check(gui->input_events);
|
furi_check(gui->input_events);
|
||||||
subscribe_pubsub(gui->input_events, gui_input_events_callback, gui);
|
furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui);
|
||||||
// Cli
|
// Cli
|
||||||
gui->cli = furi_record_open("cli");
|
gui->cli = furi_record_open("cli");
|
||||||
cli_add_command(
|
cli_add_command(
|
||||||
|
|||||||
@ -73,15 +73,9 @@ void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port);
|
|||||||
*
|
*
|
||||||
* @param gui Gui instance
|
* @param gui Gui instance
|
||||||
* @param callback GuiCanvasCommitCallback
|
* @param callback GuiCanvasCommitCallback
|
||||||
|
* @param context GuiCanvasCommitCallback context
|
||||||
*/
|
*/
|
||||||
void gui_set_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback);
|
void gui_set_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context);
|
||||||
|
|
||||||
/** Set gui canvas commit callback context
|
|
||||||
*
|
|
||||||
* @param gui Gui instance
|
|
||||||
* @param context pointer to context
|
|
||||||
*/
|
|
||||||
void gui_set_framebuffer_callback_context(Gui* gui, void* context);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,7 @@ struct Gui {
|
|||||||
|
|
||||||
// Input
|
// Input
|
||||||
osMessageQueueId_t input_queue;
|
osMessageQueueId_t input_queue;
|
||||||
PubSub* input_events;
|
FuriPubSub* input_events;
|
||||||
uint8_t ongoing_input;
|
uint8_t ongoing_input;
|
||||||
ViewPort* ongoing_input_view_port;
|
ViewPort* ongoing_input_view_port;
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
#include "icon_i.h"
|
#include "icon_i.h"
|
||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <timers.h>
|
|
||||||
|
|
||||||
IconAnimation* icon_animation_alloc(const Icon* icon) {
|
IconAnimation* icon_animation_alloc(const Icon* icon) {
|
||||||
furi_assert(icon);
|
furi_assert(icon);
|
||||||
|
|||||||
@ -246,7 +246,7 @@ void button_menu_clean(ButtonMenu* button_menu) {
|
|||||||
|
|
||||||
with_view_model(
|
with_view_model(
|
||||||
button_menu->view, (ButtonMenuModel * model) {
|
button_menu->view, (ButtonMenuModel * model) {
|
||||||
ButtonMenuItemArray_clean(model->items);
|
ButtonMenuItemArray_reset(model->items);
|
||||||
model->position = 0;
|
model->position = 0;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -139,8 +139,8 @@ void button_panel_clean(ButtonPanel* button_panel) {
|
|||||||
}
|
}
|
||||||
model->reserve_x = 0;
|
model->reserve_x = 0;
|
||||||
model->reserve_y = 0;
|
model->reserve_y = 0;
|
||||||
LabelList_clean(model->labels);
|
LabelList_reset(model->labels);
|
||||||
ButtonMatrix_clean(model->button_matrix);
|
ButtonMatrix_reset(model->button_matrix);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -150,8 +150,8 @@ static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, siz
|
|||||||
|
|
||||||
furi_check(x < model->reserve_x);
|
furi_check(x < model->reserve_x);
|
||||||
furi_check(y < model->reserve_y);
|
furi_check(y < model->reserve_y);
|
||||||
ButtonArray_t* button_array = ButtonMatrix_get_at(model->button_matrix, x);
|
ButtonArray_t* button_array = ButtonMatrix_safe_get(model->button_matrix, x);
|
||||||
ButtonItem** button_item = ButtonArray_get_at(*button_array, y);
|
ButtonItem** button_item = ButtonArray_safe_get(*button_array, y);
|
||||||
return button_item;
|
return button_item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
475
applications/gui/modules/code_input.c
Normal file
475
applications/gui/modules/code_input.c
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
#include "code_input.h"
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
#define MAX_CODE_LEN 10
|
||||||
|
|
||||||
|
struct CodeInput {
|
||||||
|
View* view;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CodeInputStateVerify,
|
||||||
|
CodeInputStateUpdate,
|
||||||
|
CodeInputStateTotal,
|
||||||
|
} CodeInputStateEnum;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CodeInputFirst,
|
||||||
|
CodeInputSecond,
|
||||||
|
CodeInputTotal,
|
||||||
|
} CodeInputsEnum;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t state;
|
||||||
|
uint8_t current;
|
||||||
|
bool ext_update;
|
||||||
|
|
||||||
|
uint8_t input_length[CodeInputTotal];
|
||||||
|
uint8_t local_buffer[CodeInputTotal][MAX_CODE_LEN];
|
||||||
|
|
||||||
|
CodeInputOkCallback ok_callback;
|
||||||
|
CodeInputFailCallback fail_callback;
|
||||||
|
void* callback_context;
|
||||||
|
|
||||||
|
const char* header;
|
||||||
|
|
||||||
|
uint8_t* ext_buffer;
|
||||||
|
uint8_t* ext_buffer_length;
|
||||||
|
} CodeInputModel;
|
||||||
|
|
||||||
|
static const Icon* keys_assets[] = {
|
||||||
|
[InputKeyUp] = &I_ButtonUp_7x4,
|
||||||
|
[InputKeyDown] = &I_ButtonDown_7x4,
|
||||||
|
[InputKeyRight] = &I_ButtonRight_4x7,
|
||||||
|
[InputKeyLeft] = &I_ButtonLeft_4x7,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare buffers
|
||||||
|
*
|
||||||
|
* @param in Input buffer pointer
|
||||||
|
* @param len_in Input array length
|
||||||
|
* @param src Source buffer pointer
|
||||||
|
* @param len_src Source array length
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src) {
|
||||||
|
bool result = false;
|
||||||
|
do {
|
||||||
|
result = (len_in && len_src);
|
||||||
|
if(!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = (len_in == len_src);
|
||||||
|
if(!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < len_in; i++) {
|
||||||
|
result = (in[i] == src[i]);
|
||||||
|
if(!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare local buffers
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static bool code_input_compare_local(CodeInputModel* model) {
|
||||||
|
uint8_t* source = model->local_buffer[CodeInputFirst];
|
||||||
|
size_t source_length = model->input_length[CodeInputFirst];
|
||||||
|
|
||||||
|
uint8_t* input = model->local_buffer[CodeInputSecond];
|
||||||
|
size_t input_length = model->input_length[CodeInputSecond];
|
||||||
|
|
||||||
|
return code_input_compare(input, input_length, source, source_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare ext with local
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static bool code_input_compare_ext(CodeInputModel* model) {
|
||||||
|
uint8_t* input = model->local_buffer[CodeInputFirst];
|
||||||
|
size_t input_length = model->input_length[CodeInputFirst];
|
||||||
|
|
||||||
|
uint8_t* source = model->ext_buffer;
|
||||||
|
size_t source_length = *model->ext_buffer_length;
|
||||||
|
|
||||||
|
return code_input_compare(input, input_length, source, source_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set ext buffer
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_set_ext(CodeInputModel* model) {
|
||||||
|
*model->ext_buffer_length = model->input_length[CodeInputFirst];
|
||||||
|
for(size_t i = 0; i <= model->input_length[CodeInputFirst]; i++) {
|
||||||
|
model->ext_buffer[i] = model->local_buffer[CodeInputFirst][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw input sequence
|
||||||
|
*
|
||||||
|
* @param canvas
|
||||||
|
* @param buffer
|
||||||
|
* @param length
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param active
|
||||||
|
*/
|
||||||
|
static void code_input_draw_sequence(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t* buffer,
|
||||||
|
uint8_t length,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
bool active) {
|
||||||
|
uint8_t pos_x = x + 6;
|
||||||
|
uint8_t pos_y = y + 3;
|
||||||
|
|
||||||
|
if(active) canvas_draw_icon(canvas, x - 4, y + 5, &I_ButtonRightSmall_3x5);
|
||||||
|
|
||||||
|
elements_slightly_rounded_frame(canvas, x, y, 116, 15);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < length; i++) {
|
||||||
|
// maybe symmetrical assets? :-/
|
||||||
|
uint8_t offset_y = buffer[i] < 2 ? 2 + (buffer[i] * 2) : 1;
|
||||||
|
canvas_draw_icon(canvas, pos_x, pos_y + offset_y, keys_assets[buffer[i]]);
|
||||||
|
pos_x += buffer[i] > 1 ? 9 : 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset input count
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_reset_count(CodeInputModel* model) {
|
||||||
|
model->input_length[model->current] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Call input callback
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_call_ok_callback(CodeInputModel* model) {
|
||||||
|
if(model->ok_callback != NULL) {
|
||||||
|
model->ok_callback(model->callback_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Call changed callback
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_call_fail_callback(CodeInputModel* model) {
|
||||||
|
if(model->fail_callback != NULL) {
|
||||||
|
model->fail_callback(model->callback_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle Back button
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static bool code_input_handle_back(CodeInputModel* model) {
|
||||||
|
if(model->current && !model->input_length[model->current]) {
|
||||||
|
--model->current;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(model->input_length[model->current]) {
|
||||||
|
code_input_reset_count(model);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_input_call_fail_callback(model);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle OK button
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_handle_ok(CodeInputModel* model) {
|
||||||
|
switch(model->state) {
|
||||||
|
case CodeInputStateVerify:
|
||||||
|
|
||||||
|
if(code_input_compare_ext(model)) {
|
||||||
|
if(model->ext_update) {
|
||||||
|
model->state = CodeInputStateUpdate;
|
||||||
|
} else {
|
||||||
|
code_input_call_ok_callback(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code_input_reset_count(model);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CodeInputStateUpdate:
|
||||||
|
|
||||||
|
if(!model->current && model->input_length[model->current]) {
|
||||||
|
model->current++;
|
||||||
|
} else {
|
||||||
|
if(code_input_compare_local(model)) {
|
||||||
|
if(model->ext_update) {
|
||||||
|
code_input_set_ext(model);
|
||||||
|
}
|
||||||
|
code_input_call_ok_callback(model);
|
||||||
|
} else {
|
||||||
|
code_input_reset_count(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle input
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
|
||||||
|
size_t code_input_push(uint8_t* buffer, size_t length, InputKey key) {
|
||||||
|
buffer[length] = key;
|
||||||
|
length = CLAMP(length + 1, MAX_CODE_LEN, 0);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle D-pad keys
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
static void code_input_handle_dpad(CodeInputModel* model, InputKey key) {
|
||||||
|
uint8_t at = model->current;
|
||||||
|
size_t new_length = code_input_push(model->local_buffer[at], model->input_length[at], key);
|
||||||
|
model->input_length[at] = new_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw callback
|
||||||
|
*
|
||||||
|
* @param canvas
|
||||||
|
* @param _model
|
||||||
|
*/
|
||||||
|
static void code_input_view_draw_callback(Canvas* canvas, void* _model) {
|
||||||
|
CodeInputModel* model = _model;
|
||||||
|
uint8_t y_offset = 0;
|
||||||
|
if(!strlen(model->header)) y_offset = 5;
|
||||||
|
canvas_clear(canvas);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
|
canvas_draw_str(canvas, 2, 9, model->header);
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
switch(model->state) {
|
||||||
|
case CodeInputStateVerify:
|
||||||
|
code_input_draw_sequence(
|
||||||
|
canvas,
|
||||||
|
model->local_buffer[CodeInputFirst],
|
||||||
|
model->input_length[CodeInputFirst],
|
||||||
|
6,
|
||||||
|
30 - y_offset,
|
||||||
|
true);
|
||||||
|
break;
|
||||||
|
case CodeInputStateUpdate:
|
||||||
|
code_input_draw_sequence(
|
||||||
|
canvas,
|
||||||
|
model->local_buffer[CodeInputFirst],
|
||||||
|
model->input_length[CodeInputFirst],
|
||||||
|
6,
|
||||||
|
14 - y_offset,
|
||||||
|
!model->current);
|
||||||
|
code_input_draw_sequence(
|
||||||
|
canvas,
|
||||||
|
model->local_buffer[CodeInputSecond],
|
||||||
|
model->input_length[CodeInputSecond],
|
||||||
|
6,
|
||||||
|
44 - y_offset,
|
||||||
|
model->current);
|
||||||
|
|
||||||
|
if(model->current) canvas_draw_str(canvas, 2, 39 - y_offset, "Repeat code");
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Input callback
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* @param context
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
static bool code_input_view_input_callback(InputEvent* event, void* context) {
|
||||||
|
CodeInput* code_input = context;
|
||||||
|
furi_assert(code_input);
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||||
|
switch(event->key) {
|
||||||
|
case InputKeyBack:
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
consumed = code_input_handle_back(model);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InputKeyOk:
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
code_input_handle_ok(model);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
code_input_handle_dpad(model, event->key);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset all input-related data in model
|
||||||
|
*
|
||||||
|
* @param model CodeInputModel
|
||||||
|
*/
|
||||||
|
static void code_input_reset_model_input_data(CodeInputModel* model) {
|
||||||
|
model->current = 0;
|
||||||
|
model->input_length[CodeInputFirst] = 0;
|
||||||
|
model->input_length[CodeInputSecond] = 0;
|
||||||
|
model->ext_buffer = NULL;
|
||||||
|
model->ext_update = false;
|
||||||
|
model->state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocate and initialize code input. This code input is used to enter codes.
|
||||||
|
*
|
||||||
|
* @return CodeInput instance pointer
|
||||||
|
*/
|
||||||
|
CodeInput* code_input_alloc() {
|
||||||
|
CodeInput* code_input = furi_alloc(sizeof(CodeInput));
|
||||||
|
code_input->view = view_alloc();
|
||||||
|
view_set_context(code_input->view, code_input);
|
||||||
|
view_allocate_model(code_input->view, ViewModelTypeLocking, sizeof(CodeInputModel));
|
||||||
|
view_set_draw_callback(code_input->view, code_input_view_draw_callback);
|
||||||
|
view_set_input_callback(code_input->view, code_input_view_input_callback);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
model->header = "";
|
||||||
|
model->ok_callback = NULL;
|
||||||
|
model->fail_callback = NULL;
|
||||||
|
model->callback_context = NULL;
|
||||||
|
code_input_reset_model_input_data(model);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return code_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deinitialize and free code input
|
||||||
|
*
|
||||||
|
* @param code_input Code input instance
|
||||||
|
*/
|
||||||
|
void code_input_free(CodeInput* code_input) {
|
||||||
|
furi_assert(code_input);
|
||||||
|
view_free(code_input->view);
|
||||||
|
free(code_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get code input view
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @return View instance that can be used for embedding
|
||||||
|
*/
|
||||||
|
View* code_input_get_view(CodeInput* code_input) {
|
||||||
|
furi_assert(code_input);
|
||||||
|
return code_input->view;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set code input callbacks
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @param ok_callback input callback fn
|
||||||
|
* @param fail_callback code match callback fn
|
||||||
|
* @param callback_context callback context
|
||||||
|
* @param buffer buffer
|
||||||
|
* @param buffer_length ptr to buffer length uint
|
||||||
|
* @param ext_update true to update buffer
|
||||||
|
*/
|
||||||
|
void code_input_set_result_callback(
|
||||||
|
CodeInput* code_input,
|
||||||
|
CodeInputOkCallback ok_callback,
|
||||||
|
CodeInputFailCallback fail_callback,
|
||||||
|
void* callback_context,
|
||||||
|
uint8_t* buffer,
|
||||||
|
uint8_t* buffer_length,
|
||||||
|
bool ext_update) {
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
code_input_reset_model_input_data(model);
|
||||||
|
model->ok_callback = ok_callback;
|
||||||
|
model->fail_callback = fail_callback;
|
||||||
|
model->callback_context = callback_context;
|
||||||
|
|
||||||
|
model->ext_buffer = buffer;
|
||||||
|
model->ext_buffer_length = buffer_length;
|
||||||
|
model->state = (*buffer_length == 0) ? 1 : 0;
|
||||||
|
model->ext_update = ext_update;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set code input header text
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @param text text to be shown
|
||||||
|
*/
|
||||||
|
void code_input_set_header_text(CodeInput* code_input, const char* text) {
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
model->header = text;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
91
applications/gui/modules/code_input.h
Normal file
91
applications/gui/modules/code_input.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* @file code_input.h
|
||||||
|
* GUI: CodeInput keyboard view module API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Code input anonymous structure */
|
||||||
|
typedef struct CodeInput CodeInput;
|
||||||
|
|
||||||
|
/** callback that is executed when entered code matches ext buffer */
|
||||||
|
typedef void (*CodeInputOkCallback)(void* context);
|
||||||
|
|
||||||
|
/** callback that is executed when entered code does not matches ext buffer */
|
||||||
|
typedef void (*CodeInputFailCallback)(void* context);
|
||||||
|
|
||||||
|
/** Allocate and initialize code input. This code input is used to enter codes.
|
||||||
|
*
|
||||||
|
* @return CodeInput instance pointer
|
||||||
|
*/
|
||||||
|
CodeInput* code_input_alloc();
|
||||||
|
|
||||||
|
/** Deinitialize and free code input
|
||||||
|
*
|
||||||
|
* @param code_input Code input instance
|
||||||
|
*/
|
||||||
|
void code_input_free(CodeInput* code_input);
|
||||||
|
|
||||||
|
/** Get code input view
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
*
|
||||||
|
* @return View instance that can be used for embedding
|
||||||
|
*/
|
||||||
|
View* code_input_get_view(CodeInput* code_input);
|
||||||
|
|
||||||
|
/** Set code input result callback
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @param ok_callback ok callback fn
|
||||||
|
* @param fail_callback fail callback fn
|
||||||
|
* @param callback_context callback context
|
||||||
|
* @param buffer buffer to use
|
||||||
|
* @param buffer_length buffer length
|
||||||
|
* @param update set true to update buffer
|
||||||
|
*/
|
||||||
|
void code_input_set_result_callback(
|
||||||
|
CodeInput* code_input,
|
||||||
|
CodeInputOkCallback ok_callback,
|
||||||
|
CodeInputFailCallback fail_callback,
|
||||||
|
void* callback_context,
|
||||||
|
uint8_t* buffer,
|
||||||
|
uint8_t* buffer_length,
|
||||||
|
bool update);
|
||||||
|
|
||||||
|
/** Set code input header text
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @param text text to be shown
|
||||||
|
*/
|
||||||
|
void code_input_set_header_text(CodeInput* code_input, const char* text);
|
||||||
|
|
||||||
|
/** Compare two buffers
|
||||||
|
*
|
||||||
|
* @param in buffer to compare to source
|
||||||
|
* @param len_in length of input buffer
|
||||||
|
* @param src source buffer
|
||||||
|
* @param len_src length of insourceput buffer
|
||||||
|
* @return true if buffers match
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src);
|
||||||
|
|
||||||
|
/** Push input into the end of array
|
||||||
|
*
|
||||||
|
* @param buffer buffer
|
||||||
|
* @param length length of buffer
|
||||||
|
* @param key input key
|
||||||
|
* @return new length of input buffer
|
||||||
|
*/
|
||||||
|
size_t code_input_push(uint8_t* buffer, size_t length, InputKey key);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -178,7 +178,7 @@ void menu_clean(Menu* menu) {
|
|||||||
furi_assert(menu);
|
furi_assert(menu);
|
||||||
with_view_model(
|
with_view_model(
|
||||||
menu->view, (MenuModel * model) {
|
menu->view, (MenuModel * model) {
|
||||||
MenuItemArray_clean(model->items);
|
MenuItemArray_reset(model->items);
|
||||||
model->position = 0;
|
model->position = 0;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -174,7 +174,7 @@ void submenu_clean(Submenu* submenu) {
|
|||||||
|
|
||||||
with_view_model(
|
with_view_model(
|
||||||
submenu->view, (SubmenuModel * model) {
|
submenu->view, (SubmenuModel * model) {
|
||||||
SubmenuItemArray_clean(model->items);
|
SubmenuItemArray_reset(model->items);
|
||||||
model->position = 0;
|
model->position = 0;
|
||||||
model->window_position = 0;
|
model->window_position = 0;
|
||||||
model->header = NULL;
|
model->header = NULL;
|
||||||
|
|||||||
36
applications/gui/modules/variable-item-list.c
Executable file → Normal file
36
applications/gui/modules/variable-item-list.c
Executable file → Normal file
@ -84,6 +84,40 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
elements_scrollbar(canvas, model->position, VariableItemArray_size(model->items));
|
elements_scrollbar(canvas, model->position, VariableItemArray_size(model->items));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index) {
|
||||||
|
with_view_model(
|
||||||
|
variable_item_list->view, (VariableItemListModel * model) {
|
||||||
|
uint8_t position = index;
|
||||||
|
if(position >= VariableItemArray_size(model->items)) {
|
||||||
|
position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
model->position = position;
|
||||||
|
model->window_position = position;
|
||||||
|
|
||||||
|
if(model->window_position > 0) {
|
||||||
|
model->window_position -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(VariableItemArray_size(model->items) <= 4) {
|
||||||
|
model->window_position = 0;
|
||||||
|
} else {
|
||||||
|
if(model->window_position >= (VariableItemArray_size(model->items) - 4)) {
|
||||||
|
model->window_position = (VariableItemArray_size(model->items) - 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list) {
|
||||||
|
VariableItemListModel* model = view_get_model(variable_item_list->view);
|
||||||
|
uint8_t idx = model->position;
|
||||||
|
view_commit_model(variable_item_list->view, false);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
static bool variable_item_list_input_callback(InputEvent* event, void* context) {
|
static bool variable_item_list_input_callback(InputEvent* event, void* context) {
|
||||||
VariableItemList* variable_item_list = context;
|
VariableItemList* variable_item_list = context;
|
||||||
furi_assert(variable_item_list);
|
furi_assert(variable_item_list);
|
||||||
@ -261,7 +295,7 @@ void variable_item_list_clean(VariableItemList* variable_item_list) {
|
|||||||
VariableItemArray_next(it)) {
|
VariableItemArray_next(it)) {
|
||||||
string_clear(VariableItemArray_ref(it)->current_value_text);
|
string_clear(VariableItemArray_ref(it)->current_value_text);
|
||||||
}
|
}
|
||||||
VariableItemArray_clean(model->items);
|
VariableItemArray_reset(model->items);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,6 +70,10 @@ void variable_item_list_set_enter_callback(
|
|||||||
VariableItemListEnterCallback callback,
|
VariableItemListEnterCallback callback,
|
||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
|
void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index);
|
||||||
|
|
||||||
|
uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list);
|
||||||
|
|
||||||
/** Set item current selected index
|
/** Set item current selected index
|
||||||
*
|
*
|
||||||
* @param item VariableItem* instance
|
* @param item VariableItem* instance
|
||||||
|
|||||||
@ -81,7 +81,7 @@ void widget_clear(Widget* widget) {
|
|||||||
element->free(element);
|
element->free(element);
|
||||||
ElementArray_next(it);
|
ElementArray_next(it);
|
||||||
}
|
}
|
||||||
ElementArray_clean(model->element);
|
ElementArray_reset(model->element);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -146,6 +146,21 @@ void widget_add_string_element(
|
|||||||
widget_add_element(widget, string_element);
|
widget_add_element(widget, string_element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void widget_add_text_box_element(
|
||||||
|
Widget* widget,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text) {
|
||||||
|
furi_assert(widget);
|
||||||
|
WidgetElement* text_box_element =
|
||||||
|
widget_element_text_box_create(x, y, width, height, horizontal, vertical, text);
|
||||||
|
widget_add_element(widget, text_box_element);
|
||||||
|
}
|
||||||
|
|
||||||
void widget_add_button_element(
|
void widget_add_button_element(
|
||||||
Widget* widget,
|
Widget* widget,
|
||||||
GuiButtonType button_type,
|
GuiButtonType button_type,
|
||||||
|
|||||||
@ -75,6 +75,30 @@ void widget_add_string_element(
|
|||||||
Font font,
|
Font font,
|
||||||
const char* text);
|
const char* text);
|
||||||
|
|
||||||
|
/** Add Text Box Element
|
||||||
|
*
|
||||||
|
* @param widget Widget instance
|
||||||
|
* @param x x coordinate
|
||||||
|
* @param y y coordinate
|
||||||
|
* @param width width to fit text
|
||||||
|
* @param height height to fit text
|
||||||
|
* @param horizontal Align instance
|
||||||
|
* @param vertical Align instance
|
||||||
|
* @param[in] text Formatted text. The following formats are available:
|
||||||
|
* "\e#Bold text\e#" - bold font is used
|
||||||
|
* "\e*Monospaced text\e*" - monospaced font is used
|
||||||
|
* "\e#Inversed text\e#" - white text on black background
|
||||||
|
*/
|
||||||
|
void widget_add_text_box_element(
|
||||||
|
Widget* widget,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text);
|
||||||
|
|
||||||
/** Add Button Element
|
/** Add Button Element
|
||||||
*
|
*
|
||||||
* @param widget Widget instance
|
* @param widget Widget instance
|
||||||
|
|||||||
@ -52,6 +52,16 @@ WidgetElement* widget_element_string_create(
|
|||||||
Font font,
|
Font font,
|
||||||
const char* text);
|
const char* text);
|
||||||
|
|
||||||
|
/** Create text box element */
|
||||||
|
WidgetElement* widget_element_text_box_create(
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text);
|
||||||
|
|
||||||
/** Create button element */
|
/** Create button element */
|
||||||
WidgetElement* widget_element_button_create(
|
WidgetElement* widget_element_button_create(
|
||||||
GuiButtonType button_type,
|
GuiButtonType button_type,
|
||||||
|
|||||||
@ -0,0 +1,71 @@
|
|||||||
|
#include "widget_element_i.h"
|
||||||
|
#include <m-string.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t x;
|
||||||
|
uint8_t y;
|
||||||
|
uint8_t width;
|
||||||
|
uint8_t height;
|
||||||
|
Align horizontal;
|
||||||
|
Align vertical;
|
||||||
|
string_t text;
|
||||||
|
} GuiTextBoxModel;
|
||||||
|
|
||||||
|
static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) {
|
||||||
|
furi_assert(canvas);
|
||||||
|
furi_assert(element);
|
||||||
|
GuiTextBoxModel* model = element->model;
|
||||||
|
|
||||||
|
if(string_size(model->text)) {
|
||||||
|
elements_text_box(
|
||||||
|
canvas,
|
||||||
|
model->x,
|
||||||
|
model->y,
|
||||||
|
model->width,
|
||||||
|
model->height,
|
||||||
|
model->horizontal,
|
||||||
|
model->vertical,
|
||||||
|
string_get_cstr(model->text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gui_text_box_free(WidgetElement* gui_string) {
|
||||||
|
furi_assert(gui_string);
|
||||||
|
|
||||||
|
GuiTextBoxModel* model = gui_string->model;
|
||||||
|
string_clear(model->text);
|
||||||
|
free(gui_string->model);
|
||||||
|
free(gui_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetElement* widget_element_text_box_create(
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text) {
|
||||||
|
furi_assert(text);
|
||||||
|
|
||||||
|
// Allocate and init model
|
||||||
|
GuiTextBoxModel* model = furi_alloc(sizeof(GuiTextBoxModel));
|
||||||
|
model->x = x;
|
||||||
|
model->y = y;
|
||||||
|
model->width = width;
|
||||||
|
model->height = height;
|
||||||
|
model->horizontal = horizontal;
|
||||||
|
model->vertical = vertical;
|
||||||
|
string_init_set_str(model->text, text);
|
||||||
|
|
||||||
|
// Allocate and init Element
|
||||||
|
WidgetElement* gui_string = furi_alloc(sizeof(WidgetElement));
|
||||||
|
gui_string->parent = NULL;
|
||||||
|
gui_string->input = NULL;
|
||||||
|
gui_string->draw = gui_text_box_draw;
|
||||||
|
gui_string->free = gui_text_box_free;
|
||||||
|
gui_string->model = model;
|
||||||
|
|
||||||
|
return gui_string;
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
#include "view_dispatcher_i.h"
|
#include "view_dispatcher_i.h"
|
||||||
|
|
||||||
|
#define TAG "ViewDispatcher"
|
||||||
|
|
||||||
ViewDispatcher* view_dispatcher_alloc() {
|
ViewDispatcher* view_dispatcher_alloc() {
|
||||||
ViewDispatcher* view_dispatcher = furi_alloc(sizeof(ViewDispatcher));
|
ViewDispatcher* view_dispatcher = furi_alloc(sizeof(ViewDispatcher));
|
||||||
|
|
||||||
@ -236,8 +238,8 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
|
|||||||
} else if(event->type == InputTypeRelease) {
|
} else if(event->type == InputTypeRelease) {
|
||||||
view_dispatcher->ongoing_input &= ~key_bit;
|
view_dispatcher->ongoing_input &= ~key_bit;
|
||||||
} else if(!(view_dispatcher->ongoing_input & key_bit)) {
|
} else if(!(view_dispatcher->ongoing_input & key_bit)) {
|
||||||
FURI_LOG_W(
|
FURI_LOG_D(
|
||||||
"ViewDispatcher",
|
TAG,
|
||||||
"non-complementary input, discarding key: %s, type: %s, sequence: %p",
|
"non-complementary input, discarding key: %s, type: %s, sequence: %p",
|
||||||
input_get_key_name(event->key),
|
input_get_key_name(event->key),
|
||||||
input_get_type_name(event->type),
|
input_get_type_name(event->type),
|
||||||
@ -275,8 +277,8 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) {
|
} else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) {
|
||||||
FURI_LOG_W(
|
FURI_LOG_D(
|
||||||
"ViewDispatcher",
|
TAG,
|
||||||
"View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
|
"View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
|
||||||
view_dispatcher->ongoing_input_view,
|
view_dispatcher->ongoing_input_view,
|
||||||
view_dispatcher->current_view,
|
view_dispatcher->current_view,
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
#include <callback-connector.h>
|
#include <callback-connector.h>
|
||||||
#include <m-string.h>
|
#include <m-string.h>
|
||||||
#include <toolbox/path.h>
|
#include <toolbox/path.h>
|
||||||
#include <toolbox/flipper-file-cpp.h>
|
#include <flipper_file/flipper_file.h>
|
||||||
|
|
||||||
const char* iButtonApp::app_folder = "/any/ibutton";
|
const char* iButtonApp::app_folder = "/any/ibutton";
|
||||||
const char* iButtonApp::app_extension = ".ibtn";
|
const char* iButtonApp::app_extension = ".ibtn";
|
||||||
@ -48,8 +48,8 @@ iButtonApp::iButtonApp()
|
|||||||
iButtonApp::~iButtonApp() {
|
iButtonApp::~iButtonApp() {
|
||||||
for(std::map<Scene, iButtonScene*>::iterator it = scenes.begin(); it != scenes.end(); ++it) {
|
for(std::map<Scene, iButtonScene*>::iterator it = scenes.begin(); it != scenes.end(); ++it) {
|
||||||
delete it->second;
|
delete it->second;
|
||||||
scenes.erase(it);
|
|
||||||
}
|
}
|
||||||
|
scenes.clear();
|
||||||
delete key_worker;
|
delete key_worker;
|
||||||
|
|
||||||
furi_hal_power_insomnia_exit();
|
furi_hal_power_insomnia_exit();
|
||||||
@ -191,7 +191,7 @@ bool iButtonApp::save_key(const char* key_name) {
|
|||||||
// Create ibutton directory if necessary
|
// Create ibutton directory if necessary
|
||||||
make_app_folder();
|
make_app_folder();
|
||||||
|
|
||||||
FlipperFileCpp file(storage);
|
FlipperFile* file = flipper_file_alloc(storage);
|
||||||
string_t key_file_name;
|
string_t key_file_name;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
string_init(key_file_name);
|
string_init(key_file_name);
|
||||||
@ -207,27 +207,30 @@ bool iButtonApp::save_key(const char* key_name) {
|
|||||||
string_printf(key_file_name, "%s/%s%s", app_folder, key.get_name(), app_extension);
|
string_printf(key_file_name, "%s/%s%s", app_folder, key.get_name(), app_extension);
|
||||||
|
|
||||||
// Open file for write
|
// Open file for write
|
||||||
if(!file.new_write(string_get_cstr(key_file_name))) break;
|
if(!flipper_file_open_always(file, string_get_cstr(key_file_name))) break;
|
||||||
|
|
||||||
// Write header
|
// Write header
|
||||||
if(!file.write_header_cstr(iButtonApp::app_filetype, 1)) break;
|
if(!flipper_file_write_header_cstr(file, iButtonApp::app_filetype, 1)) break;
|
||||||
|
|
||||||
// Write key type
|
// Write key type
|
||||||
if(!file.write_comment_cstr("Key type can be Cyfral, Dallas or Metakom")) break;
|
if(!flipper_file_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom"))
|
||||||
|
break;
|
||||||
const char* key_type = key.get_key_type_string_by_type(key.get_key_type());
|
const char* key_type = key.get_key_type_string_by_type(key.get_key_type());
|
||||||
if(!file.write_string_cstr("Key type", key_type)) break;
|
if(!flipper_file_write_string_cstr(file, "Key type", key_type)) break;
|
||||||
|
|
||||||
// Write data
|
// Write data
|
||||||
if(!file.write_comment_cstr(
|
if(!flipper_file_write_comment_cstr(
|
||||||
"Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8"))
|
file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8"))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(!file.write_hex_array("Data", key.get_data(), key.get_type_data_size())) break;
|
if(!flipper_file_write_hex(file, "Data", key.get_data(), key.get_type_data_size())) break;
|
||||||
result = true;
|
result = true;
|
||||||
|
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
file.close();
|
flipper_file_close(file);
|
||||||
|
flipper_file_free(file);
|
||||||
|
|
||||||
string_clear(key_file_name);
|
string_clear(key_file_name);
|
||||||
|
|
||||||
if(!result) {
|
if(!result) {
|
||||||
@ -238,28 +241,29 @@ bool iButtonApp::save_key(const char* key_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool iButtonApp::load_key_data(string_t key_path) {
|
bool iButtonApp::load_key_data(string_t key_path) {
|
||||||
FlipperFileCpp file(storage);
|
FlipperFile* file = flipper_file_alloc(storage);
|
||||||
bool result = false;
|
bool result = false;
|
||||||
string_t data;
|
string_t data;
|
||||||
string_init(data);
|
string_init(data);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if(!file.open_read(string_get_cstr(key_path))) break;
|
if(!flipper_file_open_existing(file, string_get_cstr(key_path))) break;
|
||||||
|
|
||||||
// header
|
// header
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
if(!file.read_header(data, &version)) break;
|
if(!flipper_file_read_header(file, data, &version)) break;
|
||||||
if(string_cmp_str(data, iButtonApp::app_filetype) != 0) break;
|
if(string_cmp_str(data, iButtonApp::app_filetype) != 0) break;
|
||||||
if(version != 1) break;
|
if(version != 1) break;
|
||||||
|
|
||||||
// key type
|
// key type
|
||||||
iButtonKeyType type;
|
iButtonKeyType type;
|
||||||
if(!file.read_string("Key type", data)) break;
|
if(!flipper_file_read_string(file, "Key type", data)) break;
|
||||||
if(!key.get_key_type_by_type_string(string_get_cstr(data), &type)) break;
|
if(!key.get_key_type_by_type_string(string_get_cstr(data), &type)) break;
|
||||||
|
|
||||||
// key data
|
// key data
|
||||||
uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0};
|
uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0};
|
||||||
if(!file.read_hex_array("Data", key_data, key.get_type_data_size_by_type(type))) break;
|
if(!flipper_file_read_hex(file, "Data", key_data, key.get_type_data_size_by_type(type)))
|
||||||
|
break;
|
||||||
|
|
||||||
key.set_type(type);
|
key.set_type(type);
|
||||||
key.set_data(key_data, IBUTTON_KEY_DATA_SIZE);
|
key.set_data(key_data, IBUTTON_KEY_DATA_SIZE);
|
||||||
@ -267,7 +271,8 @@ bool iButtonApp::load_key_data(string_t key_path) {
|
|||||||
result = true;
|
result = true;
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
file.close();
|
flipper_file_close(file);
|
||||||
|
flipper_file_free(file);
|
||||||
string_clear(data);
|
string_clear(data);
|
||||||
|
|
||||||
if(!result) {
|
if(!result) {
|
||||||
|
|||||||
@ -28,11 +28,11 @@ void input_press_timer_callback(void* arg) {
|
|||||||
input_pin->press_counter++;
|
input_pin->press_counter++;
|
||||||
if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) {
|
if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) {
|
||||||
event.type = InputTypeLong;
|
event.type = InputTypeLong;
|
||||||
notify_pubsub(&input->event_pubsub, &event);
|
furi_pubsub_publish(input->event_pubsub, &event);
|
||||||
} else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) {
|
} else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) {
|
||||||
input_pin->press_counter--;
|
input_pin->press_counter--;
|
||||||
event.type = InputTypeRepeat;
|
event.type = InputTypeRepeat;
|
||||||
notify_pubsub(&input->event_pubsub, &event);
|
furi_pubsub_publish(input->event_pubsub, &event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ void input_cli_send(Cli* cli, string_t args, void* context) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Publish input event
|
// Publish input event
|
||||||
notify_pubsub(&input->event_pubsub, &event);
|
furi_pubsub_publish(input->event_pubsub, &event);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* input_get_key_name(InputKey key) {
|
const char* input_get_key_name(InputKey key) {
|
||||||
@ -120,8 +120,8 @@ const char* input_get_type_name(InputType type) {
|
|||||||
int32_t input_srv() {
|
int32_t input_srv() {
|
||||||
input = furi_alloc(sizeof(Input));
|
input = furi_alloc(sizeof(Input));
|
||||||
input->thread = osThreadGetId();
|
input->thread = osThreadGetId();
|
||||||
init_pubsub(&input->event_pubsub);
|
input->event_pubsub = furi_pubsub_alloc();
|
||||||
furi_record_create("input_events", &input->event_pubsub);
|
furi_record_create("input_events", input->event_pubsub);
|
||||||
|
|
||||||
input->cli = furi_record_open("cli");
|
input->cli = furi_record_open("cli");
|
||||||
if(input->cli) {
|
if(input->cli) {
|
||||||
@ -168,14 +168,14 @@ int32_t input_srv() {
|
|||||||
input_timer_stop(input->pin_states[i].press_timer);
|
input_timer_stop(input->pin_states[i].press_timer);
|
||||||
if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) {
|
if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) {
|
||||||
event.type = InputTypeShort;
|
event.type = InputTypeShort;
|
||||||
notify_pubsub(&input->event_pubsub, &event);
|
furi_pubsub_publish(input->event_pubsub, &event);
|
||||||
}
|
}
|
||||||
input->pin_states[i].press_counter = 0;
|
input->pin_states[i].press_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send Press/Release event
|
// Send Press/Release event
|
||||||
event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease;
|
event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease;
|
||||||
notify_pubsub(&input->event_pubsub, &event);
|
furi_pubsub_publish(input->event_pubsub, &event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ typedef enum {
|
|||||||
InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */
|
InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */
|
||||||
} InputType;
|
} InputType;
|
||||||
|
|
||||||
/** Input Event, dispatches with PubSub */
|
/** Input Event, dispatches with FuriPubSub */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t sequence;
|
uint32_t sequence;
|
||||||
InputKey key;
|
InputKey key;
|
||||||
|
|||||||
@ -6,8 +6,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include <FreeRTOS.h>
|
|
||||||
#include <timers.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -35,7 +33,7 @@ typedef struct {
|
|||||||
/** Input state */
|
/** Input state */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
osThreadId_t thread;
|
osThreadId_t thread;
|
||||||
PubSub event_pubsub;
|
FuriPubSub* event_pubsub;
|
||||||
InputPinState* pin_states;
|
InputPinState* pin_states;
|
||||||
Cli* cli;
|
Cli* cli;
|
||||||
volatile uint32_t counter;
|
volatile uint32_t counter;
|
||||||
|
|||||||
@ -11,6 +11,8 @@
|
|||||||
#include <furi-hal-irda.h>
|
#include <furi-hal-irda.h>
|
||||||
#include <file-worker-cpp.h>
|
#include <file-worker-cpp.h>
|
||||||
|
|
||||||
|
#define TAG "IrdaFileParser"
|
||||||
|
|
||||||
bool IrdaAppFileParser::open_irda_file_read(const char* name) {
|
bool IrdaAppFileParser::open_irda_file_read(const char* name) {
|
||||||
std::string full_filename;
|
std::string full_filename;
|
||||||
if(name[0] != '/')
|
if(name[0] != '/')
|
||||||
@ -154,11 +156,7 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
|||||||
if(!irda_is_protocol_valid((IrdaProtocol)protocol)) {
|
if(!irda_is_protocol_valid((IrdaProtocol)protocol)) {
|
||||||
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
"IrdaFileParser",
|
TAG, "Unknown protocol(\'%.*s...\'): \'%s\'", end_of_str, str.c_str(), protocol_name);
|
||||||
"Unknown protocol(\'%.*s...\'): \'%s\'",
|
|
||||||
end_of_str,
|
|
||||||
str.c_str(),
|
|
||||||
protocol_name);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +165,7 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
|||||||
if(address != (address & address_mask)) {
|
if(address != (address & address_mask)) {
|
||||||
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
"IrdaFileParser",
|
TAG,
|
||||||
"Signal(\'%.*s...\'): address is too long (mask for this protocol is 0x%08X): 0x%X",
|
"Signal(\'%.*s...\'): address is too long (mask for this protocol is 0x%08X): 0x%X",
|
||||||
end_of_str,
|
end_of_str,
|
||||||
str.c_str(),
|
str.c_str(),
|
||||||
@ -181,7 +179,7 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
|||||||
if(command != (command & command_mask)) {
|
if(command != (command & command_mask)) {
|
||||||
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
"IrdaFileParser",
|
TAG,
|
||||||
"Signal(\'%.*s...\'): command is too long (mask for this protocol is 0x%08X): 0x%X",
|
"Signal(\'%.*s...\'): command is too long (mask for this protocol is 0x%08X): 0x%X",
|
||||||
end_of_str,
|
end_of_str,
|
||||||
str.c_str(),
|
str.c_str(),
|
||||||
@ -256,7 +254,7 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
|||||||
if((frequency < IRDA_MIN_FREQUENCY) || (frequency > IRDA_MAX_FREQUENCY)) {
|
if((frequency < IRDA_MIN_FREQUENCY) || (frequency > IRDA_MAX_FREQUENCY)) {
|
||||||
size_t end_of_str = MIN(string.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
size_t end_of_str = MIN(string.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
"IrdaFileParser",
|
TAG,
|
||||||
"RAW signal(\'%.*s...\'): frequency is out of bounds (%ld-%ld): %ld",
|
"RAW signal(\'%.*s...\'): frequency is out of bounds (%ld-%ld): %ld",
|
||||||
end_of_str,
|
end_of_str,
|
||||||
string.c_str(),
|
string.c_str(),
|
||||||
@ -269,7 +267,7 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
|||||||
if((duty_cycle == 0) || (duty_cycle > 100)) {
|
if((duty_cycle == 0) || (duty_cycle > 100)) {
|
||||||
size_t end_of_str = MIN(string.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
size_t end_of_str = MIN(string.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
"IrdaFileParser",
|
TAG,
|
||||||
"RAW signal(\'%.*s...\'): duty cycle is out of bounds (0-100): %ld",
|
"RAW signal(\'%.*s...\'): duty cycle is out of bounds (0-100): %ld",
|
||||||
end_of_str,
|
end_of_str,
|
||||||
string.c_str(),
|
string.c_str(),
|
||||||
@ -283,8 +281,7 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
|||||||
if(last_valid_ch != std::string_view::npos) {
|
if(last_valid_ch != std::string_view::npos) {
|
||||||
str.remove_suffix(str.size() - last_valid_ch - 1);
|
str.remove_suffix(str.size() - last_valid_ch - 1);
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(TAG, "RAW signal(\'%.*s\'): no timings", header_len, string.c_str());
|
||||||
"IrdaFileParser", "RAW signal(\'%.*s\'): no timings", header_len, string.c_str());
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +300,7 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
|||||||
parsed = std::sscanf(str.data(), "%9s", buf);
|
parsed = std::sscanf(str.data(), "%9s", buf);
|
||||||
if(parsed != 1) {
|
if(parsed != 1) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
"IrdaFileParser",
|
TAG,
|
||||||
"RAW signal(\'%.*s...\'): failed on timing[%ld] \'%*s\'",
|
"RAW signal(\'%.*s...\'): failed on timing[%ld] \'%*s\'",
|
||||||
header_len,
|
header_len,
|
||||||
string.c_str(),
|
string.c_str(),
|
||||||
@ -318,7 +315,7 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
|||||||
int value = atoi(buf);
|
int value = atoi(buf);
|
||||||
if(value <= 0) {
|
if(value <= 0) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
"IrdaFileParser",
|
TAG,
|
||||||
"RAW signal(\'%.*s...\'): failed on timing[%ld] \'%s\'",
|
"RAW signal(\'%.*s...\'): failed on timing[%ld] \'%s\'",
|
||||||
header_len,
|
header_len,
|
||||||
string.c_str(),
|
string.c_str(),
|
||||||
@ -330,7 +327,7 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
|||||||
|
|
||||||
if(raw_signal.timings_cnt >= max_raw_timings_in_signal) {
|
if(raw_signal.timings_cnt >= max_raw_timings_in_signal) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
"IrdaFileParser",
|
TAG,
|
||||||
"RAW signal(\'%.*s...\'): too much timings (max %ld)",
|
"RAW signal(\'%.*s...\'): too much timings (max %ld)",
|
||||||
header_len,
|
header_len,
|
||||||
string.c_str(),
|
string.c_str(),
|
||||||
|
|||||||
@ -211,10 +211,12 @@ void IrdaApp::notify_red_blink() {
|
|||||||
notification_message(notification, &sequence_blink_red_10);
|
notification_message(notification, &sequence_blink_red_10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IrdaApp::notify_space_blink() {
|
void IrdaApp::notify_sent_just_learnt() {
|
||||||
static const NotificationSequence sequence = {
|
static const NotificationSequence sequence = {
|
||||||
&message_green_0,
|
&message_green_0,
|
||||||
|
&message_vibro_on,
|
||||||
&message_delay_50,
|
&message_delay_50,
|
||||||
|
&message_vibro_off,
|
||||||
&message_green_255,
|
&message_green_255,
|
||||||
&message_do_not_reset,
|
&message_do_not_reset,
|
||||||
NULL,
|
NULL,
|
||||||
@ -261,10 +263,6 @@ void IrdaApp::notify_blink_green() {
|
|||||||
notification_message(notification, &sequence);
|
notification_message(notification, &sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IrdaApp::notify_double_vibro() {
|
|
||||||
notification_message(notification, &sequence_double_vibro);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IrdaApp::notify_green_on() {
|
void IrdaApp::notify_green_on() {
|
||||||
notification_message(notification, &sequence_set_only_green_255);
|
notification_message(notification, &sequence_set_only_green_255);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,8 +77,7 @@ public:
|
|||||||
|
|
||||||
void notify_success();
|
void notify_success();
|
||||||
void notify_red_blink();
|
void notify_red_blink();
|
||||||
void notify_space_blink();
|
void notify_sent_just_learnt();
|
||||||
void notify_double_vibro();
|
|
||||||
void notify_green_on();
|
void notify_green_on();
|
||||||
void notify_green_off();
|
void notify_green_off();
|
||||||
void notify_click();
|
void notify_click();
|
||||||
|
|||||||
@ -23,16 +23,24 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) {
|
|||||||
|
|
||||||
if(!signal.is_raw()) {
|
if(!signal.is_raw()) {
|
||||||
auto message = &signal.get_message();
|
auto message = &signal.get_message();
|
||||||
|
uint8_t adr_digits = ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4);
|
||||||
|
uint8_t cmd_digits = ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4);
|
||||||
|
uint8_t max_digits = MAX(adr_digits, cmd_digits);
|
||||||
|
max_digits = MIN(max_digits, 7);
|
||||||
|
size_t label_x_offset = 63 + (7 - max_digits) * 3;
|
||||||
|
|
||||||
app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol));
|
app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol));
|
||||||
app->set_text_store(
|
app->set_text_store(
|
||||||
1,
|
1,
|
||||||
"A: 0x%0*lX\nC: 0x%0*lX\n",
|
"A: 0x%0*lX\nC: 0x%0*lX\n",
|
||||||
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
|
adr_digits,
|
||||||
message->address,
|
message->address,
|
||||||
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
|
cmd_digits,
|
||||||
message->command);
|
message->command);
|
||||||
dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter);
|
|
||||||
dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop);
|
dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 7, AlignCenter, AlignCenter);
|
||||||
|
dialog_ex_set_text(
|
||||||
|
dialog_ex, app->get_text_store(1), label_x_offset, 34, AlignLeft, AlignCenter);
|
||||||
} else {
|
} else {
|
||||||
dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter);
|
dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter);
|
||||||
app->set_text_store(0, "%d samples", signal.get_raw_signal().timings_cnt);
|
app->set_text_store(0, "%d samples", signal.get_raw_signal().timings_cnt);
|
||||||
@ -42,7 +50,7 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) {
|
|||||||
dialog_ex_set_left_button_text(dialog_ex, "Retry");
|
dialog_ex_set_left_button_text(dialog_ex, "Retry");
|
||||||
dialog_ex_set_right_button_text(dialog_ex, "Save");
|
dialog_ex_set_right_button_text(dialog_ex, "Save");
|
||||||
dialog_ex_set_center_button_text(dialog_ex, "Send");
|
dialog_ex_set_center_button_text(dialog_ex, "Send");
|
||||||
dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinExcited_64x63);
|
dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
|
||||||
dialog_ex_set_result_callback(dialog_ex, dialog_result_callback);
|
dialog_ex_set_result_callback(dialog_ex, dialog_result_callback);
|
||||||
dialog_ex_set_context(dialog_ex, app);
|
dialog_ex_set_context(dialog_ex, app);
|
||||||
|
|
||||||
@ -62,7 +70,7 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
|||||||
app->switch_to_next_scene_without_saving(IrdaApp::Scene::Learn);
|
app->switch_to_next_scene_without_saving(IrdaApp::Scene::Learn);
|
||||||
break;
|
break;
|
||||||
case DialogExResultCenter: {
|
case DialogExResultCenter: {
|
||||||
app->notify_space_blink();
|
app->notify_sent_just_learnt();
|
||||||
auto signal = app->get_received_signal();
|
auto signal = app->get_received_signal();
|
||||||
signal.transmit();
|
signal.transmit();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -39,10 +39,6 @@ void IrdaAppSceneLearn::on_enter(IrdaApp* app) {
|
|||||||
popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter);
|
popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter);
|
||||||
popup_set_callback(popup, NULL);
|
popup_set_callback(popup, NULL);
|
||||||
|
|
||||||
if(app->get_learn_new_remote()) {
|
|
||||||
app->notify_double_vibro();
|
|
||||||
}
|
|
||||||
|
|
||||||
view_manager->switch_to(IrdaAppViewManager::ViewType::Popup);
|
view_manager->switch_to(IrdaAppViewManager::ViewType::Popup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,8 +10,9 @@ RfidTimerEmulator::~RfidTimerEmulator() {
|
|||||||
|
|
||||||
for(it = encoders.begin(); it != encoders.end(); ++it) {
|
for(it = encoders.begin(); it != encoders.end(); ++it) {
|
||||||
delete it->second;
|
delete it->second;
|
||||||
encoders.erase(it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encoders.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) {
|
void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) {
|
||||||
|
|||||||
@ -16,8 +16,8 @@
|
|||||||
#include "scene/lfrfid-app-scene-delete-confirm.h"
|
#include "scene/lfrfid-app-scene-delete-confirm.h"
|
||||||
#include "scene/lfrfid-app-scene-delete-success.h"
|
#include "scene/lfrfid-app-scene-delete-success.h"
|
||||||
|
|
||||||
#include <lib/toolbox/path.h>
|
#include <toolbox/path.h>
|
||||||
#include <lib/toolbox/flipper-file-cpp.h>
|
#include <flipper_file/flipper_file.h>
|
||||||
|
|
||||||
const char* LfRfidApp::app_folder = "/any/lfrfid";
|
const char* LfRfidApp::app_folder = "/any/lfrfid";
|
||||||
const char* LfRfidApp::app_extension = ".rfid";
|
const char* LfRfidApp::app_extension = ".rfid";
|
||||||
@ -119,17 +119,17 @@ bool LfRfidApp::delete_key(RfidKey* key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool LfRfidApp::load_key_data(const char* path, RfidKey* key) {
|
bool LfRfidApp::load_key_data(const char* path, RfidKey* key) {
|
||||||
FlipperFileCpp file(storage);
|
FlipperFile* file = flipper_file_alloc(storage);
|
||||||
bool result = false;
|
bool result = false;
|
||||||
string_t str_result;
|
string_t str_result;
|
||||||
string_init(str_result);
|
string_init(str_result);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if(!file.open_read(path)) break;
|
if(!flipper_file_open_existing(file, path)) break;
|
||||||
|
|
||||||
// header
|
// header
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
if(!file.read_header(str_result, &version)) break;
|
if(!flipper_file_read_header(file, str_result, &version)) break;
|
||||||
if(string_cmp_str(str_result, app_filetype) != 0) break;
|
if(string_cmp_str(str_result, app_filetype) != 0) break;
|
||||||
if(version != 1) break;
|
if(version != 1) break;
|
||||||
|
|
||||||
@ -137,13 +137,13 @@ bool LfRfidApp::load_key_data(const char* path, RfidKey* key) {
|
|||||||
LfrfidKeyType type;
|
LfrfidKeyType type;
|
||||||
RfidKey loaded_key;
|
RfidKey loaded_key;
|
||||||
|
|
||||||
if(!file.read_string("Key type", str_result)) break;
|
if(!flipper_file_read_string(file, "Key type", str_result)) break;
|
||||||
if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &type)) break;
|
if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &type)) break;
|
||||||
loaded_key.set_type(type);
|
loaded_key.set_type(type);
|
||||||
|
|
||||||
// key data
|
// key data
|
||||||
uint8_t key_data[loaded_key.get_type_data_count()] = {};
|
uint8_t key_data[loaded_key.get_type_data_count()] = {};
|
||||||
if(!file.read_hex_array("Data", key_data, loaded_key.get_type_data_count())) break;
|
if(!flipper_file_read_hex(file, "Data", key_data, loaded_key.get_type_data_count())) break;
|
||||||
loaded_key.set_data(key_data, loaded_key.get_type_data_count());
|
loaded_key.set_data(key_data, loaded_key.get_type_data_count());
|
||||||
|
|
||||||
path_extract_filename_no_ext(path, str_result);
|
path_extract_filename_no_ext(path, str_result);
|
||||||
@ -153,7 +153,8 @@ bool LfRfidApp::load_key_data(const char* path, RfidKey* key) {
|
|||||||
result = true;
|
result = true;
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
file.close();
|
flipper_file_close(file);
|
||||||
|
flipper_file_free(file);
|
||||||
string_clear(str_result);
|
string_clear(str_result);
|
||||||
|
|
||||||
if(!result) {
|
if(!result) {
|
||||||
@ -164,21 +165,27 @@ bool LfRfidApp::load_key_data(const char* path, RfidKey* key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool LfRfidApp::save_key_data(const char* path, RfidKey* key) {
|
bool LfRfidApp::save_key_data(const char* path, RfidKey* key) {
|
||||||
FlipperFileCpp file(storage);
|
FlipperFile* file = flipper_file_alloc(storage);
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if(!file.new_write(path)) break;
|
if(!flipper_file_open_always(file, path)) break;
|
||||||
if(!file.write_header_cstr(app_filetype, 1)) break;
|
if(!flipper_file_write_header_cstr(file, app_filetype, 1)) break;
|
||||||
if(!file.write_comment_cstr("Key type can be EM4100, H10301 or I40134")) break;
|
if(!flipper_file_write_comment_cstr(file, "Key type can be EM4100, H10301 or I40134"))
|
||||||
if(!file.write_string_cstr("Key type", lfrfid_key_get_type_string(key->get_type()))) break;
|
break;
|
||||||
if(!file.write_comment_cstr("Data size for EM4100 is 5, for H10301 is 3, for I40134 is 3"))
|
if(!flipper_file_write_string_cstr(
|
||||||
|
file, "Key type", lfrfid_key_get_type_string(key->get_type())))
|
||||||
|
break;
|
||||||
|
if(!flipper_file_write_comment_cstr(
|
||||||
|
file, "Data size for EM4100 is 5, for H10301 is 3, for I40134 is 3"))
|
||||||
|
break;
|
||||||
|
if(!flipper_file_write_hex(file, "Data", key->get_data(), key->get_type_data_count()))
|
||||||
break;
|
break;
|
||||||
if(!file.write_hex_array("Data", key->get_data(), key->get_type_data_count())) break;
|
|
||||||
result = true;
|
result = true;
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
file.close();
|
flipper_file_close(file);
|
||||||
|
flipper_file_free(file);
|
||||||
|
|
||||||
if(!result) {
|
if(!result) {
|
||||||
dialog_message_show_storage_error(dialogs, "Cannot save\nkey file");
|
dialog_message_show_storage_error(dialogs, "Cannot save\nkey file");
|
||||||
|
|||||||
36
applications/loader/loader.c
Executable file → Normal file
36
applications/loader/loader.c
Executable file → Normal file
@ -1,6 +1,8 @@
|
|||||||
#include "loader/loader.h"
|
#include "loader/loader.h"
|
||||||
#include "loader_i.h"
|
#include "loader_i.h"
|
||||||
|
|
||||||
|
#define TAG "LoaderSrv"
|
||||||
|
|
||||||
#define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0)
|
#define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0)
|
||||||
#define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU)
|
#define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU)
|
||||||
|
|
||||||
@ -15,15 +17,13 @@ static void loader_menu_callback(void* _ctx, uint32_t index) {
|
|||||||
if(!loader_lock(loader_instance)) return;
|
if(!loader_lock(loader_instance)) return;
|
||||||
|
|
||||||
if(furi_thread_get_state(loader_instance->thread) != FuriThreadStateStopped) {
|
if(furi_thread_get_state(loader_instance->thread) != FuriThreadStateStopped) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(TAG, "Can't start app. %s is running", loader_instance->current_app->name);
|
||||||
LOADER_LOG_TAG, "Can't start app. %s is running", loader_instance->current_app->name);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
furi_hal_power_insomnia_enter();
|
furi_hal_power_insomnia_enter();
|
||||||
loader_instance->current_app = flipper_app;
|
loader_instance->current_app = flipper_app;
|
||||||
|
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(TAG, "Starting furi application: %s", loader_instance->current_app->name);
|
||||||
LOADER_LOG_TAG, "Starting furi application: %s", loader_instance->current_app->name);
|
|
||||||
furi_thread_set_name(loader_instance->thread, flipper_app->name);
|
furi_thread_set_name(loader_instance->thread, flipper_app->name);
|
||||||
furi_thread_set_stack_size(loader_instance->thread, flipper_app->stack_size);
|
furi_thread_set_stack_size(loader_instance->thread, flipper_app->stack_size);
|
||||||
furi_thread_set_context(loader_instance->thread, NULL);
|
furi_thread_set_context(loader_instance->thread, NULL);
|
||||||
@ -79,14 +79,14 @@ LoaderStatus loader_start(Loader* instance, const char* name, const char* args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!flipper_app) {
|
if(!flipper_app) {
|
||||||
FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name);
|
FURI_LOG_E(TAG, "Can't find application with name %s", name);
|
||||||
return LoaderStatusErrorUnknownApp;
|
return LoaderStatusErrorUnknownApp;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool locked = loader_lock(instance);
|
bool locked = loader_lock(instance);
|
||||||
|
|
||||||
if(!locked || (furi_thread_get_state(instance->thread) != FuriThreadStateStopped)) {
|
if(!locked || (furi_thread_get_state(instance->thread) != FuriThreadStateStopped)) {
|
||||||
FURI_LOG_E(LOADER_LOG_TAG, "Can't start app. %s is running", instance->current_app->name);
|
FURI_LOG_E(TAG, "Can't start app. %s is running", instance->current_app->name);
|
||||||
/* no need to call loader_unlock() - it is called as soon as application stops */
|
/* no need to call loader_unlock() - it is called as soon as application stops */
|
||||||
return LoaderStatusErrorAppStarted;
|
return LoaderStatusErrorAppStarted;
|
||||||
}
|
}
|
||||||
@ -97,10 +97,10 @@ LoaderStatus loader_start(Loader* instance, const char* name, const char* args)
|
|||||||
string_set_str(instance->args, args);
|
string_set_str(instance->args, args);
|
||||||
string_strim(instance->args);
|
string_strim(instance->args);
|
||||||
thread_args = (void*)string_get_cstr(instance->args);
|
thread_args = (void*)string_get_cstr(instance->args);
|
||||||
FURI_LOG_I(LOADER_LOG_TAG, "Start %s app with args: %s", name, args);
|
FURI_LOG_I(TAG, "Start %s app with args: %s", name, args);
|
||||||
} else {
|
} else {
|
||||||
string_clean(instance->args);
|
string_reset(instance->args);
|
||||||
FURI_LOG_I(LOADER_LOG_TAG, "Start %s app with no args", name);
|
FURI_LOG_I(TAG, "Start %s app with no args", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_thread_set_name(instance->thread, flipper_app->name);
|
furi_thread_set_name(instance->thread, flipper_app->name);
|
||||||
@ -140,7 +140,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
|
|||||||
Loader* instance = context;
|
Loader* instance = context;
|
||||||
|
|
||||||
if(thread_state == FuriThreadStateRunning) {
|
if(thread_state == FuriThreadStateRunning) {
|
||||||
instance->free_heap_size = xPortGetFreeHeapSize();
|
instance->free_heap_size = memmgr_get_free_heap();
|
||||||
} else if(thread_state == FuriThreadStateStopped) {
|
} else if(thread_state == FuriThreadStateStopped) {
|
||||||
/*
|
/*
|
||||||
* Current Leak Sanitizer assumes that memory is allocated and freed
|
* Current Leak Sanitizer assumes that memory is allocated and freed
|
||||||
@ -153,9 +153,9 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
|
|||||||
* both values should be taken into account.
|
* both values should be taken into account.
|
||||||
*/
|
*/
|
||||||
delay(20);
|
delay(20);
|
||||||
int heap_diff = instance->free_heap_size - xPortGetFreeHeapSize();
|
int heap_diff = instance->free_heap_size - memmgr_get_free_heap();
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(
|
||||||
LOADER_LOG_TAG,
|
TAG,
|
||||||
"Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.",
|
"Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.",
|
||||||
heap_diff,
|
heap_diff,
|
||||||
furi_thread_get_heap_size(instance->thread));
|
furi_thread_get_heap_size(instance->thread));
|
||||||
@ -266,7 +266,7 @@ static void loader_add_cli_command(FlipperApplication* app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void loader_build_menu() {
|
static void loader_build_menu() {
|
||||||
FURI_LOG_I(LOADER_LOG_TAG, "Building main menu");
|
FURI_LOG_I(TAG, "Building main menu");
|
||||||
size_t i;
|
size_t i;
|
||||||
for(i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
for(i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
||||||
loader_add_cli_command((FlipperApplication*)&FLIPPER_APPS[i]);
|
loader_add_cli_command((FlipperApplication*)&FLIPPER_APPS[i]);
|
||||||
@ -300,7 +300,7 @@ static void loader_build_menu() {
|
|||||||
loader_submenu_callback,
|
loader_submenu_callback,
|
||||||
(void*)LoaderMenuViewSettings);
|
(void*)LoaderMenuViewSettings);
|
||||||
|
|
||||||
FURI_LOG_I(LOADER_LOG_TAG, "Building plugins menu");
|
FURI_LOG_I(TAG, "Building plugins menu");
|
||||||
for(i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
|
for(i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
|
||||||
loader_add_cli_command((FlipperApplication*)&FLIPPER_PLUGINS[i]);
|
loader_add_cli_command((FlipperApplication*)&FLIPPER_PLUGINS[i]);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
@ -311,7 +311,7 @@ static void loader_build_menu() {
|
|||||||
(void*)&FLIPPER_PLUGINS[i]);
|
(void*)&FLIPPER_PLUGINS[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I(LOADER_LOG_TAG, "Building debug menu");
|
FURI_LOG_I(TAG, "Building debug menu");
|
||||||
for(i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
|
for(i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
|
||||||
loader_add_cli_command((FlipperApplication*)&FLIPPER_DEBUG_APPS[i]);
|
loader_add_cli_command((FlipperApplication*)&FLIPPER_DEBUG_APPS[i]);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
@ -322,7 +322,7 @@ static void loader_build_menu() {
|
|||||||
(void*)&FLIPPER_DEBUG_APPS[i]);
|
(void*)&FLIPPER_DEBUG_APPS[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I(LOADER_LOG_TAG, "Building settings menu");
|
FURI_LOG_I(TAG, "Building settings menu");
|
||||||
for(i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
|
for(i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
loader_instance->settings_menu,
|
loader_instance->settings_menu,
|
||||||
@ -339,7 +339,7 @@ void loader_show_menu() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t loader_srv(void* p) {
|
int32_t loader_srv(void* p) {
|
||||||
FURI_LOG_I(LOADER_LOG_TAG, "Starting");
|
FURI_LOG_I(TAG, "Starting");
|
||||||
|
|
||||||
loader_instance = loader_alloc();
|
loader_instance = loader_alloc();
|
||||||
|
|
||||||
@ -350,7 +350,7 @@ int32_t loader_srv(void* p) {
|
|||||||
FLIPPER_ON_SYSTEM_START[i]();
|
FLIPPER_ON_SYSTEM_START[i]();
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I(LOADER_LOG_TAG, "Started");
|
FURI_LOG_I(TAG, "Started");
|
||||||
|
|
||||||
furi_record_create("loader", loader_instance);
|
furi_record_create("loader", loader_instance);
|
||||||
|
|
||||||
|
|||||||
@ -12,8 +12,6 @@
|
|||||||
#include <applications.h>
|
#include <applications.h>
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
|
|
||||||
#define LOADER_LOG_TAG "loader"
|
|
||||||
|
|
||||||
struct Loader {
|
struct Loader {
|
||||||
osThreadId_t loader_thread;
|
osThreadId_t loader_thread;
|
||||||
FuriThread* thread;
|
FuriThread* thread;
|
||||||
|
|||||||
@ -1,49 +1,80 @@
|
|||||||
#include "nfc_emv_parser.h"
|
#include "nfc_emv_parser.h"
|
||||||
|
#include <lib/flipper_file/flipper_file.h>
|
||||||
|
|
||||||
#include <file-worker.h>
|
static const char* nfc_resources_header = "Flipper EMV resources";
|
||||||
|
static const uint32_t nfc_resources_file_version = 1;
|
||||||
|
|
||||||
static bool
|
static bool nfc_emv_parser_search_data(
|
||||||
nfc_emv_parser_get_value(const char* file_path, string_t key, char delimiter, string_t value) {
|
Storage* storage,
|
||||||
bool found = false;
|
const char* file_name,
|
||||||
FileWorker* file_worker = file_worker_alloc(true);
|
string_t key,
|
||||||
|
string_t data) {
|
||||||
|
bool parsed = false;
|
||||||
|
FlipperFile* file = flipper_file_alloc(storage);
|
||||||
|
string_t temp_str;
|
||||||
|
string_init(temp_str);
|
||||||
|
|
||||||
if(file_worker_open(file_worker, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
do {
|
||||||
if(file_worker_get_value_from_key(file_worker, key, delimiter, value)) {
|
// Open file
|
||||||
found = true;
|
if(!flipper_file_open_existing(file, file_name)) break;
|
||||||
}
|
// Read file header and version
|
||||||
|
uint32_t version = 0;
|
||||||
|
if(!flipper_file_read_header(file, temp_str, &version)) break;
|
||||||
|
if(string_cmp_str(temp_str, nfc_resources_header) ||
|
||||||
|
(version != nfc_resources_file_version))
|
||||||
|
break;
|
||||||
|
if(!flipper_file_read_string(file, string_get_cstr(key), data)) break;
|
||||||
|
parsed = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
string_clear(temp_str);
|
||||||
|
flipper_file_free(file);
|
||||||
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_worker_close(file_worker);
|
bool nfc_emv_parser_get_aid_name(
|
||||||
file_worker_free(file_worker);
|
Storage* storage,
|
||||||
return found;
|
uint8_t* aid,
|
||||||
}
|
uint8_t aid_len,
|
||||||
|
string_t aid_name) {
|
||||||
bool nfc_emv_parser_get_aid_name(uint8_t* aid, uint8_t aid_len, string_t aid_name) {
|
furi_assert(storage);
|
||||||
bool result = false;
|
bool parsed = false;
|
||||||
string_t key;
|
string_t key;
|
||||||
string_init(key);
|
string_init(key);
|
||||||
for(uint8_t i = 0; i < aid_len; i++) {
|
for(uint8_t i = 0; i < aid_len; i++) {
|
||||||
string_cat_printf(key, "%02X", aid[i]);
|
string_cat_printf(key, "%02X", aid[i]);
|
||||||
}
|
}
|
||||||
result = nfc_emv_parser_get_value("/ext/nfc/emv/aid.nfc", key, ' ', aid_name);
|
if(nfc_emv_parser_search_data(storage, "/ext/nfc/emv/aid.nfc", key, aid_name)) {
|
||||||
|
parsed = true;
|
||||||
|
}
|
||||||
string_clear(key);
|
string_clear(key);
|
||||||
return result;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_emv_parser_get_country_name(uint16_t country_code, string_t country_name) {
|
bool nfc_emv_parser_get_country_name(
|
||||||
bool result = false;
|
Storage* storage,
|
||||||
|
uint16_t country_code,
|
||||||
|
string_t country_name) {
|
||||||
|
bool parsed = false;
|
||||||
string_t key;
|
string_t key;
|
||||||
string_init_printf(key, "%04X", country_code);
|
string_init_printf(key, "%04X", country_code);
|
||||||
result = nfc_emv_parser_get_value("/ext/nfc/emv/country_code.nfc", key, ' ', country_name);
|
if(nfc_emv_parser_search_data(storage, "/ext/nfc/emv/country_code.nfc", key, country_name)) {
|
||||||
|
parsed = true;
|
||||||
|
}
|
||||||
string_clear(key);
|
string_clear(key);
|
||||||
return result;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_emv_parser_get_currency_name(uint16_t currency_code, string_t currency_name) {
|
bool nfc_emv_parser_get_currency_name(
|
||||||
bool result = false;
|
Storage* storage,
|
||||||
|
uint16_t currency_code,
|
||||||
|
string_t currency_name) {
|
||||||
|
bool parsed = false;
|
||||||
string_t key;
|
string_t key;
|
||||||
string_init_printf(key, "%04X", currency_code);
|
string_init_printf(key, "%04X", currency_code);
|
||||||
result = nfc_emv_parser_get_value("/ext/nfc/emv/currency_code.nfc", key, ' ', currency_name);
|
if(nfc_emv_parser_search_data(storage, "/ext/nfc/emv/currency_code.nfc", key, currency_name)) {
|
||||||
string_clear(key);
|
parsed = true;
|
||||||
return result;
|
}
|
||||||
|
string_clear(key);
|
||||||
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,25 +3,39 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <m-string.h>
|
#include <m-string.h>
|
||||||
|
#include <storage/storage.h>
|
||||||
|
|
||||||
/** Get EMV application name by number
|
/** Get EMV application name by number
|
||||||
|
* @param storage Storage instance
|
||||||
* @param aid - AID number array
|
* @param aid - AID number array
|
||||||
* @param aid_len - AID length
|
* @param aid_len - AID length
|
||||||
* @param aid_name - string to keep AID name
|
* @param aid_name - string to keep AID name
|
||||||
* @return - true if AID found, false otherwies
|
* @return - true if AID found, false otherwies
|
||||||
*/
|
*/
|
||||||
bool nfc_emv_parser_get_aid_name(uint8_t* aid, uint8_t aid_len, string_t aid_name);
|
bool nfc_emv_parser_get_aid_name(
|
||||||
|
Storage* storage,
|
||||||
|
uint8_t* aid,
|
||||||
|
uint8_t aid_len,
|
||||||
|
string_t aid_name);
|
||||||
|
|
||||||
/** Get country name by country code
|
/** Get country name by country code
|
||||||
|
* @param storage Storage instance
|
||||||
* @param country_code - ISO 3166 country code
|
* @param country_code - ISO 3166 country code
|
||||||
* @param country_name - string to keep country name
|
* @param country_name - string to keep country name
|
||||||
* @return - true if country found, false otherwies
|
* @return - true if country found, false otherwies
|
||||||
*/
|
*/
|
||||||
bool nfc_emv_parser_get_country_name(uint16_t country_code, string_t country_name);
|
bool nfc_emv_parser_get_country_name(
|
||||||
|
Storage* storage,
|
||||||
|
uint16_t country_code,
|
||||||
|
string_t country_name);
|
||||||
|
|
||||||
/** Get currency name by currency code
|
/** Get currency name by currency code
|
||||||
|
* @param storage Storage instance
|
||||||
* @param currency_code - ISO 3166 currency code
|
* @param currency_code - ISO 3166 currency code
|
||||||
* @param currency_name - string to keep currency name
|
* @param currency_name - string to keep currency name
|
||||||
* @return - true if currency found, false otherwies
|
* @return - true if currency found, false otherwies
|
||||||
*/
|
*/
|
||||||
bool nfc_emv_parser_get_currency_name(uint16_t currency_code, string_t currency_name);
|
bool nfc_emv_parser_get_currency_name(
|
||||||
|
Storage* storage,
|
||||||
|
uint16_t currency_code,
|
||||||
|
string_t currency_name);
|
||||||
|
|||||||
@ -31,6 +31,9 @@ Nfc* nfc_alloc() {
|
|||||||
view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback);
|
view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback);
|
||||||
view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100);
|
view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100);
|
||||||
|
|
||||||
|
// Nfc device
|
||||||
|
nfc->dev = nfc_device_alloc();
|
||||||
|
|
||||||
// Open GUI record
|
// Open GUI record
|
||||||
nfc->gui = furi_record_open("gui");
|
nfc->gui = furi_record_open("gui");
|
||||||
view_dispatcher_attach_to_gui(nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
|
view_dispatcher_attach_to_gui(nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
|
||||||
@ -82,6 +85,9 @@ Nfc* nfc_alloc() {
|
|||||||
void nfc_free(Nfc* nfc) {
|
void nfc_free(Nfc* nfc) {
|
||||||
furi_assert(nfc);
|
furi_assert(nfc);
|
||||||
|
|
||||||
|
// Nfc device
|
||||||
|
nfc_device_free(nfc->dev);
|
||||||
|
|
||||||
// Submenu
|
// Submenu
|
||||||
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewMenu);
|
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewMenu);
|
||||||
submenu_free(nfc->submenu);
|
submenu_free(nfc->submenu);
|
||||||
@ -154,8 +160,12 @@ int32_t nfc_app(void* p) {
|
|||||||
char* args = p;
|
char* args = p;
|
||||||
|
|
||||||
// Check argument and run corresponding scene
|
// Check argument and run corresponding scene
|
||||||
if((*args != '\0') && nfc_device_load(&nfc->dev, p)) {
|
if((*args != '\0') && nfc_device_load(nfc->dev, p)) {
|
||||||
|
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
|
||||||
|
} else {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,45 +1,38 @@
|
|||||||
#include "nfc_device_i.h"
|
#include "nfc_device.h"
|
||||||
|
|
||||||
#include <file-worker.h>
|
|
||||||
#include <lib/toolbox/path.h>
|
#include <lib/toolbox/path.h>
|
||||||
#include <lib/toolbox/hex.h>
|
#include <lib/flipper_file/flipper_file.h>
|
||||||
|
|
||||||
#define NFC_DEVICE_MAX_DATA_LEN 14
|
|
||||||
|
|
||||||
static const char* nfc_app_folder = "/any/nfc";
|
static const char* nfc_app_folder = "/any/nfc";
|
||||||
static const char* nfc_app_extension = ".nfc";
|
static const char* nfc_app_extension = ".nfc";
|
||||||
static const char* nfc_app_shadow_extension = ".shd";
|
static const char* nfc_app_shadow_extension = ".shd";
|
||||||
|
static const char* nfc_file_header = "Flipper NFC device";
|
||||||
|
static const uint32_t nfc_file_version = 2;
|
||||||
|
|
||||||
static bool nfc_device_read_hex(string_t str, uint8_t* buff, uint16_t len, uint8_t delim_len) {
|
NfcDevice* nfc_device_alloc() {
|
||||||
string_strim(str);
|
NfcDevice* nfc_dev = furi_alloc(sizeof(NfcDevice));
|
||||||
uint8_t nibble_high = 0;
|
nfc_dev->storage = furi_record_open("storage");
|
||||||
uint8_t nibble_low = 0;
|
nfc_dev->dialogs = furi_record_open("dialogs");
|
||||||
bool parsed = true;
|
return nfc_dev;
|
||||||
|
|
||||||
for(uint16_t i = 0; i < len; i++) {
|
|
||||||
if(hex_char_to_hex_nibble(string_get_char(str, 0), &nibble_high) &&
|
|
||||||
hex_char_to_hex_nibble(string_get_char(str, 1), &nibble_low)) {
|
|
||||||
buff[i] = (nibble_high << 4) | nibble_low;
|
|
||||||
string_right(str, delim_len + 2);
|
|
||||||
} else {
|
|
||||||
parsed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parsed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string) {
|
void nfc_device_free(NfcDevice* nfc_dev) {
|
||||||
|
furi_assert(nfc_dev);
|
||||||
|
furi_record_close("storage");
|
||||||
|
furi_record_close("dialogs");
|
||||||
|
free(nfc_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string) {
|
||||||
if(dev->format == NfcDeviceSaveFormatUid) {
|
if(dev->format == NfcDeviceSaveFormatUid) {
|
||||||
string_set_str(format_string, "UID\n");
|
string_set_str(format_string, "UID");
|
||||||
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
||||||
string_set_str(format_string, "Bank card\n");
|
string_set_str(format_string, "Bank card");
|
||||||
} else if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
} else if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||||
string_set_str(format_string, "Mifare Ultralight\n");
|
string_set_str(format_string, "Mifare Ultralight");
|
||||||
} else {
|
} else {
|
||||||
string_set_str(format_string, "Unknown\n");
|
string_set_str(format_string, "Unknown");
|
||||||
}
|
}
|
||||||
return string_size(format_string);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) {
|
bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) {
|
||||||
@ -59,228 +52,166 @@ bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t nfc_device_prepare_uid_string(NfcDevice* dev, string_t uid_string) {
|
static bool nfc_device_save_mifare_ul_data(FlipperFile* file, NfcDevice* dev) {
|
||||||
NfcDeviceCommonData* uid_data = &dev->dev_data.nfc_data;
|
bool saved = false;
|
||||||
string_printf(uid_string, "UID len: %02X UID: ", dev->dev_data.nfc_data.uid_len);
|
|
||||||
for(uint8_t i = 0; i < uid_data->uid_len; i++) {
|
|
||||||
string_cat_printf(uid_string, "%02X ", uid_data->uid[i]);
|
|
||||||
}
|
|
||||||
string_cat_printf(
|
|
||||||
uid_string,
|
|
||||||
"ATQA: %02X %02X SAK: %02X\n",
|
|
||||||
uid_data->atqa[0],
|
|
||||||
uid_data->atqa[1],
|
|
||||||
uid_data->sak);
|
|
||||||
return string_size(uid_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_device_parse_uid_string(NfcDevice* dev, string_t uid_string) {
|
|
||||||
NfcDeviceCommonData* uid_data = &dev->dev_data.nfc_data;
|
|
||||||
bool parsed = false;
|
|
||||||
|
|
||||||
do {
|
|
||||||
// strlen("UID len: ") = 9
|
|
||||||
string_right(uid_string, 9);
|
|
||||||
if(!nfc_device_read_hex(uid_string, &uid_data->uid_len, 1, 1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// strlen("UID: ") = 5
|
|
||||||
string_right(uid_string, 5);
|
|
||||||
if(!nfc_device_read_hex(uid_string, uid_data->uid, uid_data->uid_len, 1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// strlen("ATQA: ") = 6
|
|
||||||
string_right(uid_string, 6);
|
|
||||||
if(!nfc_device_read_hex(uid_string, uid_data->atqa, 2, 1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// strlen("SAK: ") = 5
|
|
||||||
string_right(uid_string, 5);
|
|
||||||
if(!nfc_device_read_hex(uid_string, &uid_data->sak, 1, 1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
parsed = true;
|
|
||||||
} while(0);
|
|
||||||
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t nfc_device_prepare_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string) {
|
|
||||||
MifareUlData* data = &dev->dev_data.mf_ul_data;
|
MifareUlData* data = &dev->dev_data.mf_ul_data;
|
||||||
string_printf(mifare_ul_string, "Signature:");
|
string_t temp_str;
|
||||||
for(uint8_t i = 0; i < sizeof(data->signature); i++) {
|
string_init(temp_str);
|
||||||
string_cat_printf(mifare_ul_string, " %02X", data->signature[i]);
|
|
||||||
}
|
// Save Mifare Ultralight specific data
|
||||||
string_cat_printf(mifare_ul_string, "\nVersion:");
|
do {
|
||||||
uint8_t* version = (uint8_t*)&data->version;
|
if(!flipper_file_write_comment_cstr(file, "Mifare Ultralight specific data")) break;
|
||||||
for(uint8_t i = 0; i < sizeof(data->version); i++) {
|
if(!flipper_file_write_hex(file, "Signature", data->signature, sizeof(data->signature)))
|
||||||
string_cat_printf(mifare_ul_string, " %02X", version[i]);
|
break;
|
||||||
}
|
if(!flipper_file_write_hex(
|
||||||
|
file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version)))
|
||||||
|
break;
|
||||||
|
// Write conters and tearing flags data
|
||||||
|
bool counters_saved = true;
|
||||||
for(uint8_t i = 0; i < 3; i++) {
|
for(uint8_t i = 0; i < 3; i++) {
|
||||||
string_cat_printf(
|
string_printf(temp_str, "Counter %d", i);
|
||||||
mifare_ul_string,
|
if(!flipper_file_write_uint32(file, string_get_cstr(temp_str), &data->counter[i], 1)) {
|
||||||
"\nCounter %d: %lu Tearing flag %d: %02X",
|
counters_saved = false;
|
||||||
i,
|
break;
|
||||||
data->counter[i],
|
|
||||||
i,
|
|
||||||
data->tearing[i]);
|
|
||||||
}
|
}
|
||||||
string_cat_printf(mifare_ul_string, "\nData size: %d\n", data->data_size);
|
string_printf(temp_str, "Tearing %d", i);
|
||||||
|
if(!flipper_file_write_hex(file, string_get_cstr(temp_str), &data->tearing[i], 1)) {
|
||||||
|
counters_saved = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!counters_saved) break;
|
||||||
|
// Write pages data
|
||||||
|
uint32_t pages_total = data->data_size / 4;
|
||||||
|
if(!flipper_file_write_uint32(file, "Pages total", &pages_total, 1)) break;
|
||||||
|
bool pages_saved = true;
|
||||||
for(uint16_t i = 0; i < data->data_size; i += 4) {
|
for(uint16_t i = 0; i < data->data_size; i += 4) {
|
||||||
string_cat_printf(
|
string_printf(temp_str, "Page %d", i / 4);
|
||||||
mifare_ul_string,
|
if(!flipper_file_write_hex(file, string_get_cstr(temp_str), &data->data[i], 4)) {
|
||||||
"%02X %02X %02X %02X\n",
|
pages_saved = false;
|
||||||
data->data[i],
|
break;
|
||||||
data->data[i + 1],
|
|
||||||
data->data[i + 2],
|
|
||||||
data->data[i + 3]);
|
|
||||||
}
|
}
|
||||||
return string_size(mifare_ul_string);
|
}
|
||||||
|
if(!pages_saved) break;
|
||||||
|
saved = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
string_clear(temp_str);
|
||||||
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_device_parse_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string) {
|
bool nfc_device_load_mifare_ul_data(FlipperFile* file, NfcDevice* dev) {
|
||||||
MifareUlData* data = &dev->dev_data.mf_ul_data;
|
|
||||||
uint16_t tearing_tmp = 0;
|
|
||||||
uint16_t cnt_num = 0;
|
|
||||||
size_t ws = 0;
|
|
||||||
int res = 0;
|
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
MifareUlData* data = &dev->dev_data.mf_ul_data;
|
||||||
|
string_t temp_str;
|
||||||
|
string_init(temp_str);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// strlen("Signature: ") = 11
|
// Read signature
|
||||||
string_right(mifare_ul_string, 11);
|
if(!flipper_file_read_hex(file, "Signature", data->signature, sizeof(data->signature)))
|
||||||
if(!nfc_device_read_hex(mifare_ul_string, data->signature, sizeof(data->signature), 1)) {
|
|
||||||
break;
|
break;
|
||||||
}
|
// Read Mifare version
|
||||||
// strlen("Version: ") = 9
|
if(!flipper_file_read_hex(
|
||||||
string_right(mifare_ul_string, 9);
|
file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version)))
|
||||||
if(!nfc_device_read_hex(
|
|
||||||
mifare_ul_string, (uint8_t*)&data->version, sizeof(data->version), 1)) {
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
string_strim(mifare_ul_string);
|
|
||||||
// Read counters and tearing flags
|
// Read counters and tearing flags
|
||||||
|
bool counters_parsed = true;
|
||||||
for(uint8_t i = 0; i < 3; i++) {
|
for(uint8_t i = 0; i < 3; i++) {
|
||||||
res = sscanf(
|
string_printf(temp_str, "Counter %d", i);
|
||||||
string_get_cstr(mifare_ul_string),
|
if(!flipper_file_read_uint32(file, string_get_cstr(temp_str), &data->counter[i], 1)) {
|
||||||
"Counter %hX: %lu Tearing flag %hX: %02hX",
|
counters_parsed = false;
|
||||||
&cnt_num,
|
|
||||||
&data->counter[i],
|
|
||||||
&cnt_num,
|
|
||||||
&tearing_tmp);
|
|
||||||
if(res != 4) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
data->tearing[i] = tearing_tmp;
|
string_printf(temp_str, "Tearing %d", i);
|
||||||
ws = string_search_char(mifare_ul_string, '\n');
|
if(!flipper_file_read_hex(file, string_get_cstr(temp_str), &data->tearing[i], 1)) {
|
||||||
string_right(mifare_ul_string, ws + 1);
|
counters_parsed = false;
|
||||||
}
|
|
||||||
// Read data size
|
|
||||||
res = sscanf(string_get_cstr(mifare_ul_string), "Data size: %hu", &data->data_size);
|
|
||||||
if(res != 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ws = string_search_char(mifare_ul_string, '\n');
|
|
||||||
string_right(mifare_ul_string, ws + 1);
|
|
||||||
// Read data
|
|
||||||
for(uint16_t i = 0; i < data->data_size; i += 4) {
|
|
||||||
if(!nfc_device_read_hex(mifare_ul_string, &data->data[i], 4, 1)) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(!counters_parsed) break;
|
||||||
|
// Read pages
|
||||||
|
uint32_t pages = 0;
|
||||||
|
if(!flipper_file_read_uint32(file, "Pages total", &pages, 1)) break;
|
||||||
|
data->data_size = pages * 4;
|
||||||
|
bool pages_parsed = true;
|
||||||
|
for(uint16_t i = 0; i < pages; i++) {
|
||||||
|
string_printf(temp_str, "Page %d", i);
|
||||||
|
if(!flipper_file_read_hex(file, string_get_cstr(temp_str), &data->data[i * 4], 4)) {
|
||||||
|
pages_parsed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!pages_parsed) break;
|
||||||
parsed = true;
|
parsed = true;
|
||||||
} while(0);
|
} while(false);
|
||||||
|
|
||||||
|
string_clear(temp_str);
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_string) {
|
static bool nfc_device_save_bank_card_data(FlipperFile* file, NfcDevice* dev) {
|
||||||
|
bool saved = false;
|
||||||
NfcEmvData* data = &dev->dev_data.emv_data;
|
NfcEmvData* data = &dev->dev_data.emv_data;
|
||||||
string_printf(bank_card_string, "AID len: %d, AID:", data->aid_len);
|
uint32_t data_temp = 0;
|
||||||
for(uint8_t i = 0; i < data->aid_len; i++) {
|
|
||||||
string_cat_printf(bank_card_string, " %02X", data->aid[i]);
|
do {
|
||||||
}
|
// Write Bank card specific data
|
||||||
string_cat_printf(
|
if(!flipper_file_write_comment_cstr(file, "Bank card specific data")) break;
|
||||||
bank_card_string, "\nName: %s\nNumber len: %d\nNumber:", data->name, data->number_len);
|
if(!flipper_file_write_hex(file, "AID", data->aid, data->aid_len)) break;
|
||||||
for(uint8_t i = 0; i < data->number_len; i++) {
|
if(!flipper_file_write_string_cstr(file, "Name", data->name)) break;
|
||||||
string_cat_printf(bank_card_string, " %02X", data->number[i]);
|
if(!flipper_file_write_hex(file, "Number", data->number, data->number_len)) break;
|
||||||
}
|
|
||||||
if(data->exp_mon) {
|
if(data->exp_mon) {
|
||||||
string_cat_printf(
|
uint8_t exp_data[2] = {data->exp_mon, data->exp_year};
|
||||||
bank_card_string, "\nExp date: %02X/%02X", data->exp_mon, data->exp_year);
|
if(!flipper_file_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break;
|
||||||
}
|
}
|
||||||
if(data->country_code) {
|
if(data->country_code) {
|
||||||
string_cat_printf(bank_card_string, "\nCountry code: %04X", data->country_code);
|
data_temp = data->country_code;
|
||||||
|
if(!flipper_file_write_uint32(file, "Country code", &data_temp, 1)) break;
|
||||||
}
|
}
|
||||||
if(data->currency_code) {
|
if(data->currency_code) {
|
||||||
string_cat_printf(bank_card_string, "\nCurrency code: %04X", data->currency_code);
|
data_temp = data->currency_code;
|
||||||
|
if(!flipper_file_write_uint32(file, "Currency code", &data_temp, 1)) break;
|
||||||
}
|
}
|
||||||
return string_size(bank_card_string);
|
saved = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string) {
|
bool nfc_device_load_bank_card_data(FlipperFile* file, NfcDevice* dev) {
|
||||||
NfcEmvData* data = &dev->dev_data.emv_data;
|
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
int res = 0;
|
NfcEmvData* data = &dev->dev_data.emv_data;
|
||||||
uint8_t code[2] = {};
|
|
||||||
memset(data, 0, sizeof(NfcEmvData));
|
memset(data, 0, sizeof(NfcEmvData));
|
||||||
|
uint32_t data_cnt = 0;
|
||||||
|
string_t temp_str;
|
||||||
|
string_init(temp_str);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
res = sscanf(string_get_cstr(bank_card_string), "AID len: %hu", &data->aid_len);
|
// Load essential data
|
||||||
if(res != 1) {
|
if(!flipper_file_get_value_count(file, "AID", &data_cnt)) break;
|
||||||
break;
|
data->aid_len = data_cnt;
|
||||||
}
|
if(!flipper_file_read_hex(file, "AID", data->aid, data->aid_len)) break;
|
||||||
// strlen("AID len: ") = 9
|
if(!flipper_file_read_string(file, "Name", temp_str)) break;
|
||||||
string_right(bank_card_string, 9);
|
strlcpy(data->name, string_get_cstr(temp_str), sizeof(data->name));
|
||||||
size_t ws = string_search_char(bank_card_string, ':');
|
if(!flipper_file_get_value_count(file, "Number", &data_cnt)) break;
|
||||||
string_right(bank_card_string, ws + 1);
|
data->number_len = data_cnt;
|
||||||
if(!nfc_device_read_hex(bank_card_string, data->aid, data->aid_len, 1)) {
|
if(!flipper_file_read_hex(file, "Number", data->number, data->number_len)) break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
res = sscanf(string_get_cstr(bank_card_string), "Name: %s\n", data->name);
|
|
||||||
if(res != 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ws = string_search_char(bank_card_string, '\n');
|
|
||||||
string_right(bank_card_string, ws + 1);
|
|
||||||
res = sscanf(string_get_cstr(bank_card_string), "Number len: %hhu", &data->number_len);
|
|
||||||
if(res != 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ws = string_search_char(bank_card_string, '\n');
|
|
||||||
string_right(bank_card_string, ws + 1);
|
|
||||||
// strlen("Number: ") = 8
|
|
||||||
string_right(bank_card_string, 8);
|
|
||||||
if(!nfc_device_read_hex(bank_card_string, data->number, data->number_len, 1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
parsed = true;
|
parsed = true;
|
||||||
// Check expiration date presence
|
// Load optional data
|
||||||
ws = string_search_str(bank_card_string, "Exp date: ");
|
uint8_t exp_data[2] = {};
|
||||||
if(ws != STRING_FAILURE) {
|
if(flipper_file_read_hex(file, "Exp data", exp_data, 2)) {
|
||||||
// strlen("Exp date: ") = 10
|
data->exp_mon = exp_data[0];
|
||||||
string_right(bank_card_string, 10);
|
data->exp_year = exp_data[1];
|
||||||
nfc_device_read_hex(bank_card_string, &data->exp_mon, 1, 1);
|
|
||||||
nfc_device_read_hex(bank_card_string, &data->exp_year, 1, 1);
|
|
||||||
}
|
}
|
||||||
// Check country code presence
|
if(flipper_file_read_uint32(file, "Country code", &data_cnt, 1)) {
|
||||||
ws = string_search_str(bank_card_string, "Country code: ");
|
data->country_code = data_cnt;
|
||||||
if(ws != STRING_FAILURE) {
|
|
||||||
// strlen("Country code: ") = 14
|
|
||||||
string_right(bank_card_string, 14);
|
|
||||||
nfc_device_read_hex(bank_card_string, code, 2, 0);
|
|
||||||
data->country_code = code[0] << 8 | code[1];
|
|
||||||
}
|
}
|
||||||
// Check currency code presence
|
if(flipper_file_read_uint32(file, "Currency code", &data_cnt, 1)) {
|
||||||
ws = string_search_str(bank_card_string, "Currency code: ");
|
data->currency_code = data_cnt;
|
||||||
if(ws != STRING_FAILURE) {
|
|
||||||
// strlen("Currency code: ") = 15
|
|
||||||
string_right(bank_card_string, 15);
|
|
||||||
nfc_device_read_hex(bank_card_string, code, 2, 0);
|
|
||||||
data->currency_code = code[0] << 8 | code[1];
|
|
||||||
}
|
}
|
||||||
} while(0);
|
} while(false);
|
||||||
|
|
||||||
|
string_clear(temp_str);
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,58 +228,49 @@ static bool nfc_device_save_file(
|
|||||||
const char* extension) {
|
const char* extension) {
|
||||||
furi_assert(dev);
|
furi_assert(dev);
|
||||||
|
|
||||||
FileWorker* file_worker = file_worker_alloc(false);
|
bool saved = false;
|
||||||
string_t dev_file_name;
|
FlipperFile* file = flipper_file_alloc(dev->storage);
|
||||||
string_init(dev_file_name);
|
NfcDeviceCommonData* data = &dev->dev_data.nfc_data;
|
||||||
string_t temp_str;
|
string_t temp_str;
|
||||||
string_init(temp_str);
|
string_init(temp_str);
|
||||||
uint16_t string_len = 0;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Create nfc directory if necessary
|
// Create nfc directory if necessary
|
||||||
if(!file_worker_mkdir(file_worker, nfc_app_folder)) {
|
if(!storage_simply_mkdir(dev->storage, nfc_app_folder)) break;
|
||||||
break;
|
|
||||||
};
|
|
||||||
// First remove nfc device file if it was saved
|
// First remove nfc device file if it was saved
|
||||||
string_printf(dev_file_name, "%s/%s%s", folder, dev_name, extension);
|
string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
|
||||||
if(!file_worker_remove(file_worker, string_get_cstr(dev_file_name))) {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
// Open file
|
// Open file
|
||||||
if(!file_worker_open(
|
if(!flipper_file_open_always(file, string_get_cstr(temp_str))) break;
|
||||||
file_worker, string_get_cstr(dev_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
// Write header
|
||||||
|
if(!flipper_file_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
|
||||||
|
// Write nfc device type
|
||||||
|
if(!flipper_file_write_comment_cstr(
|
||||||
|
file, "Nfc device type can be UID, Mifare Ultralight, Bank card"))
|
||||||
break;
|
break;
|
||||||
}
|
nfc_device_prepare_format_string(dev, temp_str);
|
||||||
// Prepare and write format name on 1st line
|
if(!flipper_file_write_string(file, "Device type", temp_str)) break;
|
||||||
string_len = nfc_device_prepare_format_string(dev, temp_str);
|
// Write UID, ATQA, SAK
|
||||||
if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
|
if(!flipper_file_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats"))
|
||||||
break;
|
break;
|
||||||
}
|
if(!flipper_file_write_hex(file, "UID", data->uid, data->uid_len)) break;
|
||||||
// Prepare and write UID data on 2nd line
|
if(!flipper_file_write_hex(file, "ATQA", data->atqa, 2)) break;
|
||||||
string_len = nfc_device_prepare_uid_string(dev, temp_str);
|
if(!flipper_file_write_hex(file, "SAK", &data->sak, 1)) break;
|
||||||
if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Save more data if necessary
|
// Save more data if necessary
|
||||||
if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||||
string_len = nfc_device_prepare_mifare_ul_string(dev, temp_str);
|
if(!nfc_device_save_mifare_ul_data(file, dev)) break;
|
||||||
if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
||||||
string_len = nfc_device_prepare_bank_card_string(dev, temp_str);
|
if(!nfc_device_save_bank_card_data(file, dev)) break;
|
||||||
if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
saved = true;
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
|
if(!saved) {
|
||||||
|
dialog_message_show_storage_error(dev->dialogs, "Can not save\nkey file");
|
||||||
|
}
|
||||||
string_clear(temp_str);
|
string_clear(temp_str);
|
||||||
string_clear(dev_file_name);
|
flipper_file_close(file);
|
||||||
file_worker_close(file_worker);
|
flipper_file_free(file);
|
||||||
file_worker_free(file_worker);
|
return saved;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
||||||
@ -360,73 +282,64 @@ bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) {
|
|||||||
return nfc_device_save_file(dev, dev_name, nfc_app_folder, nfc_app_shadow_extension);
|
return nfc_device_save_file(dev, dev_name, nfc_app_folder, nfc_app_shadow_extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_device_load_data(FileWorker* file_worker, string_t path, NfcDevice* dev) {
|
static bool nfc_device_load_data(NfcDevice* dev, string_t path) {
|
||||||
string_t temp_string;
|
|
||||||
string_init(temp_string);
|
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
FlipperFile* file = flipper_file_alloc(dev->storage);
|
||||||
|
NfcDeviceCommonData* data = &dev->dev_data.nfc_data;
|
||||||
|
uint32_t data_cnt = 0;
|
||||||
|
string_t temp_str;
|
||||||
|
string_init(temp_str);
|
||||||
|
bool depricated_version = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Check existance of shadow file
|
// Check existance of shadow file
|
||||||
size_t ext_start = string_search_str(path, nfc_app_extension);
|
size_t ext_start = string_search_str(path, nfc_app_extension);
|
||||||
string_set_n(temp_string, path, 0, ext_start);
|
string_set_n(temp_str, path, 0, ext_start);
|
||||||
string_cat_printf(temp_string, "%s", nfc_app_shadow_extension);
|
string_cat_printf(temp_str, "%s", nfc_app_shadow_extension);
|
||||||
if(!file_worker_is_file_exist(
|
dev->shadow_file_exist =
|
||||||
file_worker, string_get_cstr(temp_string), &dev->shadow_file_exist)) {
|
storage_common_stat(dev->storage, string_get_cstr(temp_str), NULL) == FSE_OK;
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Open shadow file if it exists. If not - open original
|
// Open shadow file if it exists. If not - open original
|
||||||
if(dev->shadow_file_exist) {
|
if(dev->shadow_file_exist) {
|
||||||
if(!file_worker_open(
|
if(!flipper_file_open_existing(file, string_get_cstr(temp_str))) break;
|
||||||
file_worker, string_get_cstr(temp_string), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if(!file_worker_open(
|
if(!flipper_file_open_existing(file, string_get_cstr(path))) break;
|
||||||
file_worker, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
}
|
||||||
break;
|
// Read and verify file header
|
||||||
}
|
uint32_t version = 0;
|
||||||
}
|
if(!flipper_file_read_header(file, temp_str, &version)) break;
|
||||||
|
if(string_cmp_str(temp_str, nfc_file_header) || (version != nfc_file_version)) {
|
||||||
// Read and parse format from 1st line
|
depricated_version = true;
|
||||||
if(!file_worker_read_until(file_worker, temp_string, '\n')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!nfc_device_parse_format_string(dev, temp_string)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Read and parse UID data from 2nd line
|
|
||||||
if(!file_worker_read_until(file_worker, temp_string, '\n')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!nfc_device_parse_uid_string(dev, temp_string)) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Read Nfc device type
|
||||||
|
if(!flipper_file_read_string(file, "Device type", temp_str)) break;
|
||||||
|
if(!nfc_device_parse_format_string(dev, temp_str)) break;
|
||||||
|
// Read and parse UID, ATQA and SAK
|
||||||
|
if(!flipper_file_get_value_count(file, "UID", &data_cnt)) break;
|
||||||
|
data->uid_len = data_cnt;
|
||||||
|
if(!flipper_file_read_hex(file, "UID", data->uid, data->uid_len)) break;
|
||||||
|
if(!flipper_file_read_hex(file, "ATQA", data->atqa, 2)) break;
|
||||||
|
if(!flipper_file_read_hex(file, "SAK", &data->sak, 1)) break;
|
||||||
// Parse other data
|
// Parse other data
|
||||||
if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||||
// Read until EOF
|
if(!nfc_device_load_mifare_ul_data(file, dev)) break;
|
||||||
if(!file_worker_read_until(file_worker, temp_string, 0x05)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!nfc_device_parse_mifare_ul_string(dev, temp_string)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
||||||
// Read until EOF
|
if(!nfc_device_load_bank_card_data(file, dev)) break;
|
||||||
if(!file_worker_read_until(file_worker, temp_string, 0x05)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!nfc_device_parse_bank_card_string(dev, temp_string)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
parsed = true;
|
parsed = true;
|
||||||
} while(0);
|
} while(false);
|
||||||
|
|
||||||
if(!parsed) {
|
if(!parsed) {
|
||||||
file_worker_show_error(file_worker, "Can not parse\nfile");
|
if(depricated_version) {
|
||||||
|
dialog_message_show_storage_error(dev->dialogs, "File format depricated");
|
||||||
|
} else {
|
||||||
|
dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string_clear(temp_string);
|
string_clear(temp_str);
|
||||||
|
flipper_file_close(file);
|
||||||
|
flipper_file_free(file);
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,19 +347,16 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path) {
|
|||||||
furi_assert(dev);
|
furi_assert(dev);
|
||||||
furi_assert(file_path);
|
furi_assert(file_path);
|
||||||
|
|
||||||
FileWorker* file_worker = file_worker_alloc(false);
|
|
||||||
// Load device data
|
// Load device data
|
||||||
string_t path;
|
string_t path;
|
||||||
string_init_set_str(path, file_path);
|
string_init_set_str(path, file_path);
|
||||||
bool dev_load = nfc_device_load_data(file_worker, path, dev);
|
bool dev_load = nfc_device_load_data(dev, path);
|
||||||
if(dev_load) {
|
if(dev_load) {
|
||||||
// Set device name
|
// Set device name
|
||||||
path_extract_filename_no_ext(file_path, path);
|
path_extract_filename_no_ext(file_path, path);
|
||||||
nfc_device_set_name(dev, string_get_cstr(path));
|
nfc_device_set_name(dev, string_get_cstr(path));
|
||||||
}
|
}
|
||||||
string_clear(path);
|
string_clear(path);
|
||||||
file_worker_close(file_worker);
|
|
||||||
file_worker_free(file_worker);
|
|
||||||
|
|
||||||
return dev_load;
|
return dev_load;
|
||||||
}
|
}
|
||||||
@ -454,10 +364,9 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path) {
|
|||||||
bool nfc_file_select(NfcDevice* dev) {
|
bool nfc_file_select(NfcDevice* dev) {
|
||||||
furi_assert(dev);
|
furi_assert(dev);
|
||||||
|
|
||||||
FileWorker* file_worker = file_worker_alloc(false);
|
|
||||||
// Input events and views are managed by file_select
|
// Input events and views are managed by file_select
|
||||||
bool res = file_worker_file_select(
|
bool res = dialog_file_select_show(
|
||||||
file_worker,
|
dev->dialogs,
|
||||||
nfc_app_folder,
|
nfc_app_folder,
|
||||||
nfc_app_extension,
|
nfc_app_extension,
|
||||||
dev->file_name,
|
dev->file_name,
|
||||||
@ -465,18 +374,14 @@ bool nfc_file_select(NfcDevice* dev) {
|
|||||||
dev->dev_name);
|
dev->dev_name);
|
||||||
if(res) {
|
if(res) {
|
||||||
string_t dev_str;
|
string_t dev_str;
|
||||||
|
|
||||||
// Get key file path
|
// Get key file path
|
||||||
string_init_printf(dev_str, "%s/%s%s", nfc_app_folder, dev->file_name, nfc_app_extension);
|
string_init_printf(dev_str, "%s/%s%s", nfc_app_folder, dev->file_name, nfc_app_extension);
|
||||||
|
res = nfc_device_load_data(dev, dev_str);
|
||||||
res = nfc_device_load_data(file_worker, dev_str, dev);
|
|
||||||
if(res) {
|
if(res) {
|
||||||
nfc_device_set_name(dev, dev->file_name);
|
nfc_device_set_name(dev, dev->file_name);
|
||||||
}
|
}
|
||||||
string_clear(dev_str);
|
string_clear(dev_str);
|
||||||
}
|
}
|
||||||
file_worker_close(file_worker);
|
|
||||||
file_worker_free(file_worker);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -491,61 +396,48 @@ void nfc_device_clear(NfcDevice* dev) {
|
|||||||
bool nfc_device_delete(NfcDevice* dev) {
|
bool nfc_device_delete(NfcDevice* dev) {
|
||||||
furi_assert(dev);
|
furi_assert(dev);
|
||||||
|
|
||||||
bool result = true;
|
bool deleted = false;
|
||||||
FileWorker* file_worker = file_worker_alloc(false);
|
|
||||||
string_t file_path;
|
string_t file_path;
|
||||||
|
string_init(file_path);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Delete original file
|
// Delete original file
|
||||||
string_init_printf(file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
|
string_init_printf(file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
|
||||||
if(!file_worker_remove(file_worker, string_get_cstr(file_path))) {
|
if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break;
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Delete shadow file if it exists
|
// Delete shadow file if it exists
|
||||||
if(dev->shadow_file_exist) {
|
if(dev->shadow_file_exist) {
|
||||||
string_clean(file_path);
|
|
||||||
string_printf(
|
string_printf(
|
||||||
file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
|
file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
|
||||||
if(!file_worker_remove(file_worker, string_get_cstr(file_path))) {
|
if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break;
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
deleted = true;
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
|
if(!deleted) {
|
||||||
|
dialog_message_show_storage_error(dev->dialogs, "Can not remove file");
|
||||||
|
}
|
||||||
|
|
||||||
string_clear(file_path);
|
string_clear(file_path);
|
||||||
file_worker_close(file_worker);
|
return deleted;
|
||||||
file_worker_free(file_worker);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_device_restore(NfcDevice* dev) {
|
bool nfc_device_restore(NfcDevice* dev) {
|
||||||
furi_assert(dev);
|
furi_assert(dev);
|
||||||
furi_assert(dev->shadow_file_exist);
|
furi_assert(dev->shadow_file_exist);
|
||||||
|
|
||||||
bool result = true;
|
bool restored = false;
|
||||||
FileWorker* file_worker = file_worker_alloc(false);
|
|
||||||
string_t path;
|
string_t path;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
string_init_printf(
|
string_init_printf(
|
||||||
path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
|
path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
|
||||||
if(!file_worker_remove(file_worker, string_get_cstr(path))) {
|
if(!storage_simply_remove(dev->storage, string_get_cstr(path))) break;
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
dev->shadow_file_exist = false;
|
dev->shadow_file_exist = false;
|
||||||
string_clean(path);
|
|
||||||
string_printf(path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
|
string_printf(path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
|
||||||
if(!nfc_device_load_data(file_worker, path, dev)) {
|
if(!nfc_device_load_data(dev, path)) break;
|
||||||
result = false;
|
restored = true;
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
string_clear(path);
|
string_clear(path);
|
||||||
file_worker_close(file_worker);
|
return restored;
|
||||||
file_worker_free(file_worker);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <storage/storage.h>
|
||||||
|
#include <dialogs/dialogs.h>
|
||||||
|
|
||||||
#include "mifare_ultralight.h"
|
#include "mifare_ultralight.h"
|
||||||
|
|
||||||
@ -57,6 +59,8 @@ typedef struct {
|
|||||||
} NfcDeviceData;
|
} NfcDeviceData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
Storage* storage;
|
||||||
|
DialogsApp* dialogs;
|
||||||
NfcDeviceData dev_data;
|
NfcDeviceData dev_data;
|
||||||
char dev_name[NFC_DEV_NAME_MAX_LEN + 1];
|
char dev_name[NFC_DEV_NAME_MAX_LEN + 1];
|
||||||
char file_name[NFC_FILE_NAME_MAX_LEN];
|
char file_name[NFC_FILE_NAME_MAX_LEN];
|
||||||
@ -64,6 +68,10 @@ typedef struct {
|
|||||||
bool shadow_file_exist;
|
bool shadow_file_exist;
|
||||||
} NfcDevice;
|
} NfcDevice;
|
||||||
|
|
||||||
|
NfcDevice* nfc_device_alloc();
|
||||||
|
|
||||||
|
void nfc_device_free(NfcDevice* nfc_dev);
|
||||||
|
|
||||||
void nfc_device_set_name(NfcDevice* dev, const char* name);
|
void nfc_device_set_name(NfcDevice* dev, const char* name);
|
||||||
|
|
||||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name);
|
bool nfc_device_save(NfcDevice* dev, const char* dev_name);
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "nfc_device.h"
|
|
||||||
#include <m-string.h>
|
|
||||||
|
|
||||||
uint16_t nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string);
|
|
||||||
bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string);
|
|
||||||
|
|
||||||
uint16_t nfc_device_prepare_uid_string(NfcDevice* dev, string_t uid_string);
|
|
||||||
bool nfc_device_parse_uid_string(NfcDevice* dev, string_t uid_string);
|
|
||||||
|
|
||||||
uint16_t nfc_device_prepare_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string);
|
|
||||||
bool nfc_device_parse_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string);
|
|
||||||
|
|
||||||
uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_string);
|
|
||||||
bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string);
|
|
||||||
@ -36,7 +36,7 @@ struct Nfc {
|
|||||||
Gui* gui;
|
Gui* gui;
|
||||||
NotificationApp* notifications;
|
NotificationApp* notifications;
|
||||||
SceneManager* scene_manager;
|
SceneManager* scene_manager;
|
||||||
NfcDevice dev;
|
NfcDevice* dev;
|
||||||
NfcDeviceCommonData dev_edit_data;
|
NfcDeviceCommonData dev_edit_data;
|
||||||
|
|
||||||
char text_store[NFC_TEXT_STORE_SIZE + 1];
|
char text_store[NFC_TEXT_STORE_SIZE + 1];
|
||||||
|
|||||||
145
applications/nfc/nfc_worker.c
Executable file → Normal file
145
applications/nfc/nfc_worker.c
Executable file → Normal file
@ -3,14 +3,14 @@
|
|||||||
#include "nfc_protocols/emv_decoder.h"
|
#include "nfc_protocols/emv_decoder.h"
|
||||||
#include "nfc_protocols/mifare_ultralight.h"
|
#include "nfc_protocols/mifare_ultralight.h"
|
||||||
|
|
||||||
#define NFC_WORKER_TAG "nfc worker"
|
#define TAG "NfcWorker"
|
||||||
|
|
||||||
/***************************** NFC Worker API *******************************/
|
/***************************** NFC Worker API *******************************/
|
||||||
|
|
||||||
NfcWorker* nfc_worker_alloc() {
|
NfcWorker* nfc_worker_alloc() {
|
||||||
NfcWorker* nfc_worker = furi_alloc(sizeof(NfcWorker));
|
NfcWorker* nfc_worker = furi_alloc(sizeof(NfcWorker));
|
||||||
// Worker thread attributes
|
// Worker thread attributes
|
||||||
nfc_worker->thread_attr.name = "nfc_worker";
|
nfc_worker->thread_attr.name = "NfcWorker";
|
||||||
nfc_worker->thread_attr.stack_size = 8192;
|
nfc_worker->thread_attr.stack_size = 8192;
|
||||||
nfc_worker->callback = NULL;
|
nfc_worker->callback = NULL;
|
||||||
nfc_worker->context = NULL;
|
nfc_worker->context = NULL;
|
||||||
@ -144,7 +144,7 @@ void nfc_worker_emulate(NfcWorker* nfc_worker) {
|
|||||||
NfcDeviceCommonData* data = &nfc_worker->dev_data->nfc_data;
|
NfcDeviceCommonData* data = &nfc_worker->dev_data->nfc_data;
|
||||||
while(nfc_worker->state == NfcWorkerStateEmulate) {
|
while(nfc_worker->state == NfcWorkerStateEmulate) {
|
||||||
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) {
|
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Reader detected");
|
FURI_LOG_I(TAG, "Reader detected");
|
||||||
}
|
}
|
||||||
osDelay(10);
|
osDelay(10);
|
||||||
}
|
}
|
||||||
@ -174,18 +174,17 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker) {
|
|||||||
result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
|
result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
|
||||||
result->nfc_data.protocol = NfcDeviceProtocolEMV;
|
result->nfc_data.protocol = NfcDeviceProtocolEMV;
|
||||||
|
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Send select PPSE command");
|
FURI_LOG_I(TAG, "Send select PPSE command");
|
||||||
tx_len = emv_prepare_select_ppse(tx_buff);
|
tx_len = emv_prepare_select_ppse(tx_buff);
|
||||||
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err != ERR_NONE) {
|
if(err != ERR_NONE) {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Error during selection PPSE request: %d", err);
|
FURI_LOG_E(TAG, "Error during selection PPSE request: %d", err);
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(TAG, "Select PPSE response received. Start parsing response");
|
||||||
NFC_WORKER_TAG, "Select PPSE response received. Start parsing response");
|
|
||||||
if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) {
|
if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Select PPSE responce parced");
|
FURI_LOG_I(TAG, "Select PPSE responce parced");
|
||||||
// Notify caller and exit
|
// Notify caller and exit
|
||||||
result->emv_data.aid_len = emv_app.aid_len;
|
result->emv_data.aid_len = emv_app.aid_len;
|
||||||
memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len);
|
memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len);
|
||||||
@ -194,18 +193,18 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Can't find pay application");
|
FURI_LOG_E(TAG, "Can't find pay application");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Can't find EMV card
|
// Can't find EMV card
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Card doesn't support EMV");
|
FURI_LOG_W(TAG, "Card doesn't support EMV");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Can't find EMV card
|
// Can't find EMV card
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Can't find any cards");
|
FURI_LOG_W(TAG, "Can't find any cards");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
}
|
}
|
||||||
osDelay(20);
|
osDelay(20);
|
||||||
@ -236,56 +235,53 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
|
|||||||
result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
|
result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
|
||||||
result->nfc_data.protocol = NfcDeviceProtocolEMV;
|
result->nfc_data.protocol = NfcDeviceProtocolEMV;
|
||||||
|
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Send select PPSE command");
|
FURI_LOG_I(TAG, "Send select PPSE command");
|
||||||
tx_len = emv_prepare_select_ppse(tx_buff);
|
tx_len = emv_prepare_select_ppse(tx_buff);
|
||||||
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err != ERR_NONE) {
|
if(err != ERR_NONE) {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Error during selection PPSE request: %d", err);
|
FURI_LOG_E(TAG, "Error during selection PPSE request: %d", err);
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(TAG, "Select PPSE response received. Start parsing response");
|
||||||
NFC_WORKER_TAG, "Select PPSE response received. Start parsing response");
|
|
||||||
if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) {
|
if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Select PPSE responce parced");
|
FURI_LOG_I(TAG, "Select PPSE responce parced");
|
||||||
|
result->emv_data.aid_len = emv_app.aid_len;
|
||||||
|
memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len);
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Can't find pay application");
|
FURI_LOG_E(TAG, "Can't find pay application");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Starting application ...");
|
FURI_LOG_I(TAG, "Starting application ...");
|
||||||
tx_len = emv_prepare_select_app(tx_buff, &emv_app);
|
tx_len = emv_prepare_select_app(tx_buff, &emv_app);
|
||||||
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err != ERR_NONE) {
|
if(err != ERR_NONE) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(TAG, "Error during application selection request: %d", err);
|
||||||
NFC_WORKER_TAG, "Error during application selection request: %d", err);
|
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(TAG, "Select application response received. Start parsing response");
|
||||||
NFC_WORKER_TAG,
|
|
||||||
"Select application response received. Start parsing response");
|
|
||||||
if(emv_decode_select_app_response(rx_buff, *rx_len, &emv_app)) {
|
if(emv_decode_select_app_response(rx_buff, *rx_len, &emv_app)) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Card name: %s", emv_app.name);
|
FURI_LOG_I(TAG, "Card name: %s", emv_app.name);
|
||||||
memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name));
|
memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name));
|
||||||
} else if(emv_app.pdol.size > 0) {
|
} else if(emv_app.pdol.size > 0) {
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Can't find card name, but PDOL is present.");
|
FURI_LOG_W(TAG, "Can't find card name, but PDOL is present.");
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Can't find card name or PDOL");
|
FURI_LOG_E(TAG, "Can't find card name or PDOL");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Starting Get Processing Options command ...");
|
FURI_LOG_I(TAG, "Starting Get Processing Options command ...");
|
||||||
tx_len = emv_prepare_get_proc_opt(tx_buff, &emv_app);
|
tx_len = emv_prepare_get_proc_opt(tx_buff, &emv_app);
|
||||||
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err != ERR_NONE) {
|
if(err != ERR_NONE) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(TAG, "Error during Get Processing Options command: %d", err);
|
||||||
NFC_WORKER_TAG, "Error during Get Processing Options command: %d", err);
|
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(emv_decode_get_proc_opt(rx_buff, *rx_len, &emv_app)) {
|
if(emv_decode_get_proc_opt(rx_buff, *rx_len, &emv_app)) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Card number parsed");
|
FURI_LOG_I(TAG, "Card number parsed");
|
||||||
result->emv_data.number_len = emv_app.card_number_len;
|
result->emv_data.number_len = emv_app.card_number_len;
|
||||||
memcpy(result->emv_data.number, emv_app.card_number, emv_app.card_number_len);
|
memcpy(result->emv_data.number, emv_app.card_number, emv_app.card_number_len);
|
||||||
// Notify caller and exit
|
// Notify caller and exit
|
||||||
@ -309,7 +305,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
|
|||||||
tx_buff, tx_len, &rx_buff, &rx_len, false);
|
tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err != ERR_NONE) {
|
if(err != ERR_NONE) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
NFC_WORKER_TAG,
|
TAG,
|
||||||
"Error reading application sfi %d, record %d",
|
"Error reading application sfi %d, record %d",
|
||||||
sfi,
|
sfi,
|
||||||
record);
|
record);
|
||||||
@ -321,7 +317,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(pan_found) {
|
if(pan_found) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Card PAN found");
|
FURI_LOG_I(TAG, "Card PAN found");
|
||||||
result->emv_data.number_len = emv_app.card_number_len;
|
result->emv_data.number_len = emv_app.card_number_len;
|
||||||
memcpy(
|
memcpy(
|
||||||
result->emv_data.number,
|
result->emv_data.number,
|
||||||
@ -343,18 +339,18 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Can't read card number");
|
FURI_LOG_E(TAG, "Can't read card number");
|
||||||
}
|
}
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Can't find EMV card
|
// Can't find EMV card
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Card doesn't support EMV");
|
FURI_LOG_W(TAG, "Card doesn't support EMV");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Can't find EMV card
|
// Can't find EMV card
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Can't find any cards");
|
FURI_LOG_W(TAG, "Can't find any cards");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
}
|
}
|
||||||
osDelay(20);
|
osDelay(20);
|
||||||
@ -416,63 +412,63 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
|
|||||||
|
|
||||||
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
||||||
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
|
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "POS terminal detected");
|
FURI_LOG_I(TAG, "POS terminal detected");
|
||||||
// Read data from POS terminal
|
// Read data from POS terminal
|
||||||
err = furi_hal_nfc_data_exchange(NULL, 0, &rx_buff, &rx_len, false);
|
err = furi_hal_nfc_data_exchange(NULL, 0, &rx_buff, &rx_len, false);
|
||||||
if(err == ERR_NONE) {
|
if(err == ERR_NONE) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Received Select PPSE");
|
FURI_LOG_I(TAG, "Received Select PPSE");
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Error in 1st data exchange: select PPSE");
|
FURI_LOG_E(TAG, "Error in 1st data exchange: select PPSE");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Transive SELECT PPSE ANS");
|
FURI_LOG_I(TAG, "Transive SELECT PPSE ANS");
|
||||||
tx_len = emv_select_ppse_ans(tx_buff);
|
tx_len = emv_select_ppse_ans(tx_buff);
|
||||||
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err == ERR_NONE) {
|
if(err == ERR_NONE) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Received Select APP");
|
FURI_LOG_I(TAG, "Received Select APP");
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Error in 2nd data exchange: select APP");
|
FURI_LOG_E(TAG, "Error in 2nd data exchange: select APP");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Transive SELECT APP ANS");
|
FURI_LOG_I(TAG, "Transive SELECT APP ANS");
|
||||||
tx_len = emv_select_app_ans(tx_buff);
|
tx_len = emv_select_app_ans(tx_buff);
|
||||||
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err == ERR_NONE) {
|
if(err == ERR_NONE) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Received PDOL");
|
FURI_LOG_I(TAG, "Received PDOL");
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Error in 3rd data exchange: receive PDOL");
|
FURI_LOG_E(TAG, "Error in 3rd data exchange: receive PDOL");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Transive PDOL ANS");
|
FURI_LOG_I(TAG, "Transive PDOL ANS");
|
||||||
tx_len = emv_get_proc_opt_ans(tx_buff);
|
tx_len = emv_get_proc_opt_ans(tx_buff);
|
||||||
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err == ERR_NONE) {
|
if(err == ERR_NONE) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Transive PDOL ANS");
|
FURI_LOG_I(TAG, "Transive PDOL ANS");
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Error in 4rd data exchange: Transive PDOL ANS");
|
FURI_LOG_E(TAG, "Error in 4rd data exchange: Transive PDOL ANS");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*rx_len != sizeof(debug_rx) || memcmp(rx_buff, debug_rx, sizeof(debug_rx))) {
|
if(*rx_len != sizeof(debug_rx) || memcmp(rx_buff, debug_rx, sizeof(debug_rx))) {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Failed long message test");
|
FURI_LOG_E(TAG, "Failed long message test");
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Correct debug message received");
|
FURI_LOG_I(TAG, "Correct debug message received");
|
||||||
tx_len = sizeof(debug_tx);
|
tx_len = sizeof(debug_tx);
|
||||||
err = furi_hal_nfc_data_exchange(
|
err = furi_hal_nfc_data_exchange(
|
||||||
(uint8_t*)debug_tx, tx_len, &rx_buff, &rx_len, false);
|
(uint8_t*)debug_tx, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err == ERR_NONE) {
|
if(err == ERR_NONE) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Transive Debug message");
|
FURI_LOG_I(TAG, "Transive Debug message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Can't find reader");
|
FURI_LOG_W(TAG, "Can't find reader");
|
||||||
}
|
}
|
||||||
osDelay(20);
|
osDelay(20);
|
||||||
}
|
}
|
||||||
@ -499,71 +495,69 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) {
|
|||||||
dev_list[0].dev.nfca.sensRes.platformInfo,
|
dev_list[0].dev.nfca.sensRes.platformInfo,
|
||||||
dev_list[0].dev.nfca.selRes.sak)) {
|
dev_list[0].dev.nfca.selRes.sak)) {
|
||||||
// Get Mifare Ultralight version
|
// Get Mifare Ultralight version
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Found Mifare Ultralight tag. Reading tag version");
|
FURI_LOG_I(TAG, "Found Mifare Ultralight tag. Reading tag version");
|
||||||
tx_len = mf_ul_prepare_get_version(tx_buff);
|
tx_len = mf_ul_prepare_get_version(tx_buff);
|
||||||
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||||
if(err == ERR_NONE) {
|
if(err == ERR_NONE) {
|
||||||
mf_ul_parse_get_version_response(rx_buff, &mf_ul_read);
|
mf_ul_parse_get_version_response(rx_buff, &mf_ul_read);
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(
|
||||||
NFC_WORKER_TAG,
|
TAG,
|
||||||
"Mifare Ultralight Type: %d, Pages: %d",
|
"Mifare Ultralight Type: %d, Pages: %d",
|
||||||
mf_ul_read.type,
|
mf_ul_read.type,
|
||||||
mf_ul_read.pages_to_read);
|
mf_ul_read.pages_to_read);
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Reading signature ...");
|
FURI_LOG_I(TAG, "Reading signature ...");
|
||||||
tx_len = mf_ul_prepare_read_signature(tx_buff);
|
tx_len = mf_ul_prepare_read_signature(tx_buff);
|
||||||
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Failed reading signature");
|
FURI_LOG_W(TAG, "Failed reading signature");
|
||||||
memset(mf_ul_read.data.signature, 0, sizeof(mf_ul_read.data.signature));
|
memset(mf_ul_read.data.signature, 0, sizeof(mf_ul_read.data.signature));
|
||||||
} else {
|
} else {
|
||||||
mf_ul_parse_read_signature_response(rx_buff, &mf_ul_read);
|
mf_ul_parse_read_signature_response(rx_buff, &mf_ul_read);
|
||||||
}
|
}
|
||||||
} else if(err == ERR_TIMEOUT) {
|
} else if(err == ERR_TIMEOUT) {
|
||||||
FURI_LOG_W(
|
FURI_LOG_W(
|
||||||
NFC_WORKER_TAG,
|
TAG,
|
||||||
"Card doesn't respond to GET VERSION command. Setting default read parameters");
|
"Card doesn't respond to GET VERSION command. Setting default read parameters");
|
||||||
err = ERR_NONE;
|
err = ERR_NONE;
|
||||||
mf_ul_set_default_version(&mf_ul_read);
|
mf_ul_set_default_version(&mf_ul_read);
|
||||||
// Reinit device
|
// Reinit device
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
if(!furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) {
|
if(!furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Lost connection. Restarting search");
|
FURI_LOG_E(TAG, "Lost connection. Restarting search");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
NFC_WORKER_TAG,
|
TAG, "Error getting Mifare Ultralight version. Error code: %d", err);
|
||||||
"Error getting Mifare Ultralight version. Error code: %d",
|
|
||||||
err);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mf_ul_read.support_fast_read) {
|
if(mf_ul_read.support_fast_read) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Reading pages ...");
|
FURI_LOG_I(TAG, "Reading pages ...");
|
||||||
tx_len = mf_ul_prepare_fast_read(tx_buff, 0x00, mf_ul_read.pages_to_read - 1);
|
tx_len = mf_ul_prepare_fast_read(tx_buff, 0x00, mf_ul_read.pages_to_read - 1);
|
||||||
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Failed reading pages");
|
FURI_LOG_E(TAG, "Failed reading pages");
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
mf_ul_parse_fast_read_response(
|
mf_ul_parse_fast_read_response(
|
||||||
rx_buff, 0x00, mf_ul_read.pages_to_read - 1, &mf_ul_read);
|
rx_buff, 0x00, mf_ul_read.pages_to_read - 1, &mf_ul_read);
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Reading 3 counters ...");
|
FURI_LOG_I(TAG, "Reading 3 counters ...");
|
||||||
for(uint8_t i = 0; i < 3; i++) {
|
for(uint8_t i = 0; i < 3; i++) {
|
||||||
tx_len = mf_ul_prepare_read_cnt(tx_buff, i);
|
tx_len = mf_ul_prepare_read_cnt(tx_buff, i);
|
||||||
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Failed reading Counter %d", i);
|
FURI_LOG_W(TAG, "Failed reading Counter %d", i);
|
||||||
mf_ul_read.data.counter[i] = 0;
|
mf_ul_read.data.counter[i] = 0;
|
||||||
} else {
|
} else {
|
||||||
mf_ul_parse_read_cnt_response(rx_buff, i, &mf_ul_read);
|
mf_ul_parse_read_cnt_response(rx_buff, i, &mf_ul_read);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Checking tearing flags ...");
|
FURI_LOG_I(TAG, "Checking tearing flags ...");
|
||||||
for(uint8_t i = 0; i < 3; i++) {
|
for(uint8_t i = 0; i < 3; i++) {
|
||||||
tx_len = mf_ul_prepare_check_tearing(tx_buff, i);
|
tx_len = mf_ul_prepare_check_tearing(tx_buff, i);
|
||||||
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Error checking tearing flag %d", i);
|
FURI_LOG_E(TAG, "Error checking tearing flag %d", i);
|
||||||
mf_ul_read.data.tearing[i] = MF_UL_TEARING_FLAG_DEFAULT;
|
mf_ul_read.data.tearing[i] = MF_UL_TEARING_FLAG_DEFAULT;
|
||||||
} else {
|
} else {
|
||||||
mf_ul_parse_check_tearing_response(rx_buff, i, &mf_ul_read);
|
mf_ul_parse_check_tearing_response(rx_buff, i, &mf_ul_read);
|
||||||
@ -572,11 +566,10 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) {
|
|||||||
} else {
|
} else {
|
||||||
// READ card with READ command (4 pages at a time)
|
// READ card with READ command (4 pages at a time)
|
||||||
for(uint8_t page = 0; page < mf_ul_read.pages_to_read; page += 4) {
|
for(uint8_t page = 0; page < mf_ul_read.pages_to_read; page += 4) {
|
||||||
FURI_LOG_I(NFC_WORKER_TAG, "Reading pages %d - %d ...", page, page + 3);
|
FURI_LOG_I(TAG, "Reading pages %d - %d ...", page, page + 3);
|
||||||
tx_len = mf_ul_prepare_read(tx_buff, page);
|
tx_len = mf_ul_prepare_read(tx_buff, page);
|
||||||
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(TAG, "Read pages %d - %d failed", page, page + 3);
|
||||||
NFC_WORKER_TAG, "Read pages %d - %d failed", page, page + 3);
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
mf_ul_parse_read_response(rx_buff, page, &mf_ul_read);
|
mf_ul_parse_read_response(rx_buff, page, &mf_ul_read);
|
||||||
@ -600,10 +593,10 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Tag does not support Mifare Ultralight");
|
FURI_LOG_W(TAG, "Tag does not support Mifare Ultralight");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Can't find any tags");
|
FURI_LOG_W(TAG, "Can't find any tags");
|
||||||
}
|
}
|
||||||
osDelay(100);
|
osDelay(100);
|
||||||
}
|
}
|
||||||
@ -627,7 +620,7 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
|
|||||||
data->nfc_data.sak,
|
data->nfc_data.sak,
|
||||||
true,
|
true,
|
||||||
200)) {
|
200)) {
|
||||||
FURI_LOG_D(NFC_WORKER_TAG, "Anticollision passed");
|
FURI_LOG_D(TAG, "Anticollision passed");
|
||||||
if(furi_hal_nfc_get_first_frame(&rx_buff, &rx_len)) {
|
if(furi_hal_nfc_get_first_frame(&rx_buff, &rx_len)) {
|
||||||
// Data exchange loop
|
// Data exchange loop
|
||||||
while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
|
while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
|
||||||
@ -639,17 +632,17 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
|
|||||||
if(err == ERR_NONE) {
|
if(err == ERR_NONE) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(NFC_WORKER_TAG, "Communication error: %d", err);
|
FURI_LOG_E(TAG, "Communication error: %d", err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Not valid command: %02X", rx_buff[0]);
|
FURI_LOG_W(TAG, "Not valid command: %02X", rx_buff[0]);
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Error in 1st data exchange");
|
FURI_LOG_W(TAG, "Error in 1st data exchange");
|
||||||
furi_hal_nfc_deactivate();
|
furi_hal_nfc_deactivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -660,7 +653,7 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
|
|||||||
nfc_worker->callback(nfc_worker->context);
|
nfc_worker->callback(nfc_worker->context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FURI_LOG_W(NFC_WORKER_TAG, "Can't find reader");
|
FURI_LOG_W(TAG, "Can't find reader");
|
||||||
osThreadYield();
|
osThreadYield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ void nfc_scene_card_menu_on_enter(void* context) {
|
|||||||
Nfc* nfc = (Nfc*)context;
|
Nfc* nfc = (Nfc*)context;
|
||||||
Submenu* submenu = nfc->submenu;
|
Submenu* submenu = nfc->submenu;
|
||||||
|
|
||||||
if(nfc->dev.dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) {
|
if(nfc->dev->dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Run compatible app",
|
"Run compatible app",
|
||||||
@ -48,9 +48,9 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if(event.event == SubmenuIndexRunApp) {
|
if(event.event == SubmenuIndexRunApp) {
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp);
|
nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp);
|
||||||
if(nfc->dev.dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) {
|
if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl);
|
||||||
} else if(nfc->dev.dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) {
|
} else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -66,7 +66,7 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
return true;
|
return true;
|
||||||
} else if(event.event == SubmenuIndexSave) {
|
} else if(event.event == SubmenuIndexSave) {
|
||||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave);
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave);
|
||||||
nfc->dev.format = NfcDeviceSaveFormatUid;
|
nfc->dev->format = NfcDeviceSaveFormatUid;
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,14 +12,14 @@ void nfc_scene_delete_on_enter(void* context) {
|
|||||||
|
|
||||||
// Setup Custom Widget view
|
// Setup Custom Widget view
|
||||||
char delete_str[64];
|
char delete_str[64];
|
||||||
snprintf(delete_str, sizeof(delete_str), "Delete %s", nfc->dev.dev_name);
|
snprintf(delete_str, sizeof(delete_str), "\e#Delete %s\e#", nfc->dev->dev_name);
|
||||||
widget_add_string_element(nfc->widget, 64, 6, AlignCenter, AlignTop, FontPrimary, delete_str);
|
widget_add_text_box_element(nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, delete_str);
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc);
|
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc);
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc);
|
nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc);
|
||||||
char uid_str[32];
|
char uid_str[32];
|
||||||
NfcDeviceCommonData* data = &nfc->dev.dev_data.nfc_data;
|
NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
|
||||||
if(data->uid_len == 4) {
|
if(data->uid_len == 4) {
|
||||||
snprintf(
|
snprintf(
|
||||||
uid_str,
|
uid_str,
|
||||||
@ -73,7 +73,7 @@ bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if(event.event == GuiButtonTypeLeft) {
|
if(event.event == GuiButtonTypeLeft) {
|
||||||
return scene_manager_previous_scene(nfc->scene_manager);
|
return scene_manager_previous_scene(nfc->scene_manager);
|
||||||
} else if(event.event == GuiButtonTypeRight) {
|
} else if(event.event == GuiButtonTypeRight) {
|
||||||
if(nfc_device_delete(&nfc->dev)) {
|
if(nfc_device_delete(nfc->dev)) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
|
||||||
} else {
|
} else {
|
||||||
scene_manager_search_and_switch_to_previous_scene(
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#include "../nfc_i.h"
|
#include "../nfc_i.h"
|
||||||
|
#include "../helpers/nfc_emv_parser.h"
|
||||||
|
|
||||||
#define NFC_SCENE_DEVICE_INFO_BACK_EVENT (0UL)
|
#define NFC_SCENE_DEVICE_INFO_BACK_EVENT (0UL)
|
||||||
|
|
||||||
@ -35,14 +36,14 @@ void nfc_scene_device_info_on_enter(void* context) {
|
|||||||
Nfc* nfc = context;
|
Nfc* nfc = context;
|
||||||
|
|
||||||
// Setup Custom Widget view
|
// Setup Custom Widget view
|
||||||
widget_add_string_element(
|
widget_add_text_box_element(
|
||||||
nfc->widget, 64, 6, AlignCenter, AlignTop, FontSecondary, nfc->dev.dev_name);
|
nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, nfc->dev->dev_name);
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc);
|
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc);
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc);
|
nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc);
|
||||||
char uid_str[32];
|
char uid_str[32];
|
||||||
NfcDeviceCommonData* data = &nfc->dev.dev_data.nfc_data;
|
NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
|
||||||
if(data->uid_len == 4) {
|
if(data->uid_len == 4) {
|
||||||
snprintf(
|
snprintf(
|
||||||
uid_str,
|
uid_str,
|
||||||
@ -87,14 +88,14 @@ void nfc_scene_device_info_on_enter(void* context) {
|
|||||||
widget_add_string_element(nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, atqa_str);
|
widget_add_string_element(nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, atqa_str);
|
||||||
|
|
||||||
// Setup Data View
|
// Setup Data View
|
||||||
if(nfc->dev.format == NfcDeviceSaveFormatUid) {
|
if(nfc->dev->format == NfcDeviceSaveFormatUid) {
|
||||||
DialogEx* dialog_ex = nfc->dialog_ex;
|
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||||
dialog_ex_set_left_button_text(dialog_ex, "Back");
|
dialog_ex_set_left_button_text(dialog_ex, "Back");
|
||||||
dialog_ex_set_text(dialog_ex, "No data", 64, 32, AlignCenter, AlignCenter);
|
dialog_ex_set_text(dialog_ex, "No data", 64, 32, AlignCenter, AlignCenter);
|
||||||
dialog_ex_set_context(dialog_ex, nfc);
|
dialog_ex_set_context(dialog_ex, nfc);
|
||||||
dialog_ex_set_result_callback(dialog_ex, nfc_scene_device_info_dialog_callback);
|
dialog_ex_set_result_callback(dialog_ex, nfc_scene_device_info_dialog_callback);
|
||||||
} else if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
|
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||||
MifareUlData* mf_ul_data = (MifareUlData*)&nfc->dev.dev_data.mf_ul_data;
|
MifareUlData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
|
||||||
TextBox* text_box = nfc->text_box;
|
TextBox* text_box = nfc->text_box;
|
||||||
text_box_set_context(text_box, nfc);
|
text_box_set_context(text_box, nfc);
|
||||||
text_box_set_exit_callback(text_box, nfc_scene_device_info_text_box_callback);
|
text_box_set_exit_callback(text_box, nfc_scene_device_info_text_box_callback);
|
||||||
@ -107,8 +108,8 @@ void nfc_scene_device_info_on_enter(void* context) {
|
|||||||
nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]);
|
nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]);
|
||||||
}
|
}
|
||||||
text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
|
text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
|
||||||
} else if(nfc->dev.format == NfcDeviceSaveFormatBankCard) {
|
} else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
|
||||||
NfcEmvData* emv_data = &nfc->dev.dev_data.emv_data;
|
NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data;
|
||||||
BankCard* bank_card = nfc->bank_card;
|
BankCard* bank_card = nfc->bank_card;
|
||||||
bank_card_set_name(bank_card, emv_data->name);
|
bank_card_set_name(bank_card, emv_data->name);
|
||||||
bank_card_set_number(bank_card, emv_data->number, emv_data->number_len);
|
bank_card_set_number(bank_card, emv_data->number, emv_data->number_len);
|
||||||
@ -116,12 +117,29 @@ void nfc_scene_device_info_on_enter(void* context) {
|
|||||||
if(emv_data->exp_mon) {
|
if(emv_data->exp_mon) {
|
||||||
bank_card_set_exp_date(bank_card, emv_data->exp_mon, emv_data->exp_year);
|
bank_card_set_exp_date(bank_card, emv_data->exp_mon, emv_data->exp_year);
|
||||||
}
|
}
|
||||||
|
string_t display_str;
|
||||||
|
string_init(display_str);
|
||||||
if(emv_data->country_code) {
|
if(emv_data->country_code) {
|
||||||
bank_card_set_country_name(bank_card, emv_data->country_code);
|
string_t country_name;
|
||||||
|
string_init(country_name);
|
||||||
|
if(nfc_emv_parser_get_country_name(
|
||||||
|
nfc->dev->storage, emv_data->country_code, country_name)) {
|
||||||
|
string_printf(display_str, "Reg:%s", string_get_cstr(country_name));
|
||||||
|
bank_card_set_country_name(bank_card, string_get_cstr(display_str));
|
||||||
|
}
|
||||||
|
string_clear(country_name);
|
||||||
}
|
}
|
||||||
if(emv_data->currency_code) {
|
if(emv_data->currency_code) {
|
||||||
bank_card_set_currency_name(bank_card, emv_data->currency_code);
|
string_t currency_name;
|
||||||
|
string_init(currency_name);
|
||||||
|
if(nfc_emv_parser_get_currency_name(
|
||||||
|
nfc->dev->storage, emv_data->country_code, currency_name)) {
|
||||||
|
string_printf(display_str, "Cur:%s", string_get_cstr(currency_name));
|
||||||
|
bank_card_set_currency_name(bank_card, string_get_cstr(display_str));
|
||||||
}
|
}
|
||||||
|
string_clear(currency_name);
|
||||||
|
}
|
||||||
|
string_clear(display_str);
|
||||||
}
|
}
|
||||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
@ -136,17 +154,17 @@ bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeLeft)) {
|
if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeLeft)) {
|
||||||
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||||
} else if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeRight)) {
|
} else if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeRight)) {
|
||||||
if(nfc->dev.format == NfcDeviceSaveFormatUid) {
|
if(nfc->dev->format == NfcDeviceSaveFormatUid) {
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
|
nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
|
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
|
nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(nfc->dev.format == NfcDeviceSaveFormatBankCard) {
|
} else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
|
nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard);
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard);
|
||||||
@ -168,7 +186,7 @@ void nfc_scene_device_info_on_exit(void* context) {
|
|||||||
// Clear Custom Widget
|
// Clear Custom Widget
|
||||||
widget_clear(nfc->widget);
|
widget_clear(nfc->widget);
|
||||||
|
|
||||||
if(nfc->dev.format == NfcDeviceSaveFormatUid) {
|
if(nfc->dev->format == NfcDeviceSaveFormatUid) {
|
||||||
// Clear Dialog
|
// Clear Dialog
|
||||||
DialogEx* dialog_ex = nfc->dialog_ex;
|
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||||
dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
|
dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
|
||||||
@ -179,11 +197,11 @@ void nfc_scene_device_info_on_exit(void* context) {
|
|||||||
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
||||||
dialog_ex_set_result_callback(dialog_ex, NULL);
|
dialog_ex_set_result_callback(dialog_ex, NULL);
|
||||||
dialog_ex_set_context(dialog_ex, NULL);
|
dialog_ex_set_context(dialog_ex, NULL);
|
||||||
} else if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
|
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||||
// Clear TextBox
|
// Clear TextBox
|
||||||
text_box_clean(nfc->text_box);
|
text_box_clean(nfc->text_box);
|
||||||
string_clean(nfc->text_box_store);
|
string_reset(nfc->text_box_store);
|
||||||
} else if(nfc->dev.format == NfcDeviceSaveFormatBankCard) {
|
} else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
|
||||||
// Clear Bank Card
|
// Clear Bank Card
|
||||||
bank_card_clear(nfc->bank_card);
|
bank_card_clear(nfc->bank_card);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ void nfc_scene_emulate_apdu_sequence_on_enter(void* context) {
|
|||||||
// Setup and start worker
|
// Setup and start worker
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||||
nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev.dev_data, NULL, nfc);
|
nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev->dev_data, NULL, nfc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) {
|
bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
|||||||
@ -14,8 +14,8 @@ void nfc_scene_emulate_mifare_ul_on_enter(void* context) {
|
|||||||
|
|
||||||
// Setup view
|
// Setup view
|
||||||
Popup* popup = nfc->popup;
|
Popup* popup = nfc->popup;
|
||||||
if(strcmp(nfc->dev.dev_name, "")) {
|
if(strcmp(nfc->dev->dev_name, "")) {
|
||||||
nfc_text_store_set(nfc, "%s", nfc->dev.dev_name);
|
nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
|
||||||
}
|
}
|
||||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
|
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||||
popup_set_header(popup, "Emulating\nMf Ultralight", 56, 31, AlignLeft, AlignTop);
|
popup_set_header(popup, "Emulating\nMf Ultralight", 56, 31, AlignLeft, AlignTop);
|
||||||
@ -25,7 +25,7 @@ void nfc_scene_emulate_mifare_ul_on_enter(void* context) {
|
|||||||
nfc_worker_start(
|
nfc_worker_start(
|
||||||
nfc->worker,
|
nfc->worker,
|
||||||
NfcWorkerStateEmulateMifareUl,
|
NfcWorkerStateEmulateMifareUl,
|
||||||
&nfc->dev.dev_data,
|
&nfc->dev->dev_data,
|
||||||
nfc_emulate_mifare_ul_worker_callback,
|
nfc_emulate_mifare_ul_worker_callback,
|
||||||
nfc);
|
nfc);
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event
|
|||||||
NFC_MF_UL_DATA_CHANGED) {
|
NFC_MF_UL_DATA_CHANGED) {
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_NOT_CHANGED);
|
nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_NOT_CHANGED);
|
||||||
nfc_device_save_shadow(&nfc->dev, nfc->dev.dev_name);
|
nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
|
||||||
}
|
}
|
||||||
consumed = false;
|
consumed = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,10 @@ void nfc_scene_emulate_uid_on_enter(void* context) {
|
|||||||
|
|
||||||
// Setup view
|
// Setup view
|
||||||
Popup* popup = nfc->popup;
|
Popup* popup = nfc->popup;
|
||||||
NfcDeviceCommonData* data = &nfc->dev.dev_data.nfc_data;
|
NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
|
||||||
|
|
||||||
if(strcmp(nfc->dev.dev_name, "")) {
|
if(strcmp(nfc->dev->dev_name, "")) {
|
||||||
nfc_text_store_set(nfc, "%s", nfc->dev.dev_name);
|
nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
|
||||||
} else if(data->uid_len == 4) {
|
} else if(data->uid_len == 4) {
|
||||||
nfc_text_store_set(
|
nfc_text_store_set(
|
||||||
nfc, "%02X %02X %02X %02X", data->uid[0], data->uid[1], data->uid[2], data->uid[3]);
|
nfc, "%02X %02X %02X %02X", data->uid[0], data->uid[1], data->uid[2], data->uid[3]);
|
||||||
@ -32,7 +32,7 @@ void nfc_scene_emulate_uid_on_enter(void* context) {
|
|||||||
// Setup and start worker
|
// Setup and start worker
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||||
nfc_worker_start(nfc->worker, NfcWorkerStateEmulate, &nfc->dev.dev_data, NULL, nfc);
|
nfc_worker_start(nfc->worker, NfcWorkerStateEmulate, &nfc->dev->dev_data, NULL, nfc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) {
|
bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
void nfc_scene_file_select_on_enter(void* context) {
|
void nfc_scene_file_select_on_enter(void* context) {
|
||||||
Nfc* nfc = (Nfc*)context;
|
Nfc* nfc = (Nfc*)context;
|
||||||
// Process file_select return
|
// Process file_select return
|
||||||
if(nfc_file_select(&nfc->dev)) {
|
if(nfc_file_select(nfc->dev)) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
|
||||||
} else {
|
} else {
|
||||||
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
|
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user