Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FL-2754, FL-3945] EM4305 support #4069

Merged
merged 14 commits into from
Feb 12, 2025
108 changes: 65 additions & 43 deletions lib/lfrfid/lfrfid_worker_modes.c
Original file line number Diff line number Diff line change
Expand Up @@ -499,9 +499,6 @@ static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) {
static void lfrfid_worker_mode_write_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;
Expand All @@ -510,63 +507,88 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) {
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");
t5577_write(&request->t5577);

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);
protocol_dict_get_data(worker->protocols, protocol, verify_data, data_size);

if(state == LFRFIDWorkerReadOK) {
bool read_success = false;
while(!lfrfid_worker_check_for_stop(worker)) {
FURI_LOG_D(TAG, "Data write");
uint16_t skips = 0;
for(size_t i = 0; i < LFRFIDWriteTypeMax; i++) {
memset(request, 0, sizeof(LFRFIDWriteRequest));
LFRFIDWriteType write_type = i;
request->write_type = write_type;

if(read_result == protocol) {
protocol_dict_get_data(worker->protocols, protocol, read_data, data_size);
protocol_dict_set_data(worker->protocols, protocol, verify_data, data_size);

if(memcmp(read_data, verify_data, data_size) == 0) {
read_success = true;
}
}
bool can_be_written =
protocol_dict_get_write_data(worker->protocols, protocol, request);

if(read_success) {
if(!can_be_written) {
skips++;
if(skips == LFRFIDWriteTypeMax) {
if(worker->write_cb) {
worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx);
worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx);
}
break;
} else {
unsuccessful_reads++;
}
continue;
}

if(unsuccessful_reads == LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS) {
if(worker->write_cb) {
worker->write_cb(LFRFIDWorkerWriteFobCannotBeWritten, worker->cb_ctx);
}
}
memset(read_data, 0, data_size);

if(request->write_type == LFRFIDWriteTypeT5577) {
t5577_write(&request->t5577);
} else if(request->write_type == LFRFIDWriteTypeEM4305) {
em4305_write_with_mask(&request->em4305, request->em4305.mask);
} else {
furi_crash("Unknown write type");
}
}
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;
}
} 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(read_success) {
if(worker->write_cb) {
worker->write_cb(LFRFIDWorkerWriteTooLongToWrite, worker->cb_ctx);
worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx);
}
}
break;
} else {
unsuccessful_reads++;

lfrfid_worker_delay(worker, LFRFID_WORKER_WRITE_DROP_TIME_MS);
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;
}
} else {
if(worker->write_cb) {
worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx);

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

free(request);
Expand Down
6 changes: 6 additions & 0 deletions lib/lfrfid/protocols/lfrfid_protocols.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include <toolbox/protocols/protocol.h>
#include "../tools/t5577.h"
#include "../tools/em4305.h"

typedef enum {
LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */
Expand Down Expand Up @@ -31,18 +32,23 @@ typedef enum {
LFRFIDProtocolNexwatch,
LFRFIDProtocolSecurakey,
LFRFIDProtocolGProxII,

LFRFIDProtocolMax,
} LFRFIDProtocol;

extern const ProtocolBase* lfrfid_protocols[];

typedef enum {
LFRFIDWriteTypeT5577,
LFRFIDWriteTypeEM4305,

LFRFIDWriteTypeMax,
} LFRFIDWriteType;

typedef struct {
LFRFIDWriteType write_type;
union {
LFRFIDT5577 t5577;
LFRFIDEM4305 em4305;
};
} LFRFIDWriteRequest;
26 changes: 26 additions & 0 deletions lib/lfrfid/protocols/protocol_em4100.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ uint32_t protocol_em4100_get_t5577_bitrate(ProtocolEM4100* proto) {
}
}

uint32_t protocol_em4100_get_em4305_bitrate(ProtocolEM4100* proto) {
switch(proto->clock_per_bit) {
case 64:
return EM4x05_SET_BITRATE(64);
case 32:
return EM4x05_SET_BITRATE(32);
case 16:
return EM4x05_SET_BITRATE(16);
default:
return EM4x05_SET_BITRATE(64);
}
}

uint16_t protocol_em4100_get_short_time_low(ProtocolEM4100* proto) {
return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) -
EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto);
Expand Down Expand Up @@ -339,6 +352,19 @@ bool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) {
request->t5577.block[2] = protocol->encoded_data;
request->t5577.blocks_to_write = 3;
result = true;
} else if(request->write_type == LFRFIDWriteTypeEM4305) {
request->em4305.word[4] =
(EM4x05_MODULATION_MANCHESTER | protocol_em4100_get_em4305_bitrate(protocol) |
(6 << EM4x05_MAXBLOCK_SHIFT));
uint64_t encoded_data_reversed = 0;
for(uint8_t i = 0; i < 64; i++) {
encoded_data_reversed = (encoded_data_reversed << 1) |
((protocol->encoded_data >> i) & 1);
}
request->em4305.word[5] = encoded_data_reversed;
request->em4305.word[6] = encoded_data_reversed >> 32;
request->em4305.mask = 0x70;
result = true;
}
return result;
}
Expand Down
167 changes: 167 additions & 0 deletions lib/lfrfid/tools/em4305.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#include "em4305.h"
#include <furi.h>
#include <furi_hal_rfid.h>

#define TAG "EM4305"

#define EM4305_TIMING_1 (32)
#define EM4305_TIMING_0_OFF (23)
#define EM4305_TIMING_0_ON (18)

#define EM4305_FIELD_STOP_OFF_CYCLES (55)
#define EM4305_FIELD_STOP_ON_CYCLES (18)

#define EM4305_TIMING_POWER_CHECK (1480)
#define EM4305_TIMING_EEPROM_WRITE (9340)

static bool em4305_line_parity(uint8_t data) {
uint8_t parity = 0;
for(uint8_t i = 0; i < 8; i++) {
parity ^= (data >> i) & 1;
}
return parity;
}

static uint64_t em4305_prepare_data(uint32_t data) {
uint8_t i, j;
uint64_t data_with_parity = 0;

// 4 lines of 8 bits of data
// line even parity at bits 8 17 26 35
// column even parity at bits 36-43
// bit 44 is always 0
// final table is 5 lines of 9 bits

// line parity
for(i = 0; i < 4; i++) {
for(j = 0; j < 8; j++) {
data_with_parity = (data_with_parity << 1) | ((data >> (i * 8 + j)) & 1);
}
data_with_parity = (data_with_parity << 1) | (uint64_t)em4305_line_parity(data >> (i * 8));
}

// column parity
for(i = 0; i < 8; i++) {
uint8_t column_parity = 0;
for(j = 0; j < 4; j++) {
column_parity ^= (data >> (j * 8 + i)) & 1;
}
data_with_parity = (data_with_parity << 1) | column_parity;
}

// bit 44
data_with_parity = (data_with_parity << 1) | 0;

return data_with_parity;
}

static void em4305_start(void) {
furi_hal_rfid_tim_read_start(125000, 0.5);

// do not ground the antenna
furi_hal_rfid_pin_pull_release();
}

static void em4305_stop(void) {
furi_hal_rfid_tim_read_stop();
furi_hal_rfid_pins_reset();
}

static void em4305_write_bit(bool value) {
if(value) {
furi_delay_us(EM4305_TIMING_1 * 8);
} else {
furi_hal_rfid_tim_read_pause();
furi_delay_us(EM4305_TIMING_0_OFF * 8);
furi_hal_rfid_tim_read_continue();
furi_delay_us(EM4305_TIMING_0_ON * 8);
}
}

static void em4305_write_opcode(uint8_t value) {
// 3 bit opcode
for(uint8_t i = 0; i < 3; i++) {
em4305_write_bit((value >> i) & 1);
}

// parity
bool parity = 0;
for(uint8_t i = 0; i < 3; i++) {
parity ^= (value >> i) & 1;
}
em4305_write_bit(parity);
}

static void em4305_field_stop() {
furi_hal_rfid_tim_read_pause();
furi_delay_us(EM4305_FIELD_STOP_OFF_CYCLES * 8);
furi_hal_rfid_tim_read_continue();
furi_delay_us(EM4305_FIELD_STOP_ON_CYCLES * 8);
}

static void em4305_write_word(uint8_t address, uint32_t data) {
// parity
uint64_t data_with_parity = em4305_prepare_data(data);

// power up the tag
furi_delay_us(8000);

// field stop
em4305_field_stop();

// start bit
em4305_write_bit(0);

// opcode
em4305_write_opcode(EM4x05_OPCODE_WRITE);

// address
bool address_parity = 0;
for(uint8_t i = 0; i < 4; i++) {
em4305_write_bit((address >> (i)) & 1);
address_parity ^= (address >> (i)) & 1;
}
em4305_write_bit(0);
em4305_write_bit(0);
em4305_write_bit(address_parity);

// data
for(uint8_t i = 0; i < 45; i++) {
em4305_write_bit((data_with_parity >> (44 - i)) & 1);
}

// wait for power check and eeprom write
furi_delay_us(EM4305_TIMING_POWER_CHECK);
furi_delay_us(EM4305_TIMING_EEPROM_WRITE);
}

void em4305_write(LFRFIDEM4305* data) {
furi_check(data);

em4305_start();
FURI_CRITICAL_ENTER();

for(uint8_t i = 0; i < EM4x05_WORD_COUNT; i++) {
em4305_write_word(i, data->word[i]);
}

FURI_CRITICAL_EXIT();
em4305_stop();
}

void em4305_write_with_mask(LFRFIDEM4305* data, uint8_t word) {
UNUSED(word);
furi_check(data);

em4305_start();
FURI_CRITICAL_ENTER();

for(uint8_t i = 0; i < EM4x05_WORD_COUNT; i++) {
if(data->mask & (1 << i)) {
em4305_write_word(i, data->word[i]);
}
}

FURI_CRITICAL_EXIT();
em4305_stop();
}
Loading
Loading