Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
Methodius 2024-01-15 19:03:15 +09:00
commit 3a82b3aa3c
No known key found for this signature in database
GPG key ID: 122FA99A00B41679
39 changed files with 1295 additions and 250 deletions

View file

@ -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
<br><br>
#### Known NFC post-refactor regressions list:
- Mifare Mini clones reading is broken (original mini working fine) (OFW)

View file

@ -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(

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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;

View file

@ -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);
}

View file

@ -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"],

View file

@ -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);

View file

@ -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 <nfc/helpers/nfc_util.h>
#include <flipper_application/flipper_application.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#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;
}

View file

@ -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,

View file

@ -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,

View file

@ -0,0 +1,166 @@
/*
* Parser for Zolotaya Korona Online card (Russia).
* Tariffs research by DNZ1393
*
* Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "furi_hal_rtc.h"
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#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;
}

View file

@ -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) {

View file

@ -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;

View file

@ -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

View file

@ -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}*"),

View file

@ -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;

View file

@ -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

View file

@ -22,6 +22,7 @@ typedef enum {
LFRFIDWorkerIdle,
LFRFIDWorkerRead,
LFRFIDWorkerWrite,
LFRFIDWorkerWriteWithPass,
LFRFIDWorkerEmulate,
LFRFIDWorkerReadRaw,
LFRFIDWorkerEmulateRaw,

View file

@ -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},

View file

@ -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();
}

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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):

View file

@ -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

View file

@ -8,7 +8,7 @@
"configurationProvider": "ms-vscode.cpptools",
"cStandard": "gnu17",
"cppStandard": "c++17"
},
}
],
"version": 4
}

View file

@ -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

View file

@ -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",

View file

@ -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,

View file

@ -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

View file

@ -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:

View file

@ -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,,

1 entry status name type params
2 Version + 50.1 50.2
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h

View file

@ -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

1 entry status name type params
2069 Function + lfrfid_worker_stop void LFRFIDWorker*
2070 Function + lfrfid_worker_stop_thread void LFRFIDWorker*
2071 Function + lfrfid_worker_write_start void LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*
2072 Function + lfrfid_worker_write_with_pass_start void LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*
2073 Function - lgamma double double
2074 Function - lgamma_r double double, int*
2075 Function - lgammaf float float
3226 Function + submenu_set_orientation void Submenu*, ViewOrientation
3227 Function + submenu_set_selected_item void Submenu*, uint32_t
3228 Function - system int const char*
3229 Function + t5577_get_default_passwords const uint32_t* uint8_t*
3230 Function + t5577_write void LFRFIDT5577*
3231 Function + t5577_write_with_mask void LFRFIDT5577*, uint8_t, uint32_t
3232 Function + t5577_write_with_pass void LFRFIDT5577*, uint32_t
3233 Function - tan double double
3234 Function - tanf float float

View file

@ -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 =

View file

@ -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 {