Skip to content

Commit

Permalink
[FL-2754, FL-3945] EM4305 support (#4069)
Browse files Browse the repository at this point in the history
* Initial EM4305 write support
* Support for writing EM4100 data to EM4305 blank tags
* F18 API version bump
* Satisfy pvs
* Lib: cleanup em4305 code
* Mask size fix
* Electra
* Fix leftovers from a previous implementation
* Viking
* Gallagher
* LFRFID: cleanup em4305

Co-authored-by: あく <alleteam@gmail.com>
  • Loading branch information
Astrrra and skotopes authored Feb 12, 2025
1 parent 8059959 commit 00f287e
Show file tree
Hide file tree
Showing 10 changed files with 358 additions and 45 deletions.
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(&request->em4305);
} 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;
18 changes: 18 additions & 0 deletions lib/lfrfid/protocols/protocol_electra.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,24 @@ bool protocol_electra_write_data(ProtocolElectra* protocol, void* data) {
request->t5577.blocks_to_write = 5;
result = true;
}
if(request->write_type == LFRFIDWriteTypeEM4305) {
request->em4305.word[4] =
(EM4x05_MODULATION_MANCHESTER | EM4x05_SET_BITRATE(64) | (8 << EM4x05_MAXBLOCK_SHIFT));
uint64_t encoded_data_reversed = 0;
uint64_t encoded_epilogue_reversed = 0;
for(uint8_t i = 0; i < 64; i++) {
encoded_data_reversed = (encoded_data_reversed << 1) |
((protocol->encoded_base_data >> i) & 1);
encoded_epilogue_reversed = (encoded_epilogue_reversed << 1) |
((protocol->encoded_epilogue >> i) & 1);
}
request->em4305.word[5] = encoded_data_reversed & 0xFFFFFFFF;
request->em4305.word[6] = encoded_data_reversed >> 32;
request->em4305.word[7] = encoded_epilogue_reversed & 0xFFFFFFFF;
request->em4305.word[8] = encoded_epilogue_reversed >> 32;
request->em4305.mask = 0x01F0;
result = true;
}
return result;
}

Expand Down
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
14 changes: 14 additions & 0 deletions lib/lfrfid/protocols/protocol_gallagher.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,20 @@ bool protocol_gallagher_write_data(ProtocolGallagher* protocol, void* data) {
request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);
request->t5577.blocks_to_write = 4;
result = true;
} else if(request->write_type == LFRFIDWriteTypeEM4305) {
request->em4305.word[4] =
(EM4x05_MODULATION_MANCHESTER | EM4x05_SET_BITRATE(32) | (7 << EM4x05_MAXBLOCK_SHIFT));
uint32_t encoded_data_reversed[3] = {0};
for(uint8_t i = 0; i < (32 * 3); i++) {
encoded_data_reversed[i / 32] =
(encoded_data_reversed[i / 32] << 1) |
(bit_lib_get_bit(protocol->encoded_data, ((32 * 3) - i)) & 1);
}
request->em4305.word[5] = encoded_data_reversed[2];
request->em4305.word[6] = encoded_data_reversed[1];
request->em4305.word[7] = encoded_data_reversed[0];
request->em4305.mask = 0xF0;
result = true;
}
return result;
}
Expand Down
13 changes: 13 additions & 0 deletions lib/lfrfid/protocols/protocol_viking.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,19 @@ bool protocol_viking_write_data(ProtocolViking* protocol, void* data) {
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
request->t5577.blocks_to_write = 3;
result = true;
} else if(request->write_type == LFRFIDWriteTypeEM4305) {
request->em4305.word[4] =
(EM4x05_MODULATION_MANCHESTER | EM4x05_SET_BITRATE(32) | (6 << EM4x05_MAXBLOCK_SHIFT));
uint32_t encoded_data_reversed[2] = {0};
for(uint8_t i = 0; i < 64; i++) {
encoded_data_reversed[i / 32] =
(encoded_data_reversed[i / 32] << 1) |
(bit_lib_get_bit(protocol->encoded_data, (63 - i)) & 1);
}
request->em4305.word[5] = encoded_data_reversed[1];
request->em4305.word[6] = encoded_data_reversed[0];
request->em4305.mask = 0x70;
result = true;
}
return result;
}
Expand Down
Loading

0 comments on commit 00f287e

Please sign in to comment.