mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 06:54:19 +00:00
Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
3a82b3aa3c
39 changed files with 1295 additions and 250 deletions
12
CHANGELOG.md
12
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
|
||||
<br><br>
|
||||
#### Known NFC post-refactor regressions list:
|
||||
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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"],
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
475
applications/main/nfc/plugins/supported_cards/ndef.c
Normal file
475
applications/main/nfc/plugins/supported_cards/ndef.c
Normal 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;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
7
assets/dolphin/external/manifest.txt
vendored
7
assets/dolphin/external/manifest.txt
vendored
|
@ -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
|
||||
|
|
|
@ -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}*"),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -22,6 +22,7 @@ typedef enum {
|
|||
LFRFIDWorkerIdle,
|
||||
LFRFIDWorkerRead,
|
||||
LFRFIDWorkerWrite,
|
||||
LFRFIDWorkerWriteWithPass,
|
||||
LFRFIDWorkerEmulate,
|
||||
LFRFIDWorkerReadRaw,
|
||||
LFRFIDWorkerEmulateRaw,
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"configurationProvider": "ms-vscode.cpptools",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "c++17"
|
||||
},
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,,
|
||||
|
|
|
|
@ -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
|
||||
|
|
|
|
@ -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 =
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue