Files
unleashed-firmware/lib/lfrfid/protocols/protocol_hid_generic.c
T
Mykhailo ShevchukandClaude Opus 5 a70a69d9d4 RFID: share the duplicated write_data bodies (-992 B of flash) (#1183)
* RFID: share the duplicated write_data bodies (-992 B of flash)

Nearly every LF RFID protocol filled a write request the same way and differed
only in its config word: protocol_viking_write_data() and
protocol_jablotron_write_data() were byte-identical except for four constants,
and six protocols carried a verbatim copy of the same 64-bit reversal loop.

27 call sites across 20 protocols now share two bodies in
protocols/lfrfid_write_fill.c. The block or word count is read out of the
config word's maxblock field rather than passed alongside it - it is the same
number in both places at all 27 sites, and the chip streams what that field
says, so the two could never legitimately disagree.

Behaviour is unchanged. Each original inline loop was transcribed and compared
against the shared body over random vectors for every bit width in use, and the
derived counts were checked against the ones the call sites used to pass.

No API change: the per-protocol callbacks were never exported, and
protocol_dict_get_write_data() and the lfrfid_protocols vtable are untouched.

Left alone on purpose: em4100, electra and h10301 keep their encoded data as a
uint64_t or uint32_t[] rather than an MSB-first byte buffer, so a bit_lib
helper would byte-swap them; and gallagher's EM4305 arm reads its payload one
bit higher than every sibling (#1181), which folding would silently normalise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoYukcdV7vyusuaXtYcEdu

* upd changelog

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JoYukcdV7vyusuaXtYcEdu

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-20 21:41:38 +03:00

258 lines
8.4 KiB
C

#include <furi.h>
#include <toolbox/protocols/protocol.h>
#include <lfrfid/tools/fsk_demod.h>
#include <lfrfid/tools/fsk_osc.h>
#include "lfrfid_protocols.h"
#include "lfrfid_write_fill.h"
#include <bit_lib/bit_lib.h>
#define JITTER_TIME (20)
#define MIN_TIME (64 - JITTER_TIME)
#define MAX_TIME (80 + JITTER_TIME)
#define HID_DATA_SIZE 11
#define HID_PREAMBLE_SIZE 1
#define HID_PROTOCOL_SIZE_UNKNOWN 0
#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE)
#define HID_ENCODED_BIT_SIZE ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8)
#define HID_DECODED_DATA_SIZE (6)
#define HID_DECODED_BIT_SIZE ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2)
#define HID_PREAMBLE 0x1D
typedef struct {
FSKDemod* fsk_demod;
} ProtocolHIDDecoder;
typedef struct {
FSKOsc* fsk_osc;
uint8_t encoded_index;
uint32_t pulse;
} ProtocolHIDEncoder;
typedef struct {
ProtocolHIDDecoder decoder;
ProtocolHIDEncoder encoder;
uint8_t encoded_data[HID_ENCODED_DATA_SIZE];
uint8_t data[HID_DECODED_DATA_SIZE];
} ProtocolHID;
ProtocolHID* protocol_hid_generic_alloc(void) {
ProtocolHID* protocol = malloc(sizeof(ProtocolHID));
protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);
protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);
return protocol;
}
void protocol_hid_generic_free(ProtocolHID* protocol) {
fsk_demod_free(protocol->decoder.fsk_demod);
fsk_osc_free(protocol->encoder.fsk_osc);
free(protocol);
}
uint8_t* protocol_hid_generic_get_data(ProtocolHID* protocol) {
return protocol->data;
}
void protocol_hid_generic_decoder_start(ProtocolHID* protocol) {
memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE);
}
static bool protocol_hid_generic_can_be_decoded(const uint8_t* data) {
// check preamble
if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) {
return false;
}
// check for manchester encoding
for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {
for(size_t n = 0; n < 4; n++) {
uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11;
if(bit_pair == 0b11 || bit_pair == 0b00) {
return false;
}
}
}
return true;
}
static void protocol_hid_generic_decode(const uint8_t* from, uint8_t* to) {
size_t bit_index = 0;
for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {
for(size_t n = 0; n < 4; n++) {
uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11;
if(bit_pair == 0b01) {
bit_lib_set_bit(to, bit_index, 0);
} else if(bit_pair == 0b10) {
bit_lib_set_bit(to, bit_index, 1);
}
bit_index++;
}
}
}
/**
* Decodes size from the HID Proximity header:
* - If any of the first six bits is 1, the key is composed of the bits
* following the first 1
* - Otherwise, if the first six bits are 0:
* - If the seventh bit is 0, the key is composed of the remaining 37 bits.
* - If the seventh bit is 1, the size header continues until the next 1 bit,
* and the key is composed of however many bits remain.
*
* HID Proximity keys are 26 bits at minimum. If the header implies a key size
* under 26 bits, this function returns HID_PROTOCOL_SIZE_UNKNOWN.
*/
static uint8_t protocol_hid_generic_decode_protocol_size(ProtocolHID* protocol) {
for(size_t bit_index = 0; bit_index < 6; bit_index++) {
if(bit_lib_get_bit(protocol->data, bit_index)) {
return HID_DECODED_BIT_SIZE - bit_index - 1;
}
}
if(!bit_lib_get_bit(protocol->data, 6)) {
return 37;
}
size_t bit_index = 7;
uint8_t size = 36;
while(!bit_lib_get_bit(protocol->data, bit_index) && size >= 26) {
size--;
bit_index++;
}
return size < 26 ? HID_PROTOCOL_SIZE_UNKNOWN : size;
}
bool protocol_hid_generic_decoder_feed(ProtocolHID* protocol, bool level, uint32_t duration) {
bool value;
uint32_t count;
bool result = false;
fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);
if(count > 0) {
for(size_t i = 0; i < count; i++) {
bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value);
if(protocol_hid_generic_can_be_decoded(protocol->encoded_data)) {
protocol_hid_generic_decode(protocol->encoded_data, protocol->data);
result = true;
}
}
}
return result;
}
static void protocol_hid_generic_encode(ProtocolHID* protocol) {
protocol->encoded_data[0] = HID_PREAMBLE;
size_t bit_index = 0;
for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) {
bool bit = bit_lib_get_bit(protocol->data, i);
if(bit) {
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1);
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0);
} else {
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0);
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1);
}
bit_index += 2;
}
}
bool protocol_hid_generic_encoder_start(ProtocolHID* protocol) {
protocol->encoder.encoded_index = 0;
protocol->encoder.pulse = 0;
protocol_hid_generic_encode(protocol);
return true;
}
LevelDuration protocol_hid_generic_encoder_yield(ProtocolHID* protocol) {
bool level = 0;
uint32_t duration = 0;
// if pulse is zero, we need to output high, otherwise we need to output low
if(protocol->encoder.pulse == 0) {
// get bit
uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);
// get pulse from oscillator
bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);
if(advance) {
bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE);
}
// duration diveded by 2 because we need to output high and low
duration = duration / 2;
protocol->encoder.pulse = duration;
level = true;
} else {
// output low half and reset pulse
duration = protocol->encoder.pulse;
protocol->encoder.pulse = 0;
level = false;
}
return level_duration_make(level, duration);
}
bool protocol_hid_generic_write_data(ProtocolHID* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_hid_generic_encoder_start(protocol);
protocol_hid_generic_decode(protocol->encoded_data, protocol->data);
protocol_hid_generic_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
lfrfid_write_fill_t5577(
request,
LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |
(3 << LFRFID_T5577_MAXBLOCK_SHIFT),
protocol->encoded_data);
result = true;
}
return result;
}
void protocol_hid_generic_render_data(ProtocolHID* protocol, FuriString* result) {
// the data itself is shown in hex by every caller, so only the frame length is added
const uint8_t protocol_size = protocol_hid_generic_decode_protocol_size(protocol);
if(protocol_size == HID_PROTOCOL_SIZE_UNKNOWN) {
furi_string_set(result, "Generic HID Proximity");
} else {
furi_string_printf(result, "%hhu-bit HID Proximity", protocol_size);
}
}
const ProtocolBase protocol_hid_generic = {
.name = "HIDProx",
.manufacturer = "Generic",
.data_size = HID_DECODED_DATA_SIZE,
.features = LFRFIDFeatureASK,
.validate_count = 6,
.alloc = (ProtocolAlloc)protocol_hid_generic_alloc,
.free = (ProtocolFree)protocol_hid_generic_free,
.get_data = (ProtocolGetData)protocol_hid_generic_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_hid_generic_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_hid_generic_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_hid_generic_encoder_start,
.yield = (ProtocolEncoderYield)protocol_hid_generic_encoder_yield,
},
.render_data = (ProtocolRenderData)protocol_hid_generic_render_data,
.render_brief_data = (ProtocolRenderData)protocol_hid_generic_render_data,
.write_data = (ProtocolWriteData)protocol_hid_generic_write_data,
};