Merge remote-tracking branch 'origin/release-candidate' into release
17
.github/workflows/build.yml
vendored
@ -123,18 +123,11 @@ jobs:
|
||||
- name: 'Upload artifacts to update server'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts
|
||||
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
|
||||
chmod 600 ./deploy_key;
|
||||
rsync -avzP --delete --mkpath \
|
||||
-e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \
|
||||
artifacts/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:"${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${BRANCH_NAME}/";
|
||||
rm ./deploy_key;
|
||||
|
||||
- name: 'Trigger update server reindex'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }}
|
||||
FILES=$(for CUR in $(ls artifacts/); do echo "-F files=@artifacts/$CUR"; done)
|
||||
curl --fail -L -H "Token: ${{ secrets.INDEXER_TOKEN }}" \
|
||||
-F "branch=${BRANCH_NAME}" \
|
||||
${FILES[@]} \
|
||||
"${{ secrets.INDEXER_URL }}"/firmware/uploadfiles
|
||||
|
||||
- name: 'Find Previous Comment'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }}
|
||||
|
||||
3
.github/workflows/reindex.yml
vendored
@ -11,4 +11,5 @@ jobs:
|
||||
steps:
|
||||
- name: Trigger reindex
|
||||
run: |
|
||||
curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }}
|
||||
curl --fail -L -H "Token: ${{ secrets.INDEXER_TOKEN }}" \
|
||||
"${{ secrets.INDEXER_URL }}"/firmware/reindex
|
||||
|
||||
22
.gitignore
vendored
@ -30,27 +30,25 @@ bindings/
|
||||
.mxproject
|
||||
Brewfile.lock.json
|
||||
|
||||
# Visual Studio Code
|
||||
/.vscode/
|
||||
|
||||
# Kate
|
||||
.kateproject
|
||||
.kateconfig
|
||||
|
||||
# legendary cmake's
|
||||
build
|
||||
CMakeLists.txt
|
||||
|
||||
# bundle output
|
||||
dist
|
||||
|
||||
# kde
|
||||
.directory
|
||||
|
||||
# SCons
|
||||
.sconsign.dblite
|
||||
|
||||
|
||||
# Visual Studio Code
|
||||
/.vscode
|
||||
|
||||
# bundle output
|
||||
/dist
|
||||
|
||||
# SCons build dir
|
||||
build/
|
||||
/build
|
||||
|
||||
# Toolchain
|
||||
/toolchain
|
||||
@ -64,3 +62,5 @@ PVS-Studio.log
|
||||
*.PVS-Studio.*
|
||||
|
||||
.gdbinit
|
||||
|
||||
/fbt_options_local.py
|
||||
@ -14,9 +14,7 @@ void lfrfid_debug_scene_tune_on_enter(void* context) {
|
||||
furi_hal_rfid_comp_set_callback(comparator_trigger_callback, app);
|
||||
furi_hal_rfid_comp_start();
|
||||
|
||||
furi_hal_rfid_pins_read();
|
||||
furi_hal_rfid_tim_read(125000, 0.5);
|
||||
furi_hal_rfid_tim_read_start();
|
||||
furi_hal_rfid_tim_read_start(125000, 0.5);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune);
|
||||
}
|
||||
@ -43,6 +41,5 @@ void lfrfid_debug_scene_tune_on_exit(void* context) {
|
||||
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
furi_hal_rfid_tim_reset();
|
||||
furi_hal_rfid_pins_reset();
|
||||
}
|
||||
|
||||
@ -27,6 +27,12 @@ static const uint32_t nfc_test_file_version = 1;
|
||||
#define NFC_TEST_DATA_MAX_LEN 18
|
||||
#define NFC_TETS_TIMINGS_MAX_LEN 1350
|
||||
|
||||
// Maximum allowed time for buffer preparation to fit 500us nt message timeout
|
||||
#define NFC_TEST_4_BYTE_BUILD_BUFFER_TIM_MAX (150)
|
||||
#define NFC_TEST_16_BYTE_BUILD_BUFFER_TIM_MAX (640)
|
||||
#define NFC_TEST_4_BYTE_BUILD_SIGNAL_TIM_MAX (110)
|
||||
#define NFC_TEST_16_BYTE_BUILD_SIGNAL_TIM_MAX (440)
|
||||
|
||||
typedef struct {
|
||||
Storage* storage;
|
||||
NfcaSignal* signal;
|
||||
@ -89,13 +95,13 @@ static bool nfc_test_read_signal_from_file(const char* file_name) {
|
||||
|
||||
static bool nfc_test_digital_signal_test_encode(
|
||||
const char* file_name,
|
||||
uint32_t encode_max_time,
|
||||
uint32_t build_signal_max_time_us,
|
||||
uint32_t build_buffer_max_time_us,
|
||||
uint32_t timing_tolerance,
|
||||
uint32_t timings_sum_tolerance) {
|
||||
furi_assert(nfc_test);
|
||||
|
||||
bool success = false;
|
||||
uint32_t time = 0;
|
||||
uint32_t dut_timings_sum = 0;
|
||||
uint32_t ref_timings_sum = 0;
|
||||
uint8_t parity[10] = {};
|
||||
@ -109,17 +115,37 @@ static bool nfc_test_digital_signal_test_encode(
|
||||
|
||||
// Encode signal
|
||||
FURI_CRITICAL_ENTER();
|
||||
time = DWT->CYCCNT;
|
||||
uint32_t time_start = DWT->CYCCNT;
|
||||
|
||||
nfca_signal_encode(
|
||||
nfc_test->signal, nfc_test->test_data, nfc_test->test_data_len * 8, parity);
|
||||
|
||||
uint32_t time_signal =
|
||||
(DWT->CYCCNT - time_start) / furi_hal_cortex_instructions_per_microsecond();
|
||||
|
||||
time_start = DWT->CYCCNT;
|
||||
|
||||
digital_signal_prepare_arr(nfc_test->signal->tx_signal);
|
||||
time = (DWT->CYCCNT - time) / furi_hal_cortex_instructions_per_microsecond();
|
||||
|
||||
uint32_t time_buffer =
|
||||
(DWT->CYCCNT - time_start) / furi_hal_cortex_instructions_per_microsecond();
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
// Check timings
|
||||
if(time > encode_max_time) {
|
||||
if(time_signal > build_signal_max_time_us) {
|
||||
FURI_LOG_E(
|
||||
TAG, "Encoding time: %ld us while accepted value: %ld us", time, encode_max_time);
|
||||
TAG,
|
||||
"Build signal time: %ld us while accepted value: %ld us",
|
||||
time_signal,
|
||||
build_signal_max_time_us);
|
||||
break;
|
||||
}
|
||||
if(time_buffer > build_buffer_max_time_us) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Build buffer time: %ld us while accepted value: %ld us",
|
||||
time_buffer,
|
||||
build_buffer_max_time_us);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -156,7 +182,16 @@ static bool nfc_test_digital_signal_test_encode(
|
||||
break;
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Encoding time: %ld us. Acceptable time: %ld us", time, encode_max_time);
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Build signal time: %ld us. Acceptable time: %ld us",
|
||||
time_signal,
|
||||
build_signal_max_time_us);
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Build buffer time: %ld us. Acceptable time: %ld us",
|
||||
time_buffer,
|
||||
build_buffer_max_time_us);
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Timings sum difference: %ld [1/64MHZ]. Acceptable difference: %ld [1/64MHz]",
|
||||
@ -171,11 +206,19 @@ static bool nfc_test_digital_signal_test_encode(
|
||||
MU_TEST(nfc_digital_signal_test) {
|
||||
mu_assert(
|
||||
nfc_test_digital_signal_test_encode(
|
||||
NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE, 500, 1, 37),
|
||||
NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE,
|
||||
NFC_TEST_4_BYTE_BUILD_SIGNAL_TIM_MAX,
|
||||
NFC_TEST_4_BYTE_BUILD_BUFFER_TIM_MAX,
|
||||
1,
|
||||
37),
|
||||
"NFC short digital signal test failed\r\n");
|
||||
mu_assert(
|
||||
nfc_test_digital_signal_test_encode(
|
||||
NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE, 2000, 1, 37),
|
||||
NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE,
|
||||
NFC_TEST_16_BYTE_BUILD_SIGNAL_TIM_MAX,
|
||||
NFC_TEST_16_BYTE_BUILD_BUFFER_TIM_MAX,
|
||||
1,
|
||||
37),
|
||||
"NFC long digital signal test failed\r\n");
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
#include "magic.h"
|
||||
#include "classic_gen1.h"
|
||||
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
#define TAG "Magic"
|
||||
|
||||
#define MAGIC_CMD_WUPA (0x40)
|
||||
#define MAGIC_CMD_WIPE (0x41)
|
||||
#define MAGIC_CMD_ACCESS (0x43)
|
||||
|
||||
#define MAGIC_MIFARE_READ_CMD (0x30)
|
||||
@ -15,7 +14,7 @@
|
||||
|
||||
#define MAGIC_BUFFER_SIZE (32)
|
||||
|
||||
bool magic_wupa() {
|
||||
bool magic_gen1_wupa() {
|
||||
bool magic_activated = false;
|
||||
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
@ -23,19 +22,6 @@ bool magic_wupa() {
|
||||
FuriHalNfcReturn ret = 0;
|
||||
|
||||
do {
|
||||
// Setup nfc poller
|
||||
furi_hal_nfc_exit_sleep();
|
||||
furi_hal_nfc_ll_txrx_on();
|
||||
furi_hal_nfc_ll_poll();
|
||||
ret = furi_hal_nfc_ll_set_mode(
|
||||
FuriHalNfcModePollNfca, FuriHalNfcBitrate106, FuriHalNfcBitrate106);
|
||||
if(ret != FuriHalNfcReturnOk) break;
|
||||
|
||||
furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER);
|
||||
furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER);
|
||||
furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc);
|
||||
furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCA);
|
||||
|
||||
// Start communication
|
||||
tx_data[0] = MAGIC_CMD_WUPA;
|
||||
ret = furi_hal_nfc_ll_txrx_bits(
|
||||
@ -53,15 +39,10 @@ bool magic_wupa() {
|
||||
magic_activated = true;
|
||||
} while(false);
|
||||
|
||||
if(!magic_activated) {
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_hal_nfc_start_sleep();
|
||||
}
|
||||
|
||||
return magic_activated;
|
||||
}
|
||||
|
||||
bool magic_data_access_cmd() {
|
||||
bool magic_gen1_data_access_cmd() {
|
||||
bool write_cmd_success = false;
|
||||
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
@ -86,15 +67,10 @@ bool magic_data_access_cmd() {
|
||||
write_cmd_success = true;
|
||||
} while(false);
|
||||
|
||||
if(!write_cmd_success) {
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_hal_nfc_start_sleep();
|
||||
}
|
||||
|
||||
return write_cmd_success;
|
||||
}
|
||||
|
||||
bool magic_read_block(uint8_t block_num, MfClassicBlock* data) {
|
||||
bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data) {
|
||||
furi_assert(data);
|
||||
|
||||
bool read_success = false;
|
||||
@ -122,15 +98,10 @@ bool magic_read_block(uint8_t block_num, MfClassicBlock* data) {
|
||||
read_success = true;
|
||||
} while(false);
|
||||
|
||||
if(!read_success) {
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_hal_nfc_start_sleep();
|
||||
}
|
||||
|
||||
return read_success;
|
||||
}
|
||||
|
||||
bool magic_write_blk(uint8_t block_num, MfClassicBlock* data) {
|
||||
bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data) {
|
||||
furi_assert(data);
|
||||
|
||||
bool write_success = false;
|
||||
@ -170,44 +141,5 @@ bool magic_write_blk(uint8_t block_num, MfClassicBlock* data) {
|
||||
write_success = true;
|
||||
} while(false);
|
||||
|
||||
if(!write_success) {
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_hal_nfc_start_sleep();
|
||||
}
|
||||
|
||||
return write_success;
|
||||
}
|
||||
|
||||
bool magic_wipe() {
|
||||
bool wipe_success = false;
|
||||
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint16_t rx_len = 0;
|
||||
FuriHalNfcReturn ret = 0;
|
||||
|
||||
do {
|
||||
tx_data[0] = MAGIC_CMD_WIPE;
|
||||
ret = furi_hal_nfc_ll_txrx_bits(
|
||||
tx_data,
|
||||
8,
|
||||
rx_data,
|
||||
sizeof(rx_data),
|
||||
&rx_len,
|
||||
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON |
|
||||
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
|
||||
furi_hal_nfc_ll_ms2fc(2000));
|
||||
|
||||
if(ret != FuriHalNfcReturnIncompleteByte) break;
|
||||
if(rx_len != 4) break;
|
||||
if(rx_data[0] != MAGIC_ACK) break;
|
||||
|
||||
wipe_success = true;
|
||||
} while(false);
|
||||
|
||||
return wipe_success;
|
||||
}
|
||||
|
||||
void magic_deactivate() {
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_hal_nfc_sleep();
|
||||
}
|
||||
11
applications/external/nfc_magic/lib/magic/classic_gen1.h
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
|
||||
bool magic_gen1_wupa();
|
||||
|
||||
bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data);
|
||||
|
||||
bool magic_gen1_data_access_cmd();
|
||||
|
||||
bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data);
|
||||
33
applications/external/nfc_magic/lib/magic/common.c
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
#define REQA (0x26)
|
||||
#define CL1_PREFIX (0x93)
|
||||
#define SELECT (0x70)
|
||||
|
||||
#define MAGIC_BUFFER_SIZE (32)
|
||||
|
||||
bool magic_activate() {
|
||||
FuriHalNfcReturn ret = 0;
|
||||
|
||||
// Setup nfc poller
|
||||
furi_hal_nfc_exit_sleep();
|
||||
furi_hal_nfc_ll_txrx_on();
|
||||
furi_hal_nfc_ll_poll();
|
||||
ret = furi_hal_nfc_ll_set_mode(
|
||||
FuriHalNfcModePollNfca, FuriHalNfcBitrate106, FuriHalNfcBitrate106);
|
||||
if(ret != FuriHalNfcReturnOk) return false;
|
||||
|
||||
furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER);
|
||||
furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER);
|
||||
furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc);
|
||||
furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCA);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void magic_deactivate() {
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_hal_nfc_sleep();
|
||||
}
|
||||
19
applications/external/nfc_magic/lib/magic/common.h
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
MagicTypeClassicGen1,
|
||||
MagicTypeClassicDirectWrite,
|
||||
MagicTypeClassicAPDU,
|
||||
MagicTypeUltralightGen1,
|
||||
MagicTypeUltralightDirectWrite,
|
||||
MagicTypeUltralightC_Gen1,
|
||||
MagicTypeUltralightC_DirectWrite,
|
||||
MagicTypeGen4,
|
||||
} MagicType;
|
||||
|
||||
bool magic_activate();
|
||||
|
||||
void magic_deactivate();
|
||||
199
applications/external/nfc_magic/lib/magic/gen4.c
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
#include "gen4.h"
|
||||
|
||||
#include <furi_hal_nfc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define TAG "Magic"
|
||||
|
||||
#define MAGIC_CMD_PREFIX (0xCF)
|
||||
|
||||
#define MAGIC_CMD_GET_CFG (0xC6)
|
||||
#define MAGIC_CMD_WRITE (0xCD)
|
||||
#define MAGIC_CMD_READ (0xCE)
|
||||
#define MAGIC_CMD_SET_CFG (0xF0)
|
||||
#define MAGIC_CMD_FUSE_CFG (0xF1)
|
||||
#define MAGIC_CMD_SET_PWD (0xFE)
|
||||
|
||||
#define MAGIC_BUFFER_SIZE (40)
|
||||
|
||||
const uint8_t MAGIC_DEFAULT_CONFIG[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x78, 0x00, 0x91, 0x02, 0xDA, 0xBC, 0x19, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x04, 0x00, 0x08, 0x00
|
||||
};
|
||||
|
||||
const uint8_t MAGIC_DEFAULT_BLOCK0[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
const uint8_t MAGIC_EMPTY_BLOCK[16] = { 0 };
|
||||
|
||||
const uint8_t MAGIC_DEFAULT_SECTOR_TRAILER[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
static bool magic_gen4_is_block_num_trailer(uint8_t n) {
|
||||
n++;
|
||||
if (n < 32 * 4) {
|
||||
return (n % 4 == 0);
|
||||
}
|
||||
|
||||
return (n % 16 == 0);
|
||||
}
|
||||
|
||||
bool magic_gen4_get_cfg(uint32_t pwd, uint8_t* config) {
|
||||
bool is_valid_config_len = false;
|
||||
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint16_t rx_len = 0;
|
||||
FuriHalNfcReturn ret = 0;
|
||||
|
||||
do {
|
||||
// Start communication
|
||||
tx_data[0] = MAGIC_CMD_PREFIX;
|
||||
tx_data[1] = (uint8_t)(pwd >> 24);
|
||||
tx_data[2] = (uint8_t)(pwd >> 16);
|
||||
tx_data[3] = (uint8_t)(pwd >> 8);
|
||||
tx_data[4] = (uint8_t)pwd;
|
||||
tx_data[5] = MAGIC_CMD_GET_CFG;
|
||||
ret = furi_hal_nfc_ll_txrx(
|
||||
tx_data,
|
||||
6,
|
||||
rx_data,
|
||||
sizeof(rx_data),
|
||||
&rx_len,
|
||||
FURI_HAL_NFC_TXRX_DEFAULT,
|
||||
furi_hal_nfc_ll_ms2fc(20));
|
||||
if(ret != FuriHalNfcReturnOk) break;
|
||||
if(rx_len != 30 && rx_len != 32) break;
|
||||
memcpy(config, rx_data, rx_len);
|
||||
is_valid_config_len = true;
|
||||
} while(false);
|
||||
|
||||
return is_valid_config_len;
|
||||
}
|
||||
|
||||
bool magic_gen4_set_cfg(uint32_t pwd, const uint8_t* config, uint8_t config_length, bool fuse) {
|
||||
bool write_success = false;
|
||||
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint16_t rx_len = 0;
|
||||
FuriHalNfcReturn ret = 0;
|
||||
|
||||
do {
|
||||
// Start communication
|
||||
tx_data[0] = MAGIC_CMD_PREFIX;
|
||||
tx_data[1] = (uint8_t)(pwd >> 24);
|
||||
tx_data[2] = (uint8_t)(pwd >> 16);
|
||||
tx_data[3] = (uint8_t)(pwd >> 8);
|
||||
tx_data[4] = (uint8_t)pwd;
|
||||
tx_data[5] = fuse ? MAGIC_CMD_FUSE_CFG : MAGIC_CMD_SET_CFG;
|
||||
memcpy(tx_data + 6, config, config_length);
|
||||
ret = furi_hal_nfc_ll_txrx(
|
||||
tx_data,
|
||||
6 + config_length,
|
||||
rx_data,
|
||||
sizeof(rx_data),
|
||||
&rx_len,
|
||||
FURI_HAL_NFC_TXRX_DEFAULT,
|
||||
furi_hal_nfc_ll_ms2fc(20));
|
||||
if(ret != FuriHalNfcReturnOk) break;
|
||||
if(rx_len != 2) break;
|
||||
write_success = true;
|
||||
} while(false);
|
||||
|
||||
return write_success;
|
||||
}
|
||||
|
||||
bool magic_gen4_set_pwd(uint32_t old_pwd, uint32_t new_pwd) {
|
||||
bool change_success = false;
|
||||
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint16_t rx_len = 0;
|
||||
FuriHalNfcReturn ret = 0;
|
||||
|
||||
do {
|
||||
// Start communication
|
||||
tx_data[0] = MAGIC_CMD_PREFIX;
|
||||
tx_data[1] = (uint8_t)(old_pwd >> 24);
|
||||
tx_data[2] = (uint8_t)(old_pwd >> 16);
|
||||
tx_data[3] = (uint8_t)(old_pwd >> 8);
|
||||
tx_data[4] = (uint8_t)old_pwd;
|
||||
tx_data[5] = MAGIC_CMD_SET_PWD;
|
||||
tx_data[6] = (uint8_t)(new_pwd >> 24);
|
||||
tx_data[7] = (uint8_t)(new_pwd >> 16);
|
||||
tx_data[8] = (uint8_t)(new_pwd >> 8);
|
||||
tx_data[9] = (uint8_t)new_pwd;
|
||||
ret = furi_hal_nfc_ll_txrx(
|
||||
tx_data,
|
||||
10,
|
||||
rx_data,
|
||||
sizeof(rx_data),
|
||||
&rx_len,
|
||||
FURI_HAL_NFC_TXRX_DEFAULT,
|
||||
furi_hal_nfc_ll_ms2fc(20));
|
||||
FURI_LOG_I(TAG, "ret %d, len %d", ret, rx_len);
|
||||
if(ret != FuriHalNfcReturnOk) break;
|
||||
if(rx_len != 2) break;
|
||||
change_success = true;
|
||||
} while(false);
|
||||
|
||||
return change_success;
|
||||
}
|
||||
|
||||
bool magic_gen4_write_blk(uint32_t pwd, uint8_t block_num, const uint8_t* data) {
|
||||
bool write_success = false;
|
||||
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
|
||||
uint16_t rx_len = 0;
|
||||
FuriHalNfcReturn ret = 0;
|
||||
|
||||
do {
|
||||
// Start communication
|
||||
tx_data[0] = MAGIC_CMD_PREFIX;
|
||||
tx_data[1] = (uint8_t)(pwd >> 24);
|
||||
tx_data[2] = (uint8_t)(pwd >> 16);
|
||||
tx_data[3] = (uint8_t)(pwd >> 8);
|
||||
tx_data[4] = (uint8_t)pwd;
|
||||
tx_data[5] = MAGIC_CMD_WRITE;
|
||||
tx_data[6] = block_num;
|
||||
memcpy(tx_data + 7, data, 16);
|
||||
ret = furi_hal_nfc_ll_txrx(
|
||||
tx_data,
|
||||
23,
|
||||
rx_data,
|
||||
sizeof(rx_data),
|
||||
&rx_len,
|
||||
FURI_HAL_NFC_TXRX_DEFAULT,
|
||||
furi_hal_nfc_ll_ms2fc(200));
|
||||
if(ret != FuriHalNfcReturnOk) break;
|
||||
if(rx_len != 2) break;
|
||||
write_success = true;
|
||||
} while(false);
|
||||
|
||||
return write_success;
|
||||
}
|
||||
|
||||
bool magic_gen4_wipe(uint32_t pwd) {
|
||||
if(!magic_gen4_set_cfg(pwd, MAGIC_DEFAULT_CONFIG, sizeof(MAGIC_DEFAULT_CONFIG), false)) {
|
||||
FURI_LOG_E(TAG, "Set config failed");
|
||||
return false;
|
||||
}
|
||||
if(!magic_gen4_write_blk(pwd, 0, MAGIC_DEFAULT_BLOCK0)) {
|
||||
FURI_LOG_E(TAG, "Block 0 write failed");
|
||||
return false;
|
||||
}
|
||||
for(size_t i = 1; i < 64; i++) {
|
||||
const uint8_t* block = magic_gen4_is_block_num_trailer(i) ? MAGIC_DEFAULT_SECTOR_TRAILER : MAGIC_EMPTY_BLOCK;
|
||||
if(!magic_gen4_write_blk(pwd, i, block)) {
|
||||
FURI_LOG_E(TAG, "Block %d write failed", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for(size_t i = 65; i < 256; i++) {
|
||||
if(!magic_gen4_write_blk(pwd, i, MAGIC_EMPTY_BLOCK)) {
|
||||
FURI_LOG_E(TAG, "Block %d write failed", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
48
applications/external/nfc_magic/lib/magic/gen4.h
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
|
||||
#define MAGIC_GEN4_DEFAULT_PWD 0x00000000
|
||||
#define MAGIC_GEN4_CONFIG_LEN 32
|
||||
|
||||
#define NFCID1_SINGLE_SIZE 4
|
||||
#define NFCID1_DOUBLE_SIZE 7
|
||||
#define NFCID1_TRIPLE_SIZE 10
|
||||
|
||||
typedef enum {
|
||||
MagicGen4UIDLengthSingle = 0x00,
|
||||
MagicGen4UIDLengthDouble = 0x01,
|
||||
MagicGen4UIDLengthTriple = 0x02
|
||||
} MagicGen4UIDLength;
|
||||
|
||||
typedef enum {
|
||||
MagicGen4UltralightModeUL_EV1 = 0x00,
|
||||
MagicGen4UltralightModeNTAG = 0x01,
|
||||
MagicGen4UltralightModeUL_C = 0x02,
|
||||
MagicGen4UltralightModeUL = 0x03
|
||||
} MagicGen4UltralightMode;
|
||||
|
||||
typedef enum {
|
||||
// for writing original (shadow) data
|
||||
MagicGen4ShadowModePreWrite = 0x00,
|
||||
// written data can be read once before restored to original
|
||||
MagicGen4ShadowModeRestore = 0x01,
|
||||
// written data is discarded
|
||||
MagicGen4ShadowModeIgnore = 0x02,
|
||||
// apparently for UL?
|
||||
MagicGen4ShadowModeHighSpeedIgnore = 0x03
|
||||
} MagicGen4ShadowMode;
|
||||
|
||||
bool magic_gen4_get_cfg(uint32_t pwd, uint8_t* config);
|
||||
|
||||
bool magic_gen4_set_cfg(uint32_t pwd, const uint8_t* config, uint8_t config_length, bool fuse);
|
||||
|
||||
bool magic_gen4_set_pwd(uint32_t old_pwd, uint32_t new_pwd);
|
||||
|
||||
bool magic_gen4_read_blk(uint32_t pwd, uint8_t block_num, uint8_t* data);
|
||||
|
||||
bool magic_gen4_write_blk(uint32_t pwd, uint8_t block_num, const uint8_t* data);
|
||||
|
||||
bool magic_gen4_wipe(uint32_t pwd);
|
||||
|
||||
void magic_gen4_deactivate();
|
||||
@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
|
||||
bool magic_wupa();
|
||||
|
||||
bool magic_read_block(uint8_t block_num, MfClassicBlock* data);
|
||||
|
||||
bool magic_data_access_cmd();
|
||||
|
||||
bool magic_write_blk(uint8_t block_num, MfClassicBlock* data);
|
||||
|
||||
bool magic_wipe();
|
||||
|
||||
void magic_deactivate();
|
||||
23
applications/external/nfc_magic/lib/magic/types.c
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
#include "types.h"
|
||||
|
||||
const char* nfc_magic_type(MagicType type) {
|
||||
if(type == MagicTypeClassicGen1) {
|
||||
return "Classic Gen 1A/B";
|
||||
} else if(type == MagicTypeClassicDirectWrite) {
|
||||
return "Classic DirectWrite";
|
||||
} else if(type == MagicTypeClassicAPDU) {
|
||||
return "Classic APDU";
|
||||
} else if(type == MagicTypeUltralightGen1) {
|
||||
return "Ultralight Gen 1";
|
||||
} else if(type == MagicTypeUltralightDirectWrite) {
|
||||
return "Ultralight DirectWrite";
|
||||
} else if(type == MagicTypeUltralightC_Gen1) {
|
||||
return "Ultralight-C Gen 1";
|
||||
} else if(type == MagicTypeUltralightC_DirectWrite) {
|
||||
return "Ultralight-C DirectWrite";
|
||||
} else if(type == MagicTypeGen4) {
|
||||
return "Gen 4 GTU";
|
||||
} else {
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
5
applications/external/nfc_magic/lib/magic/types.h
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
const char* nfc_magic_type(MagicType type);
|
||||
20
applications/external/nfc_magic/nfc_magic.c
vendored
@ -48,8 +48,9 @@ NfcMagic* nfc_magic_alloc() {
|
||||
nfc_magic->view_dispatcher, nfc_magic_tick_event_callback, 100);
|
||||
|
||||
// Nfc device
|
||||
nfc_magic->nfc_dev = nfc_device_alloc();
|
||||
furi_string_set(nfc_magic->nfc_dev->folder, NFC_APP_FOLDER);
|
||||
nfc_magic->dev = malloc(sizeof(NfcMagicDevice));
|
||||
nfc_magic->source_dev = nfc_device_alloc();
|
||||
furi_string_set(nfc_magic->source_dev->folder, NFC_APP_FOLDER);
|
||||
|
||||
// Open GUI record
|
||||
nfc_magic->gui = furi_record_open(RECORD_GUI);
|
||||
@ -81,6 +82,13 @@ NfcMagic* nfc_magic_alloc() {
|
||||
NfcMagicViewTextInput,
|
||||
text_input_get_view(nfc_magic->text_input));
|
||||
|
||||
// Byte Input
|
||||
nfc_magic->byte_input = byte_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
nfc_magic->view_dispatcher,
|
||||
NfcMagicViewByteInput,
|
||||
byte_input_get_view(nfc_magic->byte_input));
|
||||
|
||||
// Custom Widget
|
||||
nfc_magic->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
@ -93,7 +101,8 @@ void nfc_magic_free(NfcMagic* nfc_magic) {
|
||||
furi_assert(nfc_magic);
|
||||
|
||||
// Nfc device
|
||||
nfc_device_free(nfc_magic->nfc_dev);
|
||||
free(nfc_magic->dev);
|
||||
nfc_device_free(nfc_magic->source_dev);
|
||||
|
||||
// Submenu
|
||||
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewMenu);
|
||||
@ -111,6 +120,10 @@ void nfc_magic_free(NfcMagic* nfc_magic) {
|
||||
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewTextInput);
|
||||
text_input_free(nfc_magic->text_input);
|
||||
|
||||
// Byte Input
|
||||
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput);
|
||||
byte_input_free(nfc_magic->byte_input);
|
||||
|
||||
// Custom Widget
|
||||
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewWidget);
|
||||
widget_free(nfc_magic->widget);
|
||||
@ -164,6 +177,7 @@ int32_t nfc_magic_app(void* p) {
|
||||
|
||||
view_dispatcher_run(nfc_magic->view_dispatcher);
|
||||
|
||||
magic_deactivate();
|
||||
nfc_magic_free(nfc_magic);
|
||||
|
||||
return 0;
|
||||
|
||||
2
applications/external/nfc_magic/nfc_magic.h
vendored
@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct NfcMagicDevice NfcMagicDevice;
|
||||
|
||||
typedef struct NfcMagic NfcMagic;
|
||||
|
||||
20
applications/external/nfc_magic/nfc_magic_i.h
vendored
@ -3,7 +3,10 @@
|
||||
#include "nfc_magic.h"
|
||||
#include "nfc_magic_worker.h"
|
||||
|
||||
#include "lib/magic/magic.h"
|
||||
#include "lib/magic/common.h"
|
||||
#include "lib/magic/types.h"
|
||||
#include "lib/magic/classic_gen1.h"
|
||||
#include "lib/magic/gen4.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
@ -15,6 +18,7 @@
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/byte_input.h>
|
||||
#include <gui/modules/widget.h>
|
||||
|
||||
#include <input/input.h>
|
||||
@ -39,14 +43,22 @@ enum NfcMagicCustomEvent {
|
||||
NfcMagicCustomEventTextInputDone,
|
||||
};
|
||||
|
||||
struct NfcMagicDevice {
|
||||
MagicType type;
|
||||
uint32_t cuid;
|
||||
uint32_t password;
|
||||
};
|
||||
|
||||
struct NfcMagic {
|
||||
NfcMagicWorker* worker;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Gui* gui;
|
||||
NotificationApp* notifications;
|
||||
SceneManager* scene_manager;
|
||||
// NfcMagicDevice* dev;
|
||||
NfcDevice* nfc_dev;
|
||||
struct NfcMagicDevice* dev;
|
||||
NfcDevice* source_dev;
|
||||
|
||||
uint32_t new_password;
|
||||
|
||||
FuriString* text_box_store;
|
||||
|
||||
@ -55,6 +67,7 @@ struct NfcMagic {
|
||||
Popup* popup;
|
||||
Loading* loading;
|
||||
TextInput* text_input;
|
||||
ByteInput* byte_input;
|
||||
Widget* widget;
|
||||
};
|
||||
|
||||
@ -63,6 +76,7 @@ typedef enum {
|
||||
NfcMagicViewPopup,
|
||||
NfcMagicViewLoading,
|
||||
NfcMagicViewTextInput,
|
||||
NfcMagicViewByteInput,
|
||||
NfcMagicViewWidget,
|
||||
} NfcMagicView;
|
||||
|
||||
|
||||
350
applications/external/nfc_magic/nfc_magic_worker.c
vendored
@ -1,6 +1,9 @@
|
||||
#include "nfc_magic_worker_i.h"
|
||||
|
||||
#include "lib/magic/magic.h"
|
||||
#include "nfc_magic_i.h"
|
||||
#include "lib/magic/common.h"
|
||||
#include "lib/magic/classic_gen1.h"
|
||||
#include "lib/magic/gen4.h"
|
||||
|
||||
#define TAG "NfcMagicWorker"
|
||||
|
||||
@ -43,15 +46,20 @@ void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker) {
|
||||
void nfc_magic_worker_start(
|
||||
NfcMagicWorker* nfc_magic_worker,
|
||||
NfcMagicWorkerState state,
|
||||
NfcMagicDevice* magic_dev,
|
||||
NfcDeviceData* dev_data,
|
||||
uint32_t new_password,
|
||||
NfcMagicWorkerCallback callback,
|
||||
void* context) {
|
||||
furi_assert(nfc_magic_worker);
|
||||
furi_assert(magic_dev);
|
||||
furi_assert(dev_data);
|
||||
|
||||
nfc_magic_worker->callback = callback;
|
||||
nfc_magic_worker->context = context;
|
||||
nfc_magic_worker->magic_dev = magic_dev;
|
||||
nfc_magic_worker->dev_data = dev_data;
|
||||
nfc_magic_worker->new_password = new_password;
|
||||
nfc_magic_worker_change_state(nfc_magic_worker, state);
|
||||
furi_thread_start(nfc_magic_worker->thread);
|
||||
}
|
||||
@ -63,6 +71,8 @@ int32_t nfc_magic_worker_task(void* context) {
|
||||
nfc_magic_worker_check(nfc_magic_worker);
|
||||
} else if(nfc_magic_worker->state == NfcMagicWorkerStateWrite) {
|
||||
nfc_magic_worker_write(nfc_magic_worker);
|
||||
} else if(nfc_magic_worker->state == NfcMagicWorkerStateRekey) {
|
||||
nfc_magic_worker_rekey(nfc_magic_worker);
|
||||
} else if(nfc_magic_worker->state == NfcMagicWorkerStateWipe) {
|
||||
nfc_magic_worker_wipe(nfc_magic_worker);
|
||||
}
|
||||
@ -74,59 +84,244 @@ int32_t nfc_magic_worker_task(void* context) {
|
||||
|
||||
void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) {
|
||||
bool card_found_notified = false;
|
||||
bool done = false;
|
||||
FuriHalNfcDevData nfc_data = {};
|
||||
MfClassicData* src_data = &nfc_magic_worker->dev_data->mf_classic_data;
|
||||
NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
|
||||
NfcDeviceData* dev_data = nfc_magic_worker->dev_data;
|
||||
NfcProtocol dev_protocol = dev_data->protocol;
|
||||
|
||||
while(nfc_magic_worker->state == NfcMagicWorkerStateWrite) {
|
||||
do {
|
||||
if(magic_dev->type == MagicTypeClassicGen1) {
|
||||
if(furi_hal_nfc_detect(&nfc_data, 200)) {
|
||||
if(!card_found_notified) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = true;
|
||||
}
|
||||
furi_hal_nfc_sleep();
|
||||
if(!magic_wupa()) {
|
||||
magic_deactivate();
|
||||
magic_activate();
|
||||
if(!magic_gen1_wupa()) {
|
||||
FURI_LOG_E(TAG, "No card response to WUPA (not a magic card)");
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
furi_hal_nfc_sleep();
|
||||
magic_deactivate();
|
||||
}
|
||||
if(magic_wupa()) {
|
||||
if(!magic_data_access_cmd()) {
|
||||
FURI_LOG_E(TAG, "No card response to data access command (not a magic card)");
|
||||
magic_activate();
|
||||
if(magic_gen1_wupa()) {
|
||||
if(!magic_gen1_data_access_cmd()) {
|
||||
FURI_LOG_E(
|
||||
TAG, "No card response to data access command (not a magic card)");
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
MfClassicData* mfc_data = &dev_data->mf_classic_data;
|
||||
for(size_t i = 0; i < 64; i++) {
|
||||
FURI_LOG_D(TAG, "Writing block %d", i);
|
||||
if(!magic_write_blk(i, &src_data->block[i])) {
|
||||
if(!magic_gen1_write_blk(i, &mfc_data->block[i])) {
|
||||
FURI_LOG_E(TAG, "Failed to write %d block", i);
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
done = true;
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
|
||||
done = true;
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
break;
|
||||
}
|
||||
} else if(magic_dev->type == MagicTypeGen4) {
|
||||
if(furi_hal_nfc_detect(&nfc_data, 200)) {
|
||||
uint8_t gen4_config[28];
|
||||
uint32_t password = magic_dev->password;
|
||||
|
||||
uint32_t cuid;
|
||||
if(dev_protocol == NfcDeviceProtocolMifareClassic) {
|
||||
gen4_config[0] = 0x00;
|
||||
gen4_config[27] = 0x00;
|
||||
} else if(dev_protocol == NfcDeviceProtocolMifareUl) {
|
||||
MfUltralightData* mf_ul_data = &dev_data->mf_ul_data;
|
||||
gen4_config[0] = 0x01;
|
||||
switch(mf_ul_data->type) {
|
||||
case MfUltralightTypeUL11:
|
||||
case MfUltralightTypeUL21:
|
||||
// UL-C?
|
||||
// UL?
|
||||
default:
|
||||
gen4_config[27] = MagicGen4UltralightModeUL_EV1;
|
||||
break;
|
||||
case MfUltralightTypeNTAG203:
|
||||
case MfUltralightTypeNTAG213:
|
||||
case MfUltralightTypeNTAG215:
|
||||
case MfUltralightTypeNTAG216:
|
||||
case MfUltralightTypeNTAGI2C1K:
|
||||
case MfUltralightTypeNTAGI2C2K:
|
||||
case MfUltralightTypeNTAGI2CPlus1K:
|
||||
case MfUltralightTypeNTAGI2CPlus2K:
|
||||
gen4_config[27] = MagicGen4UltralightModeNTAG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(dev_data->nfc_data.uid_len == 4) {
|
||||
gen4_config[1] = MagicGen4UIDLengthSingle;
|
||||
} else if(dev_data->nfc_data.uid_len == 7) {
|
||||
gen4_config[1] = MagicGen4UIDLengthDouble;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unexpected UID length %d", dev_data->nfc_data.uid_len);
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
gen4_config[2] = (uint8_t)(password >> 24);
|
||||
gen4_config[3] = (uint8_t)(password >> 16);
|
||||
gen4_config[4] = (uint8_t)(password >> 8);
|
||||
gen4_config[5] = (uint8_t)password;
|
||||
|
||||
if(dev_protocol == NfcDeviceProtocolMifareUl) {
|
||||
gen4_config[6] = MagicGen4ShadowModeHighSpeedIgnore;
|
||||
} else {
|
||||
gen4_config[6] = MagicGen4ShadowModeIgnore;
|
||||
}
|
||||
gen4_config[7] = 0x00;
|
||||
memset(gen4_config + 8, 0, 16);
|
||||
gen4_config[24] = dev_data->nfc_data.atqa[0];
|
||||
gen4_config[25] = dev_data->nfc_data.atqa[1];
|
||||
gen4_config[26] = dev_data->nfc_data.sak;
|
||||
|
||||
furi_hal_nfc_sleep();
|
||||
furi_hal_nfc_activate_nfca(200, &cuid);
|
||||
if(!magic_gen4_set_cfg(password, gen4_config, sizeof(gen4_config), false)) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
if(dev_protocol == NfcDeviceProtocolMifareClassic) {
|
||||
MfClassicData* mfc_data = &dev_data->mf_classic_data;
|
||||
size_t block_count = 64;
|
||||
if(mfc_data->type == MfClassicType4k) block_count = 256;
|
||||
for(size_t i = 0; i < block_count; i++) {
|
||||
FURI_LOG_D(TAG, "Writing block %d", i);
|
||||
if(!magic_gen4_write_blk(password, i, mfc_data->block[i].value)) {
|
||||
FURI_LOG_E(TAG, "Failed to write %d block", i);
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if(dev_protocol == NfcDeviceProtocolMifareUl) {
|
||||
MfUltralightData* mf_ul_data = &dev_data->mf_ul_data;
|
||||
for(size_t i = 0; (i * 4) < mf_ul_data->data_read; i++) {
|
||||
size_t data_offset = i * 4;
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Writing page %zu (%zu/%u)",
|
||||
i,
|
||||
data_offset,
|
||||
mf_ul_data->data_read);
|
||||
uint8_t* block = mf_ul_data->data + data_offset;
|
||||
if(!magic_gen4_write_blk(password, i, block)) {
|
||||
FURI_LOG_E(TAG, "Failed to write %zu page", i);
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t buffer[16] = {0};
|
||||
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
memcpy(buffer, &mf_ul_data->signature[i * 4], 4); //-V1086
|
||||
if(!magic_gen4_write_blk(password, 0xF2 + i, buffer)) {
|
||||
FURI_LOG_E(TAG, "Failed to write signature block %d", i);
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buffer[0] = mf_ul_data->version.header;
|
||||
buffer[1] = mf_ul_data->version.vendor_id;
|
||||
buffer[2] = mf_ul_data->version.prod_type;
|
||||
buffer[3] = mf_ul_data->version.prod_subtype;
|
||||
if(!magic_gen4_write_blk(password, 0xFA, buffer)) {
|
||||
FURI_LOG_E(TAG, "Failed to write version block 0");
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer[0] = mf_ul_data->version.prod_ver_major;
|
||||
buffer[1] = mf_ul_data->version.prod_ver_minor;
|
||||
buffer[2] = mf_ul_data->version.storage_size;
|
||||
buffer[3] = mf_ul_data->version.protocol_type;
|
||||
if(!magic_gen4_write_blk(password, 0xFB, buffer)) {
|
||||
FURI_LOG_E(TAG, "Failed to write version block 1");
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if(done) break;
|
||||
|
||||
if(card_found_notified) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = false;
|
||||
}
|
||||
}
|
||||
|
||||
furi_delay_ms(300);
|
||||
}
|
||||
magic_deactivate();
|
||||
}
|
||||
|
||||
void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) {
|
||||
NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
|
||||
bool card_found_notified = false;
|
||||
uint8_t gen4_config[MAGIC_GEN4_CONFIG_LEN];
|
||||
|
||||
while(nfc_magic_worker->state == NfcMagicWorkerStateCheck) {
|
||||
if(magic_wupa()) {
|
||||
magic_activate();
|
||||
if(magic_gen1_wupa()) {
|
||||
magic_dev->type = MagicTypeClassicGen1;
|
||||
if(!card_found_notified) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = true;
|
||||
}
|
||||
|
||||
furi_hal_nfc_activate_nfca(200, &magic_dev->cuid);
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
break;
|
||||
}
|
||||
|
||||
magic_deactivate();
|
||||
furi_delay_ms(300);
|
||||
magic_activate();
|
||||
|
||||
furi_hal_nfc_activate_nfca(200, &magic_dev->cuid);
|
||||
if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) {
|
||||
magic_dev->type = MagicTypeGen4;
|
||||
if(!card_found_notified) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
|
||||
@ -135,12 +330,56 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) {
|
||||
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
|
||||
if(card_found_notified) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = false;
|
||||
}
|
||||
|
||||
magic_deactivate();
|
||||
furi_delay_ms(300);
|
||||
}
|
||||
|
||||
magic_deactivate();
|
||||
}
|
||||
|
||||
void nfc_magic_worker_rekey(NfcMagicWorker* nfc_magic_worker) {
|
||||
NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
|
||||
bool card_found_notified = false;
|
||||
|
||||
if(magic_dev->type != MagicTypeGen4) {
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
|
||||
return;
|
||||
}
|
||||
|
||||
while(nfc_magic_worker->state == NfcMagicWorkerStateRekey) {
|
||||
magic_activate();
|
||||
uint32_t cuid;
|
||||
furi_hal_nfc_activate_nfca(200, &cuid);
|
||||
if(cuid != magic_dev->cuid) {
|
||||
if(card_found_notified) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = true;
|
||||
|
||||
if(magic_gen4_set_pwd(magic_dev->password, nfc_magic_worker->new_password)) {
|
||||
magic_dev->password = nfc_magic_worker->new_password;
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
break;
|
||||
}
|
||||
|
||||
if(card_found_notified) { //-V547
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = false;
|
||||
}
|
||||
furi_delay_ms(300);
|
||||
}
|
||||
@ -148,8 +387,17 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) {
|
||||
}
|
||||
|
||||
void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) {
|
||||
NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
|
||||
bool card_found_notified = false;
|
||||
bool card_wiped = false;
|
||||
|
||||
MfClassicBlock block;
|
||||
memset(&block, 0, sizeof(MfClassicBlock));
|
||||
MfClassicBlock empty_block;
|
||||
memset(&empty_block, 0, sizeof(MfClassicBlock));
|
||||
MfClassicBlock trailer_block;
|
||||
memset(&trailer_block, 0xff, sizeof(MfClassicBlock));
|
||||
|
||||
block.value[0] = 0x01;
|
||||
block.value[1] = 0x02;
|
||||
block.value[2] = 0x03;
|
||||
@ -158,15 +406,69 @@ void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) {
|
||||
block.value[5] = 0x08;
|
||||
block.value[6] = 0x04;
|
||||
|
||||
trailer_block.value[7] = 0x07;
|
||||
trailer_block.value[8] = 0x80;
|
||||
trailer_block.value[9] = 0x69;
|
||||
|
||||
while(nfc_magic_worker->state == NfcMagicWorkerStateWipe) {
|
||||
do {
|
||||
magic_deactivate();
|
||||
furi_delay_ms(300);
|
||||
if(!magic_wupa()) continue;
|
||||
if(!magic_wipe()) continue;
|
||||
if(!magic_data_access_cmd()) continue;
|
||||
if(!magic_write_blk(0, &block)) continue;
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
if(!magic_activate()) break;
|
||||
if(magic_dev->type == MagicTypeClassicGen1) {
|
||||
if(!magic_gen1_wupa()) break;
|
||||
if(!card_found_notified) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = true;
|
||||
}
|
||||
|
||||
if(!magic_gen1_data_access_cmd()) break;
|
||||
if(!magic_gen1_write_blk(0, &block)) break;
|
||||
|
||||
for(size_t i = 1; i < 64; i++) {
|
||||
FURI_LOG_D(TAG, "Wiping block %d", i);
|
||||
bool success = false;
|
||||
if((i | 0x03) == i) {
|
||||
success = magic_gen1_write_blk(i, &trailer_block);
|
||||
} else {
|
||||
success = magic_gen1_write_blk(i, &empty_block);
|
||||
}
|
||||
|
||||
if(!success) {
|
||||
FURI_LOG_E(TAG, "Failed to write %d block", i);
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventFail, nfc_magic_worker->context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
card_wiped = true;
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
} else if(magic_dev->type == MagicTypeGen4) {
|
||||
uint32_t cuid;
|
||||
if(!furi_hal_nfc_activate_nfca(200, &cuid)) break;
|
||||
if(cuid != magic_dev->cuid) break;
|
||||
if(!card_found_notified) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = true;
|
||||
}
|
||||
|
||||
if(!magic_gen4_wipe(magic_dev->password)) break;
|
||||
|
||||
card_wiped = true;
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if(card_wiped) break;
|
||||
|
||||
if(card_found_notified) {
|
||||
nfc_magic_worker->callback(
|
||||
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
|
||||
card_found_notified = false;
|
||||
}
|
||||
}
|
||||
magic_deactivate();
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/nfc_device.h>
|
||||
#include "nfc_magic.h"
|
||||
|
||||
typedef struct NfcMagicWorker NfcMagicWorker;
|
||||
|
||||
@ -9,6 +10,7 @@ typedef enum {
|
||||
|
||||
NfcMagicWorkerStateCheck,
|
||||
NfcMagicWorkerStateWrite,
|
||||
NfcMagicWorkerStateRekey,
|
||||
NfcMagicWorkerStateWipe,
|
||||
|
||||
NfcMagicWorkerStateStop,
|
||||
@ -33,6 +35,8 @@ void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker);
|
||||
void nfc_magic_worker_start(
|
||||
NfcMagicWorker* nfc_magic_worker,
|
||||
NfcMagicWorkerState state,
|
||||
NfcMagicDevice* magic_dev,
|
||||
NfcDeviceData* dev_data,
|
||||
uint32_t new_password,
|
||||
NfcMagicWorkerCallback callback,
|
||||
void* context);
|
||||
|
||||
@ -3,11 +3,14 @@
|
||||
#include <furi.h>
|
||||
|
||||
#include "nfc_magic_worker.h"
|
||||
#include "lib/magic/common.h"
|
||||
|
||||
struct NfcMagicWorker {
|
||||
FuriThread* thread;
|
||||
|
||||
NfcMagicDevice* magic_dev;
|
||||
NfcDeviceData* dev_data;
|
||||
uint32_t new_password;
|
||||
|
||||
NfcMagicWorkerCallback callback;
|
||||
void* context;
|
||||
@ -21,4 +24,6 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker);
|
||||
|
||||
void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker);
|
||||
|
||||
void nfc_magic_worker_rekey(NfcMagicWorker* nfc_magic_worker);
|
||||
|
||||
void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker);
|
||||
|
||||
50
applications/external/nfc_magic/scenes/nfc_magic_scene_actions.c
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
#include "../nfc_magic_i.h"
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexWrite,
|
||||
SubmenuIndexWipe,
|
||||
};
|
||||
|
||||
void nfc_magic_scene_actions_submenu_callback(void* context, uint32_t index) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void nfc_magic_scene_actions_on_enter(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
Submenu* submenu = nfc_magic->submenu;
|
||||
submenu_add_item(
|
||||
submenu, "Write", SubmenuIndexWrite, nfc_magic_scene_actions_submenu_callback, nfc_magic);
|
||||
submenu_add_item(
|
||||
submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_actions_submenu_callback, nfc_magic);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneActions));
|
||||
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu);
|
||||
}
|
||||
|
||||
bool nfc_magic_scene_actions_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexWrite) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexWipe) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(nfc_magic->scene_manager, NfcMagicSceneActions, event.event);
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc_magic->scene_manager, NfcMagicSceneStart);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_magic_scene_actions_on_exit(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
submenu_reset(nfc_magic->submenu);
|
||||
}
|
||||
@ -42,7 +42,9 @@ void nfc_magic_scene_check_on_enter(void* context) {
|
||||
nfc_magic_worker_start(
|
||||
nfc_magic->worker,
|
||||
NfcMagicWorkerStateCheck,
|
||||
&nfc_magic->nfc_dev->dev_data,
|
||||
nfc_magic->dev,
|
||||
&nfc_magic->source_dev->dev_data,
|
||||
nfc_magic->new_password,
|
||||
nfc_magic_check_worker_callback,
|
||||
nfc_magic);
|
||||
nfc_magic_blink_start(nfc_magic);
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
ADD_SCENE(nfc_magic, start, Start)
|
||||
ADD_SCENE(nfc_magic, key_input, KeyInput)
|
||||
ADD_SCENE(nfc_magic, actions, Actions)
|
||||
ADD_SCENE(nfc_magic, gen4_actions, Gen4Actions)
|
||||
ADD_SCENE(nfc_magic, new_key_input, NewKeyInput)
|
||||
ADD_SCENE(nfc_magic, file_select, FileSelect)
|
||||
ADD_SCENE(nfc_magic, write_confirm, WriteConfirm)
|
||||
ADD_SCENE(nfc_magic, wrong_card, WrongCard)
|
||||
@ -8,5 +12,7 @@ ADD_SCENE(nfc_magic, success, Success)
|
||||
ADD_SCENE(nfc_magic, check, Check)
|
||||
ADD_SCENE(nfc_magic, not_magic, NotMagic)
|
||||
ADD_SCENE(nfc_magic, magic_info, MagicInfo)
|
||||
ADD_SCENE(nfc_magic, rekey, Rekey)
|
||||
ADD_SCENE(nfc_magic, rekey_fail, RekeyFail)
|
||||
ADD_SCENE(nfc_magic, wipe, Wipe)
|
||||
ADD_SCENE(nfc_magic, wipe_fail, WipeFail)
|
||||
|
||||
@ -1,22 +1,60 @@
|
||||
#include "../nfc_magic_i.h"
|
||||
|
||||
static bool nfc_magic_scene_file_select_is_file_suitable(NfcDevice* nfc_dev) {
|
||||
return (nfc_dev->format == NfcDeviceSaveFormatMifareClassic) &&
|
||||
(nfc_dev->dev_data.mf_classic_data.type == MfClassicType1k) &&
|
||||
(nfc_dev->dev_data.nfc_data.uid_len == 4);
|
||||
static bool nfc_magic_scene_file_select_is_file_suitable(NfcMagic* nfc_magic) {
|
||||
NfcDevice* nfc_dev = nfc_magic->source_dev;
|
||||
if(nfc_dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
switch(nfc_magic->dev->type) {
|
||||
case MagicTypeClassicGen1:
|
||||
case MagicTypeClassicDirectWrite:
|
||||
case MagicTypeClassicAPDU:
|
||||
if((nfc_dev->dev_data.mf_classic_data.type != MfClassicType1k) ||
|
||||
(nfc_dev->dev_data.nfc_data.uid_len != 4)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case MagicTypeGen4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if(
|
||||
(nfc_dev->format == NfcDeviceSaveFormatMifareUl) &&
|
||||
(nfc_dev->dev_data.nfc_data.uid_len == 7)) {
|
||||
switch(nfc_magic->dev->type) {
|
||||
case MagicTypeUltralightGen1:
|
||||
case MagicTypeUltralightDirectWrite:
|
||||
case MagicTypeUltralightC_Gen1:
|
||||
case MagicTypeUltralightC_DirectWrite:
|
||||
case MagicTypeGen4:
|
||||
switch(nfc_dev->dev_data.mf_ul_data.type) {
|
||||
case MfUltralightTypeNTAGI2C1K:
|
||||
case MfUltralightTypeNTAGI2C2K:
|
||||
case MfUltralightTypeNTAGI2CPlus1K:
|
||||
case MfUltralightTypeNTAGI2CPlus2K:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void nfc_magic_scene_file_select_on_enter(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
// Process file_select return
|
||||
nfc_device_set_loading_callback(nfc_magic->nfc_dev, nfc_magic_show_loading_popup, nfc_magic);
|
||||
nfc_device_set_loading_callback(
|
||||
nfc_magic->source_dev, nfc_magic_show_loading_popup, nfc_magic);
|
||||
|
||||
if(!furi_string_size(nfc_magic->nfc_dev->load_path)) {
|
||||
furi_string_set_str(nfc_magic->nfc_dev->load_path, NFC_APP_FOLDER);
|
||||
if(!furi_string_size(nfc_magic->source_dev->load_path)) {
|
||||
furi_string_set_str(nfc_magic->source_dev->load_path, NFC_APP_FOLDER);
|
||||
}
|
||||
|
||||
if(nfc_file_select(nfc_magic->nfc_dev)) {
|
||||
if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic->nfc_dev)) {
|
||||
if(nfc_file_select(nfc_magic->source_dev)) {
|
||||
if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic)) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteConfirm);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrongCard);
|
||||
@ -34,5 +72,5 @@ bool nfc_magic_scene_file_select_on_event(void* context, SceneManagerEvent event
|
||||
|
||||
void nfc_magic_scene_file_select_on_exit(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
nfc_device_set_loading_callback(nfc_magic->nfc_dev, NULL, nfc_magic);
|
||||
nfc_device_set_loading_callback(nfc_magic->source_dev, NULL, nfc_magic);
|
||||
}
|
||||
|
||||
70
applications/external/nfc_magic/scenes/nfc_magic_scene_gen4_actions.c
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
#include "../nfc_magic_i.h"
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexWrite,
|
||||
SubmenuIndexChangePassword,
|
||||
SubmenuIndexWipe,
|
||||
};
|
||||
|
||||
void nfc_magic_scene_gen4_actions_submenu_callback(void* context, uint32_t index) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void nfc_magic_scene_gen4_actions_on_enter(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
Submenu* submenu = nfc_magic->submenu;
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write",
|
||||
SubmenuIndexWrite,
|
||||
nfc_magic_scene_gen4_actions_submenu_callback,
|
||||
nfc_magic);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Change password",
|
||||
SubmenuIndexChangePassword,
|
||||
nfc_magic_scene_gen4_actions_submenu_callback,
|
||||
nfc_magic);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Wipe",
|
||||
SubmenuIndexWipe,
|
||||
nfc_magic_scene_gen4_actions_submenu_callback,
|
||||
nfc_magic);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu,
|
||||
scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneGen4Actions));
|
||||
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu);
|
||||
}
|
||||
|
||||
bool nfc_magic_scene_gen4_actions_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexWrite) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexChangePassword) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNewKeyInput);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexWipe) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(
|
||||
nfc_magic->scene_manager, NfcMagicSceneGen4Actions, event.event);
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc_magic->scene_manager, NfcMagicSceneStart);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_magic_scene_gen4_actions_on_exit(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
submenu_reset(nfc_magic->submenu);
|
||||
}
|
||||
45
applications/external/nfc_magic/scenes/nfc_magic_scene_key_input.c
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
#include "../nfc_magic_i.h"
|
||||
|
||||
void nfc_magic_scene_key_input_byte_input_callback(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
nfc_magic->view_dispatcher, NfcMagicCustomEventByteInputDone);
|
||||
}
|
||||
|
||||
void nfc_magic_scene_key_input_on_enter(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
// Setup view
|
||||
ByteInput* byte_input = nfc_magic->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter the password in hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
nfc_magic_scene_key_input_byte_input_callback,
|
||||
NULL,
|
||||
nfc_magic,
|
||||
(uint8_t*)&nfc_magic->dev->password,
|
||||
4);
|
||||
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput);
|
||||
}
|
||||
|
||||
bool nfc_magic_scene_key_input_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcMagicCustomEventByteInputDone) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_magic_scene_key_input_on_exit(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
// Clear view
|
||||
byte_input_set_result_callback(nfc_magic->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(nfc_magic->byte_input, "");
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
#include "../nfc_magic_i.h"
|
||||
#include "../lib/magic/types.h"
|
||||
|
||||
void nfc_magic_scene_magic_info_widget_callback(
|
||||
GuiButtonType result,
|
||||
@ -13,14 +14,18 @@ void nfc_magic_scene_magic_info_widget_callback(
|
||||
void nfc_magic_scene_magic_info_on_enter(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
Widget* widget = nfc_magic->widget;
|
||||
const char* card_type = nfc_magic_type(nfc_magic->dev->type);
|
||||
|
||||
notification_message(nfc_magic->notifications, &sequence_success);
|
||||
|
||||
widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
|
||||
widget_add_string_element(
|
||||
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Magic card detected");
|
||||
widget_add_string_element(widget, 3, 17, AlignLeft, AlignTop, FontSecondary, card_type);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_magic_info_widget_callback, nfc_magic);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "More", nfc_magic_scene_magic_info_widget_callback, nfc_magic);
|
||||
|
||||
// Setup and start worker
|
||||
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget);
|
||||
@ -33,6 +38,15 @@ bool nfc_magic_scene_magic_info_on_event(void* context, SceneManagerEvent event)
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
consumed = scene_manager_previous_scene(nfc_magic->scene_manager);
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
MagicType type = nfc_magic->dev->type;
|
||||
if(type == MagicTypeGen4) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneGen4Actions);
|
||||
consumed = true;
|
||||
} else {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneActions);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
||||
45
applications/external/nfc_magic/scenes/nfc_magic_scene_new_key_input.c
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
#include "../nfc_magic_i.h"
|
||||
|
||||
void nfc_magic_scene_new_key_input_byte_input_callback(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
nfc_magic->view_dispatcher, NfcMagicCustomEventByteInputDone);
|
||||
}
|
||||
|
||||
void nfc_magic_scene_new_key_input_on_enter(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
// Setup view
|
||||
ByteInput* byte_input = nfc_magic->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter the password in hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
nfc_magic_scene_new_key_input_byte_input_callback,
|
||||
NULL,
|
||||
nfc_magic,
|
||||
(uint8_t*)&nfc_magic->new_password,
|
||||
4);
|
||||
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput);
|
||||
}
|
||||
|
||||
bool nfc_magic_scene_new_key_input_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcMagicCustomEventByteInputDone) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneRekey);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_magic_scene_new_key_input_on_exit(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
// Clear view
|
||||
byte_input_set_result_callback(nfc_magic->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(nfc_magic->byte_input, "");
|
||||
}
|
||||
95
applications/external/nfc_magic/scenes/nfc_magic_scene_rekey.c
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
#include "../nfc_magic_i.h"
|
||||
|
||||
enum {
|
||||
NfcMagicSceneRekeyStateCardSearch,
|
||||
NfcMagicSceneRekeyStateCardFound,
|
||||
};
|
||||
|
||||
bool nfc_magic_rekey_worker_callback(NfcMagicWorkerEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
NfcMagic* nfc_magic = context;
|
||||
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nfc_magic_scene_rekey_setup_view(NfcMagic* nfc_magic) {
|
||||
Popup* popup = nfc_magic->popup;
|
||||
popup_reset(popup);
|
||||
uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneRekey);
|
||||
|
||||
if(state == NfcMagicSceneRekeyStateCardSearch) {
|
||||
popup_set_text(
|
||||
nfc_magic->popup,
|
||||
"Apply the\nsame card\nto the back",
|
||||
128,
|
||||
32,
|
||||
AlignRight,
|
||||
AlignCenter);
|
||||
popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
} else {
|
||||
popup_set_icon(popup, 12, 23, &I_Loading_24);
|
||||
popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup);
|
||||
}
|
||||
|
||||
void nfc_magic_scene_rekey_on_enter(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
scene_manager_set_scene_state(
|
||||
nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch);
|
||||
nfc_magic_scene_rekey_setup_view(nfc_magic);
|
||||
|
||||
// Setup and start worker
|
||||
nfc_magic_worker_start(
|
||||
nfc_magic->worker,
|
||||
NfcMagicWorkerStateRekey,
|
||||
nfc_magic->dev,
|
||||
&nfc_magic->source_dev->dev_data,
|
||||
nfc_magic->new_password,
|
||||
nfc_magic_rekey_worker_callback,
|
||||
nfc_magic);
|
||||
nfc_magic_blink_start(nfc_magic);
|
||||
}
|
||||
|
||||
bool nfc_magic_scene_rekey_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcMagicWorkerEventSuccess) {
|
||||
nfc_magic->dev->password = nfc_magic->new_password;
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcMagicWorkerEventFail) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneRekeyFail);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcMagicWorkerEventCardDetected) {
|
||||
scene_manager_set_scene_state(
|
||||
nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardFound);
|
||||
nfc_magic_scene_rekey_setup_view(nfc_magic);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcMagicWorkerEventNoCardDetected) {
|
||||
scene_manager_set_scene_state(
|
||||
nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch);
|
||||
nfc_magic_scene_rekey_setup_view(nfc_magic);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_magic_scene_rekey_on_exit(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
nfc_magic_worker_stop(nfc_magic->worker);
|
||||
scene_manager_set_scene_state(
|
||||
nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch);
|
||||
// Clear view
|
||||
popup_reset(nfc_magic->popup);
|
||||
|
||||
nfc_magic_blink_stop(nfc_magic);
|
||||
}
|
||||
50
applications/external/nfc_magic/scenes/nfc_magic_scene_rekey_fail.c
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
#include "../nfc_magic_i.h"
|
||||
|
||||
void nfc_magic_scene_rekey_fail_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_magic_scene_rekey_fail_on_enter(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
Widget* widget = nfc_magic->widget;
|
||||
|
||||
notification_message(nfc_magic->notifications, &sequence_error);
|
||||
|
||||
widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48);
|
||||
widget_add_string_element(
|
||||
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Can't change password!");
|
||||
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Finish", nfc_magic_scene_rekey_fail_widget_callback, nfc_magic);
|
||||
|
||||
// Setup and start worker
|
||||
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget);
|
||||
}
|
||||
|
||||
bool nfc_magic_scene_rekey_fail_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc_magic->scene_manager, NfcMagicSceneStart);
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc_magic->scene_manager, NfcMagicSceneStart);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_magic_scene_rekey_fail_on_exit(void* context) {
|
||||
NfcMagic* nfc_magic = context;
|
||||
|
||||
widget_reset(nfc_magic->widget);
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
#include "../nfc_magic_i.h"
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexCheck,
|
||||
SubmenuIndexWriteGen1A,
|
||||
SubmenuIndexWipe,
|
||||
SubmenuIndexAuthenticateGen4,
|
||||
};
|
||||
|
||||
void nfc_magic_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
@ -22,12 +21,10 @@ void nfc_magic_scene_start_on_enter(void* context) {
|
||||
nfc_magic);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write Gen1A",
|
||||
SubmenuIndexWriteGen1A,
|
||||
"Authenticate Gen4",
|
||||
SubmenuIndexAuthenticateGen4,
|
||||
nfc_magic_scene_start_submenu_callback,
|
||||
nfc_magic);
|
||||
submenu_add_item(
|
||||
submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_start_submenu_callback, nfc_magic);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart));
|
||||
@ -40,23 +37,13 @@ bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexCheck) {
|
||||
nfc_magic->dev->password = MAGIC_GEN4_DEFAULT_PWD;
|
||||
scene_manager_set_scene_state(
|
||||
nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexCheck);
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexWriteGen1A) {
|
||||
// Explicitly save state in each branch so that the
|
||||
// correct option is reselected if the user cancels
|
||||
// loading a file.
|
||||
scene_manager_set_scene_state(
|
||||
nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWriteGen1A);
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexWipe) {
|
||||
scene_manager_set_scene_state(
|
||||
nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWipe);
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexAuthenticateGen4) {
|
||||
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneKeyInput);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,12 @@ static void nfc_magic_scene_wipe_setup_view(NfcMagic* nfc_magic) {
|
||||
if(state == NfcMagicSceneWipeStateCardSearch) {
|
||||
popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
popup_set_text(
|
||||
nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter);
|
||||
nfc_magic->popup,
|
||||
"Apply the\nsame card\nto the back",
|
||||
128,
|
||||
32,
|
||||
AlignRight,
|
||||
AlignCenter);
|
||||
} else {
|
||||
popup_set_icon(popup, 12, 23, &I_Loading_24);
|
||||
popup_set_header(popup, "Wiping\nDon't move...", 52, 32, AlignLeft, AlignCenter);
|
||||
@ -42,7 +47,9 @@ void nfc_magic_scene_wipe_on_enter(void* context) {
|
||||
nfc_magic_worker_start(
|
||||
nfc_magic->worker,
|
||||
NfcMagicWorkerStateWipe,
|
||||
&nfc_magic->nfc_dev->dev_data,
|
||||
nfc_magic->dev,
|
||||
&nfc_magic->source_dev->dev_data,
|
||||
nfc_magic->new_password,
|
||||
nfc_magic_wipe_worker_callback,
|
||||
nfc_magic);
|
||||
nfc_magic_blink_start(nfc_magic);
|
||||
|
||||
@ -21,7 +21,12 @@ static void nfc_magic_scene_write_setup_view(NfcMagic* nfc_magic) {
|
||||
|
||||
if(state == NfcMagicSceneWriteStateCardSearch) {
|
||||
popup_set_text(
|
||||
nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter);
|
||||
nfc_magic->popup,
|
||||
"Apply the\nsame card\nto the back",
|
||||
128,
|
||||
32,
|
||||
AlignRight,
|
||||
AlignCenter);
|
||||
popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
} else {
|
||||
popup_set_icon(popup, 12, 23, &I_Loading_24);
|
||||
@ -42,7 +47,9 @@ void nfc_magic_scene_write_on_enter(void* context) {
|
||||
nfc_magic_worker_start(
|
||||
nfc_magic->worker,
|
||||
NfcMagicWorkerStateWrite,
|
||||
&nfc_magic->nfc_dev->dev_data,
|
||||
nfc_magic->dev,
|
||||
&nfc_magic->source_dev->dev_data,
|
||||
nfc_magic->new_password,
|
||||
nfc_magic_write_worker_callback,
|
||||
nfc_magic);
|
||||
nfc_magic_blink_start(nfc_magic);
|
||||
|
||||
@ -26,7 +26,7 @@ void nfc_magic_scene_wrong_card_on_enter(void* context) {
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Writing is supported\nonly for 4 bytes UID\nMifare Classic 1k");
|
||||
"Writing this file is\nnot supported for\nthis magic card.");
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic);
|
||||
|
||||
|
||||
@ -16,10 +16,11 @@
|
||||
(((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
|
||||
|
||||
typedef enum {
|
||||
WorkerEvtToggle = (1 << 0),
|
||||
WorkerEvtEnd = (1 << 1),
|
||||
WorkerEvtConnect = (1 << 2),
|
||||
WorkerEvtDisconnect = (1 << 3),
|
||||
WorkerEvtStartStop = (1 << 0),
|
||||
WorkerEvtPauseResume = (1 << 1),
|
||||
WorkerEvtEnd = (1 << 2),
|
||||
WorkerEvtConnect = (1 << 3),
|
||||
WorkerEvtDisconnect = (1 << 4),
|
||||
} WorkerEvtFlags;
|
||||
|
||||
static const char ducky_cmd_id[] = {"ID"};
|
||||
@ -372,6 +373,7 @@ static int32_t bad_usb_worker(void* context) {
|
||||
BadUsbScript* bad_usb = context;
|
||||
|
||||
BadUsbWorkerState worker_state = BadUsbStateInit;
|
||||
BadUsbWorkerState pause_state = BadUsbStateRunning;
|
||||
int32_t delay_val = 0;
|
||||
|
||||
FURI_LOG_I(WORKER_TAG, "Init");
|
||||
@ -406,24 +408,24 @@ static int32_t bad_usb_worker(void* context) {
|
||||
|
||||
} else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
|
||||
uint32_t flags = bad_usb_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtConnect) {
|
||||
worker_state = BadUsbStateIdle; // Ready to run
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadUsbStateWillRun; // Will run when USB is connected
|
||||
}
|
||||
bad_usb->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadUsbStateIdle) { // State: ready to start
|
||||
uint32_t flags = bad_usb_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever);
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtToggle) { // Start executing script
|
||||
} else if(flags & WorkerEvtStartStop) { // Start executing script
|
||||
DOLPHIN_DEED(DolphinDeedBadUsbPlayScript);
|
||||
delay_val = 0;
|
||||
bad_usb->buf_len = 0;
|
||||
@ -442,7 +444,7 @@ static int32_t bad_usb_worker(void* context) {
|
||||
|
||||
} else if(worker_state == BadUsbStateWillRun) { // State: start on connection
|
||||
uint32_t flags = bad_usb_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
@ -458,17 +460,17 @@ static int32_t bad_usb_worker(void* context) {
|
||||
storage_file_seek(script_file, 0, true);
|
||||
// extra time for PC to recognize Flipper as keyboard
|
||||
flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle,
|
||||
WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop,
|
||||
FuriFlagWaitAny | FuriFlagNoClear,
|
||||
1500);
|
||||
if(flags == (unsigned)FuriFlagErrorTimeout) {
|
||||
// If nothing happened - start script execution
|
||||
worker_state = BadUsbStateRunning;
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadUsbStateIdle;
|
||||
furi_thread_flags_clear(WorkerEvtToggle);
|
||||
furi_thread_flags_clear(WorkerEvtStartStop);
|
||||
}
|
||||
} else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
|
||||
} else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution
|
||||
worker_state = BadUsbStateNotConnected;
|
||||
}
|
||||
bad_usb->st.state = worker_state;
|
||||
@ -476,18 +478,23 @@ static int32_t bad_usb_worker(void* context) {
|
||||
} else if(worker_state == BadUsbStateRunning) { // State: running
|
||||
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur);
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||
FuriFlagWaitAny,
|
||||
delay_cur);
|
||||
|
||||
delay_val -= delay_cur;
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadUsbStateIdle; // Stop executing script
|
||||
furi_hal_hid_kb_release_all();
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadUsbStateNotConnected; // USB disconnected
|
||||
furi_hal_hid_kb_release_all();
|
||||
} else if(flags & WorkerEvtPauseResume) {
|
||||
pause_state = BadUsbStateRunning;
|
||||
worker_state = BadUsbStatePaused; // Pause
|
||||
}
|
||||
bad_usb->st.state = worker_state;
|
||||
continue;
|
||||
@ -526,13 +533,13 @@ static int32_t bad_usb_worker(void* context) {
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
}
|
||||
} else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press
|
||||
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur);
|
||||
uint32_t flags = bad_usb_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||
FuriWaitForever);
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
delay_val = 0;
|
||||
worker_state = BadUsbStateRunning;
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
@ -542,21 +549,55 @@ static int32_t bad_usb_worker(void* context) {
|
||||
bad_usb->st.state = worker_state;
|
||||
continue;
|
||||
}
|
||||
} else if(worker_state == BadUsbStatePaused) { // State: Paused
|
||||
uint32_t flags = bad_usb_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||
FuriWaitForever);
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadUsbStateIdle; // Stop executing script
|
||||
bad_usb->st.state = worker_state;
|
||||
furi_hal_hid_kb_release_all();
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadUsbStateNotConnected; // USB disconnected
|
||||
bad_usb->st.state = worker_state;
|
||||
furi_hal_hid_kb_release_all();
|
||||
} else if(flags & WorkerEvtPauseResume) {
|
||||
if(pause_state == BadUsbStateRunning) {
|
||||
if(delay_val > 0) {
|
||||
bad_usb->st.state = BadUsbStateDelay;
|
||||
bad_usb->st.delay_remain = delay_val / 1000;
|
||||
} else {
|
||||
bad_usb->st.state = BadUsbStateRunning;
|
||||
delay_val = 0;
|
||||
}
|
||||
worker_state = BadUsbStateRunning; // Resume
|
||||
} else if(pause_state == BadUsbStateStringDelay) {
|
||||
bad_usb->st.state = BadUsbStateRunning;
|
||||
worker_state = BadUsbStateStringDelay; // Resume
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if(worker_state == BadUsbStateStringDelay) { // State: print string with delays
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
|
||||
FuriFlagWaitAny,
|
||||
uint32_t flags = bad_usb_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||
bad_usb->stringdelay);
|
||||
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtToggle) {
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadUsbStateIdle; // Stop executing script
|
||||
furi_hal_hid_kb_release_all();
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadUsbStateNotConnected; // USB disconnected
|
||||
furi_hal_hid_kb_release_all();
|
||||
} else if(flags & WorkerEvtPauseResume) {
|
||||
pause_state = BadUsbStateStringDelay;
|
||||
worker_state = BadUsbStatePaused; // Pause
|
||||
}
|
||||
bad_usb->st.state = worker_state;
|
||||
continue;
|
||||
@ -651,9 +692,14 @@ void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layou
|
||||
storage_file_free(layout_file);
|
||||
}
|
||||
|
||||
void bad_usb_script_toggle(BadUsbScript* bad_usb) {
|
||||
void bad_usb_script_start_stop(BadUsbScript* bad_usb) {
|
||||
furi_assert(bad_usb);
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle);
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtStartStop);
|
||||
}
|
||||
|
||||
void bad_usb_script_pause_resume(BadUsbScript* bad_usb) {
|
||||
furi_assert(bad_usb);
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtPauseResume);
|
||||
}
|
||||
|
||||
BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) {
|
||||
|
||||
@ -16,6 +16,7 @@ typedef enum {
|
||||
BadUsbStateDelay,
|
||||
BadUsbStateStringDelay,
|
||||
BadUsbStateWaitForBtn,
|
||||
BadUsbStatePaused,
|
||||
BadUsbStateDone,
|
||||
BadUsbStateScriptError,
|
||||
BadUsbStateFileError,
|
||||
@ -42,7 +43,9 @@ void bad_usb_script_start(BadUsbScript* bad_usb);
|
||||
|
||||
void bad_usb_script_stop(BadUsbScript* bad_usb);
|
||||
|
||||
void bad_usb_script_toggle(BadUsbScript* bad_usb);
|
||||
void bad_usb_script_start_stop(BadUsbScript* bad_usb);
|
||||
|
||||
void bad_usb_script_pause_resume(BadUsbScript* bad_usb);
|
||||
|
||||
BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb);
|
||||
|
||||
|
||||
@ -21,7 +21,10 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == InputKeyOk) {
|
||||
bad_usb_script_toggle(app->bad_usb_script);
|
||||
bad_usb_script_start_stop(app->bad_usb_script);
|
||||
consumed = true;
|
||||
} else if(event.event == InputKeyRight) {
|
||||
bad_usb_script_pause_resume(app->bad_usb_script);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
|
||||
@ -16,6 +16,7 @@ typedef struct {
|
||||
char file_name[MAX_NAME_LEN];
|
||||
char layout[MAX_NAME_LEN];
|
||||
BadUsbState state;
|
||||
bool pause_wait;
|
||||
uint8_t anim_frame;
|
||||
} BadUsbModel;
|
||||
|
||||
@ -31,11 +32,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
if(strlen(model->layout) == 0) {
|
||||
furi_string_set(disp_str, "(default)");
|
||||
} else {
|
||||
furi_string_reset(disp_str);
|
||||
furi_string_push_back(disp_str, '(');
|
||||
for(size_t i = 0; i < strlen(model->layout); i++)
|
||||
furi_string_push_back(disp_str, model->layout[i]);
|
||||
furi_string_push_back(disp_str, ')');
|
||||
furi_string_printf(disp_str, "(%s)", model->layout);
|
||||
}
|
||||
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
||||
canvas_draw_str(
|
||||
@ -45,34 +42,42 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
|
||||
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
|
||||
|
||||
if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) ||
|
||||
(model->state.state == BadUsbStateNotConnected)) {
|
||||
BadUsbWorkerState state = model->state.state;
|
||||
|
||||
if((state == BadUsbStateIdle) || (state == BadUsbStateDone) ||
|
||||
(state == BadUsbStateNotConnected)) {
|
||||
elements_button_center(canvas, "Run");
|
||||
elements_button_left(canvas, "Config");
|
||||
} else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) {
|
||||
} else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) {
|
||||
elements_button_center(canvas, "Stop");
|
||||
} else if(model->state.state == BadUsbStateWaitForBtn) {
|
||||
if(!model->pause_wait) {
|
||||
elements_button_right(canvas, "Pause");
|
||||
}
|
||||
} else if(state == BadUsbStatePaused) {
|
||||
elements_button_center(canvas, "End");
|
||||
elements_button_right(canvas, "Resume");
|
||||
} else if(state == BadUsbStateWaitForBtn) {
|
||||
elements_button_center(canvas, "Press to continue");
|
||||
} else if(model->state.state == BadUsbStateWillRun) {
|
||||
} else if(state == BadUsbStateWillRun) {
|
||||
elements_button_center(canvas, "Cancel");
|
||||
}
|
||||
|
||||
if(model->state.state == BadUsbStateNotConnected) {
|
||||
if(state == BadUsbStateNotConnected) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB");
|
||||
} else if(model->state.state == BadUsbStateWillRun) {
|
||||
} else if(state == BadUsbStateWillRun) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
|
||||
} else if(model->state.state == BadUsbStateFileError) {
|
||||
} else if(state == BadUsbStateFileError) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR");
|
||||
} else if(model->state.state == BadUsbStateScriptError) {
|
||||
} else if(state == BadUsbStateScriptError) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
|
||||
@ -87,12 +92,12 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
} else if(model->state.state == BadUsbStateIdle) {
|
||||
} else if(state == BadUsbStateIdle) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0");
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
} else if(model->state.state == BadUsbStateRunning) {
|
||||
} else if(state == BadUsbStateRunning) {
|
||||
if(model->anim_frame == 0) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
||||
} else {
|
||||
@ -105,13 +110,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
} else if(model->state.state == BadUsbStateDone) {
|
||||
} else if(state == BadUsbStateDone) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100");
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
} else if(model->state.state == BadUsbStateDelay) {
|
||||
} else if(state == BadUsbStateDelay) {
|
||||
if(model->anim_frame == 0) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
|
||||
} else {
|
||||
@ -129,6 +134,22 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
} else if((state == BadUsbStatePaused) || (state == BadUsbStateWaitForBtn)) {
|
||||
if(model->anim_frame == 0) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
|
||||
}
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
furi_string_printf(
|
||||
disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 127, 50, AlignRight, AlignBottom, "Paused");
|
||||
furi_string_reset(disp_str);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
}
|
||||
@ -142,7 +163,27 @@ static bool bad_usb_input_callback(InputEvent* event, void* context) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
consumed = true;
|
||||
furi_assert(bad_usb->callback);
|
||||
bad_usb->callback(event->key, bad_usb->context);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
with_view_model(
|
||||
bad_usb->view, BadUsbModel * model, { model->pause_wait = false; }, true);
|
||||
consumed = true;
|
||||
furi_assert(bad_usb->callback);
|
||||
bad_usb->callback(event->key, bad_usb->context);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
with_view_model(
|
||||
bad_usb->view,
|
||||
BadUsbModel * model,
|
||||
{
|
||||
if((model->state.state == BadUsbStateRunning) ||
|
||||
(model->state.state == BadUsbStateDelay)) {
|
||||
model->pause_wait = true;
|
||||
}
|
||||
},
|
||||
true);
|
||||
consumed = true;
|
||||
furi_assert(bad_usb->callback);
|
||||
bad_usb->callback(event->key, bad_usb->context);
|
||||
@ -215,6 +256,9 @@ void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) {
|
||||
{
|
||||
memcpy(&(model->state), st, sizeof(BadUsbState));
|
||||
model->anim_frame ^= 1;
|
||||
if(model->state.state == BadUsbStatePaused) {
|
||||
model->pause_wait = false;
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
@ -312,7 +312,8 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_worker_set_raw_signal(infrared->worker, raw->timings, raw->timings_size);
|
||||
infrared_worker_set_raw_signal(
|
||||
infrared->worker, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
|
||||
} else {
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_worker_set_decoded_signal(infrared->worker, message);
|
||||
|
||||
@ -52,6 +52,7 @@ void nfc_scene_device_info_on_enter(void* context) {
|
||||
}
|
||||
} else if(
|
||||
dev_data->protocol == NfcDeviceProtocolMifareClassic ||
|
||||
dev_data->protocol == NfcDeviceProtocolMifareDesfire ||
|
||||
dev_data->protocol == NfcDeviceProtocolMifareUl) {
|
||||
furi_string_set(temp_str, nfc->dev->dev_data.parsed_data);
|
||||
}
|
||||
|
||||
@ -20,7 +20,11 @@ void nfc_scene_mf_desfire_read_success_on_enter(void* context) {
|
||||
Widget* widget = nfc->widget;
|
||||
|
||||
// Prepare string for data display
|
||||
FuriString* temp_str = furi_string_alloc_printf("\e#MIFARE DESfire\n");
|
||||
FuriString* temp_str = NULL;
|
||||
if(furi_string_size(nfc->dev->dev_data.parsed_data)) {
|
||||
temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data);
|
||||
} else {
|
||||
temp_str = furi_string_alloc_printf("\e#MIFARE DESFire\n");
|
||||
furi_string_cat_printf(temp_str, "UID:");
|
||||
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||
@ -50,6 +54,7 @@ void nfc_scene_mf_desfire_read_success_on_enter(void* context) {
|
||||
if(n_files != 1) {
|
||||
furi_string_push_back(temp_str, 's');
|
||||
}
|
||||
}
|
||||
|
||||
notification_message_block(nfc->notifications, &sequence_set_green_255);
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) {
|
||||
Submenu* submenu = nfc->submenu;
|
||||
MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data;
|
||||
|
||||
if(!mf_ul_is_full_capture(data)) {
|
||||
if(!mf_ul_is_full_capture(data) && data->type != MfUltralightTypeULC) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Unlock",
|
||||
@ -29,12 +29,14 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) {
|
||||
}
|
||||
submenu_add_item(
|
||||
submenu, "Save", SubmenuIndexSave, nfc_scene_mf_ultralight_menu_submenu_callback, nfc);
|
||||
if(mf_ul_emulation_supported(data)) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Emulate",
|
||||
SubmenuIndexEmulate,
|
||||
nfc_scene_mf_ultralight_menu_submenu_callback,
|
||||
nfc);
|
||||
}
|
||||
submenu_add_item(
|
||||
submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_ultralight_menu_submenu_callback, nfc);
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type));
|
||||
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
|
||||
furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n");
|
||||
furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n");
|
||||
} else {
|
||||
furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n");
|
||||
}
|
||||
|
||||
@ -42,7 +42,8 @@ void nfc_scene_saved_menu_on_enter(void* context) {
|
||||
nfc);
|
||||
}
|
||||
} else if(
|
||||
nfc->dev->format == NfcDeviceSaveFormatMifareUl ||
|
||||
(nfc->dev->format == NfcDeviceSaveFormatMifareUl &&
|
||||
mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) ||
|
||||
nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
submenu_add_item(
|
||||
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
|
||||
@ -72,6 +73,7 @@ void nfc_scene_saved_menu_on_enter(void* context) {
|
||||
submenu_add_item(
|
||||
submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
|
||||
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl &&
|
||||
nfc->dev->dev_data.mf_ul_data.type != MfUltralightTypeULC &&
|
||||
!mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
@ -146,6 +148,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
application_info_present = true;
|
||||
} else if(
|
||||
dev_data->protocol == NfcDeviceProtocolMifareClassic ||
|
||||
dev_data->protocol == NfcDeviceProtocolMifareDesfire ||
|
||||
dev_data->protocol == NfcDeviceProtocolMifareUl) {
|
||||
application_info_present = nfc_supported_card_verify_and_parse(dev_data);
|
||||
}
|
||||
|
||||
@ -10,4 +10,5 @@ typedef enum {
|
||||
1, /** File parsing error, or wrong file structure, or missing required parameters. more accurate data can be obtained through the debug port */
|
||||
SubGhzErrorTypeOnlyRX =
|
||||
2, /** Transmission on this frequency is blocked by regional settings */
|
||||
SubGhzErrorTypeParserOthers = 3, /** Error in protocol parameters description */
|
||||
} SubGhzErrorType;
|
||||
|
||||
@ -40,15 +40,26 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(event.event == SubGhzCustomEventSceneRpcButtonPress) {
|
||||
bool result = false;
|
||||
if((state == SubGhzRpcStateLoaded)) {
|
||||
result = subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
|
||||
state = SubGhzRpcStateTx;
|
||||
if(result) subghz_blink_start(subghz);
|
||||
}
|
||||
if(!result) {
|
||||
switch(
|
||||
subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx))) {
|
||||
case SubGhzTxRxStartTxStateErrorOnlyRx:
|
||||
rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeOnlyRX);
|
||||
rpc_system_app_set_error_text(
|
||||
subghz->rpc_ctx,
|
||||
"Transmission on this frequency is restricted in your region");
|
||||
break;
|
||||
case SubGhzTxRxStartTxStateErrorParserOthers:
|
||||
rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeParserOthers);
|
||||
rpc_system_app_set_error_text(
|
||||
subghz->rpc_ctx, "Error in protocol parameters description");
|
||||
break;
|
||||
|
||||
default: //if(SubGhzTxRxStartTxStateOk)
|
||||
result = true;
|
||||
subghz_blink_start(subghz);
|
||||
state = SubGhzRpcStateTx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result);
|
||||
} else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) {
|
||||
@ -56,9 +67,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
if(state == SubGhzRpcStateTx) {
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
subghz_blink_stop(subghz);
|
||||
state = SubGhzRpcStateIdle;
|
||||
result = true;
|
||||
}
|
||||
state = SubGhzRpcStateIdle;
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result);
|
||||
} else if(event.event == SubGhzCustomEventSceneRpcLoad) {
|
||||
bool result = false;
|
||||
@ -95,7 +106,7 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
void subghz_scene_rpc_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc);
|
||||
if(state != SubGhzRpcStateIdle) {
|
||||
if(state == SubGhzRpcStateTx) {
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
subghz_blink_stop(subghz);
|
||||
}
|
||||
|
||||
@ -176,16 +176,19 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter);
|
||||
|
||||
if(furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter)) {
|
||||
while(!(furi_hal_subghz_is_async_tx_complete() || cli_cmd_interrupt_received(cli))) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
furi_delay_ms(333);
|
||||
}
|
||||
furi_hal_subghz_stop_async_tx();
|
||||
furi_hal_subghz_sleep();
|
||||
|
||||
} else {
|
||||
printf("Transmission on this frequency is restricted in your region\r\n");
|
||||
}
|
||||
|
||||
furi_hal_subghz_sleep();
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
|
||||
flipper_format_free(flipper_format);
|
||||
|
||||
@ -147,6 +147,9 @@ void desktop_lock(Desktop* desktop) {
|
||||
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
||||
notification_message(desktop->notification, &sequence_display_backlight_off_delay_1000);
|
||||
|
||||
DesktopStatus status = {.locked = true};
|
||||
furi_pubsub_publish(desktop->status_pubsub, &status);
|
||||
}
|
||||
|
||||
void desktop_unlock(Desktop* desktop) {
|
||||
@ -165,6 +168,9 @@ void desktop_unlock(Desktop* desktop) {
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
|
||||
DesktopStatus status = {.locked = false};
|
||||
furi_pubsub_publish(desktop->status_pubsub, &status);
|
||||
}
|
||||
|
||||
void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) {
|
||||
@ -308,63 +314,13 @@ Desktop* desktop_alloc() {
|
||||
desktop->auto_lock_timer =
|
||||
furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop);
|
||||
|
||||
desktop->status_pubsub = furi_pubsub_alloc();
|
||||
|
||||
furi_record_create(RECORD_DESKTOP, desktop);
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
void desktop_free(Desktop* desktop) {
|
||||
furi_assert(desktop);
|
||||
furi_check(furi_record_destroy(RECORD_DESKTOP));
|
||||
|
||||
furi_pubsub_unsubscribe(
|
||||
loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription);
|
||||
|
||||
if(desktop->input_events_subscription) {
|
||||
furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription);
|
||||
desktop->input_events_subscription = NULL;
|
||||
}
|
||||
|
||||
desktop->loader = NULL;
|
||||
desktop->input_events_pubsub = NULL;
|
||||
furi_record_close(RECORD_LOADER);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_INPUT_EVENTS);
|
||||
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain);
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu);
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLocked);
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdDebug);
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdHwMismatch);
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinInput);
|
||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinTimeout);
|
||||
|
||||
view_dispatcher_free(desktop->view_dispatcher);
|
||||
scene_manager_free(desktop->scene_manager);
|
||||
|
||||
animation_manager_free(desktop->animation_manager);
|
||||
view_stack_free(desktop->main_view_stack);
|
||||
desktop_main_free(desktop->main_view);
|
||||
view_stack_free(desktop->locked_view_stack);
|
||||
desktop_view_locked_free(desktop->locked_view);
|
||||
desktop_lock_menu_free(desktop->lock_menu);
|
||||
desktop_view_locked_free(desktop->locked_view);
|
||||
desktop_debug_free(desktop->debug_view);
|
||||
popup_free(desktop->hw_mismatch_popup);
|
||||
desktop_view_pin_timeout_free(desktop->pin_timeout_view);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
desktop->gui = NULL;
|
||||
|
||||
furi_thread_free(desktop->scene_thread);
|
||||
|
||||
furi_record_close("menu");
|
||||
|
||||
furi_timer_free(desktop->auto_lock_timer);
|
||||
|
||||
free(desktop);
|
||||
}
|
||||
|
||||
static bool desktop_check_file_flag(const char* flag_path) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool exists = storage_common_stat(storage, flag_path, NULL) == FSE_OK;
|
||||
@ -383,6 +339,11 @@ void desktop_api_unlock(Desktop* instance) {
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked);
|
||||
}
|
||||
|
||||
FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->status_pubsub;
|
||||
}
|
||||
|
||||
int32_t desktop_srv(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
@ -427,7 +388,8 @@ int32_t desktop_srv(void* p) {
|
||||
}
|
||||
|
||||
view_dispatcher_run(desktop->view_dispatcher);
|
||||
desktop_free(desktop);
|
||||
|
||||
furi_crash("That was unexpected");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
typedef struct Desktop Desktop;
|
||||
|
||||
#define RECORD_DESKTOP "desktop"
|
||||
@ -7,3 +9,9 @@ typedef struct Desktop Desktop;
|
||||
bool desktop_api_is_locked(Desktop* instance);
|
||||
|
||||
void desktop_api_unlock(Desktop* instance);
|
||||
|
||||
typedef struct {
|
||||
bool locked;
|
||||
} DesktopStatus;
|
||||
|
||||
FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance);
|
||||
|
||||
@ -71,6 +71,8 @@ struct Desktop {
|
||||
FuriPubSubSubscription* input_events_subscription;
|
||||
FuriTimer* auto_lock_timer;
|
||||
|
||||
FuriPubSub* status_pubsub;
|
||||
|
||||
bool in_transition;
|
||||
};
|
||||
|
||||
|
||||
@ -36,8 +36,6 @@
|
||||
#define MIN_PIN_SIZE 4
|
||||
#define MAX_APP_LENGTH 128
|
||||
|
||||
#define FAP_LOADER_APP_NAME "Applications"
|
||||
|
||||
typedef struct {
|
||||
InputKey data[MAX_PIN_SIZE];
|
||||
uint8_t length;
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
#define SNAKE_GAME_APP EXT_PATH("/apps/Games/snake_game.fap")
|
||||
#define CLOCK_APP EXT_PATH("/apps/Tools/clock.fap")
|
||||
|
||||
#define FAP_LOADER_APP_NAME "Applications"
|
||||
|
||||
static void desktop_scene_main_new_idle_animation_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Desktop* desktop = context;
|
||||
@ -77,6 +79,21 @@ static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char*
|
||||
} while(false);
|
||||
}
|
||||
|
||||
static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) {
|
||||
LoaderStatus status = LoaderStatusErrorInternal;
|
||||
if(application->is_external) {
|
||||
status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, application->name_or_path);
|
||||
} else if(strlen(application->name_or_path) > 0) {
|
||||
status = loader_start(desktop->loader, application->name_or_path, NULL);
|
||||
} else {
|
||||
status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, NULL);
|
||||
}
|
||||
|
||||
if(status != LoaderStatusOk) {
|
||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||
}
|
||||
}
|
||||
|
||||
void desktop_scene_main_callback(DesktopEvent event, void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
if(desktop->in_transition) return;
|
||||
@ -141,40 +158,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
case DesktopMainEventOpenFavoritePrimary:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
if(desktop->settings.favorite_primary.is_external) {
|
||||
LoaderStatus status = loader_start(
|
||||
desktop->loader,
|
||||
FAP_LOADER_APP_NAME,
|
||||
desktop->settings.favorite_primary.name_or_path);
|
||||
if(status != LoaderStatusOk) {
|
||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||
}
|
||||
} else {
|
||||
LoaderStatus status = loader_start(
|
||||
desktop->loader, desktop->settings.favorite_primary.name_or_path, NULL);
|
||||
if(status != LoaderStatusOk) {
|
||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||
}
|
||||
}
|
||||
desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_primary);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopMainEventOpenFavoriteSecondary:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
if(desktop->settings.favorite_secondary.is_external) {
|
||||
LoaderStatus status = loader_start(
|
||||
desktop->loader,
|
||||
FAP_LOADER_APP_NAME,
|
||||
desktop->settings.favorite_secondary.name_or_path);
|
||||
if(status != LoaderStatusOk) {
|
||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||
}
|
||||
} else {
|
||||
LoaderStatus status = loader_start(
|
||||
desktop->loader, desktop->settings.favorite_secondary.name_or_path, NULL);
|
||||
if(status != LoaderStatusOk) {
|
||||
FURI_LOG_E(TAG, "loader_start failed: %d", status);
|
||||
}
|
||||
}
|
||||
desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_secondary);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopAnimationEventCheckAnimation:
|
||||
|
||||
@ -89,15 +89,6 @@ Dolphin* dolphin_alloc() {
|
||||
return dolphin;
|
||||
}
|
||||
|
||||
void dolphin_free(Dolphin* dolphin) {
|
||||
furi_assert(dolphin);
|
||||
|
||||
dolphin_state_free(dolphin->state);
|
||||
furi_message_queue_free(dolphin->event_queue);
|
||||
|
||||
free(dolphin);
|
||||
}
|
||||
|
||||
void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) {
|
||||
furi_assert(dolphin);
|
||||
furi_assert(event);
|
||||
@ -204,7 +195,7 @@ int32_t dolphin_srv(void* p) {
|
||||
}
|
||||
}
|
||||
|
||||
dolphin_free(dolphin);
|
||||
furi_crash("That was unexpected");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -37,8 +37,6 @@ struct Dolphin {
|
||||
|
||||
Dolphin* dolphin_alloc();
|
||||
|
||||
void dolphin_free(Dolphin* dolphin);
|
||||
|
||||
void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event);
|
||||
|
||||
void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event);
|
||||
|
||||
@ -85,30 +85,6 @@ Power* power_alloc() {
|
||||
return power;
|
||||
}
|
||||
|
||||
void power_free(Power* power) {
|
||||
furi_assert(power);
|
||||
|
||||
// Gui
|
||||
view_dispatcher_remove_view(power->view_dispatcher, PowerViewOff);
|
||||
power_off_free(power->power_off);
|
||||
view_dispatcher_remove_view(power->view_dispatcher, PowerViewUnplugUsb);
|
||||
power_unplug_usb_free(power->power_unplug_usb);
|
||||
|
||||
view_port_free(power->battery_view_port);
|
||||
|
||||
// State
|
||||
furi_mutex_free(power->api_mtx);
|
||||
|
||||
// FuriPubSub
|
||||
furi_pubsub_free(power->event_pubsub);
|
||||
|
||||
// Records
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
free(power);
|
||||
}
|
||||
|
||||
static void power_check_charging_state(Power* power) {
|
||||
if(furi_hal_power_is_charging()) {
|
||||
if((power->info.charge == 100) || (furi_hal_power_is_charging_done())) {
|
||||
@ -252,7 +228,7 @@ int32_t power_srv(void* p) {
|
||||
furi_delay_ms(1000);
|
||||
}
|
||||
|
||||
power_free(power);
|
||||
furi_crash("That was unexpected");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
typedef struct {
|
||||
RpcSession* session;
|
||||
Desktop* desktop;
|
||||
FuriPubSub* status_pubsub;
|
||||
FuriPubSubSubscription* status_subscription;
|
||||
} RpcDesktop;
|
||||
|
||||
static void rpc_desktop_on_is_locked_request(const PB_Main* request, void* context) {
|
||||
@ -39,11 +41,63 @@ static void rpc_desktop_on_unlock_request(const PB_Main* request, void* context)
|
||||
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
|
||||
}
|
||||
|
||||
static void rpc_desktop_on_desktop_pubsub(const void* message, void* context) {
|
||||
RpcDesktop* rpc_desktop = context;
|
||||
RpcSession* session = rpc_desktop->session;
|
||||
const DesktopStatus* status = message;
|
||||
|
||||
PB_Main rpc_message = {
|
||||
.command_id = 0,
|
||||
.command_status = PB_CommandStatus_OK,
|
||||
.has_next = false,
|
||||
.which_content = PB_Main_desktop_status_tag,
|
||||
.content.desktop_status.locked = status->locked,
|
||||
};
|
||||
rpc_send_and_release(session, &rpc_message);
|
||||
}
|
||||
|
||||
static void rpc_desktop_on_status_subscribe_request(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
furi_assert(request->which_content == PB_Main_desktop_status_subscribe_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "StatusSubscribeRequest");
|
||||
RpcDesktop* rpc_desktop = context;
|
||||
RpcSession* session = rpc_desktop->session;
|
||||
|
||||
if(rpc_desktop->status_subscription) {
|
||||
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_ERROR);
|
||||
} else {
|
||||
rpc_desktop->status_subscription = furi_pubsub_subscribe(
|
||||
rpc_desktop->status_pubsub, rpc_desktop_on_desktop_pubsub, rpc_desktop);
|
||||
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_desktop_on_status_unsubscribe_request(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
furi_assert(request->which_content == PB_Main_desktop_status_unsubscribe_request_tag);
|
||||
|
||||
FURI_LOG_D(TAG, "StatusUnsubscribeRequest");
|
||||
RpcDesktop* rpc_desktop = context;
|
||||
RpcSession* session = rpc_desktop->session;
|
||||
|
||||
if(rpc_desktop->status_subscription) {
|
||||
furi_pubsub_unsubscribe(rpc_desktop->status_pubsub, rpc_desktop->status_subscription);
|
||||
rpc_desktop->status_subscription = NULL;
|
||||
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
|
||||
} else {
|
||||
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void* rpc_desktop_alloc(RpcSession* session) {
|
||||
furi_assert(session);
|
||||
|
||||
RpcDesktop* rpc_desktop = malloc(sizeof(RpcDesktop));
|
||||
rpc_desktop->desktop = furi_record_open(RECORD_DESKTOP);
|
||||
rpc_desktop->status_pubsub = desktop_api_get_status_pubsub(rpc_desktop->desktop);
|
||||
rpc_desktop->session = session;
|
||||
|
||||
RpcHandler rpc_handler = {
|
||||
@ -58,6 +112,12 @@ void* rpc_desktop_alloc(RpcSession* session) {
|
||||
rpc_handler.message_handler = rpc_desktop_on_unlock_request;
|
||||
rpc_add_handler(session, PB_Main_desktop_unlock_request_tag, &rpc_handler);
|
||||
|
||||
rpc_handler.message_handler = rpc_desktop_on_status_subscribe_request;
|
||||
rpc_add_handler(session, PB_Main_desktop_status_subscribe_request_tag, &rpc_handler);
|
||||
|
||||
rpc_handler.message_handler = rpc_desktop_on_status_unsubscribe_request;
|
||||
rpc_add_handler(session, PB_Main_desktop_status_unsubscribe_request_tag, &rpc_handler);
|
||||
|
||||
return rpc_desktop;
|
||||
}
|
||||
|
||||
@ -65,6 +125,10 @@ void rpc_desktop_free(void* context) {
|
||||
furi_assert(context);
|
||||
RpcDesktop* rpc_desktop = context;
|
||||
|
||||
if(rpc_desktop->status_subscription) {
|
||||
furi_pubsub_unsubscribe(rpc_desktop->status_pubsub, rpc_desktop->status_subscription);
|
||||
}
|
||||
|
||||
furi_assert(rpc_desktop->desktop);
|
||||
furi_record_close(RECORD_DESKTOP);
|
||||
|
||||
|
||||
@ -226,7 +226,7 @@ FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* filei
|
||||
*/
|
||||
FS_Error storage_common_remove(Storage* storage, const char* path);
|
||||
|
||||
/** Renames file/directory, file/directory must not be open
|
||||
/** Renames file/directory, file/directory must not be open. Will overwrite existing file.
|
||||
* @param app pointer to the api
|
||||
* @param old_path old path
|
||||
* @param new_path new path
|
||||
|
||||
@ -422,12 +422,27 @@ FS_Error storage_common_remove(Storage* storage, const char* path) {
|
||||
}
|
||||
|
||||
FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) {
|
||||
FS_Error error = storage_common_copy(storage, old_path, new_path);
|
||||
if(error == FSE_OK) {
|
||||
FS_Error error;
|
||||
|
||||
do {
|
||||
if(!storage_common_exists(storage, old_path)) {
|
||||
error = FSE_INVALID_NAME;
|
||||
break;
|
||||
}
|
||||
|
||||
if(storage_file_exists(storage, new_path)) {
|
||||
storage_common_remove(storage, new_path);
|
||||
}
|
||||
|
||||
error = storage_common_copy(storage, old_path, new_path);
|
||||
if(error != FSE_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!storage_simply_remove_recursive(storage, old_path)) {
|
||||
error = FSE_INTERNAL;
|
||||
}
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -5,6 +5,9 @@
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <fap_loader/fap_loader_app.h>
|
||||
|
||||
#define EXTERNAL_APPLICATION_NAME ("[External Application]")
|
||||
#define EXTERNAL_APPLICATION_INDEX (FLIPPER_APPS_COUNT + 1)
|
||||
|
||||
static bool favorite_fap_selector_item_callback(
|
||||
FuriString* file_path,
|
||||
void* context,
|
||||
@ -44,6 +47,8 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
||||
uint32_t primary_favorite =
|
||||
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
uint32_t pre_select_item = 0;
|
||||
FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary :
|
||||
&app->settings.favorite_secondary;
|
||||
|
||||
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
||||
submenu_add_item(
|
||||
@ -53,20 +58,24 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
||||
desktop_settings_scene_favorite_submenu_callback,
|
||||
app);
|
||||
|
||||
if(primary_favorite) { // Select favorite item in submenu
|
||||
if((app->settings.favorite_primary.is_external &&
|
||||
!strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) ||
|
||||
(!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_primary.name_or_path))) {
|
||||
pre_select_item = i;
|
||||
}
|
||||
} else {
|
||||
if((app->settings.favorite_secondary.is_external &&
|
||||
!strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) ||
|
||||
(!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_secondary.name_or_path))) {
|
||||
// Select favorite item in submenu
|
||||
if(!curr_favorite_app->is_external &&
|
||||
!strcmp(FLIPPER_APPS[i].name, curr_favorite_app->name_or_path)) {
|
||||
pre_select_item = i;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef APP_FAP_LOADER
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
EXTERNAL_APPLICATION_NAME,
|
||||
EXTERNAL_APPLICATION_INDEX,
|
||||
desktop_settings_scene_favorite_submenu_callback,
|
||||
app);
|
||||
if(curr_favorite_app->is_external) {
|
||||
pre_select_item = EXTERNAL_APPLICATION_INDEX;
|
||||
}
|
||||
#endif
|
||||
|
||||
submenu_set_header(
|
||||
submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:");
|
||||
@ -82,23 +91,11 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
|
||||
|
||||
uint32_t primary_favorite =
|
||||
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||
FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary :
|
||||
&app->settings.favorite_secondary;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME) != 0) {
|
||||
if(primary_favorite) {
|
||||
app->settings.favorite_primary.is_external = false;
|
||||
strncpy(
|
||||
app->settings.favorite_primary.name_or_path,
|
||||
FLIPPER_APPS[event.event].name,
|
||||
MAX_APP_LENGTH);
|
||||
} else {
|
||||
app->settings.favorite_secondary.is_external = false;
|
||||
strncpy(
|
||||
app->settings.favorite_secondary.name_or_path,
|
||||
FLIPPER_APPS[event.event].name,
|
||||
MAX_APP_LENGTH);
|
||||
}
|
||||
} else {
|
||||
if(event.event == EXTERNAL_APPLICATION_INDEX) {
|
||||
const DialogsFileBrowserOptions browser_options = {
|
||||
.extension = ".fap",
|
||||
.icon = &I_unknown_10px,
|
||||
@ -109,36 +106,29 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
|
||||
.base_path = EXT_PATH("apps"),
|
||||
};
|
||||
|
||||
if(primary_favorite) { // Select favorite fap in file browser
|
||||
if(favorite_fap_selector_file_exists(
|
||||
app->settings.favorite_primary.name_or_path)) {
|
||||
furi_string_set_str(temp_path, app->settings.favorite_primary.name_or_path);
|
||||
}
|
||||
} else {
|
||||
if(favorite_fap_selector_file_exists(
|
||||
app->settings.favorite_secondary.name_or_path)) {
|
||||
furi_string_set_str(temp_path, app->settings.favorite_secondary.name_or_path);
|
||||
}
|
||||
// Select favorite fap in file browser
|
||||
if(favorite_fap_selector_file_exists(curr_favorite_app->name_or_path)) {
|
||||
furi_string_set_str(temp_path, curr_favorite_app->name_or_path);
|
||||
}
|
||||
|
||||
submenu_reset(app->submenu);
|
||||
if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) {
|
||||
if(primary_favorite) {
|
||||
app->settings.favorite_primary.is_external = true;
|
||||
submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene
|
||||
curr_favorite_app->is_external = true;
|
||||
strncpy(
|
||||
app->settings.favorite_primary.name_or_path,
|
||||
curr_favorite_app->name_or_path,
|
||||
furi_string_get_cstr(temp_path),
|
||||
MAX_APP_LENGTH);
|
||||
consumed = true;
|
||||
}
|
||||
} else {
|
||||
app->settings.favorite_secondary.is_external = true;
|
||||
curr_favorite_app->is_external = false;
|
||||
strncpy(
|
||||
app->settings.favorite_secondary.name_or_path,
|
||||
furi_string_get_cstr(temp_path),
|
||||
MAX_APP_LENGTH);
|
||||
}
|
||||
}
|
||||
curr_favorite_app->name_or_path, FLIPPER_APPS[event.event].name, MAX_APP_LENGTH);
|
||||
consumed = true;
|
||||
}
|
||||
if(consumed) {
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
};
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
|
||||
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_0.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_1.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_10.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_11.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_12.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_13.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_14.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_15.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_16.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_17.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_18.png
vendored
Normal file
|
After Width: | Height: | Size: 828 B |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_19.png
vendored
Normal file
|
After Width: | Height: | Size: 817 B |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_2.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_20.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_21.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_22.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_23.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_24.png
vendored
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_25.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_26.png
vendored
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_27.png
vendored
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_28.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_29.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_3.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_30.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_31.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_32.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_33.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_34.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_35.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_36.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_37.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_38.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_39.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_4.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_40.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_41.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_42.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_43.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_44.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/dolphin/external/L1_Kaiju_128x64/frame_45.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |