diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e4a40888..631fbe743 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,15 @@
## New changes
-* NFC: Various NFC Mifare classic Read fixes (was caused by wrong logic in parsers) (mifare classic 4k, and others) (by @Leptopt1los)
-* Apps: Fixed Unitemp and ESP32 Camera suite
+* NFC: Zolotaya Korona Online parser added (by @Leptopt1los)
+* NFC: Add NFC NDEF parser (by @Willy-JL)
+* LF RFID: Write T5577 with random password added (clear password via Extra actions) (by @Leptopt1los)
+* SubGHz: Update honeywell protocol (by @Willy-JL)
* Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
+* OFW: assets: checking limits on image size; ufbt: cdb target
+* OFW: NFC: system dict skip when user dict is skipped fix (replaces our fix)
+* OFW: FuriHal: fix start duration furi_hal_subghz_async_tx
+* OFW: NFC: parsers minor cleanup
+* OFW: NFC Ntag success write freeze when not saved card
+* OFW: ufbt: fixed generated project paths on Windows
#### Known NFC post-refactor regressions list:
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
diff --git a/SConstruct b/SConstruct
index 31391b176..a2f40763e 100644
--- a/SConstruct
+++ b/SConstruct
@@ -369,7 +369,7 @@ vscode_dist = distenv.Install(
)
distenv.Precious(vscode_dist)
distenv.NoClean(vscode_dist)
-distenv.Alias("vscode_dist", vscode_dist)
+distenv.Alias("vscode_dist", (vscode_dist, firmware_env["FW_CDB"]))
# Configure shell with build tools
distenv.PhonyTarget(
diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c
index a53c70424..0e6509b62 100644
--- a/applications/debug/unit_tests/subghz/subghz_test.c
+++ b/applications/debug/unit_tests/subghz/subghz_test.c
@@ -229,17 +229,17 @@ typedef struct {
size_t pos;
} SubGhzHalAsyncTxTest;
-#define SUBGHZ_HAL_TEST_DURATION 1
+#define SUBGHZ_HAL_TEST_DURATION 3
static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
SubGhzHalAsyncTxTest* test = context;
bool is_odd = test->pos % 2;
if(test->type == SubGhzHalAsyncTxTestTypeNormal) {
- if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
+ if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
+ } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_reset();
} else {
@@ -249,36 +249,36 @@ static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
if(test->pos == 0) {
test->pos++;
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
- } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
+ } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
+ } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) {
- if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
+ if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
test->pos++;
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
- } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
+ } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
+ } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) {
- if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
+ if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
test->pos++;
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
- } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
+ } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
+ } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_reset();
} else {
@@ -292,20 +292,20 @@ static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) {
- if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
+ if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
+ } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) {
- if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
+ if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
+ } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) {
test->pos++;
return level_duration_reset();
} else {
@@ -332,6 +332,8 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
while(!furi_hal_subghz_is_async_tx_complete()) {
if(furi_hal_cortex_timer_is_expired(timer)) {
+ furi_hal_subghz_stop_async_tx();
+ furi_hal_subghz_sleep();
return false;
}
furi_delay_ms(10);
diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c
index 352a6281f..5c79f19f4 100644
--- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c
+++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c
@@ -17,18 +17,18 @@
#define TAG "SubGhzDeviceCc1101Ext"
-#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2
+#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO (&gpio_ext_pb2)
#define SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO &gpio_ext_pc3
#define SUBGHZ_DEVICE_CC1101_EXT_FORCE_DANGEROUS_RANGE false
#define SUBGHZ_DEVICE_CC1101_CONFIG_VER 1
/* DMA Channels definition */
-#define SUBGHZ_DEVICE_CC1101_EXT_DMA DMA2
-#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL LL_DMA_CHANNEL_3
-#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL LL_DMA_CHANNEL_4
-#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL LL_DMA_CHANNEL_5
-#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ FuriHalInterruptIdDma2Ch3
+#define SUBGHZ_DEVICE_CC1101_EXT_DMA (DMA2)
+#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL (LL_DMA_CHANNEL_3)
+#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL (LL_DMA_CHANNEL_4)
+#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL (LL_DMA_CHANNEL_5)
+#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ (FuriHalInterruptIdDma2Ch3)
#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF \
SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL
#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF \
@@ -37,10 +37,10 @@
SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL
/** Low level buffer dimensions and guard times */
-#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256)
+#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256u)
#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF \
(SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL / 2)
-#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME 999 << 1
+#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME (999u >> 1)
/** SubGhz state */
typedef enum {
@@ -58,13 +58,25 @@ typedef enum {
SubGhzDeviceCC1101ExtRegulationTxRx, /**TxRx*/
} SubGhzDeviceCC1101ExtRegulation;
+typedef enum {
+ SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle,
+ SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset,
+ SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun,
+} SubGhzDeviceCC1101ExtAsyncTxMiddlewareState;
+
+typedef struct {
+ SubGhzDeviceCC1101ExtAsyncTxMiddlewareState state;
+ bool is_odd_level;
+ uint32_t adder_duration;
+} SubGhzDeviceCC1101ExtAsyncTxMiddleware;
+
typedef struct {
uint32_t* buffer;
- LevelDuration carry_ld;
SubGhzDeviceCC1101ExtCallback callback;
void* callback_context;
uint32_t gpio_tx_buff[2];
uint32_t debug_gpio_buff[2];
+ SubGhzDeviceCC1101ExtAsyncTxMiddleware middleware;
} SubGhzDeviceCC1101ExtAsyncTx;
typedef struct {
@@ -283,8 +295,8 @@ void subghz_device_cc1101_ext_dump_state() {
void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) {
//load config
+ subghz_device_cc1101_ext_reset();
furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);
- cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle);
uint32_t i = 0;
uint8_t pa[8] = {0};
while(preset_data[i]) {
@@ -313,8 +325,8 @@ void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) {
}
void subghz_device_cc1101_ext_load_registers(const uint8_t* data) {
+ subghz_device_cc1101_ext_reset();
furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);
- cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle);
uint32_t i = 0;
while(data[i]) {
cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, data[i], data[i + 1]);
@@ -396,6 +408,7 @@ void subghz_device_cc1101_ext_reset() {
furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle);
cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle);
+ // Warning: push pull cc1101 clock output on GD0
cc1101_write_reg(
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance);
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
@@ -616,50 +629,91 @@ void subghz_device_cc1101_ext_stop_async_rx() {
furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
}
-static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) {
- furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx);
- while(samples > 0) {
- bool is_odd = samples % 2;
- LevelDuration ld;
- if(level_duration_is_reset(subghz_device_cc1101_ext->async_tx.carry_ld)) {
- ld = subghz_device_cc1101_ext->async_tx.callback(
- subghz_device_cc1101_ext->async_tx.callback_context);
- } else {
- ld = subghz_device_cc1101_ext->async_tx.carry_ld;
- subghz_device_cc1101_ext->async_tx.carry_ld = level_duration_reset();
+void subghz_device_cc1101_ext_async_tx_middleware_idle(
+ SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware) {
+ middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle;
+ middleware->is_odd_level = false;
+ middleware->adder_duration = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;
+}
+
+static inline uint32_t subghz_device_cc1101_ext_async_tx_middleware_get_duration(
+ SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware,
+ SubGhzDeviceCC1101ExtCallback callback) {
+ uint32_t ret = 0;
+ bool is_level = false;
+
+ if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset) return 0;
+
+ while(1) {
+ LevelDuration ld = callback(subghz_device_cc1101_ext->async_tx.callback_context);
+ if(level_duration_is_reset(ld)) {
+ middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset;
+ if(!middleware->is_odd_level) {
+ return 0;
+ } else {
+ return middleware->adder_duration;
+ }
+ } else if(level_duration_is_wait(ld)) {
+ middleware->is_odd_level = !middleware->is_odd_level;
+ ret = middleware->adder_duration + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;
+ middleware->adder_duration = 0;
+ return ret;
}
- if(level_duration_is_wait(ld)) {
- *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;
- buffer++;
- samples--;
- } else if(level_duration_is_reset(ld)) {
+ is_level = level_duration_get_level(ld);
+
+ if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle) {
+ if(is_level != middleware->is_odd_level) {
+ middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun;
+ middleware->is_odd_level = is_level;
+ middleware->adder_duration = level_duration_get_duration(ld);
+ return SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;
+ } else {
+ continue;
+ }
+ }
+
+ if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun) {
+ if(is_level == middleware->is_odd_level) {
+ middleware->adder_duration += level_duration_get_duration(ld);
+ continue;
+ } else {
+ middleware->is_odd_level = is_level;
+ ret = middleware->adder_duration;
+ middleware->adder_duration = level_duration_get_duration(ld);
+ return ret;
+ }
+ }
+ }
+}
+
+static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) {
+ furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx);
+
+ while(samples > 0) {
+ volatile uint32_t duration = subghz_device_cc1101_ext_async_tx_middleware_get_duration(
+ &subghz_device_cc1101_ext->async_tx.middleware,
+ subghz_device_cc1101_ext->async_tx.callback);
+ if(duration == 0) {
*buffer = 0;
buffer++;
samples--;
LL_DMA_DisableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);
LL_DMA_DisableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);
+ if(LL_DMA_IsActiveFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) {
+ LL_DMA_ClearFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA);
+ }
+ if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) {
+ LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA);
+ }
LL_TIM_EnableIT_UPDATE(TIM17);
break;
} else {
- bool level = level_duration_get_level(ld);
-
- // Inject guard time if level is incorrect
- if(is_odd != level) {
- *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;
- buffer++;
- samples--;
-
- // Special case: prevent buffer overflow if sample is last
- if(samples == 0) {
- subghz_device_cc1101_ext->async_tx.carry_ld = ld;
- break;
- }
- }
-
- uint32_t duration = level_duration_get_duration(ld);
- furi_assert(duration > 0);
- *buffer = duration >> 1;
+ // Lowest possible value is 4us
+ if(duration < 4) duration = 4;
+ // Divide by 2 since timer resolution is 2us
+ // Subtract 1 since we counting from 0
+ *buffer = (duration >> 1) - 1;
buffer++;
samples--;
}
@@ -691,12 +745,14 @@ static void subghz_device_cc1101_ext_async_tx_dma_isr() {
static void subghz_device_cc1101_ext_async_tx_timer_isr() {
if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) {
if(LL_TIM_GetAutoReload(TIM17) == 0) {
- LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);
- furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
- if(subghz_device_cc1101_ext->async_mirror_pin != NULL)
- furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false);
- LL_TIM_DisableCounter(TIM17);
- subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd;
+ if(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) {
+ LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);
+ subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd;
+ furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
+ if(subghz_device_cc1101_ext->async_mirror_pin != NULL)
+ furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false);
+ LL_TIM_DisableCounter(TIM17);
+ }
}
LL_TIM_ClearFlag_UPDATE(TIM17);
}
@@ -746,16 +802,18 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb
// Configure TIM
// Set the timer resolution to 2 us
- LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1);
LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP);
- LL_TIM_SetAutoReload(TIM17, 0xFFFF);
LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1);
+ LL_TIM_SetAutoReload(TIM17, 500);
+ LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1);
LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_DisableARRPreload(TIM17);
furi_hal_interrupt_set_isr(
FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL);
+ subghz_device_cc1101_ext_async_tx_middleware_idle(
+ &subghz_device_cc1101_ext->async_tx.middleware);
subghz_device_cc1101_ext_async_tx_refill(
subghz_device_cc1101_ext->async_tx.buffer, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL);
@@ -801,7 +859,6 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb
// Start counter
LL_TIM_EnableDMAReq_UPDATE(TIM17);
- LL_TIM_GenerateEvent_UPDATE(TIM17);
subghz_device_cc1101_ext_tx();
@@ -820,11 +877,15 @@ void subghz_device_cc1101_ext_stop_async_tx() {
subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx ||
subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd);
+ // Deinitialize GPIO
+ furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
+ furi_hal_gpio_init(
+ subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
+
// Shutdown radio
subghz_device_cc1101_ext_idle();
// Deinitialize Timer
- FURI_CRITICAL_ENTER();
furi_hal_bus_disable(FuriHalBusTIM17);
furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL);
@@ -833,17 +894,11 @@ void subghz_device_cc1101_ext_stop_async_tx() {
LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF);
furi_hal_interrupt_set_isr(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, NULL, NULL);
- // Deinitialize GPIO
- furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
- furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-
// Stop debug
if(subghz_device_cc1101_ext_stop_debug()) {
LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF);
}
- FURI_CRITICAL_EXIT();
-
free(subghz_device_cc1101_ext->async_tx.buffer);
subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle;
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
index e791e88ba..c42ad6acb 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
@@ -4,27 +4,9 @@
static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) {
Popup* popup = app->popup;
char curr_buf[32] = {};
- //TODO: use .txt file in resources for passwords.
- const uint32_t default_passwords[] = {
- 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F,
- 0x89A69E60, 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752,
- 0x4E457854, 0x44B44CAE, 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666,
- 0x65857569, 0x5469616E, 0x7686962A, 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de,
- 0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666,
- 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD,
- 0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001, 0x00000002,
- 0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708,
- 0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F,
- 0x01234567, 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000,
- 0x60000000, 0x70000000, 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000,
- 0xD0000000, 0xE0000000, 0xF0000000, 0x10101010, 0x01010101, 0x11223344, 0x22334455,
- 0x33445566, 0x44556677, 0x55667788, 0x66778899, 0x778899AA, 0x8899AABB, 0x99AABBCC,
- 0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11, 0x87654321, 0x12341234,
- 0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313, 0x10041004,
- 0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149,
- 0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED,
- 0x69314718, 0x57721566, 0x93C467E3, 0x27182818, 0x50415353};
- const uint8_t default_passwords_len = sizeof(default_passwords) / sizeof(uint32_t);
+
+ uint8_t default_passwords_len;
+ const uint32_t* default_passwords = t5577_get_default_passwords(&default_passwords_len);
popup_set_header(popup, "Removing\npassword", 90, 36, AlignCenter, AlignCenter);
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_config.h b/applications/main/lfrfid/scenes/lfrfid_scene_config.h
index 7789e133e..0d7dfe46d 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_config.h
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_config.h
@@ -6,6 +6,7 @@ ADD_SCENE(lfrfid, exit_confirm, ExitConfirm)
ADD_SCENE(lfrfid, delete_confirm, DeleteConfirm)
ADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu)
ADD_SCENE(lfrfid, write, Write)
+ADD_SCENE(lfrfid, write_with_pass, WriteWithPass)
ADD_SCENE(lfrfid, write_success, WriteSuccess)
ADD_SCENE(lfrfid, emulate, Emulate)
ADD_SCENE(lfrfid, save_name, SaveName)
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c
index 206074e9b..f01688a66 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c
@@ -4,6 +4,7 @@
typedef enum {
SubmenuIndexEmulate,
SubmenuIndexWrite,
+ SubmenuIndexWriteWithPass,
SubmenuIndexEdit,
SubmenuIndexDelete,
SubmenuIndexInfo,
@@ -23,6 +24,12 @@ void lfrfid_scene_saved_key_menu_on_enter(void* context) {
submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_add_item(
submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app);
+ submenu_add_item(
+ submenu,
+ "Write with pass",
+ SubmenuIndexWriteWithPass,
+ lfrfid_scene_saved_key_menu_submenu_callback,
+ app);
submenu_add_item(
submenu, "Edit", SubmenuIndexEdit, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_add_item(
@@ -48,6 +55,9 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event
} else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite);
consumed = true;
+ } else if(event.event == SubmenuIndexWriteWithPass) {
+ scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteWithPass);
+ consumed = true;
} else if(event.event == SubmenuIndexEdit) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData);
consumed = true;
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c
new file mode 100644
index 000000000..263db5cde
--- /dev/null
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c
@@ -0,0 +1,94 @@
+#include "../lfrfid_i.h"
+
+static void lfrfid_write_with_pass_callback(LFRFIDWorkerWriteResult result, void* context) {
+ LfRfid* app = context;
+ uint32_t event = 0;
+
+ if(result == LFRFIDWorkerWriteOK) {
+ event = LfRfidEventWriteOK;
+ } else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) {
+ event = LfRfidEventWriteProtocolCannotBeWritten;
+ } else if(result == LFRFIDWorkerWriteFobCannotBeWritten) {
+ event = LfRfidEventWriteFobCannotBeWritten;
+ } else if(result == LFRFIDWorkerWriteTooLongToWrite) {
+ event = LfRfidEventWriteTooLongToWrite;
+ }
+
+ view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void lfrfid_scene_write_with_pass_on_enter(void* context) {
+ LfRfid* app = context;
+ Popup* popup = app->popup;
+
+ popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop);
+ if(!furi_string_empty(app->file_name)) {
+ popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
+ } else {
+ popup_set_text(
+ popup,
+ protocol_dict_get_name(app->dict, app->protocol_id),
+ 89,
+ 43,
+ AlignCenter,
+ AlignTop);
+ }
+ popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
+
+ size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
+ protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size);
+
+ lfrfid_worker_start_thread(app->lfworker);
+ lfrfid_worker_write_with_pass_start(
+ app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_with_pass_callback, app);
+ notification_message(app->notifications, &sequence_blink_start_magenta);
+}
+
+bool lfrfid_scene_write_with_pass_on_event(void* context, SceneManagerEvent event) {
+ LfRfid* app = context;
+ Popup* popup = app->popup;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == LfRfidEventWriteOK) {
+ notification_message(app->notifications, &sequence_success);
+ scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess);
+ consumed = true;
+ } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) {
+ popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
+ popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop);
+ popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop);
+ notification_message(app->notifications, &sequence_blink_start_red);
+ consumed = true;
+ } else if(
+ (event.event == LfRfidEventWriteFobCannotBeWritten) ||
+ (event.event == LfRfidEventWriteTooLongToWrite)) {
+ popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
+ popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop);
+ popup_set_text(
+ popup,
+ "Make sure this\ncard is writable\nand not\nprotected.",
+ 3,
+ 17,
+ AlignLeft,
+ AlignTop);
+ notification_message(app->notifications, &sequence_blink_start_yellow);
+ consumed = true;
+ }
+ }
+
+ return consumed;
+}
+
+void lfrfid_scene_write_with_pass_on_exit(void* context) {
+ LfRfid* app = context;
+ notification_message(app->notifications, &sequence_blink_stop);
+ popup_reset(app->popup);
+ lfrfid_worker_stop(app->lfworker);
+ lfrfid_worker_stop_thread(app->lfworker);
+
+ size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
+ protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
+}
diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam
index fec3b6c76..d74447844 100644
--- a/applications/main/nfc/application.fam
+++ b/applications/main/nfc/application.fam
@@ -146,6 +146,15 @@ App(
sources=["plugins/supported_cards/zolotaya_korona.c"],
)
+App(
+ appid="zolotaya_korona_online_parser",
+ apptype=FlipperAppType.PLUGIN,
+ entry_point="zolotaya_korona_online_plugin_ep",
+ targets=["f7"],
+ requires=["nfc"],
+ sources=["plugins/supported_cards/zolotaya_korona_online.c"],
+)
+
App(
appid="hid_parser",
apptype=FlipperAppType.PLUGIN,
@@ -164,6 +173,15 @@ App(
sources=["plugins/supported_cards/washcity.c"],
)
+App(
+ appid="ndef_parser",
+ apptype=FlipperAppType.PLUGIN,
+ entry_point="ndef_plugin_ep",
+ targets=["f7"],
+ requires=["nfc"],
+ sources=["plugins/supported_cards/ndef.c"],
+)
+
App(
appid="nfc_start",
targets=["f7"],
diff --git a/applications/main/nfc/plugins/supported_cards/kazan.c b/applications/main/nfc/plugins/supported_cards/kazan.c
index f14c11369..a1bcbb1f8 100644
--- a/applications/main/nfc/plugins/supported_cards/kazan.c
+++ b/applications/main/nfc/plugins/supported_cards/kazan.c
@@ -328,6 +328,8 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) {
last_trip.minute);
}
+ furi_string_free(tariff_name);
+
parsed = true;
} while(false);
diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c
new file mode 100644
index 000000000..a40f59a16
--- /dev/null
+++ b/applications/main/nfc/plugins/supported_cards/ndef.c
@@ -0,0 +1,475 @@
+// Parser for NDEF format data
+// Supports multiple NDEF messages and records in same tag
+// Parsed types: URI (+ Phone, Mail), Text, BT MAC, Contact, WiFi, Empty
+// Documentation and sources indicated where relevant
+// Made by @Willy-JL
+
+#include "nfc_supported_card_plugin.h"
+
+#include
+#include
+#include
+
+#define TAG "NDEF"
+
+static bool is_text(const uint8_t* buf, size_t len) {
+ for(size_t i = 0; i < len; i++) {
+ const char c = buf[i];
+ if((c < ' ' || c > '~') && c != '\r' && c != '\n') {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void
+ print_data(FuriString* str, const char* prefix, const uint8_t* buf, size_t len, bool force_hex) {
+ if(prefix) furi_string_cat_printf(str, "%s: ", prefix);
+ if(!force_hex && is_text(buf, len)) {
+ char* tmp = malloc(len + 1);
+ memcpy(tmp, buf, len);
+ tmp[len] = '\0';
+ furi_string_cat_printf(str, "%s", tmp);
+ free(tmp);
+ } else {
+ for(uint8_t i = 0; i < len; i++) {
+ furi_string_cat_printf(str, "%02X ", buf[i]);
+ }
+ }
+ furi_string_cat(str, "\n");
+}
+
+static void parse_ndef_uri(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
+ // https://learn.adafruit.com/adafruit-pn532-rfid-nfc/ndef#uri-records-0x55-slash-u-607763
+ const char* prepends[] = {
+ [0x00] = "",
+ [0x01] = "http://www.",
+ [0x02] = "https://www.",
+ [0x03] = "http://",
+ [0x04] = "https://",
+ [0x05] = "tel:",
+ [0x06] = "mailto:",
+ [0x07] = "ftp://anonymous:anonymous@",
+ [0x08] = "ftp://ftp.",
+ [0x09] = "ftps://",
+ [0x0A] = "sftp://",
+ [0x0B] = "smb://",
+ [0x0C] = "nfs://",
+ [0x0D] = "ftp://",
+ [0x0E] = "dav://",
+ [0x0F] = "news:",
+ [0x10] = "telnet://",
+ [0x11] = "imap:",
+ [0x12] = "rtsp://",
+ [0x13] = "urn:",
+ [0x14] = "pop:",
+ [0x15] = "sip:",
+ [0x16] = "sips:",
+ [0x17] = "tftp:",
+ [0x18] = "btspp://",
+ [0x19] = "btl2cap://",
+ [0x1A] = "btgoep://",
+ [0x1B] = "tcpobex://",
+ [0x1C] = "irdaobex://",
+ [0x1D] = "file://",
+ [0x1E] = "urn:epc:id:",
+ [0x1F] = "urn:epc:tag:",
+ [0x20] = "urn:epc:pat:",
+ [0x21] = "urn:epc:raw:",
+ [0x22] = "urn:epc:",
+ [0x23] = "urn:nfc:",
+ };
+ const char* prepend = "";
+ uint8_t prepend_type = payload[0];
+ if(prepend_type < COUNT_OF(prepends)) {
+ prepend = prepends[prepend_type];
+ }
+ size_t prepend_len = strlen(prepend);
+
+ size_t uri_len = prepend_len + (payload_len - 1);
+ char* const uri_buf = malloc(uri_len);
+ memcpy(uri_buf, prepend, prepend_len);
+ memcpy(uri_buf + prepend_len, payload + 1, payload_len - 1);
+ char* uri = uri_buf;
+
+ const char* type = "URI";
+ if(strncmp(uri, "http", strlen("http")) == 0) {
+ type = "URL";
+ } else if(strncmp(uri, "tel:", strlen("tel:")) == 0) {
+ type = "Phone";
+ uri += strlen("tel:");
+ uri_len -= strlen("tel:");
+ } else if(strncmp(uri, "mailto:", strlen("mailto:")) == 0) {
+ type = "Mail";
+ uri += strlen("mailto:");
+ uri_len -= strlen("mailto:");
+ }
+
+ furi_string_cat_printf(str, "%s\n", type);
+ print_data(str, NULL, (uint8_t*)uri, uri_len, false);
+ free(uri_buf);
+}
+
+static void parse_ndef_text(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
+ furi_string_cat(str, "Text\n");
+ print_data(str, NULL, payload + 3, payload_len - 3, false);
+}
+
+static void parse_ndef_bt(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
+ furi_string_cat(str, "BT MAC\n");
+ print_data(str, NULL, payload + 2, payload_len - 2, true);
+}
+
+static void parse_ndef_vcard(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
+ char* tmp = malloc(payload_len + 1);
+ memcpy(tmp, payload, payload_len);
+ tmp[payload_len] = '\0';
+ FuriString* fmt = furi_string_alloc_set(tmp);
+ free(tmp);
+
+ furi_string_trim(fmt);
+ if(furi_string_start_with(fmt, "BEGIN:VCARD")) {
+ furi_string_right(fmt, furi_string_search_char(fmt, '\n'));
+ if(furi_string_end_with(fmt, "END:VCARD")) {
+ furi_string_left(fmt, furi_string_search_rchar(fmt, '\n'));
+ }
+ furi_string_trim(fmt);
+ if(furi_string_start_with(fmt, "VERSION:")) {
+ furi_string_right(fmt, furi_string_search_char(fmt, '\n'));
+ furi_string_trim(fmt);
+ }
+ }
+
+ furi_string_cat(str, "Contact\n");
+ print_data(str, NULL, (uint8_t*)furi_string_get_cstr(fmt), furi_string_size(fmt), false);
+ furi_string_free(fmt);
+}
+
+static void parse_ndef_wifi(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
+// https://android.googlesource.com/platform/packages/apps/Nfc/+/refs/heads/main/src/com/android/nfc/NfcWifiProtectedSetup.java
+#define CREDENTIAL_FIELD_ID (0x100E)
+#define SSID_FIELD_ID (0x1045)
+#define NETWORK_KEY_FIELD_ID (0x1027)
+#define AUTH_TYPE_FIELD_ID (0x1003)
+#define AUTH_TYPE_EXPECTED_SIZE (2)
+#define AUTH_TYPE_OPEN (0x0001)
+#define AUTH_TYPE_WPA_PSK (0x0002)
+#define AUTH_TYPE_WPA_EAP (0x0008)
+#define AUTH_TYPE_WPA2_EAP (0x0010)
+#define AUTH_TYPE_WPA2_PSK (0x0020)
+#define AUTH_TYPE_WPA_AND_WPA2_PSK (0x0022)
+#define MAX_NETWORK_KEY_SIZE_BYTES (64)
+
+ size_t i = 0;
+ while(i < payload_len) {
+ uint16_t field_id = nfc_util_bytes2num(payload + i, 2);
+ i += 2;
+ uint16_t field_len = nfc_util_bytes2num(payload + i, 2);
+ i += 2;
+
+ if(field_id == CREDENTIAL_FIELD_ID) {
+ furi_string_cat(str, "WiFi\n");
+ size_t start_position = i;
+ while(i < start_position + field_len) {
+ uint16_t cfg_id = nfc_util_bytes2num(payload + i, 2);
+ i += 2;
+ uint16_t cfg_len = nfc_util_bytes2num(payload + i, 2);
+ i += 2;
+
+ if(i + cfg_len > start_position + field_len) {
+ return;
+ }
+
+ switch(cfg_id) {
+ case SSID_FIELD_ID:
+ print_data(str, "SSID", payload + i, cfg_len, false);
+ i += cfg_len;
+ break;
+ case NETWORK_KEY_FIELD_ID:
+ if(cfg_len > MAX_NETWORK_KEY_SIZE_BYTES) {
+ return;
+ }
+ print_data(str, "PWD", payload + i, cfg_len, false);
+ i += cfg_len;
+ break;
+ case AUTH_TYPE_FIELD_ID:
+ if(cfg_len != AUTH_TYPE_EXPECTED_SIZE) {
+ return;
+ }
+ short auth_type = nfc_util_bytes2num(payload + i, 2);
+ i += 2;
+ const char* auth;
+ switch(auth_type) {
+ case AUTH_TYPE_OPEN:
+ auth = "Open";
+ break;
+ case AUTH_TYPE_WPA_PSK:
+ auth = "WPA Personal";
+ break;
+ case AUTH_TYPE_WPA_EAP:
+ auth = "WPA Enterprise";
+ break;
+ case AUTH_TYPE_WPA2_EAP:
+ auth = "WPA2 Enterprise";
+ break;
+ case AUTH_TYPE_WPA2_PSK:
+ auth = "WPA2 Personal";
+ break;
+ case AUTH_TYPE_WPA_AND_WPA2_PSK:
+ auth = "WPA/WPA2 Personal";
+ break;
+ default:
+ auth = "Unknown";
+ break;
+ }
+ print_data(str, "AUTH", (uint8_t*)auth, strlen(auth), false);
+ break;
+ default:
+ i += cfg_len;
+ break;
+ }
+ }
+ return;
+ }
+ i += field_len;
+ }
+}
+
+static void parse_ndef_payload(
+ FuriString* str,
+ uint8_t tnf,
+ const char* type,
+ uint8_t type_len,
+ const uint8_t* payload,
+ uint32_t payload_len) {
+ if(!payload_len) {
+ furi_string_cat(str, "Empty\n");
+ return;
+ }
+ switch(tnf) {
+ case 0x01: // NFC Forum well-known type [NFC RTD]
+ if(strncmp("U", type, type_len) == 0) {
+ parse_ndef_uri(str, payload, payload_len);
+ } else if(strncmp("T", type, type_len) == 0) {
+ parse_ndef_text(str, payload, payload_len);
+ } else {
+ print_data(str, "Well-known type", (uint8_t*)type, type_len, false);
+ print_data(str, "Payload", payload, payload_len, false);
+ }
+ break;
+ case 0x02: // Media-type [RFC 2046]
+ if(strncmp("application/vnd.bluetooth.ep.oob", type, type_len) == 0) {
+ parse_ndef_bt(str, payload, payload_len);
+ } else if(strncmp("text/vcard", type, type_len) == 0) {
+ parse_ndef_vcard(str, payload, payload_len);
+ } else if(strncmp("application/vnd.wfa.wsc", type, type_len) == 0) {
+ parse_ndef_wifi(str, payload, payload_len);
+ } else {
+ print_data(str, "Media Type", (uint8_t*)type, type_len, false);
+ print_data(str, "Payload", payload, payload_len, false);
+ }
+ break;
+ case 0x00: // Empty
+ case 0x03: // Absolute URI [RFC 3986]
+ case 0x04: // NFC Forum external type [NFC RTD]
+ case 0x05: // Unknown
+ case 0x06: // Unchanged
+ case 0x07: // Reserved
+ default: // Unknown
+ // Dump data without parsing
+ print_data(str, "Type name format", &tnf, 1, true);
+ print_data(str, "Type", (uint8_t*)type, type_len, false);
+ print_data(str, "Payload", payload, payload_len, false);
+ break;
+ }
+}
+
+static const uint8_t* parse_ndef_message(
+ FuriString* str,
+ size_t message_num,
+ const uint8_t* cur,
+ const uint8_t* message_end) {
+ // NDEF message and record documentation:
+ // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/protocols/nfc/index.html#ndef-message-and-record-format
+ size_t record_num = 0;
+ bool last_record = false;
+ while(cur < message_end) {
+ // Flags and TNF
+ uint8_t flags_tnf = *cur++;
+ // Message Begin should only be set on first record
+ if(record_num++ && flags_tnf & (1 << 7)) break;
+ // Message End should only be set on last record
+ if(last_record) break;
+ if(flags_tnf & (1 << 6)) last_record = true;
+ // Chunked Flag not supported
+ if(flags_tnf & (1 << 5)) break;
+ // Payload Length field of 1 vs 4 bytes
+ bool short_record = flags_tnf & (1 << 4);
+ // Is payload ID length and value present
+ bool id_present = flags_tnf & (1 << 3);
+ // Type Name Format 3 bit value
+ uint8_t tnf = flags_tnf & 0b00000111;
+
+ // Type Length
+ uint8_t type_len = *cur++;
+
+ // Payload Length
+ uint32_t payload_len;
+ if(short_record) {
+ payload_len = *cur++;
+ } else {
+ payload_len = nfc_util_bytes2num(cur, 4);
+ cur += 4;
+ }
+
+ // ID Length
+ uint8_t id_len = 0;
+ if(id_present) {
+ id_len = *cur++;
+ }
+
+ // Payload Type
+ char* type = NULL;
+ if(type_len) {
+ type = malloc(type_len);
+ memcpy(type, cur, type_len);
+ cur += type_len;
+ }
+
+ // Payload ID
+ cur += id_len;
+
+ furi_string_cat_printf(str, "\e*> M:%d R:%d - ", message_num, record_num);
+ parse_ndef_payload(str, tnf, type, type_len, cur, payload_len);
+ cur += payload_len;
+
+ free(type);
+ furi_string_trim(str, "\n");
+ furi_string_cat(str, "\n\n");
+ }
+ return cur;
+}
+
+static bool ndef_parse(const NfcDevice* device, FuriString* parsed_data) {
+ furi_assert(device);
+ furi_assert(parsed_data);
+
+ const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
+
+ bool parsed = false;
+
+ do {
+ // Memory layout documentation:
+ // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrfxlib/nfc/doc/type_2_tag.html#id2
+
+ // Double check static values layout
+ // First 4 static reserved pages for UID, internal and lock bytes
+ // (Not sure if NDEF cata can be found in cards with different layout)
+ if(data->page[0].data[0] != 0x04) break;
+ if(data->page[2].data[1] != 0x48) break; // Internal
+ if(data->page[2].data[2] != 0x00) break; // Lock bytes
+ if(data->page[2].data[3] != 0x00) break; // ...
+ if(data->page[3].data[0] != 0xE1) break; // Capability container
+ if(data->page[3].data[1] != 0x10) break; // ...
+
+ // Data content starts here at 5th page
+ const uint8_t* cur = &data->page[4].data[0];
+ const uint8_t* end = &data->page[0].data[0] +
+ (mf_ultralight_get_pages_total(data->type) * MF_ULTRALIGHT_PAGE_SIZE);
+ size_t message_num = 0;
+
+ // Parse as TLV (see docs above)
+ while(cur < end) {
+ switch(*cur++) {
+ case 0x03: { // NDEF message
+ if(cur >= end) break;
+ uint16_t len;
+ if(*cur < 0xFF) { // 1 byte length
+ len = *cur++;
+ } else { // 3 byte length (0xFF marker + 2 byte integer)
+ if(cur + 2 >= end) {
+ cur = end;
+ break;
+ }
+ len = nfc_util_bytes2num(++cur, 2);
+ cur += 2;
+ }
+ if(cur + len >= end) {
+ cur = end;
+ break;
+ }
+
+ if(message_num++ == 0) {
+ furi_string_printf(
+ parsed_data,
+ "\e#NDEF Format Data\nCard type: %s\n",
+ mf_ultralight_get_device_name(data, NfcDeviceNameTypeFull));
+ }
+
+ const uint8_t* message_end = cur + len;
+ cur = parse_ndef_message(parsed_data, message_num, cur, message_end);
+ if(cur != message_end) cur = end;
+
+ break;
+ }
+
+ case 0xFE: // TLV end
+ cur = end;
+ if(message_num != 0) parsed = true;
+ break;
+
+ case 0x00: // Padding, has no length, skip
+ break;
+
+ case 0x01: // Lock control
+ case 0x02: // Memory control
+ case 0xFD: // Proprietary
+ // We don't care, skip this TLV block
+ if(cur >= end) break;
+ if(*cur < 0xFF) { // 1 byte length
+ cur += *cur + 1; // Shift by TLV length
+ } else { // 3 byte length (0xFF marker + 2 byte integer)
+ if(cur + 2 >= end) {
+ cur = end;
+ break;
+ }
+ cur += nfc_util_bytes2num(cur + 1, 2) + 3; // Shift by TLV length
+ }
+ break;
+
+ default: // Unknown, bail to avoid problems
+ cur = end;
+ break;
+ }
+ }
+
+ if(parsed) {
+ furi_string_trim(parsed_data, "\n");
+ furi_string_cat(parsed_data, "\n");
+ } else {
+ furi_string_reset(parsed_data);
+ }
+ } while(false);
+
+ return parsed;
+}
+
+/* Actual implementation of app<>plugin interface */
+static const NfcSupportedCardsPlugin ndef_plugin = {
+ .protocol = NfcProtocolMfUltralight,
+ .verify = NULL,
+ .read = NULL,
+ .parse = ndef_parse,
+};
+
+/* Plugin descriptor to comply with basic plugin specification */
+static const FlipperAppPluginDescriptor ndef_plugin_descriptor = {
+ .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
+ .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
+ .entry_point = &ndef_plugin,
+};
+
+/* Plugin entry point - must return a pointer to const descriptor */
+const FlipperAppPluginDescriptor* ndef_plugin_ep() {
+ return &ndef_plugin_descriptor;
+}
diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c
index 79a840d9a..86ea91f99 100644
--- a/applications/main/nfc/plugins/supported_cards/washcity.c
+++ b/applications/main/nfc/plugins/supported_cards/washcity.c
@@ -161,7 +161,7 @@ static bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_string_printf(
parsed_data,
- "\e#WashCity\nCard number: %0*llX\nBalance: %lu.%02u USD",
+ "\e#WashCity\nCard number: %0*llX\nBalance: %lu.%02u EUR",
uid_len * 2,
card_number,
balance_usd,
diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c
index 2e4f51598..1778cd38e 100644
--- a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c
+++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c
@@ -34,11 +34,6 @@
#define PURSE_SECTOR_NUM (6)
#define INFO_SECTOR_NUM (15)
-typedef struct {
- uint64_t a;
- uint64_t b;
-} MfClassicKeyPair;
-
// Sector 15 data. Byte [11] contains the mistake. If byte [11] was 0xEF, bytes [1-18] means "ЗАО Золотая Корона"
static const uint8_t info_sector_signature[] = {0xE2, 0x87, 0x80, 0x8E, 0x20, 0x87, 0xAE,
0xAB, 0xAE, 0xF2, 0xA0, 0xEF, 0x20, 0x8A,
@@ -76,19 +71,24 @@ void timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime) {
datetime->second = seconds_in_day % FURI_HAL_RTC_SECONDS_PER_MINUTE;
}
-uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes) {
+uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes, bool* is_bcd) {
furi_assert(src);
+ furi_assert(len_bytes <= 9);
- uint64_t res = 0;
+ uint64_t result = 0;
+ *is_bcd = true;
for(uint8_t i = 0; i < len_bytes; i++) {
- res *= 10;
- res += src[i] / 16;
- res *= 10;
- res += src[i] % 16;
+ if(((src[i] / 16) > 9) || ((src[i] % 16) > 9)) *is_bcd = false;
+
+ result *= 10;
+ result += src[i] / 16;
+
+ result *= 10;
+ result += src[i] % 16;
}
- return res;
+ return result;
}
static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_data) {
@@ -121,12 +121,12 @@ static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_da
// INFO SECTOR
// block 1
- const uint8_t region_number = bytes2num_bcd(block_start_ptr + 10, 1);
+ const uint8_t region_number = bytes2num_bcd(block_start_ptr + 10, 1, &verified);
// block 2
block_start_ptr = &data->block[start_info_block_number + 2].data[4];
- const uint64_t card_number =
- bytes2num_bcd(block_start_ptr, 9) * 10 + bytes2num_bcd(block_start_ptr + 9, 1) / 10;
+ const uint16_t card_number_prefix = bytes2num_bcd(block_start_ptr, 2, &verified);
+ const uint64_t card_number_postfix = bytes2num_bcd(block_start_ptr + 2, 8, &verified) / 10;
// TRIP SECTOR
const uint8_t start_trip_block_number =
@@ -157,7 +157,7 @@ static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_da
block_start_ptr = &data->block[start_trip_block_number + 2].data[0];
const char validator_first_letter =
nfc_util_bytes2num_little_endian(block_start_ptr + 1, 1);
- const uint32_t validator_id = bytes2num_bcd(block_start_ptr + 2, 3);
+ const uint32_t validator_id = bytes2num_bcd(block_start_ptr + 2, 3, &verified);
const uint32_t last_trip_timestamp =
nfc_util_bytes2num_little_endian(block_start_ptr + 6, 4);
const uint8_t track_number = nfc_util_bytes2num_little_endian(block_start_ptr + 10, 1);
@@ -174,15 +174,16 @@ static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_da
block_start_ptr = &data->block[start_purse_block_number].data[0];
// block 0
- uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4);
+ const uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4);
uint32_t balance_rub = balance / 100;
uint8_t balance_kop = balance % 100;
furi_string_cat_printf(
parsed_data,
- "\e#Zolotaya korona\nCard number: %llu\nRegion: %u\nBalance: %lu.%02u RUR\nPrev. balance: %lu.%02u RUR",
- card_number,
+ "\e#Zolotaya korona\nCard number: %u%015llu\nRegion: %u\nBalance: %lu.%02u RUR\nPrev. balance: %lu.%02u RUR",
+ card_number_prefix,
+ card_number_postfix,
region_number,
balance_rub,
balance_kop,
diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c
new file mode 100644
index 000000000..eb992763e
--- /dev/null
+++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c
@@ -0,0 +1,166 @@
+/*
+ * Parser for Zolotaya Korona Online card (Russia).
+ * Tariffs research by DNZ1393
+ *
+ * Copyright 2023 Leptoptilos
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include "furi_hal_rtc.h"
+#include "nfc_supported_card_plugin.h"
+
+#include "protocols/mf_classic/mf_classic.h"
+#include
+
+#include
+#include
+#include
+
+#define TAG "Zolotaya Korona Online"
+
+#define TRIP_SECTOR_NUM (4)
+#define INFO_SECTOR_NUM (15)
+
+uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes, bool* is_bcd) {
+ furi_assert(src);
+ furi_assert(len_bytes <= 9);
+
+ uint64_t result = 0;
+ *is_bcd = true;
+
+ for(uint8_t i = 0; i < len_bytes; i++) {
+ if(((src[i] / 16) > 9) || ((src[i] % 16) > 9)) *is_bcd = false;
+
+ result *= 10;
+ result += src[i] / 16;
+
+ result *= 10;
+ result += src[i] % 16;
+ }
+
+ return result;
+}
+
+bool parse_online_card_tariff(uint16_t tariff_num, FuriString* tariff_name) {
+ bool tariff_parsed = false;
+
+ switch(tariff_num) {
+ case 0x0100:
+ furi_string_set_str(tariff_name, "Standart (online)");
+ tariff_parsed = true;
+ break;
+ case 0x0101:
+ case 0x0121:
+ furi_string_set_str(tariff_name, "Standart (airtag)");
+ tariff_parsed = true;
+ break;
+ case 0x0401:
+ furi_string_set_str(tariff_name, "Student (50%% discount)");
+ tariff_parsed = true;
+ break;
+ case 0x0402:
+ furi_string_set_str(tariff_name, "Student (travel)");
+ tariff_parsed = true;
+ break;
+ case 0x0002:
+ furi_string_set_str(tariff_name, "School (50%% discount)");
+ tariff_parsed = true;
+ break;
+ case 0x0505:
+ furi_string_set_str(tariff_name, "Social (large families)");
+ tariff_parsed = true;
+ break;
+ case 0x0528:
+ furi_string_set_str(tariff_name, "Social (handicapped)");
+ tariff_parsed = true;
+ break;
+ default:
+ furi_string_set_str(tariff_name, "Unknown");
+ tariff_parsed = false;
+ break;
+ }
+
+ return tariff_parsed;
+}
+
+static bool zolotaya_korona_online_parse(const NfcDevice* device, FuriString* parsed_data) {
+ furi_assert(device);
+
+ const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
+
+ bool parsed = false;
+
+ do {
+ // Verify info sector data (card number prefix)
+ const uint8_t start_trip_block_number =
+ mf_classic_get_first_block_num_of_sector(TRIP_SECTOR_NUM);
+ const uint8_t start_info_block_number =
+ mf_classic_get_first_block_num_of_sector(INFO_SECTOR_NUM);
+ const uint8_t* block_start_ptr = &data->block[start_info_block_number].data[3];
+
+ // Validate card number
+ bool is_bcd;
+ const uint16_t card_number_prefix = bytes2num_bcd(block_start_ptr, 2, &is_bcd);
+ if(!is_bcd) break;
+ if(card_number_prefix != 9643) break;
+ const uint64_t card_number_postfix = bytes2num_bcd(block_start_ptr + 2, 8, &is_bcd) / 10;
+ if(!is_bcd) break;
+
+ // Parse data
+ FuriString* tariff_name = furi_string_alloc();
+
+ block_start_ptr = &data->block[start_info_block_number].data[1];
+ const uint16_t tariff = nfc_util_bytes2num(block_start_ptr, 2);
+ parse_online_card_tariff(tariff, tariff_name);
+
+ block_start_ptr = &data->block[start_trip_block_number].data[0];
+ const uint8_t region_number = nfc_util_bytes2num(block_start_ptr, 1);
+
+ furi_string_cat_printf(
+ parsed_data,
+ "\e#Zolotaya korona\nCard number: %u%015llu\nTariff: %02X.%02X: %s\nRegion: %u\n",
+ card_number_prefix,
+ card_number_postfix,
+ tariff / 256,
+ tariff % 256,
+ furi_string_get_cstr(tariff_name),
+ region_number);
+
+ furi_string_free(tariff_name);
+
+ parsed = true;
+ } while(false);
+
+ return parsed;
+}
+
+/* Actual implementation of app<>plugin interface */
+static const NfcSupportedCardsPlugin zolotaya_korona_online_plugin = {
+ .protocol = NfcProtocolMfClassic,
+ .verify = NULL,
+ .read = NULL,
+ .parse = zolotaya_korona_online_parse,
+};
+
+/* Plugin descriptor to comply with basic plugin specification */
+static const FlipperAppPluginDescriptor zolotaya_korona_online_plugin_descriptor = {
+ .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
+ .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
+ .entry_point = &zolotaya_korona_online_plugin,
+};
+
+/* Plugin entry point - must return a pointer to const descriptor */
+const FlipperAppPluginDescriptor* zolotaya_korona_online_plugin_ep() {
+ return &zolotaya_korona_online_plugin_descriptor;
+}
\ No newline at end of file
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
index 22727af12..328e39132 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
@@ -173,7 +173,6 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) {
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
- instance->nfc_dict_context.is_card_present = true;
}
static void nfc_scene_mf_classic_dict_attack_notify_read(NfcApp* instance) {
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c
index 9726ef283..bb34190d2 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c
@@ -28,8 +28,11 @@ bool nfc_scene_mf_ultralight_write_success_on_event(void* context, SceneManagerE
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) {
+ bool was_saved =
+ scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu);
+
consumed = scene_manager_search_and_switch_to_previous_scene(
- instance->scene_manager, NfcSceneSavedMenu);
+ instance->scene_manager, was_saved ? NfcSceneSavedMenu : NfcSceneReadSuccess);
}
}
return consumed;
diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt
index c2583ae5a..9aaff3c6f 100644
--- a/assets/dolphin/external/manifest.txt
+++ b/assets/dolphin/external/manifest.txt
@@ -203,10 +203,3 @@ Max butthurt: 10
Min level: 3
Max level: 3
Weight: 2
-
-Name: L1_New_year_128x64
-Min butthurt: 0
-Max butthurt: 10
-Min level: 1
-Max level: 3
-Weight: 7
diff --git a/firmware.scons b/firmware.scons
index 004def9a9..901a76214 100644
--- a/firmware.scons
+++ b/firmware.scons
@@ -249,7 +249,7 @@ fw_artifacts.extend(
)
-fwcdb = fwenv.CompilationDatabase()
+fwcdb = fwenv["FW_CDB"] = fwenv.CompilationDatabase()
# without filtering, both updater & firmware commands would be generated in same file
fwenv.Replace(
COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*"),
diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c
index ffaa8ee92..6b40924d2 100644
--- a/lib/lfrfid/lfrfid_worker.c
+++ b/lib/lfrfid/lfrfid_worker.c
@@ -8,12 +8,14 @@ typedef enum {
LFRFIDEventStopMode = (1 << 1),
LFRFIDEventRead = (1 << 2),
LFRFIDEventWrite = (1 << 3),
- LFRFIDEventEmulate = (1 << 4),
- LFRFIDEventReadRaw = (1 << 5),
- LFRFIDEventEmulateRaw = (1 << 6),
+ LFRFIDEventWriteWithPass = (1 << 4),
+ LFRFIDEventEmulate = (1 << 5),
+ LFRFIDEventReadRaw = (1 << 6),
+ LFRFIDEventEmulateRaw = (1 << 7),
LFRFIDEventAll =
(LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite |
- LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw),
+ LFRFIDEventWriteWithPass | LFRFIDEventEmulate | LFRFIDEventReadRaw |
+ LFRFIDEventEmulateRaw),
} LFRFIDEventType;
static int32_t lfrfid_worker_thread(void* thread_context);
@@ -69,6 +71,18 @@ void lfrfid_worker_write_start(
furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWrite);
}
+void lfrfid_worker_write_with_pass_start(
+ LFRFIDWorker* worker,
+ LFRFIDProtocol protocol,
+ LFRFIDWorkerWriteCallback callback,
+ void* context) {
+ furi_assert(worker->mode_index == LFRFIDWorkerIdle);
+ worker->protocol = protocol;
+ worker->write_cb = callback;
+ worker->cb_ctx = context;
+ furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWriteWithPass);
+}
+
void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol) {
furi_assert(worker->mode_index == LFRFIDWorkerIdle);
worker->protocol = protocol;
@@ -145,6 +159,7 @@ static int32_t lfrfid_worker_thread(void* thread_context) {
// switch mode
if(flags & LFRFIDEventRead) worker->mode_index = LFRFIDWorkerRead;
if(flags & LFRFIDEventWrite) worker->mode_index = LFRFIDWorkerWrite;
+ if(flags & LFRFIDEventWriteWithPass) worker->mode_index = LFRFIDWorkerWriteWithPass;
if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate;
if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw;
if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw;
diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h
index 22135097e..ed09d6143 100644
--- a/lib/lfrfid/lfrfid_worker.h
+++ b/lib/lfrfid/lfrfid_worker.h
@@ -106,6 +106,20 @@ void lfrfid_worker_write_start(
LFRFIDWorkerWriteCallback callback,
void* context);
+/**
+ * @brief Start write with pass mode
+ *
+ * @param worker
+ * @param protocol
+ * @param callback
+ * @param context
+ */
+void lfrfid_worker_write_with_pass_start(
+ LFRFIDWorker* worker,
+ LFRFIDProtocol protocol,
+ LFRFIDWorkerWriteCallback callback,
+ void* context);
+
/**
* Start emulate mode
* @param worker
diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h
index 33c0bff08..16d1f9716 100644
--- a/lib/lfrfid/lfrfid_worker_i.h
+++ b/lib/lfrfid/lfrfid_worker_i.h
@@ -22,6 +22,7 @@ typedef enum {
LFRFIDWorkerIdle,
LFRFIDWorkerRead,
LFRFIDWorkerWrite,
+ LFRFIDWorkerWriteWithPass,
LFRFIDWorkerEmulate,
LFRFIDWorkerReadRaw,
LFRFIDWorkerEmulateRaw,
diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c
index 32e253259..3db438eec 100644
--- a/lib/lfrfid/lfrfid_worker_modes.c
+++ b/lib/lfrfid/lfrfid_worker_modes.c
@@ -574,6 +574,96 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) {
free(read_data);
}
+static void lfrfid_worker_mode_write_with_pass_process(LFRFIDWorker* worker) {
+ LFRFIDProtocol protocol = worker->protocol;
+ LFRFIDWriteRequest* request = malloc(sizeof(LFRFIDWriteRequest));
+ request->write_type = LFRFIDWriteTypeT5577;
+
+ bool can_be_written = protocol_dict_get_write_data(worker->protocols, protocol, request);
+
+ uint32_t write_start_time = furi_get_tick();
+ bool too_long = false;
+ size_t unsuccessful_reads = 0;
+
+ size_t data_size = protocol_dict_get_data_size(worker->protocols, protocol);
+ uint8_t* verify_data = malloc(data_size);
+ uint8_t* read_data = malloc(data_size);
+ protocol_dict_get_data(worker->protocols, protocol, verify_data, data_size);
+
+ if(can_be_written) {
+ while(!lfrfid_worker_check_for_stop(worker)) {
+ FURI_LOG_D(TAG, "Data write");
+
+ uint8_t size;
+ const uint32_t* password_list = t5577_get_default_passwords(&size);
+
+ uint32_t pass = password_list[rand() % size];
+
+ request->t5577.mask = 0b1111111;
+ request->t5577.block[0] |= 0b10000;
+ request->t5577.block[7] = pass;
+
+ t5577_write_with_mask(&request->t5577, 0, 0);
+
+ ProtocolId read_result = PROTOCOL_NO;
+ LFRFIDWorkerReadState state = lfrfid_worker_read_internal(
+ worker,
+ protocol_dict_get_features(worker->protocols, protocol),
+ LFRFID_WORKER_WRITE_VERIFY_TIME_MS,
+ &read_result);
+
+ if(state == LFRFIDWorkerReadOK) {
+ bool read_success = false;
+
+ if(read_result == protocol) {
+ protocol_dict_get_data(worker->protocols, protocol, read_data, data_size);
+
+ if(memcmp(read_data, verify_data, data_size) == 0) {
+ read_success = true;
+ }
+ }
+
+ if(read_success) {
+ FURI_LOG_D(TAG, "Write with password %08lX success", pass);
+
+ if(worker->write_cb) {
+ worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx);
+ }
+ break;
+ } else {
+ unsuccessful_reads++;
+
+ if(unsuccessful_reads == LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS) {
+ if(worker->write_cb) {
+ worker->write_cb(LFRFIDWorkerWriteFobCannotBeWritten, worker->cb_ctx);
+ }
+ }
+ }
+ } else if(state == LFRFIDWorkerReadExit) {
+ break;
+ }
+
+ if(!too_long &&
+ (furi_get_tick() - write_start_time) > LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS) {
+ too_long = true;
+ if(worker->write_cb) {
+ worker->write_cb(LFRFIDWorkerWriteTooLongToWrite, worker->cb_ctx);
+ }
+ }
+
+ lfrfid_worker_delay(worker, LFRFID_WORKER_WRITE_DROP_TIME_MS);
+ }
+ } else {
+ if(worker->write_cb) {
+ worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx);
+ }
+ }
+
+ free(request);
+ free(verify_data);
+ free(read_data);
+}
+
/**************************************************************************************************/
/******************************************* READ RAW *********************************************/
/**************************************************************************************************/
@@ -629,6 +719,7 @@ const LFRFIDWorkerModeType lfrfid_worker_modes[] = {
[LFRFIDWorkerIdle] = {.process = NULL},
[LFRFIDWorkerRead] = {.process = lfrfid_worker_mode_read_process},
[LFRFIDWorkerWrite] = {.process = lfrfid_worker_mode_write_process},
+ [LFRFIDWorkerWriteWithPass] = {.process = lfrfid_worker_mode_write_with_pass_process},
[LFRFIDWorkerEmulate] = {.process = lfrfid_worker_mode_emulate_process},
[LFRFIDWorkerReadRaw] = {.process = lfrfid_worker_mode_read_raw_process},
[LFRFIDWorkerEmulateRaw] = {.process = lfrfid_worker_mode_emulate_raw_process},
diff --git a/lib/lfrfid/tools/t5577.c b/lib/lfrfid/tools/t5577.c
index 666a5c8fe..83ae99989 100644
--- a/lib/lfrfid/tools/t5577.c
+++ b/lib/lfrfid/tools/t5577.c
@@ -13,6 +13,33 @@
#define T5577_OPCODE_PAGE_1 0b11
#define T5577_OPCODE_RESET 0b00
+#define T5577_BLOCKS_IN_PAGE_0 8
+#define T5577_BLOCKS_IN_PAGE_1 4
+
+//TODO: use .txt file in resources for passwords.
+const uint32_t default_passwords[] = {
+ 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, 0x89A69E60,
+ 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752, 0x4E457854, 0x44B44CAE,
+ 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666, 0x65857569, 0x5469616E, 0x7686962A,
+ 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de, 0x00000000, 0x11111111, 0x22222222, 0x33333333,
+ 0x44444444, 0x55555555, 0x66666666, 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB,
+ 0xCCCCCCCC, 0xDDDDDDDD, 0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001,
+ 0x00000002, 0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708,
+ 0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F, 0x01234567,
+ 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000, 0x60000000, 0x70000000,
+ 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000, 0xD0000000, 0xE0000000, 0xF0000000,
+ 0x10101010, 0x01010101, 0x11223344, 0x22334455, 0x33445566, 0x44556677, 0x55667788, 0x66778899,
+ 0x778899AA, 0x8899AABB, 0x99AABBCC, 0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11,
+ 0x87654321, 0x12341234, 0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313,
+ 0x10041004, 0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149,
+ 0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED, 0x69314718,
+ 0x57721566, 0x93C467E3, 0x27182818, 0x50415353};
+
+const uint32_t* t5577_get_default_passwords(uint8_t* len) {
+ *len = sizeof(default_passwords) / sizeof(uint32_t);
+ return default_passwords;
+}
+
static void t5577_start() {
furi_hal_rfid_tim_read_start(125000, 0.5);
@@ -52,6 +79,7 @@ static void t5577_write_reset() {
}
static void t5577_write_block_pass(
+ uint8_t page,
uint8_t block,
bool lock_bit,
uint32_t data,
@@ -62,8 +90,8 @@ static void t5577_write_block_pass(
// start gap
t5577_write_gap(T5577_TIMING_START_GAP);
- // opcode for page 0
- t5577_write_opcode(T5577_OPCODE_PAGE_0);
+ // opcode for page
+ t5577_write_opcode((page == 1) ? T5577_OPCODE_PAGE_1 : T5577_OPCODE_PAGE_0);
// password
if(with_pass) {
@@ -92,7 +120,7 @@ static void t5577_write_block_pass(
}
static void t5577_write_block_simple(uint8_t block, bool lock_bit, uint32_t data) {
- t5577_write_block_pass(block, lock_bit, data, false, 0);
+ t5577_write_block_pass(0, block, lock_bit, data, false, 0);
}
void t5577_write(LFRFIDT5577* data) {
@@ -110,9 +138,28 @@ void t5577_write_with_pass(LFRFIDT5577* data, uint32_t password) {
t5577_start();
FURI_CRITICAL_ENTER();
for(size_t i = 0; i < data->blocks_to_write; i++) {
- t5577_write_block_pass(i, false, data->block[i], true, password);
+ t5577_write_block_pass(0, i, false, data->block[i], true, password);
}
t5577_write_reset();
FURI_CRITICAL_EXIT();
t5577_stop();
}
+
+void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password) {
+ t5577_start();
+ FURI_CRITICAL_ENTER();
+
+ uint8_t mask = data->mask;
+
+ size_t pages_total = (page == 0) ? T5577_BLOCKS_IN_PAGE_0 : T5577_BLOCKS_IN_PAGE_1;
+
+ for(size_t i = 0; i < pages_total; i++) {
+ bool need_to_write = mask & 1;
+ mask >>= 1;
+ if(!need_to_write) continue;
+ t5577_write_block_pass(page, i, false, data->block[i], true, password);
+ }
+ t5577_write_reset();
+ FURI_CRITICAL_EXIT();
+ t5577_stop();
+}
\ No newline at end of file
diff --git a/lib/lfrfid/tools/t5577.h b/lib/lfrfid/tools/t5577.h
index c77984476..e78581ac0 100644
--- a/lib/lfrfid/tools/t5577.h
+++ b/lib/lfrfid/tools/t5577.h
@@ -42,8 +42,11 @@ extern "C" {
typedef struct {
uint32_t block[LFRFID_T5577_BLOCK_COUNT];
uint32_t blocks_to_write;
+ uint8_t mask;
} LFRFIDT5577;
+const uint32_t* t5577_get_default_passwords(uint8_t* len);
+
/**
* @brief Write T5577 tag data to tag
*
@@ -53,6 +56,8 @@ void t5577_write(LFRFIDT5577* data);
void t5577_write_with_pass(LFRFIDT5577* data, uint32_t password);
+void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password);
+
#ifdef __cplusplus
}
#endif
\ No newline at end of file
diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c
index dbc32a1b5..d846bba69 100644
--- a/lib/nfc/protocols/mf_classic/mf_classic_poller.c
+++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c
@@ -22,6 +22,7 @@ MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller)
instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->rx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->current_type_check = MfClassicType4k;
+ instance->card_state = MfClassicCardStateLost;
instance->mfc_event.data = &instance->mfc_event_data;
diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c
index 5fdc7f45c..903a52b86 100644
--- a/lib/subghz/protocols/honeywell.c
+++ b/lib/subghz/protocols/honeywell.c
@@ -86,9 +86,6 @@ void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) {
instance->generic.data_count_bit =
instance->decoder
.decode_count_bit; //maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it
- instance->generic.serial = (instance->decoder.decode_data >> 24) & 0xFFFFF;
- instance->generic.btn = (instance->decoder.decode_data >> 16) &
- 0xFF; //not exactly button, but can contain btn data too.
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
instance->decoder.decode_data = 0;
@@ -166,6 +163,11 @@ void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* out
furi_assert(context);
SubGhzProtocolDecoderHoneywell* instance = context;
+ // Parse here and not in decode to avoid visual glitches when loading from file
+ instance->generic.serial = (instance->generic.data >> 24) & 0xFFFFF;
+ instance->generic.btn = (instance->generic.data >> 16) &
+ 0xFF; //not exactly button, but can contain btn data too.
+
uint8_t channel = (instance->generic.data >> 44) & 0xF;
uint8_t contact = (instance->generic.btn & 0x80) >> 7;
uint8_t tamper = (instance->generic.btn & 0x40) >> 6;
diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c
index 5d7118092..ade840a36 100644
--- a/lib/subghz/subghz_file_encoder_worker.c
+++ b/lib/subghz/subghz_file_encoder_worker.c
@@ -18,7 +18,6 @@ struct SubGhzFileEncoderWorker {
volatile bool worker_running;
volatile bool worker_stopping;
- bool level;
bool is_storage_slow;
FuriString* str_data;
FuriString* file_path;
@@ -41,19 +40,8 @@ void subghz_file_encoder_worker_callback_end(
void subghz_file_encoder_worker_add_level_duration(
SubGhzFileEncoderWorker* instance,
int32_t duration) {
- bool res = true;
- if(duration < 0 && !instance->level) {
- res = false;
- } else if(duration > 0 && instance->level) {
- res = false;
- }
-
- if(res) {
- instance->level = !instance->level;
- furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100);
- } else {
- FURI_LOG_E(TAG, "Invalid level in the stream");
- }
+ size_t ret = furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100);
+ if(sizeof(int32_t) != ret) FURI_LOG_E(TAG, "Invalid add duration in the stream");
}
bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) {
@@ -214,7 +202,6 @@ SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() {
instance->str_data = furi_string_alloc();
instance->file_path = furi_string_alloc();
- instance->level = false;
instance->worker_stopping = true;
return instance;
diff --git a/scripts/assets.py b/scripts/assets.py
index 1099f0c33..711c1b440 100755
--- a/scripts/assets.py
+++ b/scripts/assets.py
@@ -24,6 +24,9 @@ ICONS_TEMPLATE_C_FRAME = "const uint8_t {name}[] = {data};\n"
ICONS_TEMPLATE_C_DATA = "const uint8_t* const {name}[] = {data};\n"
ICONS_TEMPLATE_C_ICONS = "const Icon {name} = {{.width={width},.height={height},.frame_count={frame_count},.frame_rate={frame_rate},.frames=_{name}}};\n"
+MAX_IMAGE_WIDTH = 128
+MAX_IMAGE_HEIGHT = 64
+
class Main(App):
def init(self):
@@ -102,6 +105,10 @@ class Main(App):
def _icon2header(self, file):
image = file2image(file)
+ if image.width > MAX_IMAGE_WIDTH or image.height > MAX_IMAGE_HEIGHT:
+ raise Exception(
+ f"Image {file} is too big ({image.width}x{image.height} vs. {MAX_IMAGE_WIDTH}x{MAX_IMAGE_HEIGHT})"
+ )
return image.width, image.height, image.data_as_carray()
def _iconIsSupported(self, filename):
diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct
index 2fc170ad9..8df1ae110 100644
--- a/scripts/ufbt/SConstruct
+++ b/scripts/ufbt/SConstruct
@@ -275,15 +275,16 @@ Default(install_and_check)
# Compilation database
-fwcdb = appenv.CompilationDatabase(
+app_cdb = appenv.CompilationDatabase(
original_app_dir.Dir(".vscode").File("compile_commands.json")
)
-AlwaysBuild(fwcdb)
-Precious(fwcdb)
-NoClean(fwcdb)
+AlwaysBuild(app_cdb)
+Precious(app_cdb)
+NoClean(app_cdb)
if len(apps_artifacts):
- Default(fwcdb)
+ Default(app_cdb)
+Alias("cdb", app_cdb)
# launch handler
@@ -365,7 +366,7 @@ for template_file in project_template_dir.Dir(".vscode").glob("*"):
"@UFBT_TOOLCHAIN_OPENOCD@": _path_as_posix(dist_env.WhereIs("openocd")),
"@UFBT_APP_DIR@": _path_as_posix(original_app_dir.abspath),
"@UFBT_ROOT_DIR@": _path_as_posix(Dir("#").abspath),
- "@UFBT_DEBUG_DIR@": dist_env["FBT_DEBUG_DIR"],
+ "@UFBT_DEBUG_DIR@": _path_as_posix(dist_env["FBT_DEBUG_DIR"].abspath),
"@UFBT_DEBUG_ELF_DIR@": _path_as_posix(
dist_env["FBT_FAP_DEBUG_ELF_ROOT"].abspath
),
@@ -381,7 +382,7 @@ for config_file in project_template_dir.glob(".*"):
dist_env.Precious(vscode_dist)
dist_env.NoClean(vscode_dist)
-dist_env.Alias("vscode_dist", vscode_dist)
+dist_env.Alias("vscode_dist", (vscode_dist, app_cdb))
# Creating app from base template
diff --git a/scripts/ufbt/project_template/.vscode/c_cpp_properties.json b/scripts/ufbt/project_template/.vscode/c_cpp_properties.json
index 922a9091b..f957ee98b 100644
--- a/scripts/ufbt/project_template/.vscode/c_cpp_properties.json
+++ b/scripts/ufbt/project_template/.vscode/c_cpp_properties.json
@@ -8,7 +8,7 @@
"configurationProvider": "ms-vscode.cpptools",
"cStandard": "gnu17",
"cppStandard": "c++17"
- },
+ }
],
"version": 4
}
\ No newline at end of file
diff --git a/scripts/ufbt/project_template/.vscode/extensions.json b/scripts/ufbt/project_template/.vscode/extensions.json
index ead935b08..9daefb430 100644
--- a/scripts/ufbt/project_template/.vscode/extensions.json
+++ b/scripts/ufbt/project_template/.vscode/extensions.json
@@ -1,3 +1,7 @@
+// This file is autogeneated by the ufbt.
+// You can modify it, and it will not be overwritten if exists.
+// Some paths are absolute, and will need to be updated if you move the project.
+// To regenerate the file, delete it and run `ufbt vscode_dist` again.
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
diff --git a/scripts/ufbt/project_template/.vscode/launch.json b/scripts/ufbt/project_template/.vscode/launch.json
index 3269bab57..a5639743f 100644
--- a/scripts/ufbt/project_template/.vscode/launch.json
+++ b/scripts/ufbt/project_template/.vscode/launch.json
@@ -1,3 +1,7 @@
+// This file is autogeneated by the ufbt.
+// You can modify it, and it will not be overwritten if exists.
+// Some paths are absolute, and will need to be updated if you move the project.
+// To regenerate the file, delete it and run `ufbt vscode_dist` again.
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
diff --git a/scripts/ufbt/project_template/.vscode/settings.json b/scripts/ufbt/project_template/.vscode/settings.json
index 33cd3f035..ce5210f5f 100644
--- a/scripts/ufbt/project_template/.vscode/settings.json
+++ b/scripts/ufbt/project_template/.vscode/settings.json
@@ -1,3 +1,7 @@
+// This file is autogeneated by the ufbt.
+// You can modify it, and it will not be overwritten if exists.
+// Some paths are absolute, and will need to be updated if you move the project.
+// To regenerate the file, delete it and run `ufbt vscode_dist` again.
{
"cortex-debug.enableTelemetry": false,
"cortex-debug.variableUseNaturalFormat": false,
diff --git a/scripts/ufbt/project_template/.vscode/tasks.json b/scripts/ufbt/project_template/.vscode/tasks.json
index 4b3f4bda5..65c749e07 100644
--- a/scripts/ufbt/project_template/.vscode/tasks.json
+++ b/scripts/ufbt/project_template/.vscode/tasks.json
@@ -1,3 +1,7 @@
+// This file is autogeneated by the ufbt.
+// You can modify it, and it will not be overwritten if exists.
+// Some paths are absolute, and will need to be updated if you move the project.
+// To regenerate the file, delete it and run `ufbt vscode_dist` again.
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py
index ab20e2f7d..4873b385c 100644
--- a/scripts/ufbt/site_tools/ufbt_help.py
+++ b/scripts/ufbt/site_tools/ufbt_help.py
@@ -18,6 +18,8 @@ Building:
Build all FAP apps
fap_{APPID}, launch APPSRC={APPID}:
Build FAP app with appid={APPID}; upload & start it over USB
+ cdb:
+ regenerate "compile_commands.json" file (for IDE integration)
Flashing & debugging:
flash, *jflash:
diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv
index 52aabcbea..56e47318e 100644
--- a/targets/f18/api_symbols.csv
+++ b/targets/f18/api_symbols.csv
@@ -1,5 +1,5 @@
entry,status,name,type,params
-Version,+,50.1,,
+Version,+,50.2,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,,
diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv
index ad6a4c1bd..5272c0945 100644
--- a/targets/f7/api_symbols.csv
+++ b/targets/f7/api_symbols.csv
@@ -2069,6 +2069,7 @@ Function,+,lfrfid_worker_start_thread,void,LFRFIDWorker*
Function,+,lfrfid_worker_stop,void,LFRFIDWorker*
Function,+,lfrfid_worker_stop_thread,void,LFRFIDWorker*
Function,+,lfrfid_worker_write_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*"
+Function,+,lfrfid_worker_write_with_pass_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*"
Function,-,lgamma,double,double
Function,-,lgamma_r,double,"double, int*"
Function,-,lgammaf,float,float
@@ -3225,7 +3226,9 @@ Function,+,submenu_set_header,void,"Submenu*, const char*"
Function,+,submenu_set_orientation,void,"Submenu*, ViewOrientation"
Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t"
Function,-,system,int,const char*
+Function,+,t5577_get_default_passwords,const uint32_t*,uint8_t*
Function,+,t5577_write,void,LFRFIDT5577*
+Function,+,t5577_write_with_mask,void,"LFRFIDT5577*, uint8_t, uint32_t"
Function,+,t5577_write_with_pass,void,"LFRFIDT5577*, uint32_t"
Function,-,tan,double,double
Function,-,tanf,float,float
diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c
index 20771a29a..51c65f8ac 100644
--- a/targets/f7/furi_hal/furi_hal_subghz.c
+++ b/targets/f7/furi_hal/furi_hal_subghz.c
@@ -17,13 +17,13 @@
#define TAG "FuriHalSubGhz"
-static uint32_t furi_hal_subghz_debug_gpio_buff[2];
+static uint32_t furi_hal_subghz_debug_gpio_buff[2] = {0};
/* DMA Channels definition */
-#define SUBGHZ_DMA DMA2
-#define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1
-#define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2
-#define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1
+#define SUBGHZ_DMA (DMA2)
+#define SUBGHZ_DMA_CH1_CHANNEL (LL_DMA_CHANNEL_1)
+#define SUBGHZ_DMA_CH2_CHANNEL (LL_DMA_CHANNEL_2)
+#define SUBGHZ_DMA_CH1_IRQ (FuriHalInterruptIdDma2Ch1)
#define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL
#define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL
@@ -36,7 +36,6 @@ typedef enum {
SubGhzStateAsyncRx, /**< Async RX started */
SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */
- SubGhzStateAsyncTxLast, /**< Async TX continue, DMA completed and timer got last value to go */
SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */
} SubGhzState;
@@ -106,6 +105,10 @@ void furi_hal_subghz_init() {
&FURI_HAL_SUBGHZ_TX_GPIO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
#endif
+#ifdef FURI_HAL_SUBGHZ_ASYNC_MIRROR_GPIO
+ furi_hal_subghz_set_async_mirror_pin(&FURI_HAL_SUBGHZ_ASYNC_MIRROR_GPIO);
+#endif
+
// Reset
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
cc1101_reset(&furi_hal_spi_bus_handle_subghz);
@@ -185,8 +188,8 @@ void furi_hal_subghz_dump_state() {
void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) {
//load config
+ furi_hal_subghz_reset();
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
- cc1101_reset(&furi_hal_spi_bus_handle_subghz);
uint32_t i = 0;
uint8_t pa[8] = {0};
while(preset_data[i]) {
@@ -214,8 +217,8 @@ void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) {
}
void furi_hal_subghz_load_registers(const uint8_t* data) {
+ furi_hal_subghz_reset();
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
- cc1101_reset(&furi_hal_spi_bus_handle_subghz);
uint32_t i = 0;
while(data[i]) {
cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i], data[i + 1]);
@@ -294,6 +297,7 @@ void furi_hal_subghz_reset() {
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
cc1101_reset(&furi_hal_spi_bus_handle_subghz);
+ // Warning: push pull cc1101 clock output on GD0
cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance);
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
}
@@ -435,6 +439,7 @@ void furi_hal_subghz_set_path(FuriHalSubGhzPath path) {
static bool furi_hal_subghz_start_debug() {
bool ret = false;
if(furi_hal_subghz.async_mirror_pin != NULL) {
+ furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false);
furi_hal_gpio_init(
furi_hal_subghz.async_mirror_pin,
GpioModeOutputPushPull,
@@ -576,73 +581,121 @@ void furi_hal_subghz_stop_async_rx() {
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
}
+typedef enum {
+ FuriHalSubGhzAsyncTxMiddlewareStateIdle,
+ FuriHalSubGhzAsyncTxMiddlewareStateReset,
+ FuriHalSubGhzAsyncTxMiddlewareStateRun,
+} FuriHalSubGhzAsyncTxMiddlewareState;
+
+typedef struct {
+ FuriHalSubGhzAsyncTxMiddlewareState state;
+ bool is_odd_level;
+ uint32_t adder_duration;
+} FuriHalSubGhzAsyncTxMiddleware;
+
typedef struct {
uint32_t* buffer;
- LevelDuration carry_ld;
FuriHalSubGhzAsyncTxCallback callback;
void* callback_context;
uint64_t duty_high;
uint64_t duty_low;
+ FuriHalSubGhzAsyncTxMiddleware middleware;
} FuriHalSubGhzAsyncTx;
static FuriHalSubGhzAsyncTx furi_hal_subghz_async_tx = {0};
-static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
- furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx);
- while(samples > 0) {
- bool is_odd = samples % 2;
- LevelDuration ld;
- if(level_duration_is_reset(furi_hal_subghz_async_tx.carry_ld)) {
- ld = furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context);
- } else {
- ld = furi_hal_subghz_async_tx.carry_ld;
- furi_hal_subghz_async_tx.carry_ld = level_duration_reset();
+void furi_hal_subghz_async_tx_middleware_idle(FuriHalSubGhzAsyncTxMiddleware* middleware) {
+ middleware->state = FuriHalSubGhzAsyncTxMiddlewareStateIdle;
+ middleware->is_odd_level = false;
+ middleware->adder_duration = 0;
+}
+
+static inline uint32_t furi_hal_subghz_async_tx_middleware_get_duration(
+ FuriHalSubGhzAsyncTxMiddleware* middleware,
+ FuriHalSubGhzAsyncTxCallback callback) {
+ uint32_t ret = 0;
+ bool is_level = false;
+
+ if(middleware->state == FuriHalSubGhzAsyncTxMiddlewareStateReset) return 0;
+
+ while(1) {
+ LevelDuration ld = callback(furi_hal_subghz_async_tx.callback_context);
+ if(level_duration_is_reset(ld)) {
+ middleware->state = FuriHalSubGhzAsyncTxMiddlewareStateReset;
+ if(!middleware->is_odd_level) {
+ return 0;
+ } else {
+ return middleware->adder_duration;
+ }
+ } else if(level_duration_is_wait(ld)) {
+ middleware->is_odd_level = !middleware->is_odd_level;
+ ret = middleware->adder_duration + FURI_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
+ middleware->adder_duration = 0;
+ return ret;
}
- if(level_duration_is_wait(ld)) {
- *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- buffer++;
- samples--;
- } else if(level_duration_is_reset(ld)) {
+ is_level = level_duration_get_level(ld);
+
+ if(middleware->state == FuriHalSubGhzAsyncTxMiddlewareStateIdle) {
+ if(is_level != middleware->is_odd_level) {
+ middleware->state = FuriHalSubGhzAsyncTxMiddlewareStateRun;
+ middleware->is_odd_level = is_level;
+ middleware->adder_duration = 0;
+ } else {
+ continue;
+ }
+ }
+
+ if(middleware->state == FuriHalSubGhzAsyncTxMiddlewareStateRun) {
+ if(is_level == middleware->is_odd_level) {
+ middleware->adder_duration += level_duration_get_duration(ld);
+ continue;
+ } else {
+ middleware->is_odd_level = is_level;
+ ret = middleware->adder_duration;
+ middleware->adder_duration = level_duration_get_duration(ld);
+ return ret;
+ }
+ }
+ }
+}
+
+static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
+ furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx);
+
+ while(samples > 0) {
+ volatile uint32_t duration = furi_hal_subghz_async_tx_middleware_get_duration(
+ &furi_hal_subghz_async_tx.middleware, furi_hal_subghz_async_tx.callback);
+ if(duration == 0) {
*buffer = 0;
buffer++;
samples--;
LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF);
LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF);
+ if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) {
+ LL_DMA_ClearFlag_HT1(SUBGHZ_DMA);
+ }
+ if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) {
+ LL_DMA_ClearFlag_TC1(SUBGHZ_DMA);
+ }
LL_TIM_EnableIT_UPDATE(TIM2);
break;
} else {
- bool level = level_duration_get_level(ld);
-
- // Inject guard time if level is incorrect
- if(is_odd != level) {
- *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- buffer++;
- samples--;
- if(is_odd) {
- furi_hal_subghz_async_tx.duty_high += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- } else {
- furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- }
-
- // Special case: prevent buffer overflow if sample is last
- if(samples == 0) {
- furi_hal_subghz_async_tx.carry_ld = ld;
- break;
- }
+ // Lowest possible value is 2us
+ if(duration > 2) {
+ // Subtract 1 since we counting from 0
+ *buffer = duration - 1;
+ } else {
+ *buffer = 1;
}
-
- uint32_t duration = level_duration_get_duration(ld);
- furi_assert(duration > 0);
- *buffer = duration;
buffer++;
samples--;
+ }
- if(is_odd) {
- furi_hal_subghz_async_tx.duty_high += duration;
- } else {
- furi_hal_subghz_async_tx.duty_low += duration;
- }
+ if(samples % 2) {
+ furi_hal_subghz_async_tx.duty_high += duration;
+ } else {
+ furi_hal_subghz_async_tx.duty_low += duration;
}
}
}
@@ -654,13 +707,13 @@ static void furi_hal_subghz_async_tx_dma_isr() {
if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) {
LL_DMA_ClearFlag_HT1(SUBGHZ_DMA);
furi_hal_subghz_async_tx_refill(
- furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF);
+ furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF);
}
if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) {
LL_DMA_ClearFlag_TC1(SUBGHZ_DMA);
furi_hal_subghz_async_tx_refill(
- furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF,
- API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF);
+ furi_hal_subghz_async_tx.buffer + FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF,
+ FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF);
}
#else
#error Update this code. Would you kindly?
@@ -672,15 +725,11 @@ static void furi_hal_subghz_async_tx_timer_isr() {
LL_TIM_ClearFlag_UPDATE(TIM2);
if(LL_TIM_GetAutoReload(TIM2) == 0) {
if(furi_hal_subghz.state == SubGhzStateAsyncTx) {
- furi_hal_subghz.state = SubGhzStateAsyncTxLast;
- LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF);
- } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) {
furi_hal_subghz.state = SubGhzStateAsyncTxEnd;
+ LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF);
//forcibly pulls the pin to the ground so that there is no carrier
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow);
+ furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
LL_TIM_DisableCounter(TIM2);
- } else {
- furi_crash();
}
}
}
@@ -702,7 +751,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
furi_hal_subghz_async_tx.duty_high = 0;
furi_hal_subghz_async_tx.buffer =
- malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t));
+ malloc(FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t));
// Connect CC1101_GD0 to TIM2 as output
furi_hal_gpio_init_ex(
@@ -718,7 +767,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
- dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL;
+ dma_config.NbData = FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL;
dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
dma_config.Priority = LL_DMA_MODE_NORMAL;
LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config);
@@ -730,14 +779,12 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
furi_hal_bus_enable(FuriHalBusTIM2);
// Configure TIM2
- LL_TIM_InitTypeDef TIM_InitStruct = {0};
- TIM_InitStruct.Prescaler = 64 - 1;
- TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
- TIM_InitStruct.Autoreload = 1000;
- TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
- LL_TIM_Init(TIM2, &TIM_InitStruct);
+ LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
+ LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
+ LL_TIM_SetAutoReload(TIM2, 1000);
+ LL_TIM_SetPrescaler(TIM2, 64 - 1);
LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
- LL_TIM_EnableARRPreload(TIM2);
+ LL_TIM_DisableARRPreload(TIM2);
// Configure TIM2 CH2
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
@@ -745,21 +792,21 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.CompareValue = 0;
- TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW;
+ TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct);
LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2);
LL_TIM_DisableMasterSlaveMode(TIM2);
furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL);
+ furi_hal_subghz_async_tx_middleware_idle(&furi_hal_subghz_async_tx.middleware);
furi_hal_subghz_async_tx_refill(
- furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL);
+ furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL);
LL_TIM_EnableDMAReq_UPDATE(TIM2);
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
// Start counter
- LL_TIM_GenerateEvent_UPDATE(TIM2);
#ifdef FURI_HAL_SUBGHZ_TX_GPIO
furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true);
#endif
@@ -776,8 +823,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
// furi_hal_subghz_debug_gpio_buff[0] = 0;
// furi_hal_subghz_debug_gpio_buff[1] = 0;
- furi_hal_subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER;
- furi_hal_subghz_debug_gpio_buff[1] = gpio->pin;
+ furi_hal_subghz_debug_gpio_buff[0] = gpio->pin;
+ furi_hal_subghz_debug_gpio_buff[1] = (uint32_t)gpio->pin << GPIO_NUMBER;
dma_config.MemoryOrM2MDstAddress = (uint32_t)furi_hal_subghz_debug_gpio_buff;
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR);
@@ -805,9 +852,12 @@ bool furi_hal_subghz_is_async_tx_complete() {
void furi_hal_subghz_stop_async_tx() {
furi_assert(
furi_hal_subghz.state == SubGhzStateAsyncTx ||
- furi_hal_subghz.state == SubGhzStateAsyncTxLast ||
furi_hal_subghz.state == SubGhzStateAsyncTxEnd);
+ // Deinitialize GPIO
+ // Keep in mind that cc1101 will try to pull it up in idle.
+ furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
+
// Shutdown radio
furi_hal_subghz_idle();
#ifdef FURI_HAL_SUBGHZ_TX_GPIO
@@ -815,7 +865,6 @@ void furi_hal_subghz_stop_async_tx() {
#endif
// Deinitialize Timer
- FURI_CRITICAL_ENTER();
furi_hal_bus_disable(FuriHalBusTIM2);
furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL);
@@ -824,16 +873,11 @@ void furi_hal_subghz_stop_async_tx() {
furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL);
- // Deinitialize GPIO
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-
// Stop debug
if(furi_hal_subghz_stop_debug()) {
LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF);
}
- FURI_CRITICAL_EXIT();
-
free(furi_hal_subghz_async_tx.buffer);
float duty_cycle =
diff --git a/targets/f7/furi_hal/furi_hal_subghz.h b/targets/f7/furi_hal/furi_hal_subghz.h
index 136a2af41..b901e85ea 100644
--- a/targets/f7/furi_hal/furi_hal_subghz.h
+++ b/targets/f7/furi_hal/furi_hal_subghz.h
@@ -18,10 +18,10 @@
extern "C" {
#endif
-/** Low level buffer dimensions and guard times */
-#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256)
-#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2)
-#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 999
+/** Various subghz defines */
+#define FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256u)
+#define FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2)
+#define FURI_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME (999u)
/** Switchable Radio Paths */
typedef enum {